Ecobee to Dashboard

Help Please,
I am able to read my ecobee temperature following instruction on the net. Now that I have a string output to the debug log I want to feed it to a gauge. Need a bit of help with code

var currentTemp="";
context.global.EcobeeThermostats =new Object();
context.global.EcobeeSensors =new Object();
SensorCaps =new Object();
for(i = 0; i<msg.payload.thermostatList.length; i++) 
 { 
 if (msg.payload.thermostatList[i].modelNumber=="athenaSmart")
 {
 context.global.EcobeeThermostats[msg.payload.thermostatList[i].identifier] = {
 "name":msg.payload.thermostatList[i].name,
 "connected":msg.payload.thermostatList[i].runtime.connected,
 "actualTemperature": msg.payload.thermostatList[i].runtime.actualTemperature,
 "actualHumidity": msg.payload.thermostatList[i].runtime.actualHumidity,
 "desiredHeat": msg.payload.thermostatList[i].runtime.desiredHeat,
				"desiredCool": msg.payload.thermostatList[i].runtime.desiredCool,
				"desiredFanMode": msg.payload.thermostatList[i].runtime.desiredFanMode
 };
			currentTemp=msg.payload.thermostatList[i].runtime.actualTemperature/10;
 for (j=0; j<msg.payload.thermostatList[i].remoteSensors.length; j++)
 {
 var SensorName=msg.payload.thermostatList[i].remoteSensors[j].name;
 var SensorInUse=msg.payload.thermostatList[i].remoteSensors[j].inUse;
 for (z=0; z<msg.payload.thermostatList[i].remoteSensors[j].capability.length; z++)
 {
 SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id] = {
 "type":msg.payload.thermostatList[i].remoteSensors[j].capability[z].type,
 "value":msg.payload.thermostatList[i].remoteSensors[j].capability[z].value
 };
 
 if (SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].type=="temperature")
 {
 var SensorTemp=SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].value;
 }
 if (SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].type=="occupancy")
 {
 var SensorOccupancy=SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].value;
 }
 
 context.global.EcobeeSensors[msg.payload.thermostatList[i].remoteSensors[j].name] = {
 "name":SensorName,
 "inUse":SensorInUse,
 "sensorTemp":SensorTemp,
 "sensorOccupancy":SensorOccupancy
 };
 }
 }
 }
 }

var newMsg={
 payload:" Basement Temp " + currentTemp + "*F."
};
return newMsg;

Thanks

Is your only issue getting the currentTemp to the gauge?

If so, you are halfway there.

newMsg.payload = currentTemp;
newMsg.location = ā€˜Basementā€™;

In the gauge, set the label to {{newMsg.location}} and value to {{newMsg.payload}}

If you run into the value showing with a ton of decimal places, you can format it at the gauge.
{{newMsg.payload || number: 2}}
That will round it to two decimal places.

Thanks Seth;
So close now, My javascript is very weak. I tried this

var newMsg={payload:" Basement Temp " + currentTemp + "*F."};
newMsg.payload = currentTemp;
newMsg.location = ā€˜Basementā€™;
return newMsg;

Closer still.

Letā€™s break it down.

var newMsg={payload:" Basement Temp " + currentTemp + "*F."};
newMsg.payload = currentTemp;
newMsg.location = ā€˜Basementā€™;
return newMsg;

Your first line is setting the payload to
`{ā€Basement Tempā€ + currentTemp + ā€œ*Fā€}

Your next line then overwrites the payload with the currentTemp.

Post your flow here so we can take a look at it.

Hey Seth350,
Here is my flow ;

[{"id":"61ca5481.c65dec","type":"inject","z":"a089a999.fb5f18","name":"Refresh Thermostat Data","topic":"","payload":"Refresh Thermostat Variables","payloadType":"str","repeat":"120","crontab":"","once":true,"x":158.75,"y":495.02778816223145,"wires":[["8c50e2e2.826c4"]]},{"id":"812d43f1.d52e6","type":"function","z":"a089a999.fb5f18","name":"Get ecobeePin Token","func":"// Insert your Ecobee API Key into the following variable:\ncontext.global.EcobeeClientID=\"3pwZMszIPDJiGBy6gboongMf4g8Gem3F\";\n// Insert your Ecobee API key into the above variable.\n\nvar newMsg ={\n \"url\":\"https://api.ecobee.com/authorize?response_type=ecobeePin&client_id=\"+context.global.EcobeeClientID+\"&scope=smartWrite\",\n \"method\": \"GET\",\n headers: {\n }\n};\nreturn newMsg;","outputs":1,"noerr":0,"x":282.8333435058594,"y":142.99999809265137,"wires":[["d6f0c123.3a4d6"]]},{"id":"c6553e4d.9e5d9","type":"debug","z":"a089a999.fb5f18","name":"","active":true,"console":"true","complete":"payload","x":862.8332824707031,"y":142.74999809265137,"wires":[]},{"id":"d6f0c123.3a4d6","type":"http request","z":"a089a999.fb5f18","name":"","method":"use","ret":"obj","url":"","x":489.8333435058594,"y":143.24999809265137,"wires":[["62c8a224.7cffec"]]},{"id":"9e772b1e.0740f8","type":"function","z":"a089a999.fb5f18","name":"Get Thermostats","func":"if (context.global.EcobeeRefreshtoken!==\"\")\n{\n var newMsg={\n \"url\":\"https://api.ecobee.com/1/thermostat?format=json&body=%7B%22selection%22%3A%7B%22includeAlerts%22%3A%22false%22%2C%22selectionType%22%3A%22registered%22%2C%22selectionMatch%22%3A%22%22%2C%22includeEvents%22%3A%22false%22%2C%22includeSettings%22%3A%22false%22%2C%22includeRuntime%22%3A%22true%22%2C%22includeSensors%22%3A%22true%22%7D%7D\",\n \"method\": \"GET\",\n headers: {\n \"Content-Type\":\"application/json;charset=UTF-8\",\n \"Authorization\":\"Bearer \"+context.global.EcobeeAccessToken,\n }\n };\n}\nelse {\n var newMsg={\n \"url\":\"127.0.0.1\",\n \"method\":\"GET\"\n }\n}\nreturn newMsg;","outputs":1,"noerr":0,"x":511,"y":494.52778816223145,"wires":[["c2a4b2a4.823a3"]]},{"id":"ef1be8d.ddefe18","type":"debug","z":"a089a999.fb5f18","name":"","active":true,"tosidebar":true,"console":true,"complete":"payload","x":1432.8887557983398,"y":502.56356048583984,"wires":[]},{"id":"c2a4b2a4.823a3","type":"http request","z":"a089a999.fb5f18","name":"Get Thermostat Request","method":"use","ret":"obj","url":"","tls":"","x":723.2499694824219,"y":494.52778816223145,"wires":[["645438e0.313648"]]},{"id":"7834af69.4b56","type":"inject","z":"a089a999.fb5f18","name":"Refresh token every 30 minutes","topic":"","payload":"","payloadType":"none","repeat":"1800","crontab":"","once":true,"x":177.08334350585938,"y":383.77778816223145,"wires":[["43db59f3.525778"]]},{"id":"43db59f3.525778","type":"function","z":"a089a999.fb5f18","name":"Refresh Token","func":"//context.global.EcobeeClientID=\"\";\n//context.global.EcobeeRefreshToken=\"\";\nvar newMsg ={\n \"url\":\"https://api.ecobee.com/token?grant_type=refresh_token&code=\"+context.global.EcobeeRefreshToken+\"&client_id=\"+context.global.EcobeeClientID+\"\",\n \"method\": \"POST\",\n headers: {\n }\n};\nreturn newMsg;","outputs":1,"noerr":0,"x":413.25,"y":382.5278015136719,"wires":[["a9920325.523eb"]]},{"id":"a9920325.523eb","type":"http request","z":"a089a999.fb5f18","name":"","method":"use","ret":"obj","url":"","x":574.25,"y":383.52778816223145,"wires":[["bbe692c0.8a114"]]},{"id":"bf50a8aa.73be38","type":"function","z":"a089a999.fb5f18","name":"Define Access and Refresh Token vars","func":"context.global.EcobeeAccessToken=msg.payload.access_token;\ncontext.global.EcobeeRefreshToken=msg.payload.refresh_token;\nvar msg={ payload:{\n access_token:context.global.EcobeeAccessToken,\n refresh_token:context.global.EcobeeRefreshToken\n }\n};\nreturn msg;","outputs":1,"noerr":0,"x":990.5,"y":390.02778816223145,"wires":[["60552aed.cebf24","ffa4ebfa.a35bc8"]]},{"id":"60552aed.cebf24","type":"debug","z":"a089a999.fb5f18","name":"","active":true,"console":"false","complete":"payload","x":1249.25,"y":435.77778816223145,"wires":[]},{"id":"8f3832a0.ec86c","type":"comment","z":"a089a999.fb5f18","name":"Step #3 - Refresh Token Flow a few times for good measure.","info":"This flow will refresh your token every time the input button is pressed.\n\nGlobal variables set -- EcobeeAccessToken and EcobeeRefreshToken","x":243,"y":349.27778816223145,"wires":[]},{"id":"84a6cad8.12a348","type":"comment","z":"a089a999.fb5f18","name":"Step #4 - Get Thermostat Data","info":"Get thermostat data","x":151.3888931274414,"y":458.05558013916016,"wires":[]},{"id":"645438e0.313648","type":"function","z":"a089a999.fb5f18","name":"Display temperature","func":"var currentTemp=\"\";\ncontext.global.EcobeeThermostats =new Object();\ncontext.global.EcobeeSensors =new Object();\nSensorCaps =new Object();\nfor(i = 0; i<msg.payload.thermostatList.length; i++) \n { \n if (msg.payload.thermostatList[i].modelNumber==\"athenaSmart\")\n {\n context.global.EcobeeThermostats[msg.payload.thermostatList[i].identifier] = {\n \"name\":msg.payload.thermostatList[i].name,\n \"connected\":msg.payload.thermostatList[i].runtime.connected,\n \"actualTemperature\": msg.payload.thermostatList[i].runtime.actualTemperature,\n \"actualHumidity\": msg.payload.thermostatList[i].runtime.actualHumidity,\n \"desiredHeat\": msg.payload.thermostatList[i].runtime.desiredHeat,\n\t\t\t\t\"desiredCool\": msg.payload.thermostatList[i].runtime.desiredCool,\n\t\t\t\t\"desiredFanMode\": msg.payload.thermostatList[i].runtime.desiredFanMode\n };\n\t\t\tcurrentTemp=msg.payload.thermostatList[i].runtime.actualTemperature/10;\n for (j=0; j<msg.payload.thermostatList[i].remoteSensors.length; j++)\n {\n var SensorName=msg.payload.thermostatList[i].remoteSensors[j].name;\n var SensorInUse=msg.payload.thermostatList[i].remoteSensors[j].inUse;\n for (z=0; z<msg.payload.thermostatList[i].remoteSensors[j].capability.length; z++)\n {\n SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id] = {\n \"type\":msg.payload.thermostatList[i].remoteSensors[j].capability[z].type,\n \"value\":msg.payload.thermostatList[i].remoteSensors[j].capability[z].value\n };\n \n if (SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].type==\"temperature\")\n {\n var SensorTemp=SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].value;\n }\n if (SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].type==\"occupancy\")\n {\n var SensorOccupancy=SensorCaps[msg.payload.thermostatList[i].remoteSensors[j].capability[z].id].value;\n }\n \n context.global.EcobeeSensors[msg.payload.thermostatList[i].remoteSensors[j].name] = {\n \"name\":SensorName,\n \"inUse\":SensorInUse,\n \"sensorTemp\":SensorTemp,\n \"sensorOccupancy\":SensorOccupancy\n };\n }\n }\n }\n }\n\nvar newMsg={payload:\" Basement Temp \" + currentTemp + \"*F.\"};\nnewMsg.payload = currentTemp;\nnewMsg.location = ā€˜Basementā€™;\nreturn newMsg;","outputs":1,"noerr":0,"x":995.7499542236328,"y":490.5278091430664,"wires":[["ef1be8d.ddefe18","6f44348e.7e7f9c","ba0949e2.3c3488"]]},{"id":"7735ff30.17c48","type":"http response","z":"a089a999.fb5f18","name":"","x":473.5,"y":597.2777881622314,"wires":[]},{"id":"69de9ef6.14c2b","type":"function","z":"a089a999.fb5f18","name":"Throw Ecobee Data","func":"if(msg.req.headers.authorization && msg.req.headers.authorization==\"Bearer \"+context.global.FreeboardAuthTocken){\n msg.payload.thermostats=context.global.EcobeeThermostats;\n msg.payload.sensors=context.global.EcobeeSensors;\n msg.res.statusCode=\"200\";\nreturn msg;\n}\nelse\n{\n //msg.payload=\"Not Authorized\";\n msg.res.send(\"403\",\"Forbidden\");\n return;\n}\n\n//return msg;","outputs":1,"noerr":0,"x":302.5,"y":597.2777881622314,"wires":[["7735ff30.17c48"]]},{"id":"fafbfdc8.262af","type":"http in","z":"a089a999.fb5f18","name":"Get Ecobee Data","url":"/getEcobeeData","method":"get","upload":false,"swaggerDoc":"","x":107.5,"y":597.2777881622314,"wires":[["69de9ef6.14c2b"]]},{"id":"c33432a0.5efc3","type":"inject","z":"a089a999.fb5f18","name":"Step 1","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":107.83333969116211,"y":142.49999809265137,"wires":[["812d43f1.d52e6"]]},{"id":"8e85716f.06c03","type":"comment","z":"a089a999.fb5f18","name":"Step #1 - Requesting the 4-digit ecobeePin - Log in to Ecobee.com and go to your \"My Apps\" before clicking the Inject node.","info":"The ecobeePin must be entered into your \"My Apps\" in Ecobee.com within 10 minutes.","x":449.8333435058594,"y":72.5,"wires":[]},{"id":"56e7c92.b8c4c38","type":"function","z":"a089a999.fb5f18","name":"Get Authorization Token","func":"var newMsg ={\n \"url\":\"https://api.ecobee.com/token?grant_type=ecobeePin&code=\"+context.global.EcobeeAccessToken+\"&client_id=\"+context.global.EcobeeClientID,\n \"method\": \"POST\",\n headers: {\n }\n}\nreturn newMsg;","outputs":1,"noerr":0,"x":304.8333435058594,"y":253.49999809265137,"wires":[["9443ae2d.69978"]]},{"id":"87537e19.e918a","type":"debug","z":"a089a999.fb5f18","name":"","active":false,"console":"true","complete":"payload","x":693.8333435058594,"y":323.24999809265137,"wires":[]},{"id":"9443ae2d.69978","type":"http request","z":"a089a999.fb5f18","name":"","method":"use","ret":"obj","url":"","x":506.83331298828125,"y":253.74999809265137,"wires":[["87537e19.e918a","8825c81b.8bcc68","20b0b4bf.cf1efc"]]},{"id":"e764d1e9.00ac1","type":"comment","z":"a089a999.fb5f18","name":"Step #2 - Requesting the first access token - you must run Step 2 within 10 minutes of running Step 1.","info":"Now that the app has been added to your \"My Apps\" - we need to request our first access token.","x":374.0555725097656,"y":217.99999332427979,"wires":[]},{"id":"62c8a224.7cffec","type":"function","z":"a089a999.fb5f18","name":"Save access code","func":"context.global.EcobeeAccessToken = msg.payload.code;\nvar msg = {\"payload\":\"Ecobee Pin: \"+msg.payload.ecobeePin};\nreturn msg;","outputs":1,"noerr":0,"x":678.8332824707031,"y":142.49999809265137,"wires":[["c6553e4d.9e5d9"]]},{"id":"8825c81b.8bcc68","type":"file","z":"a089a999.fb5f18","name":"EcobeeTokens","filename":"/home/pi/data/ecobee_token/test.log","appendNewline":false,"createDir":false,"overwriteFile":"true","x":700.8333435058594,"y":288.49999809265137,"wires":[[]]},{"id":"fdb9e02e.ac44a","type":"file in","z":"a089a999.fb5f18","name":"EcobeeTokens","filename":"/home/pi/data/ecobee_token/test.log","format":"utf8","sendError":true,"x":276.0555648803711,"y":682.2777690887451,"wires":[["ce69dbe.97fb528"]]},{"id":"a08c19aa.7f8ab8","type":"debug","z":"a089a999.fb5f18","name":"","active":true,"console":"true","complete":"payload","x":882.0555038452148,"y":681.9444255828857,"wires":[]},{"id":"8296edbf.c3de2","type":"inject","z":"a089a999.fb5f18","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":104.0555648803711,"y":682.2777690887451,"wires":[["fdb9e02e.ac44a"]]},{"id":"80638745.726168","type":"function","z":"a089a999.fb5f18","name":"Define Access and Refresh Tokens","func":"if (msg.payload.refresh_token!==null)\n{\n context.global.EcobeeRefreshToken = msg.payload.refresh_token;\n node.warn(\"Refresh Token as Global Context: \"+context.global.EcobeeRefreshToken);\n}\nreturn msg;","outputs":1,"noerr":0,"x":645.0555038452148,"y":682.2777690887451,"wires":[["a08c19aa.7f8ab8"]]},{"id":"ffa4ebfa.a35bc8","type":"file","z":"a089a999.fb5f18","name":"EcobeeTokens","filename":"/home/pi/data/ecobee_token/test.log","appendNewline":false,"createDir":false,"overwriteFile":"true","x":1258.5,"y":392.52778816223145,"wires":[[]]},{"id":"20b0b4bf.cf1efc","type":"function","z":"a089a999.fb5f18","name":"Define Access and Refresh Tokens","func":"context.global.EcobeeRefreshToken = msg.payload.refresh_token;\ncontext.global.EcoBeeAccessToken = msg.payload.access_token;\nvar msg={ \"payload\":context.global.EcobeeRefreshToken };\nreturn msg;","outputs":1,"noerr":0,"x":762.8333435058594,"y":253.49999809265137,"wires":[["fb74b048.c58c6"]]},{"id":"fb74b048.c58c6","type":"debug","z":"a089a999.fb5f18","name":"","active":true,"console":"false","complete":"false","x":1013.8332824707031,"y":253.49999809265137,"wires":[]},{"id":"bbe692c0.8a114","type":"switch","z":"a089a999.fb5f18","name":"Refresh Token","property":"payload.refresh_token","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","outputs":2,"x":738,"y":383.77778816223145,"wires":[["b993efa0.5340e"],["bf50a8aa.73be38"]]},{"id":"46b1500c.fff47","type":"comment","z":"a089a999.fb5f18","name":"This flow will extract the Access and Refresh tokens from the log file and put them in the global context variables.","info":"","x":409.7221908569336,"y":645.9444255828857,"wires":[]},{"id":"8c50e2e2.826c4","type":"delay","z":"a089a999.fb5f18","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":346.1666564941406,"y":494.9444751739502,"wires":[["9e772b1e.0740f8"]]},{"id":"b747a8fb.95b6d8","type":"comment","z":"a089a999.fb5f18","name":"You will have 2 minutes to enter the 4-digit pin into Ecobee.com and authorize the application. (Ecobee disgards the 4-pin code after 10 minutes!)","info":"","x":516.0833435058594,"y":107.49999809265137,"wires":[]},{"id":"65732cbd.6382d4","type":"comment","z":"a089a999.fb5f18","name":"Display Ecobee Data to Freeboard","info":"","x":159.75,"y":560.2777881622314,"wires":[]},{"id":"ce69dbe.97fb528","type":"json","z":"a089a999.fb5f18","name":"","x":428.3055648803711,"y":682.2777690887451,"wires":[["80638745.726168"]]},{"id":"b993efa0.5340e","type":"file in","z":"a089a999.fb5f18","name":"EcobeeTokens","filename":"/home/pi/data/ecobee_token/test.log","format":"utf8","sendError":true,"x":913.75,"y":357.27778816223145,"wires":[["68006dbb.8289f4"]]},{"id":"68006dbb.8289f4","type":"json","z":"a089a999.fb5f18","name":"","x":1069,"y":357.27778816223145,"wires":[["e1443a61.ce5d18"]]},{"id":"e1443a61.ce5d18","type":"function","z":"a089a999.fb5f18","name":"Define Access and Refresh Tokens","func":"if (msg.payload.refresh_token!==null)\n{\n context.global.EcobeeRefreshToken = msg.payload.refresh_token;\n node.warn(\"Refresh Token as Global Context: \"+context.global.EcobeeRefreshToken);\n}\nreturn msg;","outputs":1,"noerr":0,"x":1295.7499389648438,"y":357.27778816223145,"wires":[[]]},{"id":"61e7d1d1.e59d5","type":"inject","z":"a089a999.fb5f18","name":"Step 2","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":103.08334350585938,"y":253.49999809265137,"wires":[["56e7c92.b8c4c38"]]},{"id":"6f44348e.7e7f9c","type":"ui_text","z":"a089a999.fb5f18","group":"d97873f7.10d4a","order":3,"width":0,"height":0,"name":"","label":"Basement Temp ","format":"{{msg.payload}}","layout":"row-spread","x":1314.285743713379,"y":565.7143096923828,"wires":[]},{"id":"ba0949e2.3c3488","type":"ui_gauge","z":"a089a999.fb5f18","name":"","group":"d97873f7.10d4a","order":4,"width":0,"height":0,"gtype":"gage","title":"{{newMsg.location}}","label":"* F ","format":"{{newMsg.payload}}","min":"50","max":"80","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":1274.2856216430664,"y":658.571382522583,"wires":[]},{"id":"d97873f7.10d4a","type":"ui_group","z":"","name":"Woodstove","tab":"56fe8a47.c05bc4","disp":true,"width":"6","collapse":false},{"id":"56fe8a47.c05bc4","type":"ui_tab","z":"","name":"WoodStove","icon":"dashboard","disabled":false,"hidden":false}]

I get the following debug error
SyntaxError: Invalid or unexpected token

I found the problem. I mistakenly told you wrong.

I told you:
newMsg.location = ā€˜Basementā€™;

It should be:
newMsg.location = ā€œBasementā€;

That will take care of your syntax error.
Sorry about that! :blush:

Actually, Javascript does NOT care if you use single (') or double (") quotes, so I suspect the syntax error is actually caused by the use of "smart quotes" (that ā€˜) character -- which is NOT an apostrophe.

This is why we always recommend the use of 3 back-tics on separate lines before and after any code blocks that are posted on this forum. Otherwise, the forum may modify the pasted code to be more readable, which then causes problems when the next person tries to copy/paste your modified code into a function node...

1 Like

Thank you @shrickus for the clarification. I was suspecting that it didn't care, but none-the-less, that took care of his syntax error.

Actually, those are also smart quotes, and so should not work either... but if he changed the code by typing the ansi double-quote character, then yes, that would fix the problem.

IIRC, I actually debugged his code on my iPhone that was connected to my home PC's VNC server.

I bet when I was typing it in with my phone, it inserted the smart quotes.
Standard double quote key on my keyboard works fine.

I understand now!

Hey Guys,
newMsg.location = "Basement";
I know this is now correct because Basement is in green. I now have this error when code is triggered

TypeError: Cannot read property 'length' of undefined

Thanks for all the help.

Hey Cormad,
I was seeing that too and it is coming from one of your other functions I believe.

I attributed it to being that I was not connected to an ecobee thermostat. (I do have one though :+1:)

Hi Cormad,

What you are experiencing is one of the frustrating things with Javascript syntax:

msg.payload.thermostatList.length

will throw an exception unless msg is an object, with a payload property which is also an object, that has its own thermostatList property that also happens to be an array... so, lots of ways that can go wrong, and checking each step along the way can be tedious (although it's very necessary to code defensively).

I suspect there is a more bullet-proof way to reformat your Ecobee output to the objects you need for charting, using a change node with a JSONata expression. I'd be happy to show you what I'm thinking, if you could post a sample of the JSON data from the Ecobee, and an example of the output structure you are currently building.