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.
- pylxpweb/devices/_mid_runtime_properties.py +882 -0
- pylxpweb/devices/parallel_group.py +16 -3
- pylxpweb/models.py +81 -3
- pylxpweb/transports/data.py +6 -0
- pylxpweb/transports/modbus.py +103 -43
- {pylxpweb-0.5.0.dist-info → pylxpweb-0.5.2.dist-info}/METADATA +1 -1
- {pylxpweb-0.5.0.dist-info → pylxpweb-0.5.2.dist-info}/RECORD +9 -9
- {pylxpweb-0.5.0.dist-info → pylxpweb-0.5.2.dist-info}/WHEEL +0 -0
- {pylxpweb-0.5.0.dist-info → pylxpweb-0.5.2.dist-info}/entry_points.txt +0 -0
|
@@ -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
|
-
|
|
107
|
+
import logging
|
|
108
108
|
|
|
109
109
|
from pylxpweb.exceptions import LuxpowerAPIError, LuxpowerConnectionError
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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."""
|
pylxpweb/transports/data.py
CHANGED
|
@@ -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
|
|
pylxpweb/transports/modbus.py
CHANGED
|
@@ -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
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
378
|
+
e,
|
|
372
379
|
)
|
|
373
380
|
raise TransportReadError(
|
|
374
|
-
f"Failed to read register group '{group_name}': {
|
|
375
|
-
) from
|
|
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
|
-
|
|
404
|
-
tasks = [self._read_input_registers(start, count) for _name, (start, count) in group_list]
|
|
402
|
+
input_registers: dict[int, int] = {}
|
|
405
403
|
|
|
406
|
-
|
|
404
|
+
for group_name, (start, count) in INPUT_REGISTER_GROUPS.items():
|
|
405
|
+
if group_name not in groups_needed:
|
|
406
|
+
continue
|
|
407
407
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
416
|
+
e,
|
|
415
417
|
)
|
|
416
418
|
raise TransportReadError(
|
|
417
|
-
f"Failed to read energy register group '{group_name}': {
|
|
418
|
-
) from
|
|
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
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
50
|
-
pylxpweb-0.5.
|
|
51
|
-
pylxpweb-0.5.
|
|
52
|
-
pylxpweb-0.5.
|
|
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,,
|
|
File without changes
|
|
File without changes
|