Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Nilan Connect

An event-driven Python client for Nilan ventilation units behind a Nilan gateway, over the gateway's local micro_nabto protocol, built on modbus_event_connect.

You subscribe to the values you care about and are told when one changes - with its quality, so "offline", "no reading" and a real value never look alike.

Supported controllers

Controller Status
Nilan CTS400 Every point checked against Nilan's Modbus manual and read from a unit

Other controllers behind a Nilan or Genvex gateway follow. Until then, connect() raises UnsupportedDeviceError for them.

Installation

pip install nilan-connect

Everything you use comes from nilan_connect: the client and its values, the errors, discovery, and, in nilan_connect.testing, a simulated gateway for your own tests. modbus_event_connect is installed with it; nothing needs importing from it.

Usage

import asyncio
from nilan_connect import PointKey, create_client

def on_change(key, old, new):
    print(f"{key}: {new.value} ({new.quality.name})")

async def main():
    # The email is the account the gateway is paired with in Nilan's app.
    client = create_client("you@example.com", host="<gateway-ip>")
    await client.connect()              # reads the handshake and chooses the controller's model

    client.subscribe(PointKey.TEMP_OUTSIDE, on_change)
    client.subscribe(PointKey.FILTER_OK, on_change)

    while True:                         # you own the clock; the library owns the plan
        await client.poll()             # reads only what is due - free when nothing is
        await asyncio.sleep(1)

asyncio.run(main())

With only a device_id, the gateway is found by discovery. discover() lists the gateways that answer. port reaches a gateway that does not answer on micro_nabto's standard port.

How often values are read

The controller's readings every 10 seconds, its settings every 180 seconds, and a setting again one second after it is written.

Writing

await client.write(PointKey.TEMP_TARGET, 21.5)
await client.write(PointKey.FILTER_REPLACE_RESET, True)   # an action: nothing to read back

A value outside the limits Nilan's manual gives is refused before anything is sent. create_client(..., read_only=True) refuses every write.

Keys

A key is the stable name of a point, and carries the type of its value. A key string never changes, so what a consumer builds on it keeps working.

CTS400 (IR = input register, read as a datapoint; HR = holding register, read and written as a setpoint):

Key Type Register Access Unit Limits
bypass_active bool IR 23 read
fan_dutycycle_extract float IR 24 read %
fan_dutycycle_supply float IR 25 read %
temp_outside float IR 27 read °C
temp_supply float IR 28 read °C
temp_extract float IR 29 read °C
temp_exhaust float IR 30 read °C
humidity float IR 31 read %
humidity_average float IR 46 read %
co2_level int IR 47 read ppm
voc_level int IR 48 read ppm
filter_ok bool IR 49 read
alarm_status bool IR 50 read
alarm_1_code int IR 51 read
alarm_2_code int IR 52 read
alarm_3_code int IR 53 read
alarm_1_info int IR 56 read
alarm_2_info int IR 57 read
alarm_3_info int IR 58 read
fan_level_current int IR 63 read
humidity_high_active bool IR 64 read
humidity_high_level float IR 66 read
humidity_high_level_time float IR 70 read min
winter_mode_active bool IR 72 read
filter_replace_time_ago float IR 77 read d
defrost_active bool IR 91 read
filter_replace_time_remain int IR 110 read d
alarm_reset bool HR 30 write
humidity_low_threshold float HR 31 read, write % 15–45
fan_level_low_humidity int HR 32 read, write 0–3
fan_level_high_humidity int HR 33 read, write 2–4
fan_level_high_humidity_time int HR 34 read, write min 0–180
co2_threshold int HR 35 read, write ppm 500–2000
voc_threshold int HR 36 read, write ppm 500–2000
temp_target float HR 37 read, write °C 10–30
temp_regulation_dead_band float HR 38 read, write °C 0–4
temp_defrost_low_threshold float HR 39 read, write °C 1–5
temp_defrost_high_threshold float HR 40 read, write °C 5–10
defrost_max_time int HR 41 read, write min 5–60
defrost_break_time int HR 43 read, write min 15–760
temp_winter_mode_threshold float HR 45 read, write °C 5–20
filter_replace_interval int HR 50 read, write d 0–360
filter_replace_reset bool HR 51 write
temp_supply_min float HR 57 read, write °C 10–20
temp_supply_max float HR 58 read, write °C 10–50
fan_level1_supply_preset float HR 59 read, write % 20–100
fan_level2_supply_preset float HR 60 read, write % 20–100
fan_level3_supply_preset float HR 61 read, write % 20–100
fan_level4_supply_preset float HR 62 read, write % 20–100
fan_level1_extract_preset float HR 63 read, write % 20–100
fan_level2_extract_preset float HR 64 read, write % 20–100
fan_level3_extract_preset float HR 65 read, write % 20–100
fan_level4_extract_preset float HR 66 read, write % 20–100
fan_level int HR 69 read, write 1–4
enable bool HR 70 read, write
fan_level_high_co2 int HR 80 read, write 2–4

filter_ok is the manual's "filter change status" the other way round, and humidity_high_active its "average level humidity 24 hours OK" the other way round.

Documentation

docs/models holds the manufacturers' manuals for every controller, with every register extracted to CSV.

A note to Nilan

Thank you for publishing the Modbus descriptions of your controllers, and for gateways that answer on their owner's own network. They are what lets an owner fit a Nilan unit into a home the way they want it.

We know a gateway can be updated remotely, and that its local access could be closed. Please keep it open. A local connection needs the email the gateway is paired with in your app, and it reaches the same values your Modbus descriptions give anyone who wires to the controller directly: it adds nothing Modbus does not already allow, and it makes your gateway worth more to the people who own one.

Disclaimer

Not affiliated with Nilan or Genvex. Use at your own risk; no warranty.

License

MIT. See LICENSE.

Metadata

Release files for nilan-connect 0.2.0rc1

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

Source distribution (sdist)

Source distribution for nilan-connect 0.2.0rc1
File Size Uploaded
nilan_connect-0.2.0rc1.tar.gz 11.7 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for nilan-connect 0.2.0rc1
File Interpreter ABI Platform
nilan_connect-0.2.0rc1-py3-none-any.whl Python 3 none any Details

Total release size: 11.7 MB

Release files / nilan_connect-0.2.0rc1.tar.gz

Download URL nilan_connect-0.2.0rc1.tar.gz
Size 11.7 MB
Tags Source
SHA-256 checksum
How to use checksums
3a6363105c90b25ec3e8c7bb55005be05b021619fef568980f13d9a3674dcca9
BLAKE2b-256 checksum
How to use checksums
4c48bc1cc16d816d1775f8705778e0b76428b0bcd4032e13408d48e885cfe3de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 28, 2026.

Transparency log

Release files / nilan_connect-0.2.0rc1-py3-none-any.whl

Download URL nilan_connect-0.2.0rc1-py3-none-any.whl
Size 10.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7b1a3c3efadb4e66ba08e12dbf8c26deed7d344212f3ed8a0e045e929739cac6
BLAKE2b-256 checksum
How to use checksums
ce55db44aee8ac53f0478dd683a919292277c80a04ef5ca50f431623fcd80b36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 28, 2026.

Transparency log
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