goodwe 0.4.4__tar.gz → 0.4.6__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.4.4/goodwe.egg-info → goodwe-0.4.6}/PKG-INFO +1 -1
  2. goodwe-0.4.6/VERSION +1 -0
  3. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/dt.py +5 -0
  4. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/es.py +9 -6
  5. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/et.py +5 -2
  6. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/protocol.py +43 -10
  7. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/sensor.py +4 -4
  8. {goodwe-0.4.4 → goodwe-0.4.6/goodwe.egg-info}/PKG-INFO +1 -1
  9. {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_dt.py +64 -1
  10. {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_et.py +3 -3
  11. {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_sensor.py +14 -0
  12. goodwe-0.4.4/VERSION +0 -1
  13. {goodwe-0.4.4 → goodwe-0.4.6}/LICENSE +0 -0
  14. {goodwe-0.4.4 → goodwe-0.4.6}/README.md +0 -0
  15. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/__init__.py +0 -0
  16. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/const.py +0 -0
  17. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/exceptions.py +0 -0
  18. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/inverter.py +0 -0
  19. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/modbus.py +0 -0
  20. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/model.py +0 -0
  21. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe.egg-info/SOURCES.txt +0 -0
  22. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe.egg-info/dependency_links.txt +0 -0
  23. {goodwe-0.4.4 → goodwe-0.4.6}/goodwe.egg-info/top_level.txt +0 -0
  24. {goodwe-0.4.4 → goodwe-0.4.6}/pyproject.toml +0 -0
  25. {goodwe-0.4.4 → goodwe-0.4.6}/setup.cfg +0 -0
  26. {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_es.py +0 -0
  27. {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_modbus.py +0 -0
  28. {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_protocol.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goodwe
3
- Version: 0.4.4
3
+ Version: 0.4.6
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.4.6/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.6
@@ -114,6 +114,10 @@ class DT(Inverter):
114
114
  Integer("shadow_scan", 40326, "Shadow Scan", "", Kind.PV),
115
115
  Integer("grid_export", 40327, "Grid Export Enabled", "", Kind.GRID),
116
116
  Integer("grid_export_limit", 40328, "Grid Export Limit", "%", Kind.GRID),
117
+ Integer("start", 40330, "Start / Power On", "", Kind.GRID),
118
+ Integer("stop", 40331, "Stop / Power Off", "", Kind.GRID),
119
+ Integer("restart", 40332, "Restart", "", Kind.GRID),
120
+ Integer("grid_export_hw", 40345, "Grid Export Enabled (HW)", "", Kind.GRID),
117
121
  )
118
122
 
119
123
  # Settings for single phase inverters
@@ -196,6 +200,7 @@ class DT(Inverter):
196
200
  if ex.message == ILLEGAL_DATA_ADDRESS:
197
201
  logger.debug("Unsupported setting %s", setting.id_)
198
202
  self._settings.pop(setting.id_, None)
203
+ raise ValueError(f'Unknown setting "{setting.id_}"')
199
204
  return None
200
205
 
201
206
  async def write_setting(self, setting_id: str, value: Any):
@@ -174,11 +174,11 @@ class ES(Inverter):
174
174
  def _supports_eco_mode_v2(self) -> bool:
175
175
  if self.arm_version < 14:
176
176
  return False
177
- if "EMU" in self.serial_number:
177
+ if "EMU" in self.serial_number or "EMJ" in self.serial_number:
178
178
  return self.dsp1_version >= 11
179
- if "ESU" in self.serial_number:
179
+ if "ESU" in self.serial_number or "ESA" in self.serial_number:
180
180
  return self.dsp1_version >= 22
181
- if "BPS" in self.serial_number:
181
+ if "BPS" in self.serial_number or "BPU" in self.serial_number:
182
182
  return self.dsp1_version >= 10
183
183
  return False
184
184
 
@@ -188,7 +188,7 @@ class ES(Inverter):
188
188
  self.firmware = self._decode(response[0:5]).rstrip()
189
189
  self.model_name = self._decode(response[5:15]).rstrip()
190
190
  self.serial_number = self._decode(response[31:47])
191
- self.software_version = self._decode(response[51:63])
191
+ self.arm_firmware = self._decode(response[51:63]) # AKA software_version
192
192
  try:
193
193
  if len(self.firmware) >= 2:
194
194
  self.dsp1_version = int(self.firmware[0:2])
@@ -220,9 +220,12 @@ class ES(Inverter):
220
220
  elif setting_id.startswith("modbus"):
221
221
  response = await self._read_from_socket(self._read_command(int(setting_id[7:]), 1))
222
222
  return int.from_bytes(response.read(2), byteorder="big", signed=True)
223
- else:
223
+ elif setting_id in self._settings:
224
+ logger.debug("Reading setting %s", setting_id)
224
225
  all_settings = await self.read_settings_data()
225
226
  return all_settings.get(setting_id)
227
+ else:
228
+ raise ValueError(f'Unknown setting "{setting_id}"')
226
229
 
227
230
  async def _read_setting(self, setting: Sensor) -> Any:
228
231
  count = (setting.size_ + (setting.size_ % 2)) // 2
@@ -296,7 +299,7 @@ class ES(Inverter):
296
299
  try:
297
300
  mode = OperationMode(mode_id)
298
301
  except ValueError:
299
- logger.debug("Unknown work_mode value %d", mode_id)
302
+ logger.debug("Unknown work_mode value %s", mode_id)
300
303
  return None
301
304
  if OperationMode.ECO != mode:
302
305
  return mode
@@ -332,7 +332,7 @@ class ET(Inverter):
332
332
  # Modbus registers of inverter settings, offsets are modbus register addresses
333
333
  __all_settings: Tuple[Sensor, ...] = (
334
334
  Integer("comm_address", 45127, "Communication Address", ""),
335
- Integer("modbus_baud_rate", 45132, "Modbus Baud rate", ""),
335
+ Long("modbus_baud_rate", 45132, "Modbus Baud rate", ""),
336
336
  Timestamp("time", 45200, "Inverter time"),
337
337
 
338
338
  Integer("sensitivity_check", 45246, "Sensitivity Check Mode", "", Kind.AC),
@@ -357,6 +357,8 @@ class ET(Inverter):
357
357
  Integer("work_mode", 47000, "Work Mode", "", Kind.AC),
358
358
  Integer("dred", 47010, "DRED/Remote Shutdown", "", Kind.AC),
359
359
 
360
+ Integer("meter_target_power_offset", 47120, "Meter Target Power Offset", "W", Kind.AC),
361
+
360
362
  Integer("battery_soc_protection", 47500, "Battery SoC Protection", "", Kind.BAT),
361
363
 
362
364
  Integer("grid_export", 47509, "Grid Export Enabled", "", Kind.GRID),
@@ -626,6 +628,7 @@ class ET(Inverter):
626
628
  if ex.message == ILLEGAL_DATA_ADDRESS:
627
629
  logger.debug("Unsupported setting %s", setting.id_)
628
630
  self._settings.pop(setting.id_, None)
631
+ raise ValueError(f'Unknown setting "{setting.id_}"')
629
632
  return None
630
633
 
631
634
  async def write_setting(self, setting_id: str, value: Any):
@@ -685,7 +688,7 @@ class ET(Inverter):
685
688
  try:
686
689
  mode = OperationMode(mode_id)
687
690
  except ValueError:
688
- logger.debug("Unknown work_mode value %d", mode_id)
691
+ logger.debug("Unknown work_mode value %s", mode_id)
689
692
  return None
690
693
  if OperationMode.ECO != mode:
691
694
  return mode
@@ -141,11 +141,13 @@ class UdpInverterProtocol(InverterProtocol, asyncio.DatagramProtocol):
141
141
  logger.debug("Received response fragment (%d of %d): %s", ex.length, ex.expected, data.hex())
142
142
  self._partial_data = data
143
143
  self._partial_missing = ex.expected - ex.length
144
+ self._timer = asyncio.get_running_loop().call_later(self.timeout, self._retry_mechanism)
144
145
  except asyncio.InvalidStateError:
145
146
  logger.debug("Response already handled: %s", data.hex())
146
147
  except RequestRejectedException as ex:
147
148
  logger.debug("Received exception response: %s", data.hex())
148
- self.response_future.set_exception(ex)
149
+ if self.response_future and not self.response_future.done():
150
+ self.response_future.set_exception(ex)
149
151
  self._close_transport()
150
152
 
151
153
  def error_received(self, exc: Exception) -> None:
@@ -284,11 +286,13 @@ class TcpInverterProtocol(InverterProtocol, asyncio.Protocol):
284
286
  logger.debug("Received response fragment (%d of %d): %s", ex.length, ex.expected, data.hex())
285
287
  self._partial_data = data
286
288
  self._partial_missing = ex.expected - ex.length
289
+ self._timer = asyncio.get_running_loop().call_later(self.timeout, self._timeout_mechanism)
287
290
  except asyncio.InvalidStateError:
288
291
  logger.debug("Response already handled: %s", data.hex())
289
292
  except RequestRejectedException as ex:
290
293
  logger.debug("Received exception response: %s", data.hex())
291
- self.response_future.set_exception(ex)
294
+ if self.response_future and not self.response_future.done():
295
+ self.response_future.set_exception(ex)
292
296
  # self._close_transport()
293
297
 
294
298
  def error_received(self, exc: Exception) -> None:
@@ -469,7 +473,7 @@ class Aa55ProtocolCommand(ProtocolCommand):
469
473
  Quite probably it is some variation of the protocol used on RS-485 serial link,
470
474
  extended/adapted to UDP transport layer.
471
475
 
472
- Each request starts with header of 0xAA, 0x55, then 0xC0, 0x7F (probably some sort of address/command)
476
+ Each request starts with header of 0xAA, 0x55, then 0xC0, 0x7F (client addr, inverter addr)
473
477
  followed by actual payload data.
474
478
  It is suffixed with 2 bytes of plain checksum of header+payload.
475
479
 
@@ -478,15 +482,17 @@ class Aa55ProtocolCommand(ProtocolCommand):
478
482
  The last 2 bytes are again plain checksum of header+payload.
479
483
  """
480
484
 
481
- def __init__(self, payload: str, response_type: str):
485
+ def __init__(self, payload: str, response_type: str, offset: int = 0, value: int = 0):
482
486
  super().__init__(
483
487
  bytes.fromhex(
484
488
  "AA55C07F"
485
489
  + payload
486
490
  + self._checksum(bytes.fromhex("AA55C07F" + payload)).hex()
487
491
  ),
488
- lambda x: self._validate_response(x, response_type),
492
+ lambda x: self._validate_aa55_response(x, response_type),
489
493
  )
494
+ self.first_address: int = offset
495
+ self.value = value
490
496
 
491
497
  @staticmethod
492
498
  def _checksum(data: bytes) -> bytes:
@@ -496,7 +502,7 @@ class Aa55ProtocolCommand(ProtocolCommand):
496
502
  return checksum.to_bytes(2, byteorder="big", signed=False)
497
503
 
498
504
  @staticmethod
499
- def _validate_response(data: bytes, response_type: str) -> bool:
505
+ def _validate_aa55_response(data: bytes, response_type: str) -> bool:
500
506
  """
501
507
  Validate the response.
502
508
  data[0:3] is header
@@ -504,13 +510,20 @@ class Aa55ProtocolCommand(ProtocolCommand):
504
510
  data[6] is response payload length
505
511
  data[-2:] is checksum (plain sum of response data incl. header)
506
512
  """
507
- if len(data) <= 8 or len(data) != data[6] + 9:
513
+ if len(data) <= 8:
514
+ logger.debug("Response too short.")
515
+ return False
516
+ elif len(data) < data[6] + 9:
508
517
  raise PartialResponseException(len(data), data[6] + 9)
518
+ elif len(data) > data[6] + 9:
519
+ logger.debug("Response invalid - too long (%d).", len(data))
520
+ return False
509
521
  elif response_type:
510
522
  data_rt_int = int.from_bytes(data[4:6], byteorder="big", signed=True)
511
523
  if int(response_type, 16) != data_rt_int:
512
524
  logger.debug("Response type unexpected: %04x, expected %s.", data_rt_int, response_type)
513
525
  return False
526
+
514
527
  checksum = 0
515
528
  for each in data[:-2]:
516
529
  checksum += each
@@ -523,6 +536,17 @@ class Aa55ProtocolCommand(ProtocolCommand):
523
536
  """Trim raw response from header and checksum data"""
524
537
  return raw_response[7:-2]
525
538
 
539
+ def __repr__(self):
540
+ if self.request[4] == 1:
541
+ if self.request[5] == 2:
542
+ return f'READ device info ({self.request.hex()})'
543
+ elif self.request[5] == 6:
544
+ return f'READ runtime data ({self.request.hex()})'
545
+ elif self.request[5] == 9:
546
+ return f'READ settings ({self.request.hex()})'
547
+ else:
548
+ return self.request.hex()
549
+
526
550
 
527
551
  class Aa55ReadCommand(Aa55ProtocolCommand):
528
552
  """
@@ -530,7 +554,13 @@ class Aa55ReadCommand(Aa55ProtocolCommand):
530
554
  """
531
555
 
532
556
  def __init__(self, offset: int, count: int):
533
- super().__init__("011A03" + "{:04x}".format(offset) + "{:02x}".format(count), "019A")
557
+ super().__init__("011A03" + "{:04x}".format(offset) + "{:02x}".format(count), "019A", offset, count)
558
+
559
+ def __repr__(self):
560
+ if self.value > 1:
561
+ return f'READ {self.value} registers from {self.first_address} ({self.request.hex()})'
562
+ else:
563
+ return f'READ register {self.first_address} ({self.request.hex()})'
534
564
 
535
565
 
536
566
  class Aa55WriteCommand(Aa55ProtocolCommand):
@@ -539,7 +569,10 @@ class Aa55WriteCommand(Aa55ProtocolCommand):
539
569
  """
540
570
 
541
571
  def __init__(self, register: int, value: int):
542
- super().__init__("023905" + "{:04x}".format(register) + "01" + "{:04x}".format(value), "02B9")
572
+ super().__init__("023905" + "{:04x}".format(register) + "01" + "{:04x}".format(value), "02B9", register, value)
573
+
574
+ def __repr__(self):
575
+ return f'WRITE {self.value} to register {self.first_address} ({self.request.hex()})'
543
576
 
544
577
 
545
578
  class Aa55WriteMultiCommand(Aa55ProtocolCommand):
@@ -549,7 +582,7 @@ class Aa55WriteMultiCommand(Aa55ProtocolCommand):
549
582
 
550
583
  def __init__(self, offset: int, values: bytes):
551
584
  super().__init__("02390B" + "{:04x}".format(offset) + "{:02x}".format(len(values)) + values.hex(),
552
- "02B9")
585
+ "02B9", offset, len(values) // 2)
553
586
 
554
587
 
555
588
  class ModbusRtuProtocolCommand(ProtocolCommand):
@@ -364,7 +364,7 @@ class Decimal(Sensor):
364
364
  return read_decimal2(data, self.scale)
365
365
 
366
366
  def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
367
- return int.to_bytes(int(value * self.scale), length=2, byteorder="big", signed=True)
367
+ return int.to_bytes(int(float(value) * self.scale), length=2, byteorder="big", signed=True)
368
368
 
369
369
 
370
370
  class Float(Sensor):
@@ -868,7 +868,7 @@ def read_voltage(buffer: ProtocolResponse, offset: int = None) -> float:
868
868
 
869
869
  def encode_voltage(value: Any) -> bytes:
870
870
  """Encode voltage value to raw (2 unsigned bytes) payload"""
871
- return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=False)
871
+ return int.to_bytes(int(float(value) * 10), length=2, byteorder="big", signed=False)
872
872
 
873
873
 
874
874
  def read_current(buffer: ProtocolResponse, offset: int = None) -> float:
@@ -889,12 +889,12 @@ def read_current_signed(buffer: ProtocolResponse, offset: int = None) -> float:
889
889
 
890
890
  def encode_current(value: Any) -> bytes:
891
891
  """Encode current value to raw (2 unsigned bytes) payload"""
892
- return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=False)
892
+ return int.to_bytes(int(float(value) * 10), length=2, byteorder="big", signed=False)
893
893
 
894
894
 
895
895
  def encode_current_signed(value: Any) -> bytes:
896
896
  """Encode current value to raw (2 signed bytes) payload"""
897
- return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=True)
897
+ return int.to_bytes(int(float(value) * 10), length=2, byteorder="big", signed=True)
898
898
 
899
899
 
900
900
  def read_freq(buffer: ProtocolResponse, offset: int = None) -> float:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goodwe
3
- Version: 0.4.4
3
+ Version: 0.4.6
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
@@ -101,7 +101,7 @@ class GW6000_DT_Test(DtMock):
101
101
  self.assertFalse(self.sensor_map, f"Some sensors were not tested {self.sensor_map}")
102
102
 
103
103
  def test_GW6000_DT_setting(self):
104
- self.assertEqual(4, len(self.settings()))
104
+ self.assertEqual(8, len(self.settings()))
105
105
  settings = {s.id_: s for s in self.settings()}
106
106
  self.assertEqual('Timestamp', type(settings.get("time")).__name__)
107
107
  self.assertEqual('Integer', type(settings.get("grid_export")).__name__)
@@ -416,3 +416,66 @@ class GW20KAU_DT_Test(DtMock):
416
416
  self.assertSensor('vnbus', 298.9, 'V', data)
417
417
  self.assertSensor('derating_mode', 4, '', data)
418
418
  self.assertSensor('derating_mode_label', 'Reactive power derating(PF/QU/FixQ)', '', data)
419
+
420
+
421
+ class GW17K_DT_Test(DtMock):
422
+
423
+ def __init__(self, methodName='runTest'):
424
+ DtMock.__init__(self, methodName)
425
+ self.mock_response(self._READ_DEVICE_RUNNING_DATA, 'GW17K-DT_running_data.hex')
426
+ self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW17K-DT_device_info.hex')
427
+
428
+ def test_GW20KAU_DT_device_info(self):
429
+ self.loop.run_until_complete(self.read_device_info())
430
+ self.assertEqual('GW17KT-DT', self.model_name)
431
+ self.assertEqual('5017KDTT00BW0000', self.serial_number)
432
+ self.assertEqual(12, self.dsp1_version)
433
+ self.assertEqual(12, self.dsp2_version)
434
+ self.assertEqual(13, self.arm_version)
435
+ self.assertEqual('12.12.0d', self.firmware)
436
+
437
+ def test_GW20KAU_DT_runtime_data(self):
438
+ self.loop.run_until_complete(self.read_device_info())
439
+ data = self.loop.run_until_complete(self.read_runtime_data())
440
+ self.assertEqual(40, len(data))
441
+
442
+ self.assertSensor('timestamp', datetime.strptime('2024-05-20 10:35:55', '%Y-%m-%d %H:%M:%S'), '', data)
443
+ self.assertSensor('vpv1', 540.0, 'V', data)
444
+ self.assertSensor('ipv1', 10.5, 'A', data)
445
+ self.assertSensor('ppv1', 5670, 'W', data)
446
+ self.assertSensor('vpv2', 475.5, 'V', data)
447
+ self.assertSensor('ipv2', 14.8, 'A', data)
448
+ self.assertSensor('ppv2', 7037, 'W', data)
449
+ self.assertSensor('vline1', 413.0, 'V', data)
450
+ self.assertSensor('vline2', 411.5, 'V', data)
451
+ self.assertSensor('vline3', 409.5, 'V', data)
452
+ self.assertSensor('vgrid1', 236.7, 'V', data)
453
+ self.assertSensor('vgrid2', 238.3, 'V', data)
454
+ self.assertSensor('vgrid3', 237.3, 'V', data)
455
+ self.assertSensor('igrid1', 17.6, 'A', data)
456
+ self.assertSensor('igrid2', 17.5, 'A', data)
457
+ self.assertSensor('igrid3', 17.5, 'A', data)
458
+ self.assertSensor('fgrid1', 50.02, 'Hz', data)
459
+ self.assertSensor('fgrid2', 50.02, 'Hz', data)
460
+ self.assertSensor('fgrid3', 50.02, 'Hz', data)
461
+ self.assertSensor('pgrid1', 4166, 'W', data)
462
+ self.assertSensor('pgrid2', 4170, 'W', data)
463
+ self.assertSensor('pgrid3', 4153, 'W', data)
464
+ self.assertSensor('ppv', 12470, 'W', data)
465
+ self.assertSensor('work_mode', 1, '', data)
466
+ self.assertSensor('work_mode_label', 'Normal', '', data)
467
+ self.assertSensor('error_codes', 0, '', data)
468
+ self.assertSensor('warning_code', 0, '', data)
469
+ self.assertSensor('apparent_power', 0, 'VA', data)
470
+ self.assertSensor('reactive_power', 0, 'var', data)
471
+ self.assertSensor('temperature', 45.7, 'C', data)
472
+ self.assertSensor('e_day', 29.3, 'kWh', data)
473
+ self.assertSensor('e_total', 29984.4, 'kWh', data)
474
+ self.assertSensor('h_total', 8357, 'h', data)
475
+ self.assertSensor('safety_country', 1, '', data)
476
+ self.assertSensor('safety_country_label', 'CZ-A1', '', data)
477
+ self.assertSensor('funbit', 546, '', data)
478
+ self.assertSensor('vbus', 621.8, 'V', data)
479
+ self.assertSensor('vnbus', 314.2, 'V', data)
480
+ self.assertSensor('derating_mode', 4, '', data)
481
+ self.assertSensor('derating_mode_label', 'Reactive power derating(PF/QU/FixQ)', '', data)
@@ -240,7 +240,7 @@ class GW10K_ET_Test(EtMock):
240
240
  self.assertFalse(self.sensor_map, f"Some sensors were not tested {self.sensor_map}")
241
241
 
242
242
  def test_GW10K_ET_setting(self):
243
- self.assertEqual(65, len(self.settings()))
243
+ self.assertEqual(66, len(self.settings()))
244
244
  settings = {s.id_: s for s in self.settings()}
245
245
  self.assertEqual('Timestamp', type(settings.get("time")).__name__)
246
246
  self.assertEqual('EcoModeV1', type(settings.get("eco_mode_1")).__name__)
@@ -338,7 +338,7 @@ class GW10K_ET_fw819_Test(EtMock):
338
338
  self.assertEqual('02041-19-S00', self.arm_firmware)
339
339
 
340
340
  def test_GW10K_ET_settings_fw819(self):
341
- self.assertEqual(72, len(self.settings()))
341
+ self.assertEqual(73, len(self.settings()))
342
342
  settings = {s.id_: s for s in self.settings()}
343
343
  self.assertEqual('EcoModeV2', type(settings.get("eco_mode_1")).__name__)
344
344
  self.assertEqual(None, settings.get("peak_shaving_mode"))
@@ -379,7 +379,7 @@ class GW10K_ET_fw1023_Test(EtMock):
379
379
  self.assertEqual('02041-23-S00', self.arm_firmware)
380
380
 
381
381
  def test_GW10K_ET_setting_fw1023(self):
382
- self.assertEqual(80, len(self.settings()))
382
+ self.assertEqual(81, len(self.settings()))
383
383
  settings = {s.id_: s for s in self.settings()}
384
384
  self.assertEqual('PeakShavingMode', type(settings.get("peak_shaving_mode")).__name__)
385
385
 
@@ -30,6 +30,7 @@ class TestUtils(TestCase):
30
30
  self.assertEqual(32, testee.read(data))
31
31
 
32
32
  self.assertEqual("2039", testee.encode_value(32, bytes.fromhex("3039")).hex())
33
+ self.assertEqual("2039", testee.encode_value("32", bytes.fromhex("3039")).hex())
33
34
  self.assertEqual("ff39", testee.encode_value(-1, bytes.fromhex("3039")).hex())
34
35
  self.assertEqual("7f39", testee.encode_value(127, bytes.fromhex("3039")).hex())
35
36
  self.assertEqual("20ff", testee.encode_value(32, bytes.fromhex("ffff")).hex())
@@ -41,6 +42,7 @@ class TestUtils(TestCase):
41
42
  self.assertEqual(127, testee.read(data))
42
43
 
43
44
  self.assertEqual("3020", testee.encode_value(32, bytes.fromhex("3039")).hex())
45
+ self.assertEqual("3020", testee.encode_value("32", bytes.fromhex("3039")).hex())
44
46
  self.assertEqual("30ff", testee.encode_value(-1, bytes.fromhex("3039")).hex())
45
47
  self.assertEqual("307f", testee.encode_value(127, bytes.fromhex("3039")).hex())
46
48
  self.assertEqual("ff20", testee.encode_value(32, bytes.fromhex("ffff")).hex())
@@ -51,10 +53,12 @@ class TestUtils(TestCase):
51
53
  data = MockResponse("0031")
52
54
  self.assertEqual(49, testee.read(data))
53
55
  self.assertEqual("0031", testee.encode_value(49).hex())
56
+ self.assertEqual("0031", testee.encode_value("49").hex())
54
57
 
55
58
  data = MockResponse("ff9e")
56
59
  self.assertEqual(65438, testee.read(data))
57
60
  self.assertEqual("ff9e", testee.encode_value(65438).hex())
61
+ self.assertEqual("ff9e", testee.encode_value("65438").hex())
58
62
 
59
63
  def test_integer_signed(self):
60
64
  testee = IntegerS("", 0, "", "", None)
@@ -62,10 +66,12 @@ class TestUtils(TestCase):
62
66
  data = MockResponse("0031")
63
67
  self.assertEqual(49, testee.read(data))
64
68
  self.assertEqual("0031", testee.encode_value(49).hex())
69
+ self.assertEqual("0031", testee.encode_value("49").hex())
65
70
 
66
71
  data = MockResponse("ff9e")
67
72
  self.assertEqual(-98, testee.read(data))
68
73
  self.assertEqual("ff9e", testee.encode_value(-98).hex())
74
+ self.assertEqual("ff9e", testee.encode_value("-98").hex())
69
75
 
70
76
  def test_decimal(self):
71
77
  testee = Decimal("", 0, 10, "", "", None)
@@ -73,10 +79,12 @@ class TestUtils(TestCase):
73
79
  data = MockResponse("0031")
74
80
  self.assertEqual(4.9, testee.read(data))
75
81
  self.assertEqual("0031", testee.encode_value(4.9).hex())
82
+ self.assertEqual("0031", testee.encode_value("4.9").hex())
76
83
 
77
84
  data = MockResponse("ff9e")
78
85
  self.assertEqual(-9.8, testee.read(data))
79
86
  self.assertEqual("ff9e", testee.encode_value(-9.8).hex())
87
+ self.assertEqual("ff9e", testee.encode_value("-9.8").hex())
80
88
 
81
89
  def test_voltage(self):
82
90
  testee = Voltage("", 0, "", None)
@@ -84,10 +92,12 @@ class TestUtils(TestCase):
84
92
  data = MockResponse("0cfe")
85
93
  self.assertEqual(332.6, testee.read(data))
86
94
  self.assertEqual("0cfe", testee.encode_value(332.6).hex())
95
+ self.assertEqual("0cfe", testee.encode_value("332.6").hex())
87
96
 
88
97
  data = MockResponse("1f64")
89
98
  self.assertEqual(803.6, testee.read(data))
90
99
  self.assertEqual("1f64", testee.encode_value(803.6).hex())
100
+ self.assertEqual("1f64", testee.encode_value("803.6").hex())
91
101
 
92
102
  data = MockResponse("a000")
93
103
  self.assertEqual(4096.0, testee.read(data))
@@ -101,10 +111,12 @@ class TestUtils(TestCase):
101
111
  data = MockResponse("0031")
102
112
  self.assertEqual(4.9, testee.read(data))
103
113
  self.assertEqual("0031", testee.encode_value(4.9).hex())
114
+ self.assertEqual("0031", testee.encode_value("4.9").hex())
104
115
 
105
116
  data = MockResponse("ff9e")
106
117
  self.assertEqual(6543.8, testee.read(data))
107
118
  self.assertEqual("ff9e", testee.encode_value(6543.8).hex())
119
+ self.assertEqual("ff9e", testee.encode_value("6543.8").hex())
108
120
 
109
121
  data = MockResponse("ffff")
110
122
  self.assertEqual(0, testee.read(data))
@@ -115,10 +127,12 @@ class TestUtils(TestCase):
115
127
  data = MockResponse("0031")
116
128
  self.assertEqual(4.9, testee.read(data))
117
129
  self.assertEqual("0031", testee.encode_value(4.9).hex())
130
+ self.assertEqual("0031", testee.encode_value("4.9").hex())
118
131
 
119
132
  data = MockResponse("ff9e")
120
133
  self.assertEqual(-9.8, testee.read(data))
121
134
  self.assertEqual("ff9e", testee.encode_value(-9.8).hex())
135
+ self.assertEqual("ff9e", testee.encode_value("-9.8").hex())
122
136
 
123
137
  def test_power4(self):
124
138
  testee = Power4("", 0, "", None)
goodwe-0.4.4/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.4.4
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