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.
- {goodwe-0.4.4/goodwe.egg-info → goodwe-0.4.6}/PKG-INFO +1 -1
- goodwe-0.4.6/VERSION +1 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/dt.py +5 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/es.py +9 -6
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/et.py +5 -2
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/protocol.py +43 -10
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/sensor.py +4 -4
- {goodwe-0.4.4 → goodwe-0.4.6/goodwe.egg-info}/PKG-INFO +1 -1
- {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_dt.py +64 -1
- {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_et.py +3 -3
- {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_sensor.py +14 -0
- goodwe-0.4.4/VERSION +0 -1
- {goodwe-0.4.4 → goodwe-0.4.6}/LICENSE +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/README.md +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/__init__.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/const.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/exceptions.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/inverter.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/modbus.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe/model.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe.egg-info/SOURCES.txt +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe.egg-info/dependency_links.txt +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/goodwe.egg-info/top_level.txt +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/pyproject.toml +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/setup.cfg +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_es.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_modbus.py +0 -0
- {goodwe-0.4.4 → goodwe-0.4.6}/tests/test_protocol.py +0 -0
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.
|
|
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
|
-
|
|
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 %
|
|
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
|
-
|
|
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 %
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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
|
|
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
|
|
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:
|
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|