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. A blank
usernamealso works there: the driver logs in as the account the box itself flags as its default (the last one used in the FRITZ!Box UI). On older firmware you may only have the shared-password login flow — in that case leaveusernameblank as well. - 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 to use the account the box reports as its default (or the legacy shared-password login on older firmware).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. A box that is unreachable or refuses the login when the device connects is retried automatically: after 30 seconds, then doubling to at most every 15 minutes, until it logs in and polling starts.
- 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. Blank uses the box's default account, or the legacy shared-password login on older firmware. |
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. Values under 5000 are raised to 5000; an unparseable value falls back to 15000. |
request_timeout | no | int | 10000 | Per-request timeout in milliseconds. An unparseable, zero or negative value falls back to 10000 — a request never waits forever. |
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, scaled onto the box's 0–255. |
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. Answers an error if the device list could not be read. |
A command answers an error, and sends nothing, when its address is missing, blank or an unfilled template token such as [address], or when level is missing, blank or not a number. A blank level used to set the radiator to 8 °C or switch the bulb off; it is now refused. A reply from the box other than 2xx (AHA answers 400 to a bad parameter or unknown AIN) is reported as http <status> rather than as success, and a reply that is not a device list at all is reported as reply was not a devicelist rather than as an empty list.
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.
If the box refused the request the command now answers http 400. Otherwise 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 logs in with it on the next poll or command.
"login on hold after a rejected login".
After the box rejects a login, the driver stops sending the same credentials for a while — 30 seconds, doubling to 15 minutes on repeated rejections, and never less than the block time the box itself reports — because every rejected attempt makes the FRITZ!Box block logins for longer. Commands and polls answer this error without contacting the box until the hold ends. Correcting the password (or username) attribute, or reloading the device, lifts the hold at once.
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. With no username configured, the driver uses the account the challenge response's<Users>list flagslast="1"— the box's own default — falling back to the first listed account.
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.