Averaging Sequence for Node-Red - Hubitat

I’m looking to minimize what is done “on hub” and move most “processing” to my NR computer. To that end I want to remove “Average All” (fine HE App, never had any trouble), and replace it with a couple NR sequences.

This took me longer than expected, and there is probably an easier/better way to do it so I want to post what I did and get some feedback. Here is what I came up with so far. I’d like to be able to limit the amount of digits to one decimal place, but I have focused on that yet probably a function node (which I’m not great at).

Big Kudos and Thanks to @LosinIt for providing and troubleshooting the function node, and greatly improving this sequence.

The two inject nodes get the data set correctly, after that the average will be updated every time any device reports.

[{"id":"a29df9cd.d63af8","type":"hubitat device","z":"7442e075.dd28a","name":"Package Box Motion","server":"c0da6dea.8576a","deviceId":"2471","attribute":"temperature","sendEvent":true,"x":400,"y":740,"wires":[["ce533421.0170e8"]]},{"id":"3f19f6ca.bc61aa","type":"hubitat device","z":"7442e075.dd28a","name":"Package Box Button","server":"c0da6dea.8576a","deviceId":"2535","attribute":"temperature","sendEvent":true,"x":400,"y":680,"wires":[["ce533421.0170e8"]]},{"id":"ef121ce5.3da83","type":"hubitat device","z":"7442e075.dd28a","name":"Porch - Multi Sensor ","server":"b1088aaa.95e9e8","deviceId":"78","attribute":"temperature","sendEvent":true,"x":400,"y":800,"wires":[["ce533421.0170e8"]]},{"id":"3126a5c3.2928fa","type":"hubitat device","z":"7442e075.dd28a","name":"VD - NR - Average Outside Temp","server":"e03140c5.bed32","deviceId":"1480","attribute":"temperature","sendEvent":false,"x":700,"y":660,"wires":[[]]},{"id":"ce533421.0170e8","type":"function","z":"7442e075.dd28a","name":"Average","func":"if (msg.payload.deviceId == \"78\") context.set('78',msg.payload.value);\nif (msg.payload.deviceId == \"2535\") context.set('2535',msg.payload.value);\nif (msg.payload.deviceId == \"2471\") context.set('2471',msg.payload.value);\nmsg.payload.average = ((context.get('78') + context.get('2535') + context.get('2471')) / 3 );\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":620,"y":720,"wires":[["bf07739a.e1b43"]]},{"id":"16c597d4.4a2dd8","type":"hubitat command","z":"7442e075.dd28a","name":"VD - NR - Average Outside Temp","server":"e03140c5.bed32","deviceId":"1480","command":"setTemperature","commandArgs":"","x":760,"y":800,"wires":[[]]},{"id":"ab09b1.7ae1065","type":"change","z":"7442e075.dd28a","name":"limit decimal to 1 digit","rules":[{"t":"set","p":"payload","pt":"msg","to":"$round(payload, 1)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":760,"wires":[["72fc1ec9.b21b1"]]},{"id":"72fc1ec9.b21b1","type":"change","z":"7442e075.dd28a","name":"","rules":[{"t":"set","p":"arguments","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":760,"wires":[["16c597d4.4a2dd8"]]},{"id":"bf07739a.e1b43","type":"change","z":"7442e075.dd28a","name":"set payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.average","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":720,"wires":[["ab09b1.7ae1065"]]},{"id":"2843d3cc.496cbc","type":"inject","z":"7442e075.dd28a","name":"set temps ","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":200,"y":700,"wires":[["a29df9cd.d63af8","3f19f6ca.bc61aa","ef121ce5.3da83"]]},{"id":"ae9aaa74.0de868","type":"inject","z":"7442e075.dd28a","name":"set Average Temp","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","x":170,"y":760,"wires":[["a29df9cd.d63af8","ef121ce5.3da83"]]},{"id":"c0da6dea.8576a","type":"hubitat config","name":"ZigBee Hub","usetls":false,"host":"","port":"80","appId":"2532","nodeRedServer":"","webhookPath":"/hubitat/webhook__","autoRefresh":true,"useWebsocket":false},{"id":"b1088aaa.95e9e8","type":"hubitat config","name":"Z-WaveHub","usetls":false,"host":"","port":"80","appId":"5","nodeRedServer":"","webhookPath":"/hubitat/webhook_","autoRefresh":true,"useWebsocket":false},{"id":"e03140c5.bed32","type":"hubitat config","name":"Apps Hub","usetls":false,"host":"","port":"80","appId":"899","nodeRedServer":"","webhookPath":"/hubitat/webhook___","autoRefresh":true,"useWebsocket":false}]

Important Update: The function node uses msg.payload.deviceId get and store data for the averaging. This data is specific to every device (mine will not work for yours). To use this method you will need to get your deviceId using a debug node (complete msg) and update the function node accordingly.

EDIT: Added a node to limit decimal reading to 1 digit (my preference for dashboards). It is not essential, and can be removed, or adjusted.

What is the node labeled “average”? I searched on average in the palette manager and didn’t find one with the “balance” icon.

When I had need for averaging temps I used a function node that stored each value by device name and then returned the sum/count and it worked great in 3 lines of code. I’d paste the code but I deleted that node some weeks ago.

Ah, OK. I did look at that one and the icon was missing so I thought it wasn’t it. :roll_eyes:

I remember now that I had tried that node and it’s behavior was unsatisfactory and that’s why I went with the function node. I’m sure I can recreate it if it’ll help.

it would certainly save steps. The node could be a lot better which is why I think I need to jump so many hoops. It “works” but is certainly sub-optimal.

In an ideal world HE would have a “Virtual Multi Sensor” that was actually multi, and included Temp, Lux, Humidity, Motion, etc and then I could set all outdoor conditions in one averaged device. But that’s just me day dreaming.

I’ll be happy to code it up for ya! I need to eat something and then I’ll do so and send it back.

Cobra has an average all app but I’ve never looked at it. Still, since you’re in NR, I’d stay there.

1 Like

lol As stated in OP that is exactly what I’m replacing. It works great but you still need a Virtual Device to stored the data. HE does not provide one (that has all the attributes), so we have to create an additional device for each attrib we want to track, a waste IMO. Sometimes I do not get their rational (but I guess I don’t need to.

Much appreciated.

D’oh! Read the damn post, Steve!

I need to see the complete message from each of the Set Temp change nodes so I know what to discriminate on in the function. I see that the change nodes just move the .value up a level but that doesn’t show the needed info.

Those are just there for the “average” node. If it is being replace you can put it in what ever format you want. In the end the “payload” should be the averaged temp # and then converted to “msg.argument” to set the Virtual get node.

If you want to output the averaged value as “msg.argument” would save a node or anything else that will then be converted.

If you think it is still needed


EDIT: The sensor spits out ‘payload.value’ and average node wanted ‘payload’ which is the only reason they are there.

I understand that but I need to see the complete messages coming out of the 3 Device nodes so I see what I have to work with. Remember, I can’t reproduce it here because they are specific to your system.

so you want the message from the device not the set temp correct?

Exactly. I’ll associate each value with it’s deviceId. It isn’t a generic solution like the node you are currently using but it’ll be super easy to tweak if you add/change devices.

My confidence level here is low because I can’t test/debug but give this a try. Disable the flow you sent me and import this to a new flow.

[{"id":"8f3f66e3.0cb258","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"5b0068a5.62cdd8","type":"hubitat device","z":"8f3f66e3.0cb258","name":"Package Box Motion","server":"c0da6dea.8576a","deviceId":"2471","attribute":"temperature","sendEvent":false,"x":400,"y":320,"wires":[["dbbef892.bdcab8"]]},{"id":"2e5360f7.df8fd","type":"hubitat device","z":"8f3f66e3.0cb258","name":"Package Box Button","server":"c0da6dea.8576a","deviceId":"2535","attribute":"temperature","sendEvent":false,"x":400,"y":260,"wires":[["dbbef892.bdcab8"]]},{"id":"68eb2665.b53958","type":"hubitat device","z":"8f3f66e3.0cb258","name":"Porch - Multi Sensor ","server":"b1088aaa.95e9e8","deviceId":"78","attribute":"temperature","sendEvent":false,"x":400,"y":380,"wires":[["dbbef892.bdcab8"]]},{"id":"b74d7012.48dc4","type":"hubitat device","z":"8f3f66e3.0cb258","name":"VD - NR - Average Outside Temp","server":"e03140c5.bed32","deviceId":"1480","attribute":"temperature","sendEvent":false,"x":1100,"y":220,"wires":[[]]},{"id":"d1ebd1ba.ea58c","type":"hubitat command","z":"8f3f66e3.0cb258","name":"VD - NR - Average Outside Temp","server":"e03140c5.bed32","deviceId":"1480","command":"setTemperature","commandArgs":"","x":1150,"y":320,"wires":[[]]},{"id":"97d7c164.7b124","type":"change","z":"8f3f66e3.0cb258","name":"","rules":[{"t":"set","p":"arguments","pt":"msg","to":"average","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":890,"y":320,"wires":[["d1ebd1ba.ea58c"]]},{"id":"cda84f51.2fb1b","type":"comment","z":"8f3f66e3.0cb258","name":"Reset Average before calculating. ","info":"","x":530,"y":200,"wires":[]},{"id":"2ee612c5.05179e","type":"switch","z":"8f3f66e3.0cb258","name":"","property":"parts.index","propertyType":"msg","rules":[{"t":"cont","v":"2","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":820,"y":380,"wires":[[]]},{"id":"39eb80e4.8801a","type":"batch","z":"8f3f66e3.0cb258","name":"","mode":"interval","count":"4","overlap":0,"interval":"1","allowEmptySequence":false,"topics":[],"x":700,"y":380,"wires":[["2ee612c5.05179e"]]},{"id":"fd6b93fd.4aec6","type":"inject","z":"8f3f66e3.0cb258","name":"Every 3 Mins","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"180","crontab":"","once":true,"onceDelay":"10","topic":"","payload":"","payloadType":"date","x":140,"y":310,"wires":[["2e5360f7.df8fd","5b0068a5.62cdd8","68eb2665.b53958"]]},{"id":"c8451ec8.11949","type":"comment","z":"8f3f66e3.0cb258","name":"Wait for reset to complete. ","info":"","x":130,"y":370,"wires":[]},{"id":"dbbef892.bdcab8","type":"function","z":"8f3f66e3.0cb258","name":"Average","func":"if (msg.payload.deviceId == \"78\") context.set('78',msg.payload.value);\nif (msg.payload.deviceId == \"2335\") context.set('2335',msg.payload.value);\nif (msg.payload.deviceId == \"2471\") context.set('2471',msg.payload.value);\nmsg.payload.average = (context.get('78') + context.get('2335') + context.get('2471') / 3);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":610,"y":320,"wires":[["97d7c164.7b124"]]},{"id":"7024595f.ea93d8","type":"function","z":"8f3f66e3.0cb258","name":"msg","func":"msg.reset = true;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":310,"y":220,"wires":[[]]},{"id":"c0da6dea.8576a","type":"hubitat config","name":"ZigBee Hub","usetls":false,"host":"","port":"80","appId":"2532","nodeRedServer":"","webhookPath":"/hubitat/webhook__","autoRefresh":true,"useWebsocket":false},{"id":"b1088aaa.95e9e8","type":"hubitat config","name":"Z-WaveHub","usetls":false,"host":"","port":"80","appId":"5","nodeRedServer":"","webhookPath":"/hubitat/webhook_","autoRefresh":true,"useWebsocket":false},{"id":"e03140c5.bed32","type":"hubitat config","name":"Apps Hub","usetls":false,"host":"","port":"80","appId":"899","nodeRedServer":"","webhookPath":"/hubitat/webhook___","autoRefresh":true,"useWebsocket":false}]

I removed a couple nodes because I don’t think they are needed now. There is also no need for the reset function node now and I have changed the inject node (are you sure Timestamp is the best to send?) to wait 10s on startup and then repeat as before. The 10s has been spoken about of late in the NR community.

Give it a go and I’ll be here to help with (the almost certain) bugs.

It’s given me 3 outputs.

I should be just one.

and average says “NaN” not sure that is correct.

Ah, that’s because it updates the average on each input message so maybe your Batch node takes care of that?

But I see a bigger problem in that “average: NaN” is not right. I’ll get on that.

It was missing a () pair. Paste this code over what’s in the function node.

if (msg.payload.deviceId == “78”) context.set(‘78’,msg.payload.value);
if (msg.payload.deviceId == “2335”) context.set(‘2335’,msg.payload.value);
if (msg.payload.deviceId == “2471”) context.set(‘2471’,msg.payload.value);
msg.payload.average = ((context.get(‘78’) + context.get(‘2335’) + context.get(‘2471’)) / 3 );
return msg;

Again, this is a crude, flow-specific chunk of code. It could be more generic by dynamically accepting any number of input messages but for a quick-and-dirty this does just fine…IMO

I really appreciate the help, but it is still given the same error. TBH I don;t want you to waste your time. If it is just outputting the same basic info as the “average” node, I can stick with it. I found a change code to limit the decimal place to 1 digit and it is working pretty well now.

I went over your expression and it looks right (mathematically) but I still get NaN.

Again I really appreciate it, and if it was easily done, I’d happily use it. I’m sure you got other fish to fry than writing code you can’t test.