KNXnet/IP
Commercial building-automation driver for the KNX bus, integrated via an IP router or IP interface using the KNXnet/IP Tunnelling protocol (KNX standard volume 3, chapters 03_08_02 and 03_08_04). This is the same protocol that Gira, Jung, ABB i-bus, Siemens GAMMA, Schneider KNX, MDT, and Zennio interfaces expose on the LAN side, and the same protocol ETS uses to talk to the bus.
A live explorer is available at /admin/knx — once a knxnet_ip device is configured, the explorer exposes a Group Address read/write tool, a Bus Monitor that streams the comm log, a Diagnostics tab that surfaces watchdog state, and a read-only Settings summary of the connection attributes.
KNX is dominant in European commercial buildings and increasingly common in high-end residential. Touching it is the difference between "we can quote that project" and "we can't."
KNX is a premium integration. Adding a knxnet_ip device — and opening the KNX explorer — requires the KNX product in your license. Existing devices keep running if a license later changes. See License.
Prerequisites
- A KNX/IP router (e.g., MDT SCN-IP000.03 / SCN-IP100.03, Gira IP Router 2167-00, Weinzierl KNX IP Router 752, Siemens N146/02, ABB IPR/S 3.5.1) or a KNX/IP interface (Weinzierl 731, MDT SCN-IP110.03, ABB IPS/S 3.1.1, Gira 2169-00) on the LAN, already commissioned into the project.
- UDP port 3671 reachable on the LAN — most routers default to this.
- A current ETS project file (or printed group-address list) so you know which group addresses correspond to which lights / shades / HVAC actuators, and what DPT each one expects.
- A free tunneling slot on the router. Most modern routers expose 4-8 simultaneous tunnels; ETS will use one, GEM will use one, leave at least one free for diagnostics.
Setup
- Add the device. In
/admin/devices, create a new device of driver type knxnet_ip and set:ip— the KNX/IP router or interface IPport— 3671 (default)
- Confirm the tunnel opens. Within ~5 seconds the device should show
connectedin/admin/devices. If it doesn't, see Troubleshooting below. - Per addressable KNX object, create a GEM zone. Set:
address— the KNX group address in 3-level notation (e.g.,1/2/3)dpt— the datapoint type that group object expects. The zone editor's DPT field offers the three types that map cleanly onto a GEM zone:1.001— 1-bit boolean (switch). Driveson/off; reflects ontostate.5.001— 8-bit percent (dimmer position, blind position), 0-100. Drivesset_level; reflects ontolevel(andstate).9.001— 2-byte float (°C temperature). Reflects inbound telegrams onto the zone'stemperatureattribute. No zone verb sends a setpoint in this DPT — to write a9.001value, useset_value(see Supported DPTs below).
status_address— (optional) second group address the actuator publishes feedback on. Leave blank if the actuator publishes state back on the same group address as the control input (this is common on residential gear; commercial gear tends to use separate status groups).
- Send commands. From macros, the script console, or the UI:
command({device: id, zone: zoneId, action: 'on'})→ 1-bit GroupValue_Write of1to the zone's group address.command({device: id, zone: zoneId, action: 'off'})→ 1-bit GroupValue_Write of0.command({device: id, zone: zoneId, action: 'set_level', args: {level: 60}})→ 8-bit GroupValue_Write of60%(scaled to 0-255 on the wire).- For anything off the zone surface, use
set_valuedirectly with an explicit group address + DPT — it does not need a zone. The driver encodes the1.x,3.x,5.x, and9.xfamilies (see Supported DPTs below):{action: 'set_value', args: {group: '4/5/6', value: 1, dpt: '1.001'}}→ boolean write.{action: 'set_value', args: {group: '3/1/0', value: 21.5, dpt: '9.001'}}→ 2-byte-float °C setpoint.
read_value({action: 'read_value', args: {group: '4/5/6'}}) sends a GroupValue_Read. The response arrives asynchronously as a bus notification and is reflected onto any zone mapped to that group address (no value is returned synchronously).
Attribute reference
Device
| Attribute | Required | Default | Description |
|---|---|---|---|
ip | yes | — | KNX/IP router or interface IP |
port | no | 3671 | UDP port |
heartbeat_interval | no | 60000 | CONNECTIONSTATE_REQUEST cadence in ms |
connect_timeout | no | 10000 | If no CONNECT_RESPONSE within this many ms, recycle the socket |
op_failure_threshold | no | 5 | Consecutive op failures before the watchdog considers recycling the tunnel. Both this gate AND the staleness gate must clear before a recycle. |
op_staleness_ms | no | 120000 | How long without a successful frame send/receive before the threshold can trigger a recycle. |
Zone
| Attribute | Required | Default | Description |
|---|---|---|---|
address | yes | — | KNX group address, 3-level (e.g., 1/2/3) |
dpt | yes | 1.001 | Datapoint type — see Supported DPTs below |
status_address | no | — | Optional second group address for state feedback |
The driver maintains state (on/off), level (0-100 for percent DPTs), and temperature (°C for float DPTs) on each GEM zone as it sees notifications arrive on the bus.
Zone address format
3-level KNX group address — main/middle/sub where main is 0-31 (5 bits), middle is 0-7 (3 bits), and sub is 0-255 (8 bits). Examples:
0/0/1 1/2/3 5/7/255
2-level (main/sub) and freeform 16-bit notations are not parsed in v1; convert in ETS or by hand before pasting.
Supported DPTs
There are two layers to DPT support, and they cover different things.
Zone DPTs. A zone's dpt attribute decides how on / off / set_level encode their payload and how inbound bus telegrams update the zone. The zone editor offers the three types that map cleanly onto a GEM zone's state / level / temperature:
| Zone DPT | Description | Reflected onto |
|---|---|---|
1.001 | 1-bit switch (on/off) | state (on / off) |
5.001 | 8-bit percent (0-100%) | level (0-100); state set to on when level > 0, else off |
9.001 | 2-byte float, temperature °C | temperature (filled from the bus; no zone verb writes a setpoint back) |
Ad-hoc writes. For anything outside those three, use the set_value command — or the Group Address Tool's Write button in the KNX explorer — which encodes any group address on demand without a zone. The driver builds correct wire payloads for these DPT families:
| Family | Encoded as | Group Address Tool presets |
|---|---|---|
1.x boolean | 1 bit | 1.001 Switch, 1.002 Boolean, 1.009 Open/Close |
3.x dimming control | 4-bit control value, 0-15 (bit 3 = direction, bits 0-2 = step) | 3.007 Dimming Control |
5.x 8-bit unsigned | one byte — 5.001 scales 0-100% → 0-255, other 5.x sent as-is (0-255) | 5.001 %, 5.003 Angle, 5.010 Counter |
9.x 2-byte float | sign + 4-bit exponent + 11-bit mantissa | 9.001 °C, 9.005 Wind m/s, 9.007 Humidity %, 9.020 Voltage mV |
So a heating setpoint on a 9.001 group object is written with set_value ({group: '3/1/0', value: 21.5, dpt: '9.001'}) or by picking 9.001 — Temperature °C in the Group Address Tool and entering the value. The "read from the bus" note above applies only to the zone's temperature attribute — the driver fills it from inbound telegrams and never writes a setpoint from a zone verb — not to the 9.001 DPT itself, which you can write any time via set_value.
DPTs the driver does not yet encode natively — multi-byte unsigned/signed integers (7.x/8.x and wider), time (10.x), date (11.x), 4-byte IEEE float (14.x), and strings (16.x) — fall back to a single raw byte, which is correct only for other 8-bit values. Inbound reflection onto a zone is likewise limited to the 1.x / 5.x / 9.x families: a zone set to any other DPT can send commands but will not track state from the bus.
Known limitations
- Tunneling only — no routing mode. Multicast routing on
224.0.23.12is not implemented. Use a tunneling-capable IP router or IP interface. - One tunnel per device row. Modern routers allow several concurrent tunnels; if you need GEM, ETS, and a Gira HomeServer connected simultaneously, just verify the router has enough tunnel slots and that GEM is consuming exactly one.
- DPT trust. The driver does not negotiate DPTs with the bus; it trusts the
zone.dptattribute. Settingdpt=5.001on a group object that is actually a 1.001 switch will write a malformed payload (8 bits to a 1-bit listener) and confuse the actuator. Matchdptto whatever ETS shows for that group object. - Sequence dedupe. The driver acks every inbound TUNNELING_REQUEST as required by the spec but does not currently dedupe on the receive sequence counter; on a very flaky network you could in theory see a duplicated state notification.
- No discovery (yet). SEARCH_REQUEST on multicast 224.0.23.12 is not implemented. Enter the router IP directly.
- No ETS project import. Group address inventory is manual — paste each group address into a GEM zone. A future enhancement could parse an ETS export.
Troubleshooting
| Symptom | Check |
|---|---|
CONNECT_REQUEST sent logs but no tunnel open | UDP 3671 is being firewalled, OR the router is on a different VLAN with no IGMP forwarding. From the GEM host, nc -uv <router_ip> 3671 and watch for response. |
Tunnel opens, connected=true, but commands have no effect | The zone.dpt doesn't match the actuator's DPT. Open ETS, check the group object's DPT, set zone.dpt accordingly. |
Refused with status E_NO_MORE_CONNECTIONS (0x24) | All tunneling slots on the router are taken. Close another supervisor (ETS, Gira HomeServer, another integrator app), or reboot the router. |
| State notifications never arrive | Confirm the actuator is configured in ETS to send state-update telegrams on writes. Many actuators default to "send on change only" but some have it disabled. Also confirm status_address matches if it differs from the control address. |
| Heartbeat warnings every minute | The router refused the CONNECTIONSTATE_REQUEST. Usually means the tunnel was force-closed by ETS or another tool while GEM was idle. The driver recycles the tunnel after 3 consecutive heartbeat status-errors, or 4 unacknowledged heartbeats. |
| Float temperatures look 100× off | DPT 9.001 has a quirky mantissa/exponent encoding. The driver scales mantissa × 0.01 × 2^exp. If your °C readings look off by 10× or 100× the actuator may be sending a non-standard variant — log the raw bytes and confirm against the actuator datasheet. |
KNX Explorer (/admin/knx)
Live operator surface for the configured knxnet_ip gateway. Five tabs:
- Group Address Tool — read or write a single group address by hand. The driver coerces stringified values per the chosen DPT, so
'false'on DPT 1.001 sends bit 0 (not bit 1) — typos never silently energize a switch. Quick-action buttons (On / Off / 25-100%) cover the common DPT 1.001 + 5.001 cases. - Map to Zone — bind a group address to a GEM zone without leaving the page. Enter the GA, pick the DPT (1.001 switch / 5.001 percent / 9.001 temperature) and optional status GA, then pick or inline-create the zone. The mapper sets the zone's
addressto the GA (that's how the driver routes telegrams), writes thedpt/status_addressattributes, warns with a one-click fix when the zone isn't bound to this gateway, and offers Test Write / Request Read before committing. Existing zones on the gateway are listed below the form. - Bus Monitor — tails the driver's comm log (last 50 frames) with filters for direction (tx / rx / meta) and group address. Recycle and error rows render highlighted. Auto-refreshes every 2 s.
- Diagnostics — tunnel state, heartbeat-pending count, channel ID, watchdog counters (consecutive failures, recycles, last successful op, last client error). The watchdog recycles the tunnel when consecutive op failures cross the threshold AND no successful op has happened in the staleness window; the keepalive also recycles after 3 status-errors or 4 unacked heartbeats.
- Settings — read-only summary of the gateway's current connection attributes (IP, port, heartbeat interval, connect timeout, watchdog threshold/staleness). Edits happen on the Devices page and require a driver reload to take effect.