diff --git a/goodwe/et.py b/goodwe/et.py index b311e8e..2088968 100644 --- a/goodwe/et.py +++ b/goodwe/et.py @@ -255,6 +255,17 @@ class ET(Inverter): Apparent4("meter_apparent_power_total", 82, "Meter Apparent Power Total", Kind.GRID), # 36041/42 Integer("meter_type", 86, "Meter Type", "", Kind.GRID), # 36043 (0: Single phase, 1: 3P3W, 2: 3P4W, 3: HomeKit) Integer("meter_sw_version", 88, "Meter Software Version", "", Kind.GRID), # 36044 + # Sensors added in some ARM fw update + Power4("meter2_active_power", 90, "Meter 2 Active Power", Kind.GRID), # 36045/46 + Float("meter2_e_total_exp", 94, 1000, "Meter 2 Total Energy (export)", "kWh", Kind.GRID), # 36047/48 + Float("meter2_e_total_imp", 98, 1000, "Meter 2 Total Energy (import)", "kWh", Kind.GRID), # 36049/50 + Integer("meter2_comm_status", 102, "Meter 2 Communication Status"), # 36051 + Voltage("meter_voltage1", 104, "Meter L1 Voltage", Kind.GRID), # 36052 + Voltage("meter_voltage2", 106, "Meter L2 Voltage", Kind.GRID), # 36053 + Voltage("meter_voltage3", 108, "Meter L3 Voltage", Kind.GRID), # 36054 + Current("meter_current1", 110, "Meter L1 Current", Kind.GRID), # 36055 + Current("meter_current2", 112, "Meter L2 Current", Kind.GRID), # 36056 + Current("meter_current3", 114, "Meter L3 Current", Kind.GRID), # 36057 ) # Inverter's MPPT data @@ -399,11 +410,13 @@ def __init__(self, host: str, comm_addr: int = 0, timeout: int = 1, retries: int self._READ_DEVICE_VERSION_INFO: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x88b8, 0x0021) self._READ_RUNNING_DATA: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x891c, 0x007d) self._READ_METER_DATA: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x8ca0, 0x2d) + self._READ_METER_DATA_EXTENDED: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x8ca0, 0x3a) self._READ_BATTERY_INFO: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x9088, 0x0018) self._READ_BATTERY2_INFO: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x9858, 0x0016) - self._READ_MPTT_DATA: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x89a5, 0x3d) + self._READ_MPTT_DATA: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x89e5, 0x3d) self._has_battery: bool = True self._has_battery2: bool = False + self._has_meter_extended: bool = False self._has_mptt: bool = False self._sensors = self.__all_sensors self._sensors_battery = self.__all_sensors_battery @@ -423,6 +436,11 @@ def _single_phase_only(s: Sensor) -> bool: """Filter to exclude phase2/3 sensors on single phase inverters""" return not ((s.id_.endswith('2') or s.id_.endswith('3')) and 'pv' not in s.id_) + @staticmethod + def _not_extended_meter(s: Sensor) -> bool: + """Filter to exclude extended meter sensors""" + return s.offset < 90 + async def read_device_info(self): response = await self._read_from_socket(self._READ_DEVICE_VERSION_INFO) response = response[5:-2] @@ -457,6 +475,9 @@ async def read_device_info(self): if self.rated_power >= 15000: self._has_mptt = True + self._has_meter_extended = True + else: + self._sensors_meter = tuple(filter(self._not_extended_meter, self._sensors_meter)) if self.arm_version >= 19 or self.rated_power >= 15000: self._settings.update({s.id_: s for s in self.__settings_arm_fw_19}) @@ -476,6 +497,8 @@ async def read_runtime_data(self, include_unknown_sensors: bool = False) -> Dict if ex.message == 'ILLEGAL DATA ADDRESS': logger.warning("Cannot read battery values, disabling further attempts.") self._has_battery = False + else: + raise ex if self._has_battery2: try: raw_data = await self._read_from_socket(self._READ_BATTERY2_INFO) @@ -484,9 +507,25 @@ async def read_runtime_data(self, include_unknown_sensors: bool = False) -> Dict if ex.message == 'ILLEGAL DATA ADDRESS': logger.warning("Cannot read battery 2 values, disabling further attempts.") self._has_battery2 = False + else: + raise ex - raw_data = await self._read_from_socket(self._READ_METER_DATA) - data.update(self._map_response(raw_data[5:-2], self._sensors_meter, include_unknown_sensors)) + if self._has_meter_extended: + try: + raw_data = await self._read_from_socket(self._READ_METER_DATA_EXTENDED) + data.update(self._map_response(raw_data[5:-2], self._sensors_meter, include_unknown_sensors)) + except RequestRejectedException as ex: + if ex.message == 'ILLEGAL DATA ADDRESS': + logger.warning("Cannot read extended meter values, disabling further attempts.") + self._has_meter_extended = False + self._sensors_meter = tuple(filter(self._not_extended_meter, self._sensors_meter)) + raw_data = await self._read_from_socket(self._READ_METER_DATA) + data.update(self._map_response(raw_data[5:-2], self._sensors_meter, include_unknown_sensors)) + else: + raise ex + else: + raw_data = await self._read_from_socket(self._READ_METER_DATA) + data.update(self._map_response(raw_data[5:-2], self._sensors_meter, include_unknown_sensors)) if self._has_mptt: try: @@ -496,6 +535,8 @@ async def read_runtime_data(self, include_unknown_sensors: bool = False) -> Dict if ex.message == 'ILLEGAL DATA ADDRESS': logger.warning("Cannot read MPPT values, disabling further attempts.") self._has_mptt = False + else: + raise ex return data diff --git a/tests/sample/et/GW25K-ET_meter_data.hex b/tests/sample/et/GW25K-ET_meter_data.hex index 8dc8265..3bca628 100644 --- a/tests/sample/et/GW25K-ET_meter_data.hex +++ b/tests/sample/et/GW25K-ET_meter_data.hex @@ -1 +1 @@ -aa55f7035a00020064000a01110001021c021701b605eaff806c1071ec4c0863dc138400000000000000000000021c00000217000001b6000005eaffffffc8ffffffcbffffffeeffffff800000024300000234000002010000067900020005059b \ No newline at end of file +aa55f7037400020064000a01110001ff30ff5aff8efe1704ad5eac7e5091d879a013870000000000000000ffffff30ffffff5affffff8efffffe17000001a5000001b80000014f000004adfffffe01fffffe03fffffe40fffffa4200020005000000000000000000000000000008f208f808f00016001600130540 \ No newline at end of file diff --git a/tests/test_et.py b/tests/test_et.py index 44ed226..dbda968 100644 --- a/tests/test_et.py +++ b/tests/test_et.py @@ -888,7 +888,7 @@ def __init__(self, methodName='runTest'): EtMock.__init__(self, methodName) self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW25K-ET_device_info.hex') self.mock_response(self._READ_RUNNING_DATA, 'GW25K-ET_running_data.hex') - self.mock_response(self._READ_METER_DATA, 'GW25K-ET_meter_data.hex') + self.mock_response(self._READ_METER_DATA_EXTENDED, 'GW25K-ET_meter_data.hex') self.mock_response(self._READ_BATTERY_INFO, 'GW25K-ET_battery_info.hex') # self.mock_response(self._READ_BATTERY_INFO2, 'GW25K-ET_battery2_info.hex') self.mock_response(self._READ_MPTT_DATA, 'GW25K-ET_mptt_data.hex') @@ -914,7 +914,7 @@ def test_GW25K_ET_runtime_data(self): self.sensor_map = {s.id_: s.unit for s in self.sensors()} data = self.loop.run_until_complete(self.read_runtime_data(True)) - self.assertEqual(221, len(data)) + self.assertEqual(231, len(data)) self.assertSensor('timestamp', datetime.strptime('2023-12-03 14:07:07', '%Y-%m-%d %H:%M:%S'), '', data) self.assertSensor('vpv1', 737.9, 'V', data) @@ -1066,32 +1066,42 @@ def test_GW25K_ET_runtime_data(self): self.assertSensor('manufacture_code', 10, '', data) self.assertSensor('meter_test_status', 273, '', data) self.assertSensor('meter_comm_status', 1, '', data) - self.assertSensor('active_power1', 540, 'W', data) - self.assertSensor('active_power2', 535, 'W', data) - self.assertSensor('active_power3', 438, 'W', data) - self.assertSensor('active_power_total', 1514, 'W', data) - self.assertSensor('reactive_power_total', -128, 'var', data) - self.assertSensor('meter_power_factor1', 27.664, '', data) - self.assertSensor('meter_power_factor2', 29.164, '', data) - self.assertSensor('meter_power_factor3', 19.464, '', data) - self.assertSensor('meter_power_factor', 25.564, '', data) - self.assertSensor('meter_freq', 49.96, 'Hz', data) + self.assertSensor('active_power1', -208, 'W', data) + self.assertSensor('active_power2', -166, 'W', data) + self.assertSensor('active_power3', -114, 'W', data) + self.assertSensor('active_power_total', -489, 'W', data) + self.assertSensor('reactive_power_total', 1197, 'var', data) + self.assertSensor('meter_power_factor1', 24.236, '', data) + self.assertSensor('meter_power_factor2', 32.336, '', data) + self.assertSensor('meter_power_factor3', -28.2, '', data) + self.assertSensor('meter_power_factor', 31.136, '', data) + self.assertSensor('meter_freq', 49.99, 'Hz', data) self.assertSensor('meter_e_total_exp', 0.0, 'kWh', data) self.assertSensor('meter_e_total_imp', 0.0, 'kWh', data) - self.assertSensor('meter_active_power1', 540, 'W', data) - self.assertSensor('meter_active_power2', 535, 'W', data) - self.assertSensor('meter_active_power3', 438, 'W', data) - self.assertSensor('meter_active_power_total', 1514, 'W', data) - self.assertSensor('meter_reactive_power1', -56, 'var', data) - self.assertSensor('meter_reactive_power2', -53, 'var', data) - self.assertSensor('meter_reactive_power3', -18, 'var', data) - self.assertSensor('meter_reactive_power_total', -128, 'var', data) - self.assertSensor('meter_apparent_power1', 579, 'VA', data) - self.assertSensor('meter_apparent_power2', 564, 'VA', data) - self.assertSensor('meter_apparent_power3', 513, 'VA', data) - self.assertSensor('meter_apparent_power_total', 1657, 'VA', data) + self.assertSensor('meter_active_power1', -208, 'W', data) + self.assertSensor('meter_active_power2', -166, 'W', data) + self.assertSensor('meter_active_power3', -114, 'W', data) + self.assertSensor('meter_active_power_total', -489, 'W', data) + self.assertSensor('meter_reactive_power1', 421, 'var', data) + self.assertSensor('meter_reactive_power2', 440, 'var', data) + self.assertSensor('meter_reactive_power3', 335, 'var', data) + self.assertSensor('meter_reactive_power_total', 1197, 'var', data) + self.assertSensor('meter_apparent_power1', -511, 'VA', data) + self.assertSensor('meter_apparent_power2', -509, 'VA', data) + self.assertSensor('meter_apparent_power3', -448, 'VA', data) + self.assertSensor('meter_apparent_power_total', -1470, 'VA', data) self.assertSensor('meter_type', 2, '', data) self.assertSensor('meter_sw_version', 5, '', data) + self.assertSensor('meter2_active_power', 0, 'W', data) + self.assertSensor('meter2_e_total_exp', 0.0, 'kWh', data) + self.assertSensor('meter2_e_total_imp', 0.0, 'kWh', data) + self.assertSensor('meter2_comm_status', 0, '', data) + self.assertSensor('meter_voltage1', 229.0, 'V', data) + self.assertSensor('meter_voltage2', 229.6, 'V', data) + self.assertSensor('meter_voltage3', 228.8, 'V', data) + self.assertSensor('meter_current1', 2.2, 'A', data) + self.assertSensor('meter_current2', 2.2, 'A', data) + self.assertSensor('meter_current3', 1.9, 'A', data) self.assertSensor('ppv_total', 529, 'W', data) self.assertSensor('vpv5', 0.0, 'V', data) self.assertSensor('ipv5', 0.0, 'A', data)