pylxpweb 0.1.0__py3-none-any.whl → 0.5.2__py3-none-any.whl
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.
- pylxpweb/__init__.py +47 -2
- pylxpweb/api_namespace.py +241 -0
- pylxpweb/cli/__init__.py +3 -0
- pylxpweb/cli/collect_device_data.py +874 -0
- pylxpweb/client.py +387 -26
- pylxpweb/constants/__init__.py +481 -0
- pylxpweb/constants/api.py +48 -0
- pylxpweb/constants/devices.py +98 -0
- pylxpweb/constants/locations.py +227 -0
- pylxpweb/{constants.py → constants/registers.py} +72 -238
- pylxpweb/constants/scaling.py +479 -0
- pylxpweb/devices/__init__.py +32 -0
- pylxpweb/devices/_firmware_update_mixin.py +504 -0
- pylxpweb/devices/_mid_runtime_properties.py +1427 -0
- pylxpweb/devices/base.py +122 -0
- pylxpweb/devices/battery.py +589 -0
- pylxpweb/devices/battery_bank.py +331 -0
- pylxpweb/devices/inverters/__init__.py +32 -0
- pylxpweb/devices/inverters/_features.py +378 -0
- pylxpweb/devices/inverters/_runtime_properties.py +596 -0
- pylxpweb/devices/inverters/base.py +2124 -0
- pylxpweb/devices/inverters/generic.py +192 -0
- pylxpweb/devices/inverters/hybrid.py +274 -0
- pylxpweb/devices/mid_device.py +183 -0
- pylxpweb/devices/models.py +126 -0
- pylxpweb/devices/parallel_group.py +364 -0
- pylxpweb/devices/station.py +908 -0
- pylxpweb/endpoints/control.py +980 -2
- pylxpweb/endpoints/devices.py +249 -16
- pylxpweb/endpoints/firmware.py +43 -10
- pylxpweb/endpoints/plants.py +15 -19
- pylxpweb/exceptions.py +4 -0
- pylxpweb/models.py +708 -41
- pylxpweb/transports/__init__.py +78 -0
- pylxpweb/transports/capabilities.py +101 -0
- pylxpweb/transports/data.py +501 -0
- pylxpweb/transports/exceptions.py +59 -0
- pylxpweb/transports/factory.py +119 -0
- pylxpweb/transports/http.py +329 -0
- pylxpweb/transports/modbus.py +617 -0
- pylxpweb/transports/protocol.py +217 -0
- {pylxpweb-0.1.0.dist-info → pylxpweb-0.5.2.dist-info}/METADATA +130 -85
- pylxpweb-0.5.2.dist-info/RECORD +52 -0
- {pylxpweb-0.1.0.dist-info → pylxpweb-0.5.2.dist-info}/WHEEL +1 -1
- pylxpweb-0.5.2.dist-info/entry_points.txt +3 -0
- pylxpweb-0.1.0.dist-info/RECORD +0 -19
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
"""Runtime properties mixin for BaseInverter.
|
|
2
|
+
|
|
3
|
+
This mixin provides properly-scaled property accessors for all runtime
|
|
4
|
+
sensor data from the inverter. All properties return typed, scaled values
|
|
5
|
+
with graceful None handling.
|
|
6
|
+
|
|
7
|
+
Properties are organized by category:
|
|
8
|
+
- PV (Solar Panel) Properties
|
|
9
|
+
- AC Grid Properties
|
|
10
|
+
- EPS (Emergency Power Supply) Properties
|
|
11
|
+
- Power Flow Properties
|
|
12
|
+
- Battery Properties
|
|
13
|
+
- Temperature Properties
|
|
14
|
+
- Bus Voltage Properties
|
|
15
|
+
- AC Couple & Generator Properties
|
|
16
|
+
- Consumption Properties
|
|
17
|
+
- Status & Info Properties
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
from pylxpweb.constants import scale_runtime_value
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from pylxpweb.models import InverterRuntime
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class InverterRuntimePropertiesMixin:
|
|
31
|
+
"""Mixin providing runtime property accessors for inverters."""
|
|
32
|
+
|
|
33
|
+
_runtime: InverterRuntime | None
|
|
34
|
+
|
|
35
|
+
# ===========================================
|
|
36
|
+
# PV (Solar Panel) Properties
|
|
37
|
+
# ===========================================
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def pv1_voltage(self) -> float:
|
|
41
|
+
"""Get PV string 1 voltage in volts.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
PV1 voltage (÷10), or 0.0 if no data.
|
|
45
|
+
"""
|
|
46
|
+
if self._runtime is None:
|
|
47
|
+
return 0.0
|
|
48
|
+
return scale_runtime_value("vpv1", self._runtime.vpv1)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def pv2_voltage(self) -> float:
|
|
52
|
+
"""Get PV string 2 voltage in volts.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
PV2 voltage (÷10), or 0.0 if no data.
|
|
56
|
+
"""
|
|
57
|
+
if self._runtime is None:
|
|
58
|
+
return 0.0
|
|
59
|
+
|
|
60
|
+
return scale_runtime_value("vpv2", self._runtime.vpv2)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def pv3_voltage(self) -> float:
|
|
64
|
+
"""Get PV string 3 voltage in volts (if available).
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
PV3 voltage (÷10), or 0.0 if no data or not supported.
|
|
68
|
+
"""
|
|
69
|
+
if self._runtime is None or self._runtime.vpv3 is None:
|
|
70
|
+
return 0.0
|
|
71
|
+
|
|
72
|
+
return scale_runtime_value("vpv3", self._runtime.vpv3)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def pv1_power(self) -> int:
|
|
76
|
+
"""Get PV string 1 power in watts.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
PV1 power in watts, or 0 if no data.
|
|
80
|
+
"""
|
|
81
|
+
if self._runtime is None:
|
|
82
|
+
return 0
|
|
83
|
+
return self._runtime.ppv1
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def pv2_power(self) -> int:
|
|
87
|
+
"""Get PV string 2 power in watts.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
PV2 power in watts, or 0 if no data.
|
|
91
|
+
"""
|
|
92
|
+
if self._runtime is None:
|
|
93
|
+
return 0
|
|
94
|
+
return self._runtime.ppv2
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def pv3_power(self) -> int:
|
|
98
|
+
"""Get PV string 3 power in watts (if available).
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
PV3 power in watts, or 0 if no data or not supported.
|
|
102
|
+
"""
|
|
103
|
+
if self._runtime is None or self._runtime.ppv3 is None:
|
|
104
|
+
return 0
|
|
105
|
+
return self._runtime.ppv3
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def pv_total_power(self) -> int:
|
|
109
|
+
"""Get total PV power from all strings in watts.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Total PV power in watts, or 0 if no data.
|
|
113
|
+
"""
|
|
114
|
+
if self._runtime is None:
|
|
115
|
+
return 0
|
|
116
|
+
return self._runtime.ppv
|
|
117
|
+
|
|
118
|
+
# ===========================================
|
|
119
|
+
# AC Grid Properties
|
|
120
|
+
# ===========================================
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def grid_voltage_r(self) -> float:
|
|
124
|
+
"""Get grid AC voltage phase R in volts.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
AC grid voltage R phase (÷10), or 0.0 if no data.
|
|
128
|
+
"""
|
|
129
|
+
if self._runtime is None:
|
|
130
|
+
return 0.0
|
|
131
|
+
|
|
132
|
+
return scale_runtime_value("vacr", self._runtime.vacr)
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def grid_voltage_s(self) -> float:
|
|
136
|
+
"""Get grid AC voltage phase S in volts.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
AC grid voltage S phase (÷10), or 0.0 if no data.
|
|
140
|
+
"""
|
|
141
|
+
if self._runtime is None:
|
|
142
|
+
return 0.0
|
|
143
|
+
|
|
144
|
+
return scale_runtime_value("vacs", self._runtime.vacs)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def grid_voltage_t(self) -> float:
|
|
148
|
+
"""Get grid AC voltage phase T in volts.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
AC grid voltage T phase (÷10), or 0.0 if no data.
|
|
152
|
+
"""
|
|
153
|
+
if self._runtime is None:
|
|
154
|
+
return 0.0
|
|
155
|
+
|
|
156
|
+
return scale_runtime_value("vact", self._runtime.vact)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def grid_frequency(self) -> float:
|
|
160
|
+
"""Get grid AC frequency in Hz.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Grid frequency (÷100), or 0.0 if no data.
|
|
164
|
+
"""
|
|
165
|
+
if self._runtime is None:
|
|
166
|
+
return 0.0
|
|
167
|
+
|
|
168
|
+
return scale_runtime_value("fac", self._runtime.fac)
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def power_factor(self) -> str:
|
|
172
|
+
"""Get power factor.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Power factor as string, or empty string if no data.
|
|
176
|
+
"""
|
|
177
|
+
if self._runtime is None:
|
|
178
|
+
return ""
|
|
179
|
+
return self._runtime.pf
|
|
180
|
+
|
|
181
|
+
# ===========================================
|
|
182
|
+
# EPS (Emergency Power Supply) Properties
|
|
183
|
+
# ===========================================
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def eps_voltage_r(self) -> float:
|
|
187
|
+
"""Get EPS voltage phase R in volts.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
EPS voltage R phase (÷10), or 0.0 if no data.
|
|
191
|
+
"""
|
|
192
|
+
if self._runtime is None:
|
|
193
|
+
return 0.0
|
|
194
|
+
|
|
195
|
+
return scale_runtime_value("vepsr", self._runtime.vepsr)
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def eps_voltage_s(self) -> float:
|
|
199
|
+
"""Get EPS voltage phase S in volts.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
EPS voltage S phase (÷10), or 0.0 if no data.
|
|
203
|
+
"""
|
|
204
|
+
if self._runtime is None:
|
|
205
|
+
return 0.0
|
|
206
|
+
|
|
207
|
+
return scale_runtime_value("vepss", self._runtime.vepss)
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def eps_voltage_t(self) -> float:
|
|
211
|
+
"""Get EPS voltage phase T in volts.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
EPS voltage T phase (÷10), or 0.0 if no data.
|
|
215
|
+
"""
|
|
216
|
+
if self._runtime is None:
|
|
217
|
+
return 0.0
|
|
218
|
+
|
|
219
|
+
return scale_runtime_value("vepst", self._runtime.vepst)
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def eps_frequency(self) -> float:
|
|
223
|
+
"""Get EPS frequency in Hz.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
EPS frequency (÷100), or 0.0 if no data.
|
|
227
|
+
"""
|
|
228
|
+
if self._runtime is None:
|
|
229
|
+
return 0.0
|
|
230
|
+
|
|
231
|
+
return scale_runtime_value("feps", self._runtime.feps)
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def eps_power(self) -> int:
|
|
235
|
+
"""Get EPS power in watts.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
EPS power in watts, or 0 if no data.
|
|
239
|
+
"""
|
|
240
|
+
if self._runtime is None:
|
|
241
|
+
return 0
|
|
242
|
+
return self._runtime.peps
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def eps_power_l1(self) -> int:
|
|
246
|
+
"""Get EPS L1 power in watts.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
EPS L1 power in watts, or 0 if no data.
|
|
250
|
+
"""
|
|
251
|
+
if self._runtime is None:
|
|
252
|
+
return 0
|
|
253
|
+
return self._runtime.pEpsL1N
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def eps_power_l2(self) -> int:
|
|
257
|
+
"""Get EPS L2 power in watts.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
EPS L2 power in watts, or 0 if no data.
|
|
261
|
+
"""
|
|
262
|
+
if self._runtime is None:
|
|
263
|
+
return 0
|
|
264
|
+
return self._runtime.pEpsL2N
|
|
265
|
+
|
|
266
|
+
# ===========================================
|
|
267
|
+
# Power Flow Properties
|
|
268
|
+
# ===========================================
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def power_to_grid(self) -> int:
|
|
272
|
+
"""Get power flowing to grid in watts.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Power to grid in watts, or 0 if no data.
|
|
276
|
+
"""
|
|
277
|
+
if self._runtime is None:
|
|
278
|
+
return 0
|
|
279
|
+
return self._runtime.pToGrid
|
|
280
|
+
|
|
281
|
+
@property
|
|
282
|
+
def power_to_user(self) -> int:
|
|
283
|
+
"""Get power flowing to user loads in watts.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
Power to user in watts, or 0 if no data.
|
|
287
|
+
"""
|
|
288
|
+
if self._runtime is None:
|
|
289
|
+
return 0
|
|
290
|
+
return self._runtime.pToUser
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def inverter_power(self) -> int:
|
|
294
|
+
"""Get inverter power in watts.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Inverter power in watts, or 0 if no data.
|
|
298
|
+
"""
|
|
299
|
+
if self._runtime is None:
|
|
300
|
+
return 0
|
|
301
|
+
return self._runtime.pinv
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def rectifier_power(self) -> int:
|
|
305
|
+
"""Get rectifier power in watts.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Rectifier power in watts, or 0 if no data.
|
|
309
|
+
"""
|
|
310
|
+
if self._runtime is None:
|
|
311
|
+
return 0
|
|
312
|
+
return self._runtime.prec
|
|
313
|
+
|
|
314
|
+
# ===========================================
|
|
315
|
+
# Battery Properties
|
|
316
|
+
# ===========================================
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def battery_voltage(self) -> float:
|
|
320
|
+
"""Get battery voltage in volts.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Battery voltage (÷10), or 0.0 if no data.
|
|
324
|
+
"""
|
|
325
|
+
if self._runtime is None:
|
|
326
|
+
return 0.0
|
|
327
|
+
|
|
328
|
+
return scale_runtime_value("vBat", self._runtime.vBat)
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def battery_charge_power(self) -> int:
|
|
332
|
+
"""Get battery charging power in watts.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Battery charge power in watts, or 0 if no data.
|
|
336
|
+
"""
|
|
337
|
+
if self._runtime is None:
|
|
338
|
+
return 0
|
|
339
|
+
return self._runtime.pCharge
|
|
340
|
+
|
|
341
|
+
@property
|
|
342
|
+
def battery_discharge_power(self) -> int:
|
|
343
|
+
"""Get battery discharging power in watts.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
Battery discharge power in watts, or 0 if no data.
|
|
347
|
+
"""
|
|
348
|
+
if self._runtime is None:
|
|
349
|
+
return 0
|
|
350
|
+
return self._runtime.pDisCharge
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def battery_power(self) -> int:
|
|
354
|
+
"""Get net battery power in watts (positive = charging, negative = discharging).
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
Battery power in watts, or 0 if no data.
|
|
358
|
+
"""
|
|
359
|
+
if self._runtime is None:
|
|
360
|
+
return 0
|
|
361
|
+
return self._runtime.batPower
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def battery_temperature(self) -> int:
|
|
365
|
+
"""Get battery temperature in Celsius.
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Battery temperature in °C, or 0 if no data.
|
|
369
|
+
"""
|
|
370
|
+
if self._runtime is None:
|
|
371
|
+
return 0
|
|
372
|
+
return self._runtime.tBat
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def max_charge_current(self) -> float:
|
|
376
|
+
"""Get maximum charge current in amps.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Max charge current (÷100), or 0.0 if no data.
|
|
380
|
+
"""
|
|
381
|
+
if self._runtime is None:
|
|
382
|
+
return 0.0
|
|
383
|
+
|
|
384
|
+
return scale_runtime_value("maxChgCurr", self._runtime.maxChgCurr)
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def max_discharge_current(self) -> float:
|
|
388
|
+
"""Get maximum discharge current in amps.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
Max discharge current (÷100), or 0.0 if no data.
|
|
392
|
+
"""
|
|
393
|
+
if self._runtime is None:
|
|
394
|
+
return 0.0
|
|
395
|
+
|
|
396
|
+
return scale_runtime_value("maxDischgCurr", self._runtime.maxDischgCurr)
|
|
397
|
+
|
|
398
|
+
# ===========================================
|
|
399
|
+
# Temperature Properties
|
|
400
|
+
# ===========================================
|
|
401
|
+
|
|
402
|
+
@property
|
|
403
|
+
def inverter_temperature(self) -> int:
|
|
404
|
+
"""Get inverter internal temperature in Celsius.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
Internal temperature in °C, or 0 if no data.
|
|
408
|
+
"""
|
|
409
|
+
if self._runtime is None:
|
|
410
|
+
return 0
|
|
411
|
+
return self._runtime.tinner
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def radiator1_temperature(self) -> int:
|
|
415
|
+
"""Get radiator 1 temperature in Celsius.
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
Radiator 1 temperature in °C, or 0 if no data.
|
|
419
|
+
"""
|
|
420
|
+
if self._runtime is None:
|
|
421
|
+
return 0
|
|
422
|
+
return self._runtime.tradiator1
|
|
423
|
+
|
|
424
|
+
@property
|
|
425
|
+
def radiator2_temperature(self) -> int:
|
|
426
|
+
"""Get radiator 2 temperature in Celsius.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
Radiator 2 temperature in °C, or 0 if no data.
|
|
430
|
+
"""
|
|
431
|
+
if self._runtime is None:
|
|
432
|
+
return 0
|
|
433
|
+
return self._runtime.tradiator2
|
|
434
|
+
|
|
435
|
+
# ===========================================
|
|
436
|
+
# Bus Voltage Properties
|
|
437
|
+
# ===========================================
|
|
438
|
+
|
|
439
|
+
@property
|
|
440
|
+
def bus1_voltage(self) -> float:
|
|
441
|
+
"""Get bus 1 voltage in volts.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
Bus 1 voltage (÷100), or 0.0 if no data.
|
|
445
|
+
"""
|
|
446
|
+
if self._runtime is None:
|
|
447
|
+
return 0.0
|
|
448
|
+
|
|
449
|
+
return scale_runtime_value("vBus1", self._runtime.vBus1)
|
|
450
|
+
|
|
451
|
+
@property
|
|
452
|
+
def bus2_voltage(self) -> float:
|
|
453
|
+
"""Get bus 2 voltage in volts.
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
Bus 2 voltage (÷100), or 0.0 if no data.
|
|
457
|
+
"""
|
|
458
|
+
if self._runtime is None:
|
|
459
|
+
return 0.0
|
|
460
|
+
|
|
461
|
+
return scale_runtime_value("vBus2", self._runtime.vBus2)
|
|
462
|
+
|
|
463
|
+
# ===========================================
|
|
464
|
+
# AC Couple & Generator Properties
|
|
465
|
+
# ===========================================
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def ac_couple_power(self) -> int:
|
|
469
|
+
"""Get AC coupled power in watts.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
AC couple power in watts, or 0 if no data.
|
|
473
|
+
"""
|
|
474
|
+
if self._runtime is None:
|
|
475
|
+
return 0
|
|
476
|
+
return self._runtime.acCouplePower
|
|
477
|
+
|
|
478
|
+
@property
|
|
479
|
+
def generator_voltage(self) -> float:
|
|
480
|
+
"""Get generator voltage in volts.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
Generator voltage (÷10), or 0.0 if no data.
|
|
484
|
+
"""
|
|
485
|
+
if self._runtime is None:
|
|
486
|
+
return 0.0
|
|
487
|
+
|
|
488
|
+
return scale_runtime_value("genVolt", self._runtime.genVolt)
|
|
489
|
+
|
|
490
|
+
@property
|
|
491
|
+
def generator_frequency(self) -> float:
|
|
492
|
+
"""Get generator frequency in Hz.
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
Generator frequency (÷100), or 0.0 if no data.
|
|
496
|
+
"""
|
|
497
|
+
if self._runtime is None:
|
|
498
|
+
return 0.0
|
|
499
|
+
|
|
500
|
+
return scale_runtime_value("genFreq", self._runtime.genFreq)
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
def generator_power(self) -> int:
|
|
504
|
+
"""Get generator power in watts.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
Generator power in watts, or 0 if no data.
|
|
508
|
+
"""
|
|
509
|
+
if self._runtime is None:
|
|
510
|
+
return 0
|
|
511
|
+
return self._runtime.genPower
|
|
512
|
+
|
|
513
|
+
@property
|
|
514
|
+
def is_using_generator(self) -> bool:
|
|
515
|
+
"""Check if generator is currently in use.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
True if using generator, False otherwise.
|
|
519
|
+
"""
|
|
520
|
+
if self._runtime is None:
|
|
521
|
+
return False
|
|
522
|
+
return self._runtime._12KUsingGenerator
|
|
523
|
+
|
|
524
|
+
# ===========================================
|
|
525
|
+
# Consumption Properties
|
|
526
|
+
# ===========================================
|
|
527
|
+
|
|
528
|
+
@property
|
|
529
|
+
def consumption_power(self) -> int:
|
|
530
|
+
"""Get consumption power in watts.
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
Consumption power in watts, or 0 if no data.
|
|
534
|
+
"""
|
|
535
|
+
if self._runtime is None:
|
|
536
|
+
return 0
|
|
537
|
+
return self._runtime.consumptionPower
|
|
538
|
+
|
|
539
|
+
# ===========================================
|
|
540
|
+
# Status & Info Properties
|
|
541
|
+
# ===========================================
|
|
542
|
+
|
|
543
|
+
@property
|
|
544
|
+
def firmware_version(self) -> str:
|
|
545
|
+
"""Get firmware version.
|
|
546
|
+
|
|
547
|
+
Returns:
|
|
548
|
+
Firmware version string, or empty string if no data.
|
|
549
|
+
"""
|
|
550
|
+
if self._runtime is None:
|
|
551
|
+
return ""
|
|
552
|
+
return self._runtime.fwCode
|
|
553
|
+
|
|
554
|
+
@property
|
|
555
|
+
def status(self) -> int:
|
|
556
|
+
"""Get inverter status code.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
Status code, or 0 if no data.
|
|
560
|
+
"""
|
|
561
|
+
if self._runtime is None:
|
|
562
|
+
return 0
|
|
563
|
+
return self._runtime.status
|
|
564
|
+
|
|
565
|
+
@property
|
|
566
|
+
def status_text(self) -> str:
|
|
567
|
+
"""Get inverter status as text.
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
Status text, or empty string if no data.
|
|
571
|
+
"""
|
|
572
|
+
if self._runtime is None:
|
|
573
|
+
return ""
|
|
574
|
+
return self._runtime.statusText
|
|
575
|
+
|
|
576
|
+
@property
|
|
577
|
+
def is_lost(self) -> bool:
|
|
578
|
+
"""Check if inverter connection is lost.
|
|
579
|
+
|
|
580
|
+
Returns:
|
|
581
|
+
True if connection lost, False otherwise.
|
|
582
|
+
"""
|
|
583
|
+
if self._runtime is None:
|
|
584
|
+
return True # No data means lost
|
|
585
|
+
return self._runtime.lost
|
|
586
|
+
|
|
587
|
+
@property
|
|
588
|
+
def power_rating(self) -> str:
|
|
589
|
+
"""Get power rating text (e.g., "16kW").
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
Power rating string, or empty string if no data.
|
|
593
|
+
"""
|
|
594
|
+
if self._runtime is None:
|
|
595
|
+
return ""
|
|
596
|
+
return self._runtime.powerRatingText
|