-
Notifications
You must be signed in to change notification settings - Fork 2
Usage and Examples
This page shows how an application installs the library, creates a transport, probes a controller, reads values, checks hydronic and sensor availability, and performs validated writes.
- Python 3.12 or newer
trovis-modbus- a
modbus-connectionbackend suitable for the selected transport
Install the published library:
python -m pip install trovis-modbusFor the repository command-line query tool, install the optional CLI backend:
python -m pip install "trovis-modbus[cli]"For an editable source checkout:
python -m pip install -e .trovis-modbus does not open the transport itself. The application:
- opens a
modbus-connectionconnection, - obtains a
ModbusUnitfor the controller station address, - passes that unit to
Trovis557x, - closes the connection when finished.
Common transport forms are:
| Transport | Typical use | Framer |
|---|---|---|
| RTU over TCP | Transparent serial gateway | rtu |
| Native Modbus TCP | Device or gateway using MBAP framing | socket |
| Serial RTU | Local USB/RS-485 adapter | configured by serial parameters |
The default TROVIS Modbus station address is commonly 246, but the application
must use the address configured on the actual controller.
This example uses the pymodbus backend supplied by modbus-connection.
import asyncio
from modbus_connection.pymodbus import connect_tcp
from trovis_modbus import Trovis557x
async def main() -> None:
connection = await connect_tcp(
"192.168.1.50",
port=502,
framer="rtu",
)
try:
unit = connection.for_unit(246)
probe = await Trovis557x.async_probe(unit)
device = Trovis557x(
unit,
model=probe.model,
detected_sensors=probe.detected_sensors,
)
await device.async_update()
print("Model:", device.model_definition.model.value)
print("System code:", device.info.system_code)
print("Outside temperature:", device.sensors.af1)
print("Controller date:", device.clock.date)
print("System activity:", device.system_activity)
finally:
await connection.close()
asyncio.run(main())Use framer="socket" for native Modbus TCP with MBAP framing.
import asyncio
from modbus_connection.pymodbus import connect_serial
from trovis_modbus import Trovis557x
async def main() -> None:
connection = await connect_serial(
"/dev/ttyUSB0",
baudrate=19200,
parity="N",
stopbits=1,
bytesize=8,
)
try:
unit = connection.for_unit(246)
probe = await Trovis557x.async_probe(unit)
device = Trovis557x(
unit,
model=probe.model,
detected_sensors=probe.detected_sensors,
)
await device.async_update()
print(device.info)
finally:
await connection.close()
asyncio.run(main())Use the serial settings configured on the controller. Supported baud rates and interface options differ by TROVIS model and installed communication module.
The safe probe is the preferred setup sequence:
probe = await Trovis557x.async_probe(unit)The result contains:
probe.model
probe.model_name
probe.detected_sensorsConstruct the full object with the probe result:
device = Trovis557x(
unit,
model=probe.model,
detected_sensors=probe.detected_sensors,
)Do not hard-code a default model in an application when the controller can be probed.
Refresh all active components in one grouped update:
await device.async_update()Examples of normal values:
model = device.model_definition.model
firmware = device.info.firmware_version
system_code = device.info.system_code
outside_temperature = device.sensors.af1
controller_date = device.clock.date
controller_time = device.clock.time
activity = device.system_activityA value can be None when it is unavailable, not active, not installed, or
represented by a TROVIS invalid-value sentinel.
Do not assume that every built-in Rk1-Rk3 slot is a room-heating circuit. Inspect the current hydronic role:
for index in device.control_circuit_indices:
print(index, device.control_circuit_role(index))Room-heating slots are available separately:
for index in device.room_heating_circuit_indices:
circuit = getattr(device, f"rk{index}")
print(index, circuit.flow_setpoint)Optional topology checks:
if device.has_rk4:
print(device.rk4)
if device.has_buffer_tank_circuit:
print(device.buffer_tank)
if device.has_solar:
print(device.solar)The objects remain stable API attributes; the availability properties decide whether they belong to the configured installation.
Detected physical values and logical sensor roles are separate concepts. Applications should normally expose only:
device.available_sensor_keysDiagnostic sets are also available:
device.unresolved_detected_sensor_keys
device.inactive_detected_sensor_keys
device.unsupported_detected_sensorsDetailed resolver information:
resolution = device.sensor_variant_resolution
for result in resolution.variants:
print(
result.variant_sensor_keys,
result.status,
result.selected_sensor_key,
result.reason,
result.evidence,
)An application should not choose between alternative roles merely because one value looks like a plausible temperature.
Writing must be explicitly enabled:
await device.async_enable_writing()
try:
await device.rk1.set_room_setpoint_day(21.5)
finally:
await device.async_disable_writing()A custom controller access code can be supplied:
await device.async_enable_writing(access_code=1234)Generic metadata-driven writes are also available on components:
await component.async_write_datapoint(field, value)The library validates type, range, step, enum options, writeability, and controller-specific preconditions before sending the write.
Use a user-facing write-enable control in applications. Enabling write access in the controller does not by itself define application authorization.
Read the current controller state directly:
is_enabled = await device.async_read_writing_enabled()The local property:
device.writing_enabledtracks whether the current library object enabled writing. Another client may change the real controller state, so use the direct read when that distinction matters.
The repository contains script/query.py for checking a physical controller
without Home Assistant. It reads the complete device once and prints all
available subsystems plus sensor-variant diagnostics.
Install the CLI extra in a source checkout:
python -m pip install -e ".[cli]"RTU over TCP through a transparent gateway:
python script/query.py tcp 192.168.1.50 --unit 246Native Modbus TCP:
python script/query.py tcp 192.168.1.50 --unit 246 --framer socketSerial RTU:
python script/query.py serial /dev/ttyUSB0 --unit 246 --baudrate 19200The query output includes:
- device information,
- controller values,
- date and time,
- functions and parameters,
- measurements,
- Rk1 through Rk4,
- buffer-tank data when applicable,
- solar data when applicable,
- sensor selection and unresolved variants,
- elapsed time and Modbus read count.
Transport errors come from modbus-connection and its selected backend.
Controller validation and write errors come from trovis-modbus.
A normal application should handle both categories:
from modbus_connection import ModbusError
from trovis_modbus import TrovisValueValidationError
try:
await device.async_update()
except ModbusError as err:
print(f"Communication failed: {err}")
try:
await device.rk1.set_room_setpoint_day(100.0)
except TrovisValueValidationError as err:
print(f"Value rejected: {err}")The exact exception classes available to callers are part of the library API and should be preferred over matching error-message text.
The library does not create Home Assistant entities. The separate
trovis-modbus-hass
project owns connection setup, device and entity creation, update coordination,
and Home Assistant presentation behavior.
Disclaimer: Any information on this wiki is informal advice only. It is not supported nor endorsed by Samson, Sauter, YADOS, Pewo or any other equipment maker. There is no warranty expressed or implied: you take sole responsibility for everything you do with your heating controller.