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.
Files changed (46) hide show
  1. pylxpweb/__init__.py +47 -2
  2. pylxpweb/api_namespace.py +241 -0
  3. pylxpweb/cli/__init__.py +3 -0
  4. pylxpweb/cli/collect_device_data.py +874 -0
  5. pylxpweb/client.py +387 -26
  6. pylxpweb/constants/__init__.py +481 -0
  7. pylxpweb/constants/api.py +48 -0
  8. pylxpweb/constants/devices.py +98 -0
  9. pylxpweb/constants/locations.py +227 -0
  10. pylxpweb/{constants.py → constants/registers.py} +72 -238
  11. pylxpweb/constants/scaling.py +479 -0
  12. pylxpweb/devices/__init__.py +32 -0
  13. pylxpweb/devices/_firmware_update_mixin.py +504 -0
  14. pylxpweb/devices/_mid_runtime_properties.py +1427 -0
  15. pylxpweb/devices/base.py +122 -0
  16. pylxpweb/devices/battery.py +589 -0
  17. pylxpweb/devices/battery_bank.py +331 -0
  18. pylxpweb/devices/inverters/__init__.py +32 -0
  19. pylxpweb/devices/inverters/_features.py +378 -0
  20. pylxpweb/devices/inverters/_runtime_properties.py +596 -0
  21. pylxpweb/devices/inverters/base.py +2124 -0
  22. pylxpweb/devices/inverters/generic.py +192 -0
  23. pylxpweb/devices/inverters/hybrid.py +274 -0
  24. pylxpweb/devices/mid_device.py +183 -0
  25. pylxpweb/devices/models.py +126 -0
  26. pylxpweb/devices/parallel_group.py +364 -0
  27. pylxpweb/devices/station.py +908 -0
  28. pylxpweb/endpoints/control.py +980 -2
  29. pylxpweb/endpoints/devices.py +249 -16
  30. pylxpweb/endpoints/firmware.py +43 -10
  31. pylxpweb/endpoints/plants.py +15 -19
  32. pylxpweb/exceptions.py +4 -0
  33. pylxpweb/models.py +708 -41
  34. pylxpweb/transports/__init__.py +78 -0
  35. pylxpweb/transports/capabilities.py +101 -0
  36. pylxpweb/transports/data.py +501 -0
  37. pylxpweb/transports/exceptions.py +59 -0
  38. pylxpweb/transports/factory.py +119 -0
  39. pylxpweb/transports/http.py +329 -0
  40. pylxpweb/transports/modbus.py +617 -0
  41. pylxpweb/transports/protocol.py +217 -0
  42. {pylxpweb-0.1.0.dist-info → pylxpweb-0.5.2.dist-info}/METADATA +130 -85
  43. pylxpweb-0.5.2.dist-info/RECORD +52 -0
  44. {pylxpweb-0.1.0.dist-info → pylxpweb-0.5.2.dist-info}/WHEEL +1 -1
  45. pylxpweb-0.5.2.dist-info/entry_points.txt +3 -0
  46. pylxpweb-0.1.0.dist-info/RECORD +0 -19
@@ -0,0 +1,1427 @@
1
+ """Runtime properties mixin for MIDDevice (GridBOSS).
2
+
3
+ This mixin provides properly-scaled property accessors for all GridBOSS
4
+ sensor data from the MID device runtime API. All properties return typed,
5
+ scaled values with graceful None handling.
6
+
7
+ Properties are organized by category:
8
+ - Voltage Properties (Grid, UPS, Generator - aggregate and per-phase)
9
+ - Current Properties (Grid, Load, Generator, UPS - per-phase)
10
+ - Power Properties (Grid, Load, Generator, UPS - per-phase and totals)
11
+ - Frequency Properties
12
+ - Smart Port Status
13
+ - System Status & Info
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import TYPE_CHECKING
19
+
20
+ from pylxpweb.constants import scale_mid_frequency, scale_mid_voltage
21
+
22
+ if TYPE_CHECKING:
23
+ from pylxpweb.models import MidboxRuntime
24
+
25
+
26
+ class MIDRuntimePropertiesMixin:
27
+ """Mixin providing runtime property accessors for MID devices."""
28
+
29
+ _runtime: MidboxRuntime | None
30
+
31
+ # ===========================================
32
+ # Voltage Properties - Aggregate
33
+ # ===========================================
34
+
35
+ @property
36
+ def grid_voltage(self) -> float:
37
+ """Get aggregate grid voltage in volts.
38
+
39
+ Returns:
40
+ Grid RMS voltage (÷10), or 0.0 if no data.
41
+ """
42
+ if self._runtime is None:
43
+ return 0.0
44
+
45
+ return scale_mid_voltage(self._runtime.midboxData.gridRmsVolt)
46
+
47
+ @property
48
+ def ups_voltage(self) -> float:
49
+ """Get aggregate UPS voltage in volts.
50
+
51
+ Returns:
52
+ UPS RMS voltage (÷10), or 0.0 if no data.
53
+ """
54
+ if self._runtime is None:
55
+ return 0.0
56
+
57
+ return scale_mid_voltage(self._runtime.midboxData.upsRmsVolt)
58
+
59
+ @property
60
+ def generator_voltage(self) -> float:
61
+ """Get aggregate generator voltage in volts.
62
+
63
+ Returns:
64
+ Generator RMS voltage (÷10), or 0.0 if no data.
65
+ """
66
+ if self._runtime is None:
67
+ return 0.0
68
+
69
+ return scale_mid_voltage(self._runtime.midboxData.genRmsVolt)
70
+
71
+ # ===========================================
72
+ # Voltage Properties - Grid Per-Phase
73
+ # ===========================================
74
+
75
+ @property
76
+ def grid_l1_voltage(self) -> float:
77
+ """Get grid L1 voltage in volts.
78
+
79
+ Returns:
80
+ Grid L1 RMS voltage (÷10), or 0.0 if no data.
81
+ """
82
+ if self._runtime is None:
83
+ return 0.0
84
+
85
+ return scale_mid_voltage(self._runtime.midboxData.gridL1RmsVolt)
86
+
87
+ @property
88
+ def grid_l2_voltage(self) -> float:
89
+ """Get grid L2 voltage in volts.
90
+
91
+ Returns:
92
+ Grid L2 RMS voltage (÷10), or 0.0 if no data.
93
+ """
94
+ if self._runtime is None:
95
+ return 0.0
96
+
97
+ return scale_mid_voltage(self._runtime.midboxData.gridL2RmsVolt)
98
+
99
+ # ===========================================
100
+ # Voltage Properties - UPS Per-Phase
101
+ # ===========================================
102
+
103
+ @property
104
+ def ups_l1_voltage(self) -> float:
105
+ """Get UPS L1 voltage in volts.
106
+
107
+ Returns:
108
+ UPS L1 RMS voltage (÷10), or 0.0 if no data.
109
+ """
110
+ if self._runtime is None:
111
+ return 0.0
112
+
113
+ return scale_mid_voltage(self._runtime.midboxData.upsL1RmsVolt)
114
+
115
+ @property
116
+ def ups_l2_voltage(self) -> float:
117
+ """Get UPS L2 voltage in volts.
118
+
119
+ Returns:
120
+ UPS L2 RMS voltage (÷10), or 0.0 if no data.
121
+ """
122
+ if self._runtime is None:
123
+ return 0.0
124
+
125
+ return scale_mid_voltage(self._runtime.midboxData.upsL2RmsVolt)
126
+
127
+ # ===========================================
128
+ # Voltage Properties - Generator Per-Phase
129
+ # ===========================================
130
+
131
+ @property
132
+ def generator_l1_voltage(self) -> float:
133
+ """Get generator L1 voltage in volts.
134
+
135
+ Returns:
136
+ Generator L1 RMS voltage (÷10), or 0.0 if no data.
137
+ """
138
+ if self._runtime is None:
139
+ return 0.0
140
+
141
+ return scale_mid_voltage(self._runtime.midboxData.genL1RmsVolt)
142
+
143
+ @property
144
+ def generator_l2_voltage(self) -> float:
145
+ """Get generator L2 voltage in volts.
146
+
147
+ Returns:
148
+ Generator L2 RMS voltage (÷10), or 0.0 if no data.
149
+ """
150
+ if self._runtime is None:
151
+ return 0.0
152
+
153
+ return scale_mid_voltage(self._runtime.midboxData.genL2RmsVolt)
154
+
155
+ # ===========================================
156
+ # Current Properties - Grid
157
+ # ===========================================
158
+
159
+ @property
160
+ def grid_l1_current(self) -> float:
161
+ """Get grid L1 current in amps.
162
+
163
+ Returns:
164
+ Grid L1 RMS current (÷100), or 0.0 if no data.
165
+ """
166
+ if self._runtime is None:
167
+ return 0.0
168
+ return self._runtime.midboxData.gridL1RmsCurr / 100.0
169
+
170
+ @property
171
+ def grid_l2_current(self) -> float:
172
+ """Get grid L2 current in amps.
173
+
174
+ Returns:
175
+ Grid L2 RMS current (÷100), or 0.0 if no data.
176
+ """
177
+ if self._runtime is None:
178
+ return 0.0
179
+ return self._runtime.midboxData.gridL2RmsCurr / 100.0
180
+
181
+ # ===========================================
182
+ # Current Properties - Load
183
+ # ===========================================
184
+
185
+ @property
186
+ def load_l1_current(self) -> float:
187
+ """Get load L1 current in amps.
188
+
189
+ Returns:
190
+ Load L1 RMS current (÷100), or 0.0 if no data.
191
+ """
192
+ if self._runtime is None:
193
+ return 0.0
194
+ return self._runtime.midboxData.loadL1RmsCurr / 100.0
195
+
196
+ @property
197
+ def load_l2_current(self) -> float:
198
+ """Get load L2 current in amps.
199
+
200
+ Returns:
201
+ Load L2 RMS current (÷100), or 0.0 if no data.
202
+ """
203
+ if self._runtime is None:
204
+ return 0.0
205
+ return self._runtime.midboxData.loadL2RmsCurr / 100.0
206
+
207
+ # ===========================================
208
+ # Current Properties - Generator
209
+ # ===========================================
210
+
211
+ @property
212
+ def generator_l1_current(self) -> float:
213
+ """Get generator L1 current in amps.
214
+
215
+ Returns:
216
+ Generator L1 RMS current (÷100), or 0.0 if no data.
217
+ """
218
+ if self._runtime is None:
219
+ return 0.0
220
+ return self._runtime.midboxData.genL1RmsCurr / 100.0
221
+
222
+ @property
223
+ def generator_l2_current(self) -> float:
224
+ """Get generator L2 current in amps.
225
+
226
+ Returns:
227
+ Generator L2 RMS current (÷100), or 0.0 if no data.
228
+ """
229
+ if self._runtime is None:
230
+ return 0.0
231
+ return self._runtime.midboxData.genL2RmsCurr / 100.0
232
+
233
+ # ===========================================
234
+ # Current Properties - UPS
235
+ # ===========================================
236
+
237
+ @property
238
+ def ups_l1_current(self) -> float:
239
+ """Get UPS L1 current in amps.
240
+
241
+ Returns:
242
+ UPS L1 RMS current (÷100), or 0.0 if no data.
243
+ """
244
+ if self._runtime is None:
245
+ return 0.0
246
+ return self._runtime.midboxData.upsL1RmsCurr / 100.0
247
+
248
+ @property
249
+ def ups_l2_current(self) -> float:
250
+ """Get UPS L2 current in amps.
251
+
252
+ Returns:
253
+ UPS L2 RMS current (÷100), or 0.0 if no data.
254
+ """
255
+ if self._runtime is None:
256
+ return 0.0
257
+ return self._runtime.midboxData.upsL2RmsCurr / 100.0
258
+
259
+ # ===========================================
260
+ # Power Properties - Grid
261
+ # ===========================================
262
+
263
+ @property
264
+ def grid_l1_power(self) -> int:
265
+ """Get grid L1 active power in watts.
266
+
267
+ Returns:
268
+ Grid L1 power, or 0 if no data.
269
+ """
270
+ if self._runtime is None:
271
+ return 0
272
+ return self._runtime.midboxData.gridL1ActivePower
273
+
274
+ @property
275
+ def grid_l2_power(self) -> int:
276
+ """Get grid L2 active power in watts.
277
+
278
+ Returns:
279
+ Grid L2 power, or 0 if no data.
280
+ """
281
+ if self._runtime is None:
282
+ return 0
283
+ return self._runtime.midboxData.gridL2ActivePower
284
+
285
+ @property
286
+ def grid_power(self) -> int:
287
+ """Get total grid power in watts (L1 + L2).
288
+
289
+ Returns:
290
+ Total grid power, or 0 if no data.
291
+ """
292
+ if self._runtime is None:
293
+ return 0
294
+ return (
295
+ self._runtime.midboxData.gridL1ActivePower + self._runtime.midboxData.gridL2ActivePower
296
+ )
297
+
298
+ # ===========================================
299
+ # Power Properties - Load
300
+ # ===========================================
301
+
302
+ @property
303
+ def load_l1_power(self) -> int:
304
+ """Get load L1 active power in watts.
305
+
306
+ Returns:
307
+ Load L1 power, or 0 if no data.
308
+ """
309
+ if self._runtime is None:
310
+ return 0
311
+ return self._runtime.midboxData.loadL1ActivePower
312
+
313
+ @property
314
+ def load_l2_power(self) -> int:
315
+ """Get load L2 active power in watts.
316
+
317
+ Returns:
318
+ Load L2 power, or 0 if no data.
319
+ """
320
+ if self._runtime is None:
321
+ return 0
322
+ return self._runtime.midboxData.loadL2ActivePower
323
+
324
+ @property
325
+ def load_power(self) -> int:
326
+ """Get total load power in watts (L1 + L2).
327
+
328
+ Returns:
329
+ Total load power, or 0 if no data.
330
+ """
331
+ if self._runtime is None:
332
+ return 0
333
+ return (
334
+ self._runtime.midboxData.loadL1ActivePower + self._runtime.midboxData.loadL2ActivePower
335
+ )
336
+
337
+ # ===========================================
338
+ # Power Properties - Generator
339
+ # ===========================================
340
+
341
+ @property
342
+ def generator_l1_power(self) -> int:
343
+ """Get generator L1 active power in watts.
344
+
345
+ Returns:
346
+ Generator L1 power, or 0 if no data.
347
+ """
348
+ if self._runtime is None:
349
+ return 0
350
+ return self._runtime.midboxData.genL1ActivePower
351
+
352
+ @property
353
+ def generator_l2_power(self) -> int:
354
+ """Get generator L2 active power in watts.
355
+
356
+ Returns:
357
+ Generator L2 power, or 0 if no data.
358
+ """
359
+ if self._runtime is None:
360
+ return 0
361
+ return self._runtime.midboxData.genL2ActivePower
362
+
363
+ @property
364
+ def generator_power(self) -> int:
365
+ """Get total generator power in watts (L1 + L2).
366
+
367
+ Returns:
368
+ Total generator power, or 0 if no data.
369
+ """
370
+ if self._runtime is None:
371
+ return 0
372
+ return self._runtime.midboxData.genL1ActivePower + self._runtime.midboxData.genL2ActivePower
373
+
374
+ # ===========================================
375
+ # Power Properties - UPS
376
+ # ===========================================
377
+
378
+ @property
379
+ def ups_l1_power(self) -> int:
380
+ """Get UPS L1 active power in watts.
381
+
382
+ Returns:
383
+ UPS L1 power, or 0 if no data.
384
+ """
385
+ if self._runtime is None:
386
+ return 0
387
+ return self._runtime.midboxData.upsL1ActivePower
388
+
389
+ @property
390
+ def ups_l2_power(self) -> int:
391
+ """Get UPS L2 active power in watts.
392
+
393
+ Returns:
394
+ UPS L2 power, or 0 if no data.
395
+ """
396
+ if self._runtime is None:
397
+ return 0
398
+ return self._runtime.midboxData.upsL2ActivePower
399
+
400
+ @property
401
+ def ups_power(self) -> int:
402
+ """Get total UPS power in watts (L1 + L2).
403
+
404
+ Returns:
405
+ Total UPS power, or 0 if no data.
406
+ """
407
+ if self._runtime is None:
408
+ return 0
409
+ return self._runtime.midboxData.upsL1ActivePower + self._runtime.midboxData.upsL2ActivePower
410
+
411
+ # ===========================================
412
+ # Power Properties - Hybrid System
413
+ # ===========================================
414
+
415
+ @property
416
+ def hybrid_power(self) -> int:
417
+ """Get hybrid system power in watts.
418
+
419
+ Returns:
420
+ Hybrid power (combined system power), or 0 if no data.
421
+ """
422
+ if self._runtime is None:
423
+ return 0
424
+ return self._runtime.midboxData.hybridPower
425
+
426
+ # ===========================================
427
+ # Frequency Properties
428
+ # ===========================================
429
+
430
+ @property
431
+ def grid_frequency(self) -> float:
432
+ """Get grid frequency in Hz.
433
+
434
+ Returns:
435
+ Grid frequency (÷100), or 0.0 if no data.
436
+ """
437
+ if self._runtime is None:
438
+ return 0.0
439
+
440
+ return scale_mid_frequency(self._runtime.midboxData.gridFreq)
441
+
442
+ # ===========================================
443
+ # Smart Port Status
444
+ # ===========================================
445
+
446
+ @property
447
+ def smart_port1_status(self) -> int:
448
+ """Get smart port 1 status.
449
+
450
+ Returns:
451
+ Port 1 status code, or 0 if no data.
452
+ """
453
+ if self._runtime is None:
454
+ return 0
455
+ return self._runtime.midboxData.smartPort1Status
456
+
457
+ @property
458
+ def smart_port2_status(self) -> int:
459
+ """Get smart port 2 status.
460
+
461
+ Returns:
462
+ Port 2 status code, or 0 if no data.
463
+ """
464
+ if self._runtime is None:
465
+ return 0
466
+ return self._runtime.midboxData.smartPort2Status
467
+
468
+ @property
469
+ def smart_port3_status(self) -> int:
470
+ """Get smart port 3 status.
471
+
472
+ Returns:
473
+ Port 3 status code, or 0 if no data.
474
+ """
475
+ if self._runtime is None:
476
+ return 0
477
+ return self._runtime.midboxData.smartPort3Status
478
+
479
+ @property
480
+ def smart_port4_status(self) -> int:
481
+ """Get smart port 4 status.
482
+
483
+ Returns:
484
+ Port 4 status code, or 0 if no data.
485
+ """
486
+ if self._runtime is None:
487
+ return 0
488
+ return self._runtime.midboxData.smartPort4Status
489
+
490
+ # ===========================================
491
+ # System Status & Info
492
+ # ===========================================
493
+
494
+ @property
495
+ def status(self) -> int:
496
+ """Get device status code.
497
+
498
+ Returns:
499
+ Status code, or 0 if no data.
500
+ """
501
+ if self._runtime is None:
502
+ return 0
503
+ return self._runtime.midboxData.status
504
+
505
+ @property
506
+ def server_time(self) -> str:
507
+ """Get server timestamp.
508
+
509
+ Returns:
510
+ Server time string, or empty string if no data.
511
+ """
512
+ if self._runtime is None:
513
+ return ""
514
+ return self._runtime.midboxData.serverTime
515
+
516
+ @property
517
+ def device_time(self) -> str:
518
+ """Get device timestamp.
519
+
520
+ Returns:
521
+ Device time string, or empty string if no data.
522
+ """
523
+ if self._runtime is None:
524
+ return ""
525
+ return self._runtime.midboxData.deviceTime
526
+
527
+ @property
528
+ def firmware_version(self) -> str:
529
+ """Get firmware version.
530
+
531
+ Returns:
532
+ Firmware version string, or empty string if no data.
533
+ """
534
+ if self._runtime is None:
535
+ return ""
536
+ return self._runtime.fwCode
537
+
538
+ @property
539
+ def has_data(self) -> bool:
540
+ """Check if device has runtime data.
541
+
542
+ Returns:
543
+ True if runtime data is available.
544
+ """
545
+ return self._runtime is not None
546
+
547
+ # ===========================================
548
+ # Energy Properties - UPS
549
+ # ===========================================
550
+
551
+ @property
552
+ def e_ups_today_l1(self) -> float | None:
553
+ """Get UPS L1 energy today in kWh.
554
+
555
+ Returns:
556
+ UPS L1 energy today (÷10), or None if not available.
557
+ """
558
+ if self._runtime is None:
559
+ return None
560
+ val = self._runtime.midboxData.eUpsTodayL1
561
+ return val / 10.0 if val is not None else None
562
+
563
+ @property
564
+ def e_ups_today_l2(self) -> float | None:
565
+ """Get UPS L2 energy today in kWh.
566
+
567
+ Returns:
568
+ UPS L2 energy today (÷10), or None if not available.
569
+ """
570
+ if self._runtime is None:
571
+ return None
572
+ val = self._runtime.midboxData.eUpsTodayL2
573
+ return val / 10.0 if val is not None else None
574
+
575
+ @property
576
+ def e_ups_total_l1(self) -> float | None:
577
+ """Get UPS L1 lifetime energy in kWh.
578
+
579
+ Returns:
580
+ UPS L1 lifetime energy (÷10), or None if not available.
581
+ """
582
+ if self._runtime is None:
583
+ return None
584
+ val = self._runtime.midboxData.eUpsTotalL1
585
+ return val / 10.0 if val is not None else None
586
+
587
+ @property
588
+ def e_ups_total_l2(self) -> float | None:
589
+ """Get UPS L2 lifetime energy in kWh.
590
+
591
+ Returns:
592
+ UPS L2 lifetime energy (÷10), or None if not available.
593
+ """
594
+ if self._runtime is None:
595
+ return None
596
+ val = self._runtime.midboxData.eUpsTotalL2
597
+ return val / 10.0 if val is not None else None
598
+
599
+ # ===========================================
600
+ # Energy Properties - Grid Export
601
+ # ===========================================
602
+
603
+ @property
604
+ def e_to_grid_today_l1(self) -> float | None:
605
+ """Get grid export L1 energy today in kWh.
606
+
607
+ Returns:
608
+ Grid export L1 energy today (÷10), or None if not available.
609
+ """
610
+ if self._runtime is None:
611
+ return None
612
+ val = self._runtime.midboxData.eToGridTodayL1
613
+ return val / 10.0 if val is not None else None
614
+
615
+ @property
616
+ def e_to_grid_today_l2(self) -> float | None:
617
+ """Get grid export L2 energy today in kWh.
618
+
619
+ Returns:
620
+ Grid export L2 energy today (÷10), or None if not available.
621
+ """
622
+ if self._runtime is None:
623
+ return None
624
+ val = self._runtime.midboxData.eToGridTodayL2
625
+ return val / 10.0 if val is not None else None
626
+
627
+ @property
628
+ def e_to_grid_total_l1(self) -> float | None:
629
+ """Get grid export L1 lifetime energy in kWh.
630
+
631
+ Returns:
632
+ Grid export L1 lifetime energy (÷10), or None if not available.
633
+ """
634
+ if self._runtime is None:
635
+ return None
636
+ val = self._runtime.midboxData.eToGridTotalL1
637
+ return val / 10.0 if val is not None else None
638
+
639
+ @property
640
+ def e_to_grid_total_l2(self) -> float | None:
641
+ """Get grid export L2 lifetime energy in kWh.
642
+
643
+ Returns:
644
+ Grid export L2 lifetime energy (÷10), or None if not available.
645
+ """
646
+ if self._runtime is None:
647
+ return None
648
+ val = self._runtime.midboxData.eToGridTotalL2
649
+ return val / 10.0 if val is not None else None
650
+
651
+ # ===========================================
652
+ # Energy Properties - Grid Import
653
+ # ===========================================
654
+
655
+ @property
656
+ def e_to_user_today_l1(self) -> float | None:
657
+ """Get grid import L1 energy today in kWh.
658
+
659
+ Returns:
660
+ Grid import L1 energy today (÷10), or None if not available.
661
+ """
662
+ if self._runtime is None:
663
+ return None
664
+ val = self._runtime.midboxData.eToUserTodayL1
665
+ return val / 10.0 if val is not None else None
666
+
667
+ @property
668
+ def e_to_user_today_l2(self) -> float | None:
669
+ """Get grid import L2 energy today in kWh.
670
+
671
+ Returns:
672
+ Grid import L2 energy today (÷10), or None if not available.
673
+ """
674
+ if self._runtime is None:
675
+ return None
676
+ val = self._runtime.midboxData.eToUserTodayL2
677
+ return val / 10.0 if val is not None else None
678
+
679
+ @property
680
+ def e_to_user_total_l1(self) -> float | None:
681
+ """Get grid import L1 lifetime energy in kWh.
682
+
683
+ Returns:
684
+ Grid import L1 lifetime energy (÷10), or None if not available.
685
+ """
686
+ if self._runtime is None:
687
+ return None
688
+ val = self._runtime.midboxData.eToUserTotalL1
689
+ return val / 10.0 if val is not None else None
690
+
691
+ @property
692
+ def e_to_user_total_l2(self) -> float | None:
693
+ """Get grid import L2 lifetime energy in kWh.
694
+
695
+ Returns:
696
+ Grid import L2 lifetime energy (÷10), or None if not available.
697
+ """
698
+ if self._runtime is None:
699
+ return None
700
+ val = self._runtime.midboxData.eToUserTotalL2
701
+ return val / 10.0 if val is not None else None
702
+
703
+ # ===========================================
704
+ # Energy Properties - Load
705
+ # ===========================================
706
+
707
+ @property
708
+ def e_load_today_l1(self) -> float | None:
709
+ """Get load L1 energy today in kWh.
710
+
711
+ Returns:
712
+ Load L1 energy today (÷10), or None if not available.
713
+ """
714
+ if self._runtime is None:
715
+ return None
716
+ val = self._runtime.midboxData.eLoadTodayL1
717
+ return val / 10.0 if val is not None else None
718
+
719
+ @property
720
+ def e_load_today_l2(self) -> float | None:
721
+ """Get load L2 energy today in kWh.
722
+
723
+ Returns:
724
+ Load L2 energy today (÷10), or None if not available.
725
+ """
726
+ if self._runtime is None:
727
+ return None
728
+ val = self._runtime.midboxData.eLoadTodayL2
729
+ return val / 10.0 if val is not None else None
730
+
731
+ @property
732
+ def e_load_total_l1(self) -> float | None:
733
+ """Get load L1 lifetime energy in kWh.
734
+
735
+ Returns:
736
+ Load L1 lifetime energy (÷10), or None if not available.
737
+ """
738
+ if self._runtime is None:
739
+ return None
740
+ val = self._runtime.midboxData.eLoadTotalL1
741
+ return val / 10.0 if val is not None else None
742
+
743
+ @property
744
+ def e_load_total_l2(self) -> float | None:
745
+ """Get load L2 lifetime energy in kWh.
746
+
747
+ Returns:
748
+ Load L2 lifetime energy (÷10), or None if not available.
749
+ """
750
+ if self._runtime is None:
751
+ return None
752
+ val = self._runtime.midboxData.eLoadTotalL2
753
+ return val / 10.0 if val is not None else None
754
+
755
+ # ===========================================
756
+ # Energy Properties - AC Couple 1
757
+ # ===========================================
758
+
759
+ @property
760
+ def e_ac_couple1_today_l1(self) -> float | None:
761
+ """Get AC Couple 1 L1 energy today in kWh.
762
+
763
+ Returns:
764
+ AC Couple 1 L1 energy today (÷10), or None if not available.
765
+ """
766
+ if self._runtime is None:
767
+ return None
768
+ val = self._runtime.midboxData.eACcouple1TodayL1
769
+ return val / 10.0 if val is not None else None
770
+
771
+ @property
772
+ def e_ac_couple1_today_l2(self) -> float | None:
773
+ """Get AC Couple 1 L2 energy today in kWh.
774
+
775
+ Returns:
776
+ AC Couple 1 L2 energy today (÷10), or None if not available.
777
+ """
778
+ if self._runtime is None:
779
+ return None
780
+ val = self._runtime.midboxData.eACcouple1TodayL2
781
+ return val / 10.0 if val is not None else None
782
+
783
+ @property
784
+ def e_ac_couple1_total_l1(self) -> float | None:
785
+ """Get AC Couple 1 L1 lifetime energy in kWh.
786
+
787
+ Returns:
788
+ AC Couple 1 L1 lifetime energy (÷10), or None if not available.
789
+ """
790
+ if self._runtime is None:
791
+ return None
792
+ val = self._runtime.midboxData.eACcouple1TotalL1
793
+ return val / 10.0 if val is not None else None
794
+
795
+ @property
796
+ def e_ac_couple1_total_l2(self) -> float | None:
797
+ """Get AC Couple 1 L2 lifetime energy in kWh.
798
+
799
+ Returns:
800
+ AC Couple 1 L2 lifetime energy (÷10), or None if not available.
801
+ """
802
+ if self._runtime is None:
803
+ return None
804
+ val = self._runtime.midboxData.eACcouple1TotalL2
805
+ return val / 10.0 if val is not None else None
806
+
807
+ # ===========================================
808
+ # Energy Properties - AC Couple 2
809
+ # ===========================================
810
+
811
+ @property
812
+ def e_ac_couple2_today_l1(self) -> float | None:
813
+ """Get AC Couple 2 L1 energy today in kWh.
814
+
815
+ Returns:
816
+ AC Couple 2 L1 energy today (÷10), or None if not available.
817
+ """
818
+ if self._runtime is None:
819
+ return None
820
+ val = self._runtime.midboxData.eACcouple2TodayL1
821
+ return val / 10.0 if val is not None else None
822
+
823
+ @property
824
+ def e_ac_couple2_today_l2(self) -> float | None:
825
+ """Get AC Couple 2 L2 energy today in kWh.
826
+
827
+ Returns:
828
+ AC Couple 2 L2 energy today (÷10), or None if not available.
829
+ """
830
+ if self._runtime is None:
831
+ return None
832
+ val = self._runtime.midboxData.eACcouple2TodayL2
833
+ return val / 10.0 if val is not None else None
834
+
835
+ @property
836
+ def e_ac_couple2_total_l1(self) -> float | None:
837
+ """Get AC Couple 2 L1 lifetime energy in kWh.
838
+
839
+ Returns:
840
+ AC Couple 2 L1 lifetime energy (÷10), or None if not available.
841
+ """
842
+ if self._runtime is None:
843
+ return None
844
+ val = self._runtime.midboxData.eACcouple2TotalL1
845
+ return val / 10.0 if val is not None else None
846
+
847
+ @property
848
+ def e_ac_couple2_total_l2(self) -> float | None:
849
+ """Get AC Couple 2 L2 lifetime energy in kWh.
850
+
851
+ Returns:
852
+ AC Couple 2 L2 lifetime energy (÷10), or None if not available.
853
+ """
854
+ if self._runtime is None:
855
+ return None
856
+ val = self._runtime.midboxData.eACcouple2TotalL2
857
+ return val / 10.0 if val is not None else None
858
+
859
+ # ===========================================
860
+ # Energy Properties - AC Couple 3
861
+ # ===========================================
862
+
863
+ @property
864
+ def e_ac_couple3_today_l1(self) -> float | None:
865
+ """Get AC Couple 3 L1 energy today in kWh.
866
+
867
+ Returns:
868
+ AC Couple 3 L1 energy today (÷10), or None if not available.
869
+ """
870
+ if self._runtime is None:
871
+ return None
872
+ val = self._runtime.midboxData.eACcouple3TodayL1
873
+ return val / 10.0 if val is not None else None
874
+
875
+ @property
876
+ def e_ac_couple3_today_l2(self) -> float | None:
877
+ """Get AC Couple 3 L2 energy today in kWh.
878
+
879
+ Returns:
880
+ AC Couple 3 L2 energy today (÷10), or None if not available.
881
+ """
882
+ if self._runtime is None:
883
+ return None
884
+ val = self._runtime.midboxData.eACcouple3TodayL2
885
+ return val / 10.0 if val is not None else None
886
+
887
+ @property
888
+ def e_ac_couple3_total_l1(self) -> float | None:
889
+ """Get AC Couple 3 L1 lifetime energy in kWh.
890
+
891
+ Returns:
892
+ AC Couple 3 L1 lifetime energy (÷10), or None if not available.
893
+ """
894
+ if self._runtime is None:
895
+ return None
896
+ val = self._runtime.midboxData.eACcouple3TotalL1
897
+ return val / 10.0 if val is not None else None
898
+
899
+ @property
900
+ def e_ac_couple3_total_l2(self) -> float | None:
901
+ """Get AC Couple 3 L2 lifetime energy in kWh.
902
+
903
+ Returns:
904
+ AC Couple 3 L2 lifetime energy (÷10), or None if not available.
905
+ """
906
+ if self._runtime is None:
907
+ return None
908
+ val = self._runtime.midboxData.eACcouple3TotalL2
909
+ return val / 10.0 if val is not None else None
910
+
911
+ # ===========================================
912
+ # Energy Properties - AC Couple 4
913
+ # ===========================================
914
+
915
+ @property
916
+ def e_ac_couple4_today_l1(self) -> float | None:
917
+ """Get AC Couple 4 L1 energy today in kWh.
918
+
919
+ Returns:
920
+ AC Couple 4 L1 energy today (÷10), or None if not available.
921
+ """
922
+ if self._runtime is None:
923
+ return None
924
+ val = self._runtime.midboxData.eACcouple4TodayL1
925
+ return val / 10.0 if val is not None else None
926
+
927
+ @property
928
+ def e_ac_couple4_today_l2(self) -> float | None:
929
+ """Get AC Couple 4 L2 energy today in kWh.
930
+
931
+ Returns:
932
+ AC Couple 4 L2 energy today (÷10), or None if not available.
933
+ """
934
+ if self._runtime is None:
935
+ return None
936
+ val = self._runtime.midboxData.eACcouple4TodayL2
937
+ return val / 10.0 if val is not None else None
938
+
939
+ @property
940
+ def e_ac_couple4_total_l1(self) -> float | None:
941
+ """Get AC Couple 4 L1 lifetime energy in kWh.
942
+
943
+ Returns:
944
+ AC Couple 4 L1 lifetime energy (÷10), or None if not available.
945
+ """
946
+ if self._runtime is None:
947
+ return None
948
+ val = self._runtime.midboxData.eACcouple4TotalL1
949
+ return val / 10.0 if val is not None else None
950
+
951
+ @property
952
+ def e_ac_couple4_total_l2(self) -> float | None:
953
+ """Get AC Couple 4 L2 lifetime energy in kWh.
954
+
955
+ Returns:
956
+ AC Couple 4 L2 lifetime energy (÷10), or None if not available.
957
+ """
958
+ if self._runtime is None:
959
+ return None
960
+ val = self._runtime.midboxData.eACcouple4TotalL2
961
+ return val / 10.0 if val is not None else None
962
+
963
+ # ===========================================
964
+ # Energy Properties - Smart Load 1
965
+ # ===========================================
966
+
967
+ @property
968
+ def e_smart_load1_today_l1(self) -> float | None:
969
+ """Get Smart Load 1 L1 energy today in kWh.
970
+
971
+ Returns:
972
+ Smart Load 1 L1 energy today (÷10), or None if not available.
973
+ """
974
+ if self._runtime is None:
975
+ return None
976
+ val = self._runtime.midboxData.eSmartLoad1TodayL1
977
+ return val / 10.0 if val is not None else None
978
+
979
+ @property
980
+ def e_smart_load1_today_l2(self) -> float | None:
981
+ """Get Smart Load 1 L2 energy today in kWh.
982
+
983
+ Returns:
984
+ Smart Load 1 L2 energy today (÷10), or None if not available.
985
+ """
986
+ if self._runtime is None:
987
+ return None
988
+ val = self._runtime.midboxData.eSmartLoad1TodayL2
989
+ return val / 10.0 if val is not None else None
990
+
991
+ @property
992
+ def e_smart_load1_total_l1(self) -> float | None:
993
+ """Get Smart Load 1 L1 lifetime energy in kWh.
994
+
995
+ Returns:
996
+ Smart Load 1 L1 lifetime energy (÷10), or None if not available.
997
+ """
998
+ if self._runtime is None:
999
+ return None
1000
+ val = self._runtime.midboxData.eSmartLoad1TotalL1
1001
+ return val / 10.0 if val is not None else None
1002
+
1003
+ @property
1004
+ def e_smart_load1_total_l2(self) -> float | None:
1005
+ """Get Smart Load 1 L2 lifetime energy in kWh.
1006
+
1007
+ Returns:
1008
+ Smart Load 1 L2 lifetime energy (÷10), or None if not available.
1009
+ """
1010
+ if self._runtime is None:
1011
+ return None
1012
+ val = self._runtime.midboxData.eSmartLoad1TotalL2
1013
+ return val / 10.0 if val is not None else None
1014
+
1015
+ # ===========================================
1016
+ # Energy Properties - Smart Load 2
1017
+ # ===========================================
1018
+
1019
+ @property
1020
+ def e_smart_load2_today_l1(self) -> float | None:
1021
+ """Get Smart Load 2 L1 energy today in kWh.
1022
+
1023
+ Returns:
1024
+ Smart Load 2 L1 energy today (÷10), or None if not available.
1025
+ """
1026
+ if self._runtime is None:
1027
+ return None
1028
+ val = self._runtime.midboxData.eSmartLoad2TodayL1
1029
+ return val / 10.0 if val is not None else None
1030
+
1031
+ @property
1032
+ def e_smart_load2_today_l2(self) -> float | None:
1033
+ """Get Smart Load 2 L2 energy today in kWh.
1034
+
1035
+ Returns:
1036
+ Smart Load 2 L2 energy today (÷10), or None if not available.
1037
+ """
1038
+ if self._runtime is None:
1039
+ return None
1040
+ val = self._runtime.midboxData.eSmartLoad2TodayL2
1041
+ return val / 10.0 if val is not None else None
1042
+
1043
+ @property
1044
+ def e_smart_load2_total_l1(self) -> float | None:
1045
+ """Get Smart Load 2 L1 lifetime energy in kWh.
1046
+
1047
+ Returns:
1048
+ Smart Load 2 L1 lifetime energy (÷10), or None if not available.
1049
+ """
1050
+ if self._runtime is None:
1051
+ return None
1052
+ val = self._runtime.midboxData.eSmartLoad2TotalL1
1053
+ return val / 10.0 if val is not None else None
1054
+
1055
+ @property
1056
+ def e_smart_load2_total_l2(self) -> float | None:
1057
+ """Get Smart Load 2 L2 lifetime energy in kWh.
1058
+
1059
+ Returns:
1060
+ Smart Load 2 L2 lifetime energy (÷10), or None if not available.
1061
+ """
1062
+ if self._runtime is None:
1063
+ return None
1064
+ val = self._runtime.midboxData.eSmartLoad2TotalL2
1065
+ return val / 10.0 if val is not None else None
1066
+
1067
+ # ===========================================
1068
+ # Energy Properties - Smart Load 3
1069
+ # ===========================================
1070
+
1071
+ @property
1072
+ def e_smart_load3_today_l1(self) -> float | None:
1073
+ """Get Smart Load 3 L1 energy today in kWh.
1074
+
1075
+ Returns:
1076
+ Smart Load 3 L1 energy today (÷10), or None if not available.
1077
+ """
1078
+ if self._runtime is None:
1079
+ return None
1080
+ val = self._runtime.midboxData.eSmartLoad3TodayL1
1081
+ return val / 10.0 if val is not None else None
1082
+
1083
+ @property
1084
+ def e_smart_load3_today_l2(self) -> float | None:
1085
+ """Get Smart Load 3 L2 energy today in kWh.
1086
+
1087
+ Returns:
1088
+ Smart Load 3 L2 energy today (÷10), or None if not available.
1089
+ """
1090
+ if self._runtime is None:
1091
+ return None
1092
+ val = self._runtime.midboxData.eSmartLoad3TodayL2
1093
+ return val / 10.0 if val is not None else None
1094
+
1095
+ @property
1096
+ def e_smart_load3_total_l1(self) -> float | None:
1097
+ """Get Smart Load 3 L1 lifetime energy in kWh.
1098
+
1099
+ Returns:
1100
+ Smart Load 3 L1 lifetime energy (÷10), or None if not available.
1101
+ """
1102
+ if self._runtime is None:
1103
+ return None
1104
+ val = self._runtime.midboxData.eSmartLoad3TotalL1
1105
+ return val / 10.0 if val is not None else None
1106
+
1107
+ @property
1108
+ def e_smart_load3_total_l2(self) -> float | None:
1109
+ """Get Smart Load 3 L2 lifetime energy in kWh.
1110
+
1111
+ Returns:
1112
+ Smart Load 3 L2 lifetime energy (÷10), or None if not available.
1113
+ """
1114
+ if self._runtime is None:
1115
+ return None
1116
+ val = self._runtime.midboxData.eSmartLoad3TotalL2
1117
+ return val / 10.0 if val is not None else None
1118
+
1119
+ # ===========================================
1120
+ # Energy Properties - Smart Load 4
1121
+ # ===========================================
1122
+
1123
+ @property
1124
+ def e_smart_load4_today_l1(self) -> float | None:
1125
+ """Get Smart Load 4 L1 energy today in kWh.
1126
+
1127
+ Returns:
1128
+ Smart Load 4 L1 energy today (÷10), or None if not available.
1129
+ """
1130
+ if self._runtime is None:
1131
+ return None
1132
+ val = self._runtime.midboxData.eSmartLoad4TodayL1
1133
+ return val / 10.0 if val is not None else None
1134
+
1135
+ @property
1136
+ def e_smart_load4_today_l2(self) -> float | None:
1137
+ """Get Smart Load 4 L2 energy today in kWh.
1138
+
1139
+ Returns:
1140
+ Smart Load 4 L2 energy today (÷10), or None if not available.
1141
+ """
1142
+ if self._runtime is None:
1143
+ return None
1144
+ val = self._runtime.midboxData.eSmartLoad4TodayL2
1145
+ return val / 10.0 if val is not None else None
1146
+
1147
+ @property
1148
+ def e_smart_load4_total_l1(self) -> float | None:
1149
+ """Get Smart Load 4 L1 lifetime energy in kWh.
1150
+
1151
+ Returns:
1152
+ Smart Load 4 L1 lifetime energy (÷10), or None if not available.
1153
+ """
1154
+ if self._runtime is None:
1155
+ return None
1156
+ val = self._runtime.midboxData.eSmartLoad4TotalL1
1157
+ return val / 10.0 if val is not None else None
1158
+
1159
+ @property
1160
+ def e_smart_load4_total_l2(self) -> float | None:
1161
+ """Get Smart Load 4 L2 lifetime energy in kWh.
1162
+
1163
+ Returns:
1164
+ Smart Load 4 L2 lifetime energy (÷10), or None if not available.
1165
+ """
1166
+ if self._runtime is None:
1167
+ return None
1168
+ val = self._runtime.midboxData.eSmartLoad4TotalL2
1169
+ return val / 10.0 if val is not None else None
1170
+
1171
+ # ===========================================
1172
+ # Aggregate Energy Properties (L1 + L2)
1173
+ # ===========================================
1174
+
1175
+ def _sum_energy(self, l1: float | None, l2: float | None) -> float | None:
1176
+ """Sum L1 and L2 energy values, returning None if both are None.
1177
+
1178
+ Args:
1179
+ l1: L1 phase energy value or None
1180
+ l2: L2 phase energy value or None
1181
+
1182
+ Returns:
1183
+ Sum of L1 + L2, treating None as 0, or None if both are None.
1184
+ """
1185
+ if l1 is None and l2 is None:
1186
+ return None
1187
+ return (l1 or 0.0) + (l2 or 0.0)
1188
+
1189
+ # UPS Energy Aggregates
1190
+
1191
+ @property
1192
+ def e_ups_today(self) -> float | None:
1193
+ """Get total UPS energy today in kWh (L1 + L2).
1194
+
1195
+ Returns:
1196
+ Total UPS energy today, or None if not available.
1197
+ """
1198
+ return self._sum_energy(self.e_ups_today_l1, self.e_ups_today_l2)
1199
+
1200
+ @property
1201
+ def e_ups_total(self) -> float | None:
1202
+ """Get total UPS lifetime energy in kWh (L1 + L2).
1203
+
1204
+ Returns:
1205
+ Total UPS lifetime energy, or None if not available.
1206
+ """
1207
+ return self._sum_energy(self.e_ups_total_l1, self.e_ups_total_l2)
1208
+
1209
+ # Grid Export Energy Aggregates
1210
+
1211
+ @property
1212
+ def e_to_grid_today(self) -> float | None:
1213
+ """Get total grid export energy today in kWh (L1 + L2).
1214
+
1215
+ Returns:
1216
+ Total grid export energy today, or None if not available.
1217
+ """
1218
+ return self._sum_energy(self.e_to_grid_today_l1, self.e_to_grid_today_l2)
1219
+
1220
+ @property
1221
+ def e_to_grid_total(self) -> float | None:
1222
+ """Get total grid export lifetime energy in kWh (L1 + L2).
1223
+
1224
+ Returns:
1225
+ Total grid export lifetime energy, or None if not available.
1226
+ """
1227
+ return self._sum_energy(self.e_to_grid_total_l1, self.e_to_grid_total_l2)
1228
+
1229
+ # Grid Import Energy Aggregates
1230
+
1231
+ @property
1232
+ def e_to_user_today(self) -> float | None:
1233
+ """Get total grid import energy today in kWh (L1 + L2).
1234
+
1235
+ Returns:
1236
+ Total grid import energy today, or None if not available.
1237
+ """
1238
+ return self._sum_energy(self.e_to_user_today_l1, self.e_to_user_today_l2)
1239
+
1240
+ @property
1241
+ def e_to_user_total(self) -> float | None:
1242
+ """Get total grid import lifetime energy in kWh (L1 + L2).
1243
+
1244
+ Returns:
1245
+ Total grid import lifetime energy, or None if not available.
1246
+ """
1247
+ return self._sum_energy(self.e_to_user_total_l1, self.e_to_user_total_l2)
1248
+
1249
+ # Load Energy Aggregates
1250
+
1251
+ @property
1252
+ def e_load_today(self) -> float | None:
1253
+ """Get total load energy today in kWh (L1 + L2).
1254
+
1255
+ Returns:
1256
+ Total load energy today, or None if not available.
1257
+ """
1258
+ return self._sum_energy(self.e_load_today_l1, self.e_load_today_l2)
1259
+
1260
+ @property
1261
+ def e_load_total(self) -> float | None:
1262
+ """Get total load lifetime energy in kWh (L1 + L2).
1263
+
1264
+ Returns:
1265
+ Total load lifetime energy, or None if not available.
1266
+ """
1267
+ return self._sum_energy(self.e_load_total_l1, self.e_load_total_l2)
1268
+
1269
+ # AC Couple 1 Energy Aggregates
1270
+
1271
+ @property
1272
+ def e_ac_couple1_today(self) -> float | None:
1273
+ """Get total AC Couple 1 energy today in kWh (L1 + L2).
1274
+
1275
+ Returns:
1276
+ Total AC Couple 1 energy today, or None if not available.
1277
+ """
1278
+ return self._sum_energy(self.e_ac_couple1_today_l1, self.e_ac_couple1_today_l2)
1279
+
1280
+ @property
1281
+ def e_ac_couple1_total(self) -> float | None:
1282
+ """Get total AC Couple 1 lifetime energy in kWh (L1 + L2).
1283
+
1284
+ Returns:
1285
+ Total AC Couple 1 lifetime energy, or None if not available.
1286
+ """
1287
+ return self._sum_energy(self.e_ac_couple1_total_l1, self.e_ac_couple1_total_l2)
1288
+
1289
+ # AC Couple 2 Energy Aggregates
1290
+
1291
+ @property
1292
+ def e_ac_couple2_today(self) -> float | None:
1293
+ """Get total AC Couple 2 energy today in kWh (L1 + L2).
1294
+
1295
+ Returns:
1296
+ Total AC Couple 2 energy today, or None if not available.
1297
+ """
1298
+ return self._sum_energy(self.e_ac_couple2_today_l1, self.e_ac_couple2_today_l2)
1299
+
1300
+ @property
1301
+ def e_ac_couple2_total(self) -> float | None:
1302
+ """Get total AC Couple 2 lifetime energy in kWh (L1 + L2).
1303
+
1304
+ Returns:
1305
+ Total AC Couple 2 lifetime energy, or None if not available.
1306
+ """
1307
+ return self._sum_energy(self.e_ac_couple2_total_l1, self.e_ac_couple2_total_l2)
1308
+
1309
+ # AC Couple 3 Energy Aggregates
1310
+
1311
+ @property
1312
+ def e_ac_couple3_today(self) -> float | None:
1313
+ """Get total AC Couple 3 energy today in kWh (L1 + L2).
1314
+
1315
+ Returns:
1316
+ Total AC Couple 3 energy today, or None if not available.
1317
+ """
1318
+ return self._sum_energy(self.e_ac_couple3_today_l1, self.e_ac_couple3_today_l2)
1319
+
1320
+ @property
1321
+ def e_ac_couple3_total(self) -> float | None:
1322
+ """Get total AC Couple 3 lifetime energy in kWh (L1 + L2).
1323
+
1324
+ Returns:
1325
+ Total AC Couple 3 lifetime energy, or None if not available.
1326
+ """
1327
+ return self._sum_energy(self.e_ac_couple3_total_l1, self.e_ac_couple3_total_l2)
1328
+
1329
+ # AC Couple 4 Energy Aggregates
1330
+
1331
+ @property
1332
+ def e_ac_couple4_today(self) -> float | None:
1333
+ """Get total AC Couple 4 energy today in kWh (L1 + L2).
1334
+
1335
+ Returns:
1336
+ Total AC Couple 4 energy today, or None if not available.
1337
+ """
1338
+ return self._sum_energy(self.e_ac_couple4_today_l1, self.e_ac_couple4_today_l2)
1339
+
1340
+ @property
1341
+ def e_ac_couple4_total(self) -> float | None:
1342
+ """Get total AC Couple 4 lifetime energy in kWh (L1 + L2).
1343
+
1344
+ Returns:
1345
+ Total AC Couple 4 lifetime energy, or None if not available.
1346
+ """
1347
+ return self._sum_energy(self.e_ac_couple4_total_l1, self.e_ac_couple4_total_l2)
1348
+
1349
+ # Smart Load 1 Energy Aggregates
1350
+
1351
+ @property
1352
+ def e_smart_load1_today(self) -> float | None:
1353
+ """Get total Smart Load 1 energy today in kWh (L1 + L2).
1354
+
1355
+ Returns:
1356
+ Total Smart Load 1 energy today, or None if not available.
1357
+ """
1358
+ return self._sum_energy(self.e_smart_load1_today_l1, self.e_smart_load1_today_l2)
1359
+
1360
+ @property
1361
+ def e_smart_load1_total(self) -> float | None:
1362
+ """Get total Smart Load 1 lifetime energy in kWh (L1 + L2).
1363
+
1364
+ Returns:
1365
+ Total Smart Load 1 lifetime energy, or None if not available.
1366
+ """
1367
+ return self._sum_energy(self.e_smart_load1_total_l1, self.e_smart_load1_total_l2)
1368
+
1369
+ # Smart Load 2 Energy Aggregates
1370
+
1371
+ @property
1372
+ def e_smart_load2_today(self) -> float | None:
1373
+ """Get total Smart Load 2 energy today in kWh (L1 + L2).
1374
+
1375
+ Returns:
1376
+ Total Smart Load 2 energy today, or None if not available.
1377
+ """
1378
+ return self._sum_energy(self.e_smart_load2_today_l1, self.e_smart_load2_today_l2)
1379
+
1380
+ @property
1381
+ def e_smart_load2_total(self) -> float | None:
1382
+ """Get total Smart Load 2 lifetime energy in kWh (L1 + L2).
1383
+
1384
+ Returns:
1385
+ Total Smart Load 2 lifetime energy, or None if not available.
1386
+ """
1387
+ return self._sum_energy(self.e_smart_load2_total_l1, self.e_smart_load2_total_l2)
1388
+
1389
+ # Smart Load 3 Energy Aggregates
1390
+
1391
+ @property
1392
+ def e_smart_load3_today(self) -> float | None:
1393
+ """Get total Smart Load 3 energy today in kWh (L1 + L2).
1394
+
1395
+ Returns:
1396
+ Total Smart Load 3 energy today, or None if not available.
1397
+ """
1398
+ return self._sum_energy(self.e_smart_load3_today_l1, self.e_smart_load3_today_l2)
1399
+
1400
+ @property
1401
+ def e_smart_load3_total(self) -> float | None:
1402
+ """Get total Smart Load 3 lifetime energy in kWh (L1 + L2).
1403
+
1404
+ Returns:
1405
+ Total Smart Load 3 lifetime energy, or None if not available.
1406
+ """
1407
+ return self._sum_energy(self.e_smart_load3_total_l1, self.e_smart_load3_total_l2)
1408
+
1409
+ # Smart Load 4 Energy Aggregates
1410
+
1411
+ @property
1412
+ def e_smart_load4_today(self) -> float | None:
1413
+ """Get total Smart Load 4 energy today in kWh (L1 + L2).
1414
+
1415
+ Returns:
1416
+ Total Smart Load 4 energy today, or None if not available.
1417
+ """
1418
+ return self._sum_energy(self.e_smart_load4_today_l1, self.e_smart_load4_today_l2)
1419
+
1420
+ @property
1421
+ def e_smart_load4_total(self) -> float | None:
1422
+ """Get total Smart Load 4 lifetime energy in kWh (L1 + L2).
1423
+
1424
+ Returns:
1425
+ Total Smart Load 4 lifetime energy, or None if not available.
1426
+ """
1427
+ return self._sum_energy(self.e_smart_load4_total_l1, self.e_smart_load4_total_l2)