tado2mqtt
v0.1.0
Published
Interface between the tado° cloud (heating zones, hot water, home presence) and MQTT
Maintainers
Readme
tado2mqtt
Interface between the tado° cloud and MQTT, following the mqtt-smarthome convention, built on mqtt-interfaces-core.
Heating zones, hot water and the home presence become retained status topics; target temperatures, modes and the presence are set over MQTT; Home Assistant finds every zone through MQTT discovery. The api allows 100 requests a day without Auto-Assist, so the adapter polls once per interval for all zones at once and keeps a budget with a reserve for commands.
Use TadoLocal next to it when you want local control without the cloud: it talks HomeKit to the bridge on the lan. tado2mqtt adds what only the cloud knows: heating power, the schedule, the home presence, the manual-override state.
Install
npm install -g tado2mqtt
tado2mqtt --login # once: approve the device code in a browser
tado2mqtt -u mqtt://broker # run in the foreground
sudo tado2mqtt --install -n tado -u mqtt://broker # systemd service tado2mqtt@tado--login prints a url and a code; open the url, log in with the tado account and approve. The
refresh token goes to <state-dir>/tokens.json and rotates from there. A running instance without
tokens starts the same flow by itself: the url and the code are in the log and retained on
<name>/status/login until approved, with a new code whenever one expires, so an unattended
service (a container) can be logged in from any dashboard that shows the topic.
Docker
Multi-arch image (amd64, arm64, armv7). The login happens while running:
docker run -d --name tado2mqtt --restart unless-stopped -v tado2mqtt:/data \
-e TADO2MQTT_MQTT_URL=mqtt://broker ghcr.io/handijk/tado2mqtt
docker logs tado2mqtt # "login: open https://login.tado.com/oauth2/device?user_code=... and approve"/data is the state directory: tokens.json and budget.json live there. The image runs as
uid 1000; a bind mount must be writable for that user.
Topics
Instance name tado (--name); status payloads are {"val": ..., "ts": ..., "lc": ...} unless
--no-json-payloads.
| topic | retained | value |
| ----------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| tado/connected | yes | 0 gone, 1 broker only, 2 the last poll succeeded |
| tado/status/login | yes | {verification_uri, verification_uri_complete, user_code, expires_at} while a login is pending; cleared after |
| tado/status/home/id, .../home/name | yes | the home |
| tado/status/home/presence | yes | HOME or AWAY |
| tado/status/home/presence_locked | yes | true when set by hand, false under geofencing |
| tado/status/bridge/zones | yes | number of zones |
| tado/status/bridge/quota | yes | {used, limit, reserve, remaining, reset, blocked, api} |
| tado/status/zones/<zone>/temperature | yes | °C |
| tado/status/zones/<zone>/humidity | yes | % |
| tado/status/zones/<zone>/heating_power | yes | % (heating zones) |
| tado/status/zones/<zone>/target_temperature | yes | °C, null when off |
| tado/status/zones/<zone>/power | yes | ON or OFF |
| tado/status/zones/<zone>/mode | yes | auto (schedule), heat (manual override), off |
| tado/status/zones/<zone>/action | yes | heating, idle, off |
| tado/status/zones/<zone>/overlay_active | yes | true while a manual override is on |
| tado/status/zones/<zone>/overlay | yes | {termination: {type, expiry, remainingTimeInSeconds}} or null |
| tado/status/zones/<zone>/open_window | yes | true while open-window detection pauses the zone |
| tado/status/zones/<zone>/open_window_remaining | yes | seconds, or null |
| tado/status/zones/<zone>/link | yes | ONLINE or OFFLINE |
| tado/status/zones/<zone>/next_schedule_change | yes | ISO time or null |
| tado/status/zones/<zone>/tado_mode | yes | HOME or AWAY as the zone sees it |
| tado/status/zones/<zone>/id, .../name, .../type | yes | the zone (HEATING, HOT_WATER, AIR_CONDITIONING) |
<zone> is the zone name as a slug (Woonkamer → woonkamer), or the zone id with
--zone-topics id. Two zones with the same slug get their id appended. A renamed zone moves to
new topics and the old ones are cleared.
Commands
| topic | payload |
| ------------------------------------------ | ------------------------------------------------------------------------- |
| tado/set/zones/<zone>/target_temperature | 21.5 — manual override at that temperature, --termination long |
| tado/set/zones/<zone>/mode | auto back to the schedule, heat override at the current target, off |
| tado/set/zones/<zone>/power | off override with heating off, on back to the schedule |
| tado/set/zones/<zone>/overlay | {"temperature": 21, "power": "ON", "termination": "manual"} |
| tado/set/home/presence | HOME, AWAY or AUTO (geofencing) |
termination is next_time_block (until the schedule changes, the default), manual (until
changed again) or a number of seconds. Every command is followed by a poll, so the status reflects
what tado did, not what was asked.
Home Assistant
Discovery is on by default (--no-ha-discovery turns it off and clears it). The home is the
bridge device with a presence select and the quota sensor; every zone is a device of its own
behind it with a climate entity (modes auto, heat, off; action from the heating power),
humidity, heating power, open window and manual-override sensors. A hot water zone is a switch.
The request budget
tado allows 100 api requests a day per account without Auto-Assist and answers every request with its own counters. The adapter spends them like this:
| what | requests |
| ---------------------------------------------------- | ------------------------------------- |
| the home, once | 1 |
| zone states, every --poll-interval (20 min) | 1 per poll, 72 a day |
| zone list + presence, every --zones-interval (6 h) | 2 per refresh, 8 a day |
| every command | 2 (the command and the poll after it) |
Polling stops when only --budget-reserve requests are left (10), so commands still work; a 429
blocks until the api's window resets. tado/status/bridge/quota shows where the day stands.
Options
Run tado2mqtt --help for the full list; every option is also an environment variable
(TADO2MQTT_POLL_INTERVAL, …), see --config-schema.
| option | default | |
| ------------------ | ------------------------- | ------------------------------------------------------------------------------- |
| --state-dir | $STATE_DIRECTORY or . | tokens, counters |
| --token-file | <state-dir>/tokens.json | |
| --refresh-token | | a refresh token from another tado client, used once when there is no token file |
| --home-id | the first home | |
| --poll-interval | 1200 | seconds between zone state polls |
| --zones-interval | 21600 | seconds between zone list and presence refreshes |
| --daily-limit | 100 | requests a day the budget allows |
| --budget-reserve | 10 | requests kept for commands |
| --termination | next_time_block | manual, next_time_block or seconds |
| --zone-topics | name | name or id |
| --login | | run the login on the terminal and exit |
Shared options: --mqtt-url/-u, --mqtt-username, --mqtt-password, --mqtt-tls-ca, --name/-n,
--json-payloads, --ha-discovery, --ha-prefix, --maintenance, --stats-interval,
--verbosity/-v, --install, --uninstall, --config-schema.
License
MIT
