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.
Bulk group-address mapping with the AI assistant
Mapping a floor of KNX group objects by hand is two admin pages per zone — the zone row for address, the attribute editor for dpt — and the bus never tells you when you got it wrong. The AI assistant skill map_knx_group_addresses does the whole list in one call. Paste the ETS group-address export (or a printed GA sheet) into the assistant and ask it to create the zones:
Map these KNX group addresses on the MDT router — 1/2/1 kitchen downlights switch, 1/2/3 kitchen pendants brightness with 1/2/2 as its switch object, 3/1/0 the hallway temperature sensor.
What it enforces that the forms do not:
- Group-address grammar. Every address is parsed with the driver's own bit bounds (main 0-31, middle 0-7, sub 0-255). A 2-level or raw-integer address is rejected up front instead of turning into
invalid group addresson every command later. - Address normalization.
01/02/03is stored as1/2/3. Inbound telegrams are matched numerically but the outbound zone map is keyed by the raw string, so two spellings of one group address behave as two half-working zones. - DPT reality. Only the families the driver encodes (
1.x,3.x,5.x,9.x) are accepted — anything wider would go out as a single raw byte. Zones on a family the driver cannot decode inbound (3.x) are reported as send-only, and9.xzones are reported as read-only through zone verbs. - The dimmer trap.
on/offalways write a 1-bit1.001telegram to the zone's own address, whileset_levelwrites an 8-bit5.001to that same address — but ETS exposes switching and brightness as two different group objects, so a5.001zone silently ignores on/off. Give the entry aswitch_addressand the skill wires the zone'son_action/off_actionoverrides toset_valueagainst the switch object, so both verbs land correctly. - Duplicate addresses. Two zones on one group address are both written by every inbound telegram; the skill rejects duplicates in the batch and against zones already on the device.
Run it with dry_run: true first when the list was pasted — it returns the resolved address, DPT and what each zone will reflect onto, without writing. The whole batch is atomic: created zones are deleted and touched attributes restored if any entry fails. Reloads are zone-only on purpose — reloading the device would drop the tunnel, and tunneling slots are finite.
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. There is no
.knxprojparser — group address inventory starts as a paste. The AI assistant'smap_knx_group_addressesskill turns a pasted group-address list into zones in one call (see above), but nothing reads the ETS project file directly.
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). The device name links straight through to the row on the Devices page; edits happen there and require a driver reload to take effect.