goodwe 0.3.3__tar.gz → 0.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {goodwe-0.3.3/goodwe.egg-info → goodwe-0.3.4}/PKG-INFO +1 -1
  2. goodwe-0.3.4/VERSION +1 -0
  3. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/es.py +12 -6
  4. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/et.py +23 -12
  5. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/inverter.py +3 -2
  6. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/sensor.py +12 -11
  7. {goodwe-0.3.3 → goodwe-0.3.4/goodwe.egg-info}/PKG-INFO +1 -1
  8. {goodwe-0.3.3 → goodwe-0.3.4}/tests/test_dt.py +3 -3
  9. {goodwe-0.3.3 → goodwe-0.3.4}/tests/test_et.py +15 -15
  10. {goodwe-0.3.3 → goodwe-0.3.4}/tests/test_sensor.py +2 -2
  11. goodwe-0.3.3/VERSION +0 -1
  12. {goodwe-0.3.3 → goodwe-0.3.4}/LICENSE +0 -0
  13. {goodwe-0.3.3 → goodwe-0.3.4}/README.md +0 -0
  14. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/__init__.py +0 -0
  15. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/const.py +0 -0
  16. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/dt.py +0 -0
  17. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/exceptions.py +0 -0
  18. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/modbus.py +0 -0
  19. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/model.py +0 -0
  20. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe/protocol.py +0 -0
  21. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe.egg-info/SOURCES.txt +0 -0
  22. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe.egg-info/dependency_links.txt +0 -0
  23. {goodwe-0.3.3 → goodwe-0.3.4}/goodwe.egg-info/top_level.txt +0 -0
  24. {goodwe-0.3.3 → goodwe-0.3.4}/pyproject.toml +0 -0
  25. {goodwe-0.3.3 → goodwe-0.3.4}/setup.cfg +0 -0
  26. {goodwe-0.3.3 → goodwe-0.3.4}/tests/test_es.py +0 -0
  27. {goodwe-0.3.3 → goodwe-0.3.4}/tests/test_modbus.py +0 -0
  28. {goodwe-0.3.3 → goodwe-0.3.4}/tests/test_protocol.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goodwe
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Read data from GoodWe inverter via local network
5
5
  Home-page: https://github.com/marcelblijleven/goodwe
6
6
  Author: Martin Letenay, Marcel Blijleven
goodwe-0.3.4/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.4
@@ -95,7 +95,7 @@ class ES(Inverter):
95
95
  Power("pback_up", 81, "Back-up Power", Kind.UPS),
96
96
  # pload + pback_up
97
97
  Calculated("plant_power",
98
- lambda data: round(read_bytes2(data, 47) + read_bytes2(data, 81)),
98
+ lambda data: round(read_bytes2(data, 47, 0) + read_bytes2(data, 81, 0)),
99
99
  "Plant Power", "W", Kind.AC),
100
100
  Decimal("meter_power_factor", 83, 1000, "Meter Power Factor", "", Kind.GRID), # modbus 0x531
101
101
  # Integer("xx85", 85, "Unknown sensor@85"),
@@ -135,7 +135,7 @@ class ES(Inverter):
135
135
  Integer("charge_i", 26, "Charge Current", "A", ),
136
136
  Integer("discharge_i", 28, "Discharge Current", "A", ),
137
137
  Decimal("discharge_v", 30, 10, "Discharge Voltage", "V"),
138
- Calculated("dod", lambda data: 100 - read_bytes2(data, 32), "Depth of Discharge", "%"),
138
+ Calculated("dod", lambda data: 100 - read_bytes2(data, 32, 0), "Depth of Discharge", "%"),
139
139
  Integer("battery_activated", 34, "Battery Activated"),
140
140
  Integer("bp_off_grid_charge", 36, "BP Off-grid Charge"),
141
141
  Integer("bp_pv_discharge", 38, "BP PV Discharge"),
@@ -285,19 +285,25 @@ class ES(Inverter):
285
285
  async def get_operation_modes(self, include_emulated: bool) -> Tuple[OperationMode, ...]:
286
286
  result = [e for e in OperationMode]
287
287
  result.remove(OperationMode.PEAK_SHAVING)
288
+ result.remove(OperationMode.SELF_USE)
288
289
  if not include_emulated:
289
290
  result.remove(OperationMode.ECO_CHARGE)
290
291
  result.remove(OperationMode.ECO_DISCHARGE)
291
292
  return tuple(result)
292
293
 
293
294
  async def get_operation_mode(self) -> OperationMode:
294
- mode = OperationMode(await self.read_setting('work_mode'))
295
+ mode_id = await self.read_setting('work_mode')
296
+ try:
297
+ mode = OperationMode(mode_id)
298
+ except ValueError:
299
+ logger.debug("Unknown work_mode value %d", mode_id)
300
+ return None
295
301
  if OperationMode.ECO != mode:
296
302
  return mode
297
- ecomode = await self.read_setting('eco_mode_1')
298
- if ecomode.is_eco_charge_mode():
303
+ eco_mode = await self.read_setting('eco_mode_1')
304
+ if eco_mode.is_eco_charge_mode():
299
305
  return OperationMode.ECO_CHARGE
300
- elif ecomode.is_eco_discharge_mode():
306
+ elif eco_mode.is_eco_discharge_mode():
301
307
  return OperationMode.ECO_DISCHARGE
302
308
  else:
303
309
  return OperationMode.ECO
@@ -35,10 +35,10 @@ class ET(Inverter):
35
35
  # ppv1 + ppv2 + ppv3 + ppv4
36
36
  Calculated("ppv",
37
37
  lambda data:
38
- max(0, read_bytes4(data, 35105)) +
39
- max(0, read_bytes4(data, 35109)) +
40
- max(0, read_bytes4(data, 35113)) +
41
- max(0, read_bytes4(data, 35117)),
38
+ max(0, read_bytes4(data, 35105, 0)) +
39
+ max(0, read_bytes4(data, 35109, 0)) +
40
+ max(0, read_bytes4(data, 35113, 0)) +
41
+ max(0, read_bytes4(data, 35117, 0)),
42
42
  "PV Power", "W", Kind.PV),
43
43
  ByteH("pv4_mode", 35119, "PV4 Mode code", "", Kind.PV),
44
44
  EnumH("pv4_mode_label", 35119, PV_MODES, "PV4 Mode", Kind.PV),
@@ -145,10 +145,10 @@ class ET(Inverter):
145
145
  # ppv1 + ppv2 + ppv3 + ppv4 + pbattery1 - active_power
146
146
  Calculated("house_consumption",
147
147
  lambda data:
148
- read_bytes4(data, 35105) +
149
- read_bytes4(data, 35109) +
150
- read_bytes4(data, 35113) +
151
- read_bytes4(data, 35117) +
148
+ read_bytes4(data, 35105, 0) +
149
+ read_bytes4(data, 35109, 0) +
150
+ read_bytes4(data, 35113, 0) +
151
+ read_bytes4(data, 35117, 0) +
152
152
  read_bytes4_signed(data, 35182) -
153
153
  read_bytes2_signed(data, 35140),
154
154
  "House Consumption", "W", Kind.AC),
@@ -604,19 +604,26 @@ class ET(Inverter):
604
604
  result = [e for e in OperationMode]
605
605
  if not self._has_peak_shaving:
606
606
  result.remove(OperationMode.PEAK_SHAVING)
607
+ if not is_745_platform(self):
608
+ result.remove(OperationMode.SELF_USE)
607
609
  if not include_emulated:
608
610
  result.remove(OperationMode.ECO_CHARGE)
609
611
  result.remove(OperationMode.ECO_DISCHARGE)
610
612
  return tuple(result)
611
613
 
612
614
  async def get_operation_mode(self) -> OperationMode:
613
- mode = OperationMode(await self.read_setting('work_mode'))
615
+ mode_id = await self.read_setting('work_mode')
616
+ try:
617
+ mode = OperationMode(mode_id)
618
+ except ValueError:
619
+ logger.debug("Unknown work_mode value %d", mode_id)
620
+ return None
614
621
  if OperationMode.ECO != mode:
615
622
  return mode
616
- ecomode = await self.read_setting('eco_mode_1')
617
- if ecomode.is_eco_charge_mode():
623
+ eco_mode = await self.read_setting('eco_mode_1')
624
+ if eco_mode.is_eco_charge_mode():
618
625
  return OperationMode.ECO_CHARGE
619
- elif ecomode.is_eco_discharge_mode():
626
+ elif eco_mode.is_eco_discharge_mode():
620
627
  return OperationMode.ECO_DISCHARGE
621
628
  else:
622
629
  return OperationMode.ECO
@@ -644,6 +651,10 @@ class ET(Inverter):
644
651
  await self.write_setting('work_mode', 4)
645
652
  await self._set_offline(False)
646
653
  await self._clear_battery_mode_param()
654
+ elif operation_mode == OperationMode.SELF_USE:
655
+ await self.write_setting('work_mode', 5)
656
+ await self._set_offline(False)
657
+ await self._clear_battery_mode_param()
647
658
  elif operation_mode in (OperationMode.ECO_CHARGE, OperationMode.ECO_DISCHARGE):
648
659
  if eco_mode_power < 0 or eco_mode_power > 100:
649
660
  raise ValueError()
@@ -76,8 +76,9 @@ class OperationMode(IntEnum):
76
76
  BACKUP = 2
77
77
  ECO = 3
78
78
  PEAK_SHAVING = 4
79
- ECO_CHARGE = 10
80
- ECO_DISCHARGE = 11
79
+ SELF_USE = 5
80
+ ECO_CHARGE = 98
81
+ ECO_DISCHARGE = 99
81
82
 
82
83
 
83
84
  class Inverter(ABC):
@@ -183,7 +183,7 @@ class Energy(Sensor):
183
183
 
184
184
  def read_value(self, data: ProtocolResponse):
185
185
  value = read_bytes2(data)
186
- return float(value) / 10
186
+ return float(value) / 10 if value else None
187
187
 
188
188
 
189
189
  class Energy4(Sensor):
@@ -194,7 +194,7 @@ class Energy4(Sensor):
194
194
 
195
195
  def read_value(self, data: ProtocolResponse):
196
196
  value = read_bytes4(data)
197
- return float(value) / 10
197
+ return float(value) / 10 if value else None
198
198
 
199
199
 
200
200
  class Apparent(Sensor):
@@ -308,7 +308,7 @@ class Integer(Sensor):
308
308
  super().__init__(id_, offset, name, 2, unit, kind)
309
309
 
310
310
  def read_value(self, data: ProtocolResponse):
311
- return read_bytes2(data)
311
+ return read_bytes2(data, None, 0)
312
312
 
313
313
  def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
314
314
  return int.to_bytes(int(value), length=2, byteorder="big", signed=False)
@@ -334,7 +334,7 @@ class Long(Sensor):
334
334
  super().__init__(id_, offset, name, 4, unit, kind)
335
335
 
336
336
  def read_value(self, data: ProtocolResponse):
337
- return read_bytes4(data)
337
+ return read_bytes4(data, None, 0)
338
338
 
339
339
  def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
340
340
  return int.to_bytes(int(value), length=4, byteorder="big", signed=False)
@@ -414,7 +414,7 @@ class EnumH(Sensor):
414
414
 
415
415
 
416
416
  class EnumL(Sensor):
417
- """Sensor representing label from enumeration encoded in 1 bytes (low 8 bits of 16bit register)"""
417
+ """Sensor representing label from enumeration encoded in 1 byte (low 8 bits of 16bit register)"""
418
418
 
419
419
  def __init__(self, id_: str, offset: int, labels: Dict, name: str, kind: Optional[SensorKind] = None):
420
420
  super().__init__(id_, offset, name, 1, "", kind)
@@ -433,7 +433,7 @@ class Enum2(Sensor):
433
433
  self._labels: Dict = labels
434
434
 
435
435
  def read_value(self, data: ProtocolResponse):
436
- return self._labels.get(read_bytes2(data))
436
+ return self._labels.get(read_bytes2(data, None, 0))
437
437
 
438
438
 
439
439
  class EnumBitmap4(Sensor):
@@ -464,7 +464,8 @@ class EnumBitmap22(Sensor):
464
464
  raise NotImplementedError()
465
465
 
466
466
  def read(self, data: ProtocolResponse):
467
- return decode_bitmap(read_bytes2(data, self.offset) << 16 + read_bytes2(data, self._offsetL), self._labels)
467
+ return decode_bitmap(read_bytes2(data, self.offset, 0) << 16 + read_bytes2(data, self._offsetL, 0),
468
+ self._labels)
468
469
 
469
470
 
470
471
  class EnumCalculated(Sensor):
@@ -785,12 +786,12 @@ def read_byte(buffer: ProtocolResponse, offset: int = None) -> int:
785
786
  return int.from_bytes(buffer.read(1), byteorder="big", signed=True)
786
787
 
787
788
 
788
- def read_bytes2(buffer: ProtocolResponse, offset: int = None) -> int:
789
+ def read_bytes2(buffer: ProtocolResponse, offset: int = None, undef: int = None) -> int:
789
790
  """Retrieve 2 byte (unsigned int) value from buffer"""
790
791
  if offset is not None:
791
792
  buffer.seek(offset)
792
793
  value = int.from_bytes(buffer.read(2), byteorder="big", signed=False)
793
- return value if value != 0xffff else 0
794
+ return undef if value == 0xffff else value
794
795
 
795
796
 
796
797
  def read_bytes2_signed(buffer: ProtocolResponse, offset: int = None) -> int:
@@ -800,12 +801,12 @@ def read_bytes2_signed(buffer: ProtocolResponse, offset: int = None) -> int:
800
801
  return int.from_bytes(buffer.read(2), byteorder="big", signed=True)
801
802
 
802
803
 
803
- def read_bytes4(buffer: ProtocolResponse, offset: int = None) -> int:
804
+ def read_bytes4(buffer: ProtocolResponse, offset: int = None, undef: int = None) -> int:
804
805
  """Retrieve 4 byte (unsigned int) value from buffer"""
805
806
  if offset is not None:
806
807
  buffer.seek(offset)
807
808
  value = int.from_bytes(buffer.read(4), byteorder="big", signed=False)
808
- return value if value != 0xffffffff else 0
809
+ return undef if value == 0xffffffff else value
809
810
 
810
811
 
811
812
  def read_bytes4_signed(buffer: ProtocolResponse, offset: int = None) -> int:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goodwe
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Read data from GoodWe inverter via local network
5
5
  Home-page: https://github.com/marcelblijleven/goodwe
6
6
  Author: Martin Letenay, Marcel Blijleven
@@ -167,8 +167,8 @@ class GW8K_DT_Test(DtMock):
167
167
  self.assertSensor("apparent_power", 0, "VA", data),
168
168
  self.assertSensor("reactive_power", 0, "var", data),
169
169
  self.assertSensor('temperature', 45.3, 'C', data)
170
- self.assertSensor('e_day', 0.0, 'kWh', data)
171
- self.assertSensor('e_total', 0.0, 'kWh', data)
170
+ self.assertSensor('e_day', None, 'kWh', data)
171
+ self.assertSensor('e_total', None, 'kWh', data)
172
172
  self.assertSensor('h_total', 0, 'h', data)
173
173
  self.assertSensor('safety_country', 32, '', data)
174
174
  self.assertSensor('safety_country_label', '50Hz 230Vac Default', '', data)
@@ -221,7 +221,7 @@ class GW5000D_NS_Test(DtMock):
221
221
  self.assertSensor("apparent_power", -1, "VA", data),
222
222
  self.assertSensor("reactive_power", -1, "var", data),
223
223
  self.assertSensor('temperature', 1.4, 'C', data)
224
- self.assertSensor('e_day', 0.0, 'kWh', data)
224
+ self.assertSensor('e_day', None, 'kWh', data)
225
225
  self.assertSensor('e_total', 881.7, 'kWh', data)
226
226
  self.assertSensor('h_total', 955, 'h', data)
227
227
  self.assertSensor('safety_country', 73, '', data)
@@ -164,7 +164,7 @@ class GW10K_ET_Test(EtMock):
164
164
  self.assertSensor('h_total', 9246, 'h', data)
165
165
  self.assertSensor("e_day_exp", 9.8, 'kWh', data)
166
166
  self.assertSensor("e_total_imp", 58.0, 'kWh', data)
167
- self.assertSensor("e_day_imp", 0.0, 'kWh', data)
167
+ self.assertSensor("e_day_imp", None, 'kWh', data)
168
168
  self.assertSensor("e_load_total", 8820.2, 'kWh', data)
169
169
  self.assertSensor("e_load_day", 11.6, 'kWh', data)
170
170
  self.assertSensor("e_bat_charge_total", 2758.1, 'kWh', data)
@@ -457,14 +457,14 @@ class GW6000_EH_Test(EtMock):
457
457
  self.assertSensor("e_total_exp", 58.6, 'kWh', data)
458
458
  self.assertSensor('h_total', 33, 'h', data)
459
459
  self.assertSensor("e_day_exp", 21.6, 'kWh', data)
460
- self.assertSensor("e_total_imp", 0.0, 'kWh', data)
461
- self.assertSensor("e_day_imp", 0.0, 'kWh', data)
460
+ self.assertSensor("e_total_imp", None, 'kWh', data)
461
+ self.assertSensor("e_day_imp", None, 'kWh', data)
462
462
  self.assertSensor("e_load_total", 70.1, 'kWh', data)
463
463
  self.assertSensor("e_load_day", 27.1, 'kWh', data)
464
- self.assertSensor("e_bat_charge_total", 0.0, 'kWh', data)
465
- self.assertSensor("e_bat_charge_day", 0.0, 'kWh', data)
466
- self.assertSensor("e_bat_discharge_total", 0.0, 'kWh', data)
467
- self.assertSensor("e_bat_discharge_day", 0.0, 'kWh', data)
464
+ self.assertSensor("e_bat_charge_total", None, 'kWh', data)
465
+ self.assertSensor("e_bat_charge_day", None, 'kWh', data)
466
+ self.assertSensor("e_bat_discharge_total", None, 'kWh', data)
467
+ self.assertSensor("e_bat_discharge_day", None, 'kWh', data)
468
468
  self.assertSensor('diagnose_result', 117983303, '', data)
469
469
  self.assertSensor('diagnose_result_label',
470
470
  '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',
@@ -558,8 +558,8 @@ class GEH10_1U_10_Test(EtMock):
558
558
  self.assertSensor('e_total_exp', 10273.3, 'kWh', data)
559
559
  self.assertSensor('h_total', 3256, 'h', data)
560
560
  self.assertSensor('e_day_exp', 16.6, 'kWh', data)
561
- self.assertSensor('e_total_imp', 0.0, 'kWh', data)
562
- self.assertSensor('e_day_imp', 0.0, 'kWh', data)
561
+ self.assertSensor('e_total_imp', None, 'kWh', data)
562
+ self.assertSensor('e_day_imp', None, 'kWh', data)
563
563
  self.assertSensor('e_load_total', 4393.9, 'kWh', data)
564
564
  self.assertSensor('e_load_day', 10.7, 'kWh', data)
565
565
  self.assertSensor('e_bat_charge_total', 141.9, 'kWh', data)
@@ -771,7 +771,7 @@ class GW25K_ET_Test(EtMock):
771
771
  self.assertSensor('e_bat_charge_total', 91.3, 'kWh', data)
772
772
  self.assertSensor('e_bat_charge_day', 11.0, 'kWh', data)
773
773
  self.assertSensor('e_bat_discharge_total', 69.6, 'kWh', data)
774
- self.assertSensor('e_bat_discharge_day', 0.0, 'kWh', data)
774
+ self.assertSensor('e_bat_discharge_day', None, 'kWh', data)
775
775
  self.assertSensor('diagnose_result', 33816960, '', data)
776
776
  self.assertSensor('diagnose_result_label',
777
777
  'BMS: Discharge current low, APP: Discharge current too low, BMS: Charge disabled, PF value set',
@@ -1042,13 +1042,13 @@ class GW29K9_ET_Test(EtMock):
1042
1042
  self.assertSensor('h_total', 1175, 'h', data)
1043
1043
  self.assertSensor('e_day_exp', 1.2, 'kWh', data)
1044
1044
  self.assertSensor('e_total_imp', 8.7, 'kWh', data)
1045
- self.assertSensor('e_day_imp', 0.0, 'kWh', data)
1045
+ self.assertSensor('e_day_imp', None, 'kWh', data)
1046
1046
  self.assertSensor('e_load_total', 10742.2, 'kWh', data)
1047
1047
  self.assertSensor('e_load_day', 43.8, 'kWh', data)
1048
- self.assertSensor('e_bat_charge_total', 0.0, 'kWh', data)
1049
- self.assertSensor('e_bat_charge_day', 0.0, 'kWh', data)
1050
- self.assertSensor('e_bat_discharge_total', 0.0, 'kWh', data)
1051
- self.assertSensor('e_bat_discharge_day', 0.0, 'kWh', data)
1048
+ self.assertSensor('e_bat_charge_total', None, 'kWh', data)
1049
+ self.assertSensor('e_bat_charge_day', None, 'kWh', data)
1050
+ self.assertSensor('e_bat_discharge_total', None, 'kWh', data)
1051
+ self.assertSensor('e_bat_discharge_day', None, 'kWh', data)
1052
1052
  self.assertSensor('diagnose_result', 33816782, '', data)
1053
1053
  self.assertSensor('diagnose_result_label',
1054
1054
  'Battery SOC low, Battery SOC in back, BMS: Discharge disabled, '
@@ -130,7 +130,7 @@ class TestUtils(TestCase):
130
130
  self.assertEqual(4294967293, testee.read(data))
131
131
 
132
132
  data = MockResponse("ffffffff")
133
- self.assertEqual(0, testee.read(data))
133
+ self.assertIsNone(testee.read(data))
134
134
 
135
135
  def test_power4_signed(self):
136
136
  testee = Power4S("", 0, "", None)
@@ -153,7 +153,7 @@ class TestUtils(TestCase):
153
153
  data = MockResponse("00020972")
154
154
  self.assertEqual(13349.0, testee.read(data))
155
155
  data = MockResponse("ffffffff")
156
- self.assertEqual(0.0, testee.read(data))
156
+ self.assertIsNone(testee.read(data))
157
157
 
158
158
  def test_timestamp(self):
159
159
  testee = Timestamp("", 0, "", None)
goodwe-0.3.3/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.3.3
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes