luxmodbus 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.
- luxmodbus-0.1.0/PKG-INFO +178 -0
- luxmodbus-0.1.0/README.md +154 -0
- luxmodbus-0.1.0/pyproject.toml +95 -0
- luxmodbus-0.1.0/src/luxmodbus/__init__.py +111 -0
- luxmodbus-0.1.0/src/luxmodbus/discovery.py +198 -0
- luxmodbus-0.1.0/src/luxmodbus/protocol.py +400 -0
- luxmodbus-0.1.0/src/luxmodbus/py.typed +0 -0
- luxmodbus-0.1.0/src/luxmodbus/registers.py +2356 -0
- luxmodbus-0.1.0/src/luxmodbus/transport.py +292 -0
luxmodbus-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: luxmodbus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Framing, register map, and discovery for the LuxPower inverter Modbus protocol (HA-free).
|
|
5
|
+
Keywords: luxpower,modbus,inverter,solar,home-assistant,lumen
|
|
6
|
+
Author: Steven Marks
|
|
7
|
+
Author-email: Steven Marks <marksie1988@users.noreply.github.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Home Automation
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Project-URL: homepage, https://github.com/totaldebug/luxmodbus
|
|
22
|
+
Project-URL: repository, https://github.com/totaldebug/luxmodbus
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
<a name="readme-top"></a>
|
|
26
|
+
|
|
27
|
+
[![Release][release-shield]][release-url]
|
|
28
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
29
|
+
![codecov][codecov-shield]
|
|
30
|
+
|
|
31
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
32
|
+
[![Forks][forks-shield]][forks-url]
|
|
33
|
+
[![Issues][issues-shield]][issues-url]
|
|
34
|
+
|
|
35
|
+
[![MIT License][license-shield]][license-url]
|
|
36
|
+
|
|
37
|
+
<!-- PROJECT HEADER -->
|
|
38
|
+
<br />
|
|
39
|
+
<div align="center">
|
|
40
|
+
<a href="https://github.com/totaldebug/luxmodbus">
|
|
41
|
+
<h3 align="center">luxmodbus</h3>
|
|
42
|
+
</a>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
Framing, register map, and discovery for the LuxPower inverter Modbus protocol.
|
|
46
|
+
</p>
|
|
47
|
+
<br />
|
|
48
|
+
<a href="https://github.com/totaldebug/luxmodbus/issues/new?labels=type%2Fbug&template=bug_report.yml">Report Bug</a>
|
|
49
|
+
·
|
|
50
|
+
<a href="https://github.com/totaldebug/luxmodbus/issues/new?labels=type%2Ffeature&template=feature_request.yml">Request Feature</a>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<!-- TABLE OF CONTENTS -->
|
|
54
|
+
<details>
|
|
55
|
+
<summary>Table of Contents</summary>
|
|
56
|
+
<ol>
|
|
57
|
+
<li><a href="#about-the-project">About The Project</a></li>
|
|
58
|
+
<li><a href="#getting-started">Getting Started</a></li>
|
|
59
|
+
<li><a href="#usage">Usage</a></li>
|
|
60
|
+
<li><a href="#the-frame">The Frame</a></li>
|
|
61
|
+
<li><a href="#provenance">Provenance</a></li>
|
|
62
|
+
<li><a href="#contributing">Contributing</a></li>
|
|
63
|
+
<li><a href="#license">License</a></li>
|
|
64
|
+
</ol>
|
|
65
|
+
</details>
|
|
66
|
+
|
|
67
|
+
## About The Project
|
|
68
|
+
|
|
69
|
+
`luxmodbus` is a small, dependency-free Python library for the **LuxPower
|
|
70
|
+
inverter Modbus protocol** — packet framing, the declarative register map, and
|
|
71
|
+
register discovery. It has **no Home Assistant dependency** and is the protocol
|
|
72
|
+
core consumed by the [Lumen](https://github.com/totaldebug/lumen) Home Assistant
|
|
73
|
+
integration. Because it imports nothing from Home Assistant, it can be tested
|
|
74
|
+
entirely offline against captured packet bytes.
|
|
75
|
+
|
|
76
|
+
### Status
|
|
77
|
+
|
|
78
|
+
Early. Implemented so far:
|
|
79
|
+
|
|
80
|
+
- `protocol.py` — frame encode/decode (LuxPower TCP envelope + inner Modbus RTU
|
|
81
|
+
data frame), read/write request builders, and read-response unpacking. No I/O,
|
|
82
|
+
no register-meaning knowledge.
|
|
83
|
+
- `registers.py` — declarative address → meaning map (the single source of
|
|
84
|
+
truth) with a decode engine and a bounds-checked `encode_value` for writes.
|
|
85
|
+
- `discovery.py` — passive diff-and-log engine: compares observed registers
|
|
86
|
+
against the known map and records the unknown with a rolling value history.
|
|
87
|
+
|
|
88
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
89
|
+
|
|
90
|
+
## Getting Started
|
|
91
|
+
|
|
92
|
+
This project uses [uv](https://docs.astral.sh/uv/).
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone https://github.com/totaldebug/luxmodbus.git
|
|
96
|
+
cd luxmodbus
|
|
97
|
+
uv sync
|
|
98
|
+
uv run nox -s tests
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from luxmodbus import Frame, decode_inputs
|
|
107
|
+
|
|
108
|
+
frame = Frame.decode(raw_bytes) # validates prefix + CRC
|
|
109
|
+
data = frame.data_frame() # inner Modbus frame
|
|
110
|
+
# raw register values -> {key: scaled value}
|
|
111
|
+
values = decode_inputs({1: 2503, 4: 530, 5: (90 << 8) | 88})
|
|
112
|
+
# {"pv1_voltage": 250.3, "battery_voltage": 53.0, "soc": 88, "soh": 90}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
116
|
+
|
|
117
|
+
## The Frame
|
|
118
|
+
|
|
119
|
+
LuxPower wraps a modified Modbus RTU "data frame" inside a TCP envelope:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
prefix(2)=A1 1A | protocol(u16 LE) | frame_length(u16 LE) | reserved(1)=01 |
|
|
123
|
+
tcp_function(1) | dongle_serial(10) | data_length(u16 LE) | data_frame(N) | crc16(u16 LE)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `frame_length = total_len - 6`
|
|
127
|
+
- `data_length = len(data_frame) + 2` (the trailing CRC)
|
|
128
|
+
- `crc16` is the standard Modbus CRC (poly `0xA001`, init `0xFFFF`) over the
|
|
129
|
+
data frame, appended little-endian.
|
|
130
|
+
|
|
131
|
+
The inner data frame:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
action(1) | device_function(1) | inverter_serial(10) | register(u16 LE) | value
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
See [`docs/capturing-packets.md`](docs/capturing-packets.md) for how to capture
|
|
138
|
+
real packets and turn them into test fixtures.
|
|
139
|
+
|
|
140
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
141
|
+
|
|
142
|
+
## Provenance
|
|
143
|
+
|
|
144
|
+
Clean-room: the protocol *facts* (field layout, CRC algorithm, function codes,
|
|
145
|
+
register meanings) are taken from the official Lux Power Modbus RTU
|
|
146
|
+
specification and validated against real packet bytes. No code is copied from
|
|
147
|
+
other implementations.
|
|
148
|
+
|
|
149
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
150
|
+
|
|
151
|
+
## Contributing
|
|
152
|
+
|
|
153
|
+
Contributions are welcome. Please open an issue first to discuss changes, then
|
|
154
|
+
ensure `uv run nox -s tests` passes (style, types, docstring coverage, and the
|
|
155
|
+
test suite) before opening a PR.
|
|
156
|
+
|
|
157
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
162
|
+
|
|
163
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
164
|
+
|
|
165
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
|
166
|
+
[release-shield]: https://img.shields.io/github/v/release/totaldebug/luxmodbus?style=for-the-badge
|
|
167
|
+
[release-url]: https://github.com/totaldebug/luxmodbus/releases
|
|
168
|
+
[stars-shield]: https://img.shields.io/github/stars/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
169
|
+
[stars-url]: https://github.com/totaldebug/luxmodbus/stargazers
|
|
170
|
+
[codecov-shield]: https://img.shields.io/codecov/c/github/totaldebug/luxmodbus?style=for-the-badge
|
|
171
|
+
[contributors-shield]: https://img.shields.io/github/contributors/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
172
|
+
[contributors-url]: https://github.com/totaldebug/luxmodbus/graphs/contributors
|
|
173
|
+
[forks-shield]: https://img.shields.io/github/forks/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
174
|
+
[forks-url]: https://github.com/totaldebug/luxmodbus/network/members
|
|
175
|
+
[issues-shield]: https://img.shields.io/github/issues/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
176
|
+
[issues-url]: https://github.com/totaldebug/luxmodbus/issues
|
|
177
|
+
[license-shield]: https://img.shields.io/github/license/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
178
|
+
[license-url]: https://github.com/totaldebug/luxmodbus/blob/main/LICENSE
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<a name="readme-top"></a>
|
|
2
|
+
|
|
3
|
+
[![Release][release-shield]][release-url]
|
|
4
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
5
|
+
![codecov][codecov-shield]
|
|
6
|
+
|
|
7
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
8
|
+
[![Forks][forks-shield]][forks-url]
|
|
9
|
+
[![Issues][issues-shield]][issues-url]
|
|
10
|
+
|
|
11
|
+
[![MIT License][license-shield]][license-url]
|
|
12
|
+
|
|
13
|
+
<!-- PROJECT HEADER -->
|
|
14
|
+
<br />
|
|
15
|
+
<div align="center">
|
|
16
|
+
<a href="https://github.com/totaldebug/luxmodbus">
|
|
17
|
+
<h3 align="center">luxmodbus</h3>
|
|
18
|
+
</a>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
Framing, register map, and discovery for the LuxPower inverter Modbus protocol.
|
|
22
|
+
</p>
|
|
23
|
+
<br />
|
|
24
|
+
<a href="https://github.com/totaldebug/luxmodbus/issues/new?labels=type%2Fbug&template=bug_report.yml">Report Bug</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="https://github.com/totaldebug/luxmodbus/issues/new?labels=type%2Ffeature&template=feature_request.yml">Request Feature</a>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- TABLE OF CONTENTS -->
|
|
30
|
+
<details>
|
|
31
|
+
<summary>Table of Contents</summary>
|
|
32
|
+
<ol>
|
|
33
|
+
<li><a href="#about-the-project">About The Project</a></li>
|
|
34
|
+
<li><a href="#getting-started">Getting Started</a></li>
|
|
35
|
+
<li><a href="#usage">Usage</a></li>
|
|
36
|
+
<li><a href="#the-frame">The Frame</a></li>
|
|
37
|
+
<li><a href="#provenance">Provenance</a></li>
|
|
38
|
+
<li><a href="#contributing">Contributing</a></li>
|
|
39
|
+
<li><a href="#license">License</a></li>
|
|
40
|
+
</ol>
|
|
41
|
+
</details>
|
|
42
|
+
|
|
43
|
+
## About The Project
|
|
44
|
+
|
|
45
|
+
`luxmodbus` is a small, dependency-free Python library for the **LuxPower
|
|
46
|
+
inverter Modbus protocol** — packet framing, the declarative register map, and
|
|
47
|
+
register discovery. It has **no Home Assistant dependency** and is the protocol
|
|
48
|
+
core consumed by the [Lumen](https://github.com/totaldebug/lumen) Home Assistant
|
|
49
|
+
integration. Because it imports nothing from Home Assistant, it can be tested
|
|
50
|
+
entirely offline against captured packet bytes.
|
|
51
|
+
|
|
52
|
+
### Status
|
|
53
|
+
|
|
54
|
+
Early. Implemented so far:
|
|
55
|
+
|
|
56
|
+
- `protocol.py` — frame encode/decode (LuxPower TCP envelope + inner Modbus RTU
|
|
57
|
+
data frame), read/write request builders, and read-response unpacking. No I/O,
|
|
58
|
+
no register-meaning knowledge.
|
|
59
|
+
- `registers.py` — declarative address → meaning map (the single source of
|
|
60
|
+
truth) with a decode engine and a bounds-checked `encode_value` for writes.
|
|
61
|
+
- `discovery.py` — passive diff-and-log engine: compares observed registers
|
|
62
|
+
against the known map and records the unknown with a rolling value history.
|
|
63
|
+
|
|
64
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
65
|
+
|
|
66
|
+
## Getting Started
|
|
67
|
+
|
|
68
|
+
This project uses [uv](https://docs.astral.sh/uv/).
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/totaldebug/luxmodbus.git
|
|
72
|
+
cd luxmodbus
|
|
73
|
+
uv sync
|
|
74
|
+
uv run nox -s tests
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from luxmodbus import Frame, decode_inputs
|
|
83
|
+
|
|
84
|
+
frame = Frame.decode(raw_bytes) # validates prefix + CRC
|
|
85
|
+
data = frame.data_frame() # inner Modbus frame
|
|
86
|
+
# raw register values -> {key: scaled value}
|
|
87
|
+
values = decode_inputs({1: 2503, 4: 530, 5: (90 << 8) | 88})
|
|
88
|
+
# {"pv1_voltage": 250.3, "battery_voltage": 53.0, "soc": 88, "soh": 90}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
92
|
+
|
|
93
|
+
## The Frame
|
|
94
|
+
|
|
95
|
+
LuxPower wraps a modified Modbus RTU "data frame" inside a TCP envelope:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
prefix(2)=A1 1A | protocol(u16 LE) | frame_length(u16 LE) | reserved(1)=01 |
|
|
99
|
+
tcp_function(1) | dongle_serial(10) | data_length(u16 LE) | data_frame(N) | crc16(u16 LE)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- `frame_length = total_len - 6`
|
|
103
|
+
- `data_length = len(data_frame) + 2` (the trailing CRC)
|
|
104
|
+
- `crc16` is the standard Modbus CRC (poly `0xA001`, init `0xFFFF`) over the
|
|
105
|
+
data frame, appended little-endian.
|
|
106
|
+
|
|
107
|
+
The inner data frame:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
action(1) | device_function(1) | inverter_serial(10) | register(u16 LE) | value
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
See [`docs/capturing-packets.md`](docs/capturing-packets.md) for how to capture
|
|
114
|
+
real packets and turn them into test fixtures.
|
|
115
|
+
|
|
116
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
117
|
+
|
|
118
|
+
## Provenance
|
|
119
|
+
|
|
120
|
+
Clean-room: the protocol *facts* (field layout, CRC algorithm, function codes,
|
|
121
|
+
register meanings) are taken from the official Lux Power Modbus RTU
|
|
122
|
+
specification and validated against real packet bytes. No code is copied from
|
|
123
|
+
other implementations.
|
|
124
|
+
|
|
125
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
Contributions are welcome. Please open an issue first to discuss changes, then
|
|
130
|
+
ensure `uv run nox -s tests` passes (style, types, docstring coverage, and the
|
|
131
|
+
test suite) before opening a PR.
|
|
132
|
+
|
|
133
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
138
|
+
|
|
139
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
140
|
+
|
|
141
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
|
142
|
+
[release-shield]: https://img.shields.io/github/v/release/totaldebug/luxmodbus?style=for-the-badge
|
|
143
|
+
[release-url]: https://github.com/totaldebug/luxmodbus/releases
|
|
144
|
+
[stars-shield]: https://img.shields.io/github/stars/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
145
|
+
[stars-url]: https://github.com/totaldebug/luxmodbus/stargazers
|
|
146
|
+
[codecov-shield]: https://img.shields.io/codecov/c/github/totaldebug/luxmodbus?style=for-the-badge
|
|
147
|
+
[contributors-shield]: https://img.shields.io/github/contributors/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
148
|
+
[contributors-url]: https://github.com/totaldebug/luxmodbus/graphs/contributors
|
|
149
|
+
[forks-shield]: https://img.shields.io/github/forks/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
150
|
+
[forks-url]: https://github.com/totaldebug/luxmodbus/network/members
|
|
151
|
+
[issues-shield]: https://img.shields.io/github/issues/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
152
|
+
[issues-url]: https://github.com/totaldebug/luxmodbus/issues
|
|
153
|
+
[license-shield]: https://img.shields.io/github/license/totaldebug/luxmodbus.svg?style=for-the-badge
|
|
154
|
+
[license-url]: https://github.com/totaldebug/luxmodbus/blob/main/LICENSE
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "luxmodbus"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Framing, register map, and discovery for the LuxPower inverter Modbus protocol (HA-free)."
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Steven Marks", email = "marksie1988@users.noreply.github.com" }
|
|
7
|
+
]
|
|
8
|
+
license = { text = "MIT" }
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = ["luxpower", "modbus", "inverter", "solar", "home-assistant", "lumen"]
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Natural Language :: English",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Topic :: Home Automation",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = []
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
homepage = "https://github.com/totaldebug/luxmodbus"
|
|
29
|
+
repository = "https://github.com/totaldebug/luxmodbus"
|
|
30
|
+
|
|
31
|
+
[dependency-groups]
|
|
32
|
+
dev = [
|
|
33
|
+
"ruff>=0.9.0",
|
|
34
|
+
"mypy>=1.10.0",
|
|
35
|
+
"pre-commit>=3.7.1",
|
|
36
|
+
"interrogate>=1.5.0",
|
|
37
|
+
"pytest>=8.2.2",
|
|
38
|
+
"pytest-asyncio>=0.23",
|
|
39
|
+
"pytest-cov>=5.0.0",
|
|
40
|
+
"nox>=2024.4.15",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["uv_build>=0.10.0,<0.11.0"]
|
|
45
|
+
build-backend = "uv_build"
|
|
46
|
+
|
|
47
|
+
[tool.uv.build-backend]
|
|
48
|
+
module-name = "luxmodbus"
|
|
49
|
+
module-root = "src"
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
target-version = "py312"
|
|
54
|
+
exclude = ["tests"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
58
|
+
ignore = []
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint.isort]
|
|
61
|
+
known-first-party = ["luxmodbus"]
|
|
62
|
+
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
python_version = "3.12"
|
|
66
|
+
warn_return_any = true
|
|
67
|
+
warn_unused_configs = true
|
|
68
|
+
mypy_path = "src"
|
|
69
|
+
|
|
70
|
+
[[tool.mypy.overrides]]
|
|
71
|
+
module = "luxmodbus.*"
|
|
72
|
+
disallow_untyped_defs = true
|
|
73
|
+
|
|
74
|
+
[tool.interrogate]
|
|
75
|
+
ignore-init-method = true
|
|
76
|
+
ignore-init-module = false
|
|
77
|
+
ignore-magic = false
|
|
78
|
+
ignore-semiprivate = false
|
|
79
|
+
ignore-private = false
|
|
80
|
+
ignore-property-decorators = false
|
|
81
|
+
ignore-module = true
|
|
82
|
+
ignore-nested-functions = false
|
|
83
|
+
ignore-nested-classes = true
|
|
84
|
+
ignore-setters = false
|
|
85
|
+
fail-under = 100
|
|
86
|
+
exclude = ["build", ".devcontainer", ".nox", ".cache", "tests"]
|
|
87
|
+
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
|
|
88
|
+
verbose = 0
|
|
89
|
+
quiet = false
|
|
90
|
+
color = true
|
|
91
|
+
|
|
92
|
+
[tool.pytest.ini_options]
|
|
93
|
+
testpaths = ["tests"]
|
|
94
|
+
asyncio_mode = "auto"
|
|
95
|
+
addopts = "-q"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""luxmodbus — HA-free framing, register map, and discovery for LuxPower inverters."""
|
|
2
|
+
|
|
3
|
+
from luxmodbus.discovery import (
|
|
4
|
+
DEFAULT_HISTORY_LIMIT,
|
|
5
|
+
DiscoveryStore,
|
|
6
|
+
UnknownRegister,
|
|
7
|
+
)
|
|
8
|
+
from luxmodbus.protocol import (
|
|
9
|
+
CrcError,
|
|
10
|
+
DataFrame,
|
|
11
|
+
DeviceFunction,
|
|
12
|
+
Frame,
|
|
13
|
+
PrefixError,
|
|
14
|
+
ProtocolError,
|
|
15
|
+
TcpFunction,
|
|
16
|
+
TruncatedFrameError,
|
|
17
|
+
crc16,
|
|
18
|
+
decode_read_response,
|
|
19
|
+
extract_frames,
|
|
20
|
+
)
|
|
21
|
+
from luxmodbus.registers import (
|
|
22
|
+
FLAG_REGISTERS,
|
|
23
|
+
HOLD_REGISTERS,
|
|
24
|
+
INPUT_REGISTERS,
|
|
25
|
+
SELECT_REGISTERS,
|
|
26
|
+
TIME_REGISTERS,
|
|
27
|
+
ByteSelect,
|
|
28
|
+
FlagDef,
|
|
29
|
+
FlagRegister,
|
|
30
|
+
Measurement,
|
|
31
|
+
RegisterBank,
|
|
32
|
+
RegisterDef,
|
|
33
|
+
SelectRegister,
|
|
34
|
+
TimeRegister,
|
|
35
|
+
ValueType,
|
|
36
|
+
decode_flags,
|
|
37
|
+
decode_holds,
|
|
38
|
+
decode_inputs,
|
|
39
|
+
decode_select,
|
|
40
|
+
decode_time,
|
|
41
|
+
decode_value,
|
|
42
|
+
encode_time,
|
|
43
|
+
encode_value,
|
|
44
|
+
find_hold,
|
|
45
|
+
find_input,
|
|
46
|
+
mapped_hold_addresses,
|
|
47
|
+
mapped_input_addresses,
|
|
48
|
+
set_flag,
|
|
49
|
+
set_select,
|
|
50
|
+
)
|
|
51
|
+
from luxmodbus.transport import (
|
|
52
|
+
ClientTransport,
|
|
53
|
+
ServerTransport,
|
|
54
|
+
Transport,
|
|
55
|
+
TransportConnectError,
|
|
56
|
+
TransportError,
|
|
57
|
+
TransportNotConnectedError,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
__version__ = "0.1.0"
|
|
61
|
+
|
|
62
|
+
__all__ = [
|
|
63
|
+
"DEFAULT_HISTORY_LIMIT",
|
|
64
|
+
"FLAG_REGISTERS",
|
|
65
|
+
"HOLD_REGISTERS",
|
|
66
|
+
"INPUT_REGISTERS",
|
|
67
|
+
"ByteSelect",
|
|
68
|
+
"ClientTransport",
|
|
69
|
+
"CrcError",
|
|
70
|
+
"DataFrame",
|
|
71
|
+
"DeviceFunction",
|
|
72
|
+
"DiscoveryStore",
|
|
73
|
+
"FlagDef",
|
|
74
|
+
"FlagRegister",
|
|
75
|
+
"Frame",
|
|
76
|
+
"Measurement",
|
|
77
|
+
"PrefixError",
|
|
78
|
+
"ProtocolError",
|
|
79
|
+
"RegisterBank",
|
|
80
|
+
"RegisterDef",
|
|
81
|
+
"SELECT_REGISTERS",
|
|
82
|
+
"TIME_REGISTERS",
|
|
83
|
+
"SelectRegister",
|
|
84
|
+
"ServerTransport",
|
|
85
|
+
"TcpFunction",
|
|
86
|
+
"TimeRegister",
|
|
87
|
+
"Transport",
|
|
88
|
+
"TransportConnectError",
|
|
89
|
+
"TransportError",
|
|
90
|
+
"TransportNotConnectedError",
|
|
91
|
+
"TruncatedFrameError",
|
|
92
|
+
"UnknownRegister",
|
|
93
|
+
"ValueType",
|
|
94
|
+
"crc16",
|
|
95
|
+
"decode_flags",
|
|
96
|
+
"decode_holds",
|
|
97
|
+
"decode_inputs",
|
|
98
|
+
"decode_read_response",
|
|
99
|
+
"decode_select",
|
|
100
|
+
"decode_time",
|
|
101
|
+
"decode_value",
|
|
102
|
+
"encode_time",
|
|
103
|
+
"encode_value",
|
|
104
|
+
"extract_frames",
|
|
105
|
+
"find_hold",
|
|
106
|
+
"find_input",
|
|
107
|
+
"mapped_hold_addresses",
|
|
108
|
+
"mapped_input_addresses",
|
|
109
|
+
"set_flag",
|
|
110
|
+
"set_select",
|
|
111
|
+
]
|