How to track temperature trend

I can't quite figure out how to do something in Node Red so thought I'd ask here.

I have a networked thermometer in my fridge and I'm trying to have Node Red send me an alert if the following two conditions are both met and I can't figure out how to write the function for the second condition:
Condition 1: The temperature is above 40F
Condition 2: Over a two hour period the 40+F temperature is not decreasing

Basically, for a working refrigerator, temperature varies as people open and close it or put hot food in to cool, meaning the temperature will spike but start to lower and exhibit a downward trend over time.

For a broken fridge, the temperature will either remain above 40F or trend upwards.

What I can't figure out is how to tell Node Red to watch the temperature over a specified period of time and alert me if it's not decreasing, i.e. I can't figure out how to have it tell me the trend rather than a given number.

Thanks in advance for any help.

1 Like

Store the current reading in a global and the current time state. Wait a period of time and check the current temp with the saved one. If it has gone down, update the global temp and time. If it has stayed the same or gone up and is 40 or greater, check to see if two hours has elapsed. If so sent an alert, if not wait some more.

1 Like

Good question and post nicely elaborated.

The flow proposed by Paul @zenofmud should work indeed.

I propose though a variation using only a watchdog timer.

Try this flow to see if it works like you want.

[{"id":"36d3c2c5.ae2cfe","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"48903220.9e496c","type":"trigger","z":"36d3c2c5.ae2cfe","op1":"","op2":"Higher more than 2 hours","op1type":"nul","op2type":"str","duration":"20","extend":false,"units":"s","reset":"","bytopic":"all","name":"Wait 2 hours (20s)","x":630,"y":240,"wires":[["3eb152ea.4f93fe"]]},{"id":"956894e1.7db488","type":"debug","z":"36d3c2c5.ae2cfe","name":"Notification 1","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":910,"y":160,"wires":[]},{"id":"12a655be.36062a","type":"change","z":"36d3c2c5.ae2cfe","name":"reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"reset","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":300,"wires":[["48903220.9e496c"]]},{"id":"d939862c.6050a8","type":"inject","z":"36d3c2c5.ae2cfe","name":"Sensor reading - 42 F","topic":"","payload":"42","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":220,"wires":[["1c40bc28.122904"]]},{"id":"1c40bc28.122904","type":"switch","z":"36d3c2c5.ae2cfe","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"40","vt":"num"},{"t":"lte","v":"40","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":390,"y":240,"wires":[["48903220.9e496c","37c301a3.3625ee"],["12a655be.36062a"]]},{"id":"b411cff5.6c3e4","type":"inject","z":"36d3c2c5.ae2cfe","name":"Sensor reading - 38 F","topic":"","payload":"38","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":260,"wires":[["1c40bc28.122904"]]},{"id":"3eb152ea.4f93fe","type":"debug","z":"36d3c2c5.ae2cfe","name":"Notification 2","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":830,"y":240,"wires":[]},{"id":"37c301a3.3625ee","type":"change","z":"36d3c2c5.ae2cfe","name":"Higher than 40 F","rules":[{"t":"set","p":"payload","pt":"msg","to":"higher than 40 F","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":160,"wires":[["77d86c84.e30dc4"]]},{"id":"77d86c84.e30dc4","type":"rbe","z":"36d3c2c5.ae2cfe","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":770,"y":160,"wires":[["956894e1.7db488"]]}]

I'm sure @Andrei has done it better than me.

But here's my flow.

[{"id":"e160bf63.005e38","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"25","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":480,"wires":[["a48b7956.f51108"]]},{"id":"fe1338c3.00e4c","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"26","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":430,"wires":[["a48b7956.f51108"]]},{"id":"343da64f.95298a","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"24","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":530,"wires":[["a48b7956.f51108"]]},{"id":"f2a0f4c.a975a08","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"40","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":330,"wires":[["a48b7956.f51108"]]},{"id":"c689a861.885c08","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"41","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":280,"wires":[["a48b7956.f51108"]]},{"id":"a955a7e8.cf2868","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"42","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":230,"wires":[["a48b7956.f51108"]]},{"id":"a48b7956.f51108","type":"function","z":"1d85b106.5dfcd7","name":"Dummy","func":"\nreturn msg;","outputs":1,"noerr":0,"x":320,"y":530,"wires":[["56cc6c42.c53dc4","1e5f8ed2.d8db39"]]},{"id":"56cc6c42.c53dc4","type":"change","z":"1d85b106.5dfcd7","name":"","rules":[{"t":"set","p":"last_temp","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":500,"y":470,"wires":[[]]},{"id":"1e5f8ed2.d8db39","type":"function","z":"1d85b106.5dfcd7","name":"monitor temperatures","func":"let previous = context.get('previous') || 40;\nlet hot = flow.get('hot') || 0;\n\nnode.warn(hot);\n\nlet t = parseInt(msg.payload);\nif (t < 40)\n{\n    //  Too HOT.\n    flow.set('hot', hot + 1);   //  Increment counter\n}\nif (t >= 40)\n{\n    //  Ok range.\n    if (hot > 0)\n    {\n        flow.set('hot', hot - 1);   //  Decrement counter\n    }\n}\nif (t > 45)\n{\n    //  Really good.\n    flow.set('hot',0);          //  All good.  Forget past.\n}\nif (hot > 3)                    //  Change the 3 as needed\n{\n    msg.payload = \"ALARM\";\n}\nreturn msg;\n","outputs":1,"noerr":0,"x":520,"y":530,"wires":[["cbbf3b89.c4d308"]]},{"id":"cbbf3b89.c4d308","type":"switch","z":"1d85b106.5dfcd7","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"ALARM","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":720,"y":530,"wires":[["20f2fdab.b72002","a5896cf9.e0b028"]]},{"id":"20f2fdab.b72002","type":"function","z":"1d85b106.5dfcd7","name":"DO WHAT IS NEEDED","func":"\nreturn msg;","outputs":1,"noerr":0,"x":930,"y":530,"wires":[[]]},{"id":"a5896cf9.e0b028","type":"debug","z":"1d85b106.5dfcd7","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":890,"y":460,"wires":[]},{"id":"c7307bb2.28f5f8","type":"comment","z":"1d85b106.5dfcd7","name":"Bad range","info":"","x":320,"y":480,"wires":[]},{"id":"7624f879.220f8","type":"comment","z":"1d85b106.5dfcd7","name":"Around the danger range","info":"","x":390,"y":330,"wires":[]},{"id":"6d865fea.346908","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"39","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":380,"wires":[["a48b7956.f51108"]]},{"id":"1cb5496d.9ad22f","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"49","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":170,"wires":[["a48b7956.f51108"]]},{"id":"ae98c64d.90d998","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"50","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":120,"wires":[["a48b7956.f51108"]]},{"id":"d407ab65.bc2ef","type":"inject","z":"1d85b106.5dfcd7","name":"","topic":"","payload":"51","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":70,"wires":[["a48b7956.f51108"]]},{"id":"6703ef5e.4cf508","type":"comment","z":"1d85b106.5dfcd7","name":"Good range  COLD","info":"","x":380,"y":120,"wires":[]}]

And his use of the delay node (and reset) to handle the 2 hour time is way better than how I did it.

Thanks so much! I think I want to try to combine @Trying_to_learn's function with @Andrei's flow because the notion is I want it to reset as long as the temperature is dropping even if it's above 40, i.e. if my thermostat reads 50 but twenty minutes later reads 49 then I know the fridge itself is working properly (my main concern) and that there's probably just hot food in there.

Just to make sure I understand the function properly: it looks at the current temp (actually via MQTT in my case) and creates a variable "t". It then creates a flow variable called "hot". It increments "hot" up or down depending on how "t" compares to the previous "t"?

I guess what I'm not clear on is where/how the previous "t" is being stored for comparison. Let's say my inject node is checking my MQTT reading every 10 minutes. At 10am it detects the temperature is 38. At 10:10am it detects the temperature is 42. I understand "hot" would be greater than zero at that point (meaning temp is going up) but where in the function does it store the 10am temperature so it can be compared to the 10:10am temperature? Thanks.

The previous t is stored in what is called flow.context.

That is done in the node flow.last_temp. The yellow one just to the right of all the inject nodes.

This allows variables to be shared between multiple nodes on the same flow - in this case. Thus: flow.

The function node below it load the value and compares the part and present one.

I am sorry it is a bit of a mess. I am really not with it. @Andrei 's version is better than mine in quite a few ways.

Mine is expecting timed inputs every..... (how ever long). You need to determine how many messages are received in the time you want to call critical for temperature change.

@andrei 's is better controlled in that way because he uses a delay node which I didn't think to use.

You also have to tweak the values. But I guess you have to do that in any case.
Just mine aren't as easily seen/defined in retrospect.

So if you are taking readings every 10 minutes: so 2 hours is..... 12 readings.

As I said, mine is a mess. I just bashed it together to show you a methodology of what to do.
I don't think it is really useful.

(I'll shut up, as I think I am digging a hole.)

Yes I saw flow.last_temp but I just didn't see it referenced again in the function so wasn't sure if I was missing something. And yes I like the Delay function but if I'm understanding Andrei's properly, it's tracking either above or below 40 rather than tracking an upward or downward trend. Unless I'm misreading, in Andrei's, if the temperature drops from 50 to 45 over two hours, it will trigger an alarm whereas, because the trend is heading down, I wouldn't want an alarm in that case.

Probably because I goofed in using it.

I think that was the second or third iteration of what I finally posted, so it may be a leftover artefact.

Err, no.

If it goes from 50 to 45, that is getting .... Ah! Deg F. Sorry I live with Celsius and was thinking negative numbers.

Ok, I think that is an honest mistake by @Andrei All you would need to do is reverse the logic of what it does.

No. It is from -50 to -45 looking at the screen shots.
That is getting warmer.
Ok, looking again it is -42F and -38F as per:

Or did I miss something. Sorry thinking if Deg F and the values is not easy.
For me keeping food cold is from 0 (freezing) to 3 degrees.
Above that is the danger zone.

I/we are using wide variations only to show you examples. (Well, I am/was)

I've been awake for ...... 23-6 = 17 hours. And spent 3 of those bashing my head against the wall with python and a node not working as expected.

Sorry, but I think my usefulness is at an end for today.

I just looked at the flow @Andrei posted.

It works how it is described.

Where are you getting the 50 and 45 values?

His are 38 and 42.


You are wanting an alarm if it is OVER 40 Deg F for more than 2 hours.

So given: It is 50 and drops to 45 in 2 hours, you would want an alarm.

Only if it is BELOW 40 do you not want an alarm.


(I can see you are replying, so I'm staying online to remain helpful as much as I can)

HA - get some sleep! But no: I want to track the TREND regardless of actual temperature. If the trend is downward, I don't want an alarm even if it starts at 10 Celsius as long as the temp is going downward over time. If it's either staying at 4C or, worse, going up over a two hour period, then I want an alarm. Make sense?

In other words, if CurrentTemp is below 4C, do nothing; if CurrentTemp > 4C AND (temp-from-2-hours-ago) - (CurrentTemp) <= 0 then alarm else if CurrentTemp > 4C AND (temp-from-2-hours-ago) - (CurrentTemp) > 0 keep tracking this until CurrentTemp drops below 4C again.

Now if I could only figure out how to translate that into a Node Red function, I'd be good! :slight_smile:

No worries.

You are kind of changing the rules. No problems.
But you did state that if the temperature is above 40 degrees after a 2 hour period, you wanted an alarm.

Below 40 you weren't too worried.

So yes, I can get you are looking at the trend but that is less important than the actual temperature.

Indulge me an example with your new method:
Each reading is 20 minutes apart.
1 - 35 deg
2 - 37 deg
3 - 40 deg 1 hour gone.
4 - 35 deg
5 - 40 deg
6 - 50 deg 1 hour gone.
7 - 49 deg
8 - 48 deg
9 - 47 deg 1 hour gone.
10 - 46 deg
11 - 44 deg
12 - 41 deg 1 hour gone.
13 - 43 deg
14 - 42 deg
15 - 41 deg 1 hour gone.

You wouldn't want to eat that food. Too long above 40.
So you need the underlying rule that if it is above 40 for more than 2 hours: set off an alarm.

Can you see the importance of the fact that if it is grater that 40 for more than 2 hours the alarm goes off, irrespective of the trend?

And with that, I think I will hit the sack.

Until next time.

Yes I see that. But actually I'm not really looking at the food safety part in particular, just the temperature trend. If you want to know specifically why I'm tracking this :): I have a fridge that where the compressor fan occasionally gets stuck and I have to go give it a good smack to get it running again. When the fan stops, the temperature trend goes upwards. When the the fan is working, it's like any other fridge, i.e. temp rises when door is opened or hot food is put in but trends downwards over time. In a nutshell, I want an alarm to go off if the fan is stopped so I can go smack it and the way I can track that is if the fridge temp is rising over time.

The place where I'm getting stuck is how do I tell Node Red to store the current temp as a variable for 2 hours then run the if/then I wrote out above on the stored variable then replace that stored variable with the new current temperature?

I know how to have NR store the current temperature as a variable but I don't know how to have it sit on that variable for a specific time period then use it in a function then replace the variable with the updated current temp so it can run the function again 2 hours later.

Sounds to me a new fridge would be the proper solution

1 Like

Once you have got the flow going are you going to attach a hammer to a servo to give it a bash when it decides it has stopped?

1 Like

Go back and read my first post. You might need a 'current-reading temp/time' pair and a 'previous-reading temp/time' pair,

Write out the logic in sentences explaining what needs to be done and tehn use that to code a function or connect nodes together to solve the issue.

Cool, home automation at it’s best: A lot of engineering for a indicator to kick an broken fan in the a.... At least that is what humans are still good for in the future. Until someone invents a “fridge fan smacking device” controlled by mqtt. :wink:

But beside the fun, a friend lost his hole kitchen because of a dust explosion behind his fridge caused by a spark from a damaged relay. So perhaps consider to repair the fan.

I guess I understand. Indeed the flow from my first reply will not help you. I need to think over but it is too late already in my time zone.

Just a question. How would it possible to track a temperature trend if anyone can open the fridge door at random ? Is it a good approach to track a trend given that the temperature can be disrupted at any time, several times during the time frame ?

I wonder if this use case is similar to a previous one in this forum about tracking humidity trend. The solution was not trivial at all.

:laughing: How about adding a sensor to measure the fan RPM plus a vibration sensor in the top of the compressor plus a sensor to detect every time the fridge door is opened plus a power measurement device to compare power consumption versus inside temperature in the fridge? That upgrade package would give you a smart fridge.


HA because, also in the spirit of home automation, I'm making do with the cheap stuff I have sitting around, one of which was a Zigbee temp/humidity sensor.

My thought at this point is to check the temp and store it as a flow variable, have a delay node for 2 hours, check again and store the new temp as a different variable then, since they're both integers, do a simple function comparing them - if "old temp" minus "new temp" is greater than 0 then all is good; otherwise I need to go smack the fan. I think that would work though I'm not sure? Specifically this:

[{"id":"e49a6050.5ee198","type":"inject","z":"9fb28e31.fdea6","name":"Check Fridge Temp","topic":"","payload":"FridgeTemp","payloadType":"global","repeat":"7200","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":480,"wires":[["28f695e0.321042"]]},{"id":"28f695e0.321042","type":"switch","z":"9fb28e31.fdea6","name":"Less then 40?","property":"FridgeTemp","propertyType":"global","rules":[{"t":"lte","v":"40","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":420,"y":480,"wires":[[],["9f586fc8.613798"]]},{"id":"9f586fc8.613798","type":"change","z":"9fb28e31.fdea6","name":"","rules":[{"t":"set","p":"FridgeTemp","pt":"flow","to":"FridgeTemp","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":600,"wires":[["741faea.899cad"]]},{"id":"741faea.899cad","type":"delay","z":"9fb28e31.fdea6","name":"Delay 119 minutes","pauseType":"delay","timeout":"119","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":550,"y":660,"wires":[["2d7e3761.bf6db"]]},{"id":"2d7e3761.bf6db","type":"switch","z":"9fb28e31.fdea6","name":"Did the temp go down?","property":"FridgeTemp","propertyType":"global","rules":[{"t":"lt","v":"FridgeTemp","vt":"flow"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":650,"y":500,"wires":[["69c427f3.962e38"],["953f1916.505778"]]},{"id":"69c427f3.962e38","type":"change","z":"9fb28e31.fdea6","name":"","rules":[{"t":"set","p":"FridgeStatus","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":840,"y":400,"wires":[[]]},{"id":"953f1916.505778","type":"change","z":"9fb28e31.fdea6","name":"","rules":[{"t":"set","p":"FridgeStatus","pt":"global","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":560,"wires":[[]]}]


That will give you false positives

  • if the temperature is constant
  • you will have some sensor noise

Minimum is to do the comparison only if the temperature is above a certain threshold.