Skip to content
totaldebugPublic

About

Framing, register map, and discovery for the LuxPower inverter Modbus protocol.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Release Stargazers codecov

Contributors Forks Issues

MIT License


Framing, register map, and discovery for the LuxPower inverter Modbus protocol.


Report Bug · Request Feature
Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. The Frame
  5. Provenance
  6. Contributing
  7. License

About The Project

luxmodbus is a small, dependency-free Python library for the LuxPower inverter Modbus protocol — packet framing, the declarative register map, and register discovery. It has no Home Assistant dependency and is the protocol core consumed by the Lumen Home Assistant integration. Because it imports nothing from Home Assistant, it can be tested entirely offline against captured packet bytes.

Status

Early. Implemented so far:

  • protocol.py — frame encode/decode (LuxPower TCP envelope + inner Modbus RTU data frame), read/write request builders, and read-response unpacking. No I/O, no register-meaning knowledge.
  • registers.py — declarative address → meaning map (the single source of truth) with a decode engine and a bounds-checked encode_value for writes.
  • discovery.py — passive diff-and-log engine: compares observed registers against the known map and records the unknown with a rolling value history.

(back to top)

Getting Started

This project uses uv.

git clone https://github.com/totaldebug/luxmodbus.git
cd luxmodbus
uv sync
uv run nox -s tests

(back to top)

Usage

from luxmodbus import Frame, decode_inputs

frame = Frame.decode(raw_bytes)          # validates prefix + CRC
data = frame.data_frame()                # inner Modbus frame
# raw register values -> {key: scaled value}
values = decode_inputs({1: 2503, 4: 530, 5: (90 << 8) | 88})
# {"pv1_voltage": 250.3, "battery_voltage": 53.0, "soc": 88, "soh": 90}

(back to top)

The Frame

LuxPower wraps a modified Modbus RTU "data frame" inside a TCP envelope:

prefix(2)=A1 1A | protocol(u16 LE) | frame_length(u16 LE) | reserved(1)=01 |
tcp_function(1) | dongle_serial(10) | data_length(u16 LE) | data_frame(N) | crc16(u16 LE)
  • frame_length = total_len - 6
  • data_length = len(data_frame) + 2 (the trailing CRC)
  • crc16 is the standard Modbus CRC (poly 0xA001, init 0xFFFF) over the data frame, appended little-endian.

The inner data frame:

action(1) | device_function(1) | inverter_serial(10) | register(u16 LE) | value

See docs/capturing-packets.md for how to capture real packets and turn them into test fixtures.

(back to top)

Provenance

Clean-room: the protocol facts (field layout, CRC algorithm, function codes, register meanings) are taken from the official Lux Power Modbus RTU specification and validated against real packet bytes. No code is copied from other implementations.

(back to top)

Contributing

Contributions are welcome. Please open an issue first to discuss changes, then ensure uv run nox -s tests passes (style, types, docstring coverage, and the test suite) before opening a PR.

(back to top)

License

Distributed under the MIT License. See LICENSE for more information.

(back to top)

About

Framing, register map, and discovery for the LuxPower inverter Modbus protocol.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages