Access TADO API with Python

This guide shall demonstrate how to download log data of all your Tado devices from the undocumented Tado API with a simple python script.

Prerequisites

Tado is a well-known manufacturer of smart thermostats and air conditioning controls. Their devices are connected to a cloud service via a “bridge” device. All recorded data is stored online and the only way to access it is with their official app or web interface. Both of these methods do not currently support log exports, meaning that you cannot store them somewhere else.

Tado does have an API but does not provide any documentation for it. Some blogs out there have managed to tinker with the API enough to give a basic overview of the supported functions. Everything that I will show in this guide is based on an API explanation by “Terence Eden”.

Link: https://shkspr.mobi/blog/2019/02/tado-api-guide-updated-for-2019/

While his guide is not 100% accurate anymore, it did help me a lot with exploring all the possibilities.

I will go over the basic setup that is needed to gain access to the API and will then show how I managed to automate log downloads with a python script.

Please note that I am an absolute beginner with Python. This little experiment was basically my “trial by fire” in understanding Python. You should probably not trust my code even tho “it works on my machine”.

That being said – Enjoy!

Setting up Python packages

You will need to following python packages to work with this code:

  • requests
  • json
  • datetime

Establish a connection

To connect to the Tado API you will need:

  • Username (email address)
  • Password
  • Client Secret
  • A Tado device (to generate logs)

Obtaining the client secret (manually and automated)

In order to obtain the client secret, you need to visit https://app.tado.com/env.js. Under “clientSecret” you can find the current value. At the time of writing, this is the output you would receive.

var TD = {
	config: {
		version: 'v965',
		environment: 'production',
		debugEnabled: false,
		logEndpoint: 'https://ovihbsxusa.execute-api.eu-west-1.amazonaws.com/log/event',
		baseUrl: 'https://my.tado.com',
		tgaEndpoint: 'https://my.tado.com',
		tgaRestApiEndpoint: 'https://my.tado.com/api/v1',
		tgaRestApiV2Endpoint: 'https://my.tado.com/api/v2',
		susiApiEndpoint: 'https://susi.tado.com/api',
		homeBackendBaseUrl: 'https://my.tado.com/home/show',
		hvacApiEndpoint: 'https://hvactool.tado.com',
		hvacIncludeInstallFlowsUnderDevelopment: false,
		genieRestApiV2Endpoint: 'https://genie.tado.com/api/v2',
		ivarRestApiEndpoint: 'https://ivar.tado.com',
		minderRestApiEndpoint: 'https://minder.tado.com/v1',
		gaTrackingId: 'UA-36131363-7',
		oauth: {
			clientApiEndpoint: 'https://my.tado.com/oauth/clients',
			apiEndpoint: 'https://auth.tado.com/oauth',
			clientId: 'tado-web-app',
			clientSecret: 'wZaRN7rpjn3FoNyF5IFuxg9uMzYJcvOoQ8QWiIqS3hfk6gLhVlG57j5YNoZL2Rtc'
		}
	}
};

If you want to automate the retrieval of this key you can use the following function.

import requests

def getSecret():
    r=requests.get("https://app.tado.com/env.js")
    response=str(r.text)
    for item in response.split("\n"):
       if "clientSecret:" in item:
        item = item.strip().split("'")
        return(item[1])

client_secret = getSecret()

This function polls https://app.tado.com/env.js, formats and filters the response and finally returns the client secret. While the client secret does not change too often, automating this could be useful for a long-term project.

getSecret()
> wZaRN7rpjn3FoNyF5IFuxg9uMzYJcvOoQ8QWiIqS3hfk6gLhVlG57j5YNoZL2Rtc

Obtaining the Access Token (Bearer Token)

The bearer token is a special key that will grant you access to your account via API for 600 seconds. After 600 seconds the access token will change and you will need to request a new one.

The following curl request would yield a JSON response containing the current access token:

curl -s "https://auth.tado.com/oauth/token" -d client_id=tado-web-app -d grant_type=password -d scope=home.user -d username="username" -d password="password" -d client_secret="client_secret"

Response:

{"access_token":"XXX","expires_in":599,"scope":"home.user","jti":"abc123"}

This Python code automates the process:

import requests
import json

username = "XXX" # your tado login 
password = "XXX" # your tado password
client_secret = "XXX" # your tado client secret

def getBearer():
    r=requests.post("https://auth.tado.com/oauth/token", data={'client_id' : 'tado-web-app', 'grant_type' : 'password', 'scope' : 'home.user', 'username' : username, 'password' : password, 'client_secret' : client_secret})
    json_data=json.loads(r.text)
    return json_data['access_token']

bearerToken = getBearer()

Obtaining your HomeID

The HomeID is a unique identifier for your home. It is needed to request data from your Tado setup.

The following curl request would yield your HomeID:

curl -s "https://my.tado.com/api/v1/me" -H "Authorization: Bearer bearerToken"

The response should look like this:

{"name":"XXX","email":"XXX","username":"XXX","enabled":true,"id":"XXX","homeId":XXX,"locale":"de","type":"WEB_USER"}

The resulting python code looks like this:

import requests
import json

bearerToken = "xxx"

def getHomeId():
    r=requests.get("https://my.tado.com/api/v1/me", headers={"Authorization" : "Bearer" + bearerToken})
    json_data=json.loads(r.text)
    return str(json_data['homeId'])

homeID = getHomeId()

With all this data you are now ready to request information about your setup.

Understanding Zones

Tado uses “zones” to identify different spaces in your home. This could be a setup like “living room”, “bedroom” and “kitchen”, or a setup where one zone manages heating and the other zone does hot water. This is completely dependent on how you have set up your Tado system.

You can get information about your zones with this CURL request:

curl -s "https://my.tado.com/api/v2/homes/homeId/zones/" -H "Authorization: Bearer bearerToken"

To view your zones with Python you can use the following code:

import requests
import json

homeId = "xxx"
bearerToken = "xxx"

def getZones():
    r=requests.get("https://my.tado.com/api/v2/homes/" + homeId + "/zones", headers={"Authorization" : "Bearer" + bearerToken})
    response = json.loads(r.text)
    for zones in response:
        print(str(zones["id"]) + ": " + zones["name"])

getZones()

Which for me returns:

1: Badezimmer (Bathroom)
2: Schlafzimmer (Bedroom)
3: Kueche (Kitchen)
4: Wohnzimmer (Living Room)
5: Buero (Office)

Getting real data

Now is the time to download raw measurement data. You need:

  • Bearer token
  • Home ID
  • Zone ID
  • Date

Once you have all that ready you can use this CURL command to request data:

curl -s "https://my.tado.com/api/v2/homes/homeId/zones/zoneId/dayReport?date=date" -H "Authorization: Bearer bearerToken"

The date needs to be specified in the following format: 2021-10-22. The response will be in JSON format.

Output:

{'zoneType': 'HEATING', 'interval': {'from': '2021-10-09T21:45:00.000Z', 'to': '2021-10-10T22:15:00.000Z'}, 'hoursInDay': 24, 'measuredData': {'measuringDeviceConnected': {'timeSeriesType': 'dataIntervals', 'valueType': 'boolean', 'dataIntervals': [{'from': '2021-10-09T21:45:00.000Z', 'to': '2021-10-10T22:15:00.000Z', 'value': True}]}, 'insideTemperature': {'timeSeriesType': 'dataPoints', 'valueType': 'temperature', 'min': {'celsius': 18.46, 'fahrenheit': 65.23}, 'max': {'celsius': 23.9, 'fahrenheit': 75.02}, 'dataPoints': [{'timestamp': '2021-10-09T21:45:00.000Z', 'value': {'celsius': 21.88, 'fahrenheit': 71.38}}, {'timestamp': '2021-10-09T22:00:00.000Z', 'value': {'celsius': 21.55, 'fahrenheit': 70.79}}, {'timestamp': '2021-10-09T22:15:00.000Z', 'value': {'celsius': 20.16, 'fahrenheit': 68.29}}, {'timestamp': '2021-10-09T22:30:00.000Z', 'value': {'celsius': 20.14, 'fahrenheit': 68.25}}, {'timestamp': '2021-10-09T22:45:00.000Z', 'value': {'celsius': 20.59, 'fahrenheit': 69.06}}, {'timestamp': '2021-10-09T23:00:00.000Z', 'value': {'celsius': 20.79, 'fahrenheit': 69.42}}, {'timestamp': '2021-10-09T23:15:00.000Z', 'value': {'celsius': 20.89, 'fahrenheit': 69.6}}, {'timestamp': '2021-10-09T23:30:00.000Z', 'value': {'celsius': 20.86, 'fahrenheit': 69.55}}, {'timestamp': '2021-10-09T23:45:00.000Z', 'value': {'celsius': 20.77, 'fahrenheit': 69.39}}, {'timestamp': '2021-10-10T00:00:00.000Z', 'value': {'celsius': 20.68, 'fahrenheit': 69.22}}, {'timestamp': '2021-10-10T00:15:00.000Z', 'value': {'celsius': 20.61, 'fahrenheit': 69.1}}, .........

To do the same with python you can use the following code snippet:

import requests
import json
import datetime

homeId = "xxx"
zoneId = "xxx"
date = datetime.date(xxxx,xx,xx) #2022,10,1 / 2022,10,24
bearerToken = "xxx"

def getHistory():
    r=requests.get("https://my.tado.com/api/v2/homes/" + homeId + "/zones/" + zoneId + "/dayReport?date=" + str(date), headers={"Authorization" : "Bearer" + bearerToken})
    print(json.loads(r.text))
    
getHistory()

The data you will receive is barely humanly readable so you will need to figure out a way to format it to a better state. I have tried the same for a couple of weeks. If you are interested in my approach you can check out my GitHub repo here:

https://github.com/DiddyRise/tado-grabber

BIG DISCLAIMER: This code is by no means perfect and I encourage you to do it yourself – hopefully, better. If you can find some inspiration in my code feel free to use it. Maybe credit me if you publish your work? 🙂

The End

Thank you for reading this article. I hope you have learned something. If you have any questions please feel free to ask. I will try my best to answer.

Cheers,

-Diddy

Leave a comment

Your email address will not be published. Required fields are marked *

Consent Management Platform by Real Cookie Banner