From b4a9fe997df71fb0d9f471332f4367bbb1308ef7 Mon Sep 17 00:00:00 2001 From: Matt Redfearn Date: Tue, 21 Sep 2021 08:34:55 +0100 Subject: [PATCH] Add support for export control block The export control block allows control of how much power is exported from the system to the grid. This may be useful, for example, to prevent export if the export rate is negative. --- .../solaredge_modbus/__init__.py | 43 +++++++++++++++++-- custom_components/solaredge_modbus/const.py | 21 +++++++++ custom_components/solaredge_modbus/number.py | 19 +++++++- custom_components/solaredge_modbus/select.py | 18 +++++++- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/custom_components/solaredge_modbus/__init__.py b/custom_components/solaredge_modbus/__init__.py index efe53b3..6dd0a90 100644 --- a/custom_components/solaredge_modbus/__init__.py +++ b/custom_components/solaredge_modbus/__init__.py @@ -32,6 +32,8 @@ DEFAULT_READ_BATTERY1, DEFAULT_READ_BATTERY2, BATTERY_STATUSSES, + EXPORT_CONTROL_MODE, + EXPORT_CONTROL_LIMIT_MODE, STOREDGE_CONTROL_MODE, STOREDGE_AC_CHARGE_POLICY, STOREDGE_CHARGE_DISCHARGE_MODE @@ -208,6 +210,16 @@ def connect(self): with self._lock: self._client.connect() + @property + def has_meter(self): + """Return true if a meter is available""" + return self.read_meter1 or self.read_meter2 or self.read_meter3 + + @property + def has_battery(self): + """Return true if a battery is available""" + return self.read_battery1 or self.read_battery2 + def read_holding_registers(self, unit, address, count): """Read holding registers.""" with self._lock: @@ -669,15 +681,40 @@ def read_modbus_data_inverter(self): return False def read_modbus_data_storage(self): - if not self.read_battery1 and not self.read_battery2: - return True + if self.has_battery: + count = 0x12 # Read storedge block as well + elif self.has_meter: + count = 4 # Just read export control block + else: + return True # Nothing to read here - storage_data = self.read_holding_registers(unit=1, address=57348, count=14) + storage_data = self.read_holding_registers(unit=1, address=0xE000, count=count) if not storage_data.isError(): decoder = BinaryPayloadDecoder.fromRegisters( storage_data.registers, byteorder=Endian.Big,wordorder=Endian.Little ) + #0xE000 - 1 - Export control mode + export_control_mode = decoder.decode_16bit_uint() & 7 + if export_control_mode in EXPORT_CONTROL_MODE: + self.data["export_control_mode"] = EXPORT_CONTROL_MODE[export_control_mode] + else: + self.data["export_control_mode"] = export_control_mode + + #0xE001 - 1 - Export control limit mode + export_control_limit_mode = decoder.decode_16bit_uint() & 1 + if export_control_limit_mode in EXPORT_CONTROL_MODE: + self.data["export_control_limit_mode"] = EXPORT_CONTROL_LIMIT_MODE[export_control_limit_mode] + else: + self.data["export_control_limit_mode"] = export_control_limit_mode + + #0xE002 - 2 - Export control site limit + self.data["export_control_site_limit"] = round(decoder.decode_32bit_float(), 3) + + if not self.has_battery: + # Done with the export control block + return True + #0xE004 - 1 - storage control mode storage_control_mode = decoder.decode_16bit_uint() if storage_control_mode in STOREDGE_CONTROL_MODE: diff --git a/custom_components/solaredge_modbus/const.py b/custom_components/solaredge_modbus/const.py index adce588..b067571 100644 --- a/custom_components/solaredge_modbus/const.py +++ b/custom_components/solaredge_modbus/const.py @@ -283,6 +283,18 @@ 10: "Sleep" } +EXPORT_CONTROL_MODE = { + 0: "Disabled", + 1: "Direct Export Limitation", + 2: "Indirect Export Limitation", + 4: "Production Limitation" +} + +EXPORT_CONTROL_LIMIT_MODE = { + 0: "Total", + 1: "Per phase" +} + STOREDGE_CONTROL_MODE = { 0: "Disabled", 1: "Maximize Self Consumption", @@ -308,6 +320,15 @@ 7: "Maximize self consumption", } +EXPORT_CONTROL_SELECT_TYPES = [ + ["Export control mode", "export_control_mode", 0xE000, EXPORT_CONTROL_MODE], + ["Export control limit mode", "export_control_limit_mode", 0xE001, EXPORT_CONTROL_LIMIT_MODE], +] + +EXPORT_CONTROL_NUMBER_TYPES = [ + ["Export control site limit", "export_control_site_limit", 0xE002, "f", {"min": 0, "max": 10000, "unit": "W"}], +] + STORAGE_SELECT_TYPES = [ ["Storage Control Mode", "storage_contol_mode", 0xE004, STOREDGE_CONTROL_MODE], ["Storage AC Charge Policy", "storage_ac_charge_policy", 0xE005, STOREDGE_AC_CHARGE_POLICY], diff --git a/custom_components/solaredge_modbus/number.py b/custom_components/solaredge_modbus/number.py index e7e1564..e478321 100644 --- a/custom_components/solaredge_modbus/number.py +++ b/custom_components/solaredge_modbus/number.py @@ -4,6 +4,7 @@ from .const import ( DOMAIN, ATTR_MANUFACTURER, + EXPORT_CONTROL_NUMBER_TYPES, STORAGE_NUMBER_TYPES, ) @@ -32,7 +33,23 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None: entities = [] - if hub.read_battery1 == True or hub.read_battery2 == True: + # If a meter is available add export control + if hub.has_meter: + for number_info in EXPORT_CONTROL_NUMBER_TYPES: + number = SolarEdgeNumber( + hub_name, + hub, + device_info, + number_info[0], + number_info[1], + number_info[2], + number_info[3], + number_info[4], + ) + entities.append(number) + + # If a battery is available add storage control + if hub.has_battery: for number_info in STORAGE_NUMBER_TYPES: number = SolarEdgeNumber( hub_name, diff --git a/custom_components/solaredge_modbus/select.py b/custom_components/solaredge_modbus/select.py index 42eab32..ec11602 100644 --- a/custom_components/solaredge_modbus/select.py +++ b/custom_components/solaredge_modbus/select.py @@ -4,6 +4,7 @@ from .const import ( DOMAIN, ATTR_MANUFACTURER, + EXPORT_CONTROL_SELECT_TYPES, STORAGE_SELECT_TYPES, ) @@ -29,7 +30,22 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None: entities = [] - if hub.read_battery1 == True or hub.read_battery2 == True: + # If a meter is available add export control + if hub.has_meter: + for select_info in EXPORT_CONTROL_SELECT_TYPES: + select = SolarEdgeSelect( + hub_name, + hub, + device_info, + select_info[0], + select_info[1], + select_info[2], + select_info[3], + ) + entities.append(select) + + # If a battery is available add storage control + if hub.has_battery: for select_info in STORAGE_SELECT_TYPES: select = SolarEdgeSelect( hub_name,