python-intelbras-solar-local 0.1.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.
- python_intelbras_solar_local-0.1.0/.github/workflows/docs.yml +33 -0
- python_intelbras_solar_local-0.1.0/.github/workflows/release.yml +26 -0
- python_intelbras_solar_local-0.1.0/.github/workflows/tests.yml +30 -0
- python_intelbras_solar_local-0.1.0/.gitignore +11 -0
- python_intelbras_solar_local-0.1.0/CHANGELOG.md +12 -0
- python_intelbras_solar_local-0.1.0/LICENSE +21 -0
- python_intelbras_solar_local-0.1.0/PKG-INFO +122 -0
- python_intelbras_solar_local-0.1.0/README.md +93 -0
- python_intelbras_solar_local-0.1.0/docs/api.md +23 -0
- python_intelbras_solar_local-0.1.0/docs/assets/icon.png +0 -0
- python_intelbras_solar_local-0.1.0/docs/assets/logo.png +0 -0
- python_intelbras_solar_local-0.1.0/docs/changelog.md +1 -0
- python_intelbras_solar_local-0.1.0/docs/index.md +28 -0
- python_intelbras_solar_local-0.1.0/docs/protocol.md +77 -0
- python_intelbras_solar_local-0.1.0/docs/usage.md +53 -0
- python_intelbras_solar_local-0.1.0/examples/status.py +25 -0
- python_intelbras_solar_local-0.1.0/mkdocs.yml +50 -0
- python_intelbras_solar_local-0.1.0/pyproject.toml +46 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/__init__.py +43 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/client.py +138 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/const.py +55 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/exceptions.py +14 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/modbus.py +99 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/models.py +188 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/py.typed +0 -0
- python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/winet.py +72 -0
- python_intelbras_solar_local-0.1.0/tests/__init__.py +0 -0
- python_intelbras_solar_local-0.1.0/tests/conftest.py +126 -0
- python_intelbras_solar_local-0.1.0/tests/test_client.py +44 -0
- python_intelbras_solar_local-0.1.0/tests/test_modbus.py +36 -0
- python_intelbras_solar_local-0.1.0/tests/test_models.py +75 -0
- python_intelbras_solar_local-0.1.0/tests/test_winet.py +37 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
pages: write
|
|
8
|
+
id-token: write
|
|
9
|
+
concurrency:
|
|
10
|
+
group: pages
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
- run: pip install -e ".[docs]"
|
|
21
|
+
- run: mkdocs build --strict
|
|
22
|
+
- uses: actions/upload-pages-artifact@v3
|
|
23
|
+
with:
|
|
24
|
+
path: site
|
|
25
|
+
deploy:
|
|
26
|
+
needs: build
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
environment:
|
|
29
|
+
name: github-pages
|
|
30
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
31
|
+
steps:
|
|
32
|
+
- id: deployment
|
|
33
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
pypi-publish:
|
|
12
|
+
name: Build and publish to PyPI
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write # required for PyPI Trusted Publishing (OIDC)
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Build distributions
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade build
|
|
24
|
+
python -m build
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: pip install -e ".[test]"
|
|
25
|
+
|
|
26
|
+
- name: Lint with ruff
|
|
27
|
+
run: ruff check .
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: pytest
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-10-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `IntelbrasSolarClient`: async client for Intelbras (Sungrow WiNet-S) inverters with `async_get_status`, `async_get_inverter_data`, and the controls `async_set_inverter_on`, `async_set_power_limit_enabled`, `async_set_power_limit` and `async_set_clock`.
|
|
9
|
+
- `ModbusTcpClient`: dependency-free asyncio Modbus TCP client (FC3, FC4, FC6).
|
|
10
|
+
- `WiNetClient`: reader for the datalogger WebSocket (`connect`, `state`, `fault` and `devicelist` services).
|
|
11
|
+
- `InverterStatus`, `DataloggerStatus` and `Mppt` dataclasses, built by `parse_registers` and `parse_datalogger`.
|
|
12
|
+
- Exceptions `IntelbrasSolarError`, `IntelbrasSolarConnectionError` and `IntelbrasSolarProtocolError`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hudson Brendon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: python-intelbras-solar-local
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async Python client for Intelbras solar inverters (Sungrow WiNet-S) over Modbus TCP and the datalogger WebSocket
|
|
5
|
+
Project-URL: Homepage, https://github.com/hudsonbrendon/python-intelbras-solar-local
|
|
6
|
+
Project-URL: Documentation, https://hudsonbrendon.github.io/python-intelbras-solar-local/
|
|
7
|
+
Project-URL: Issues, https://github.com/hudsonbrendon/python-intelbras-solar-local/issues
|
|
8
|
+
Author-email: Hudson Brendon <contato.hudsonbrendon@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: aiohttp,async,home-assistant,intelbras,inverter,modbus,solar,sungrow,winet
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: aiohttp>=3.9
|
|
21
|
+
Provides-Extra: docs
|
|
22
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
23
|
+
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
|
|
26
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
27
|
+
Requires-Dist: ruff; extra == 'test'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<p align="center"><img src="docs/assets/logo.png" width="300" alt="Intelbras"></p>
|
|
31
|
+
|
|
32
|
+
# python-intelbras-solar-local
|
|
33
|
+
|
|
34
|
+
[](https://github.com/hudsonbrendon/python-intelbras-solar-local/actions/workflows/tests.yml)
|
|
35
|
+
[](https://pypi.org/project/python-intelbras-solar-local/)
|
|
36
|
+
[](https://pypi.org/project/python-intelbras-solar-local/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
This is an async Python client that reads and controls **Intelbras solar inverters** over the local network, with no cloud
|
|
40
|
+
and no account. It was built for the
|
|
41
|
+
[`ha-intelbras-solar-local`](https://github.com/hudsonbrendon/ha-intelbras-solar-local) Home Assistant integration.
|
|
42
|
+
|
|
43
|
+
Intelbras IONS inverters are rebranded **Sungrow** string inverters, and their Wi-Fi datalogger is a Sungrow **WiNet-S**.
|
|
44
|
+
The datalogger exposes two interfaces without a login, and this library uses both:
|
|
45
|
+
|
|
46
|
+
- **Modbus TCP** on port `502`, using the Sungrow SG-RS register map. This is the main data source.
|
|
47
|
+
- The **datalogger WebSocket** on port `8082`, which the built-in web UI uses. It provides Wi-Fi and cloud link state,
|
|
48
|
+
active faults and alarms, and the commercial model.
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- ⚡ Power readings: AC, DC, apparent and reactive power, plus power factor.
|
|
53
|
+
- 🔋 Energy counters for today, the month, the year and the lifetime total (0.1 kWh resolution).
|
|
54
|
+
- ☀️ Voltage, current and power for each MPPT input.
|
|
55
|
+
- 🔌 Grid voltage, current and frequency (0.01 Hz).
|
|
56
|
+
- 🩺 Health data: work state, internal temperature, bus voltage, insulation resistance and fault code.
|
|
57
|
+
- 📡 Datalogger data: Wi-Fi and cloud connectivity, active fault list and alarm count.
|
|
58
|
+
- 🛠️ Controls: start or stop the inverter, switch power limitation on or off, set the power limit (%), set the clock.
|
|
59
|
+
- No `pymodbus` dependency. The library ships a tiny asyncio Modbus TCP client, so it never clashes with the version
|
|
60
|
+
that Home Assistant pins.
|
|
61
|
+
- Fully async (`aiohttp`) and typed (`py.typed`).
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install python-intelbras-solar-local
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quickstart
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import asyncio
|
|
73
|
+
from intelbras_solar_local import IntelbrasSolarClient
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def main() -> None:
|
|
77
|
+
async with IntelbrasSolarClient("192.168.31.17") as client:
|
|
78
|
+
status = await client.async_get_status()
|
|
79
|
+
print(status.model, status.serial, status.work_state)
|
|
80
|
+
print(f"{status.power_ac} W now, {status.energy_today} kWh today")
|
|
81
|
+
for n, mppt in enumerate(status.mppts, 1):
|
|
82
|
+
print(f"MPPT{n}: {mppt.voltage} V x {mppt.current} A = {mppt.power} W")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
asyncio.run(main())
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Supported hardware
|
|
89
|
+
|
|
90
|
+
| Inverter | Datalogger firmware | Sungrow type code |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| Intelbras IONS-5K M5 | `WIFI-SV300.001.03.P002` (WiNet-S) | `0x2614` |
|
|
93
|
+
|
|
94
|
+
Other Intelbras IONS models and Sungrow SG-RS inverters behind a WiNet-S should work as well.
|
|
95
|
+
If yours differs, open an issue with the output of `examples/status.py`.
|
|
96
|
+
|
|
97
|
+
## API overview
|
|
98
|
+
|
|
99
|
+
| Method | Description |
|
|
100
|
+
|--------|-------------|
|
|
101
|
+
| `async_get_status()` | Returns an `InverterStatus` from Modbus, with a `DataloggerStatus` when the WebSocket answers (best-effort) |
|
|
102
|
+
| `async_get_inverter_data()` | Modbus-only `InverterStatus` |
|
|
103
|
+
| `winet.async_get_status()` | Datalogger-only `DataloggerStatus` |
|
|
104
|
+
| `async_set_inverter_on(on)` | Starts or stops generation (holding register 5005) |
|
|
105
|
+
| `async_set_power_limit_enabled(enabled)` | Switches power limitation on or off (5006) |
|
|
106
|
+
| `async_set_power_limit(percent)` | Sets the power limit, 0–100 % (5007) |
|
|
107
|
+
| `async_set_clock(datetime)` | Sets the inverter clock (4999–5004) |
|
|
108
|
+
|
|
109
|
+
The register map and the WebSocket protocol are documented in the
|
|
110
|
+
[docs](https://hudsonbrendon.github.io/python-intelbras-solar-local/protocol/).
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv venv && uv pip install -e ".[test]"
|
|
116
|
+
pytest
|
|
117
|
+
ruff check .
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT © Hudson Brendon. This project is not affiliated with Intelbras or Sungrow.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<p align="center"><img src="docs/assets/logo.png" width="300" alt="Intelbras"></p>
|
|
2
|
+
|
|
3
|
+
# python-intelbras-solar-local
|
|
4
|
+
|
|
5
|
+
[](https://github.com/hudsonbrendon/python-intelbras-solar-local/actions/workflows/tests.yml)
|
|
6
|
+
[](https://pypi.org/project/python-intelbras-solar-local/)
|
|
7
|
+
[](https://pypi.org/project/python-intelbras-solar-local/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
This is an async Python client that reads and controls **Intelbras solar inverters** over the local network, with no cloud
|
|
11
|
+
and no account. It was built for the
|
|
12
|
+
[`ha-intelbras-solar-local`](https://github.com/hudsonbrendon/ha-intelbras-solar-local) Home Assistant integration.
|
|
13
|
+
|
|
14
|
+
Intelbras IONS inverters are rebranded **Sungrow** string inverters, and their Wi-Fi datalogger is a Sungrow **WiNet-S**.
|
|
15
|
+
The datalogger exposes two interfaces without a login, and this library uses both:
|
|
16
|
+
|
|
17
|
+
- **Modbus TCP** on port `502`, using the Sungrow SG-RS register map. This is the main data source.
|
|
18
|
+
- The **datalogger WebSocket** on port `8082`, which the built-in web UI uses. It provides Wi-Fi and cloud link state,
|
|
19
|
+
active faults and alarms, and the commercial model.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- ⚡ Power readings: AC, DC, apparent and reactive power, plus power factor.
|
|
24
|
+
- 🔋 Energy counters for today, the month, the year and the lifetime total (0.1 kWh resolution).
|
|
25
|
+
- ☀️ Voltage, current and power for each MPPT input.
|
|
26
|
+
- 🔌 Grid voltage, current and frequency (0.01 Hz).
|
|
27
|
+
- 🩺 Health data: work state, internal temperature, bus voltage, insulation resistance and fault code.
|
|
28
|
+
- 📡 Datalogger data: Wi-Fi and cloud connectivity, active fault list and alarm count.
|
|
29
|
+
- 🛠️ Controls: start or stop the inverter, switch power limitation on or off, set the power limit (%), set the clock.
|
|
30
|
+
- No `pymodbus` dependency. The library ships a tiny asyncio Modbus TCP client, so it never clashes with the version
|
|
31
|
+
that Home Assistant pins.
|
|
32
|
+
- Fully async (`aiohttp`) and typed (`py.typed`).
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install python-intelbras-solar-local
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import asyncio
|
|
44
|
+
from intelbras_solar_local import IntelbrasSolarClient
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def main() -> None:
|
|
48
|
+
async with IntelbrasSolarClient("192.168.31.17") as client:
|
|
49
|
+
status = await client.async_get_status()
|
|
50
|
+
print(status.model, status.serial, status.work_state)
|
|
51
|
+
print(f"{status.power_ac} W now, {status.energy_today} kWh today")
|
|
52
|
+
for n, mppt in enumerate(status.mppts, 1):
|
|
53
|
+
print(f"MPPT{n}: {mppt.voltage} V x {mppt.current} A = {mppt.power} W")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
asyncio.run(main())
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Supported hardware
|
|
60
|
+
|
|
61
|
+
| Inverter | Datalogger firmware | Sungrow type code |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| Intelbras IONS-5K M5 | `WIFI-SV300.001.03.P002` (WiNet-S) | `0x2614` |
|
|
64
|
+
|
|
65
|
+
Other Intelbras IONS models and Sungrow SG-RS inverters behind a WiNet-S should work as well.
|
|
66
|
+
If yours differs, open an issue with the output of `examples/status.py`.
|
|
67
|
+
|
|
68
|
+
## API overview
|
|
69
|
+
|
|
70
|
+
| Method | Description |
|
|
71
|
+
|--------|-------------|
|
|
72
|
+
| `async_get_status()` | Returns an `InverterStatus` from Modbus, with a `DataloggerStatus` when the WebSocket answers (best-effort) |
|
|
73
|
+
| `async_get_inverter_data()` | Modbus-only `InverterStatus` |
|
|
74
|
+
| `winet.async_get_status()` | Datalogger-only `DataloggerStatus` |
|
|
75
|
+
| `async_set_inverter_on(on)` | Starts or stops generation (holding register 5005) |
|
|
76
|
+
| `async_set_power_limit_enabled(enabled)` | Switches power limitation on or off (5006) |
|
|
77
|
+
| `async_set_power_limit(percent)` | Sets the power limit, 0–100 % (5007) |
|
|
78
|
+
| `async_set_clock(datetime)` | Sets the inverter clock (4999–5004) |
|
|
79
|
+
|
|
80
|
+
The register map and the WebSocket protocol are documented in the
|
|
81
|
+
[docs](https://hudsonbrendon.github.io/python-intelbras-solar-local/protocol/).
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv venv && uv pip install -e ".[test]"
|
|
87
|
+
pytest
|
|
88
|
+
ruff check .
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT © Hudson Brendon. This project is not affiliated with Intelbras or Sungrow.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
::: intelbras_solar_local.IntelbrasSolarClient
|
|
4
|
+
|
|
5
|
+
::: intelbras_solar_local.InverterStatus
|
|
6
|
+
|
|
7
|
+
::: intelbras_solar_local.DataloggerStatus
|
|
8
|
+
|
|
9
|
+
::: intelbras_solar_local.Mppt
|
|
10
|
+
|
|
11
|
+
::: intelbras_solar_local.parse_registers
|
|
12
|
+
|
|
13
|
+
::: intelbras_solar_local.parse_datalogger
|
|
14
|
+
|
|
15
|
+
::: intelbras_solar_local.ModbusTcpClient
|
|
16
|
+
|
|
17
|
+
::: intelbras_solar_local.WiNetClient
|
|
18
|
+
|
|
19
|
+
::: intelbras_solar_local.IntelbrasSolarError
|
|
20
|
+
|
|
21
|
+
::: intelbras_solar_local.IntelbrasSolarConnectionError
|
|
22
|
+
|
|
23
|
+
::: intelbras_solar_local.IntelbrasSolarProtocolError
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--8<-- "CHANGELOG.md"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<p align="center"><img src="assets/logo.png" width="300" alt="Intelbras"></p>
|
|
2
|
+
|
|
3
|
+
# python-intelbras-solar-local
|
|
4
|
+
|
|
5
|
+
**python-intelbras-solar-local** is an async Python library that reads and controls Intelbras solar inverters straight
|
|
6
|
+
from your LAN. Intelbras IONS inverters are rebranded Sungrow units with a WiNet-S Wi-Fi datalogger. The library talks
|
|
7
|
+
Modbus TCP to the inverter, and reads datalogger state from the WebSocket of its built-in web UI. Neither interface asks
|
|
8
|
+
for a login. The library has no Home Assistant dependency.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install python-intelbras-solar-local
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quickstart
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import asyncio
|
|
20
|
+
from intelbras_solar_local import IntelbrasSolarClient
|
|
21
|
+
|
|
22
|
+
async def main():
|
|
23
|
+
async with IntelbrasSolarClient("192.168.31.17") as client:
|
|
24
|
+
status = await client.async_get_status()
|
|
25
|
+
print(status.model, status.power_ac, status.energy_today)
|
|
26
|
+
|
|
27
|
+
asyncio.run(main())
|
|
28
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Protocol
|
|
2
|
+
|
|
3
|
+
These notes come from reverse-engineering an Intelbras IONS-5K M5 with datalogger firmware `WIFI-SV300.001.03.P002`
|
|
4
|
+
on 2026-10-24. The library was tested against that real unit.
|
|
5
|
+
|
|
6
|
+
## Identification
|
|
7
|
+
|
|
8
|
+
- The datalogger web UI (`GET /`, a Vue + layui app) contains `version:"WiNet_V1.0.0AB2020112601"`. That string marks it
|
|
9
|
+
as a **Sungrow WiNet-S** sold under the Intelbras brand.
|
|
10
|
+
- The WebSocket `devicelist` reports `dev_model "IONS-5K M5"`, `dev_code 9748` (`0x2614`) and `dev_type 21`
|
|
11
|
+
(string inverter).
|
|
12
|
+
- Open ports: 80 (web UI), 443, **502 (Modbus TCP)** and **8082 (WebSocket)**. Ports 502 and 8082 require no login.
|
|
13
|
+
|
|
14
|
+
## WebSocket: `ws://<ip>:8082/ws/home/overview`
|
|
15
|
+
|
|
16
|
+
Requests and replies are JSON. Every reply has the shape `{result_code: 1, result_msg, result_data: {service, ...}}`.
|
|
17
|
+
The server also pushes unsolicited `notice` messages, so match each reply on its `service` field.
|
|
18
|
+
|
|
19
|
+
| Request | Reply |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `{"service":"connect","token":""}` | Session `token` (no login) |
|
|
22
|
+
| `devicelist` with `type:"0"`, `is_check_token:"0"` | Inverters: serial, model, `dev_id`, `link_status` |
|
|
23
|
+
| `real` with `dev_id` | 16 readings with I18N `data_name` keys |
|
|
24
|
+
| `direct` with `dev_id` | Voltage and current for each MPPT |
|
|
25
|
+
| `statistics` / `runtime` | Daily and total energy, current power, adjustable P/Q limits |
|
|
26
|
+
| `state` | `total_fault`, `total_alarm`, `wifi_conn_sts`, `cloud_conn_sts` |
|
|
27
|
+
| `fault` | Active fault list |
|
|
28
|
+
| `real_battery`, `deviceInfo` | `result_code` 211/215 on this model (not supported) |
|
|
29
|
+
|
|
30
|
+
## Modbus TCP (port 502, unit 1)
|
|
31
|
+
|
|
32
|
+
This is the Sungrow SG-RS map. Addresses are 0-based (documentation address − 1), and 32-bit values are sent low word
|
|
33
|
+
first.
|
|
34
|
+
|
|
35
|
+
### Input registers (FC4)
|
|
36
|
+
|
|
37
|
+
| Register | Value | Scale |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| 4989–4998 | Serial number (ASCII) | |
|
|
40
|
+
| 4999 | Device type code | |
|
|
41
|
+
| 5000 | Nominal power | 0.1 kW |
|
|
42
|
+
| 5002 | Daily energy | 0.1 kWh |
|
|
43
|
+
| 5003 U32 | Total energy | kWh |
|
|
44
|
+
| 5005 U32 | Total running time | h |
|
|
45
|
+
| 5007 S16 | Internal temperature | 0.1 °C |
|
|
46
|
+
| 5008 U32 | Apparent power | VA |
|
|
47
|
+
| 5010/5011, 5012/5013 | MPPT1 and MPPT2 voltage / current | 0.1 V / 0.1 A |
|
|
48
|
+
| 5016 U32 | Total DC power | W |
|
|
49
|
+
| 5018 / 5021 | Phase A voltage / current | 0.1 V / 0.1 A |
|
|
50
|
+
| 5030 U32 | Active power | W |
|
|
51
|
+
| 5032 S32 | Reactive power | var |
|
|
52
|
+
| 5034 S16 | Power factor | 0.001 |
|
|
53
|
+
| 5037 | Work state (see `WORK_STATES`) | |
|
|
54
|
+
| 5038–5043 / 5044 | Fault time / fault code | |
|
|
55
|
+
| 5070 | Array insulation resistance | kΩ |
|
|
56
|
+
| 5112 | Running time today | min |
|
|
57
|
+
| 5127 U32 | Monthly energy | 0.1 kWh |
|
|
58
|
+
| 5143 U32 | Total energy (high resolution) | 0.1 kWh |
|
|
59
|
+
| 5146 / 5147 | Bus voltage / grid frequency | 0.1 V / 0.01 Hz |
|
|
60
|
+
| 5225 U32 | Yearly energy (inferred) | 0.1 kWh |
|
|
61
|
+
|
|
62
|
+
### Holding registers (FC3 read, FC6 write)
|
|
63
|
+
|
|
64
|
+
| Register | Value |
|
|
65
|
+
|---|---|
|
|
66
|
+
| 4999–5004 | Clock: year, month, day, hour, minute, second |
|
|
67
|
+
| 5005 | `0xCF` start, `0xCE` stop |
|
|
68
|
+
| 5006 | Power limitation: `0xAA` on, `0x55` off |
|
|
69
|
+
| 5007 | Power limit in 0.1 % |
|
|
70
|
+
|
|
71
|
+
On this unit, a block read of the holding registers fails with exception 4 once it reaches register 5019.
|
|
72
|
+
|
|
73
|
+
## Not verified
|
|
74
|
+
|
|
75
|
+
- The yearly energy register (5225) is inferred. It read 6907 kWh while the Intelbras cloud showed 6842 kWh.
|
|
76
|
+
- The control register values come from Sungrow's documentation and match what this unit reads back. They are
|
|
77
|
+
exercised against a fake server in the tests.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Usage
|
|
2
|
+
|
|
3
|
+
## Reading
|
|
4
|
+
|
|
5
|
+
`async_get_status()` reads every Modbus block. It then tries the datalogger WebSocket. A WebSocket failure leaves
|
|
6
|
+
`status.datalogger` as `None` and does not fail the call; only Modbus errors raise.
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
import asyncio
|
|
10
|
+
from intelbras_solar_local import (
|
|
11
|
+
IntelbrasSolarClient,
|
|
12
|
+
IntelbrasSolarConnectionError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
async def main():
|
|
16
|
+
async with IntelbrasSolarClient("192.168.31.17") as client:
|
|
17
|
+
try:
|
|
18
|
+
status = await client.async_get_status()
|
|
19
|
+
except IntelbrasSolarConnectionError:
|
|
20
|
+
print("Datalogger unreachable (the inverter sleeps at night)")
|
|
21
|
+
return
|
|
22
|
+
print(status.work_state, status.producing, status.has_fault)
|
|
23
|
+
print(status.energy_today, status.energy_month, status.energy_year)
|
|
24
|
+
if status.datalogger:
|
|
25
|
+
print("Wi-Fi:", status.datalogger.wifi_connected)
|
|
26
|
+
print("Faults:", status.datalogger.faults)
|
|
27
|
+
|
|
28
|
+
asyncio.run(main())
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
To share an `aiohttp.ClientSession`, as Home Assistant does, pass `session=`. The client then leaves the session open
|
|
32
|
+
when it closes.
|
|
33
|
+
|
|
34
|
+
## Controls
|
|
35
|
+
|
|
36
|
+
!!! warning
|
|
37
|
+
These calls write holding registers on the inverter. Stopping the inverter halts generation until it is started
|
|
38
|
+
again.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
await client.async_set_power_limit_enabled(True)
|
|
42
|
+
await client.async_set_power_limit(80) # % of nominal power
|
|
43
|
+
await client.async_set_inverter_on(False) # stop generating
|
|
44
|
+
await client.async_set_inverter_on(True) # start again
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Errors
|
|
48
|
+
|
|
49
|
+
| Exception | When |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `IntelbrasSolarConnectionError` | The host is unreachable, the request times out, or the socket closes |
|
|
52
|
+
| `IntelbrasSolarProtocolError` | Modbus exception reply, or WiNet `result_code` other than 1 |
|
|
53
|
+
| `IntelbrasSolarError` | Base class of both |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Print everything the inverter reports.
|
|
2
|
+
|
|
3
|
+
Usage: python examples/status.py 192.168.31.17
|
|
4
|
+
"""
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from dataclasses import asdict
|
|
9
|
+
|
|
10
|
+
from intelbras_solar_local import IntelbrasSolarClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def main(host: str) -> None:
|
|
14
|
+
async with IntelbrasSolarClient(host) as client:
|
|
15
|
+
status = await client.async_get_status()
|
|
16
|
+
data = asdict(status) | {
|
|
17
|
+
"model": status.model,
|
|
18
|
+
"work_state": status.work_state,
|
|
19
|
+
"producing": status.producing,
|
|
20
|
+
"has_fault": status.has_fault,
|
|
21
|
+
}
|
|
22
|
+
print(json.dumps(data, indent=1, default=str, ensure_ascii=False))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
asyncio.run(main(sys.argv[1]))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
site_name: python-intelbras-solar-local
|
|
2
|
+
site_description: Async Python client for Intelbras solar inverters (Sungrow WiNet-S) over the LAN
|
|
3
|
+
site_url: https://hudsonbrendon.github.io/python-intelbras-solar-local/
|
|
4
|
+
repo_url: https://github.com/hudsonbrendon/python-intelbras-solar-local
|
|
5
|
+
repo_name: hudsonbrendon/python-intelbras-solar-local
|
|
6
|
+
docs_dir: docs
|
|
7
|
+
theme:
|
|
8
|
+
name: material
|
|
9
|
+
logo: assets/icon.png
|
|
10
|
+
favicon: assets/icon.png
|
|
11
|
+
palette:
|
|
12
|
+
- media: "(prefers-color-scheme: light)"
|
|
13
|
+
scheme: default
|
|
14
|
+
toggle: {icon: material/weather-night, name: Switch to dark mode}
|
|
15
|
+
- media: "(prefers-color-scheme: dark)"
|
|
16
|
+
scheme: slate
|
|
17
|
+
toggle: {icon: material/weather-sunny, name: Switch to light mode}
|
|
18
|
+
features:
|
|
19
|
+
- navigation.sections
|
|
20
|
+
- navigation.top
|
|
21
|
+
- content.code.copy
|
|
22
|
+
- search.suggest
|
|
23
|
+
- search.highlight
|
|
24
|
+
plugins:
|
|
25
|
+
- search
|
|
26
|
+
- mkdocstrings:
|
|
27
|
+
handlers:
|
|
28
|
+
python:
|
|
29
|
+
paths: [src]
|
|
30
|
+
options:
|
|
31
|
+
docstring_style: google
|
|
32
|
+
show_source: true
|
|
33
|
+
show_root_heading: true
|
|
34
|
+
heading_level: 2
|
|
35
|
+
members_order: source
|
|
36
|
+
separate_signature: true
|
|
37
|
+
show_signature_annotations: true
|
|
38
|
+
markdown_extensions:
|
|
39
|
+
- admonition
|
|
40
|
+
- pymdownx.highlight
|
|
41
|
+
- pymdownx.superfences
|
|
42
|
+
- pymdownx.snippets
|
|
43
|
+
- toc:
|
|
44
|
+
permalink: true
|
|
45
|
+
nav:
|
|
46
|
+
- Home: index.md
|
|
47
|
+
- Usage: usage.md
|
|
48
|
+
- Protocol: protocol.md
|
|
49
|
+
- API reference: api.md
|
|
50
|
+
- Changelog: changelog.md
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python-intelbras-solar-local"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Async Python client for Intelbras solar inverters (Sungrow WiNet-S) over Modbus TCP and the datalogger WebSocket"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "Hudson Brendon", email = "contato.hudsonbrendon@gmail.com"}]
|
|
13
|
+
keywords = ["intelbras", "sungrow", "winet", "solar", "inverter", "modbus", "home-assistant", "async", "aiohttp"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Framework :: AsyncIO",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Topic :: Software Development :: Libraries",
|
|
22
|
+
]
|
|
23
|
+
dependencies = ["aiohttp>=3.9"]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/hudsonbrendon/python-intelbras-solar-local"
|
|
27
|
+
Documentation = "https://hudsonbrendon.github.io/python-intelbras-solar-local/"
|
|
28
|
+
Issues = "https://github.com/hudsonbrendon/python-intelbras-solar-local/issues"
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
test = ["pytest>=8", "pytest-asyncio>=0.23", "ruff"]
|
|
32
|
+
docs = ["mkdocs-material>=9.5", "mkdocstrings[python]>=0.25"]
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["src/intelbras_solar_local"]
|
|
36
|
+
|
|
37
|
+
[tool.ruff]
|
|
38
|
+
target-version = "py311"
|
|
39
|
+
line-length = 88
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
asyncio_mode = "auto"
|
|
46
|
+
addopts = "-q"
|