Skip to content

Commit

Permalink
Add support for extended meter sensors on ETT inverters
Browse files Browse the repository at this point in the history
Add support for extended meter sensors on  ETT inverters
  • Loading branch information
mletenay committed Dec 10, 2023
1 parent 6a12808 commit e5f4f69
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 28 deletions.
47 changes: 44 additions & 3 deletions goodwe/et.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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})
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/sample/et/GW25K-ET_meter_data.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
aa55f7035a00020064000a01110001021c021701b605eaff806c1071ec4c0863dc138400000000000000000000021c00000217000001b6000005eaffffffc8ffffffcbffffffeeffffff800000024300000234000002010000067900020005059b
aa55f7037400020064000a01110001ff30ff5aff8efe1704ad5eac7e5091d879a013870000000000000000ffffff30ffffff5affffff8efffffe17000001a5000001b80000014f000004adfffffe01fffffe03fffffe40fffffa4200020005000000000000000000000000000008f208f808f00016001600130540
58 changes: 34 additions & 24 deletions tests/test_et.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit e5f4f69

Please sign in to comment.