goodwe 0.4.7__tar.gz → 0.4.8__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.7/goodwe.egg-info → goodwe-0.4.8}/PKG-INFO +1 -1
- goodwe-0.4.8/VERSION +1 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/dt.py +34 -10
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/protocol.py +48 -45
- {goodwe-0.4.7 → goodwe-0.4.8/goodwe.egg-info}/PKG-INFO +1 -1
- {goodwe-0.4.7 → goodwe-0.4.8}/tests/test_dt.py +64 -33
- {goodwe-0.4.7 → goodwe-0.4.8}/tests/test_protocol.py +34 -34
- goodwe-0.4.7/VERSION +0 -1
- {goodwe-0.4.7 → goodwe-0.4.8}/LICENSE +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/README.md +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/__init__.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/const.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/es.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/et.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/exceptions.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/inverter.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/modbus.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/model.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe/sensor.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe.egg-info/SOURCES.txt +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe.egg-info/dependency_links.txt +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/goodwe.egg-info/top_level.txt +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/pyproject.toml +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/setup.cfg +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/tests/test_es.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/tests/test_et.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/tests/test_modbus.py +0 -0
- {goodwe-0.4.7 → goodwe-0.4.8}/tests/test_sensor.py +0 -0
goodwe-0.4.8/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.4.8
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import Tuple
|
|
5
5
|
|
|
6
|
-
from .exceptions import InverterError, RequestRejectedException
|
|
6
|
+
from .exceptions import InverterError, RequestFailedException, RequestRejectedException
|
|
7
7
|
from .inverter import Inverter
|
|
8
8
|
from .inverter import OperationMode
|
|
9
9
|
from .inverter import SensorKind as Kind
|
|
@@ -69,7 +69,7 @@ class DT(Inverter):
|
|
|
69
69
|
lambda data: round(read_voltage(data, 30120) * read_current(data, 30123)),
|
|
70
70
|
"On-grid L3 Power", "W", Kind.AC),
|
|
71
71
|
# 30127 reserved
|
|
72
|
-
PowerS("
|
|
72
|
+
PowerS("total_inverter_power", 30128, "Total Power", Kind.AC),
|
|
73
73
|
Integer("work_mode", 30129, "Work Mode code"),
|
|
74
74
|
Enum2("work_mode_label", 30129, WORK_MODES, "Work Mode"),
|
|
75
75
|
Long("error_codes", 30130, "Error Codes"),
|
|
@@ -78,7 +78,7 @@ class DT(Inverter):
|
|
|
78
78
|
Reactive4("reactive_power", 30135, "Reactive Power", Kind.AC),
|
|
79
79
|
# 30137 reserved
|
|
80
80
|
# 30138 reserved
|
|
81
|
-
|
|
81
|
+
Decimal("power_factor", 30139, 1000, "Power Factor", "", Kind.GRID),
|
|
82
82
|
# 30140 reserved
|
|
83
83
|
Temp("temperature", 30141, "Inverter Temperature", Kind.AC),
|
|
84
84
|
# 30142 reserved
|
|
@@ -113,6 +113,12 @@ class DT(Inverter):
|
|
|
113
113
|
# 30172 reserved
|
|
114
114
|
)
|
|
115
115
|
|
|
116
|
+
# Inverter's meter data
|
|
117
|
+
# Modbus registers from offset 0x75f4 (30196)
|
|
118
|
+
__all_sensors_meter: Tuple[Sensor, ...] = (
|
|
119
|
+
PowerS("active_power", 30196, "Active Power", Kind.GRID),
|
|
120
|
+
)
|
|
121
|
+
|
|
116
122
|
# Modbus registers of inverter settings, offsets are modbus register addresses
|
|
117
123
|
__all_settings: Tuple[Sensor, ...] = (
|
|
118
124
|
Timestamp("time", 40313, "Inverter time"),
|
|
@@ -139,9 +145,12 @@ class DT(Inverter):
|
|
|
139
145
|
def __init__(self, host: str, port: int, comm_addr: int = 0, timeout: int = 1, retries: int = 3):
|
|
140
146
|
super().__init__(host, port, comm_addr if comm_addr else 0x7f, timeout, retries)
|
|
141
147
|
self._READ_DEVICE_VERSION_INFO: ProtocolCommand = self._read_command(0x7531, 0x0028)
|
|
142
|
-
self.
|
|
148
|
+
self._READ_RUNNING_DATA: ProtocolCommand = self._read_command(0x7594, 0x0049)
|
|
149
|
+
self._READ_METER_DATA: ProtocolCommand = self._read_command(0x75f4, 0x01)
|
|
143
150
|
self._sensors = self.__all_sensors
|
|
151
|
+
self._sensors_meter = self.__all_sensors_meter
|
|
144
152
|
self._settings: dict[str, Sensor] = {s.id_: s for s in self.__all_settings}
|
|
153
|
+
self._has_meter: bool = True
|
|
145
154
|
|
|
146
155
|
@staticmethod
|
|
147
156
|
def _single_phase_only(s: Sensor) -> bool:
|
|
@@ -160,10 +169,13 @@ class DT(Inverter):
|
|
|
160
169
|
self.model_name = response[22:32].decode("ascii").rstrip()
|
|
161
170
|
except:
|
|
162
171
|
print("No model name sent from the inverter.")
|
|
163
|
-
|
|
164
|
-
self.
|
|
165
|
-
self.
|
|
166
|
-
self.
|
|
172
|
+
# Modbus registers from 30001 - 30040
|
|
173
|
+
self.serial_number = self._decode(response[6:22]) # 30004 - 30012
|
|
174
|
+
self.dsp1_version = read_unsigned_int(response, 66) # 30034
|
|
175
|
+
self.dsp2_version = read_unsigned_int(response, 68) # 30035
|
|
176
|
+
self.arm_version = read_unsigned_int(response, 70) # 30036
|
|
177
|
+
self.dsp_svn_version = read_unsigned_int(response, 72) # 35037
|
|
178
|
+
self.arm_svn_version = read_unsigned_int(response, 74) # 35038
|
|
167
179
|
self.firmware = "{}.{}.{:02x}".format(self.dsp1_version, self.dsp2_version, self.arm_version)
|
|
168
180
|
|
|
169
181
|
if is_single_phase(self):
|
|
@@ -182,8 +194,17 @@ class DT(Inverter):
|
|
|
182
194
|
pass
|
|
183
195
|
|
|
184
196
|
async def read_runtime_data(self) -> Dict[str, Any]:
|
|
185
|
-
response = await self._read_from_socket(self.
|
|
197
|
+
response = await self._read_from_socket(self._READ_RUNNING_DATA)
|
|
186
198
|
data = self._map_response(response, self._sensors)
|
|
199
|
+
|
|
200
|
+
if self._has_meter:
|
|
201
|
+
try:
|
|
202
|
+
response = await self._read_from_socket(self._READ_METER_DATA)
|
|
203
|
+
data.update(self._map_response(response, self._sensors_meter))
|
|
204
|
+
except (RequestRejectedException, RequestFailedException):
|
|
205
|
+
logger.info("Meter values not supported, disabling further attempts.")
|
|
206
|
+
self._has_meter = False
|
|
207
|
+
|
|
187
208
|
return data
|
|
188
209
|
|
|
189
210
|
async def read_setting(self, setting_id: str) -> Any:
|
|
@@ -263,7 +284,10 @@ class DT(Inverter):
|
|
|
263
284
|
raise InverterError("Operation not supported, inverter has no batteries.")
|
|
264
285
|
|
|
265
286
|
def sensors(self) -> Tuple[Sensor, ...]:
|
|
266
|
-
|
|
287
|
+
result = self._sensors
|
|
288
|
+
if self._has_meter:
|
|
289
|
+
result = result + self._sensors_meter
|
|
290
|
+
return result
|
|
267
291
|
|
|
268
292
|
def settings(self) -> Tuple[Sensor, ...]:
|
|
269
293
|
return tuple(self._settings.values())
|
|
@@ -37,7 +37,7 @@ class InverterProtocol:
|
|
|
37
37
|
self._timer: asyncio.TimerHandle | None = None
|
|
38
38
|
self.timeout: int = timeout
|
|
39
39
|
self.retries: int = retries
|
|
40
|
-
self.keep_alive: bool =
|
|
40
|
+
self.keep_alive: bool = False
|
|
41
41
|
self.protocol: asyncio.Protocol | None = None
|
|
42
42
|
self.response_future: Future | None = None
|
|
43
43
|
self.command: ProtocolCommand | None = None
|
|
@@ -62,6 +62,24 @@ class InverterProtocol:
|
|
|
62
62
|
self._close_transport()
|
|
63
63
|
return self._lock
|
|
64
64
|
|
|
65
|
+
def _max_retries_reached(self) -> Future:
|
|
66
|
+
logger.debug("Max number of retries (%d) reached, request %s failed.", self.retries, self.command)
|
|
67
|
+
self._close_transport()
|
|
68
|
+
self.response_future = asyncio.get_running_loop().create_future()
|
|
69
|
+
self.response_future.set_exception(MaxRetriesException)
|
|
70
|
+
return self.response_future
|
|
71
|
+
|
|
72
|
+
def _close_transport(self) -> None:
|
|
73
|
+
if self._transport:
|
|
74
|
+
try:
|
|
75
|
+
self._transport.close()
|
|
76
|
+
except RuntimeError:
|
|
77
|
+
logger.debug("Failed to close transport.")
|
|
78
|
+
self._transport = None
|
|
79
|
+
# Cancel Future on connection lost
|
|
80
|
+
if self.response_future and not self.response_future.done():
|
|
81
|
+
self.response_future.cancel()
|
|
82
|
+
|
|
65
83
|
async def close(self) -> None:
|
|
66
84
|
"""Close the underlying transport/connection."""
|
|
67
85
|
raise NotImplementedError()
|
|
@@ -133,15 +151,16 @@ class UdpInverterProtocol(InverterProtocol, asyncio.DatagramProtocol):
|
|
|
133
151
|
self._partial_missing = 0
|
|
134
152
|
if self.command.validator(data):
|
|
135
153
|
logger.debug("Received: %s", data.hex())
|
|
154
|
+
self._retry = 0
|
|
136
155
|
self.response_future.set_result(data)
|
|
137
156
|
else:
|
|
138
157
|
logger.debug("Received invalid response: %s", data.hex())
|
|
139
|
-
asyncio.get_running_loop().call_soon(self.
|
|
158
|
+
asyncio.get_running_loop().call_soon(self._timeout_mechanism)
|
|
140
159
|
except PartialResponseException as ex:
|
|
141
160
|
logger.debug("Received response fragment (%d of %d): %s", ex.length, ex.expected, data.hex())
|
|
142
161
|
self._partial_data = data
|
|
143
162
|
self._partial_missing = ex.expected - ex.length
|
|
144
|
-
self._timer = asyncio.get_running_loop().call_later(self.timeout, self.
|
|
163
|
+
self._timer = asyncio.get_running_loop().call_later(self.timeout, self._timeout_mechanism)
|
|
145
164
|
except asyncio.InvalidStateError:
|
|
146
165
|
logger.debug("Response already handled: %s", data.hex())
|
|
147
166
|
except RequestRejectedException as ex:
|
|
@@ -158,13 +177,28 @@ class UdpInverterProtocol(InverterProtocol, asyncio.DatagramProtocol):
|
|
|
158
177
|
|
|
159
178
|
async def send_request(self, command: ProtocolCommand) -> Future:
|
|
160
179
|
"""Send message via transport"""
|
|
161
|
-
|
|
180
|
+
await self._ensure_lock().acquire()
|
|
181
|
+
try:
|
|
162
182
|
await self._connect()
|
|
163
183
|
response_future = asyncio.get_running_loop().create_future()
|
|
164
|
-
self._retry = 0
|
|
165
184
|
self._send_request(command, response_future)
|
|
166
185
|
await response_future
|
|
167
186
|
return response_future
|
|
187
|
+
except asyncio.CancelledError:
|
|
188
|
+
if self._retry < self.retries:
|
|
189
|
+
self._retry += 1
|
|
190
|
+
if self._lock and self._lock.locked():
|
|
191
|
+
self._lock.release()
|
|
192
|
+
if not self.keep_alive:
|
|
193
|
+
self._close_transport()
|
|
194
|
+
return await self.send_request(command)
|
|
195
|
+
else:
|
|
196
|
+
return self._max_retries_reached()
|
|
197
|
+
finally:
|
|
198
|
+
if self._lock and self._lock.locked():
|
|
199
|
+
self._lock.release()
|
|
200
|
+
if not self.keep_alive:
|
|
201
|
+
self._close_transport()
|
|
168
202
|
|
|
169
203
|
def _send_request(self, command: ProtocolCommand, response_future: Future) -> None:
|
|
170
204
|
"""Send message via transport"""
|
|
@@ -178,32 +212,19 @@ class UdpInverterProtocol(InverterProtocol, asyncio.DatagramProtocol):
|
|
|
178
212
|
else:
|
|
179
213
|
logger.debug("Sending: %s", self.command)
|
|
180
214
|
self._transport.sendto(payload)
|
|
181
|
-
self._timer = asyncio.get_running_loop().call_later(self.timeout, self.
|
|
215
|
+
self._timer = asyncio.get_running_loop().call_later(self.timeout, self._timeout_mechanism)
|
|
182
216
|
|
|
183
|
-
def
|
|
184
|
-
"""
|
|
185
|
-
if self.response_future.done():
|
|
217
|
+
def _timeout_mechanism(self) -> None:
|
|
218
|
+
"""Timeout mechanism to prevent hanging transport"""
|
|
219
|
+
if self.response_future and self.response_future.done():
|
|
186
220
|
logger.debug("Response already received.")
|
|
187
|
-
|
|
221
|
+
self._retry = 0
|
|
222
|
+
else:
|
|
188
223
|
if self._timer:
|
|
189
224
|
logger.debug("Failed to receive response to %s in time (%ds).", self.command, self.timeout)
|
|
190
|
-
|
|
191
|
-
self.
|
|
192
|
-
|
|
193
|
-
logger.debug("Max number of retries (%d) reached, request %s failed.", self.retries, self.command)
|
|
194
|
-
self.response_future.set_exception(MaxRetriesException)
|
|
195
|
-
self._close_transport()
|
|
196
|
-
|
|
197
|
-
def _close_transport(self) -> None:
|
|
198
|
-
if self._transport:
|
|
199
|
-
try:
|
|
200
|
-
self._transport.close()
|
|
201
|
-
except RuntimeError:
|
|
202
|
-
logger.debug("Failed to close transport.")
|
|
203
|
-
self._transport = None
|
|
204
|
-
# Cancel Future on connection close
|
|
205
|
-
if self.response_future and not self.response_future.done():
|
|
206
|
-
self.response_future.cancel()
|
|
225
|
+
self._timer = None
|
|
226
|
+
if self.response_future and not self.response_future.done():
|
|
227
|
+
self.response_future.cancel()
|
|
207
228
|
|
|
208
229
|
async def close(self):
|
|
209
230
|
self._close_transport()
|
|
@@ -358,24 +379,6 @@ class TcpInverterProtocol(InverterProtocol, asyncio.Protocol):
|
|
|
358
379
|
self._timer = None
|
|
359
380
|
self._close_transport()
|
|
360
381
|
|
|
361
|
-
def _max_retries_reached(self) -> Future:
|
|
362
|
-
logger.debug("Max number of retries (%d) reached, request %s failed.", self.retries, self.command)
|
|
363
|
-
self._close_transport()
|
|
364
|
-
self.response_future = asyncio.get_running_loop().create_future()
|
|
365
|
-
self.response_future.set_exception(MaxRetriesException)
|
|
366
|
-
return self.response_future
|
|
367
|
-
|
|
368
|
-
def _close_transport(self) -> None:
|
|
369
|
-
if self._transport:
|
|
370
|
-
try:
|
|
371
|
-
self._transport.close()
|
|
372
|
-
except RuntimeError:
|
|
373
|
-
logger.debug("Failed to close transport.")
|
|
374
|
-
self._transport = None
|
|
375
|
-
# Cancel Future on connection lost
|
|
376
|
-
if self.response_future and not self.response_future.done():
|
|
377
|
-
self.response_future.cancel()
|
|
378
|
-
|
|
379
382
|
async def close(self):
|
|
380
383
|
await self._ensure_lock().acquire()
|
|
381
384
|
try:
|
|
@@ -4,7 +4,8 @@ from datetime import datetime
|
|
|
4
4
|
from unittest import TestCase
|
|
5
5
|
|
|
6
6
|
from goodwe.dt import DT
|
|
7
|
-
from goodwe.exceptions import RequestFailedException
|
|
7
|
+
from goodwe.exceptions import RequestFailedException, RequestRejectedException
|
|
8
|
+
from goodwe.modbus import ILLEGAL_DATA_ADDRESS
|
|
8
9
|
from goodwe.protocol import ProtocolCommand, ProtocolResponse
|
|
9
10
|
|
|
10
11
|
|
|
@@ -13,7 +14,7 @@ class DtMock(TestCase, DT):
|
|
|
13
14
|
def __init__(self, methodName='runTest', port=8899):
|
|
14
15
|
TestCase.__init__(self, methodName)
|
|
15
16
|
DT.__init__(self, "localhost", port)
|
|
16
|
-
self.sensor_map = {s.id_: s
|
|
17
|
+
self.sensor_map = {s.id_: s for s in self.sensors()}
|
|
17
18
|
self._mock_responses = {}
|
|
18
19
|
|
|
19
20
|
def mock_response(self, command: ProtocolCommand, filename: str):
|
|
@@ -24,6 +25,10 @@ class DtMock(TestCase, DT):
|
|
|
24
25
|
root_dir = os.path.dirname(os.path.abspath(__file__))
|
|
25
26
|
filename = self._mock_responses.get(command)
|
|
26
27
|
if filename is not None:
|
|
28
|
+
if ILLEGAL_DATA_ADDRESS == filename:
|
|
29
|
+
raise RequestRejectedException(ILLEGAL_DATA_ADDRESS)
|
|
30
|
+
if 'NO RESPONSE' == filename:
|
|
31
|
+
raise RequestFailedException()
|
|
27
32
|
with open(root_dir + '/sample/dt/' + filename, 'r') as f:
|
|
28
33
|
response = bytes.fromhex(f.read())
|
|
29
34
|
if not command.validator(response):
|
|
@@ -33,10 +38,11 @@ class DtMock(TestCase, DT):
|
|
|
33
38
|
self.request = command.request
|
|
34
39
|
return ProtocolResponse(bytes.fromhex("aa557f00010203040506070809"), command)
|
|
35
40
|
|
|
36
|
-
def assertSensor(self,
|
|
37
|
-
self.assertEqual(expected_value, data.get(
|
|
38
|
-
|
|
39
|
-
self.
|
|
41
|
+
def assertSensor(self, sensor_name, expected_value, expected_unit, data):
|
|
42
|
+
self.assertEqual(expected_value, data.get(sensor_name))
|
|
43
|
+
sensor = self.sensor_map.get(sensor_name);
|
|
44
|
+
self.assertEqual(expected_unit, sensor.unit)
|
|
45
|
+
self.sensor_map.pop(sensor_name)
|
|
40
46
|
|
|
41
47
|
@classmethod
|
|
42
48
|
def setUpClass(cls):
|
|
@@ -47,12 +53,15 @@ class GW6000_DT_Test(DtMock):
|
|
|
47
53
|
|
|
48
54
|
def __init__(self, methodName='runTest'):
|
|
49
55
|
DtMock.__init__(self, methodName)
|
|
50
|
-
self.mock_response(self.
|
|
56
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW6000-DT_running_data.hex')
|
|
57
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
51
58
|
|
|
52
59
|
def test_GW6000_DT_runtime_data(self):
|
|
53
60
|
self.loop.run_until_complete(self.read_device_info())
|
|
54
61
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
55
|
-
self.assertEqual(
|
|
62
|
+
self.assertEqual(42, len(data))
|
|
63
|
+
|
|
64
|
+
self.sensor_map = {s.id_: s for s in self.sensors()}
|
|
56
65
|
|
|
57
66
|
self.assertSensor('timestamp', datetime.strptime('2021-08-31 12:03:02', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
58
67
|
self.assertSensor('vpv1', 320.8, 'V', data)
|
|
@@ -61,9 +70,6 @@ class GW6000_DT_Test(DtMock):
|
|
|
61
70
|
self.assertSensor('vpv2', 324.1, 'V', data)
|
|
62
71
|
self.assertSensor('ipv2', 3.2, 'A', data)
|
|
63
72
|
self.assertSensor('ppv2', 1037, 'W', data)
|
|
64
|
-
self.assertSensor('vpv3', None, 'V', data)
|
|
65
|
-
self.assertSensor('ipv3', None, 'A', data)
|
|
66
|
-
self.assertSensor('ppv3', None, 'W', data)
|
|
67
73
|
self.assertSensor('ppv', 2031, 'W', data)
|
|
68
74
|
self.assertSensor('vline1', 0, 'V', data)
|
|
69
75
|
self.assertSensor('vline2', 0, 'V', data)
|
|
@@ -80,13 +86,14 @@ class GW6000_DT_Test(DtMock):
|
|
|
80
86
|
self.assertSensor('pgrid1', 609, 'W', data)
|
|
81
87
|
self.assertSensor('pgrid2', 597, 'W', data)
|
|
82
88
|
self.assertSensor('pgrid3', 624, 'W', data)
|
|
83
|
-
self.assertSensor('
|
|
89
|
+
self.assertSensor('total_inverter_power', 1835, 'W', data)
|
|
84
90
|
self.assertSensor('work_mode', 1, '', data)
|
|
85
91
|
self.assertSensor('work_mode_label', 'Normal', '', data)
|
|
86
92
|
self.assertSensor('error_codes', 0, '', data)
|
|
87
93
|
self.assertSensor('warning_code', 0, '', data)
|
|
88
94
|
self.assertSensor("apparent_power", -1, "VA", data),
|
|
89
95
|
self.assertSensor("reactive_power", -1, "var", data),
|
|
96
|
+
self.assertSensor("power_factor", 0.0, "", data),
|
|
90
97
|
self.assertSensor('temperature', 41.3, 'C', data)
|
|
91
98
|
self.assertSensor('e_day', 6.0, 'kWh', data)
|
|
92
99
|
self.assertSensor('e_total', 13350.2, 'kWh', data)
|
|
@@ -121,8 +128,9 @@ class GW8K_DT_Test(DtMock):
|
|
|
121
128
|
|
|
122
129
|
def __init__(self, methodName='runTest'):
|
|
123
130
|
DtMock.__init__(self, methodName)
|
|
124
|
-
self.mock_response(self._READ_DEVICE_RUNNING_DATA, 'GW8K-DT_running_data.hex')
|
|
125
131
|
self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW8K-DT_device_info.hex')
|
|
132
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW8K-DT_running_data.hex')
|
|
133
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
126
134
|
|
|
127
135
|
def test_GW8K_DT_device_info(self):
|
|
128
136
|
self.loop.run_until_complete(self.read_device_info())
|
|
@@ -130,13 +138,15 @@ class GW8K_DT_Test(DtMock):
|
|
|
130
138
|
self.assertEqual('00000DTS00000000', self.serial_number)
|
|
131
139
|
self.assertEqual(1010, self.dsp1_version)
|
|
132
140
|
self.assertEqual(1010, self.dsp2_version)
|
|
141
|
+
self.assertEqual(728, self.dsp_svn_version)
|
|
133
142
|
self.assertEqual(8, self.arm_version)
|
|
143
|
+
self.assertEqual(49, self.arm_svn_version)
|
|
134
144
|
self.assertEqual('1010.1010.08', self.firmware)
|
|
135
145
|
|
|
136
146
|
def test_GW8K_DT_runtime_data(self):
|
|
137
147
|
self.loop.run_until_complete(self.read_device_info())
|
|
138
148
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
139
|
-
self.assertEqual(
|
|
149
|
+
self.assertEqual(42, len(data))
|
|
140
150
|
|
|
141
151
|
self.assertSensor('timestamp', datetime.strptime('2021-08-24 16:43:27', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
142
152
|
self.assertSensor('vpv1', 275.5, 'V', data)
|
|
@@ -161,13 +171,14 @@ class GW8K_DT_Test(DtMock):
|
|
|
161
171
|
self.assertSensor('pgrid1', 237, 'W', data)
|
|
162
172
|
self.assertSensor('pgrid2', 240, 'W', data)
|
|
163
173
|
self.assertSensor('pgrid3', 235, 'W', data)
|
|
164
|
-
self.assertSensor('
|
|
174
|
+
self.assertSensor('total_inverter_power', 643, 'W', data)
|
|
165
175
|
self.assertSensor('work_mode', 1, '', data)
|
|
166
176
|
self.assertSensor('work_mode_label', 'Normal', '', data)
|
|
167
177
|
self.assertSensor('error_codes', 0, '', data)
|
|
168
178
|
self.assertSensor('warning_code', 0, '', data)
|
|
169
179
|
self.assertSensor("apparent_power", 0, "VA", data),
|
|
170
180
|
self.assertSensor("reactive_power", 0, "var", data),
|
|
181
|
+
self.assertSensor("power_factor", 0.0, "", data),
|
|
171
182
|
self.assertSensor('temperature', 45.3, 'C', data)
|
|
172
183
|
self.assertSensor('e_day', None, 'kWh', data)
|
|
173
184
|
self.assertSensor('e_total', None, 'kWh', data)
|
|
@@ -195,13 +206,14 @@ class GW5000D_NS_Test(DtMock):
|
|
|
195
206
|
|
|
196
207
|
def __init__(self, methodName='runTest'):
|
|
197
208
|
DtMock.__init__(self, methodName)
|
|
198
|
-
self.mock_response(self._READ_DEVICE_RUNNING_DATA, 'GW5000D-NS_running_data.hex')
|
|
199
209
|
self.mock_response(self._READ_DEVICE_VERSION_INFO, 'Mock_device_info.hex')
|
|
210
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW5000D-NS_running_data.hex')
|
|
211
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
200
212
|
|
|
201
213
|
def test_GW5000D_NS_runtime_data(self):
|
|
202
214
|
self.loop.run_until_complete(self.read_device_info())
|
|
203
215
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
204
|
-
self.assertEqual(
|
|
216
|
+
self.assertEqual(32, len(data))
|
|
205
217
|
|
|
206
218
|
self.assertSensor('timestamp', datetime.strptime('2021-09-06 06:56:01', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
207
219
|
self.assertSensor('vpv1', 224.4, 'V', data)
|
|
@@ -216,13 +228,14 @@ class GW5000D_NS_Test(DtMock):
|
|
|
216
228
|
self.assertSensor('igrid1', 0.0, 'A', data)
|
|
217
229
|
self.assertSensor('fgrid1', 49.97, 'Hz', data)
|
|
218
230
|
self.assertSensor('pgrid1', 0, 'W', data)
|
|
219
|
-
self.assertSensor('
|
|
231
|
+
self.assertSensor('total_inverter_power', 0, 'W', data)
|
|
220
232
|
self.assertSensor('work_mode', 0, '', data)
|
|
221
233
|
self.assertSensor('work_mode_label', 'Wait Mode', '', data)
|
|
222
234
|
self.assertSensor('error_codes', 0, '', data)
|
|
223
235
|
self.assertSensor('warning_code', 0, '', data)
|
|
224
236
|
self.assertSensor("apparent_power", -1, "VA", data),
|
|
225
237
|
self.assertSensor("reactive_power", -1, "var", data),
|
|
238
|
+
self.assertSensor("power_factor", -0.001, "", data),
|
|
226
239
|
self.assertSensor('temperature', 1.4, 'C', data)
|
|
227
240
|
self.assertSensor('e_day', 0, 'kWh', data)
|
|
228
241
|
self.assertSensor('e_total', 881.7, 'kWh', data)
|
|
@@ -250,8 +263,9 @@ class GW5000_MS_Test(DtMock):
|
|
|
250
263
|
|
|
251
264
|
def __init__(self, methodName='runTest'):
|
|
252
265
|
DtMock.__init__(self, methodName)
|
|
253
|
-
self.mock_response(self._READ_DEVICE_RUNNING_DATA, 'GW5000-MS_running_data.hex')
|
|
254
266
|
self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW5000-MS_device_info.hex')
|
|
267
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW5000-MS_running_data.hex')
|
|
268
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
255
269
|
|
|
256
270
|
def test_GW6000_MS_device_info(self):
|
|
257
271
|
self.loop.run_until_complete(self.read_device_info())
|
|
@@ -259,13 +273,15 @@ class GW5000_MS_Test(DtMock):
|
|
|
259
273
|
self.assertEqual('00000MSU00000000', self.serial_number)
|
|
260
274
|
self.assertEqual(12, self.dsp1_version)
|
|
261
275
|
self.assertEqual(12, self.dsp2_version)
|
|
276
|
+
self.assertEqual(65535, self.dsp_svn_version)
|
|
262
277
|
self.assertEqual(16, self.arm_version)
|
|
278
|
+
self.assertEqual(271, self.arm_svn_version)
|
|
263
279
|
self.assertEqual('12.12.10', self.firmware)
|
|
264
280
|
|
|
265
281
|
def test_GW5000_MS_runtime_data(self):
|
|
266
282
|
self.loop.run_until_complete(self.read_device_info())
|
|
267
283
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
268
|
-
self.assertEqual(
|
|
284
|
+
self.assertEqual(35, len(data))
|
|
269
285
|
|
|
270
286
|
self.assertSensor('timestamp', datetime.strptime('2021-10-15 09:03:12', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
271
287
|
self.assertSensor('vpv1', 319.6, 'V', data)
|
|
@@ -283,13 +299,14 @@ class GW5000_MS_Test(DtMock):
|
|
|
283
299
|
self.assertSensor('igrid1', 0.9, 'A', data)
|
|
284
300
|
self.assertSensor('fgrid1', 49.98, 'Hz', data)
|
|
285
301
|
self.assertSensor('pgrid1', 216, 'W', data)
|
|
286
|
-
self.assertSensor('
|
|
302
|
+
self.assertSensor('total_inverter_power', 295, 'W', data)
|
|
287
303
|
self.assertSensor('work_mode', 1, '', data)
|
|
288
304
|
self.assertSensor('work_mode_label', 'Normal', '', data)
|
|
289
305
|
self.assertSensor('error_codes', 0, '', data)
|
|
290
306
|
self.assertSensor('warning_code', 0, '', data)
|
|
291
307
|
self.assertSensor("apparent_power", -1, "VA", data),
|
|
292
308
|
self.assertSensor("reactive_power", -1, "var", data),
|
|
309
|
+
self.assertSensor("power_factor", -0.001, "", data),
|
|
293
310
|
self.assertSensor('temperature', 10.7, 'C', data)
|
|
294
311
|
self.assertSensor('e_day', 0.4, 'kWh', data)
|
|
295
312
|
self.assertSensor('e_total', 6.8, 'kWh', data)
|
|
@@ -308,7 +325,8 @@ class GW10K_MS_30_Test(DtMock):
|
|
|
308
325
|
def __init__(self, methodName='runTest'):
|
|
309
326
|
DtMock.__init__(self, methodName)
|
|
310
327
|
self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW10K-MS-30_device_info.hex')
|
|
311
|
-
self.mock_response(self.
|
|
328
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW10K-MS-30_running_data.hex')
|
|
329
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
312
330
|
|
|
313
331
|
def test_GW10K_MS_30_device_info(self):
|
|
314
332
|
self.loop.run_until_complete(self.read_device_info())
|
|
@@ -316,13 +334,15 @@ class GW10K_MS_30_Test(DtMock):
|
|
|
316
334
|
self.assertEqual('5010KMSC000W0000', self.serial_number)
|
|
317
335
|
self.assertEqual(0, self.dsp1_version)
|
|
318
336
|
self.assertEqual(0, self.dsp2_version)
|
|
337
|
+
self.assertEqual(504, self.dsp_svn_version)
|
|
319
338
|
self.assertEqual(2, self.arm_version)
|
|
339
|
+
self.assertEqual(13, self.arm_svn_version)
|
|
320
340
|
self.assertEqual('0.0.02', self.firmware)
|
|
321
341
|
|
|
322
342
|
def test_GW10K_MS_30_runtime_data(self):
|
|
323
343
|
self.loop.run_until_complete(self.read_device_info())
|
|
324
344
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
325
|
-
self.assertEqual(
|
|
345
|
+
self.assertEqual(35, len(data))
|
|
326
346
|
|
|
327
347
|
self.assertSensor('timestamp', datetime.strptime('2024-01-09 22:08:20', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
328
348
|
self.assertSensor('vpv1', 0.0, 'V', data)
|
|
@@ -340,13 +360,14 @@ class GW10K_MS_30_Test(DtMock):
|
|
|
340
360
|
self.assertSensor('igrid1', 0.0, 'A', data)
|
|
341
361
|
self.assertSensor('fgrid1', 50.0, 'Hz', data)
|
|
342
362
|
self.assertSensor('pgrid1', 0, 'W', data)
|
|
343
|
-
self.assertSensor('
|
|
363
|
+
self.assertSensor('total_inverter_power', 0, 'W', data)
|
|
344
364
|
self.assertSensor('work_mode', 0, '', data)
|
|
345
365
|
self.assertSensor('work_mode_label', 'Wait Mode', '', data)
|
|
346
366
|
self.assertSensor('error_codes', 0, '', data)
|
|
347
367
|
self.assertSensor('warning_code', 0, '', data)
|
|
348
368
|
self.assertSensor("apparent_power", 0, "VA", data),
|
|
349
369
|
self.assertSensor("reactive_power", 0, "var", data),
|
|
370
|
+
self.assertSensor("power_factor", 0.0, "", data),
|
|
350
371
|
self.assertSensor('temperature', 24.3, 'C', data)
|
|
351
372
|
self.assertSensor('e_day', 71.8, 'kWh', data)
|
|
352
373
|
self.assertSensor('e_total', 3433.4, 'kWh', data)
|
|
@@ -364,12 +385,13 @@ class GW10K_MS_TCP_Test(DtMock):
|
|
|
364
385
|
|
|
365
386
|
def __init__(self, methodName='runTest'):
|
|
366
387
|
DtMock.__init__(self, methodName, 502)
|
|
367
|
-
self.mock_response(self.
|
|
388
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW10K-MS-30_tcp_running_data.hex')
|
|
389
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
368
390
|
|
|
369
391
|
def test_GW10K_MS_TCP_runtime_data(self):
|
|
370
392
|
self.loop.run_until_complete(self.read_device_info())
|
|
371
393
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
372
|
-
self.assertEqual(
|
|
394
|
+
self.assertEqual(42, len(data))
|
|
373
395
|
|
|
374
396
|
self.assertSensor('timestamp', datetime.strptime('2024-06-02 09:07:17', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
375
397
|
self.assertSensor('vpv1', 400.6, 'V', data)
|
|
@@ -394,13 +416,14 @@ class GW10K_MS_TCP_Test(DtMock):
|
|
|
394
416
|
self.assertSensor('pgrid1', 5955, 'W', data)
|
|
395
417
|
self.assertSensor('pgrid2', 0, 'W', data)
|
|
396
418
|
self.assertSensor('pgrid3', 0, 'W', data)
|
|
397
|
-
self.assertSensor('
|
|
419
|
+
self.assertSensor('total_inverter_power', 5914, 'W', data)
|
|
398
420
|
self.assertSensor('work_mode', 1, '', data)
|
|
399
421
|
self.assertSensor('work_mode_label', 'Normal', '', data)
|
|
400
422
|
self.assertSensor('error_codes', 0, '', data)
|
|
401
423
|
self.assertSensor('warning_code', 0, '', data)
|
|
402
424
|
self.assertSensor('apparent_power', 5957, 'VA', data)
|
|
403
425
|
self.assertSensor('reactive_power', -6, 'var', data)
|
|
426
|
+
self.assertSensor("power_factor", 0.999, "", data),
|
|
404
427
|
self.assertSensor('temperature', 36.0, 'C', data)
|
|
405
428
|
self.assertSensor('e_day', 4.3, 'kWh', data)
|
|
406
429
|
self.assertSensor('e_total', 998.2, 'kWh', data)
|
|
@@ -418,8 +441,9 @@ class GW20KAU_DT_Test(DtMock):
|
|
|
418
441
|
|
|
419
442
|
def __init__(self, methodName='runTest'):
|
|
420
443
|
DtMock.__init__(self, methodName)
|
|
421
|
-
self.mock_response(self._READ_DEVICE_RUNNING_DATA, 'GW20KAU-DT_running_data.hex')
|
|
422
444
|
self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW20KAU-DT_device_info.hex')
|
|
445
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW20KAU-DT_running_data.hex')
|
|
446
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
423
447
|
|
|
424
448
|
def test_GW20KAU_DT_device_info(self):
|
|
425
449
|
self.loop.run_until_complete(self.read_device_info())
|
|
@@ -427,13 +451,15 @@ class GW20KAU_DT_Test(DtMock):
|
|
|
427
451
|
self.assertEqual('0000KDTA00000000', self.serial_number)
|
|
428
452
|
self.assertEqual(15, self.dsp1_version)
|
|
429
453
|
self.assertEqual(15, self.dsp2_version)
|
|
454
|
+
self.assertEqual(1099, self.dsp_svn_version)
|
|
430
455
|
self.assertEqual(16, self.arm_version)
|
|
456
|
+
self.assertEqual(187, self.arm_svn_version)
|
|
431
457
|
self.assertEqual('15.15.10', self.firmware)
|
|
432
458
|
|
|
433
459
|
def test_GW20KAU_DT_runtime_data(self):
|
|
434
460
|
self.loop.run_until_complete(self.read_device_info())
|
|
435
461
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
436
|
-
self.assertEqual(
|
|
462
|
+
self.assertEqual(42, len(data))
|
|
437
463
|
|
|
438
464
|
self.assertSensor('timestamp', datetime.strptime('2022-10-21 19:23:42', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
439
465
|
self.assertSensor('vpv1', 390.5, 'V', data)
|
|
@@ -458,13 +484,14 @@ class GW20KAU_DT_Test(DtMock):
|
|
|
458
484
|
self.assertSensor('pgrid1', 1628, 'W', data)
|
|
459
485
|
self.assertSensor('pgrid2', 1655, 'W', data)
|
|
460
486
|
self.assertSensor('pgrid3', 1621, 'W', data)
|
|
461
|
-
self.assertSensor('
|
|
487
|
+
self.assertSensor('total_inverter_power', 4957, 'W', data)
|
|
462
488
|
self.assertSensor('work_mode', 1, '', data)
|
|
463
489
|
self.assertSensor('work_mode_label', 'Normal', '', data)
|
|
464
490
|
self.assertSensor('error_codes', 0, '', data)
|
|
465
491
|
self.assertSensor('warning_code', 0, '', data)
|
|
466
492
|
self.assertSensor("apparent_power", 0, "VA", data),
|
|
467
493
|
self.assertSensor("reactive_power", 205, "var", data),
|
|
494
|
+
self.assertSensor("power_factor", 0.999, "", data),
|
|
468
495
|
self.assertSensor('temperature', 36.4, 'C', data)
|
|
469
496
|
self.assertSensor('e_day', 19.8, 'kWh', data)
|
|
470
497
|
self.assertSensor('e_total', 4304.8, 'kWh', data)
|
|
@@ -482,8 +509,9 @@ class GW17K_DT_Test(DtMock):
|
|
|
482
509
|
|
|
483
510
|
def __init__(self, methodName='runTest'):
|
|
484
511
|
DtMock.__init__(self, methodName)
|
|
485
|
-
self.mock_response(self._READ_DEVICE_RUNNING_DATA, 'GW17K-DT_running_data.hex')
|
|
486
512
|
self.mock_response(self._READ_DEVICE_VERSION_INFO, 'GW17K-DT_device_info.hex')
|
|
513
|
+
self.mock_response(self._READ_RUNNING_DATA, 'GW17K-DT_running_data.hex')
|
|
514
|
+
self.mock_response(self._READ_METER_DATA, ILLEGAL_DATA_ADDRESS)
|
|
487
515
|
|
|
488
516
|
def test_GW20KAU_DT_device_info(self):
|
|
489
517
|
self.loop.run_until_complete(self.read_device_info())
|
|
@@ -491,13 +519,15 @@ class GW17K_DT_Test(DtMock):
|
|
|
491
519
|
self.assertEqual('5017KDTT00BW0000', self.serial_number)
|
|
492
520
|
self.assertEqual(12, self.dsp1_version)
|
|
493
521
|
self.assertEqual(12, self.dsp2_version)
|
|
522
|
+
self.assertEqual(931, self.dsp_svn_version)
|
|
494
523
|
self.assertEqual(13, self.arm_version)
|
|
524
|
+
self.assertEqual(130, self.arm_svn_version)
|
|
495
525
|
self.assertEqual('12.12.0d', self.firmware)
|
|
496
526
|
|
|
497
527
|
def test_GW20KAU_DT_runtime_data(self):
|
|
498
528
|
self.loop.run_until_complete(self.read_device_info())
|
|
499
529
|
data = self.loop.run_until_complete(self.read_runtime_data())
|
|
500
|
-
self.assertEqual(
|
|
530
|
+
self.assertEqual(42, len(data))
|
|
501
531
|
|
|
502
532
|
self.assertSensor('timestamp', datetime.strptime('2024-05-20 10:35:55', '%Y-%m-%d %H:%M:%S'), '', data)
|
|
503
533
|
self.assertSensor('vpv1', 540.0, 'V', data)
|
|
@@ -522,13 +552,14 @@ class GW17K_DT_Test(DtMock):
|
|
|
522
552
|
self.assertSensor('pgrid1', 4166, 'W', data)
|
|
523
553
|
self.assertSensor('pgrid2', 4170, 'W', data)
|
|
524
554
|
self.assertSensor('pgrid3', 4153, 'W', data)
|
|
525
|
-
self.assertSensor('
|
|
555
|
+
self.assertSensor('total_inverter_power', 12470, 'W', data)
|
|
526
556
|
self.assertSensor('work_mode', 1, '', data)
|
|
527
557
|
self.assertSensor('work_mode_label', 'Normal', '', data)
|
|
528
558
|
self.assertSensor('error_codes', 0, '', data)
|
|
529
559
|
self.assertSensor('warning_code', 0, '', data)
|
|
530
560
|
self.assertSensor('apparent_power', 0, 'VA', data)
|
|
531
561
|
self.assertSensor('reactive_power', 0, 'var', data)
|
|
562
|
+
self.assertSensor("power_factor", 0.0, "", data),
|
|
532
563
|
self.assertSensor('temperature', 45.7, 'C', data)
|
|
533
564
|
self.assertSensor('e_day', 29.3, 'kWh', data)
|
|
534
565
|
self.assertSensor('e_total', 29984.4, 'kWh', data)
|
|
@@ -36,14 +36,14 @@ class TestUDPClientProtocol(TestCase):
|
|
|
36
36
|
mock_loop = mock.Mock()
|
|
37
37
|
mock_get_event_loop.return_value = mock_loop
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
self.protocol.
|
|
39
|
+
mock_timeout_mechanism = mock.Mock()
|
|
40
|
+
self.protocol._timeout_mechanism = mock_timeout_mechanism
|
|
41
41
|
self.protocol.connection_made(transport)
|
|
42
42
|
self.protocol._send_request(self.protocol.command, self.protocol.response_future)
|
|
43
43
|
|
|
44
44
|
transport.sendto.assert_called_with(self.protocol.command.request)
|
|
45
45
|
mock_get_event_loop.assert_called()
|
|
46
|
-
mock_loop.call_later.assert_called_with(1,
|
|
46
|
+
mock_loop.call_later.assert_called_with(1, mock_timeout_mechanism)
|
|
47
47
|
|
|
48
48
|
def test_connection_lost(self):
|
|
49
49
|
self.protocol.response_future.done.return_value = True
|
|
@@ -59,41 +59,41 @@ class TestUDPClientProtocol(TestCase):
|
|
|
59
59
|
self.protocol._transport = mock.Mock()
|
|
60
60
|
self.protocol._send_request = mock.Mock()
|
|
61
61
|
self.protocol.response_future.done.return_value = True
|
|
62
|
-
self.protocol.
|
|
62
|
+
self.protocol._timeout_mechanism()
|
|
63
63
|
|
|
64
64
|
# self.protocol._transport.close.assert_called()
|
|
65
65
|
self.protocol._send_request.assert_not_called()
|
|
66
66
|
|
|
67
|
-
@mock.patch('goodwe.protocol.asyncio.get_running_loop')
|
|
68
|
-
def test_retry_mechanism_two_retries(self, mock_get_event_loop):
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
@mock.patch('goodwe.protocol.asyncio.get_running_loop')
|
|
84
|
-
def test_retry_mechanism_max_retries(self, mock_get_event_loop):
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
67
|
+
# @mock.patch('goodwe.protocol.asyncio.get_running_loop')
|
|
68
|
+
# def test_retry_mechanism_two_retries(self, mock_get_event_loop):
|
|
69
|
+
# def call_later(_: int, retry_func: Callable):
|
|
70
|
+
# retry_func()
|
|
71
|
+
#
|
|
72
|
+
# mock_loop = mock.Mock()
|
|
73
|
+
# mock_get_event_loop.return_value = mock_loop
|
|
74
|
+
# mock_loop.call_later = call_later
|
|
75
|
+
#
|
|
76
|
+
# self.protocol._transport = mock.Mock()
|
|
77
|
+
# self.protocol.response_future.done.side_effect = [False, False, True, False]
|
|
78
|
+
# self.protocol._timeout_mechanism()
|
|
79
|
+
#
|
|
80
|
+
# # self.protocol._transport.close.assert_called()
|
|
81
|
+
# self.assertEqual(self.protocol._retry, 2)
|
|
82
|
+
|
|
83
|
+
# @mock.patch('goodwe.protocol.asyncio.get_running_loop')
|
|
84
|
+
# def test_retry_mechanism_max_retries(self, mock_get_event_loop):
|
|
85
|
+
# def call_later(_: int, retry_func: Callable):
|
|
86
|
+
# retry_func()
|
|
87
|
+
#
|
|
88
|
+
# mock_loop = mock.Mock()
|
|
89
|
+
# mock_get_event_loop.return_value = mock_loop
|
|
90
|
+
# mock_loop.call_later = call_later
|
|
91
|
+
#
|
|
92
|
+
# self.protocol._transport = mock.Mock()
|
|
93
|
+
# self.protocol.response_future.done.side_effect = [False, False, False, False, False]
|
|
94
|
+
# self.protocol._timeout_mechanism()
|
|
95
|
+
# self.protocol.response_future.set_exception.assert_called_once_with(MaxRetriesException)
|
|
96
|
+
# self.assertEqual(self.protocol._retry, 3)
|
|
97
97
|
|
|
98
98
|
def test_modbus_rtu_read_command(self):
|
|
99
99
|
command = ModbusRtuReadCommand(0xf7, 0x88b8, 0x0021)
|
goodwe-0.4.7/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.4.7
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|