pylxpweb 0.5.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.
@@ -543,3 +543,885 @@ class MIDRuntimePropertiesMixin:
543
543
  True if runtime data is available.
544
544
  """
545
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)
@@ -104,13 +104,26 @@ class ParallelGroup:
104
104
  Args:
105
105
  serial_number: Serial number of first inverter in group.
106
106
  """
107
- from contextlib import suppress
107
+ import logging
108
108
 
109
109
  from pylxpweb.exceptions import LuxpowerAPIError, LuxpowerConnectionError
110
110
 
111
- # Keep existing cached data on error
112
- with suppress(LuxpowerAPIError, LuxpowerConnectionError):
111
+ _logger = logging.getLogger(__name__)
112
+
113
+ try:
113
114
  self._energy = await self._client.api.devices.get_parallel_energy(serial_number)
115
+ _logger.debug(
116
+ "Parallel group %s energy data fetched: todayYielding=%s",
117
+ self.name,
118
+ self._energy.todayYielding if self._energy else None,
119
+ )
120
+ except (LuxpowerAPIError, LuxpowerConnectionError) as e:
121
+ # Keep existing cached data on error, but log the failure
122
+ _logger.warning(
123
+ "Failed to fetch parallel group %s energy data: %s",
124
+ self.name,
125
+ e,
126
+ )
114
127
 
115
128
  async def get_combined_energy(self) -> dict[str, float]:
116
129
  """Get combined energy statistics for all inverters in group.
pylxpweb/models.py CHANGED
@@ -769,12 +769,13 @@ class MidboxData(BaseModel):
769
769
 
770
770
  Note: Voltages are in decivolts (÷10), currents in centiamps (÷100),
771
771
  frequency in centihertz (÷100). Power values are in watts (no scaling).
772
+ Energy values are in deciwatt-hours (÷10 for kWh).
772
773
  """
773
774
 
774
775
  status: int
775
776
  serverTime: str
776
777
  deviceTime: str
777
- # Grid voltages (10 for volts, e.g., 2418 = 241.8V)
778
+ # Grid voltages (÷10 for volts, e.g., 2418 = 241.8V)
778
779
  gridRmsVolt: int
779
780
  upsRmsVolt: int
780
781
  genRmsVolt: int
@@ -784,7 +785,7 @@ class MidboxData(BaseModel):
784
785
  upsL2RmsVolt: int
785
786
  genL1RmsVolt: int
786
787
  genL2RmsVolt: int
787
- # Currents (100 for amps)
788
+ # Currents (÷100 for amps)
788
789
  gridL1RmsCurr: int
789
790
  gridL2RmsCurr: int
790
791
  loadL1RmsCurr: int
@@ -808,9 +809,86 @@ class MidboxData(BaseModel):
808
809
  smartPort2Status: int
809
810
  smartPort3Status: int
810
811
  smartPort4Status: int
811
- # Grid frequency (100 for Hz)
812
+ # Grid frequency (÷100 for Hz)
812
813
  gridFreq: int
813
814
 
815
+ # ===========================================
816
+ # Energy Fields (÷10 for kWh)
817
+ # Optional - not all devices report all fields
818
+ # ===========================================
819
+
820
+ # UPS Energy (Today and Lifetime)
821
+ eUpsTodayL1: int | None = None
822
+ eUpsTodayL2: int | None = None
823
+ eUpsTotalL1: int | None = None
824
+ eUpsTotalL2: int | None = None
825
+
826
+ # Grid Export Energy (Today and Lifetime)
827
+ eToGridTodayL1: int | None = None
828
+ eToGridTodayL2: int | None = None
829
+ eToGridTotalL1: int | None = None
830
+ eToGridTotalL2: int | None = None
831
+
832
+ # Grid Import Energy (Today and Lifetime)
833
+ eToUserTodayL1: int | None = None
834
+ eToUserTodayL2: int | None = None
835
+ eToUserTotalL1: int | None = None
836
+ eToUserTotalL2: int | None = None
837
+
838
+ # Load Energy (Today and Lifetime)
839
+ eLoadTodayL1: int | None = None
840
+ eLoadTodayL2: int | None = None
841
+ eLoadTotalL1: int | None = None
842
+ eLoadTotalL2: int | None = None
843
+
844
+ # AC Couple 1 Energy (Today and Lifetime)
845
+ eACcouple1TodayL1: int | None = None
846
+ eACcouple1TodayL2: int | None = None
847
+ eACcouple1TotalL1: int | None = None
848
+ eACcouple1TotalL2: int | None = None
849
+
850
+ # AC Couple 2 Energy (Today and Lifetime)
851
+ eACcouple2TodayL1: int | None = None
852
+ eACcouple2TodayL2: int | None = None
853
+ eACcouple2TotalL1: int | None = None
854
+ eACcouple2TotalL2: int | None = None
855
+
856
+ # AC Couple 3 Energy (Today and Lifetime)
857
+ eACcouple3TodayL1: int | None = None
858
+ eACcouple3TodayL2: int | None = None
859
+ eACcouple3TotalL1: int | None = None
860
+ eACcouple3TotalL2: int | None = None
861
+
862
+ # AC Couple 4 Energy (Today and Lifetime)
863
+ eACcouple4TodayL1: int | None = None
864
+ eACcouple4TodayL2: int | None = None
865
+ eACcouple4TotalL1: int | None = None
866
+ eACcouple4TotalL2: int | None = None
867
+
868
+ # Smart Load 1 Energy (Today and Lifetime)
869
+ eSmartLoad1TodayL1: int | None = None
870
+ eSmartLoad1TodayL2: int | None = None
871
+ eSmartLoad1TotalL1: int | None = None
872
+ eSmartLoad1TotalL2: int | None = None
873
+
874
+ # Smart Load 2 Energy (Today and Lifetime)
875
+ eSmartLoad2TodayL1: int | None = None
876
+ eSmartLoad2TodayL2: int | None = None
877
+ eSmartLoad2TotalL1: int | None = None
878
+ eSmartLoad2TotalL2: int | None = None
879
+
880
+ # Smart Load 3 Energy (Today and Lifetime)
881
+ eSmartLoad3TodayL1: int | None = None
882
+ eSmartLoad3TodayL2: int | None = None
883
+ eSmartLoad3TotalL1: int | None = None
884
+ eSmartLoad3TotalL2: int | None = None
885
+
886
+ # Smart Load 4 Energy (Today and Lifetime)
887
+ eSmartLoad4TodayL1: int | None = None
888
+ eSmartLoad4TodayL2: int | None = None
889
+ eSmartLoad4TotalL1: int | None = None
890
+ eSmartLoad4TotalL2: int | None = None
891
+
814
892
 
815
893
  class MidboxRuntime(BaseModel):
816
894
  """GridBOSS/MID device runtime response."""
@@ -234,6 +234,7 @@ class InverterRuntimeData:
234
234
  pv_total_power=float(pv1_power + pv2_power + pv3_power),
235
235
  # Battery
236
236
  battery_voltage=apply_scale(get_reg(4), ScaleFactor.SCALE_100),
237
+ battery_current=apply_scale(get_reg(75), ScaleFactor.SCALE_100), # Battery current (A)
237
238
  battery_soc=get_reg(5),
238
239
  battery_charge_power=float(charge_power),
239
240
  battery_discharge_power=float(discharge_power),
@@ -264,8 +265,13 @@ class InverterRuntimeData:
264
265
  internal_temperature=float(get_reg(61)),
265
266
  radiator_temperature_1=float(get_reg(62)),
266
267
  radiator_temperature_2=float(get_reg(63)),
268
+ battery_control_temperature=float(get_reg(65)),
267
269
  # Status
268
270
  device_status=get_reg(0),
271
+ # BMS data (registers 88-90)
272
+ battery_soh=get_reg(88, 100), # State of Health (%)
273
+ fault_code=get_reg(89), # BMS fault code
274
+ warning_code=get_reg(90), # BMS warning code
269
275
  )
270
276
 
271
277
 
@@ -43,6 +43,11 @@ HOLD_REGISTER_GROUPS = {
43
43
  "battery": (90, 40), # Registers 90-129: Battery config
44
44
  }
45
45
 
46
+ # Serial number is stored in input registers 115-119 (5 registers, 10 ASCII chars)
47
+ # Each register contains 2 ASCII characters: low byte = char[0], high byte = char[1]
48
+ SERIAL_NUMBER_START_REGISTER = 115
49
+ SERIAL_NUMBER_REGISTER_COUNT = 5
50
+
46
51
 
47
52
  class ModbusTransport(BaseTransport):
48
53
  """Modbus TCP transport for local inverter communication.
@@ -347,42 +352,34 @@ class ModbusTransport(BaseTransport):
347
352
  async def read_runtime(self) -> InverterRuntimeData:
348
353
  """Read runtime data via Modbus input registers.
349
354
 
355
+ Note: Register reads are serialized (not concurrent) to prevent
356
+ transaction ID desynchronization issues with pymodbus and some
357
+ Modbus TCP gateways (e.g., Waveshare RS485-to-Ethernet adapters).
358
+
350
359
  Returns:
351
360
  Runtime data with all values properly scaled
352
361
 
353
362
  Raises:
354
363
  TransportReadError: If read operation fails
355
364
  """
356
- # Read all input register groups concurrently
357
- group_names = list(INPUT_REGISTER_GROUPS.keys())
358
- tasks = [
359
- self._read_input_registers(start, count)
360
- for start, count in INPUT_REGISTER_GROUPS.values()
361
- ]
362
-
363
- results = await asyncio.gather(*tasks, return_exceptions=True)
364
-
365
- # Check for errors in results
366
- for group_name, result in zip(group_names, results, strict=True):
367
- if isinstance(result, Exception):
365
+ # Read register groups sequentially to avoid transaction ID issues
366
+ # See: https://github.com/joyfulhouse/pylxpweb/issues/95
367
+ input_registers: dict[int, int] = {}
368
+
369
+ for group_name, (start, count) in INPUT_REGISTER_GROUPS.items():
370
+ try:
371
+ values = await self._read_input_registers(start, count)
372
+ for offset, value in enumerate(values):
373
+ input_registers[start + offset] = value
374
+ except Exception as e:
368
375
  _LOGGER.error(
369
376
  "Failed to read register group '%s': %s",
370
377
  group_name,
371
- result,
378
+ e,
372
379
  )
373
380
  raise TransportReadError(
374
- f"Failed to read register group '{group_name}': {result}"
375
- ) from result
376
-
377
- # Combine results into single register dict
378
- input_registers: dict[int, int] = {}
379
- for (_group_name, (start, _count)), values in zip(
380
- INPUT_REGISTER_GROUPS.items(), results, strict=True
381
- ):
382
- # Type narrowing: we've verified no exceptions above
383
- assert isinstance(values, list)
384
- for offset, value in enumerate(values):
385
- input_registers[start + offset] = value
381
+ f"Failed to read register group '{group_name}': {e}"
382
+ ) from e
386
383
 
387
384
  return InverterRuntimeData.from_modbus_registers(input_registers)
388
385
 
@@ -392,38 +389,35 @@ class ModbusTransport(BaseTransport):
392
389
  Energy data comes from the same input registers as runtime data,
393
390
  so we read the relevant groups.
394
391
 
392
+ Note: Register reads are serialized to prevent transaction ID issues.
393
+
395
394
  Returns:
396
395
  Energy data with all values in kWh
397
396
 
398
397
  Raises:
399
398
  TransportReadError: If read operation fails
400
399
  """
401
- # Read energy-related register groups
400
+ # Read energy-related register groups sequentially
402
401
  groups_needed = ["status_energy", "advanced"]
403
- group_list = [(n, s) for n, s in INPUT_REGISTER_GROUPS.items() if n in groups_needed]
404
- tasks = [self._read_input_registers(start, count) for _name, (start, count) in group_list]
402
+ input_registers: dict[int, int] = {}
405
403
 
406
- results = await asyncio.gather(*tasks, return_exceptions=True)
404
+ for group_name, (start, count) in INPUT_REGISTER_GROUPS.items():
405
+ if group_name not in groups_needed:
406
+ continue
407
407
 
408
- # Check for errors in results
409
- for (group_name, _), result in zip(group_list, results, strict=True):
410
- if isinstance(result, Exception):
408
+ try:
409
+ values = await self._read_input_registers(start, count)
410
+ for offset, value in enumerate(values):
411
+ input_registers[start + offset] = value
412
+ except Exception as e:
411
413
  _LOGGER.error(
412
414
  "Failed to read energy register group '%s': %s",
413
415
  group_name,
414
- result,
416
+ e,
415
417
  )
416
418
  raise TransportReadError(
417
- f"Failed to read energy register group '{group_name}': {result}"
418
- ) from result
419
-
420
- # Combine results
421
- input_registers: dict[int, int] = {}
422
- for (_group_name, (start, _count)), values in zip(group_list, results, strict=True):
423
- # Type narrowing: we've verified no exceptions above
424
- assert isinstance(values, list)
425
- for offset, value in enumerate(values):
426
- input_registers[start + offset] = value
419
+ f"Failed to read energy register group '{group_name}': {e}"
420
+ ) from e
427
421
 
428
422
  return InverterEnergyData.from_modbus_registers(input_registers)
429
423
 
@@ -555,3 +549,69 @@ class ModbusTransport(BaseTransport):
555
549
  await self._write_holding_registers(start_address, values)
556
550
 
557
551
  return True
552
+
553
+ async def read_serial_number(self) -> str:
554
+ """Read inverter serial number from input registers 115-119.
555
+
556
+ The serial number is stored as 10 ASCII characters across 5 registers.
557
+ Each register contains 2 characters: low byte = char[0], high byte = char[1].
558
+
559
+ This can be used to:
560
+ - Validate the user-entered serial matches the actual device
561
+ - Auto-discover the serial during setup
562
+ - Detect cable swaps in multi-inverter setups
563
+
564
+ Returns:
565
+ 10-character serial number string (e.g., "BA12345678")
566
+
567
+ Raises:
568
+ TransportReadError: If read operation fails
569
+
570
+ Example:
571
+ >>> transport = ModbusTransport(host="192.168.1.100", serial="")
572
+ >>> await transport.connect()
573
+ >>> actual_serial = await transport.read_serial_number()
574
+ >>> print(f"Connected to inverter: {actual_serial}")
575
+ """
576
+ values = await self._read_input_registers(
577
+ SERIAL_NUMBER_START_REGISTER, SERIAL_NUMBER_REGISTER_COUNT
578
+ )
579
+
580
+ # Decode ASCII characters from register values
581
+ chars: list[str] = []
582
+ for value in values:
583
+ low_byte = value & 0xFF
584
+ high_byte = (value >> 8) & 0xFF
585
+ # Filter out non-printable characters
586
+ if 32 <= low_byte <= 126:
587
+ chars.append(chr(low_byte))
588
+ if 32 <= high_byte <= 126:
589
+ chars.append(chr(high_byte))
590
+
591
+ serial = "".join(chars)
592
+ _LOGGER.debug("Read serial number from Modbus: %s", serial)
593
+ return serial
594
+
595
+ async def validate_serial(self, expected_serial: str) -> bool:
596
+ """Validate that the connected inverter matches the expected serial.
597
+
598
+ Args:
599
+ expected_serial: The serial number the user expects to connect to
600
+
601
+ Returns:
602
+ True if serials match, False otherwise
603
+
604
+ Raises:
605
+ TransportReadError: If read operation fails
606
+ """
607
+ actual_serial = await self.read_serial_number()
608
+ matches = actual_serial == expected_serial
609
+
610
+ if not matches:
611
+ _LOGGER.warning(
612
+ "Serial mismatch: expected %s, got %s",
613
+ expected_serial,
614
+ actual_serial,
615
+ )
616
+
617
+ return matches
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pylxpweb
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Python client library for Luxpower/EG4 inverter web monitoring API
5
5
  Keywords: luxpower,eg4,inverter,solar,api,client
6
6
  Author: Bryan Li
@@ -11,7 +11,7 @@ pylxpweb/constants/registers.py,sha256=V5B3Bkvgbp-8mJ_tVpN8z5GM0OnWrLE2LOR4DfYEH
11
11
  pylxpweb/constants/scaling.py,sha256=l1QHf4Fa1VQKqcVK9snTHy9BOB0mNzb3d5bGynZSmJ8,17819
12
12
  pylxpweb/devices/__init__.py,sha256=gWJ-lHEwfIm7bFwMx4TRDG-eaKg7jObCzSGyflvEfls,788
13
13
  pylxpweb/devices/_firmware_update_mixin.py,sha256=LkwS36dSvoE6iTlCk3osRqcOySgEG2MD09pPnbnaIEU,21045
14
- pylxpweb/devices/_mid_runtime_properties.py,sha256=7UHm-27HetxKyqwb2raIhbYIO7Ve8H0ulpA5tKm-QlA,15100
14
+ pylxpweb/devices/_mid_runtime_properties.py,sha256=bV_66CH5Uyw4F3kGvWyU8TEWln5njUeJVnzG7E9Q1MI,44156
15
15
  pylxpweb/devices/base.py,sha256=Hdr7O-ct-Ouju8O8XqmzfGMAHKa5JuchElhSye2SYDk,3627
16
16
  pylxpweb/devices/battery.py,sha256=6YQ3vG70kPu18Av6RA3Rd0u8qktSgJbVRa-Jkyveq-4,18032
17
17
  pylxpweb/devices/battery_bank.py,sha256=lQurUHx8THXgMQMB3ZVudcIVEGF9Lo0w1mCn-NSfZrc,9620
@@ -23,7 +23,7 @@ pylxpweb/devices/inverters/generic.py,sha256=dfydpVSa38fHCptfaCPDhE1Q3tpP8OPizgO
23
23
  pylxpweb/devices/inverters/hybrid.py,sha256=rRncXS0SfVxWLKE7PHTqbQMTDHkiAZvFlfojzKwUpo0,9203
24
24
  pylxpweb/devices/mid_device.py,sha256=xXCr5XcL3eRA6TqoN_pxxg99mVkWdX4GXfOQQXumEeU,6167
25
25
  pylxpweb/devices/models.py,sha256=OAMGZ9FCkgE3jY9xIky5Qf71dLzgi5bSd_dng-YMwak,3931
26
- pylxpweb/devices/parallel_group.py,sha256=Fbwad4604PQnI0B6paHrwZmmidrv5ceWjLhIN66Dito,11525
26
+ pylxpweb/devices/parallel_group.py,sha256=kij59OTKUMF8DiuEP9_Tf_4kH-xU640F8F1vmtIIHlc,11963
27
27
  pylxpweb/devices/station.py,sha256=r7616lb8DUx_BSI20AWBB4bzqn2l6zYw2P4PCs9Kdzw,34067
28
28
  pylxpweb/endpoints/__init__.py,sha256=lzHDUiFCoyqRgncBYs5_EHy4I8I5CJAReTx8o4TdbTk,934
29
29
  pylxpweb/endpoints/analytics.py,sha256=Zyi9lVDXGr-4cvpv-POdLaF1OE3qgOcYwyzMNvL1xzI,14482
@@ -35,18 +35,18 @@ pylxpweb/endpoints/firmware.py,sha256=vwuPRzc0TcDNpY2ygtLslUDn8LyxVh11uHum4mzofo
35
35
  pylxpweb/endpoints/forecasting.py,sha256=0Zs4qGdqIFrNsKn94qSpDixQJrWJIadfPs8XULbEn4w,3836
36
36
  pylxpweb/endpoints/plants.py,sha256=zUl1vdQXpQSdG2YhzZfAIRyeSfjbxmTZIqZW4DBcD2g,16278
37
37
  pylxpweb/exceptions.py,sha256=-D-dbUFPMGupzhiV52X9Kz3vx7UOvx67lsCx6hR0KWI,671
38
- pylxpweb/models.py,sha256=I5FT9tdX1D9L3Jf5Lpz4fxdjTltDsXADQFLB2EpOC4U,38258
38
+ pylxpweb/models.py,sha256=rczIBZDOaJUEjwQhiBJ5Ll_MJNsJCf0k1rYKnLbp9Sc,40967
39
39
  pylxpweb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  pylxpweb/registers.py,sha256=Chk1uIoc5DjapSquEHYtBYgiPKuPhTjVn7PMl5JuPF0,12946
41
41
  pylxpweb/transports/__init__.py,sha256=xvLjLWS0VaiyQJqgiuxwq3_3uHETUIMmmZZOnNDKSdU,2119
42
42
  pylxpweb/transports/capabilities.py,sha256=IOJ_eWaLFInKKXObXjdTHXYz1UZNgLyP6QvUhAfWK7k,3460
43
- pylxpweb/transports/data.py,sha256=o02IzK5eQFHYmPOLCLOEKmteh31llEnpYD9mnJY_Crs,18193
43
+ pylxpweb/transports/data.py,sha256=VE6FzVUPyqsnDCOxjsaDkkZgz2APNFCPJdMmdEvn1Iw,18571
44
44
  pylxpweb/transports/exceptions.py,sha256=re2wCq2h25TIhXKrz7BjZjA1G1sZtTjZzVooTNHsaWU,1437
45
45
  pylxpweb/transports/factory.py,sha256=hcorD4SHOouoBG5qAP-0TqJbKMPah2kTl-z58UQ-t8E,3380
46
46
  pylxpweb/transports/http.py,sha256=O93lxfU052YLKesx-opwAq_gHnAN9n_PR3tb1wbfM6I,13358
47
- pylxpweb/transports/modbus.py,sha256=xUSmUvVYGU_tdxoFbWEkDh-T2XqEQgrBQDj2JmrUQO4,19412
47
+ pylxpweb/transports/modbus.py,sha256=A3-Q_G1oj-wwv-ZXiKnE4G0lQ9Gh-07C0O2OCOep2Uo,21418
48
48
  pylxpweb/transports/protocol.py,sha256=yIsZJIJ1x0zO1YC0ymEY2Y7T4Ld3zPGojubr_rPsrl0,6069
49
- pylxpweb-0.5.0.dist-info/WHEEL,sha256=KSLUh82mDPEPk0Bx0ScXlWL64bc8KmzIPNcpQZFV-6E,79
50
- pylxpweb-0.5.0.dist-info/entry_points.txt,sha256=4BNqpz9UhA301Z2CKJy1914L4AlHVHHrweIem2qk64g,76
51
- pylxpweb-0.5.0.dist-info/METADATA,sha256=hKCq_wGt_j_mgXb131apjOlpTP4HmUKxM2cZuN6B_yY,17575
52
- pylxpweb-0.5.0.dist-info/RECORD,,
49
+ pylxpweb-0.5.2.dist-info/WHEEL,sha256=KSLUh82mDPEPk0Bx0ScXlWL64bc8KmzIPNcpQZFV-6E,79
50
+ pylxpweb-0.5.2.dist-info/entry_points.txt,sha256=4BNqpz9UhA301Z2CKJy1914L4AlHVHHrweIem2qk64g,76
51
+ pylxpweb-0.5.2.dist-info/METADATA,sha256=_t1ulJPWbf7d3YeR2xUkIFopVMlfRMNUhe-6qoC35fg,17575
52
+ pylxpweb-0.5.2.dist-info/RECORD,,