Is it possible to link the NEST thermostat to nodered using NORA?

is it possiable link the NEST thermostat to nodered using NORA?
THX!

Assuming you mean have the Nest operating in Google Home and use NORA to integrate if with Node-Red, then no. Nora creates it's own "devices" in Google Home which then can control / report status of flows in Node-Red.

I've created some flows which include the data from my Nest thermostats. I did not use NORA to gain access to the data, but directly access the Nest devices thru Google's Smart Device management API.

https://developers.google.com/nest/device-access (check the Guides in the top part of the page)

understood,thank u

is there any tutorial for this?like “how to integrate Nest thermostats with nodered using SDM api”,im a new guy here,thank you

then im confused,the NORA device controls nothing?

I'm a Node RED newbie too, just installed it 2 days ago after I learned about Node RED in my search how to access the SDM API. Why googling I came across this YouTube intro to the API:

And this post on the home assistant forum:

The post of mbuscher showed Node RED flows, I dove into them and got the API working. In the meantime I got very interested in Node RED, a cool piece of kit :slight_smile:

I'll post my flows of my implementation later today when I continue work on using the Nest device info in my home.

1 Like

thx man, its really helpful.
can u control the google device thru SDM api,or just got data from it

Did you check the Google device-access page? :wink:

One thing I am missing from the API output is the actual controlling state of the thermostat. What I am looking for is, is the thermostat actually commanding the central heater to heat? At the moment I am calculating it from the actual temp minus the set temp to "guess" if the heater is on. But I have noticed that there is a slight overshoot of the room temp compared to the set temp, so my calculation may not represent the actual heating mode of the situation. I can remember that it was in the old Nest API... I'm going to give google a ring to ask for the actual heating status.

Here is my flow (my credentials removed). You first have to run thru the Guide to obtain the projectid, token and refresh token. The token needs to refreshed every hours so I created a refresh flow. That token is set as a enviromental variable (flow in this case) and the the 2nd flow, gets the data from the API so you can copy/paste the json objects. The 3rd flow injects/transforms that data and forwards the thermostat temp/humidity data to my MQQT brokers (Domoticz). The Catch flow is to send a telegram message to my phone, I want to be notified in case the API does not work.

[{"id":"2c0ed98a.a1c186","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"60880bbc.d12c64","type":"inject","z":"2c0ed98a.a1c186","name":"Hourly","props":[],"repeat":"3500","crontab":"","once":true,"onceDelay":0.1,"topic":"","payloadType":"str","x":100,"y":80,"wires":[["6c837807.bc5f88","488dea8f.317dd4"]]},{"id":"6c837807.bc5f88","type":"function","z":"2c0ed98a.a1c186","name":"Client details","func":"let clientId = 'clientid';\nlet clientSecret = 'clientsecret';\nlet refreshToken = 'refreshtoken';\nmsg.url = \"https://www.googleapis.com/oauth2/v4/token\";\nmsg.method = \"POST\";\nmsg.payload = {\n    \"client_id\": clientId,\n    \"client_secret\": clientSecret,\n    \"refresh_token\": refreshToken,\n    \"grant_type\": 'refresh_token'\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":270,"y":80,"wires":[["3317d87f.0643d8"]]},{"id":"3317d87f.0643d8","type":"http request","z":"2c0ed98a.a1c186","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":450,"y":80,"wires":[["db63470a.142e18"]]},{"id":"db63470a.142e18","type":"change","z":"2c0ed98a.a1c186","name":"","rules":[{"t":"set","p":"access_token","pt":"flow","to":"payload.access_token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":80,"wires":[["fcc03829.6ce668"]]},{"id":"fcc03829.6ce668","type":"debug","z":"2c0ed98a.a1c186","name":"Token updated","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":860,"y":80,"wires":[]},{"id":"f00903af.64d5c","type":"inject","z":"2c0ed98a.a1c186","name":"Get devices","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":110,"y":280,"wires":[["14f51631.6362ba"]]},{"id":"14f51631.6362ba","type":"function","z":"2c0ed98a.a1c186","name":"","func":"let projectId = 'ProjectID';\nmsg.url = \"https://smartdevicemanagement.googleapis.com/v1/enterprises/\" + projectId + \"/devices\"\nmsg.method = \"GET\";\nmsg.headers = {\n    'Authorization': 'Bearer ' + flow.get(\"access_token\"),\n    'Content-Type': 'application/json'\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":260,"y":280,"wires":[["930ae093.c7863","864d2e99.d9c99"]]},{"id":"59f7a033.81ca7","type":"debug","z":"2c0ed98a.a1c186","name":"Devices","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":640,"y":280,"wires":[]},{"id":"930ae093.c7863","type":"http request","z":"2c0ed98a.a1c186","name":"Devices","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":460,"y":280,"wires":[["59f7a033.81ca7"]]},{"id":"864d2e99.d9c99","type":"debug","z":"2c0ed98a.a1c186","name":"Function","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":460,"y":340,"wires":[]},{"id":"474b00a4.d23d5","type":"comment","z":"2c0ed98a.a1c186","name":"Token refresh every hour, set to flow.access_token","info":"","x":240,"y":40,"wires":[]},{"id":"ca714a17.239828","type":"comment","z":"2c0ed98a.a1c186","name":"Use access_token to GET devices JSON from Googleapi to debug","info":"","x":280,"y":240,"wires":[]},{"id":"c5c1a292.319b7","type":"change","z":"2c0ed98a.a1c186","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"access_token","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":140,"wires":[["b32f327d.134a7"]]},{"id":"488dea8f.317dd4","type":"delay","z":"2c0ed98a.a1c186","name":"Delay 10s","pauseType":"delay","timeout":"10","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":260,"y":140,"wires":[["c5c1a292.319b7","5b8d7b99.9de9d4"]]},{"id":"b32f327d.134a7","type":"GSheet","z":"2c0ed98a.a1c186","creds":"","method":"update","action":"","sheet":"SpreadsheetID","cells":"GoogleAPI!B6","flatten":false,"name":"Gsheets update","x":640,"y":140,"wires":[["24deb9bf.d35aa6"]]},{"id":"24deb9bf.d35aa6","type":"debug","z":"2c0ed98a.a1c186","name":"Gsheets","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":840,"y":140,"wires":[]},{"id":"5b8d7b99.9de9d4","type":"change","z":"2c0ed98a.a1c186","name":"Timestamp","rules":[{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":200,"wires":[["2d21cda4.da1292"]]},{"id":"a4af53b8.54ad","type":"GSheet","z":"2c0ed98a.a1c186","creds":"","method":"update","action":"","sheet":"SpreadsheetID","cells":"GoogleAPI!B7","flatten":false,"name":"Gsheets update","x":780,"y":200,"wires":[[]]},{"id":"2d21cda4.da1292","type":"function","z":"2c0ed98a.a1c186","name":"Date","func":"// Create a Date object from the payload\nvar date = new Date(msg.payload);\n// Change the payload to be a formatted Date string\nmsg.payload = date.toString();\n// Return the message so it can be sent on\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":610,"y":200,"wires":[["a4af53b8.54ad"]]},{"id":"c9bce080.9ab7d","type":"inject","z":"2c0ed98a.a1c186","name":"Get SDM status","props":[],"repeat":"300","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":110,"y":460,"wires":[["62ec750d.7f682c"]]},{"id":"62ec750d.7f682c","type":"function","z":"2c0ed98a.a1c186","name":"","func":"let projectId = 'projectID';\nmsg.url = \"https://smartdevicemanagement.googleapis.com/v1/enterprises/\" + projectId + \"/devices\"\nmsg.method = \"GET\";\nmsg.headers = {\n    'Authorization': 'Bearer ' + flow.get(\"access_token\"),\n    'Content-Type': 'application/json'\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":280,"y":460,"wires":[["6beb5182.0b382"]]},{"id":"6beb5182.0b382","type":"http request","z":"2c0ed98a.a1c186","name":"Devices","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":470,"y":460,"wires":[["7c202629.e96cf8","702f74f2.e07a4c","5c6bc7bb.601438","fd15e133.1394b","662b024f.561b8c","953af8c0.b2a148"]]},{"id":"702f74f2.e07a4c","type":"change","z":"2c0ed98a.a1c186","name":"","rules":[{"t":"set","p":"living.humidity","pt":"global","to":"payload.devices[0].traits[\"sdm.devices.traits.Humidity\"].ambientHumidityPercent","tot":"msg"},{"t":"set","p":"living.temperature","pt":"global","to":"payload.devices[1].traits[\"sdm.devices.traits.Temperature\"].ambientTemperatureCelsius","tot":"msg"},{"t":"set","p":"living.settemp","pt":"global","to":"payload.devices[1].traits[\"sdm.devices.traits.ThermostatTemperatureSetpoint\"].heatCelsius","tot":"msg"},{"t":"set","p":"bedroom.temperature","pt":"global","to":"payload.devices[0].traits[\"sdm.devices.traits.Temperature\"].ambientTemperatureCelsius","tot":"msg"},{"t":"set","p":"bedroom.settemp","pt":"global","to":"payload.devices[0].traits[\"sdm.devices.traits.ThermostatTemperatureSetpoint\"].heatCelsius","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":460,"wires":[[]]},{"id":"18bda007.da537","type":"mqtt out","z":"2c0ed98a.a1c186","name":"Domoticz","topic":"domoticz/in","qos":"0","retain":"false","broker":"","x":660,"y":580,"wires":[]},{"id":"7c202629.e96cf8","type":"debug","z":"2c0ed98a.a1c186","name":"SDM Devices","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":680,"y":520,"wires":[]},{"id":"fd15e133.1394b","type":"function","z":"2c0ed98a.a1c186","name":"Nest HK set 93","func":"var SETTEMPT = msg.payload.devices[1].traits[\"sdm.devices.traits.ThermostatTemperatureSetpoint\"].heatCelsius;\nvar SETTEMP = SETTEMPT.toFixed(1)\nmsg.payload = {\"idx\":93,\"svalue\":SETTEMP};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":480,"y":620,"wires":[["5fa793fe.54241c","93326e3e.1276d"]]},{"id":"5c6bc7bb.601438","type":"function","z":"2c0ed98a.a1c186","name":"Nest HK idx  92","func":"var TEMPT = msg.payload.devices[1].traits[\"sdm.devices.traits.Temperature\"].ambientTemperatureCelsius;\nvar TEMP = TEMPT.toFixed(1)\nvar HUM = msg.payload.devices[1].traits[\"sdm.devices.traits.Humidity\"].ambientHumidityPercent;\nif (HUM < 30) {\n    HUM_STAT=2;\n} else if ((HUM >= 30) & (HUM < 45)) {\n    HUM_STAT=0;\n} else if ((HUM >= 45) & (HUM < 70)) {\n    HUM_STAT=1;\n} else if (HUM >= 70) {\n    HUM_STAT=3;\n}\nmsg.payload = {\"idx\":92,\"nvalue\":0,\"svalue\":TEMP + \";\" + HUM + \";\" + HUM_STAT};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":480,"y":580,"wires":[["18bda007.da537","d2476fa3.442cb"]]},{"id":"5fa793fe.54241c","type":"mqtt out","z":"2c0ed98a.a1c186","name":"Domoticz","topic":"domoticz/in","qos":"0","retain":"false","broker":"","x":660,"y":620,"wires":[]},{"id":"d2476fa3.442cb","type":"debug","z":"2c0ed98a.a1c186","name":"idx 92","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":580,"wires":[]},{"id":"93326e3e.1276d","type":"debug","z":"2c0ed98a.a1c186","name":"idx 93","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":620,"wires":[]},{"id":"662b024f.561b8c","type":"function","z":"2c0ed98a.a1c186","name":"Nest HK set 95","func":"var SETTEMPT = msg.payload.devices[0].traits[\"sdm.devices.traits.ThermostatTemperatureSetpoint\"].heatCelsius;\nvar SETTEMP = SETTEMPT.toFixed(1)\nmsg.payload = {\"idx\":95,\"svalue\":SETTEMP};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":480,"y":700,"wires":[["343f198f.28f636","7abe058.478cafc"]]},{"id":"953af8c0.b2a148","type":"function","z":"2c0ed98a.a1c186","name":"Nest HK idx  94","func":"var TEMPT = msg.payload.devices[0].traits[\"sdm.devices.traits.Temperature\"].ambientTemperatureCelsius;\nvar TEMP = TEMPT.toFixed(1)\nvar HUM = msg.payload.devices[0].traits[\"sdm.devices.traits.Humidity\"].ambientHumidityPercent;\nif (HUM < 30) {\n    HUM_STAT=2;\n} else if ((HUM >= 30) & (HUM < 45)) {\n    HUM_STAT=0;\n} else if ((HUM >= 45) & (HUM < 70)) {\n    HUM_STAT=1;\n} else if (HUM >= 70) {\n    HUM_STAT=3;\n}\nmsg.payload = {\"idx\":94,\"nvalue\":0,\"svalue\":TEMP + \";\" + HUM + \";\" + HUM_STAT};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":480,"y":660,"wires":[["a5045608.5ecb38","89083c30.dd6dc"]]},{"id":"343f198f.28f636","type":"mqtt out","z":"2c0ed98a.a1c186","name":"Domoticz","topic":"domoticz/in","qos":"0","retain":"false","broker":"","x":660,"y":700,"wires":[]},{"id":"a5045608.5ecb38","type":"mqtt out","z":"2c0ed98a.a1c186","name":"Domoticz","topic":"domoticz/in","qos":"0","retain":"false","broker":"","x":660,"y":660,"wires":[]},{"id":"89083c30.dd6dc","type":"debug","z":"2c0ed98a.a1c186","name":"idx 94","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":660,"wires":[]},{"id":"7abe058.478cafc","type":"debug","z":"2c0ed98a.a1c186","name":"idx 95","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":700,"wires":[]},{"id":"55438db8.6ae974","type":"comment","z":"2c0ed98a.a1c186","name":"Thermostat Living & Bedroom to MQTT every 5 min","info":"","x":220,"y":420,"wires":[]},{"id":"93101f84.5da5e","type":"catch","z":"2c0ed98a.a1c186","name":"","scope":["7c202629.e96cf8","fd15e133.1394b","5c6bc7bb.601438","662b024f.561b8c","953af8c0.b2a148"],"uncaught":false,"x":70,"y":520,"wires":[["9b643695.62fcc8"]]},{"id":"420b7109.6ef4f","type":"telegram sender","z":"2c0ed98a.a1c186","name":"Catch","bot":"","x":310,"y":520,"wires":[[]]},{"id":"9b643695.62fcc8","type":"change","z":"2c0ed98a.a1c186","name":"Chatid","rules":[{"t":"set","p":"payload.chatId","pt":"msg","to":"TelegramchatID","tot":"num"},{"t":"set","p":"payload.type","pt":"msg","to":"message","tot":"str"},{"t":"set","p":"payload.content","pt":"msg","to":"payload.error.message","tot":"str"},{"t":"set","p":"failsafe","pt":"global","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":190,"y":520,"wires":[["420b7109.6ef4f"]]}]

Let me know if you have any questions.

3 Likes

Think of Nora as a way to link to virtual devices, one in Google Home and the other in Node-Red to sync status and commands from Google Home, You then use flows within Node-Red to connect the Nora devices to physical devices.

To control physical devices in Google Home, like your Nest, you have to use other way of connecting like the API connections like @mintgroen shared.

Hope that helps

been busy these days。
i m reading the document on the SDM website,seems no “current state“ data feedback

thx a billion!!!!!!
follow the tutorial ,im trying to make ur flow work
wot should i fill in the http reauest ,url blank?
it seems u can just get the data from API,but u cant controll the thermostat thru the API

understood,thx!
i saw pico563 has integart NEST thermostat using NORA

Yes you can (and I tried, works) see the use the API guide, execute a command:

The URL and method is set in the function node, you should keep the method in the http node, to set by msg method.

What this looks like (though should confirm with @pico563) is that this is just creating a voice control device in Google Home but all the actual status/control of the thermostat named Nest is being done in Node-Red

its not woking,am i do it right? thx

@piggy Looks ok to me. (although I think it may not be a good idea to post your credentials on the www, someone else could start controlling your thermostat ;))

What does the debug show?

delete the pix,good advise,thx
its not working, says "invalid_grant"
20201128232838

Did you link your account correctly?