TADO° thermostat

This guide will be based on importing a ready-made flow and then modify the configuration. This is an advanced flow that aim to demonstrate more than just live input.

Features

  • Query API to determine configuration options.
  • Live updates with dynamic down-sampling.
  • Historic imports of:
    • specific dates
    • time range
    • nightly updates
  • Dynamic down-sampling of live updates.

Data read by flow

  • Zone day report (historic and nightly data):
    • Call for heating (enum)
    • Sun status (enum)
    • Humidity (percentage)
    • Inside temperature (degrees Celsius)
    • Outside temperature (degrees Celsius)
  • Local weather (live data):
    • Outside temperature (degrees Celsius)
    • Sun intensity (percentage)
  • Zone state (live data):
    • Humidity (percentage)
    • Inside temperature (degrees Celsius)
Tado° Smart Radiator Thermostat V3+Tado° Smart Radiator Thermostat V3+

Tado° Smart Radiator Thermostat V3+

Complete flow in Node-REDComplete flow in Node-RED

Complete flow in Node-RED

Prequisite

Before continuing this guide, you need the following.

Physical equipment:

You need to have access to compatible equipment from https://www.tado.com/ that are installed and working. The guide is tested with Tado° Smart Radiator Thermostat V3+.

Credentials:

You need to have the following credentials at hand:

  • The username and password you use for tado.com, or the Tado app.
  • A Clarify credentials.json file. See Setting up an integration.

Node-RED instance:

You need access to a Node-RED instance with Node version 12 or later. See Introduction to Node-RED for installation options. If you are running your own Node-RED instance through Docker, make sure you use one of the tags that include -12, e.g. latest-12. Using a Node-RED instance on IBM Cloud should also work.

Install dependencies

For this flow to work, you need to install the following plugins under "Mange palette" > "Install":

  • node-red-contrib-clarify
  • node-red-contrib-credentials
  • node-red-contrib-tado-client

Be sure to install node-red-contrib-tado-client and not the conflicting node-red-contrib-tado.

The "Manage palette" menu option lets you install plugins.The "Manage palette" menu option lets you install plugins.

The "Manage palette" menu option lets you install plugins.

You should install "node-red-contrib-tado-client".You should install "node-red-contrib-tado-client".

You should install "node-red-contrib-tado-client".

Import flow

Copy the JSON below to your clipboard.

[{"id":"6f9e4912.43c788","type":"tab","label":"Tado","disabled":false,"info":""},{"id":"46090f51.c1783","type":"group","z":"6f9e4912.43c788","name":"collect data","style":{"label":true},"nodes":["232cadf5.8457aa","e2ed6835.9626b","1227dbff.c318cc","79adb764.d9a708","e0b2a5db.04242","2250d47b.1a2eb4","fbc1ab0f.f6a27"],"x":14,"y":739,"w":902,"h":122},{"id":"4a776c34.35efe4","type":"group","z":"6f9e4912.43c788","name":"Trigger data collection","style":{"label":true},"nodes":["dfaa5cb4.cc3ec","db87959f.3f7e","46b92241.906654","d853c6ce.f68cb","7de7f3c5.0abdac","6178be2e.ba6c","81f8d515.73b35","5e165e0b.41e558"],"x":14,"y":459,"w":902,"h":262},{"id":"4b03b400.75c114","type":"group","z":"6f9e4912.43c788","name":"Insert data","style":{"label":true},"nodes":["8dcfa8cd.a619","f63bdca7.df27a8","bb708504.935c7","b7bd6b63.ad6fb8"],"x":14,"y":1019,"w":392,"h":122},{"id":"9c3208e5.89712","type":"group","z":"6f9e4912.43c788","name":"Tado log home info","style":{"stroke":"#92d04f","label":true},"nodes":["78c20171.74b0d8","b4bb3d14.d58ca8","f4eb03fb.e1dbd8","d6f26380.77b98","c68681a7.b078c8","de04f615.6ba0c8","d4a86e44.8ec4b8"],"x":14,"y":179,"w":892,"h":162},{"id":"aefe6eea.38e218","type":"group","z":"6f9e4912.43c788","name":"Configure data colleciton flow","style":{"label":true},"nodes":["297a0746.2f885","cdd4163a.dbd2c","ed3c06f6.f79f78"],"x":14,"y":359,"w":552,"h":82},{"id":"b167ce07.c4e0e","type":"group","z":"6f9e4912.43c788","name":"Trigger log home info","style":{"stroke":"#92d04f","label":true},"nodes":["b50b6b64.c55eb8","9059a878.95517"],"x":14,"y":79,"w":282,"h":82},{"id":"d93c633f.b27eb","type":"group","z":"6f9e4912.43c788","name":"Dynmic down sample (live data)","style":{"label":true},"nodes":["f02a766f.6219e","19e936a7.0ac509","7ffaeed1.c486b","2684b974.9d3ea6","49f4dc8.8393124","aaae165e.1d6a78","efa5ae6c.196b7"],"x":14,"y":879,"w":902,"h":122},{"id":"8dcfa8cd.a619","type":"debug","z":"6f9e4912.43c788","g":"4b03b400.75c114","name":"requests","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":300,"y":1060,"wires":[]},{"id":"f63bdca7.df27a8","type":"debug","z":"6f9e4912.43c788","g":"4b03b400.75c114","name":"error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":290,"y":1100,"wires":[]},{"id":"232cadf5.8457aa","type":"link in","z":"6f9e4912.43c788","g":"46090f51.c1783","name":"collect data config","links":["db87959f.3f7e"],"x":55,"y":800,"wires":[["e2ed6835.9626b"]]},{"id":"e2ed6835.9626b","type":"delay","z":"6f9e4912.43c788","g":"46090f51.c1783","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"20","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":260,"y":800,"wires":[["79adb764.d9a708"]]},{"id":"bb708504.935c7","type":"clarify_insert","z":"6f9e4912.43c788","g":"4b03b400.75c114","name":"","apiRef":"9ba5bda2.221a88","bufferTime":"5","alwaysSaveMetadata":false,"signalId":"topic","signalName":"signal.name","signalDataType":"signal.type","signalDescription":"signal.description","signalLabels":"signal.labels","signalAnnotations":"signal.annotations","signalEngUnit":"signal.engUnit","signalEnumValues":"signal.enumValues","signalSourceType":"signal.sourceType","signalSampleInterval":"signal.sampleInterval","signalGapDetection":"signal.gapDetection","signalDataTimes":"payload.times","signalDataSeries":"payload.values","x":150,"y":1080,"wires":[["8dcfa8cd.a619"],["f63bdca7.df27a8"]]},{"id":"b50b6b64.c55eb8","type":"inject","z":"6f9e4912.43c788","g":"b167ce07.c4e0e","name":"log home info","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"getMe","x":130,"y":120,"wires":[["9059a878.95517"]]},{"id":"78c20171.74b0d8","type":"debug","z":"6f9e4912.43c788","g":"9c3208e5.89712","name":"zoneId","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":810,"y":220,"wires":[]},{"id":"f4eb03fb.e1dbd8","type":"function","z":"6f9e4912.43c788","g":"9c3208e5.89712","name":"get homeId","func":"msg.payload.homes.forEach(function(home){\n    node.send([\n        {\n            homeId: home.id,\n        },\n        {\n            payload: `${home.name}: ${home.id}`,\n        },\n    ]);\n});\n\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":260,"wires":[["d6f26380.77b98"],["c68681a7.b078c8"]]},{"id":"c68681a7.b078c8","type":"debug","z":"6f9e4912.43c788","g":"9c3208e5.89712","name":"homeId","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":480,"y":300,"wires":[]},{"id":"de04f615.6ba0c8","type":"function","z":"6f9e4912.43c788","g":"9c3208e5.89712","name":"get zoneId","func":"msg.payload.forEach(function(zone){\n    node.send({\n        payload: `${zone.name}: ${zone.id}`,\n    })\n});\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":630,"y":220,"wires":[["78c20171.74b0d8"]]},{"id":"a79f84c8.ad5b78","type":"comment","z":"6f9e4912.43c788","name":"Help","info":"# Usage\n\nTo configure this flow, you must do the following.\n\n## 1: Install dependencies\n\nThe following plugins must be added to your palette:\n\n- node-red-contrib-clarify\n- node-red-contrib-tado-client\n- node-red-contrib-credentials\n\n## 2: Configure credenitals\n\n- All Tado nodes\n- Clarify insert node\n\n\n## 3: Condfigure home info\n\n- Deploy and trigger \"log home info\".\n- Set `msg.homeId` an `msg.zoneId` in the \"home info\" node.\n\n\n### 4: Import historical data\n\n- Configure \"Date range\" or \"Dates\".\n- Deploy and trigger the flow.\n\nTIP: It's recommended to start with a _single date_ to check that things work.\n\n### 5: Enable recurring flows (optional)\n\nIf you want to continously update Clarify with new data, you can enable one or more of the recurring flows:\n\n- Nightly: report to same signals as historic data import.\n- Zone state: report to separate signals.\n- Weather: report to separate signals.\n","x":70,"y":40,"wires":[]},{"id":"d4a86e44.8ec4b8","type":"link in","z":"6f9e4912.43c788","g":"9c3208e5.89712","name":"log home info input","links":["9059a878.95517"],"x":55,"y":260,"wires":[["b4bb3d14.d58ca8"]]},{"id":"9059a878.95517","type":"link out","z":"6f9e4912.43c788","g":"b167ce07.c4e0e","name":"to log home info","links":["d4a86e44.8ec4b8"],"x":255,"y":120,"wires":[]},{"id":"b4bb3d14.d58ca8","type":"tado","z":"6f9e4912.43c788","g":"9c3208e5.89712","configName":"5d86a70.fc3e658","apiCall":"getMe","homeId":"169771","deviceId":"","zoneId":"","power":"on","temperature":"18","terminationType":"manual","terminationTimeout":900,"name":"","reportDate":"","presence":"HOME","geoTracking":true,"temperatureOffset":0,"windowDetection":true,"windowDetectionTimeout":900,"openWindowMode":true,"timetableId":"","x":150,"y":260,"wires":[["f4eb03fb.e1dbd8"]]},{"id":"d6f26380.77b98","type":"tado","z":"6f9e4912.43c788","g":"9c3208e5.89712","configName":"5d86a70.fc3e658","apiCall":"getZones","homeId":"","deviceId":"","zoneId":"","power":"on","temperature":"18","terminationType":"manual","terminationTimeout":900,"name":"","reportDate":"","presence":"HOME","geoTracking":true,"temperatureOffset":0,"windowDetection":true,"windowDetectionTimeout":900,"openWindowMode":true,"timetableId":"","x":470,"y":220,"wires":[["de04f615.6ba0c8"]]},{"id":"d853c6ce.f68cb","type":"inject","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"zone report: Nightly","props":[{"p":"apiCall","v":"getZoneDayReport","vt":"str"},{"p":"time","v":"","vt":"date"}],"repeat":"","crontab":"00 03 * * *","once":false,"onceDelay":0.1,"topic":"","x":160,"y":600,"wires":[["5e165e0b.41e558"]]},{"id":"dfaa5cb4.cc3ec","type":"inject","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"zone report: Date range","props":[{"p":"apiCall","v":"getZoneDayReport","vt":"str"},{"p":"fromDate","v":"2019-05-03","vt":"str"},{"p":"toDate","v":"2021-05-02","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":160,"y":500,"wires":[["5e165e0b.41e558"]]},{"id":"db87959f.3f7e","type":"link out","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"to collect data","links":["232cadf5.8457aa"],"x":875,"y":580,"wires":[]},{"id":"46b92241.906654","type":"inject","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"zone report: Dates","props":[{"p":"apiCall","v":"getZoneDayReport","vt":"str"},{"p":"dates","v":"[\"2019-05-11\",\"2019-05-12\"]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170,"y":540,"wires":[["5e165e0b.41e558"]]},{"id":"7de7f3c5.0abdac","type":"inject","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"zone state: 15 min","props":[{"p":"apiCall","v":"getZoneState","vt":"str"},{"p":"time","v":"","vt":"date"}],"repeat":"900","crontab":"","once":true,"onceDelay":"1","topic":"","x":170,"y":640,"wires":[["5e165e0b.41e558"]]},{"id":"6178be2e.ba6c","type":"inject","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"weather: 15 min","props":[{"p":"apiCall","v":"getWeather","vt":"str"},{"p":"time","v":"","vt":"date"}],"repeat":"900","crontab":"","once":true,"onceDelay":"1","topic":"","x":170,"y":680,"wires":[["5e165e0b.41e558"]]},{"id":"1227dbff.c318cc","type":"function","z":"6f9e4912.43c788","g":"46090f51.c1783","name":"To Clarify input","func":"function getPath(item, path) {\n    let target = item;\n    (path || []).forEach(function(key) {\n        target = target[key];    \n    })\n    \n    return target;\n}\n\nfunction parseData(source, valueConf) {\n    switch (source.timeSeriesType) {\n    case \"dataPoints\":\n        return parseDataPoints(source.dataPoints, valueConf);\n    case \"dataIntervals\":\n        return parseDataIntervals(source.dataIntervals, valueConf);\n    }\n    if (source.type) {\n        return parseDataState(source, valueConf);\n    }\n    \n    throw new Error(`Unsupported source timeSeriesType:'${source.timeSeriesType}, type:${source.type}'`);\n}\n\nfunction parseDataIntervals(dataIntervals, valueConf) {\n    let gapFillHours = (valueConf || {}).gapFillHours || 12;\n    \n    const times = [];\n    const values = [];\n    \n    dataIntervals.forEach(function(point) {\n        let v = getValue(point.value, valueConf);\n        \n        // Add duplicate values from the interval period\n        // to prevent gap deteciton.\n        let time = new Date(point.from);\n        let end = new Date(point.to)\n        while (time < end) {\n            times.push(time.toISOString());\n            values.push(v);\n\n            time.setHours(time.getHours()+gapFillHours);\n        }\n    });\n    return {\n        times: times,\n        values: values,\n    };\n}\n\nfunction parseDataPoints(dataPoints, valueConf) {\n    const times = [];\n    const values = [];\n    \n    dataPoints.forEach(function(point) {\n        let v = getValue(point.value, valueConf)\n        \n        times.push(point.timestamp);\n        values.push(v);\n    });\n    return {\n        times: times,\n        values: values,\n    };\n}\n\nfunction parseDataState(state, valueConf) {\n    return {\n        times: [state.timestamp],\n        values: [getValue(state, valueConf)],\n    };\n}\n\n\nfunction getValue(source, valueConf) {\n    let conf = valueConf || {};\n    let toEnum = conf.toEnum || null;\n    let v = getPath(source, conf.path);\n    \n    // Convert string value to numeric value.\n    if (toEnum != null) {\n        v = toEnum[String(v)];\n    }\n    \n    // Apply offsett and multiplier.\n    let o = valueConf.offset || 0;\n    let m = valueConf.multiplier || 1;\n    v = o+v*m;\n\n    return v;\n}\n\n\n// Get sourcePaths based on requested apiCall.\nlet sourcePaths;\nlet filterEqualValue;\nswitch (msg.topic) {\n    case \"getZoneDayReport\":\n        filterEqualValue = false;\n        sourcePaths = flow.get(\"zoneDayReportPaths\");\n        break;\n    case \"getZoneState\":\n        filterEqualValue = true;\n         sourcePaths = flow.get(\"zoneStatePaths\");\n        break;\n    case \"getWeather\":\n        filterEqualValue = true;\n         sourcePaths = flow.get(\"weatherPaths\");\n        break;\n    default:\n        node.error(`unsuported topic '${msg.topic}'`);\n        return;\n}\n\n// Proccess messages.\nsourcePaths.forEach(function(cfg, index) {\n    let source = getPath(msg.payload, cfg.path);\n    if (source) {\n        node.send({\n            filterEqualValue: filterEqualValue,\n            topic: cfg.topic,\n            signal: cfg.signal,\n            payload: parseData(source, cfg.value),\n        });\n    }\n});\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":800,"wires":[["e0b2a5db.04242"]]},{"id":"79adb764.d9a708","type":"tado","z":"6f9e4912.43c788","g":"46090f51.c1783","configName":"5d86a70.fc3e658","apiCall":"getMe","homeId":"","deviceId":"","zoneId":"","power":"on","temperature":"18","terminationType":"manual","terminationTimeout":900,"name":"","reportDate":"","presence":"HOME","geoTracking":true,"temperatureOffset":0,"windowDetection":true,"windowDetectionTimeout":900,"openWindowMode":true,"timetableId":"","x":430,"y":800,"wires":[["1227dbff.c318cc"]]},{"id":"e0b2a5db.04242","type":"switch","z":"6f9e4912.43c788","g":"46090f51.c1783","name":"","property":"filterEqualValue","propertyType":"msg","rules":[{"t":"true"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":770,"y":800,"wires":[["2250d47b.1a2eb4"],["fbc1ab0f.f6a27"]]},{"id":"2250d47b.1a2eb4","type":"link out","z":"6f9e4912.43c788","g":"46090f51.c1783","name":"to filter","links":["7ffaeed1.c486b"],"x":875,"y":780,"wires":[]},{"id":"fbc1ab0f.f6a27","type":"link out","z":"6f9e4912.43c788","g":"46090f51.c1783","name":"to insert","links":["b7bd6b63.ad6fb8"],"x":875,"y":820,"wires":[]},{"id":"def59d5b.0151b8","type":"link in","z":"6f9e4912.43c788","name":"","links":[],"x":-15,"y":860,"wires":[[]]},{"id":"b7bd6b63.ad6fb8","type":"link in","z":"6f9e4912.43c788","g":"4b03b400.75c114","name":"insert input","links":["2684b974.9d3ea6","fbc1ab0f.f6a27"],"x":55,"y":1080,"wires":[["bb708504.935c7"]]},{"id":"f02a766f.6219e","type":"rbe","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"value change >= 0.1","func":"deadbandEq","gap":"0.1","start":"","inout":"out","septopics":true,"property":"payload.value","x":360,"y":920,"wires":[["19e936a7.0ac509"]]},{"id":"19e936a7.0ac509","type":"rbe","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"block equal time","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload.time","x":580,"y":940,"wires":[["aaae165e.1d6a78"]]},{"id":"7ffaeed1.c486b","type":"link in","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"filter input","links":["2250d47b.1a2eb4"],"x":55,"y":940,"wires":[["49f4dc8.8393124"]]},{"id":"2684b974.9d3ea6","type":"link out","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"to insert","links":["b7bd6b63.ad6fb8"],"x":875,"y":940,"wires":[]},{"id":"81f8d515.73b35","type":"function","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"to apiCalls","func":"// Send one or more api calls to collet messages.\n\n// Handle current time.\nif (msg.time) {\n    let time = new Date(msg.time);\n    \n    // In the case of zoneDayReport triggered by\n    // a timestamp, import data from the day before.\n    // For other requests, the reportDate parameter\n    // is ignored.\n    time.setHours(time.getHours()-24);\n    \n    node.send({\n        apiCall: msg.apiCall,\n        homeId:  msg.homeId,\n        zoneId:  msg.zoneId,\n        // reportDate format is YYYY-MM-DD.\n        reportDate: time.toISOString().slice(0, 10),\n    });\n}\n\n// Handle explicit dates list.\nif (msg.dates) {\n    msg.dates.forEach(function(date){\n        node.send({\n            apiCall: msg.apiCall,\n            homeId: homeId,\n            zoneId: zoneId,\n            reportDate: date,\n        });\n    });\n}\n\n// Handle date range.\nif (msg.fromDate) {\n    const toTime = new Date(msg.toDate);\n    \n    let time = new Date(msg.fromDate);\n    while (time < toTime) {\n        node.send({\n            apiCall: msg.apiCall,\n            homeId:  msg.homeId,\n            zoneId:  msg.zoneId,\n            // reportDate format is YYYY-MM-DD.\n            reportDate: time.toISOString().slice(0, 10),\n        });\n        time.setHours(time.getHours()+24);\n    }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":580,"wires":[["db87959f.3f7e"]]},{"id":"49f4dc8.8393124","type":"function","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"to points","func":"msg.payload.times.forEach(function(time, index) {\n    node.send({\n        topic: msg.topic,\n        payload: {\n            time: (new Date(time)).getTime(),\n            value: msg.payload.values[index],\n        },\n        signal: msg.signal,\n    })\n});","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":160,"y":940,"wires":[["f02a766f.6219e","efa5ae6c.196b7"]]},{"id":"aaae165e.1d6a78","type":"function","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"to series","func":"\nreturn {\n    topic: msg.topic,\n    payload: {\n        times: [(new Date(msg.payload.time)).toISOString()],\n        values: [msg.payload.value],\n    },\n    signal: msg.signal,\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":940,"wires":[["2684b974.9d3ea6"]]},{"id":"cdd4163a.dbd2c","type":"inject","z":"6f9e4912.43c788","g":"aefe6eea.38e218","name":"flow config","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":400,"wires":[["297a0746.2f885"]]},{"id":"297a0746.2f885","type":"credentials","z":"6f9e4912.43c788","g":"aefe6eea.38e218","name":"home info","props":[{"value":"homeId","type":"msg"},{"value":"zoneId","type":"msg"}],"x":300,"y":400,"wires":[["ed3c06f6.f79f78"]]},{"id":"ed3c06f6.f79f78","type":"function","z":"6f9e4912.43c788","g":"aefe6eea.38e218","name":"flow config","func":"// Code added here will be run once\n// whenever the node is started.\nconst homeId = msg.homeId;\nconst zoneId = msg.zoneId;\n\nflow.set(\"homeId\", homeId);\nflow.set(\"zoneId\", zoneId);\n\n// FIXME: insert node annotation support is broken.\nconst commonAnnotations = {\n    \"tado-com/collection-method\": `${msg.topic}`,\n    \"tado-com/home-id\": `${homeId}`,\n    \"tado-com/zone-id\": `${zoneId}`,\n}\n\nconst zoneLabels = {\n    \"data-source\": [\"Tado°\"],\n    \"location\": [\"My Home\", \"Living Room\"],\n};\nconst homeLabels = {\n    \"data-source\": [\"Tado°\"],\n    \"location\": [\"My Home\"],\n};\n\n// zoneDayReportPaths lists a series of paths to extract\n// information from from getZoneDayReport responses.\nconst zoneDayReportPaths = [\n    {\n        path: [\"measuredData\", \"insideTemperature\"],\n        value: {\n            path: [\"celsius\"],\n        },\n        topic: `${homeId}-${zoneId}-zr-temp`,\n        signal: {\n            name: \"Inside temperature\",\n            type: \"numeric\",\n            engUnit: \"°C\",\n            gapDetection: \"PT1H\",\n            labels: zoneLabels,\n        },\n    },\n    {\n        path: [\"measuredData\", \"humidity\"],\n        value: {\n            multiplier: 100,\n        },\n        topic: `${homeId}-${zoneId}-zr-humidity`,\n        signal: {\n            name: \"Relative humidity\",\n            type: \"numeric\",\n            engUnit: \"%\",\n            gapDetection: \"PT1H\",\n             labels: zoneLabels,\n        },\n    },\n    {\n        path: [\"weather\", \"condition\"],\n        value: {\n            path: [\"temperature\", \"celsius\"],\n            gapFillHours: 4,\n        },\n        topic: `${homeId}-r-out-temp`,\n        signal: {\n            name: \"Outside temperature\",\n            engUnit: \"°C\",\n            gapDetection: \"PT4H1S\",\n            sourceType: \"aggregation\",\n            labels: homeLabels,\n        },\n    },\n    {\n        path: [\"callForHeat\"],\n        value: {\n            toEnum: {\n              \"NONE\": 0,\n              \"LOW\": 2,\n              \"MEDIUM\": 3,\n              \"HIGH\": 4,\n            },\n            gapFillHours: 6,\n        },\n        topic: `${homeId}-${zoneId}-zr-heat-call`,\n        signal: {\n            name: \"Call for heating\",\n            type: \"enum\",\n            sourceType: \"aggregation\",\n            enumValues: {\n                \"0\": \"Off\",\n                \"2\": \"Low\",\n                \"3\": \"Medium\",\n                \"4\": \"High\",\n            },\n            gapDetection: \"PT6H1S\",\n            labels: zoneLabels,\n        },\n    },\n    {\n        path: [\"weather\", \"sunny\"],\n        value: {\n            toEnum: {\n              \"false\": 0,\n              \"true\": 1,\n            },\n            gapFillHours: 12,\n        },\n        topic:`${homeId}-zr-sun`,\n        signal: {\n            name: \"Sun\",\n            gapDetection: \"PT12H1S\",\n            sourceType: \"aggregation\",\n            type: \"enum\",\n            enumValues: {\n                \"0\": \"Not sunny\",\n                \"1\": \"Sunny\",\n            },\n            labels: homeLabels,\n        },\n    },\n];\n\n// zoneDayReportPaths lists a series of paths to extract\n// information from from getZoneSate responses.\nconst zoneStatePaths = [\n      {\n        path: [\"activityDataPoints\", \"heatingPower\"],\n        value: {\n            path: [\"percentage\"],\n        },\n        topic: `${homeId}-${zoneId}-zs-heat-pw`,\n        signal: {\n            name: \"Heating power (live)\",\n            engUnit: \"%\",\n            samplingInterval: \"PT15M\",\n            gapDetection: \"PT1H10M\",\n            labels: zoneLabels,\n        },\n    },\n    {\n        path: [\"sensorDataPoints\", \"insideTemperature\"],\n        value: {\n            path: [\"celsius\"],\n        },\n        topic: `${homeId}-${zoneId}-zs-temp`,\n        signal: {\n            name: \"Inside temperature (live)\",\n            engUnit: \"%\",\n            samplingInterval: \"PT15M\",\n            gapDetection: \"PT1H10M\",\n            labels: zoneLabels,\n        },\n    },\n    {\n        path: [\"sensorDataPoints\", \"humidity\"],\n        value: {\n            path: [\"percentage\"],\n        },\n        topic: `${homeId}-${zoneId}-zs-humidity`,\n        signal: {\n            name: \"Relative humidity (live)\",\n            engUnit: \"%\",\n            samplingInterval: \"PT15M\",\n            gapDetection: \"PT1H10M\",\n            labels: zoneLabels,\n        },\n    },\n];\n\n// zoneDayReportPaths lists a series of paths to extract\n// information from from getZoneSate responses.\nconst weatherPaths = [\n      {\n        path: [\"outsideTemperature\"],\n        value: {\n            path: [\"celsius\"],\n        },\n        topic: `${homeId}-w-out-temp`,\n        signal: {\n            name: \"Outside temperature (live)\",\n            engUnit: \"°C\",\n            samplingInterval: \"PT1H\",\n            gapDetection: \"PT4H\",\n            sourceType: \"prediction\",\n            labels: homeLabels,\n        },\n    },\n    {\n        path: [\"solarIntensity\"],\n        value: {\n            path: [\"percentage\"],\n        },\n        topic: `${homeId}-w-sun`,\n        signal: {\n            name: \"Solar intensity (live)\",\n            engUnit: \"%\",\n            samplingInterval: \"PT1H\",\n            gapDetection: \"PT4H\",\n            sourceType: \"prediction\",\n            labels: homeLabels,\n        },\n    },\n];\n\nflow.set(\"zoneDayReportPaths\", zoneDayReportPaths);\nflow.set(\"zoneStatePaths\", zoneStatePaths);\nflow.set(\"weatherPaths\", weatherPaths);","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":400,"wires":[]},{"id":"5e165e0b.41e558","type":"change","z":"6f9e4912.43c788","g":"4a776c34.35efe4","name":"Set home info","rules":[{"t":"set","p":"homeId","pt":"msg","to":"homeId","tot":"flow"},{"t":"set","p":"zoneId","pt":"msg","to":"zoneId","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":580,"wires":[["81f8d515.73b35"]]},{"id":"efa5ae6c.196b7","type":"rbe","z":"6f9e4912.43c788","g":"d93c633f.b27eb","name":"time change >= 1h","func":"deadbandEq","gap":"3600000","start":"","inout":"out","septopics":true,"property":"payload.time","x":350,"y":960,"wires":[["19e936a7.0ac509"]]},{"id":"9ba5bda2.221a88","type":"clarify_api","name":"Node-RED"},{"id":"5d86a70.fc3e658","type":"tado-config","name":"Tado"}]

Select Import in node RED, and paste the content there.

Select Import from the menu.Select Import from the menu.

Select Import from the menu.

Paste content into the pink box.Paste content into the pink box.

Paste content into the pink box.

Configure credentials

Before you can use the new flow, you need to configure credentials for all the Tado and Clarify nodes. Double-click each of the nodes to configure relevant credentials.

Note that once you have configured the first Tado node, you should configure all the other nodes to use the same credentials. By default, we have provided a global configuration node for Clarify and Tado that you can configure with the correct credentials. You can choose to keep these nodes, rename them, or configure your own.

Double click the unconfigured nodes to complete the configurationDouble click the unconfigured nodes to complete the configuration

Double click the unconfigured nodes to complete the configuration

If you want to reuse our imported config nodes, you must modify them to set correct credentials.If you want to reuse our imported config nodes, you must modify them to set correct credentials.

If you want to reuse our imported config nodes, you must modify them to set correct credentials.

Specify your Tado credentials.Specify your Tado credentials.

Specify your Tado credentials.

Configure Home Info

The next thing we should do is to get the homeId and zoneId to use for our data import. To make these values easy to find, we provide a helper flow (in green) that you can run to extract the relevant information.

Trigger the log home info flow. If you only have one home and one zone, then you can get the homeId and zoneId to use from looking at the node status as shown below. If you however have multiple homes or multiple zones, you should check the debug log to determine which information you want to use.

You can get your home information by running the "log home info" flow.You can get your home information by running the "log home info" flow.

You can get your home information by running the "log home info" flow.

If you have multiple homes, or just want place where you can copy the ID, you can have a look at the debug log.If you have multiple homes, or just want place where you can copy the ID, you can have a look at the debug log.

If you have multiple homes, or just want place where you can copy the ID, you can have a look at the debug log.

You should configure the "home info" block to contain your `homeId` and `zoneId`You should configure the "home info" block to contain your `homeId` and `zoneId`

You should configure the "home info" block to contain your homeId and zoneId

Configure your `homeId` and `zoneId` in the input boxes.Configure your `homeId` and `zoneId` in the input boxes.

Configure your homeId and zoneId in the input boxes.

📘

Multi zone imports?

The imported flow is assuming that you only want to import data from a single homeId and single zoneId. If you want to import data from multiple zones or homes at once in the same flow, you need to find a way to modify the "flow config" and/or "home info" nodes.

Hint: You should think about which zones to log for each home, and if you want to log the same information (including weather data) for each of these zones.

Test live data flows

Deploy your system and test that your live data import is working. The recurring nodes will trigger after 1 second. The Clarify node will group incoming data for another 5 seconds, before sending the data to Clarify.

Once your flow has completed, you should go to your integration in Clarify, and check that signals are have been created. You can now publish them, and drag them into a (new) timeline. Did you receive any data?

Import historic data

👍

Test with one date first

When doing historic imports, test with one date first. This allows you to test if the flow is correctly configured, and works for your device configuration.

Now you should be ready to trigger a historic import of your data. I would recommend testing with one date before importing a longer data period. To do this, you can first edit the Manual dates block to contain a date when your equipment was installed and active.

Hit Deploy, and manually trigger the Manual dates flow. If this works well, you can move on to configuring Date range, Deploy again, and manually trigger it. If everything went well, you should now see your historic Tado data in Clarify.


Disclaimer
By using our guides you agree to the following disclaimer. In addition, because the TADO API is not publicly documented, this guide rely on third-party tools developed through reverse-engineering.


Did this page help you?