victron-modbus Python library
victron-modbus is an asynchronous, transport-independent Python library for
reading Victron Energy devices over Modbus. It has no dependencies beyond
modbus-connection, so any Python application can use it.
Purpose and scope
Victron products that speak Modbus share conventions: the same
identification registers (product ID at 0x1000, serial number at 0x1001),
data types and unavailable-value markers. Each product type still has its own
register map. victron-modbus follows that shape: one library, one
sub-package per device type, each with its own register map, data types,
scaling and units.
The library:
- contains the register map for each supported device type, grouped by sub-system. A component reads only the registers it declares, merging adjacent ones into one request. Registers outside the map can depend on the model and firmware, and a device may reject a whole request that touches one it lacks,
- does not create or own the Modbus transport. Applications using the
library provide a
modbus_connection.ModbusUnitand may use any backend supported bymodbus-connection(pymodbus, tmodbus, ...).
An example script script/query.py shows how to build an application that
queries a supported device over Modbus/UDP or Modbus/TCP
(python script/query.py 192.168.1.101; the model is detected from the
product ID). It uses the tmodbus backend, so install it with
pip install victron-modbus[cli].
Supported devices
| Device | Product ID | Package | Registers read |
|---|---|---|---|
| Victron VM-3P75CT | 0xA1B1, 0xA1B5 |
victron_modbus.vm_3p75ct |
total power/energy, PE-N voltage, frequency (also in 0.001 Hz, where the firmware has it), L1-L3 voltage/current/power/energy |
| Victron VM-3P5A | 0xA1B2 |
victron_modbus.vm_3p5a |
total power/energy, PE-N voltage, frequency (also in 0.001 Hz, where the firmware has it), L1-L3 voltage/current/power/energy |
| Victron P1Link | 0xC02F |
victron_modbus.p1link |
total power/energy, L1-L3 voltage/current/power/energy |
The VM-3P75CT reads phase current from the 16-bit current registers (0x3041, 0x3049, 0x3051); the VM-3P5A and P1Link from the 32-bit ones (0x3058-0x305D).
Every device also exposes info (product ID, serial number, hardware
version) and power (total and per-phase active power, positive while
importing). Power changes from moment to moment and the rest slowly, so they
can be polled apart: async_update(FAST_SUB_SYSTEMS) reads only power, and
async_update(SLOW_SUB_SYSTEMS) the rest.
On the first poll the device also reads what doesn't change while it runs:
info, unlessasync_identifyalready read it;- the phase configuration, exposed as
connected_phases(for example(1,)for a single-phase installation on L1). A meter that doesn't report it is taken as three-phase; - whether the firmware has the 0.001 Hz frequency register. If it doesn't,
meter.frequency_finestaysNone.
After a firmware update or a wiring change, create a new device object to pick up the change.
Other Victron products with a Modbus interface of their own, such as the EV Charging Station, can be added as further sub-packages - see Adding a device below. Contributions are welcome.
Products that only speak Modbus through a GX device (inverter/chargers, battery monitors, solar chargers, ...) are out of scope: the GX device publishes all of them over MQTT, which is the better interface to read them through.
Data provided by the library
For each supported device, victron-modbus provides typed attributes for
every register it reads, plus neutral datapoint metadata such as unit and
scale. The example uses the tmodbus backend; install it with
pip install victron-modbus[cli] (the library itself only needs
modbus-connection). See the VM-3P75CT example:
from modbus_connection.tmodbus import connect_udp
from victron_modbus.vm_3p75ct import VM3P75CT
connection = await connect_udp("192.168.1.101")
try:
device = VM3P75CT(connection.for_unit(1))
report = await device.async_update() # which sub-systems updated / failed
print(device.info.serial_number, device.power.total, device.phase_1.voltage)
finally:
await connection.close()
A reading the device reports as unavailable is None, for example the
voltage of a phase that isn't connected. Each sub-system that fails to read is
listed in report.failed; check the report before using its values.
The device sets a 1 s request timeout on the unit, unless the connection sets
its own. Modbus/UDP never retransmits a lost datagram, and a meter on Wi-Fi
can lose a good share of them, so a request that times out is repeated, three
attempts in all. That covers async_identify, the reads at setup and every
poll. A device that answers nothing therefore takes three timeouts (3 s) to
give up on. The bound is three timeouts per sub-system that is read: when
several sub-systems each time out in turn, the total is longer, and a caller
that polls them separately (for example fast and slow) gets the bound on each
poll. Refusals and dropped connections are not retried.
When the model is not known up front, async_identify reads the product ID
and returns the matching device object (or raises UnsupportedDeviceError):
from victron_modbus import async_identify
device = await async_identify(unit)
Adding a device
- Create
src/victron_modbus/<device>/withComponentsubclasses grouping its registers by sub-system (seevm_3p75ct/meter.pyandvm_3p75ct/phase.pyfor examples), and a top-level device object indevice.pyfollowing the device-object pattern. A three-phase energy meter only needs its register map: subclass the sharedEnergyMeterfrom_energy_meter.py(seevm_3p75ct/device.py), which already handles polling, partial-failure reporting and raw diagnostics. - Export the device object and its components from
<device>/__init__.py. - Add tests under
tests/using themodbus_connectionmock backend - no physical device or Modbus server required. For an energy meter, test its register map intests/test_<device>.py, and add it toDEVICESintests/test_energy_meter.py. - Add its product ID to
PRODUCTSinvictron_modbus/__init__.py, and the model toDEVICESinscript/query.py. - Add the device to the table above.
Testing and validation
The library ships with a test suite that runs entirely against the in-memory
mock backend provided by modbus-connection. No physical device or Modbus
server is required to run the tests.
Documentation, development and contribution guidelines
- Each
Componentclass documents the registers it reads via attribute docstrings. script/query.py- a standalone CLI that reads a real device once and prints every value; the fastest way to check physical hardware.- Run
script/run_checks.shbefore committing: it checks formatting, lints, type-checks and runs the tests with coverage. - Run
script/format_code.shto format the code.
Pull requests are welcome, including for new device types. Please open an issue first for anything beyond a small fix, so the approach can be discussed before you invest time in it.
License
Apache-2.0. See LICENSE.
Metadata
Release files for victron-modbus 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| victron_modbus-0.2.0.tar.gz | 23.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| victron_modbus-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.4 kB
Release files / victron_modbus-0.2.0.tar.gz
| Download URL | victron_modbus-0.2.0.tar.gz |
|---|---|
| Size | 23.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e71ee5e3fd7336a03f39a6eea5a820efe01578c5a6f1f983eeedb038e8b3f577
|
|
BLAKE2b-256 checksum How to use checksums |
dd52cd025ad3797c35b4aa5afb82a92623f98e405b6b4f538a3b945ca3885156
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / victron_modbus-0.2.0-py3-none-any.whl
| Download URL | victron_modbus-0.2.0-py3-none-any.whl |
|---|---|
| Size | 21.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a59031aa06537940278d3c794ccef3a508f7a48baa11975ce96258e2669a879e
|
|
BLAKE2b-256 checksum How to use checksums |
a65dd611826a335be670eb60ab3e8a5b757b8497953b2273ce7fb5eaefca34e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for victron-modbus 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for victron_modbus-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.