diff --git a/custom_components/solaredge_modbus/__init__.py b/custom_components/solaredge_modbus/__init__.py index ceb9d12..f1c6788 100644 --- a/custom_components/solaredge_modbus/__init__.py +++ b/custom_components/solaredge_modbus/__init__.py @@ -31,6 +31,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 @@ -191,6 +193,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: @@ -652,15 +664,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,