PySaxPower 1.0.0__tar.gz → 2.0.0__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.
- pysaxpower-2.0.0/PKG-INFO +112 -0
- pysaxpower-2.0.0/PySaxPower.egg-info/PKG-INFO +112 -0
- pysaxpower-2.0.0/PySaxPower.egg-info/SOURCES.txt +16 -0
- pysaxpower-2.0.0/PySaxPower.egg-info/requires.txt +1 -0
- pysaxpower-2.0.0/PySaxPower.egg-info/top_level.txt +1 -0
- pysaxpower-2.0.0/README.md +99 -0
- {pysaxpower-1.0.0 → pysaxpower-2.0.0}/pyproject.toml +4 -4
- pysaxpower-2.0.0/pysaxpower/__init__.py +5 -0
- pysaxpower-2.0.0/pysaxpower/basic_protocol/basic_register.py +5 -0
- pysaxpower-2.0.0/pysaxpower/basic_protocol/power_home_plus_basic.py +62 -0
- pysaxpower-2.0.0/pysaxpower/sunspec/__init__.py +0 -0
- pysaxpower-2.0.0/pysaxpower/sunspec/power_home_plus_sunspec.py +106 -0
- pysaxpower-2.0.0/pysaxpower/sunspec/register.py +217 -0
- pysaxpower-2.0.0/pysaxpower/sunspec/scalefactors.py +86 -0
- pysaxpower-1.0.0/PKG-INFO +0 -56
- pysaxpower-1.0.0/PySaxPower.egg-info/PKG-INFO +0 -56
- pysaxpower-1.0.0/PySaxPower.egg-info/SOURCES.txt +0 -10
- pysaxpower-1.0.0/PySaxPower.egg-info/requires.txt +0 -1
- pysaxpower-1.0.0/PySaxPower.egg-info/top_level.txt +0 -1
- pysaxpower-1.0.0/README.md +0 -43
- pysaxpower-1.0.0/sax_power/power_home_plus.py +0 -47
- {pysaxpower-1.0.0 → pysaxpower-2.0.0}/LICENSE +0 -0
- {pysaxpower-1.0.0 → pysaxpower-2.0.0}/PySaxPower.egg-info/dependency_links.txt +0 -0
- /pysaxpower-1.0.0/sax_power/__init_.py → /pysaxpower-2.0.0/pysaxpower/basic_protocol/__init__.py +0 -0
- {pysaxpower-1.0.0 → pysaxpower-2.0.0}/setup.cfg +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PySaxPower
|
|
3
|
+
Version: 2.0.0
|
|
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. The package is divided into two protocol implementations: **SunSpec** and **Basic**. The **Basic** protocol supports read-only operations.
|
|
27
|
+
|
|
28
|
+
> **Note:**
|
|
29
|
+
> Due to a firmware bug in SAX devices, **only the SunSpec protocol supports writing to registers**.
|
|
30
|
+
> To use this feature, ensure your battery has at least **Master V61** and **Gateway V54** firmware versions. If not, you may need to update your firmware or contact **Customer Support**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Supported Devices
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
| **Device** | **Documentation** |
|
|
40
|
+
| --------------------------------- |----------------------------------------------------------------------------------|
|
|
41
|
+
| Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) |
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install PySaxPower
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Sunspec
|
|
57
|
+
|
|
58
|
+
#### Read register
|
|
59
|
+
```python
|
|
60
|
+
from pysaxpower import PowerHomePlusSunspec
|
|
61
|
+
from pysaxpower import SunSpecRegister
|
|
62
|
+
|
|
63
|
+
device = PowerHomePlusSunspec("192.168.178.60")
|
|
64
|
+
|
|
65
|
+
register = SunSpecRegister()
|
|
66
|
+
|
|
67
|
+
read_values = {
|
|
68
|
+
"Soc": register.CurrentSoC,
|
|
69
|
+
"Battery Power": register.ActivePower_Storage_Sum,
|
|
70
|
+
"Grid Power": register.ActiveGridPower,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
values = device.get_formatted_values(read_values)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Write register
|
|
77
|
+
```python
|
|
78
|
+
from pysaxpower import PowerHomePlusSunspec
|
|
79
|
+
from pysaxpower import SunSpecRegister
|
|
80
|
+
|
|
81
|
+
device = PowerHomePlusSunspec("192.168.178.60")
|
|
82
|
+
|
|
83
|
+
register = SunSpecRegister()
|
|
84
|
+
|
|
85
|
+
write_values = {
|
|
86
|
+
register.ControlMode: 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
device.write_registers(write_values)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Basic
|
|
93
|
+
```python
|
|
94
|
+
from pysaxpower import PowerHomePlusBasic
|
|
95
|
+
from pysaxpower import BasicRegister
|
|
96
|
+
|
|
97
|
+
device = PowerHomePlusBasic("192.168.178.60")
|
|
98
|
+
|
|
99
|
+
register = BasicRegister
|
|
100
|
+
|
|
101
|
+
read_values = {
|
|
102
|
+
"Operating Mode": register.OperatingMode,
|
|
103
|
+
"Soc": register.SOC,
|
|
104
|
+
"Battery Power": register.ActivePower,
|
|
105
|
+
"Grid Power": register.GridPower,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
values = device.get_values(read_values)
|
|
109
|
+
print(values)
|
|
110
|
+
|
|
111
|
+
device.close()
|
|
112
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PySaxPower
|
|
3
|
+
Version: 2.0.0
|
|
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. The package is divided into two protocol implementations: **SunSpec** and **Basic**. The **Basic** protocol supports read-only operations.
|
|
27
|
+
|
|
28
|
+
> **Note:**
|
|
29
|
+
> Due to a firmware bug in SAX devices, **only the SunSpec protocol supports writing to registers**.
|
|
30
|
+
> To use this feature, ensure your battery has at least **Master V61** and **Gateway V54** firmware versions. If not, you may need to update your firmware or contact **Customer Support**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Supported Devices
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
| **Device** | **Documentation** |
|
|
40
|
+
| --------------------------------- |----------------------------------------------------------------------------------|
|
|
41
|
+
| Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) |
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install PySaxPower
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Sunspec
|
|
57
|
+
|
|
58
|
+
#### Read register
|
|
59
|
+
```python
|
|
60
|
+
from pysaxpower import PowerHomePlusSunspec
|
|
61
|
+
from pysaxpower import SunSpecRegister
|
|
62
|
+
|
|
63
|
+
device = PowerHomePlusSunspec("192.168.178.60")
|
|
64
|
+
|
|
65
|
+
register = SunSpecRegister()
|
|
66
|
+
|
|
67
|
+
read_values = {
|
|
68
|
+
"Soc": register.CurrentSoC,
|
|
69
|
+
"Battery Power": register.ActivePower_Storage_Sum,
|
|
70
|
+
"Grid Power": register.ActiveGridPower,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
values = device.get_formatted_values(read_values)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Write register
|
|
77
|
+
```python
|
|
78
|
+
from pysaxpower import PowerHomePlusSunspec
|
|
79
|
+
from pysaxpower import SunSpecRegister
|
|
80
|
+
|
|
81
|
+
device = PowerHomePlusSunspec("192.168.178.60")
|
|
82
|
+
|
|
83
|
+
register = SunSpecRegister()
|
|
84
|
+
|
|
85
|
+
write_values = {
|
|
86
|
+
register.ControlMode: 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
device.write_registers(write_values)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Basic
|
|
93
|
+
```python
|
|
94
|
+
from pysaxpower import PowerHomePlusBasic
|
|
95
|
+
from pysaxpower import BasicRegister
|
|
96
|
+
|
|
97
|
+
device = PowerHomePlusBasic("192.168.178.60")
|
|
98
|
+
|
|
99
|
+
register = BasicRegister
|
|
100
|
+
|
|
101
|
+
read_values = {
|
|
102
|
+
"Operating Mode": register.OperatingMode,
|
|
103
|
+
"Soc": register.SOC,
|
|
104
|
+
"Battery Power": register.ActivePower,
|
|
105
|
+
"Grid Power": register.GridPower,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
values = device.get_values(read_values)
|
|
109
|
+
print(values)
|
|
110
|
+
|
|
111
|
+
device.close()
|
|
112
|
+
```
|
|
@@ -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 @@
|
|
|
1
|
+
pymodbus==3.15.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pysaxpower
|
|
@@ -0,0 +1,99 @@
|
|
|
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. The package is divided into two protocol implementations: **SunSpec** and **Basic**. The **Basic** protocol supports read-only operations.
|
|
14
|
+
|
|
15
|
+
> **Note:**
|
|
16
|
+
> Due to a firmware bug in SAX devices, **only the SunSpec protocol supports writing to registers**.
|
|
17
|
+
> To use this feature, ensure your battery has at least **Master V61** and **Gateway V54** firmware versions. If not, you may need to update your firmware or contact **Customer Support**.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Supported Devices
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
| **Device** | **Documentation** |
|
|
27
|
+
| --------------------------------- |----------------------------------------------------------------------------------|
|
|
28
|
+
| Power Home Plus 5.8 kWh / 7.7 kWh | [Product page](https://sax-power.net/download/Handbuch_SAX_Home_Plus_7,7_EN.pdf) |
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install PySaxPower
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Sunspec
|
|
44
|
+
|
|
45
|
+
#### Read register
|
|
46
|
+
```python
|
|
47
|
+
from pysaxpower import PowerHomePlusSunspec
|
|
48
|
+
from pysaxpower import SunSpecRegister
|
|
49
|
+
|
|
50
|
+
device = PowerHomePlusSunspec("192.168.178.60")
|
|
51
|
+
|
|
52
|
+
register = SunSpecRegister()
|
|
53
|
+
|
|
54
|
+
read_values = {
|
|
55
|
+
"Soc": register.CurrentSoC,
|
|
56
|
+
"Battery Power": register.ActivePower_Storage_Sum,
|
|
57
|
+
"Grid Power": register.ActiveGridPower,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
values = device.get_formatted_values(read_values)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Write register
|
|
64
|
+
```python
|
|
65
|
+
from pysaxpower import PowerHomePlusSunspec
|
|
66
|
+
from pysaxpower import SunSpecRegister
|
|
67
|
+
|
|
68
|
+
device = PowerHomePlusSunspec("192.168.178.60")
|
|
69
|
+
|
|
70
|
+
register = SunSpecRegister()
|
|
71
|
+
|
|
72
|
+
write_values = {
|
|
73
|
+
register.ControlMode: 1
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
device.write_registers(write_values)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Basic
|
|
80
|
+
```python
|
|
81
|
+
from pysaxpower import PowerHomePlusBasic
|
|
82
|
+
from pysaxpower import BasicRegister
|
|
83
|
+
|
|
84
|
+
device = PowerHomePlusBasic("192.168.178.60")
|
|
85
|
+
|
|
86
|
+
register = BasicRegister
|
|
87
|
+
|
|
88
|
+
read_values = {
|
|
89
|
+
"Operating Mode": register.OperatingMode,
|
|
90
|
+
"Soc": register.SOC,
|
|
91
|
+
"Battery Power": register.ActivePower,
|
|
92
|
+
"Grid Power": register.GridPower,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
values = device.get_values(read_values)
|
|
96
|
+
print(values)
|
|
97
|
+
|
|
98
|
+
device.close()
|
|
99
|
+
```
|
|
@@ -4,16 +4,16 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "PySaxPower"
|
|
7
|
-
version = "
|
|
8
|
-
description = "
|
|
7
|
+
version = "2.0.0"
|
|
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"
|
|
13
|
+
{name = "Hannes"}
|
|
14
14
|
]
|
|
15
15
|
dependencies = [
|
|
16
|
-
"pymodbus==
|
|
16
|
+
"pymodbus==3.15.0"
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
[project.urls]
|
|
@@ -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
|
|
@@ -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.0/PKG-INFO
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: PySaxPower
|
|
3
|
-
Version: 1.0.0
|
|
4
|
-
Summary: Package for reading Sax devices
|
|
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==2.5
|
|
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 sax_power.power_home_plus 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.0
|
|
4
|
-
Summary: Package for reading Sax devices
|
|
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==2.5
|
|
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 sax_power.power_home_plus 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
|
-
sax_power/__init_.py
|
|
10
|
-
sax_power/power_home_plus.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pymodbus==2.5
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
sax_power
|
pysaxpower-1.0.0/README.md
DELETED
|
@@ -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 sax_power.power_home_plus 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,47 +0,0 @@
|
|
|
1
|
-
from pymodbus.client.sync 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
|
-
@staticmethod
|
|
17
|
-
def apply_offset(value: int):
|
|
18
|
-
offset = 16384
|
|
19
|
-
return value - offset
|
|
20
|
-
|
|
21
|
-
def read_register(self):
|
|
22
|
-
"""
|
|
23
|
-
46: battery charge level
|
|
24
|
-
47: battery power
|
|
25
|
-
48: battery grid power
|
|
26
|
-
"""
|
|
27
|
-
addresses = [46, 47, 48]
|
|
28
|
-
|
|
29
|
-
results = []
|
|
30
|
-
|
|
31
|
-
for i in range(len(addresses)):
|
|
32
|
-
value = self.client.read_holding_registers(address=addresses[i], count=1, unit=64)
|
|
33
|
-
|
|
34
|
-
if not value.isError():
|
|
35
|
-
if i > 0:
|
|
36
|
-
decoded_value = self.apply_offset(value.registers[0])
|
|
37
|
-
results.append(decoded_value)
|
|
38
|
-
else:
|
|
39
|
-
results.append(value.registers[0])
|
|
40
|
-
else:
|
|
41
|
-
print(f"Error reading register {addresses[i]} from Sax Power Home Plus Battery")
|
|
42
|
-
results.append(None)
|
|
43
|
-
|
|
44
|
-
return results
|
|
45
|
-
|
|
46
|
-
def close(self):
|
|
47
|
-
self.client.close()
|
|
File without changes
|
|
File without changes
|
/pysaxpower-1.0.0/sax_power/__init_.py → /pysaxpower-2.0.0/pysaxpower/basic_protocol/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|