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.
Files changed (32) hide show
  1. python_intelbras_solar_local-0.1.0/.github/workflows/docs.yml +33 -0
  2. python_intelbras_solar_local-0.1.0/.github/workflows/release.yml +26 -0
  3. python_intelbras_solar_local-0.1.0/.github/workflows/tests.yml +30 -0
  4. python_intelbras_solar_local-0.1.0/.gitignore +11 -0
  5. python_intelbras_solar_local-0.1.0/CHANGELOG.md +12 -0
  6. python_intelbras_solar_local-0.1.0/LICENSE +21 -0
  7. python_intelbras_solar_local-0.1.0/PKG-INFO +122 -0
  8. python_intelbras_solar_local-0.1.0/README.md +93 -0
  9. python_intelbras_solar_local-0.1.0/docs/api.md +23 -0
  10. python_intelbras_solar_local-0.1.0/docs/assets/icon.png +0 -0
  11. python_intelbras_solar_local-0.1.0/docs/assets/logo.png +0 -0
  12. python_intelbras_solar_local-0.1.0/docs/changelog.md +1 -0
  13. python_intelbras_solar_local-0.1.0/docs/index.md +28 -0
  14. python_intelbras_solar_local-0.1.0/docs/protocol.md +77 -0
  15. python_intelbras_solar_local-0.1.0/docs/usage.md +53 -0
  16. python_intelbras_solar_local-0.1.0/examples/status.py +25 -0
  17. python_intelbras_solar_local-0.1.0/mkdocs.yml +50 -0
  18. python_intelbras_solar_local-0.1.0/pyproject.toml +46 -0
  19. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/__init__.py +43 -0
  20. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/client.py +138 -0
  21. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/const.py +55 -0
  22. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/exceptions.py +14 -0
  23. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/modbus.py +99 -0
  24. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/models.py +188 -0
  25. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/py.typed +0 -0
  26. python_intelbras_solar_local-0.1.0/src/intelbras_solar_local/winet.py +72 -0
  27. python_intelbras_solar_local-0.1.0/tests/__init__.py +0 -0
  28. python_intelbras_solar_local-0.1.0/tests/conftest.py +126 -0
  29. python_intelbras_solar_local-0.1.0/tests/test_client.py +44 -0
  30. python_intelbras_solar_local-0.1.0/tests/test_modbus.py +36 -0
  31. python_intelbras_solar_local-0.1.0/tests/test_models.py +75 -0
  32. 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,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .coverage
10
+ .DS_Store
11
+ site/
@@ -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
+ [![Tests](https://github.com/hudsonbrendon/python-intelbras-solar-local/actions/workflows/tests.yml/badge.svg)](https://github.com/hudsonbrendon/python-intelbras-solar-local/actions/workflows/tests.yml)
35
+ [![PyPI version](https://img.shields.io/pypi/v/python-intelbras-solar-local.svg)](https://pypi.org/project/python-intelbras-solar-local/)
36
+ [![Python](https://img.shields.io/pypi/pyversions/python-intelbras-solar-local.svg)](https://pypi.org/project/python-intelbras-solar-local/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ [![Tests](https://github.com/hudsonbrendon/python-intelbras-solar-local/actions/workflows/tests.yml/badge.svg)](https://github.com/hudsonbrendon/python-intelbras-solar-local/actions/workflows/tests.yml)
6
+ [![PyPI version](https://img.shields.io/pypi/v/python-intelbras-solar-local.svg)](https://pypi.org/project/python-intelbras-solar-local/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/python-intelbras-solar-local.svg)](https://pypi.org/project/python-intelbras-solar-local/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
@@ -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"