AVM FRITZ!Box Smart Home
Control and monitor FRITZ!DECT devices paired to an AVM FRITZ!Box router — the router is the DECT ULE base station, and this driver talks the on-router /webservices/homeautoswitch.lua (AHA-HTTP) endpoint to enumerate, poll, and command each paired device.
Supported device families:
- FRITZ!DECT 200 / 210 — smart plug with energy metering (indoor / outdoor).
- FRITZ!DECT 301 / 302 — radiator thermostats.
- FRITZ!DECT 500 — dimmable colour bulb (basic on/off + brightness in this driver; colour temperature and hue follow-ups are noted as follow-up work).
- FRITZ!DECT Repeater 100 with paired sensors — sensor readings surface as read-only attributes.
- Paired HAN-FUN endpoints — third-party DECT ULE devices announce their capabilities via the same
functionbitmaskbitfield and are exposed as far as their AHA schema supports.
FRITZ!Box is one of the most common residential broadband routers in the DACH region (Germany / Austria / Switzerland) and often the only smart-home hub on a residential LAN in that market. This driver lets a GEM install piggy-back on the existing FRITZ!Box hub instead of adding a second DECT bridge.
Prerequisites
- FRITZ!OS 7.0 or later on the FRITZ!Box. Older firmware speaks a different challenge format that this driver does not implement.
- A FRITZ!Box user account with the Smart Home permission enabled. On FRITZ!OS 7.24+ the default admin account works out of the box. On older firmware you may only have the shared-password login flow — in that case leave
usernameblank. - At least one DECT / HAN-FUN device paired to the box (in the FRITZ!Box UI: Home Network → Smart Home → Register new device). The base station itself is not a controllable zone.
- LAN reachability from the GEM host to
fritz.box(or the router's LAN IP). If the FRITZ!Box is behind another edge router that hijacks port 443, use the AHA-native49443port or plain HTTP on49000from an isolated LAN.
Setup
- Add the device. In the admin, create a new device with driver
fritz_box:ip—fritz.boxon a Fritz-managed LAN, otherwise the router LAN IP.password— the account password. Stored encrypted; sent hashed on the wire.username— the account username. Leave blank for the legacy shared-password login.port— leave on443unless the box is on a non-default port.49443is the direct AHA HTTPS port if443is intercepted upstream.protocol—httpsstrongly recommended; only downgrade tohttpon an isolated LAN.
- Confirm the connection state flips to Connected. If it does not, jump to Troubleshooting — it is almost always the Smart Home permission on the user account.
- Enumerate paired devices. From the device detail page, run
get_devices. The response is an array of records like:Copy the{"ain": "116300123456","id": 16,"productname": "FRITZ!DECT 200","name": "Espresso Machine","functionbitmask": 35712,"present": true,"on": true,"power_w": 42.5,"energy_kwh": 1832.45,"voltage_v": 229.4}ain— it is what you will use as the zone address. - Create one zone per AIN. Zone
address = AIN(12 digits, no dashes, no spaces). The zone can live under any subsystem —lightsfor plugs,climatefor thermostats,powerif you only care about metering. - Adjust
status_interval(optional). Defaults to 15000 ms. The FRITZ!Box AHA endpoint is cheap; you can safely run at 5 s if you need fast state feedback on a switch flip.
Attributes
Device attributes
| Name | Required | Type | Default | Description |
|---|---|---|---|---|
ip | yes | string | fritz.box | LAN host of the FRITZ!Box. |
password | yes | string (secure) | — | Account password. Stored encrypted; hashed on the wire. |
username | no | string | — | Account username. Leave blank for legacy shared-password login. |
port | no | int | 443 | 443, 49443, 80, or 49000 (see setup). |
protocol | no | string | https | http or https. |
status_interval | no | int | 15000 | Poll cadence in milliseconds. |
Zone attributes (read-only, populated by the polling loop)
| Name | Type | Description |
|---|---|---|
device_name | string | Name of the paired device as entered in the FRITZ!Box UI. |
product | string | Product code (e.g. FRITZ!DECT 200). |
power_w | float | Instantaneous line power in watts (plugs / meters only). |
energy_kwh | float | Lifetime energy in kWh. |
voltage_v | float | Line voltage in volts. |
temperature_c | float | Ambient temperature in °C (thermostats and repeaters with paired sensors). |
setpoint_c | float | Thermostat target temperature in °C (16.0–28.0 in 0.5 °C steps). |
battery_low | bool | Battery low warning (thermostats). |
state | string | Zone rollup — on, off, or offline (device unresponsive). |
Zone address format
Zone address is a 12-digit AIN (Actor Identification Number) with no dashes and no spaces — e.g. 116300123456. Get the AIN from get_devices or from the FRITZ!Box UI (Home Network → Smart Home → the gear icon on any device).
HAN-FUN endpoints on paired third-party devices use a longer identifier of the form AIN-N where N is the sub-endpoint index. Pass it exactly as reported.
Commands
| Command | Args | What it does |
|---|---|---|
get_devices | — | Enumerate every paired DECT / HAN-FUN device. Use during setup. |
on | address | Switch on a FRITZ!DECT plug / relay by AIN. |
off | address | Switch off a FRITZ!DECT plug / relay by AIN. |
toggle | address | Toggle a FRITZ!DECT plug / relay by AIN. |
set_temperature | address, level | Set thermostat setpoint in °C. Clamped to 8–28 °C, rounded to the nearest 0.5 °C step. |
set_level | address, level | Set dimmable-bulb level 0–100. |
get_state | address | Read on/off / setpoint / power for one AIN, on demand. |
poll_now | — | Force a status poll of every wired-up zone right now. Cadence is unchanged. |
Known limitations
- Colour bulb hue / colour-temperature commands are not yet wired.
set_levelhandles brightness on the FRITZ!DECT 500; hue and colour temperature use separate AHA sub-commands (setcolor,setcolortemperature) that this driver does not implement in this pass. Roadmap item. - Thermostat OFF and BOOST modes are intentionally not exposed via
set_temperature. AVM encodes OFF as the sentinel value 253 and always-ON as 254; the driver refuses those to avoid aset_temperature: 253typo shutting off heat unexpectedly. Use the FRITZ!Box UI for OFF/BOOST or a dedicated command in a follow-up driver revision. - Read-only surface for FRITZ!DECT sensors (temperature-only devices, motion sensors, door/window contacts). The driver reads the values; there is no command to write on a sensor-only device.
- PBKDF2 login is used automatically when the box advertises it. On FRITZ!OS 7.24+ the driver detects the
2$challenge prefix and uses PBKDF2 (per AVM's recommended flow); older firmware falls back to MD5-over-UTF-16LE. - Multiple FRITZ!Box devices on one GEM install — supported. Each FRITZ!Box is its own device row with its own zone set; there is no cross-box coordination.
Troubleshooting
Login always returns SID 0000000000000000.
Wrong username / password, or the account lacks the Smart Home permission. In the FRITZ!Box UI go to System → FRITZ!Box Users → your user and tick "Smart Home". Save and try again.
SSL/TLS errors on port 443.
The box is behind another router or gateway that intercepts 443. Switch port to 49443 (the AHA-native HTTPS port) or, on a fully isolated LAN, use plain HTTP on 49000.
get_devices returns an empty list.
No DECT devices paired with the box, or the user account has restricted Smart Home visibility. Pair a device at fritz.box → Home Network → Smart Home first.
set_temperature reports success but the radiator does not respond.
The AIN was probably wrong or belongs to a different device class (e.g. sending set_temperature to a plug). Run get_devices and confirm the device's functionbitmask includes the thermostat capability (bit 6 = HKR).
Zone attributes stop updating for a period, then resume.
FRITZ!OS is polling its DECT children on its own cadence (~15 s baseline). If the DECT link is momentarily lost, present flips to false and the driver stops writing readings; recovery is automatic when the device rejoins.
"Login rejected (SID 0)" after previously working.
The account password was changed (or the account was reset by the FRITZ!Box "reset all users" upgrade path). Update the password attribute; the driver re-logs in within one poll cycle.
Under the hood — the login flow
FRITZ!OS uses a two-round challenge-response on /login_sid.lua?version=2:
- Get challenge. An unauthenticated GET returns an XML
<SessionInfo>with<Challenge>and current<SID>. On FRITZ!OS 7.24+, the challenge is prefixed2$and encodes a PBKDF2 parameter block; on older firmware it is a plain hex nonce. - Compute response.
- PBKDF2 path (recommended by AVM since 7.24):
2$iter1$salt1$iter2$salt2. Computehash1 = PBKDF2-SHA256(password, salt1, iter1, 32), thenhash2 = PBKDF2-SHA256(hash1, salt2, iter2, 32). Response issalt2$hex(hash2). - MD5 path (pre-7.24): compute
MD5(challenge + "-" + password)where the string is re-encoded UTF-16 little-endian before hashing. Response ischallenge-hex(md5).
- PBKDF2 path (recommended by AVM since 7.24):
- Post response back. GET
/login_sid.lua?version=2&username=X&response=Y. If accepted, the returned<SID>is a 16-hex-character session token; otherwise it is0000000000000000and the SID is rejected.
The SID has a 60 minute idle timeout on the box. The driver refreshes proactively at 45 minutes and lazily on any AHA request that returns HTTP 403 or the sentinel inval body.