PySaxPower 1.0.1__tar.gz → 2.0.1__tar.gz

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.
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: PySaxPower
3
+ Version: 2.0.1
4
+ Summary: Python package to read and write data from/to SAX Power Home Plus batteries.
5
+ Author: Hannes
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Hans-4/pysaxpower
8
+ Requires-Python: >=3.7
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pymodbus==3.15.0
12
+ Dynamic: license-file
13
+
14
+ # PySaxPower
15
+
16
+ **Python package to read and write data from/to SAX Power Home Plus batteries.**
17
+
18
+ ---
19
+
20
+ ## Overview
21
+
22
+ Here’s a more polished and concise version of your text with improved clarity, flow, and grammar:
23
+
24
+ ---
25
+
26
+ With **PySaxPower**, you can read Modbus registers from **SAX Power Home Plus** batteries.
27
+ The package is divided into two protocol implementations: **SunSpec** and **Basic**.
28
+ The **Basic** protocol supports read-only operations.
29
+ View the [manual](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) for more informations.
30
+
31
+ > **Note:**
32
+ > Due to a firmware error in SAX devices, only the SunSpec protocol supports writing to registers.
33
+ > To use this function, please ensure that at least firmware versions `Master V61` and `Gateway V54`, are installed on your battery. If this is not the case, you will need to update your firmware.
34
+ > You have to contact the customer support for this.
35
+
36
+ ---
37
+
38
+ ## Supported Devices
39
+
40
+
41
+ | **Device** | **Documentation** |
42
+ | --------------------------------- |----------------------------------------------------------------------------------|
43
+ | Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/en/products/sax-power-home-plus-7-7-kwh/) |
44
+
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install PySaxPower
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Usage
57
+
58
+ ### Sunspec
59
+
60
+ #### Read register
61
+ ```python
62
+ from pysaxpower import PowerHomePlusSunspec
63
+ from pysaxpower import SunSpecRegister
64
+
65
+ device = PowerHomePlusSunspec("192.168.178.60")
66
+
67
+ register = SunSpecRegister()
68
+
69
+ read_values = {
70
+ "Soc": register.CurrentSoC,
71
+ "Battery Power": register.ActivePower_Storage_Sum,
72
+ "Grid Power": register.ActiveGridPower,
73
+ }
74
+
75
+ values = device.get_formatted_values(read_values)
76
+ ```
77
+
78
+ #### Write register
79
+ ```python
80
+ from pysaxpower import PowerHomePlusSunspec
81
+ from pysaxpower import SunSpecRegister
82
+
83
+ device = PowerHomePlusSunspec("192.168.178.60")
84
+
85
+ register = SunSpecRegister()
86
+
87
+ write_values = {
88
+ register.ControlMode: 1
89
+ }
90
+
91
+ device.write_registers(write_values)
92
+ ```
93
+
94
+ ---
95
+
96
+ ### Basic
97
+ ```python
98
+ from pysaxpower import PowerHomePlusBasic
99
+ from pysaxpower import BasicRegister
100
+
101
+ device = PowerHomePlusBasic("192.168.178.60")
102
+
103
+ register = BasicRegister
104
+
105
+ read_values = {
106
+ "Operating Mode": register.OperatingMode,
107
+ "Soc": register.SOC,
108
+ "Battery Power": register.ActivePower,
109
+ "Grid Power": register.GridPower,
110
+ }
111
+
112
+ values = device.get_values(read_values)
113
+ print(values)
114
+
115
+ device.close()
116
+ ```
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: PySaxPower
3
+ Version: 2.0.1
4
+ Summary: Python package to read and write data from/to SAX Power Home Plus batteries.
5
+ Author: Hannes
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Hans-4/pysaxpower
8
+ Requires-Python: >=3.7
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pymodbus==3.15.0
12
+ Dynamic: license-file
13
+
14
+ # PySaxPower
15
+
16
+ **Python package to read and write data from/to SAX Power Home Plus batteries.**
17
+
18
+ ---
19
+
20
+ ## Overview
21
+
22
+ Here’s a more polished and concise version of your text with improved clarity, flow, and grammar:
23
+
24
+ ---
25
+
26
+ With **PySaxPower**, you can read Modbus registers from **SAX Power Home Plus** batteries.
27
+ The package is divided into two protocol implementations: **SunSpec** and **Basic**.
28
+ The **Basic** protocol supports read-only operations.
29
+ View the [manual](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) for more informations.
30
+
31
+ > **Note:**
32
+ > Due to a firmware error in SAX devices, only the SunSpec protocol supports writing to registers.
33
+ > To use this function, please ensure that at least firmware versions `Master V61` and `Gateway V54`, are installed on your battery. If this is not the case, you will need to update your firmware.
34
+ > You have to contact the customer support for this.
35
+
36
+ ---
37
+
38
+ ## Supported Devices
39
+
40
+
41
+ | **Device** | **Documentation** |
42
+ | --------------------------------- |----------------------------------------------------------------------------------|
43
+ | Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/en/products/sax-power-home-plus-7-7-kwh/) |
44
+
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install PySaxPower
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Usage
57
+
58
+ ### Sunspec
59
+
60
+ #### Read register
61
+ ```python
62
+ from pysaxpower import PowerHomePlusSunspec
63
+ from pysaxpower import SunSpecRegister
64
+
65
+ device = PowerHomePlusSunspec("192.168.178.60")
66
+
67
+ register = SunSpecRegister()
68
+
69
+ read_values = {
70
+ "Soc": register.CurrentSoC,
71
+ "Battery Power": register.ActivePower_Storage_Sum,
72
+ "Grid Power": register.ActiveGridPower,
73
+ }
74
+
75
+ values = device.get_formatted_values(read_values)
76
+ ```
77
+
78
+ #### Write register
79
+ ```python
80
+ from pysaxpower import PowerHomePlusSunspec
81
+ from pysaxpower import SunSpecRegister
82
+
83
+ device = PowerHomePlusSunspec("192.168.178.60")
84
+
85
+ register = SunSpecRegister()
86
+
87
+ write_values = {
88
+ register.ControlMode: 1
89
+ }
90
+
91
+ device.write_registers(write_values)
92
+ ```
93
+
94
+ ---
95
+
96
+ ### Basic
97
+ ```python
98
+ from pysaxpower import PowerHomePlusBasic
99
+ from pysaxpower import BasicRegister
100
+
101
+ device = PowerHomePlusBasic("192.168.178.60")
102
+
103
+ register = BasicRegister
104
+
105
+ read_values = {
106
+ "Operating Mode": register.OperatingMode,
107
+ "Soc": register.SOC,
108
+ "Battery Power": register.ActivePower,
109
+ "Grid Power": register.GridPower,
110
+ }
111
+
112
+ values = device.get_values(read_values)
113
+ print(values)
114
+
115
+ device.close()
116
+ ```
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ PySaxPower.egg-info/PKG-INFO
5
+ PySaxPower.egg-info/SOURCES.txt
6
+ PySaxPower.egg-info/dependency_links.txt
7
+ PySaxPower.egg-info/requires.txt
8
+ PySaxPower.egg-info/top_level.txt
9
+ pysaxpower/__init__.py
10
+ pysaxpower/basic_protocol/__init__.py
11
+ pysaxpower/basic_protocol/basic_register.py
12
+ pysaxpower/basic_protocol/power_home_plus_basic.py
13
+ pysaxpower/sunspec/__init__.py
14
+ pysaxpower/sunspec/power_home_plus_sunspec.py
15
+ pysaxpower/sunspec/register.py
16
+ pysaxpower/sunspec/scalefactors.py
@@ -0,0 +1,103 @@
1
+ # PySaxPower
2
+
3
+ **Python package to read and write data from/to SAX Power Home Plus batteries.**
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ Here’s a more polished and concise version of your text with improved clarity, flow, and grammar:
10
+
11
+ ---
12
+
13
+ With **PySaxPower**, you can read Modbus registers from **SAX Power Home Plus** batteries.
14
+ The package is divided into two protocol implementations: **SunSpec** and **Basic**.
15
+ The **Basic** protocol supports read-only operations.
16
+ View the [manual](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) for more informations.
17
+
18
+ > **Note:**
19
+ > Due to a firmware error in SAX devices, only the SunSpec protocol supports writing to registers.
20
+ > To use this function, please ensure that at least firmware versions `Master V61` and `Gateway V54`, are installed on your battery. If this is not the case, you will need to update your firmware.
21
+ > You have to contact the customer support for this.
22
+
23
+ ---
24
+
25
+ ## Supported Devices
26
+
27
+
28
+ | **Device** | **Documentation** |
29
+ | --------------------------------- |----------------------------------------------------------------------------------|
30
+ | Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/en/products/sax-power-home-plus-7-7-kwh/) |
31
+
32
+
33
+ ---
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install PySaxPower
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Usage
44
+
45
+ ### Sunspec
46
+
47
+ #### Read register
48
+ ```python
49
+ from pysaxpower import PowerHomePlusSunspec
50
+ from pysaxpower import SunSpecRegister
51
+
52
+ device = PowerHomePlusSunspec("192.168.178.60")
53
+
54
+ register = SunSpecRegister()
55
+
56
+ read_values = {
57
+ "Soc": register.CurrentSoC,
58
+ "Battery Power": register.ActivePower_Storage_Sum,
59
+ "Grid Power": register.ActiveGridPower,
60
+ }
61
+
62
+ values = device.get_formatted_values(read_values)
63
+ ```
64
+
65
+ #### Write register
66
+ ```python
67
+ from pysaxpower import PowerHomePlusSunspec
68
+ from pysaxpower import SunSpecRegister
69
+
70
+ device = PowerHomePlusSunspec("192.168.178.60")
71
+
72
+ register = SunSpecRegister()
73
+
74
+ write_values = {
75
+ register.ControlMode: 1
76
+ }
77
+
78
+ device.write_registers(write_values)
79
+ ```
80
+
81
+ ---
82
+
83
+ ### Basic
84
+ ```python
85
+ from pysaxpower import PowerHomePlusBasic
86
+ from pysaxpower import BasicRegister
87
+
88
+ device = PowerHomePlusBasic("192.168.178.60")
89
+
90
+ register = BasicRegister
91
+
92
+ read_values = {
93
+ "Operating Mode": register.OperatingMode,
94
+ "Soc": register.SOC,
95
+ "Battery Power": register.ActivePower,
96
+ "Grid Power": register.GridPower,
97
+ }
98
+
99
+ values = device.get_values(read_values)
100
+ print(values)
101
+
102
+ device.close()
103
+ ```
@@ -4,17 +4,17 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PySaxPower"
7
- version = "1.0.1"
8
- description = "Package for reading Sax devices via modbus"
7
+ version = "2.0.1"
8
+ description = "Python package to read and write data from/to SAX Power Home Plus batteries."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
11
11
  license = "MIT"
12
12
  authors = [
13
- {name = "Hannes", email = "hannes.210@gmx.de"}
13
+ {name = "Hannes"}
14
14
  ]
15
15
  dependencies = [
16
16
  "pymodbus==3.15.0"
17
17
  ]
18
18
 
19
19
  [project.urls]
20
- Homepage = "https://github.com/Hans-4/pysaxpower"
20
+ Homepage = "https://github.com/Hans-4/pysaxpower"
@@ -0,0 +1,5 @@
1
+ from pysaxpower.sunspec.power_home_plus_sunspec import PowerHomePlusSunspec
2
+ from pysaxpower.sunspec.register import SunSpecRegister
3
+
4
+ from pysaxpower.basic_protocol.power_home_plus_basic import PowerHomePlusBasic
5
+ from pysaxpower.basic_protocol.basic_register import BasicRegister
File without changes
@@ -0,0 +1,5 @@
1
+ class BasicRegister(object):
2
+ OperatingMode = 45
3
+ SOC = 46
4
+ ActivePower = 47
5
+ GridPower = 48
@@ -0,0 +1,62 @@
1
+ from pymodbus.client import ModbusTcpClient
2
+
3
+ class PowerHomePlusBasic:
4
+ def __init__(
5
+ self,
6
+ ip: str,
7
+ port: int =502,
8
+ timeout: int =10
9
+ ):
10
+ self.client = ModbusTcpClient(
11
+ host=ip,
12
+ port=port,
13
+ timeout=timeout
14
+ )
15
+
16
+ self.connect()
17
+
18
+ @staticmethod
19
+ def apply_offset(value: int):
20
+ offset = 16384
21
+ return value - offset
22
+
23
+ def get_values(self, addresses: dict[str, int]) -> dict[str, int]:
24
+
25
+ results = {}
26
+
27
+ for name, value in addresses.items():
28
+ raw_value = self.client.read_holding_registers(address=value, count=1, device_id=64)
29
+
30
+ if not raw_value.isError():
31
+ if value != 45 and value != 46:
32
+ decoded_value = self.apply_offset(raw_value.registers[0])
33
+ results[name] = decoded_value
34
+ else:
35
+ results[name] = raw_value.registers[0]
36
+ else:
37
+ print(f"Error reading {name}: {value} from Sax Power Home Plus Battery")
38
+ results[name] = None
39
+
40
+ return results
41
+
42
+
43
+ def get_raw_values(self, addresses: dict[str, int]) -> dict[str, int]:
44
+ results = {}
45
+
46
+ for name, value in addresses.items():
47
+ raw_value = self.client.read_holding_registers(address=value, count=1, device_id=64)
48
+
49
+ if not raw_value.isError():
50
+ results[name] = raw_value.registers[0]
51
+ else:
52
+ print(f"Error reading {name}: {value} from Sax Power Home Plus Battery")
53
+ results[name] = None
54
+
55
+ return results
56
+
57
+
58
+ def connect(self):
59
+ self.client.connect()
60
+
61
+ def close(self):
62
+ self.client.close()
File without changes
@@ -0,0 +1,106 @@
1
+ from pymodbus.client import ModbusTcpClient
2
+ from pysaxpower.sunspec.register import SunSpecRegister
3
+ from pysaxpower.sunspec.scalefactors import SunSpecScaleFactors
4
+
5
+ class PowerHomePlusSunspec:
6
+ def __init__(
7
+ self,
8
+ ip: str,
9
+ port: int =502,
10
+ timeout: int =10
11
+ ):
12
+ self.client = ModbusTcpClient(
13
+ host=ip,
14
+ port=port,
15
+ timeout=timeout
16
+ )
17
+
18
+ self.registers = SunSpecRegister()
19
+ self.scalefactor = SunSpecScaleFactors()
20
+
21
+ self.connect()
22
+
23
+
24
+ def write_registers(self, values: dict[int, int | float]):
25
+
26
+ for address, value in values.items():
27
+ write_value = self.check_for_specific_datatype(address, value)
28
+
29
+ self.client.write_register(address=address, value=write_value, device_id=100)
30
+
31
+
32
+ def check_for_specific_datatype(self, address: int, value: int | float) -> int:
33
+ if address == 40049:
34
+ return self.apply_power_setpoint_offset(value)
35
+
36
+ else:
37
+ return int(value)
38
+
39
+
40
+ @staticmethod
41
+ def apply_power_setpoint_offset(raw: int | float) -> int:
42
+ value = int(raw * 100)
43
+ return 65536 + value if value < 0 else value
44
+
45
+
46
+ @staticmethod
47
+ def encode_int16(raw: int) -> int:
48
+ return raw - 65536 if raw > 32767 else raw
49
+
50
+
51
+ def get_raw_values(self, registers: dict[str, int]) -> dict[str, int] | None:
52
+ """
53
+ Enter a dictionary of register values and names and get the raw int16 values
54
+ """
55
+ values = {}
56
+ for name, register in registers.items():
57
+ raw_values = self.client.read_holding_registers(register, count=1, device_id=100)
58
+
59
+ if raw_values.isError():
60
+ pass
61
+ else:
62
+ values[name] = raw_values.registers[0]
63
+
64
+ return values
65
+
66
+
67
+ def get_formatted_values(self, registers: dict[str, int]) -> dict[str, int]:
68
+
69
+ formatted_values = {}
70
+ for name, register in registers.items():
71
+
72
+ int16_register_value = self.client.read_holding_registers(
73
+ register, count=1, device_id=100
74
+ )
75
+ int_16_value = int16_register_value.registers[0]
76
+
77
+ scale_factor_register = self.scalefactor.search_scale_factor(
78
+ register
79
+ )
80
+
81
+ if scale_factor_register is not None:
82
+ scale_factor = self.client.read_holding_registers(
83
+ scale_factor_register, count=1, device_id=100
84
+ )
85
+
86
+ formatted_scale_factor = self.encode_int16(scale_factor.registers[0])
87
+
88
+ formatted_value = (int_16_value * 10 ** (formatted_scale_factor))
89
+
90
+ else:
91
+ formatted_value = int_16_value
92
+
93
+ value = self.encode_int16(
94
+ formatted_value
95
+ )
96
+
97
+ formatted_values[name] = value
98
+
99
+ return formatted_values
100
+
101
+
102
+ def connect(self):
103
+ self.client.connect()
104
+
105
+ def close(self):
106
+ self.client.close()
@@ -0,0 +1,217 @@
1
+ class SunSpecRegister(object):
2
+
3
+ #-----------------------
4
+ # SunSpec / Device info
5
+ #-----------------------
6
+ SunSpecID_1 = 40000
7
+ SunSpecID_2 = 40001
8
+ SunSpecModelID = 40002
9
+ Length = 40003
10
+ Manufacturer_1 = 40004
11
+ Manufacturer_2 = 40005
12
+ Manufacturer_3 = 40006
13
+ Manufacturer_4 = 40007
14
+ DeviceModel_1 = 40008
15
+ DeviceModel_2 = 40009
16
+ DeviceModel_3 = 40010
17
+ VersionMaster = 40011
18
+ VersionGateway = 40012
19
+ SerialNumber_High= 40013
20
+ SerialNumber_Low = 40014
21
+
22
+
23
+ # Sunspec 103
24
+ Model_Identifier_103 = 40015
25
+ Model_Length_32 = 40016
26
+
27
+ # AC Current (Storage)
28
+ AC_Current_Sum_Storage = 40017
29
+ AC_Current_Storage_A = 40018
30
+ AC_Current_Storage_B = 40019
31
+ AC_Current_Storage_C = 40020
32
+ AC_Current_Scalefactor = 40021
33
+
34
+ # Voltage (Storage)
35
+ Voltage_Storage_L1_L2 = 40022
36
+ Voltage_Storage_L2_L3 = 40023
37
+ Voltage_Storage_L3_L1 = 40024
38
+ Voltage_Storage_A = 40025
39
+ Voltage_Storage_B = 40026
40
+ Voltage_Storage_C = 40027
41
+ Voltage_Scalefactor = 40028
42
+
43
+ # Power (Storage)
44
+ ActivePower_Storage_Sum = 40029
45
+ ActivePower_Scalefactor = 40030
46
+
47
+ #------------------------------
48
+ # Frequency
49
+ #------------------------------
50
+ GridFrequency = 40031
51
+ Frequency_Scalefactor = 40032
52
+
53
+ #--------------------------------
54
+ # Apparent Power (Storage)
55
+ #--------------------------------
56
+ ApparentPower_Storage_Sum = 40033
57
+ ApparentPower_Scalefactor = 40034
58
+
59
+ #--------------------------------
60
+ # Reactive Power (Storage)
61
+ #--------------------------------
62
+ ReactivePower_Storage_Sum = 40035
63
+ ReactivePower_Scalefactor = 40036
64
+
65
+ #-------------------------------
66
+ # Power Factor (Storage)
67
+ #-------------------------------
68
+ PowerFactor_Storage_Sum = 40037
69
+ PowerFactor_Scalefactor = 40038
70
+
71
+ #------------------------------
72
+ # Temperature
73
+ #-------------------------------
74
+ MaxCellTemperature = 40041
75
+ Temperature_Scalefactor = 40042
76
+
77
+ #--------------
78
+ # Status
79
+ #--------------
80
+ State = 40043
81
+ Event = 40044
82
+
83
+ #------------------------------
84
+ # PV Power (ADW200 only)
85
+ #------------------------------
86
+ PV_Power = 40045
87
+ PV_Power_Scalefactor = 40046
88
+
89
+ #------------------------------
90
+ # Sunspec
91
+ #------------------------------
92
+ Sunspec_Model_ID_123 = 40047
93
+ Sunspec_Lenght_7 = 40048
94
+
95
+ #------------------------------
96
+ # Control
97
+ #------------------------------
98
+ PowerTarget = 40049
99
+ PowerTargetTimeout = 40050
100
+ ControlMode = 40051
101
+ PowerTargetScalefactor = 40052
102
+ MaxPowerReference = 40053
103
+
104
+ #--------------------------
105
+ # Sunspec
106
+ #--------------------------
107
+ Sunspec_Model_ID = 40054
108
+ Sunspec_Lenght = 40055
109
+
110
+ #--------------------------------------
111
+ # AC Current (Grid)
112
+ #--------------------------------------
113
+ AC_Current_Sum_Grid = 40056
114
+ AC_Current_Grid_L1 = 40057
115
+ AC_Current_Grid_L2 = 40058
116
+ AC_Current_Grid_L3 = 40059
117
+ AC_Current_Scalefactor_Grid = 40060
118
+
119
+ #----------------------------------
120
+ # Voltage (Grid)
121
+ #----------------------------------
122
+ AverageVoltage_Grid_L_N = 40061
123
+ GridVoltage_L1 = 40062
124
+ GridVoltage_L2 = 40063
125
+ GridVoltage_L3 = 40064
126
+ AverageVoltage_Grid_L_L = 40065
127
+ GridVoltage_L1_L2 = 40066
128
+ GridVoltage_L2_L3 = 40067
129
+ GridVoltage_L1_L3 = 40068
130
+ Voltage_Scalefactor_Grid = 40069
131
+
132
+ #----------------------------------
133
+ # Frequency (Grid)
134
+ #----------------------------------
135
+ GridFrequency_203 = 40070
136
+ Frequency_Scalefactor_Grid = 40071
137
+
138
+ #--------------------------------------
139
+ # Active Power (Grid)
140
+ #--------------------------------------
141
+ ActiveGridPower = 40072
142
+ ActiveGridPower_L1 = 40073
143
+ ActiveGridPower_L2 = 40074
144
+ ActiveGridPower_L3 = 40075
145
+ ActiveGridPower_Scalefactor = 40076
146
+
147
+ #--------------------------------------
148
+ # Apparent Power (Grid)
149
+ #--------------------------------------
150
+ ApparentPower_Sum_Grid = 40077
151
+ ApparentPower_Grid_L1 = 40078
152
+ ApparentPower_Grid_L2 = 40079
153
+ ApparentPower_Grid_L3 = 40080
154
+ ApparentPower_Scalefactor_Grid = 40081
155
+
156
+ #--------------------------------------
157
+ # Reactive Power (Grid)
158
+ #--------------------------------------
159
+ ReactivePower_Sum_Grid = 40082
160
+ ReactivePower_Grid_L1 = 40083
161
+ ReactivePower_Grid_L2 = 40084
162
+ ReactivePower_Grid_L3 = 40085
163
+ ReactivePower_Scalefactor_Grid = 40086
164
+
165
+ #--------------------------------------
166
+ # Power Factor (Grid)
167
+ #--------------------------------------
168
+ PowerFactor_Sum_Grid = 40087
169
+ PowerFactor_Grid_L1 = 40088
170
+ PowerFactor_Grid_L2 = 40089
171
+ PowerFactor_Grid_L3 = 40090
172
+ PowerFactor_Scalefactor_Grid = 40091
173
+
174
+ #--------------------------------------
175
+ # Sunspec
176
+ #--------------------------------------
177
+ Sunspec_ModelId_Base_Model = 40095
178
+ Sunspec_Length = 40096
179
+
180
+ #----------------------------------
181
+ # Battery System
182
+ #----------------------------------
183
+ BatterySystemCapacity = 40097
184
+ AvailableChargePower = 40098
185
+ AvailableDischargePower = 40099
186
+ MaxSoC = 40100
187
+ MinSoC = 40101
188
+ CurrentSoC = 40102
189
+ DepthOfDischarge = 40103
190
+
191
+ #------------------------------
192
+ # Battery Status
193
+ #------------------------------
194
+ BatteryChargingStatus = 40106
195
+ BatteryEvent = 40108
196
+ AverageCellVoltage = 40109
197
+
198
+ #------------------------------------------
199
+ # Scalefactors (Battery)
200
+ #------------------------------------------
201
+ Capacity_Scalefactor = 40110
202
+ ChargeDischargePower_Scalefactor = 40111
203
+ SoC_Scalefactor = 40112
204
+ CellVoltage_Scalefactor = 40114
205
+
206
+ #--------------------
207
+ # Reserve Registers
208
+ #--------------------
209
+ Reserve_40039 = 40039
210
+ Reserve_40040 = 40040
211
+ Reserve_40092 = 40092
212
+ Reserve_40093 = 40093
213
+ Reserve_40094 = 40094
214
+ Reserve_40104 = 40104
215
+ Reserve_40105 = 40105
216
+ Reserve_40107 = 40107
217
+ Reserve_40113 = 40113
@@ -0,0 +1,86 @@
1
+ from pysaxpower.sunspec.register import SunSpecRegister
2
+
3
+
4
+ class SunSpecScaleFactors(object):
5
+ """Ordnet einer Registeradresse die Adresse des zugehoerigen sunsf-Registers zu.
6
+
7
+ Rueckgabe ist die ADRESSE des Scalefaktor-Registers, nicht dessen Wert.
8
+ None bedeutet: fuer dieses Register existiert laut Dokumentation kein
9
+ Scalefaktor (Well-Known-Werte, Enums, Timeouts, sunsf-Register selbst).
10
+ """
11
+
12
+ def __init__(self):
13
+ self.register = SunSpecRegister()
14
+
15
+ def search_scale_factor(self, address: int) -> int | None:
16
+ r = self.register
17
+
18
+ # ------------------------------------------------------------------
19
+ # Modell 103 - Speicherelektronik (40015 - 40046)
20
+ # ------------------------------------------------------------------
21
+ if 40017 <= address <= 40020: # AC Strom Speicher Summe/A/B/C
22
+ return r.AC_Current_Scalefactor # 40021, SF = -2
23
+ elif 40022 <= address <= 40027: # Spannung Speicher L-L und A/B/C
24
+ return r.Voltage_Scalefactor # 40028, SF = -1
25
+ elif address == 40029: # Wirkleistung Speicher Summe
26
+ return r.ActivePower_Scalefactor # 40030, SF = 0
27
+ elif address == 40031: # Netzfrequenz (Speicher)
28
+ return r.Frequency_Scalefactor # 40032, SF = -2
29
+ elif address == 40033: # Scheinleistung Speicher Summe
30
+ return r.ApparentPower_Scalefactor # 40034, SF = 0
31
+ elif address == 40035: # Blindleistung Speicher Summe
32
+ return r.ReactivePower_Scalefactor # 40036, SF = 0
33
+ elif address == 40037: # Leistungsfaktor Speicher Summe
34
+ return r.PowerFactor_Scalefactor # 40038, SF = -3
35
+ elif address == 40041: # Maximale Zelltemperatur
36
+ return r.Temperature_Scalefactor # 40042, SF = 0
37
+ elif address == 40045: # PV-Leistung (nur mit ADW200)
38
+ return r.PV_Power_Scalefactor # 40046, SF = 1
39
+
40
+ # ------------------------------------------------------------------
41
+ # Modell 123 - Immediate Controls (40047 - 40053)
42
+ # ------------------------------------------------------------------
43
+ elif address == 40049: # Leistungsvorgabe prozentual
44
+ return r.PowerTargetScalefactor # 40052, SF = -2
45
+ # 40050 Timeout (s), 40051 Steuermodus (Enum),
46
+ # 40053 Referenzwert Maximalleistung (W, Well-Known) -> kein SF
47
+
48
+ # ------------------------------------------------------------------
49
+ # Modell 203 - WYE Connect 3Ph Meter ABC / ADW200 (40054 - 40094)
50
+ # ------------------------------------------------------------------
51
+ elif 40056 <= address <= 40059: # AC Strom Netz Summe/L1/L2/L3
52
+ return r.AC_Current_Scalefactor_Grid # 40060, SF = -1
53
+ elif 40061 <= address <= 40068: # Netzspannungen L-N und L-L
54
+ return r.Voltage_Scalefactor_Grid # 40069, SF = -1
55
+ elif address == 40070: # Netzfrequenz
56
+ return r.Frequency_Scalefactor_Grid # 40071, SF = -2
57
+ elif 40072 <= address <= 40075: # Wirkleistung Netz Summe/L1/L2/L3
58
+ return r.ActiveGridPower_Scalefactor # 40076, SF = 1
59
+ elif 40077 <= address <= 40080: # Scheinleistung Netz Summe/L1/L2/L3
60
+ return r.ApparentPower_Scalefactor_Grid # 40081, SF = 1
61
+ elif 40082 <= address <= 40085: # Blindleistung Netz Summe/L1/L2/L3
62
+ return r.ReactivePower_Scalefactor_Grid # 40086, SF = 1
63
+ elif 40087 <= address <= 40090: # Leistungsfaktor Netz Summe/L1/L2/L3
64
+ return r.PowerFactor_Scalefactor_Grid # 40091, SF = -3
65
+
66
+ # ------------------------------------------------------------------
67
+ # Modell 802 - Battery Base Model (40095 - 40114)
68
+ # ------------------------------------------------------------------
69
+ elif address == 40097: # Kapazitaet Speichersystem (Wh)
70
+ return r.Capacity_Scalefactor # 40110, SF = 0
71
+ elif 40098 <= address <= 40099: # Verf. Lade-/Entladeleistung (W)
72
+ return r.ChargeDischargePower_Scalefactor # 40111, SF = 0
73
+ elif 40100 <= address <= 40102: # Max/Min/Aktueller SoC (%)
74
+ return r.SoC_Scalefactor # 40112, SF = 0
75
+ elif address == 40109: # Durchschnittliche Zellspannung (mV)
76
+ return r.CellVoltage_Scalefactor # 40114, SF = 0
77
+
78
+ # 40103 Entladetiefe (%): in der Dokumentation ist KEIN Scalefaktor
79
+ # zugeordnet. Kandidat waere 40113 (Reserve, int16 sunsf, Wert 0),
80
+ # das an der Position von DoD_SF im SunSpec-Modell 802 steht.
81
+ # Bewusst nicht geraten -> None.
82
+
83
+ # 40000-40016, 40039/40040, 40043/40044, 40092-40094, 40104-40108,
84
+ # sowie alle sunsf-Register selbst haben keinen Scalefaktor.
85
+ else:
86
+ return None
pysaxpower-1.0.1/PKG-INFO DELETED
@@ -1,56 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: PySaxPower
3
- Version: 1.0.1
4
- Summary: Package for reading Sax devices via modbus
5
- Author-email: Hannes <hannes.210@gmx.de>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/Hans-4/pysaxpower
8
- Requires-Python: >=3.7
9
- Description-Content-Type: text/markdown
10
- License-File: LICENSE
11
- Requires-Dist: pymodbus==3.15.0
12
- Dynamic: license-file
13
-
14
- # PySaxPower
15
-
16
- **A Python package for reading data from SAX Power Home Plus batteries.**
17
-
18
- ---
19
-
20
- ## Overview
21
-
22
- With `PySaxPower` you can read the modbus register from the SAX Power Home Plus batteries.
23
-
24
- > **Note:** Due to a firmware bug in SAX devices, **writing data to the battery is currently unsupported**. This limitation will be addressed in a future update once SAX resolves the issue. For more details, see the [EVCC GitHub discussion](https://github.com/evcc-io/evcc/discussions/22155).
25
-
26
- ---
27
-
28
- ## Supported Devices
29
-
30
-
31
- | **Device** | **Documentation** |
32
- | --------------------------------- |----------------------------------------------------------------------------------|
33
- | Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) |
34
-
35
-
36
- ---
37
-
38
- ## Installation
39
-
40
- ```bash
41
- pip install PySaxPower
42
- ```
43
-
44
- ---
45
-
46
- ## Usage
47
-
48
- ```python
49
- from pysaxpower import PowerHomePlus
50
-
51
- device = PowerHomePlus("192.168.178.60")
52
-
53
- level, power, grid_power = device.read_register()
54
-
55
- print(f"Battery Level: {level}% | Power: {power}W | Grid Power: {grid_power}W")
56
- ```
@@ -1,56 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: PySaxPower
3
- Version: 1.0.1
4
- Summary: Package for reading Sax devices via modbus
5
- Author-email: Hannes <hannes.210@gmx.de>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/Hans-4/pysaxpower
8
- Requires-Python: >=3.7
9
- Description-Content-Type: text/markdown
10
- License-File: LICENSE
11
- Requires-Dist: pymodbus==3.15.0
12
- Dynamic: license-file
13
-
14
- # PySaxPower
15
-
16
- **A Python package for reading data from SAX Power Home Plus batteries.**
17
-
18
- ---
19
-
20
- ## Overview
21
-
22
- With `PySaxPower` you can read the modbus register from the SAX Power Home Plus batteries.
23
-
24
- > **Note:** Due to a firmware bug in SAX devices, **writing data to the battery is currently unsupported**. This limitation will be addressed in a future update once SAX resolves the issue. For more details, see the [EVCC GitHub discussion](https://github.com/evcc-io/evcc/discussions/22155).
25
-
26
- ---
27
-
28
- ## Supported Devices
29
-
30
-
31
- | **Device** | **Documentation** |
32
- | --------------------------------- |----------------------------------------------------------------------------------|
33
- | Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) |
34
-
35
-
36
- ---
37
-
38
- ## Installation
39
-
40
- ```bash
41
- pip install PySaxPower
42
- ```
43
-
44
- ---
45
-
46
- ## Usage
47
-
48
- ```python
49
- from pysaxpower import PowerHomePlus
50
-
51
- device = PowerHomePlus("192.168.178.60")
52
-
53
- level, power, grid_power = device.read_register()
54
-
55
- print(f"Battery Level: {level}% | Power: {power}W | Grid Power: {grid_power}W")
56
- ```
@@ -1,10 +0,0 @@
1
- LICENSE
2
- README.md
3
- pyproject.toml
4
- PySaxPower.egg-info/PKG-INFO
5
- PySaxPower.egg-info/SOURCES.txt
6
- PySaxPower.egg-info/dependency_links.txt
7
- PySaxPower.egg-info/requires.txt
8
- PySaxPower.egg-info/top_level.txt
9
- pysaxpower/__init__.py
10
- pysaxpower/power_home_plus.py
@@ -1,43 +0,0 @@
1
- # PySaxPower
2
-
3
- **A Python package for reading data from SAX Power Home Plus batteries.**
4
-
5
- ---
6
-
7
- ## Overview
8
-
9
- With `PySaxPower` you can read the modbus register from the SAX Power Home Plus batteries.
10
-
11
- > **Note:** Due to a firmware bug in SAX devices, **writing data to the battery is currently unsupported**. This limitation will be addressed in a future update once SAX resolves the issue. For more details, see the [EVCC GitHub discussion](https://github.com/evcc-io/evcc/discussions/22155).
12
-
13
- ---
14
-
15
- ## Supported Devices
16
-
17
-
18
- | **Device** | **Documentation** |
19
- | --------------------------------- |----------------------------------------------------------------------------------|
20
- | Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) |
21
-
22
-
23
- ---
24
-
25
- ## Installation
26
-
27
- ```bash
28
- pip install PySaxPower
29
- ```
30
-
31
- ---
32
-
33
- ## Usage
34
-
35
- ```python
36
- from pysaxpower import PowerHomePlus
37
-
38
- device = PowerHomePlus("192.168.178.60")
39
-
40
- level, power, grid_power = device.read_register()
41
-
42
- print(f"Battery Level: {level}% | Power: {power}W | Grid Power: {grid_power}W")
43
- ```
@@ -1 +0,0 @@
1
- from pysaxpower.power_home_plus import PowerHomePlus
@@ -1,52 +0,0 @@
1
- from pymodbus.client import ModbusTcpClient
2
-
3
- class PowerHomePlus:
4
- def __init__(
5
- self,
6
- ip: str,
7
- port: int =502,
8
- timeout: int =10
9
- ):
10
- self.client = ModbusTcpClient(
11
- host=ip,
12
- port=port,
13
- timeout=timeout
14
- )
15
-
16
- self.connect()
17
-
18
- @staticmethod
19
- def apply_offset(value: int):
20
- offset = 16384
21
- return value - offset
22
-
23
- def read_register(self):
24
- """
25
- 46: battery charge level
26
- 47: battery power
27
- 48: battery grid power
28
- """
29
- addresses = [46, 47, 48]
30
-
31
- results = []
32
-
33
- for i in range(len(addresses)):
34
- value = self.client.read_holding_registers(address=addresses[i], count=1, device_id=64)
35
-
36
- if not value.isError():
37
- if i > 0:
38
- decoded_value = self.apply_offset(value.registers[0])
39
- results.append(decoded_value)
40
- else:
41
- results.append(value.registers[0])
42
- else:
43
- print(f"Error reading register {addresses[i]} from Sax Power Home Plus Battery")
44
- results.append(None)
45
-
46
- return results
47
-
48
- def connect(self):
49
- self.client.connect()
50
-
51
- def close(self):
52
- self.client.close()
File without changes
File without changes