goodwe 0.3.1__py3-none-any.whl → 0.3.3__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.
goodwe/es.py CHANGED
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
15
15
 
16
16
 
17
17
  class ES(Inverter):
18
- """Class representing inverter of ES/EM/BP family"""
18
+ """Class representing inverter of ES/EM/BP family AKA platform 105"""
19
19
 
20
20
  _READ_DEVICE_VERSION_INFO: ProtocolCommand = Aa55ProtocolCommand("010200", "0182")
21
21
  _READ_DEVICE_RUNNING_DATA: ProtocolCommand = Aa55ProtocolCommand("010600", "0186")
@@ -67,7 +67,7 @@ class ES(Inverter):
67
67
  Voltage("vgrid", 34, "On-grid Voltage", Kind.AC),
68
68
  Current("igrid", 36, "On-grid Current", Kind.AC),
69
69
  Calculated("pgrid",
70
- lambda data: abs(read_bytes2(data, 38)) * (-1 if read_byte(data, 80) == 2 else 1),
70
+ lambda data: abs(read_bytes2_signed(data, 38)) * (-1 if read_byte(data, 80) == 2 else 1),
71
71
  "On-grid Export Power", "W", Kind.AC),
72
72
  Frequency("fgrid", 40, "On-grid Frequency", Kind.AC),
73
73
  Byte("grid_mode", 42, "Work Mode code", "", Kind.GRID),
@@ -87,7 +87,7 @@ class ES(Inverter):
87
87
  Energy("e_day", 67, "Today's PV Generation", Kind.PV),
88
88
  Energy("e_load_day", 69, "Today's Load", Kind.AC),
89
89
  Energy4("e_load_total", 71, "Total Load", Kind.AC),
90
- Power("total_power", 75, "Total Power", Kind.AC), # modbus 0x52c
90
+ PowerS("total_power", 75, "Total Power", Kind.AC), # modbus 0x52c
91
91
  Byte("effective_work_mode", 77, "Effective Work Mode code"),
92
92
  Integer("effective_relay_control", 78, "Effective Relay Control", "", None),
93
93
  Byte("grid_in_out", 80, "On-grid Mode code", "", Kind.GRID),
@@ -121,7 +121,7 @@ class ES(Inverter):
121
121
  round(read_voltage(data, 5) * read_current(data, 7)) +
122
122
  (abs(round(read_voltage(data, 10) * read_current(data, 18))) *
123
123
  (-1 if read_byte(data, 30) == 3 else 1)) -
124
- (abs(read_bytes2(data, 38)) * (-1 if read_byte(data, 80) == 2 else 1)),
124
+ (abs(read_bytes2_signed(data, 38)) * (-1 if read_byte(data, 80) == 2 else 1)),
125
125
  "House Consumption", "W", Kind.AC),
126
126
  )
127
127
 
goodwe/et.py CHANGED
@@ -7,7 +7,7 @@ from .exceptions import RequestRejectedException
7
7
  from .inverter import Inverter
8
8
  from .inverter import OperationMode
9
9
  from .inverter import SensorKind as Kind
10
- from .model import is_2_battery, is_4_mppt, is_single_phase
10
+ from .model import is_2_battery, is_4_mppt, is_745_platform, is_single_phase
11
11
  from .protocol import ProtocolCommand, ModbusReadCommand, ModbusWriteCommand, ModbusWriteMultiCommand
12
12
  from .sensor import *
13
13
 
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
15
15
 
16
16
 
17
17
  class ET(Inverter):
18
- """Class representing inverter of ET/EH/BT/BH or GE's GEH families"""
18
+ """Class representing inverter of ET/EH/BT/BH or GE's GEH families AKA platform 205 or 745"""
19
19
 
20
20
  # Modbus registers from offset 0x891c (35100), count 0x7d (125)
21
21
  __all_sensors: Tuple[Sensor, ...] = (
@@ -52,23 +52,23 @@ class ET(Inverter):
52
52
  Current("igrid", 35122, "On-grid L1 Current", Kind.AC),
53
53
  Frequency("fgrid", 35123, "On-grid L1 Frequency", Kind.AC),
54
54
  # 35124 reserved
55
- Power("pgrid", 35125, "On-grid L1 Power", Kind.AC),
55
+ PowerS("pgrid", 35125, "On-grid L1 Power", Kind.AC),
56
56
  Voltage("vgrid2", 35126, "On-grid L2 Voltage", Kind.AC),
57
57
  Current("igrid2", 35127, "On-grid L2 Current", Kind.AC),
58
58
  Frequency("fgrid2", 35128, "On-grid L2 Frequency", Kind.AC),
59
59
  # 35129 reserved
60
- Power("pgrid2", 35130, "On-grid L2 Power", Kind.AC),
60
+ PowerS("pgrid2", 35130, "On-grid L2 Power", Kind.AC),
61
61
  Voltage("vgrid3", 35131, "On-grid L3 Voltage", Kind.AC),
62
62
  Current("igrid3", 35132, "On-grid L3 Current", Kind.AC),
63
63
  Frequency("fgrid3", 35133, "On-grid L3 Frequency", Kind.AC),
64
64
  # 35134 reserved
65
- Power("pgrid3", 35135, "On-grid L3 Power", Kind.AC),
65
+ PowerS("pgrid3", 35135, "On-grid L3 Power", Kind.AC),
66
66
  Integer("grid_mode", 35136, "Grid Mode code", "", Kind.PV),
67
67
  Enum2("grid_mode_label", 35136, GRID_MODES, "Grid Mode", Kind.PV),
68
68
  # 35137 reserved
69
- Power("total_inverter_power", 35138, "Total Power", Kind.AC),
69
+ PowerS("total_inverter_power", 35138, "Total Power", Kind.AC),
70
70
  # 35139 reserved
71
- Power("active_power", 35140, "Active Power", Kind.GRID),
71
+ PowerS("active_power", 35140, "Active Power", Kind.GRID),
72
72
  Calculated("grid_in_out",
73
73
  lambda data: read_grid_mode(data, 35140),
74
74
  "On-grid Mode code", "", Kind.GRID),
@@ -84,29 +84,29 @@ class ET(Inverter):
84
84
  Frequency("backup_f1", 35147, "Back-up L1 Frequency", Kind.UPS),
85
85
  Integer("load_mode1", 35148, "Load Mode L1"),
86
86
  # 35149 reserved
87
- Power("backup_p1", 35150, "Back-up L1 Power", Kind.UPS),
87
+ PowerS("backup_p1", 35150, "Back-up L1 Power", Kind.UPS),
88
88
  Voltage("backup_v2", 35151, "Back-up L2 Voltage", Kind.UPS),
89
89
  Current("backup_i2", 35152, "Back-up L2 Current", Kind.UPS),
90
90
  Frequency("backup_f2", 35153, "Back-up L2 Frequency", Kind.UPS),
91
91
  Integer("load_mode2", 35154, "Load Mode L2"),
92
92
  # 35155 reserved
93
- Power("backup_p2", 35156, "Back-up L2 Power", Kind.UPS),
93
+ PowerS("backup_p2", 35156, "Back-up L2 Power", Kind.UPS),
94
94
  Voltage("backup_v3", 35157, "Back-up L3 Voltage", Kind.UPS),
95
95
  Current("backup_i3", 35158, "Back-up L3 Current", Kind.UPS),
96
96
  Frequency("backup_f3", 35159, "Back-up L3 Frequency", Kind.UPS),
97
97
  Integer("load_mode3", 35160, "Load Mode L3"),
98
98
  # 35161 reserved
99
- Power("backup_p3", 35162, "Back-up L3 Power", Kind.UPS),
99
+ PowerS("backup_p3", 35162, "Back-up L3 Power", Kind.UPS),
100
100
  # 35163 reserved
101
- Power("load_p1", 35164, "Load L1", Kind.AC),
101
+ PowerS("load_p1", 35164, "Load L1", Kind.AC),
102
102
  # 35165 reserved
103
- Power("load_p2", 35166, "Load L2", Kind.AC),
103
+ PowerS("load_p2", 35166, "Load L2", Kind.AC),
104
104
  # 35167 reserved
105
- Power("load_p3", 35168, "Load L3", Kind.AC),
105
+ PowerS("load_p3", 35168, "Load L3", Kind.AC),
106
106
  # 35169 reserved
107
- Power("backup_ptotal", 35170, "Back-up Load", Kind.UPS),
107
+ PowerS("backup_ptotal", 35170, "Back-up Load", Kind.UPS),
108
108
  # 35171 reserved
109
- Power("load_ptotal", 35172, "Load", Kind.AC),
109
+ PowerS("load_ptotal", 35172, "Load", Kind.AC),
110
110
  Integer("ups_load", 35173, "Ups Load", "%", Kind.UPS),
111
111
  Temp("temperature_air", 35174, "Inverter Temperature (Air)", Kind.AC),
112
112
  Temp("temperature_module", 35175, "Inverter Temperature (Module)"),
@@ -115,8 +115,8 @@ class ET(Inverter):
115
115
  Voltage("bus_voltage", 35178, "Bus Voltage", None),
116
116
  Voltage("nbus_voltage", 35179, "NBus Voltage", None),
117
117
  Voltage("vbattery1", 35180, "Battery Voltage", Kind.BAT),
118
- Current("ibattery1", 35181, "Battery Current", Kind.BAT),
119
- Power4("pbattery1", 35182, "Battery Power", Kind.BAT),
118
+ CurrentS("ibattery1", 35181, "Battery Current", Kind.BAT),
119
+ Power4S("pbattery1", 35182, "Battery Power", Kind.BAT),
120
120
  Integer("battery_mode", 35184, "Battery Mode code", "", Kind.BAT),
121
121
  Enum2("battery_mode_label", 35184, BATTERY_MODES, "Battery Mode", Kind.BAT),
122
122
  Integer("warning_code", 35185, "Warning code"),
@@ -149,8 +149,8 @@ class ET(Inverter):
149
149
  read_bytes4(data, 35109) +
150
150
  read_bytes4(data, 35113) +
151
151
  read_bytes4(data, 35117) +
152
- read_bytes4(data, 35182) -
153
- read_bytes2(data, 35140),
152
+ read_bytes4_signed(data, 35182) -
153
+ read_bytes2_signed(data, 35140),
154
154
  "House Consumption", "W", Kind.AC),
155
155
  )
156
156
 
@@ -226,10 +226,10 @@ class ET(Inverter):
226
226
  Integer("manufacture_code", 36002, "Manufacture Code"),
227
227
  Integer("meter_test_status", 36003, "Meter Test Status"), # 1: correct,2: reverse,3: incorrect,0: not checked
228
228
  Integer("meter_comm_status", 36004, "Meter Communication Status"), # 1 OK, 0 NotOK
229
- Power("active_power1", 36005, "Active Power L1", Kind.GRID),
230
- Power("active_power2", 36006, "Active Power L2", Kind.GRID),
231
- Power("active_power3", 36007, "Active Power L3", Kind.GRID),
232
- Power("active_power_total", 36008, "Active Power Total", Kind.GRID),
229
+ PowerS("active_power1", 36005, "Active Power L1", Kind.GRID),
230
+ PowerS("active_power2", 36006, "Active Power L2", Kind.GRID),
231
+ PowerS("active_power3", 36007, "Active Power L3", Kind.GRID),
232
+ PowerS("active_power_total", 36008, "Active Power Total", Kind.GRID),
233
233
  Reactive("reactive_power_total", 36009, "Reactive Power Total", Kind.GRID),
234
234
  Decimal("meter_power_factor1", 36010, 1000, "Meter Power Factor L1", "", Kind.GRID),
235
235
  Decimal("meter_power_factor2", 36011, 1000, "Meter Power Factor L2", "", Kind.GRID),
@@ -238,10 +238,10 @@ class ET(Inverter):
238
238
  Frequency("meter_freq", 36014, "Meter Frequency", Kind.GRID),
239
239
  Float("meter_e_total_exp", 36015, 1000, "Meter Total Energy (export)", "kWh", Kind.GRID),
240
240
  Float("meter_e_total_imp", 36017, 1000, "Meter Total Energy (import)", "kWh", Kind.GRID),
241
- Power4("meter_active_power1", 36019, "Meter Active Power L1", Kind.GRID),
242
- Power4("meter_active_power2", 36021, "Meter Active Power L2", Kind.GRID),
243
- Power4("meter_active_power3", 36023, "Meter Active Power L3", Kind.GRID),
244
- Power4("meter_active_power_total", 36025, "Meter Active Power Total", Kind.GRID),
241
+ Power4S("meter_active_power1", 36019, "Meter Active Power L1", Kind.GRID),
242
+ Power4S("meter_active_power2", 36021, "Meter Active Power L2", Kind.GRID),
243
+ Power4S("meter_active_power3", 36023, "Meter Active Power L3", Kind.GRID),
244
+ Power4S("meter_active_power_total", 36025, "Meter Active Power Total", Kind.GRID),
245
245
  Reactive4("meter_reactive_power1", 36027, "Meter Reactive Power L1", Kind.GRID),
246
246
  Reactive4("meter_reactive_power2", 36029, "Meter Reactive Power L2", Kind.GRID),
247
247
  Reactive4("meter_reactive_power3", 36031, "Meter Reactive Power L2", Kind.GRID),
@@ -253,7 +253,7 @@ class ET(Inverter):
253
253
  Integer("meter_type", 36043, "Meter Type", "", Kind.GRID), # (0: Single phase, 1: 3P3W, 2: 3P4W, 3: HomeKit)
254
254
  Integer("meter_sw_version", 36044, "Meter Software Version", "", Kind.GRID),
255
255
  # Sensors added in some ARM fw update, read when flag _has_meter_extended is on
256
- Power4("meter2_active_power", 36045, "Meter 2 Active Power", Kind.GRID),
256
+ Power4S("meter2_active_power", 36045, "Meter 2 Active Power", Kind.GRID),
257
257
  Float("meter2_e_total_exp", 36047, 1000, "Meter 2 Total Energy (export)", "kWh", Kind.GRID),
258
258
  Float("meter2_e_total_imp", 36049, 1000, "Meter 2 Total Energy (import)", "kWh", Kind.GRID),
259
259
  Integer("meter2_comm_status", 36051, "Meter 2 Communication Status"),
@@ -384,19 +384,25 @@ class ET(Inverter):
384
384
 
385
385
  Integer("load_control_mode", 47595, "Load Control Mode", "", Kind.AC),
386
386
  Integer("load_control_switch", 47596, "Load Control Switch", "", Kind.AC),
387
- Integer("load_control_soc", 47596, "Load Control SoC", "", Kind.AC),
387
+ Integer("load_control_soc", 47597, "Load Control SoC", "", Kind.AC),
388
388
 
389
389
  Integer("fast_charging_power", 47603, "Fast Charging Power", "%", Kind.BAT),
390
390
  )
391
391
 
392
392
  # Settings added in ARM firmware 22
393
393
  __settings_arm_fw_22: Tuple[Sensor, ...] = (
394
+ Long("peak_shaving_power_limit", 47542, "Peak Shaving Power Limit"),
395
+ Integer("peak_shaving_soc", 47544, "Peak Shaving SoC"),
394
396
  # EcoModeV2("eco_modeV2_5", 47571, "Eco Mode Version 2 Power Group 5"),
395
397
  # EcoModeV2("eco_modeV2_6", 47577, "Eco Mode Version 2 Power Group 6"),
396
398
  # EcoModeV2("eco_modeV2_7", 47583, "Eco Mode Version 2 Power Group 7"),
397
399
  PeakShavingMode("peak_shaving_mode", 47589, "Peak Shaving Mode"),
398
400
 
399
401
  Integer("dod_holding", 47602, "DoD Holding", "", Kind.BAT),
402
+ Integer("backup_mode_enable", 47605, "Backup Mode Switch"),
403
+ Integer("max_charge_power", 47606, "Max Charge Power"),
404
+ Integer("smart_charging_enable", 47609, "Smart Charging Mode Switch"),
405
+ Integer("eco_mode_enable", 47612, "Eco Mode Switch"),
400
406
  )
401
407
 
402
408
  def __init__(self, host: str, comm_addr: int = 0, timeout: int = 1, retries: int = 3):
@@ -626,6 +632,7 @@ class ET(Inverter):
626
632
  await self._set_offline(True)
627
633
  await self.write_setting('backup_supply', 1)
628
634
  await self.write_setting('cold_start', 4)
635
+ await self._clear_battery_mode_param()
629
636
  elif operation_mode == OperationMode.BACKUP:
630
637
  await self.write_setting('work_mode', 2)
631
638
  await self._set_offline(False)
@@ -636,13 +643,20 @@ class ET(Inverter):
636
643
  elif operation_mode == OperationMode.PEAK_SHAVING:
637
644
  await self.write_setting('work_mode', 4)
638
645
  await self._set_offline(False)
646
+ await self._clear_battery_mode_param()
639
647
  elif operation_mode in (OperationMode.ECO_CHARGE, OperationMode.ECO_DISCHARGE):
640
648
  if eco_mode_power < 0 or eco_mode_power > 100:
641
649
  raise ValueError()
642
650
  if eco_mode_soc < 0 or eco_mode_soc > 100:
643
651
  raise ValueError()
652
+
644
653
  eco_mode: EcoMode | Sensor = self._settings.get('eco_mode_1')
645
- await self._read_setting(eco_mode)
654
+ # Load the current values to try to detect schedule type
655
+ try:
656
+ await self._read_setting(eco_mode)
657
+ except ValueError:
658
+ pass
659
+ eco_mode.set_schedule_type(ScheduleType.ECO_MODE, is_745_platform(self))
646
660
  if operation_mode == OperationMode.ECO_CHARGE:
647
661
  await self.write_setting('eco_mode_1', eco_mode.encode_charge(eco_mode_power, eco_mode_soc))
648
662
  else:
goodwe/inverter.py CHANGED
@@ -76,8 +76,8 @@ class OperationMode(IntEnum):
76
76
  BACKUP = 2
77
77
  ECO = 3
78
78
  PEAK_SHAVING = 4
79
- ECO_CHARGE = 5
80
- ECO_DISCHARGE = 6
79
+ ECO_CHARGE = 10
80
+ ECO_DISCHARGE = 11
81
81
 
82
82
 
83
83
  class Inverter(ABC):
goodwe/model.py CHANGED
@@ -1,27 +1,32 @@
1
+ # Serial number tags to identify inverter type
1
2
  from .inverter import Inverter
2
3
 
3
- # Serial number tags to identify inverter type
4
- ET_MODEL_TAGS = ["ETU", "ETL", "ETR", "ETC", "EHU", "EHR", "EHB", "BTU", "BTN", "BTC", "BHU", "AES", "ABP", "HHI",
5
- "HSB", "HUA", "CUA",
6
- "ESN", "EMN", "ERN", "EBN", # ES Gen 2
7
- "HLB", "HMB", "HBB", "SPN"] # Gen 2
8
- ES_MODEL_TAGS = ["ESU", "EMU", "ESA", "BPS", "BPU", "EMJ", "IJL"]
9
- DT_MODEL_TAGS = ["DTU", "DTS",
4
+ PLATFORM_105_MODELS = ("ESU", "EMU", "ESA", "BPS", "BPU", "EMJ", "IJL")
5
+ PLATFORM_205_MODELS = ("ETU", "ETL", "ETR", "BHN", "EHU", "BHU", "EHR", "BTU")
6
+ PLATFORM_745_LV_MODELS = ("ESN", "EBN", "EMN", "SPN", "ERN", "ESC", "HLB", "HMB", "HBB", "EOA")
7
+ PLATFORM_745_HV_MODELS = ("ETT", "HTA", "HUB", "AEB", "SPB", "CUB", "EUB", "HEB", "ERB", "BTT", "ETF", "ARB", "URB",
8
+ "EBR")
9
+ PLATFORM_753_MODELS = ("AES", "HHI", "ABP", "EHB", "HSB", "HUA", "CUA")
10
+
11
+ ET_MODEL_TAGS = PLATFORM_205_MODELS + PLATFORM_745_LV_MODELS + PLATFORM_745_HV_MODELS + PLATFORM_753_MODELS + (
12
+ "ETC", "BTC", "BTN") # Qianhai
13
+ ES_MODEL_TAGS = PLATFORM_105_MODELS
14
+ DT_MODEL_TAGS = ("DTU", "DTS",
10
15
  "MSU", "MST", "MSC", "DSN", "DTN", "DST", "NSU", "SSN", "SST", "SSX", "SSY",
11
- "PSB", "PSC"]
16
+ "PSB", "PSC")
12
17
 
13
- SINGLE_PHASE_MODELS = ["DSN", "DST", "NSU", "SSN", "SST", "SSX", "SSY", # DT
18
+ SINGLE_PHASE_MODELS = ("DSN", "DST", "NSU", "SSN", "SST", "SSX", "SSY", # DT
14
19
  "MSU", "MST", "PSB", "PSC",
15
20
  "MSC", # Found on third gen MS
16
21
  "EHU", "EHR", "HSB", # ET
17
- "ESN", "EMN", "ERN", "EBN", "HLB", "HMB", "HBB", "SPN"] # ES Gen 2
22
+ "ESN", "EMN", "ERN", "EBN", "HLB", "HMB", "HBB", "SPN") # ES Gen 2
18
23
 
19
- MPPT3_MODELS = ["MSU", "MST", "PSC", "MSC",
20
- "25KET", "29K9ET"]
24
+ MPPT3_MODELS = ("MSU", "MST", "PSC", "MSC",
25
+ "25KET", "29K9ET")
21
26
 
22
- MPPT4_MODELS = ["HSB"]
27
+ MPPT4_MODELS = ("HSB",)
23
28
 
24
- BAT_2_MODELS = ["25KET", "29K9ET"]
29
+ BAT_2_MODELS = ("25KET", "29K9ET")
25
30
 
26
31
 
27
32
  def is_single_phase(inverter: Inverter) -> bool:
@@ -38,3 +43,8 @@ def is_4_mppt(inverter: Inverter) -> bool:
38
43
 
39
44
  def is_2_battery(inverter: Inverter) -> bool:
40
45
  return any(model in inverter.serial_number for model in BAT_2_MODELS)
46
+
47
+
48
+ def is_745_platform(inverter: Inverter) -> bool:
49
+ return any(model in inverter.serial_number for model in PLATFORM_745_LV_MODELS) or any(
50
+ model in inverter.serial_number for model in PLATFORM_745_HV_MODELS)
goodwe/sensor.py CHANGED
@@ -21,12 +21,13 @@ class ScheduleType(IntEnum):
21
21
  PEAK_SHAVING = 3,
22
22
  BACKUP_MODE = 4,
23
23
  SMART_CHARGE_MODE = 5,
24
- ECO_MODE_745 = 6
24
+ ECO_MODE_745 = 6,
25
+ NOT_SET = 85
25
26
 
26
27
  @classmethod
27
28
  def detect_schedule_type(cls, value: int) -> ScheduleType:
28
29
  """Detect schedule type from its on/off value"""
29
- if value in (0, -1, 85):
30
+ if value in (0, -1):
30
31
  return ScheduleType.ECO_MODE
31
32
  elif value in (1, -2):
32
33
  return ScheduleType.DRY_CONTACT_LOAD
@@ -40,6 +41,8 @@ class ScheduleType(IntEnum):
40
41
  return ScheduleType.SMART_CHARGE_MODE
41
42
  elif value in (6, -7):
42
43
  return ScheduleType.ECO_MODE_745
44
+ elif value == 85:
45
+ return ScheduleType.NOT_SET
43
46
  else:
44
47
  raise ValueError(f"{value}: on_off value {value} out of range.")
45
48
 
@@ -52,12 +55,13 @@ class ScheduleType(IntEnum):
52
55
 
53
56
  def decode_power(self, value: int) -> int:
54
57
  """Decode human readable value of power parameter"""
55
- if self == ScheduleType.ECO_MODE:
56
- return value
57
- elif self == ScheduleType.PEAK_SHAVING:
58
+ if self == ScheduleType.PEAK_SHAVING:
58
59
  return value * 10
59
- if self == ScheduleType.ECO_MODE_745:
60
+ elif self == ScheduleType.ECO_MODE_745:
60
61
  return int(value / 10)
62
+ elif self == ScheduleType.NOT_SET:
63
+ # Prevent out of range values when changing mode
64
+ return value if -100 <= value <= 100 else int(value / 10)
61
65
  else:
62
66
  return value
63
67
 
@@ -67,7 +71,7 @@ class ScheduleType(IntEnum):
67
71
  return value
68
72
  elif self == ScheduleType.PEAK_SHAVING:
69
73
  return int(value / 10)
70
- if self == ScheduleType.ECO_MODE_745:
74
+ elif self == ScheduleType.ECO_MODE_745:
71
75
  return value * 10
72
76
  else:
73
77
  return value
@@ -76,14 +80,14 @@ class ScheduleType(IntEnum):
76
80
  """Check if the value fits in allowed values range"""
77
81
  if self == ScheduleType.ECO_MODE:
78
82
  return -100 <= value <= 100
79
- if self == ScheduleType.ECO_MODE_745:
83
+ elif self == ScheduleType.ECO_MODE_745:
80
84
  return -1000 <= value <= 1000
81
85
  else:
82
86
  return True
83
87
 
84
88
 
85
89
  class Voltage(Sensor):
86
- """Sensor representing voltage [V] value encoded in 2 bytes"""
90
+ """Sensor representing voltage [V] value encoded in 2 (unsigned) bytes"""
87
91
 
88
92
  def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
89
93
  super().__init__(id_, offset, name, 2, "V", kind)
@@ -96,7 +100,7 @@ class Voltage(Sensor):
96
100
 
97
101
 
98
102
  class Current(Sensor):
99
- """Sensor representing current [A] value encoded in 2 bytes"""
103
+ """Sensor representing current [A] value encoded in 2 (unsigned) bytes"""
100
104
 
101
105
  def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
102
106
  super().__init__(id_, offset, name, 2, "A", kind)
@@ -108,6 +112,19 @@ class Current(Sensor):
108
112
  return encode_current(value)
109
113
 
110
114
 
115
+ class CurrentS(Sensor):
116
+ """Sensor representing current [A] value encoded in 2 (signed) bytes"""
117
+
118
+ def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
119
+ super().__init__(id_, offset, name, 2, "A", kind)
120
+
121
+ def read_value(self, data: ProtocolResponse):
122
+ return read_current_signed(data)
123
+
124
+ def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
125
+ return encode_current_signed(value)
126
+
127
+
111
128
  class Frequency(Sensor):
112
129
  """Sensor representing frequency [Hz] value encoded in 2 bytes"""
113
130
 
@@ -119,7 +136,7 @@ class Frequency(Sensor):
119
136
 
120
137
 
121
138
  class Power(Sensor):
122
- """Sensor representing power [W] value encoded in 2 bytes"""
139
+ """Sensor representing power [W] value encoded in 2 (unsigned) bytes"""
123
140
 
124
141
  def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
125
142
  super().__init__(id_, offset, name, 2, "W", kind)
@@ -128,8 +145,18 @@ class Power(Sensor):
128
145
  return read_bytes2(data)
129
146
 
130
147
 
148
+ class PowerS(Sensor):
149
+ """Sensor representing power [W] value encoded in 2 (signed) bytes"""
150
+
151
+ def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
152
+ super().__init__(id_, offset, name, 2, "W", kind)
153
+
154
+ def read_value(self, data: ProtocolResponse):
155
+ return read_bytes2_signed(data)
156
+
157
+
131
158
  class Power4(Sensor):
132
- """Sensor representing power [W] value encoded in 4 bytes"""
159
+ """Sensor representing power [W] value encoded in 4 (unsigned) bytes"""
133
160
 
134
161
  def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
135
162
  super().__init__(id_, offset, name, 4, "W", kind)
@@ -138,6 +165,16 @@ class Power4(Sensor):
138
165
  return read_bytes4(data)
139
166
 
140
167
 
168
+ class Power4S(Sensor):
169
+ """Sensor representing power [W] value encoded in 4 (signed) bytes"""
170
+
171
+ def __init__(self, id_: str, offset: int, name: str, kind: Optional[SensorKind]):
172
+ super().__init__(id_, offset, name, 4, "W", kind)
173
+
174
+ def read_value(self, data: ProtocolResponse):
175
+ return read_bytes4_signed(data)
176
+
177
+
141
178
  class Energy(Sensor):
142
179
  """Sensor representing energy [kWh] value encoded in 2 bytes"""
143
180
 
@@ -146,10 +183,7 @@ class Energy(Sensor):
146
183
 
147
184
  def read_value(self, data: ProtocolResponse):
148
185
  value = read_bytes2(data)
149
- if value == -1:
150
- return None
151
- else:
152
- return float(value) / 10
186
+ return float(value) / 10
153
187
 
154
188
 
155
189
  class Energy4(Sensor):
@@ -160,10 +194,7 @@ class Energy4(Sensor):
160
194
 
161
195
  def read_value(self, data: ProtocolResponse):
162
196
  value = read_bytes4(data)
163
- if value == -1:
164
- return None
165
- else:
166
- return float(value) / 10
197
+ return float(value) / 10
167
198
 
168
199
 
169
200
  class Apparent(Sensor):
@@ -173,7 +204,7 @@ class Apparent(Sensor):
173
204
  super().__init__(id_, offset, name, 2, "VA", kind)
174
205
 
175
206
  def read_value(self, data: ProtocolResponse):
176
- return read_bytes2(data)
207
+ return read_bytes2_signed(data)
177
208
 
178
209
 
179
210
  class Apparent4(Sensor):
@@ -183,7 +214,7 @@ class Apparent4(Sensor):
183
214
  super().__init__(id_, offset, name, 2, "VA", kind)
184
215
 
185
216
  def read_value(self, data: ProtocolResponse):
186
- return read_bytes4(data)
217
+ return read_bytes4_signed(data)
187
218
 
188
219
 
189
220
  class Reactive(Sensor):
@@ -193,7 +224,7 @@ class Reactive(Sensor):
193
224
  super().__init__(id_, offset, name, 2, "var", kind)
194
225
 
195
226
  def read_value(self, data: ProtocolResponse):
196
- return read_bytes2(data)
227
+ return read_bytes2_signed(data)
197
228
 
198
229
 
199
230
  class Reactive4(Sensor):
@@ -203,7 +234,7 @@ class Reactive4(Sensor):
203
234
  super().__init__(id_, offset, name, 2, "var", kind)
204
235
 
205
236
  def read_value(self, data: ProtocolResponse):
206
- return read_bytes4(data)
237
+ return read_bytes4_signed(data)
207
238
 
208
239
 
209
240
  class Temp(Sensor):
@@ -271,7 +302,7 @@ class ByteL(Byte):
271
302
 
272
303
 
273
304
  class Integer(Sensor):
274
- """Sensor representing signed int value encoded in 2 bytes"""
305
+ """Sensor representing unsigned int value encoded in 2 bytes"""
275
306
 
276
307
  def __init__(self, id_: str, offset: int, name: str, unit: str = "", kind: Optional[SensorKind] = None):
277
308
  super().__init__(id_, offset, name, 2, unit, kind)
@@ -279,12 +310,25 @@ class Integer(Sensor):
279
310
  def read_value(self, data: ProtocolResponse):
280
311
  return read_bytes2(data)
281
312
 
313
+ def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
314
+ return int.to_bytes(int(value), length=2, byteorder="big", signed=False)
315
+
316
+
317
+ class IntegerS(Sensor):
318
+ """Sensor representing signed int value encoded in 2 bytes"""
319
+
320
+ def __init__(self, id_: str, offset: int, name: str, unit: str = "", kind: Optional[SensorKind] = None):
321
+ super().__init__(id_, offset, name, 2, unit, kind)
322
+
323
+ def read_value(self, data: ProtocolResponse):
324
+ return read_bytes2_signed(data)
325
+
282
326
  def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
283
327
  return int.to_bytes(int(value), length=2, byteorder="big", signed=True)
284
328
 
285
329
 
286
330
  class Long(Sensor):
287
- """Sensor representing signed int value encoded in 4 bytes"""
331
+ """Sensor representing unsigned int value encoded in 4 bytes"""
288
332
 
289
333
  def __init__(self, id_: str, offset: int, name: str, unit: str = "", kind: Optional[SensorKind] = None):
290
334
  super().__init__(id_, offset, name, 4, unit, kind)
@@ -292,6 +336,19 @@ class Long(Sensor):
292
336
  def read_value(self, data: ProtocolResponse):
293
337
  return read_bytes4(data)
294
338
 
339
+ def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
340
+ return int.to_bytes(int(value), length=4, byteorder="big", signed=False)
341
+
342
+
343
+ class LongS(Sensor):
344
+ """Sensor representing signed int value encoded in 4 bytes"""
345
+
346
+ def __init__(self, id_: str, offset: int, name: str, unit: str = "", kind: Optional[SensorKind] = None):
347
+ super().__init__(id_, offset, name, 4, unit, kind)
348
+
349
+ def read_value(self, data: ProtocolResponse):
350
+ return read_bytes4_signed(data)
351
+
295
352
  def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
296
353
  return int.to_bytes(int(value), length=4, byteorder="big", signed=True)
297
354
 
@@ -390,7 +447,7 @@ class EnumBitmap4(Sensor):
390
447
  raise NotImplementedError()
391
448
 
392
449
  def read(self, data: ProtocolResponse):
393
- bits = read_bytes4(data, self.offset)
450
+ bits = read_bytes4_signed(data, self.offset)
394
451
  return decode_bitmap(bits if bits != -1 else 0, self._labels)
395
452
 
396
453
 
@@ -449,6 +506,14 @@ class EcoMode(ABC):
449
506
  def is_eco_discharge_mode(self) -> bool:
450
507
  """Answer if it represents the emulated 24/7 fulltime discharge mode"""
451
508
 
509
+ @abstractmethod
510
+ def get_schedule_type(self) -> ScheduleType:
511
+ """Answer the schedule type"""
512
+
513
+ @abstractmethod
514
+ def set_schedule_type(self, schedule_type: ScheduleType, is745: bool):
515
+ """Set the schedule type"""
516
+
452
517
 
453
518
  class EcoModeV1(Sensor, EcoMode):
454
519
  """Sensor representing Eco Mode Battery Power Group encoded in 8 bytes"""
@@ -483,7 +548,7 @@ class EcoModeV1(Sensor, EcoMode):
483
548
  self.end_m = read_byte(data)
484
549
  if self.end_m < 0 or self.end_m > 59:
485
550
  raise ValueError(f"{self.id_}: end_m value {self.end_m} out of range.")
486
- self.power = read_bytes2(data) # negative=charge, positive=discharge
551
+ self.power = read_bytes2_signed(data) # negative=charge, positive=discharge
487
552
  if self.power < -100 or self.power > 100:
488
553
  raise ValueError(f"{self.id_}: power value {self.power} out of range.")
489
554
  self.on_off = read_byte(data)
@@ -491,8 +556,6 @@ class EcoModeV1(Sensor, EcoMode):
491
556
  raise ValueError(f"{self.id_}: on_off value {self.on_off} out of range.")
492
557
  self.day_bits = read_byte(data)
493
558
  self.days = decode_day_of_week(self.day_bits)
494
- if self.day_bits < 0:
495
- raise ValueError(f"{self.id_}: day_bits value {self.day_bits} out of range.")
496
559
  return self
497
560
 
498
561
  def encode_value(self, value: Any, register_value: bytes = None) -> bytes:
@@ -534,6 +597,14 @@ class EcoModeV1(Sensor, EcoMode):
534
597
  and self.day_bits == 127 \
535
598
  and self.power > 0
536
599
 
600
+ def get_schedule_type(self) -> ScheduleType:
601
+ """Answer the schedule type"""
602
+ return ScheduleType.ECO_MODE
603
+
604
+ def set_schedule_type(self, schedule_type: ScheduleType, is745: bool):
605
+ """Set the schedule type"""
606
+ pass
607
+
537
608
  def as_eco_mode_v2(self) -> EcoModeV2:
538
609
  """Convert V1 to V2 EcoMode"""
539
610
  result = EcoModeV2(self.id_, self.offset, self.name)
@@ -590,15 +661,13 @@ class Schedule(Sensor, EcoMode):
590
661
  self.schedule_type = ScheduleType.detect_schedule_type(self.on_off)
591
662
  self.day_bits = read_byte(data)
592
663
  self.days = decode_day_of_week(self.day_bits)
593
- if self.day_bits < 0:
594
- raise ValueError(f"{self.id_}: day_bits value {self.day_bits} out of range.")
595
- self.power = read_bytes2(data) # negative=charge, positive=discharge
664
+ self.power = read_bytes2_signed(data) # negative=charge, positive=discharge
596
665
  if not self.schedule_type.is_in_range(self.power):
597
666
  raise ValueError(f"{self.id_}: power value {self.power} out of range.")
598
- self.soc = read_bytes2(data)
667
+ self.soc = read_bytes2_signed(data)
599
668
  if self.soc < 0 or self.soc > 100:
600
669
  raise ValueError(f"{self.id_}: SoC value {self.soc} out of range.")
601
- self.month_bits = read_bytes2(data)
670
+ self.month_bits = read_bytes2_signed(data)
602
671
  self.months = decode_months(self.month_bits)
603
672
  return self
604
673
 
@@ -653,6 +722,19 @@ class Schedule(Sensor, EcoMode):
653
722
  and self.power > 0 \
654
723
  and (self.month_bits == 0 or self.month_bits == 0x0fff)
655
724
 
725
+ def get_schedule_type(self) -> ScheduleType:
726
+ """Answer the schedule type"""
727
+ return self.schedule_type
728
+
729
+ def set_schedule_type(self, schedule_type: ScheduleType, is745: bool):
730
+ """Set the schedule type"""
731
+ if schedule_type == ScheduleType.ECO_MODE:
732
+ # try to keep-reuse the type, use is745 only when necessary
733
+ if self.schedule_type not in (ScheduleType.ECO_MODE, ScheduleType.ECO_MODE_745):
734
+ self.schedule_type = ScheduleType.ECO_MODE_745 if is745 else ScheduleType.ECO_MODE
735
+ else:
736
+ self.schedule_type = schedule_type
737
+
656
738
  def as_eco_mode_v1(self) -> EcoModeV1:
657
739
  """Convert V2 to V1 EcoMode"""
658
740
  result = EcoModeV1(self.id_, self.offset, self.name)
@@ -704,6 +786,14 @@ def read_byte(buffer: ProtocolResponse, offset: int = None) -> int:
704
786
 
705
787
 
706
788
  def read_bytes2(buffer: ProtocolResponse, offset: int = None) -> int:
789
+ """Retrieve 2 byte (unsigned int) value from buffer"""
790
+ if offset is not None:
791
+ buffer.seek(offset)
792
+ value = int.from_bytes(buffer.read(2), byteorder="big", signed=False)
793
+ return value if value != 0xffff else 0
794
+
795
+
796
+ def read_bytes2_signed(buffer: ProtocolResponse, offset: int = None) -> int:
707
797
  """Retrieve 2 byte (signed int) value from buffer"""
708
798
  if offset is not None:
709
799
  buffer.seek(offset)
@@ -711,6 +801,14 @@ def read_bytes2(buffer: ProtocolResponse, offset: int = None) -> int:
711
801
 
712
802
 
713
803
  def read_bytes4(buffer: ProtocolResponse, offset: int = None) -> int:
804
+ """Retrieve 4 byte (unsigned int) value from buffer"""
805
+ if offset is not None:
806
+ buffer.seek(offset)
807
+ value = int.from_bytes(buffer.read(4), byteorder="big", signed=False)
808
+ return value if value != 0xffffffff else 0
809
+
810
+
811
+ def read_bytes4_signed(buffer: ProtocolResponse, offset: int = None) -> int:
714
812
  """Retrieve 4 byte (signed int) value from buffer"""
715
813
  if offset is not None:
716
814
  buffer.seek(offset)
@@ -736,20 +834,28 @@ def read_float4(buffer: ProtocolResponse, offset: int = None) -> float:
736
834
 
737
835
 
738
836
  def read_voltage(buffer: ProtocolResponse, offset: int = None) -> float:
739
- """Retrieve voltage [V] value (2 bytes) from buffer"""
837
+ """Retrieve voltage [V] value (2 unsigned bytes) from buffer"""
740
838
  if offset is not None:
741
839
  buffer.seek(offset)
742
- value = int.from_bytes(buffer.read(2), byteorder="big", signed=True)
743
- return float(value) / 10
840
+ value = int.from_bytes(buffer.read(2), byteorder="big", signed=False)
841
+ return float(value) / 10 if value != 0xffff else 0
744
842
 
745
843
 
746
844
  def encode_voltage(value: Any) -> bytes:
747
- """Encode voltage value to raw (2 bytes) payload"""
748
- return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=True)
845
+ """Encode voltage value to raw (2 unsigned bytes) payload"""
846
+ return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=False)
749
847
 
750
848
 
751
849
  def read_current(buffer: ProtocolResponse, offset: int = None) -> float:
752
- """Retrieve current [A] value (2 bytes) from buffer"""
850
+ """Retrieve current [A] value (2 unsigned bytes) from buffer"""
851
+ if offset is not None:
852
+ buffer.seek(offset)
853
+ value = int.from_bytes(buffer.read(2), byteorder="big", signed=False)
854
+ return float(value) / 10 if value != 0xffff else 0
855
+
856
+
857
+ def read_current_signed(buffer: ProtocolResponse, offset: int = None) -> float:
858
+ """Retrieve current [A] value (2 signed bytes) from buffer"""
753
859
  if offset is not None:
754
860
  buffer.seek(offset)
755
861
  value = int.from_bytes(buffer.read(2), byteorder="big", signed=True)
@@ -757,7 +863,12 @@ def read_current(buffer: ProtocolResponse, offset: int = None) -> float:
757
863
 
758
864
 
759
865
  def encode_current(value: Any) -> bytes:
760
- """Encode current value to raw (2 bytes) payload"""
866
+ """Encode current value to raw (2 unsigned bytes) payload"""
867
+ return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=False)
868
+
869
+
870
+ def encode_current_signed(value: Any) -> bytes:
871
+ """Encode current value to raw (2 signed bytes) payload"""
761
872
  return int.to_bytes(int(value * 10), length=2, byteorder="big", signed=True)
762
873
 
763
874
 
@@ -809,7 +920,7 @@ def encode_datetime(value: Any) -> bytes:
809
920
 
810
921
  def read_grid_mode(buffer: ProtocolResponse, offset: int = None) -> int:
811
922
  """Retrieve 'grid mode' sign value from buffer"""
812
- value = read_bytes2(buffer, offset)
923
+ value = read_bytes2_signed(buffer, offset)
813
924
  if value < -90:
814
925
  return 2
815
926
  elif value >= 90:
@@ -835,6 +946,10 @@ def decode_bitmap(value: int, bitmap: Dict[int, str]) -> str:
835
946
 
836
947
 
837
948
  def decode_day_of_week(data: int) -> str:
949
+ if data == -1:
950
+ return "Mon-Sun"
951
+ elif data == 0:
952
+ return ""
838
953
  bits = bin(data)[2:]
839
954
  daynames = list(DAY_NAMES)
840
955
  days = ""
@@ -848,7 +963,7 @@ def decode_day_of_week(data: int) -> str:
848
963
 
849
964
 
850
965
  def decode_months(data: int) -> str | None:
851
- if data == 0 or data == 0x0fff:
966
+ if data <= 0 or data == 0x0fff:
852
967
  return None
853
968
  bits = bin(data)[2:]
854
969
  monthnames = list(MONTH_NAMES)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goodwe
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Read data from GoodWe inverter via local network
5
5
  Home-page: https://github.com/marcelblijleven/goodwe
6
6
  Author: Martin Letenay, Marcel Blijleven
@@ -0,0 +1,16 @@
1
+ goodwe/__init__.py,sha256=PInrrZEpTmMOQKk494vIz8EKSaw_qLBNz-6t9eLIUcg,5642
2
+ goodwe/const.py,sha256=Nw-nd4UJuqUOLfbmOrxTHEdS1AuaTDSpZzQqR6tBb8w,7912
3
+ goodwe/dt.py,sha256=bI53MVdZjtxTYU2qJLO8icsvF6UiXrkgH95V3iUwXT0,10581
4
+ goodwe/es.py,sha256=KZLBydRSzzAnbI8-o_-sKiJeunJGuCKr7N8V8_Ft__U,22434
5
+ goodwe/et.py,sha256=P4H5Q17Gx8V8Y9os5HfiECsVIZ1znx13pGRFzc93n54,39228
6
+ goodwe/exceptions.py,sha256=I6PHG0GTWgxNrDVZwJZBnyzItRq5eiM6ci23-EEsn1I,1012
7
+ goodwe/inverter.py,sha256=uiQdti_4lGVLrSG94GgxgALWuevY6hl9ekdHQTomR-0,10325
8
+ goodwe/modbus.py,sha256=ZPib-zKnOVE5zc0RNnhlf0w_26QBees1ScWGo6bAj0o,4685
9
+ goodwe/model.py,sha256=dWBjMFJMnhZoUdDd9fGT54DERDANz4TirK0Wy8kWMbk,2068
10
+ goodwe/protocol.py,sha256=pUkXTP2DqpKXGO7rbRfHq1x82Y1QM6OiRVx8cAtS0sM,13162
11
+ goodwe/sensor.py,sha256=cPZCC3_9gk5zPVTkzhuDa6byz_dM4xCyE8FtV5nxqgg,36845
12
+ goodwe-0.3.3.dist-info/LICENSE,sha256=aZAhk3lRdYT1YZV-IKRHISEcc_KNUmgfuNO3QhRamNM,1073
13
+ goodwe-0.3.3.dist-info/METADATA,sha256=0sEhEL3Uv3TSMsOTCqZSE1YEpeoDD609fcQodacEIvs,3050
14
+ goodwe-0.3.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
+ goodwe-0.3.3.dist-info/top_level.txt,sha256=kKoiqiVvAxDaDJYMZZQLgHQj9cuWT1MXLfXElTDuf8s,7
16
+ goodwe-0.3.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- goodwe/__init__.py,sha256=PInrrZEpTmMOQKk494vIz8EKSaw_qLBNz-6t9eLIUcg,5642
2
- goodwe/const.py,sha256=Nw-nd4UJuqUOLfbmOrxTHEdS1AuaTDSpZzQqR6tBb8w,7912
3
- goodwe/dt.py,sha256=bI53MVdZjtxTYU2qJLO8icsvF6UiXrkgH95V3iUwXT0,10581
4
- goodwe/es.py,sha256=Hla0gIxeKmSlad3F2yREaN5lz8Dwo-sxOcNXF0ESCes,22402
5
- goodwe/et.py,sha256=Ui_xuUZHmqHO-AKyi2BWgms2fxgpWTjv1Ag1B9UVYvU,38405
6
- goodwe/exceptions.py,sha256=I6PHG0GTWgxNrDVZwJZBnyzItRq5eiM6ci23-EEsn1I,1012
7
- goodwe/inverter.py,sha256=HvVtFBz5Zvl1je1V2AuLqpoB1vsur_gDHu-6SjNbJ8o,10323
8
- goodwe/modbus.py,sha256=ZPib-zKnOVE5zc0RNnhlf0w_26QBees1ScWGo6bAj0o,4685
9
- goodwe/model.py,sha256=Yy662c6VpuLozxo1Q7Llw1g34UhT5qJd9_rITIjmEd4,1523
10
- goodwe/protocol.py,sha256=pUkXTP2DqpKXGO7rbRfHq1x82Y1QM6OiRVx8cAtS0sM,13162
11
- goodwe/sensor.py,sha256=02_vM3zIufaLa8C3IvtuUq76abftxZITFCfGeG_GMWs,32353
12
- goodwe-0.3.1.dist-info/LICENSE,sha256=aZAhk3lRdYT1YZV-IKRHISEcc_KNUmgfuNO3QhRamNM,1073
13
- goodwe-0.3.1.dist-info/METADATA,sha256=4UDROUHfYZOaFF9cbsNuEECaG9jyzuERUsJrXSFB7Sk,3050
14
- goodwe-0.3.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
15
- goodwe-0.3.1.dist-info/top_level.txt,sha256=kKoiqiVvAxDaDJYMZZQLgHQj9cuWT1MXLfXElTDuf8s,7
16
- goodwe-0.3.1.dist-info/RECORD,,