From 07814b4d18df7901702080a90136c11bca41ba50 Mon Sep 17 00:00:00 2001 From: Colin Haven <10933733+virtitnerd@users.noreply.github.com> Date: Sat, 23 May 2026 12:17:47 -0400 Subject: [PATCH 1/2] Move I_Status and I_Status_Vendor from int16 to uint16 register decode SunSpec Implementation Technical Note v3.2 (June 2025) defines: 40107 I_Status uint16 Operating State 40108 I_Status_Vendor uint16 Vendor-defined operating state/error codes Both registers were being decoded as int16, which is incorrect per spec. The not-implemented sentinel for uint16 is 0xFFFF (SunSpecNotImpl.UINT16), not 0x8000 (SunSpecNotImpl.INT16) used for signed registers. For I_Status values 1-8 the decoded value is identical under either type. For I_Status_Vendor the distinction matters: vendor-defined error codes are unsigned and a code of 0x8000 would be misread as not-implemented if decoded as int16. The spec notes I_Status_Vendor applies to firmware <= 3.19.xx; I_Status_Vendor4 (uint32) applies to firmware >= 3.20.xx. Verified against: SolarEdge SunSpec Implementation Technical Note v3.2 Table: Inverter Model MODBUS Register Map, addresses 40107-40108 --- custom_components/solaredge_modbus_multi/hub.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/custom_components/solaredge_modbus_multi/hub.py b/custom_components/solaredge_modbus_multi/hub.py index 5fe29a8e..8e4d6a72 100644 --- a/custom_components/solaredge_modbus_multi/hub.py +++ b/custom_components/solaredge_modbus_multi/hub.py @@ -1094,6 +1094,8 @@ async def read_modbus_data(self) -> None: "AC_Energy_WH_SF", "I_DC_Current", "I_DC_Voltage", + "I_Status", + "I_Status_Vendor", ] uint16_data = ( inverter_data.registers[0:6] @@ -1101,6 +1103,7 @@ async def read_modbus_data(self) -> None: + [inverter_data.registers[16]] + inverter_data.registers[26:28] + [inverter_data.registers[29]] + + inverter_data.registers[38:40] ) self.decoded_model = dict( zip( @@ -1134,15 +1137,13 @@ async def read_modbus_data(self) -> None: "I_Temp_Trns", "I_Temp_Other", "I_Temp_SF", - "I_Status", - "I_Status_Vendor", ] int16_data = ( [inverter_data.registers[6]] + inverter_data.registers[13:16] + inverter_data.registers[17:24] + [inverter_data.registers[28]] - + inverter_data.registers[30:40] + + inverter_data.registers[30:38] ) self.decoded_model.update( From f219c47744b4965dc948abb6fc37bed95e6d7760 Mon Sep 17 00:00:00 2001 From: Colin Haven <10933733+virtitnerd@users.noreply.github.com> Date: Sat, 23 May 2026 12:17:58 -0400 Subject: [PATCH 2/2] Update not-implemented sentinels to UINT16 for I_Status and I_Status_Vendor Following the register type correction in the previous commit, the not-implemented checks that guard I_Status and I_Status_Vendor must use SunSpecNotImpl.UINT16 (0xFFFF) instead of SunSpecNotImpl.INT16 (0x8000). Without this change, after decoding I_Status_Vendor as uint16, the guard in native_value would never match on 0xFFFF, allowing an unrecognised value to fall through to the DEVICE_STATUS dict lookup and raise a KeyError. Also updates VENDOR_STATUS in const.py: the not-implemented sentinel key changes from SunSpecNotImpl.INT16 to SunSpecNotImpl.UINT16 to match. --- custom_components/solaredge_modbus_multi/const.py | 2 +- custom_components/solaredge_modbus_multi/sensor.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/solaredge_modbus_multi/const.py b/custom_components/solaredge_modbus_multi/const.py index 6ad5cc30..13f9388c 100644 --- a/custom_components/solaredge_modbus_multi/const.py +++ b/custom_components/solaredge_modbus_multi/const.py @@ -234,7 +234,7 @@ class SunSpecNotImpl(IntEnum): } VENDOR_STATUS = { - SunSpecNotImpl.INT16: None, + SunSpecNotImpl.UINT16: None, 0: "No Error", 17: "Temperature Too High", 25: "Isolation Faults", diff --git a/custom_components/solaredge_modbus_multi/sensor.py b/custom_components/solaredge_modbus_multi/sensor.py index d74a067e..722a979f 100644 --- a/custom_components/solaredge_modbus_multi/sensor.py +++ b/custom_components/solaredge_modbus_multi/sensor.py @@ -1328,7 +1328,7 @@ class SolarEdgeInverterStatus(SolarEdgeStatusSensor): @property def native_value(self): try: - if self._platform.decoded_model["I_Status"] == SunSpecNotImpl.INT16: + if self._platform.decoded_model["I_Status"] == SunSpecNotImpl.UINT16: return None return str(DEVICE_STATUS[self._platform.decoded_model["I_Status"]]) @@ -1410,7 +1410,7 @@ def entity_registry_enabled_default(self) -> bool: @property def native_value(self): try: - if self._platform.decoded_model["I_Status_Vendor"] == SunSpecNotImpl.INT16: + if self._platform.decoded_model["I_Status_Vendor"] == SunSpecNotImpl.UINT16: return None else: