batpred icon indicating copy to clipboard operation
batpred copied to clipboard

Adaptions for better Huawei support

Open Rhysgh opened this issue 7 months ago • 6 comments

Hi, i have been looking to resolve the clipping issue for Huawei inverters as they seem to operate different to most others.

when you force charge or more importantly discharge the battery using the huawei_solar/forcible_discharge_soc service it take priority over the PV production so i dont want to use that in the case where clipping is the potental.

before predbat what i did was change the battery mode between max_self_consumption and fully_fed_to_grid. the latter sends as much power as possabel to the gird starting with PV and topping up with battery.

i tried to trace how predbat worked and i came up with this that im trying to test on my end. i did have the select.select_option incorrect ly defined so i just fixed that now but i cant find a way to force this to try the code out..

in apps.yaml i added this, "battery_option_service: " to the template.

`# Services to charge/discharge charge_start_service: service: huawei_solar/forcible_charge_soc charge_stop_service: service: huawei_solar/stop_forcible_charge discharge_start_service: service: huawei_solar/forcible_discharge_soc discharge_stop_service: service: huawei_solar/stop_forcible_charge

battery_option_service: service: select.select_option target: entity_id: select.batteries_working_mode data: max_self_consumption: "maximise_self_consumption" fftg: "fully_fed_to_grid"`

and in the inverter.py file i added /amended this from line 2145

        """
        Adjust from exporting or not exporting based on passed target soc
        """
        max_self_consumption = self.base.get_arg('battery_option_service', 'data', 'max_self_consumption', index=self.id)
        fftg = self.base.get_arg('battery_option_service', 'data', 'fftg', index=self.id)
        service_data_stop = {"device_id": self.base.get_arg("device_id", index=self.id, default="")}
        extra_data = {"discharge_start_time": self.base.get_arg("discharge_start_time", index=self.id, default="00:00:00"), "discharge_end_time": self.base.get_arg("discharge_end_time", index=self.id, default="00:00:00")}
        if target_soc > 0 and target_soc < 100:
            service_data = {
                "device_id": self.base.get_arg("device_id", index=self.id, default=""),
                "target_soc": target_soc,
                "power": int(self.battery_rate_max_discharge * MINUTE_WATT),
            }

            # Stop charge
            self.call_service_template("charge_stop_service", service_data_stop, domain="charge"):
            self.call_service_template("battery_option_service", {"option": max_self_consumption}, domain="select")

            # Start discharge or discharge freeze
            if freeze:
                if not self.call_service_template("discharge_freeze_service", service_data, domain="discharge", extra_data=extra_data):
                    if not self.call_service_template("battery_option_service", {"option": fftg}, domain="select"):
                        self.call_service_template("discharge_start_service", service_data, domain="discharge", extra_data=extra_data)
            else:
                if not self.call_service_template("battery_option_service", {"option": fftg}, domain="select"):
                    self.call_service_template("discharge_start_service", service_data, domain="discharge", extra_data=extra_data)
        else:
            self.call_service_template("battery_option_service", {"option": max_self_consumption}, domain="select")
            if not self.call_service_template("discharge_stop_service", service_data_stop, domain="discharge"):
                self.call_service_template("charge_stop_service", service_data_stop, domain="discharge")```

do you have any input on this? can you help me refine this? once i have the matter mode working correctly i can address the freeze service as that is also something we can utalise 

Rhysgh avatar Apr 06 '25 13:04 Rhysgh

@springfall2008 i would appreciate your input on this one

Rhysgh avatar Apr 06 '25 16:04 Rhysgh

i have tried hard coding the options but i still get this error ... Warn: Service call select/select_option data {'target': {'entity_id': 'select.batteries_working_mode'}, 'data': {'max_self_consumption': 'maximise_self_consumption', 'fftg': 'fully_fed_to_grid'}} failed with response None

Rhysgh avatar Apr 07 '25 12:04 Rhysgh

i have tried hard coding the options but i still get this error

What happens if you call the service to set the inverter mode from developer tools rather than via predbat?

WyndStryke avatar Apr 07 '25 13:04 WyndStryke

i have tried hard coding the options but i still get this error

What happens if you call the service to set the inverter mode from developer tools rather than via predbat?

this works action: select.select_option target: entity_id: select.batteries_working_mode data: option: maximise_self_consumption

Image

Rhysgh avatar Apr 07 '25 13:04 Rhysgh

latest code.. does seem to remove the error but does not work

` def adjust_export_immediate(self, target_soc, freeze=False): """ Adjust from exporting or not exporting based on passed target soc """ # Retrieve battery options and settings from the app config (battery_option_service) battery_options = self.base.get_arg("battery_option_service", index=self.id, default={}) mode_map = battery_options.get("data", {}) max_self_consumption = mode_map.get("max_self_consumption") fftg = mode_map.get("fftg")

    # Prepare basic service data for stopping services
    service_data_stop = {"device_id": self.base.get_arg("device_id", index=self.id, default="")}
    extra_data = {
        "discharge_start_time": self.base.get_arg("discharge_start_time", index=self.id, default="00:00:00"),
        "discharge_end_time": self.base.get_arg("discharge_end_time", index=self.id, default="00:00:00")
    }

    if target_soc > 0 and target_soc < 100:
        service_data = {
            "device_id": self.base.get_arg("device_id", index=self.id, default=""),
            "target_soc": target_soc,
            "power": int(self.battery_rate_max_discharge * MINUTE_WATT),
        }

        # Stop charge
        self.call_service_template("charge_stop_service", service_data_stop, domain="charge")
    
        # Set battery mode to 'maximise_self_consumption'
        self.call_service_template(
            "select.select_option",
            {
                "target": {"entity_id": "select.batteries_working_mode"},
                "data": {"option": "maximise_self_consumption"}
            },
            domain="select"
        )

        # Start discharge or discharge freeze
        if freeze:
            # Attempt to call discharge_freeze_service
            if not self.call_service_template("discharge_freeze_service", service_data, domain="discharge", extra_data=extra_data):
                # If discharge_freeze_service fails, try selecting 'fully_fed_to_grid' mode
                if not self.call_service_template(
                    "select.select_option",
                    {
                        "target": {"entity_id": "select.batteries_working_mode"},
                        "data": {"option": "fully_fed_to_grid"}
                    },
                    domain="select"
                ):
                    # If both fail, start discharge
                    self.call_service_template("discharge_start_service", service_data, domain="discharge", extra_data=extra_data)
        else:
            # If not in freeze mode, attempt to select 'fully_fed_to_grid'
            service_called = self.call_service_template(
                "select.select_option",
                {
                    "target": {"entity_id": "select.batteries_working_mode"},
                    "data": {"option": "fully_fed_to_grid"}
                },
                domain="select"
            )

            # If selecting 'fully_fed_to_grid' fails, start the discharge service
            if not service_called:
                self.call_service_template("discharge_start_service", service_data, domain="discharge", extra_data=extra_data)

    else:
        # When target_soc is 0 or 100, we stop the discharge or charge process
        self.call_service_template("select.select_option", {
            "target": {"entity_id": "select.batteries_working_mode"},
            "data": {"option": "maximise_self_consumption"}
        }, domain="select")

        # Stop discharge or charge if needed
        if not self.call_service_template("discharge_stop_service", service_data_stop, domain="discharge"):
            self.call_service_template("charge_stop_service", service_data_stop, domain="discharge")`

Rhysgh avatar Apr 07 '25 14:04 Rhysgh

I don't really understand what you are doing here, without changing the predbat code why don't just call the select option server direct from discharge_start/freeze options e.g:

discharge_start_service:
   service: select.select_option
   entity_id: select.batteries_working_mode
   option: "maximise_self_consumption"

springfall2008 avatar Apr 12 '25 14:04 springfall2008