Skip to content

Commit

Permalink
Fix decoding of power4 values
Browse files Browse the repository at this point in the history
Power sensors are encoded as both signed and unsigned values.
Former Power4 sensor is now U32, new Power4S is for S32 values.
  • Loading branch information
mletenay committed Apr 8, 2024
1 parent 895500a commit 71c79a9
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 15 deletions.
14 changes: 7 additions & 7 deletions goodwe/et.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class ET(Inverter):
Voltage("nbus_voltage", 35179, "NBus Voltage", None),
Voltage("vbattery1", 35180, "Battery Voltage", Kind.BAT),
CurrentS("ibattery1", 35181, "Battery Current", Kind.BAT),
Power4("pbattery1", 35182, "Battery Power", Kind.BAT),
Power4S("pbattery1", 35182, "Battery Power", Kind.BAT),
Integer("battery_mode", 35184, "Battery Mode code", "", Kind.BAT),
Enum2("battery_mode_label", 35184, BATTERY_MODES, "Battery Mode", Kind.BAT),
Integer("warning_code", 35185, "Warning code"),
Expand Down Expand Up @@ -149,7 +149,7 @@ class ET(Inverter):
read_bytes4(data, 35109) +
read_bytes4(data, 35113) +
read_bytes4(data, 35117) +
read_bytes4(data, 35182) -
read_bytes4_signed(data, 35182) -
read_bytes2(data, 35140),
"House Consumption", "W", Kind.AC),
)
Expand Down Expand Up @@ -238,10 +238,10 @@ class ET(Inverter):
Frequency("meter_freq", 36014, "Meter Frequency", Kind.GRID),
Float("meter_e_total_exp", 36015, 1000, "Meter Total Energy (export)", "kWh", Kind.GRID),
Float("meter_e_total_imp", 36017, 1000, "Meter Total Energy (import)", "kWh", Kind.GRID),
Power4("meter_active_power1", 36019, "Meter Active Power L1", Kind.GRID),
Power4("meter_active_power2", 36021, "Meter Active Power L2", Kind.GRID),
Power4("meter_active_power3", 36023, "Meter Active Power L3", Kind.GRID),
Power4("meter_active_power_total", 36025, "Meter Active Power Total", Kind.GRID),
Power4S("meter_active_power1", 36019, "Meter Active Power L1", Kind.GRID),
Power4S("meter_active_power2", 36021, "Meter Active Power L2", Kind.GRID),
Power4S("meter_active_power3", 36023, "Meter Active Power L3", Kind.GRID),
Power4S("meter_active_power_total", 36025, "Meter Active Power Total", Kind.GRID),
Reactive4("meter_reactive_power1", 36027, "Meter Reactive Power L1", Kind.GRID),
Reactive4("meter_reactive_power2", 36029, "Meter Reactive Power L2", Kind.GRID),
Reactive4("meter_reactive_power3", 36031, "Meter Reactive Power L2", Kind.GRID),
Expand All @@ -253,7 +253,7 @@ class ET(Inverter):
Integer("meter_type", 36043, "Meter Type", "", Kind.GRID), # (0: Single phase, 1: 3P3W, 2: 3P4W, 3: HomeKit)
Integer("meter_sw_version", 36044, "Meter Software Version", "", Kind.GRID),
# Sensors added in some ARM fw update, read when flag _has_meter_extended is on
Power4("meter2_active_power", 36045, "Meter 2 Active Power", Kind.GRID),
Power4S("meter2_active_power", 36045, "Meter 2 Active Power", Kind.GRID),
Float("meter2_e_total_exp", 36047, 1000, "Meter 2 Total Energy (export)", "kWh", Kind.GRID),
Float("meter2_e_total_imp", 36049, 1000, "Meter 2 Total Energy (import)", "kWh", Kind.GRID),
Integer("meter2_comm_status", 36051, "Meter 2 Communication Status"),
Expand Down
32 changes: 25 additions & 7 deletions goodwe/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def read_value(self, data: ProtocolResponse):


class Power4(Sensor):
"""Sensor representing power [W] value encoded in 4 bytes"""
"""Sensor representing power [W] value encoded in 4 (unsigned) bytes"""

def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
super().__init__(id_, offset, name, 4, "W", kind)
Expand All @@ -151,6 +151,16 @@ def read_value(self, data: ProtocolResponse):
return read_bytes4(data)


class Power4S(Sensor):
"""Sensor representing power [W] value encoded in 4 (signed) bytes"""

def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
super().__init__(id_, offset, name, 4, "W", kind)

def read_value(self, data: ProtocolResponse):
return read_bytes4_signed(data)


class Energy(Sensor):
"""Sensor representing energy [kWh] value encoded in 2 bytes"""

Expand All @@ -172,7 +182,7 @@ def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind])
super().__init__(id_, offset, name, 4, "kWh", kind)

def read_value(self, data: ProtocolResponse):
value = read_bytes4(data)
value = read_bytes4_signed(data)
if value == -1:
return None
else:
Expand All @@ -196,7 +206,7 @@ def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind])
super().__init__(id_, offset, name, 2, "VA", kind)

def read_value(self, data: ProtocolResponse):
return read_bytes4(data)
return read_bytes4_signed(data)


class Reactive(Sensor):
Expand All @@ -216,7 +226,7 @@ def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind])
super().__init__(id_, offset, name, 2, "var", kind)

def read_value(self, data: ProtocolResponse):
return read_bytes4(data)
return read_bytes4_signed(data)


class Temp(Sensor):
Expand Down Expand Up @@ -303,7 +313,7 @@ def __init__(self, id_: str, offset: int, name: str, unit: str = "", kind: Optio
super().__init__(id_, offset, name, 4, unit, kind)

def read_value(self, data: ProtocolResponse):
return read_bytes4(data)
return read_bytes4_signed(data)

def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
return int.to_bytes(int(value), length=4, byteorder="big", signed=True)
Expand Down Expand Up @@ -403,7 +413,7 @@ def read_value(self, data: ProtocolResponse) -> Any:
raise NotImplementedError()

def read(self, data: ProtocolResponse):
bits = read_bytes4(data, self.offset)
bits = read_bytes4_signed(data, self.offset)
return decode_bitmap(bits if bits != -1 else 0, self._labels)


Expand Down Expand Up @@ -724,6 +734,14 @@ def read_bytes2(buffer: ProtocolResponse, offset: int = None) -> int:


def read_bytes4(buffer: ProtocolResponse, offset: int = None) -> int:
"""Retrieve 4 byte (unsigned int) value from buffer"""
if offset is not None:
buffer.seek(offset)
value = int.from_bytes(buffer.read(4), byteorder="big", signed=False)
return value if value != 0xffffffff else 0


def read_bytes4_signed(buffer: ProtocolResponse, offset: int = None) -> int:
"""Retrieve 4 byte (signed int) value from buffer"""
if offset is not None:
buffer.seek(offset)
Expand Down Expand Up @@ -766,7 +784,7 @@ def read_current(buffer: ProtocolResponse, offset: int = None) -> float:
if offset is not None:
buffer.seek(offset)
value = int.from_bytes(buffer.read(2), byteorder="big", signed=False)
return float(value) / 10
return float(value) / 10 if value != 0xffff else 0


def read_current_signed(buffer: ProtocolResponse, offset: int = None) -> float:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_et.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ def test_GW6000_EH_runtime_data(self):
self.assertSensor('diagnose_result_label',
'Battery voltage low, Battery SOC low, Battery SOC in back, Discharge Driver On, Self-use load light, Battery Disconnected, Self-use off, Export power limit set, PF value set, Real power limit set',
'', data)
self.assertSensor('house_consumption', 1710, 'W', data)
self.assertSensor('house_consumption', 1712, 'W', data)


class GEH10_1U_10_Test(EtMock):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ def test_current(self):
self.assertEqual(6543.8, testee.read(data))
self.assertEqual("ff9e", testee.encode_value(6543.8).hex())

data = MockResponse("ffff")
self.assertEqual(0, testee.read(data))

def test_current_signed(self):
testee = CurrentS("", 0, "", None)

Expand All @@ -112,6 +115,18 @@ def test_power4(self):
data = MockResponse("0000069f")
self.assertEqual(1695, testee.read(data))

data = MockResponse("fffffffd")
self.assertEqual(4294967293, testee.read(data))

data = MockResponse("ffffffff")
self.assertEqual(0, testee.read(data))

def test_power4_signed(self):
testee = Power4S("", 0, "", None)

data = MockResponse("0000069f")
self.assertEqual(1695, testee.read(data))

data = MockResponse("fffffffd")
self.assertEqual(-3, testee.read(data))

Expand Down

0 comments on commit 71c79a9

Please sign in to comment.