Skip to main content

fairland-modbus

An asynchronous Python client for Fairland Group pool equipment over Modbus, built on modbus-connection. The library declares the register layout of each device and decodes it into typed values. The caller owns the connection and hands the library a ModbusUnit, so the same code runs on the pymodbus and tmodbus backends, and inside Home Assistant.

Supported devices

Device Module Status
Inverter pool heat pumps with an MWH216 or MWH298 board (Fairland IPHCR and InverX, and the same board under other brands) fairland_modbus.heat_pump Written from the Fairland register table and tested against a simulator of it. Tested on a Geco GEPAC08 with an MWH298-V3 board. Feedback from other units welcome.

Install

pip install fairland-modbus

The library itself needs no Modbus backend. Install one to talk to a device:

pip install "modbus-connection[tmodbus]"

Heat pumps with an MWH216 or MWH298 board

Which heat pumps have this board

Owners have published working setups on this register table for:

  • Fairland IPHCR45 (spdr870), and an IPHCR on an MWH298 board with super silence;
  • Fairland InverX Vertical;
  • a Madimack 9 kW heat-only unit, a rebadged Fairland;
  • a BWT pool heat pump, with the ESPHome setup of rstcologne.

Not every Fairland heat pump has this board. The PC1004 board (VBIV, Norsup and some Fairland units) answers on another port and with another register map: it fails async_validate() with NotAFairlandDeviceError. An Aquaforte unit with an MWH367 board reads the same addresses, but its owner codes the working mode differently (0 silence, 1 smart, 2 turbo), so that board is not claimed here.

Heat-only units exist: on those, the board keeps heating when asked to cool.

Wiring

The board has a 4-pin RS485 port, the one meant for the optional Wi-Fi module, marked B, A, G (ground) and +12V. Wire A, B and G to an RS485 to Ethernet gateway, or to a USB RS485 adapter. The line runs at 9600 baud, 8 data bits, no parity, 1 stop bit, and the board answers at unit ID 1.

A gateway must be set to translate Modbus TCP to Modbus RTU (on a Waveshare RS485 to ETH, the "Modbus TCP to RTU" protocol).

Usage

import asyncio

from modbus_connection import ModbusTcpParams
from modbus_connection.tmodbus import ModbusConnection

from fairland_modbus.heat_pump import FairlandHeatPump, Function, WorkingMode


async def main() -> None:
    connection = ModbusConnection(ModbusTcpParams(host="192.168.1.200", port=502))
    try:
        heat_pump = FairlandHeatPump(connection.for_unit(1))
        await heat_pump.async_validate()

        report = await heat_pump.async_update()
        print(report.updated, report.failed)
        print(heat_pump.readings.inlet_water_temperature)
        print(heat_pump.settings.function, heat_pump.target_temperature)
        print(heat_pump.status.active_codes)

        await heat_pump.async_set_function(Function.HEATING)
        await heat_pump.async_set_target_temperature(28)
        await heat_pump.async_set_working_mode(WorkingMode.SILENCE)
    finally:
        await connection.close()


asyncio.run(main())

How the board is read

The register table sets two rules, and the board "may return incorrect data" when a read breaks them:

  • a read must start at an address the table allows, and
  • it must cover exactly 48 coils or discrete inputs, or exactly 3 registers.

The table also asks for at least 60 ms between two requests. The library follows all three rules. One poll takes 12 requests: 1 coil block, 2 discrete input blocks, 5 input register blocks and 4 holding register blocks.

What is read and written

Space Addresses Content
Coils 0, 1 Power, forced defrost. Coil 2 (factory reset) is never written.
Discrete inputs 0 to 17 On, defrosting, DIN1 to DIN5 (DIN2 is the remote on/off contact, DIN3 the water flow switch on a GEPAC08), OUT1 to OUT9, malfunction, compressor demand
Discrete inputs 48 to 91 Fault and protection codes E0 to Ed, P0 to PA, F0 to Fb
Input registers 0 to 14 Compressor, PFC voltage, water and refrigerant temperatures, current, expansion valve, fan
Holding registers 0 to 10 Function, working mode, the three setpoints, pump mode and the service parameters P1 to P5

Temperatures come in the table's two encodings: type 1 is raw / 2 - 30, type 2 is raw / 2. Setpoints step by one degree.

The working mode takes 0 (smart), 1 (silence) and 3 (turbo) per the table. Value 2 (super silence) is not in the table, but owners of IPHCR heat pumps use it. Boards do not take every mode. A GEPAC08 takes silence, takes smart only while the compressor runs, and acknowledges turbo and super silence without switching to them. The library reads the mode back, and raises FairlandRefusedError when the board kept its mode or answered the write with an exception.

Errors

The library never leaks a transport exception:

Exception Meaning
FairlandTimeoutError The link is up but the heat pump does not answer, like a gateway answering exception 0B because the pump is off.
FairlandConnectionError The link itself failed.
FairlandRefusedError The board answered and refused the request.
NotAFairlandDeviceError The device answers, but not like this board.
FairlandError The base class, also raised for a value the table does not allow.

A block that fails during async_update() lands in the returned report, so a caller can mark only the values of that block as unavailable.

Development

This is a uv project:

uv sync
uv run pytest          # unit tests on the mock, and end-to-end tests
uv run ruff check .
uv run mypy

tools/simulator.py simulates a heat pump from the register table alone. It serves Modbus TCP or RTU, and refuses any read that breaks the block rules:

uv run python tools/simulator.py --tcp 5020
uv run python tools/simulator.py --tcp 5020 --fault E3
uv run python tools/simulator.py --tcp 5020 --refuse-working-mode
uv run python tools/simulator.py --tcp 5020 --gateway-silent
uv run python tools/simulator.py --tcp 5020 --other-board

Sources

None of these projects carries a license, so no code was taken from them: the module is written from the register table.

License

Apache License 2.0, see LICENSE.

Metadata

Release files for fairland-modbus 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fairland-modbus 0.1.1
File Size Uploaded Provenance
fairland_modbus-0.1.1.tar.gz 20.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for fairland-modbus 0.1.1
File Interpreter ABI Platform
fairland_modbus-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 42.0 kB

Release files / fairland_modbus-0.1.1.tar.gz

Download URL fairland_modbus-0.1.1.tar.gz
Size 20.9 kB
Tags Source
SHA-256 checksum
How to use checksums
cee2bbaabd2498b0919b4c2ff830464a3b257a277e7013ef4931ad4277e181ed
BLAKE2b-256 checksum
How to use checksums
3fd1d53bbc5b0962fd55fc385cba2136d91734569ce095dd6d91ed24638b5e0d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / fairland_modbus-0.1.1-py3-none-any.whl

Download URL fairland_modbus-0.1.1-py3-none-any.whl
Size 21.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
14b89a42b8a7bae93a3a80c1f153b2d5436e9ec811b343dc895074bb252ea502
BLAKE2b-256 checksum
How to use checksums
8eb37ef586e69bc42f416e2fef392c9801c7516491e380d017b6b08cee33a6cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for fairland-modbus 0.1.1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page