goodwe 0.3.0__tar.gz → 0.3.2__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.3.0/goodwe.egg-info → goodwe-0.3.2}/PKG-INFO +1 -1
- goodwe-0.3.2/VERSION +1 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/es.py +40 -41
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/et.py +54 -46
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/protocol.py +20 -1
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/sensor.py +214 -105
- {goodwe-0.3.0 → goodwe-0.3.2/goodwe.egg-info}/PKG-INFO +1 -1
- {goodwe-0.3.0 → goodwe-0.3.2}/tests/test_dt.py +9 -9
- {goodwe-0.3.0 → goodwe-0.3.2}/tests/test_et.py +12 -5
- {goodwe-0.3.0 → goodwe-0.3.2}/tests/test_sensor.py +92 -4
- goodwe-0.3.0/VERSION +0 -1
- {goodwe-0.3.0 → goodwe-0.3.2}/LICENSE +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/README.md +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/__init__.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/const.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/dt.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/exceptions.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/inverter.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/modbus.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe/model.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe.egg-info/SOURCES.txt +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe.egg-info/dependency_links.txt +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/goodwe.egg-info/top_level.txt +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/pyproject.toml +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/setup.cfg +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/tests/test_es.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/tests/test_modbus.py +0 -0
- {goodwe-0.3.0 → goodwe-0.3.2}/tests/test_protocol.py +0 -0
goodwe-0.3.2/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.2
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Tuple
|
|
4
|
+
from typing import Tuple
|
|
5
5
|
|
|
6
6
|
from .exceptions import InverterError
|
|
7
7
|
from .inverter import Inverter
|
|
@@ -67,7 +67,7 @@ class ES(Inverter):
|
|
|
67
67
|
Voltage("vgrid", 34, "On-grid Voltage", Kind.AC),
|
|
68
68
|
Current("igrid", 36, "On-grid Current", Kind.AC),
|
|
69
69
|
Calculated("pgrid",
|
|
70
|
-
lambda data: abs(
|
|
70
|
+
lambda data: abs(read_bytes2_signed(data, 38)) * (-1 if read_byte(data, 80) == 2 else 1),
|
|
71
71
|
"On-grid Export Power", "W", Kind.AC),
|
|
72
72
|
Frequency("fgrid", 40, "On-grid Frequency", Kind.AC),
|
|
73
73
|
Byte("grid_mode", 42, "Work Mode code", "", Kind.GRID),
|
|
@@ -87,7 +87,7 @@ class ES(Inverter):
|
|
|
87
87
|
Energy("e_day", 67, "Today's PV Generation", Kind.PV),
|
|
88
88
|
Energy("e_load_day", 69, "Today's Load", Kind.AC),
|
|
89
89
|
Energy4("e_load_total", 71, "Total Load", Kind.AC),
|
|
90
|
-
|
|
90
|
+
PowerS("total_power", 75, "Total Power", Kind.AC), # modbus 0x52c
|
|
91
91
|
Byte("effective_work_mode", 77, "Effective Work Mode code"),
|
|
92
92
|
Integer("effective_relay_control", 78, "Effective Relay Control", "", None),
|
|
93
93
|
Byte("grid_in_out", 80, "On-grid Mode code", "", Kind.GRID),
|
|
@@ -121,7 +121,7 @@ class ES(Inverter):
|
|
|
121
121
|
round(read_voltage(data, 5) * read_current(data, 7)) +
|
|
122
122
|
(abs(round(read_voltage(data, 10) * read_current(data, 18))) *
|
|
123
123
|
(-1 if read_byte(data, 30) == 3 else 1)) -
|
|
124
|
-
(abs(
|
|
124
|
+
(abs(read_bytes2_signed(data, 38)) * (-1 if read_byte(data, 80) == 2 else 1)),
|
|
125
125
|
"House Consumption", "W", Kind.AC),
|
|
126
126
|
)
|
|
127
127
|
|
|
@@ -220,17 +220,20 @@ class ES(Inverter):
|
|
|
220
220
|
setting: Sensor | None = self._settings.get(setting_id)
|
|
221
221
|
if not setting:
|
|
222
222
|
raise ValueError(f'Unknown setting "{setting_id}"')
|
|
223
|
-
|
|
224
|
-
if self._is_modbus_setting(setting):
|
|
225
|
-
response = await self._read_from_socket(ModbusReadCommand(self.comm_addr, setting.offset, count))
|
|
226
|
-
return setting.read_value(response)
|
|
227
|
-
else:
|
|
228
|
-
response = await self._read_from_socket(Aa55ReadCommand(setting.offset, count))
|
|
229
|
-
return setting.read_value(response)
|
|
223
|
+
return await self._read_setting(setting)
|
|
230
224
|
else:
|
|
231
225
|
all_settings = await self.read_settings_data()
|
|
232
226
|
return all_settings.get(setting_id)
|
|
233
227
|
|
|
228
|
+
async def _read_setting(self, setting: Sensor) -> Any:
|
|
229
|
+
count = (setting.size_ + (setting.size_ % 2)) // 2
|
|
230
|
+
if self._is_modbus_setting(setting):
|
|
231
|
+
response = await self._read_from_socket(ModbusReadCommand(self.comm_addr, setting.offset, count))
|
|
232
|
+
return setting.read_value(response)
|
|
233
|
+
else:
|
|
234
|
+
response = await self._read_from_socket(Aa55ReadCommand(setting.offset, count))
|
|
235
|
+
return setting.read_value(response)
|
|
236
|
+
|
|
234
237
|
async def write_setting(self, setting_id: str, value: Any):
|
|
235
238
|
if setting_id == 'time':
|
|
236
239
|
await self._read_from_socket(
|
|
@@ -240,27 +243,30 @@ class ES(Inverter):
|
|
|
240
243
|
setting: Sensor | None = self._settings.get(setting_id)
|
|
241
244
|
if not setting:
|
|
242
245
|
raise ValueError(f'Unknown setting "{setting_id}"')
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
await self._write_setting(setting, value)
|
|
247
|
+
|
|
248
|
+
async def _write_setting(self, setting: Sensor, value: Any):
|
|
249
|
+
if setting.size_ == 1:
|
|
250
|
+
# modbus can address/store only 16 bit values, read the other 8 bytes
|
|
251
|
+
if self._is_modbus_setting(setting):
|
|
252
|
+
response = await self._read_from_socket(ModbusReadCommand(self.comm_addr, setting.offset, 1))
|
|
253
|
+
raw_value = setting.encode_value(value, response.response_data()[0:2])
|
|
251
254
|
else:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
response = await self._read_from_socket(Aa55ReadCommand(setting.offset, 1))
|
|
256
|
+
raw_value = setting.encode_value(value, response.response_data()[2:4])
|
|
257
|
+
else:
|
|
258
|
+
raw_value = setting.encode_value(value)
|
|
259
|
+
if len(raw_value) <= 2:
|
|
260
|
+
value = int.from_bytes(raw_value, byteorder="big", signed=True)
|
|
261
|
+
if self._is_modbus_setting(setting):
|
|
262
|
+
await self._read_from_socket(ModbusWriteCommand(self.comm_addr, setting.offset, value))
|
|
263
|
+
else:
|
|
264
|
+
await self._read_from_socket(Aa55WriteCommand(setting.offset, value))
|
|
265
|
+
else:
|
|
266
|
+
if self._is_modbus_setting(setting):
|
|
267
|
+
await self._read_from_socket(ModbusWriteMultiCommand(self.comm_addr, setting.offset, raw_value))
|
|
259
268
|
else:
|
|
260
|
-
|
|
261
|
-
await self._read_from_socket(ModbusWriteMultiCommand(self.comm_addr, setting.offset, raw_value))
|
|
262
|
-
else:
|
|
263
|
-
await self._read_from_socket(Aa55WriteMultiCommand(setting.offset, raw_value))
|
|
269
|
+
await self._read_from_socket(Aa55WriteMultiCommand(setting.offset, raw_value))
|
|
264
270
|
|
|
265
271
|
async def read_settings_data(self) -> Dict[str, Any]:
|
|
266
272
|
response = await self._read_from_socket(self._READ_DEVICE_SETTINGS_DATA)
|
|
@@ -313,7 +319,8 @@ class ES(Inverter):
|
|
|
313
319
|
raise ValueError()
|
|
314
320
|
if eco_mode_soc < 0 or eco_mode_soc > 100:
|
|
315
321
|
raise ValueError()
|
|
316
|
-
eco_mode: EcoMode = self.
|
|
322
|
+
eco_mode: EcoMode | Sensor = self._settings.get('eco_mode_1')
|
|
323
|
+
await self._read_setting(eco_mode)
|
|
317
324
|
if operation_mode == OperationMode.ECO_CHARGE:
|
|
318
325
|
await self.write_setting('eco_mode_1', eco_mode.encode_charge(eco_mode_power, eco_mode_soc))
|
|
319
326
|
else:
|
|
@@ -327,7 +334,7 @@ class ES(Inverter):
|
|
|
327
334
|
return await self.read_setting('dod')
|
|
328
335
|
|
|
329
336
|
async def set_ongrid_battery_dod(self, dod: int) -> None:
|
|
330
|
-
if 0 <= dod <=
|
|
337
|
+
if 0 <= dod <= 100:
|
|
331
338
|
await self._read_from_socket(Aa55WriteCommand(0x560, 100 - dod))
|
|
332
339
|
|
|
333
340
|
async def _reset_inverter(self) -> None:
|
|
@@ -427,13 +434,5 @@ class ES(Inverter):
|
|
|
427
434
|
async def _set_work_mode(self, mode: int) -> None:
|
|
428
435
|
await self._read_from_socket(Aa55ProtocolCommand("035901" + "{:02x}".format(mode), "03D9"))
|
|
429
436
|
|
|
430
|
-
def _convert_eco_mode(self, sensor: Sensor) -> Sensor | EcoMode:
|
|
431
|
-
if EcoModeV1 == type(sensor) and self._supports_eco_mode_v2():
|
|
432
|
-
return cast(EcoModeV1, sensor).as_eco_mode_v2()
|
|
433
|
-
elif EcoModeV2 == type(sensor) and not self._supports_eco_mode_v2():
|
|
434
|
-
return cast(EcoModeV2, sensor).as_eco_mode_v1()
|
|
435
|
-
else:
|
|
436
|
-
return sensor
|
|
437
|
-
|
|
438
437
|
def _is_modbus_setting(self, sensor: Sensor) -> bool:
|
|
439
|
-
return
|
|
438
|
+
return sensor.offset > 30000
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Tuple
|
|
4
|
+
from typing import Tuple
|
|
5
5
|
|
|
6
6
|
from .exceptions import RequestRejectedException
|
|
7
7
|
from .inverter import Inverter
|
|
@@ -52,23 +52,23 @@ class ET(Inverter):
|
|
|
52
52
|
Current("igrid", 35122, "On-grid L1 Current", Kind.AC),
|
|
53
53
|
Frequency("fgrid", 35123, "On-grid L1 Frequency", Kind.AC),
|
|
54
54
|
# 35124 reserved
|
|
55
|
-
|
|
55
|
+
PowerS("pgrid", 35125, "On-grid L1 Power", Kind.AC),
|
|
56
56
|
Voltage("vgrid2", 35126, "On-grid L2 Voltage", Kind.AC),
|
|
57
57
|
Current("igrid2", 35127, "On-grid L2 Current", Kind.AC),
|
|
58
58
|
Frequency("fgrid2", 35128, "On-grid L2 Frequency", Kind.AC),
|
|
59
59
|
# 35129 reserved
|
|
60
|
-
|
|
60
|
+
PowerS("pgrid2", 35130, "On-grid L2 Power", Kind.AC),
|
|
61
61
|
Voltage("vgrid3", 35131, "On-grid L3 Voltage", Kind.AC),
|
|
62
62
|
Current("igrid3", 35132, "On-grid L3 Current", Kind.AC),
|
|
63
63
|
Frequency("fgrid3", 35133, "On-grid L3 Frequency", Kind.AC),
|
|
64
64
|
# 35134 reserved
|
|
65
|
-
|
|
65
|
+
PowerS("pgrid3", 35135, "On-grid L3 Power", Kind.AC),
|
|
66
66
|
Integer("grid_mode", 35136, "Grid Mode code", "", Kind.PV),
|
|
67
67
|
Enum2("grid_mode_label", 35136, GRID_MODES, "Grid Mode", Kind.PV),
|
|
68
68
|
# 35137 reserved
|
|
69
|
-
|
|
69
|
+
PowerS("total_inverter_power", 35138, "Total Power", Kind.AC),
|
|
70
70
|
# 35139 reserved
|
|
71
|
-
|
|
71
|
+
PowerS("active_power", 35140, "Active Power", Kind.GRID),
|
|
72
72
|
Calculated("grid_in_out",
|
|
73
73
|
lambda data: read_grid_mode(data, 35140),
|
|
74
74
|
"On-grid Mode code", "", Kind.GRID),
|
|
@@ -84,29 +84,29 @@ class ET(Inverter):
|
|
|
84
84
|
Frequency("backup_f1", 35147, "Back-up L1 Frequency", Kind.UPS),
|
|
85
85
|
Integer("load_mode1", 35148, "Load Mode L1"),
|
|
86
86
|
# 35149 reserved
|
|
87
|
-
|
|
87
|
+
PowerS("backup_p1", 35150, "Back-up L1 Power", Kind.UPS),
|
|
88
88
|
Voltage("backup_v2", 35151, "Back-up L2 Voltage", Kind.UPS),
|
|
89
89
|
Current("backup_i2", 35152, "Back-up L2 Current", Kind.UPS),
|
|
90
90
|
Frequency("backup_f2", 35153, "Back-up L2 Frequency", Kind.UPS),
|
|
91
91
|
Integer("load_mode2", 35154, "Load Mode L2"),
|
|
92
92
|
# 35155 reserved
|
|
93
|
-
|
|
93
|
+
PowerS("backup_p2", 35156, "Back-up L2 Power", Kind.UPS),
|
|
94
94
|
Voltage("backup_v3", 35157, "Back-up L3 Voltage", Kind.UPS),
|
|
95
95
|
Current("backup_i3", 35158, "Back-up L3 Current", Kind.UPS),
|
|
96
96
|
Frequency("backup_f3", 35159, "Back-up L3 Frequency", Kind.UPS),
|
|
97
97
|
Integer("load_mode3", 35160, "Load Mode L3"),
|
|
98
98
|
# 35161 reserved
|
|
99
|
-
|
|
99
|
+
PowerS("backup_p3", 35162, "Back-up L3 Power", Kind.UPS),
|
|
100
100
|
# 35163 reserved
|
|
101
|
-
|
|
101
|
+
PowerS("load_p1", 35164, "Load L1", Kind.AC),
|
|
102
102
|
# 35165 reserved
|
|
103
|
-
|
|
103
|
+
PowerS("load_p2", 35166, "Load L2", Kind.AC),
|
|
104
104
|
# 35167 reserved
|
|
105
|
-
|
|
105
|
+
PowerS("load_p3", 35168, "Load L3", Kind.AC),
|
|
106
106
|
# 35169 reserved
|
|
107
|
-
|
|
107
|
+
PowerS("backup_ptotal", 35170, "Back-up Load", Kind.UPS),
|
|
108
108
|
# 35171 reserved
|
|
109
|
-
|
|
109
|
+
PowerS("load_ptotal", 35172, "Load", Kind.AC),
|
|
110
110
|
Integer("ups_load", 35173, "Ups Load", "%", Kind.UPS),
|
|
111
111
|
Temp("temperature_air", 35174, "Inverter Temperature (Air)", Kind.AC),
|
|
112
112
|
Temp("temperature_module", 35175, "Inverter Temperature (Module)"),
|
|
@@ -115,8 +115,8 @@ class ET(Inverter):
|
|
|
115
115
|
Voltage("bus_voltage", 35178, "Bus Voltage", None),
|
|
116
116
|
Voltage("nbus_voltage", 35179, "NBus Voltage", None),
|
|
117
117
|
Voltage("vbattery1", 35180, "Battery Voltage", Kind.BAT),
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
CurrentS("ibattery1", 35181, "Battery Current", Kind.BAT),
|
|
119
|
+
Power4S("pbattery1", 35182, "Battery Power", Kind.BAT),
|
|
120
120
|
Integer("battery_mode", 35184, "Battery Mode code", "", Kind.BAT),
|
|
121
121
|
Enum2("battery_mode_label", 35184, BATTERY_MODES, "Battery Mode", Kind.BAT),
|
|
122
122
|
Integer("warning_code", 35185, "Warning code"),
|
|
@@ -149,8 +149,8 @@ class ET(Inverter):
|
|
|
149
149
|
read_bytes4(data, 35109) +
|
|
150
150
|
read_bytes4(data, 35113) +
|
|
151
151
|
read_bytes4(data, 35117) +
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
read_bytes4_signed(data, 35182) -
|
|
153
|
+
read_bytes2_signed(data, 35140),
|
|
154
154
|
"House Consumption", "W", Kind.AC),
|
|
155
155
|
)
|
|
156
156
|
|
|
@@ -226,10 +226,10 @@ class ET(Inverter):
|
|
|
226
226
|
Integer("manufacture_code", 36002, "Manufacture Code"),
|
|
227
227
|
Integer("meter_test_status", 36003, "Meter Test Status"), # 1: correct,2: reverse,3: incorrect,0: not checked
|
|
228
228
|
Integer("meter_comm_status", 36004, "Meter Communication Status"), # 1 OK, 0 NotOK
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
PowerS("active_power1", 36005, "Active Power L1", Kind.GRID),
|
|
230
|
+
PowerS("active_power2", 36006, "Active Power L2", Kind.GRID),
|
|
231
|
+
PowerS("active_power3", 36007, "Active Power L3", Kind.GRID),
|
|
232
|
+
PowerS("active_power_total", 36008, "Active Power Total", Kind.GRID),
|
|
233
233
|
Reactive("reactive_power_total", 36009, "Reactive Power Total", Kind.GRID),
|
|
234
234
|
Decimal("meter_power_factor1", 36010, 1000, "Meter Power Factor L1", "", Kind.GRID),
|
|
235
235
|
Decimal("meter_power_factor2", 36011, 1000, "Meter Power Factor L2", "", Kind.GRID),
|
|
@@ -238,10 +238,10 @@ class ET(Inverter):
|
|
|
238
238
|
Frequency("meter_freq", 36014, "Meter Frequency", Kind.GRID),
|
|
239
239
|
Float("meter_e_total_exp", 36015, 1000, "Meter Total Energy (export)", "kWh", Kind.GRID),
|
|
240
240
|
Float("meter_e_total_imp", 36017, 1000, "Meter Total Energy (import)", "kWh", Kind.GRID),
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
Power4S("meter_active_power1", 36019, "Meter Active Power L1", Kind.GRID),
|
|
242
|
+
Power4S("meter_active_power2", 36021, "Meter Active Power L2", Kind.GRID),
|
|
243
|
+
Power4S("meter_active_power3", 36023, "Meter Active Power L3", Kind.GRID),
|
|
244
|
+
Power4S("meter_active_power_total", 36025, "Meter Active Power Total", Kind.GRID),
|
|
245
245
|
Reactive4("meter_reactive_power1", 36027, "Meter Reactive Power L1", Kind.GRID),
|
|
246
246
|
Reactive4("meter_reactive_power2", 36029, "Meter Reactive Power L2", Kind.GRID),
|
|
247
247
|
Reactive4("meter_reactive_power3", 36031, "Meter Reactive Power L2", Kind.GRID),
|
|
@@ -253,7 +253,7 @@ class ET(Inverter):
|
|
|
253
253
|
Integer("meter_type", 36043, "Meter Type", "", Kind.GRID), # (0: Single phase, 1: 3P3W, 2: 3P4W, 3: HomeKit)
|
|
254
254
|
Integer("meter_sw_version", 36044, "Meter Software Version", "", Kind.GRID),
|
|
255
255
|
# Sensors added in some ARM fw update, read when flag _has_meter_extended is on
|
|
256
|
-
|
|
256
|
+
Power4S("meter2_active_power", 36045, "Meter 2 Active Power", Kind.GRID),
|
|
257
257
|
Float("meter2_e_total_exp", 36047, 1000, "Meter 2 Total Energy (export)", "kWh", Kind.GRID),
|
|
258
258
|
Float("meter2_e_total_imp", 36049, 1000, "Meter 2 Total Energy (import)", "kWh", Kind.GRID),
|
|
259
259
|
Integer("meter2_comm_status", 36051, "Meter 2 Communication Status"),
|
|
@@ -411,6 +411,8 @@ class ET(Inverter):
|
|
|
411
411
|
self._READ_BATTERY_INFO: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x9088, 0x0018)
|
|
412
412
|
self._READ_BATTERY2_INFO: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x9858, 0x0016)
|
|
413
413
|
self._READ_MPPT_DATA: ProtocolCommand = ModbusReadCommand(self.comm_addr, 0x89e5, 0x3d)
|
|
414
|
+
self._has_eco_mode_v2: bool = True
|
|
415
|
+
self._has_peak_shaving: bool = True
|
|
414
416
|
self._has_battery: bool = True
|
|
415
417
|
self._has_battery2: bool = False
|
|
416
418
|
self._has_meter_extended: bool = False
|
|
@@ -422,12 +424,6 @@ class ET(Inverter):
|
|
|
422
424
|
self._sensors_mppt = self.__all_sensors_mppt
|
|
423
425
|
self._settings: dict[str, Sensor] = {s.id_: s for s in self.__all_settings}
|
|
424
426
|
|
|
425
|
-
def _supports_eco_mode_v2(self) -> bool:
|
|
426
|
-
return self.arm_version >= 19 or 'ETU' not in self.serial_number
|
|
427
|
-
|
|
428
|
-
def _supports_peak_shaving(self) -> bool:
|
|
429
|
-
return self.arm_version >= 22 or 'ETU' not in self.serial_number
|
|
430
|
-
|
|
431
427
|
@staticmethod
|
|
432
428
|
def _single_phase_only(s: Sensor) -> bool:
|
|
433
429
|
"""Filter to exclude phase2/3 sensors on single phase inverters"""
|
|
@@ -474,10 +470,23 @@ class ET(Inverter):
|
|
|
474
470
|
else:
|
|
475
471
|
self._sensors_meter = tuple(filter(self._not_extended_meter, self._sensors_meter))
|
|
476
472
|
|
|
477
|
-
|
|
473
|
+
# Check and add EcoModeV2 settings added in (ETU fw 19)
|
|
474
|
+
try:
|
|
475
|
+
await self._read_from_socket(ModbusReadCommand(self.comm_addr, 47547, 6))
|
|
478
476
|
self._settings.update({s.id_: s for s in self.__settings_arm_fw_19})
|
|
479
|
-
|
|
477
|
+
except RequestRejectedException as ex:
|
|
478
|
+
if ex.message == 'ILLEGAL DATA ADDRESS':
|
|
479
|
+
logger.debug("Cannot read EcoModeV2 settings, using to EcoModeV1.")
|
|
480
|
+
self._has_eco_mode_v2 = False
|
|
481
|
+
|
|
482
|
+
# Check and add Peak Shaving settings added in (ETU fw 22)
|
|
483
|
+
try:
|
|
484
|
+
await self._read_from_socket(ModbusReadCommand(self.comm_addr, 47589, 6))
|
|
480
485
|
self._settings.update({s.id_: s for s in self.__settings_arm_fw_22})
|
|
486
|
+
except RequestRejectedException as ex:
|
|
487
|
+
if ex.message == 'ILLEGAL DATA ADDRESS':
|
|
488
|
+
logger.debug("Cannot read PeakShaving setting, disabling it.")
|
|
489
|
+
self._has_peak_shaving = False
|
|
481
490
|
|
|
482
491
|
async def read_runtime_data(self) -> Dict[str, Any]:
|
|
483
492
|
response = await self._read_from_socket(self._READ_RUNNING_DATA)
|
|
@@ -541,6 +550,9 @@ class ET(Inverter):
|
|
|
541
550
|
setting = self._settings.get(setting_id)
|
|
542
551
|
if not setting:
|
|
543
552
|
raise ValueError(f'Unknown setting "{setting_id}"')
|
|
553
|
+
return await self._read_setting(setting)
|
|
554
|
+
|
|
555
|
+
async def _read_setting(self, setting: Sensor) -> Any:
|
|
544
556
|
count = (setting.size_ + (setting.size_ % 2)) // 2
|
|
545
557
|
response = await self._read_from_socket(ModbusReadCommand(self.comm_addr, setting.offset, count))
|
|
546
558
|
return setting.read_value(response)
|
|
@@ -549,6 +561,9 @@ class ET(Inverter):
|
|
|
549
561
|
setting = self._settings.get(setting_id)
|
|
550
562
|
if not setting:
|
|
551
563
|
raise ValueError(f'Unknown setting "{setting_id}"')
|
|
564
|
+
await self._write_setting(setting, value)
|
|
565
|
+
|
|
566
|
+
async def _write_setting(self, setting: Sensor, value: Any):
|
|
552
567
|
if setting.size_ == 1:
|
|
553
568
|
# modbus can address/store only 16 bit values, read the other 8 bytes
|
|
554
569
|
response = await self._read_from_socket(ModbusReadCommand(self.comm_addr, setting.offset, 1))
|
|
@@ -581,7 +596,7 @@ class ET(Inverter):
|
|
|
581
596
|
|
|
582
597
|
async def get_operation_modes(self, include_emulated: bool) -> Tuple[OperationMode, ...]:
|
|
583
598
|
result = [e for e in OperationMode]
|
|
584
|
-
if not self.
|
|
599
|
+
if not self._has_peak_shaving:
|
|
585
600
|
result.remove(OperationMode.PEAK_SHAVING)
|
|
586
601
|
if not include_emulated:
|
|
587
602
|
result.remove(OperationMode.ECO_CHARGE)
|
|
@@ -626,7 +641,8 @@ class ET(Inverter):
|
|
|
626
641
|
raise ValueError()
|
|
627
642
|
if eco_mode_soc < 0 or eco_mode_soc > 100:
|
|
628
643
|
raise ValueError()
|
|
629
|
-
eco_mode: EcoMode = self.
|
|
644
|
+
eco_mode: EcoMode | Sensor = self._settings.get('eco_mode_1')
|
|
645
|
+
await self._read_setting(eco_mode)
|
|
630
646
|
if operation_mode == OperationMode.ECO_CHARGE:
|
|
631
647
|
await self.write_setting('eco_mode_1', eco_mode.encode_charge(eco_mode_power, eco_mode_soc))
|
|
632
648
|
else:
|
|
@@ -641,7 +657,7 @@ class ET(Inverter):
|
|
|
641
657
|
return 100 - await self.read_setting('battery_discharge_depth')
|
|
642
658
|
|
|
643
659
|
async def set_ongrid_battery_dod(self, dod: int) -> None:
|
|
644
|
-
if 0 <= dod <=
|
|
660
|
+
if 0 <= dod <= 100:
|
|
645
661
|
await self.write_setting('battery_discharge_depth', 100 - dod)
|
|
646
662
|
|
|
647
663
|
def sensors(self) -> Tuple[Sensor, ...]:
|
|
@@ -663,11 +679,3 @@ class ET(Inverter):
|
|
|
663
679
|
async def _set_offline(self, mode: bool) -> None:
|
|
664
680
|
value = bytes.fromhex('00070000') if mode else bytes.fromhex('00010000')
|
|
665
681
|
await self._read_from_socket(ModbusWriteMultiCommand(self.comm_addr, 0xb997, value))
|
|
666
|
-
|
|
667
|
-
def _convert_eco_mode(self, sensor: Sensor) -> Sensor | EcoMode:
|
|
668
|
-
if EcoModeV1 == type(sensor) and self._supports_eco_mode_v2():
|
|
669
|
-
return cast(EcoModeV1, sensor).as_eco_mode_v2()
|
|
670
|
-
elif EcoModeV2 == type(sensor) and not self._supports_eco_mode_v2():
|
|
671
|
-
return cast(EcoModeV2, sensor).as_eco_mode_v1()
|
|
672
|
-
else:
|
|
673
|
-
return sensor
|
|
@@ -85,7 +85,7 @@ class UdpInverterProtocol(asyncio.DatagramProtocol):
|
|
|
85
85
|
class ProtocolResponse:
|
|
86
86
|
"""Definition of response to protocol command"""
|
|
87
87
|
|
|
88
|
-
def __init__(self, raw_data: bytes, command: ProtocolCommand):
|
|
88
|
+
def __init__(self, raw_data: bytes, command: Optional[ProtocolCommand]):
|
|
89
89
|
self.raw_data: bytes = raw_data
|
|
90
90
|
self.command: ProtocolCommand = command
|
|
91
91
|
self._bytes: io.BytesIO = io.BytesIO(self.response_data())
|
|
@@ -116,6 +116,15 @@ class ProtocolCommand:
|
|
|
116
116
|
self.request: bytes = request
|
|
117
117
|
self.validator: Callable[[bytes], bool] = validator
|
|
118
118
|
|
|
119
|
+
def __eq__(self, other):
|
|
120
|
+
if not isinstance(other, ProtocolCommand):
|
|
121
|
+
# don't attempt to compare against unrelated types
|
|
122
|
+
return NotImplemented
|
|
123
|
+
return self.request == other.request
|
|
124
|
+
|
|
125
|
+
def __hash__(self):
|
|
126
|
+
return hash(self.request)
|
|
127
|
+
|
|
119
128
|
def __repr__(self):
|
|
120
129
|
return self.request.hex()
|
|
121
130
|
|
|
@@ -276,6 +285,7 @@ class ModbusProtocolCommand(ProtocolCommand):
|
|
|
276
285
|
lambda x: validate_modbus_response(x, cmd, offset, value),
|
|
277
286
|
)
|
|
278
287
|
self.first_address: int = offset
|
|
288
|
+
self.value = value
|
|
279
289
|
|
|
280
290
|
def trim_response(self, raw_response: bytes):
|
|
281
291
|
"""Trim raw response from header and checksum data"""
|
|
@@ -296,6 +306,12 @@ class ModbusReadCommand(ModbusProtocolCommand):
|
|
|
296
306
|
create_modbus_request(comm_addr, MODBUS_READ_CMD, offset, count),
|
|
297
307
|
MODBUS_READ_CMD, offset, count)
|
|
298
308
|
|
|
309
|
+
def __repr__(self):
|
|
310
|
+
if self.value > 1:
|
|
311
|
+
return f'READ {self.value} registers from {self.first_address} ({self.request.hex()})'
|
|
312
|
+
else:
|
|
313
|
+
return f'READ register {self.first_address} ({self.request.hex()})'
|
|
314
|
+
|
|
299
315
|
|
|
300
316
|
class ModbusWriteCommand(ModbusProtocolCommand):
|
|
301
317
|
"""
|
|
@@ -307,6 +323,9 @@ class ModbusWriteCommand(ModbusProtocolCommand):
|
|
|
307
323
|
create_modbus_request(comm_addr, MODBUS_WRITE_CMD, register, value),
|
|
308
324
|
MODBUS_WRITE_CMD, register, value)
|
|
309
325
|
|
|
326
|
+
def __repr__(self):
|
|
327
|
+
return f'WRITE {self.value} to register {self.first_address} ({self.request.hex()})'
|
|
328
|
+
|
|
310
329
|
|
|
311
330
|
class ModbusWriteMultiCommand(ModbusProtocolCommand):
|
|
312
331
|
"""
|