Efficient way to compare/check when an entity was last changed?

I have a button next to my furnace air filter. When I change the filter, I press the button which updates a input_datetime helper to the current date/time.

I would like an efficient way to check this date to current date/time and have an alert sent to me if the two are more than 45 days apart.

For other things, I’ve just used a trigger node and had it wait, but that was for hours, not days. I feel like its not good to have a trigger node counting for 45 days.

Is there some sort of compare I can do? And if it passes the compare, it would then send my phone an alert. I can handle the alert part, I just need help with the compare part.

[{"id":"df526c3aff47686a","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"98701981c0ba49b4","type":"inject","z":"df526c3aff47686a","name":"Simulate button press","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":985,"y":225,"wires":[["f2597808097bf36e"]]},{"id":"ffa393f8100d7dcf","type":"debug","z":"df526c3aff47686a","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1590,"y":330,"wires":[]},{"id":"f2597808097bf36e","type":"moment","z":"df526c3aff47686a","name":"","topic":"","input":"payload","inputType":"msg","inTz":"ETC/UTC","adjAmount":0,"adjType":"days","adjDir":"add","format":"YYYY-MM-DD HH:mm:ss","locale":"en","output":"payload","outputType":"msg","outTz":"America/Chicago","x":1220,"y":225,"wires":[["6371f7bd722fabf8"]]},{"id":"6371f7bd722fabf8","type":"api-call-service","z":"df526c3aff47686a","name":"Set input_datetime to current date & time","server":"ab3e2c53.e3491","version":5,"debugenabled":false,"domain":"input_datetime","service":"set_datetime","areaId":[],"deviceId":[],"entityId":["input_datetime.hvac_filter_last_changed"],"data":"{\"datetime\":\"{{payload}}\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1540,"y":225,"wires":[[]]},{"id":"07f4164a677307ec","type":"inject","z":"df526c3aff47686a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":960,"y":330,"wires":[["59de48367b1b0db5"]]},{"id":"59de48367b1b0db5","type":"api-current-state","z":"df526c3aff47686a","name":"","server":"ab3e2c53.e3491","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_datetime.hvac_filter_last_changed","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1270,"y":330,"wires":[["ffa393f8100d7dcf"]]},{"id":"ab3e2c53.e3491","type":"server","name":"AvilaSmartHome","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

I think this is what you are asking for:

trigger:
  - platform: template
    value_template: >-
      {{(as_timestamp(now()) - as_timestamp(states.sensor.button_hvac_filter.last_changed)) > 3888000 }}

However, I would be concerned that the last_changed of that button could get reset during a reboot or update, especially for such a long time period. Sometimes if entities go unavailable, it can mess with those attributes.

My thought for making this more robust would be to run a script when you press that button to save the current date into a helper.

sequence:
  - action: input_datetime.set_datetime
    data:
      datetime: "{{now()}}"
    target:
      entity_id: input_datetime.test

and then trigger based on that value

trigger:
  - platform: template
    value_template: >-
      {{(as_timestamp(now()) - as_timestamp(states.input_datetime.test.state)) > 3888000 }}

I am actually working on something similar to this using a to-do list. If I ever get back around to that, I will try to remember and share it here as well.

Edit: I just realized this is tagged Node-RED…I have not used that before. Hope this is still at least somewhat useful for you.

2 Likes

@mwaterbu Thank you for suggestion. I amended my original post. I like your idea of adding a input_datetime helper. So I did that, but i still need node red help in comparing the value in the helper to the current date.

When you press the button set the date time helper to 45 days later. Use the date time helper in a time node to trigger the notification. Use the time formatter node to add 45 days to the time stamp.

image

[{"id":"55a025b417f03531","type":"api-call-service","z":"0a325c35fc29f44e","name":"set date time","server":"6b1110b5.183a4","version":6,"debugenabled":false,"action":"input_datetime.set_datetime","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_datetime.testdatetime"],"labelId":[],"data":"{\"datetime\": payload }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","domain":"input_datetime","service":"set_datetime","x":570,"y":1220,"wires":[[]]},{"id":"823397682ab455ce","type":"moment","z":"0a325c35fc29f44e","name":"add 45 days","topic":"","input":"payload","inputType":"msg","inTz":"ETC/UTC","adjAmount":"45","adjType":"days","adjDir":"add","format":"YYYY-MM-DD HH:mm:ss","locale":"En","output":"payload","outputType":"msg","outTz":"America/New_York","x":370,"y":1220,"wires":[["55a025b417f03531"]]},{"id":"a180fa6d46315b21","type":"server-state-changed","z":"0a325c35fc29f44e","name":"button press","server":"6b1110b5.183a4","version":6,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["input_boolean.my_test_toggle"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"str","ifState":"on","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"date"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":150,"y":1220,"wires":[["823397682ab455ce"],[]]},{"id":"6b1110b5.183a4","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"id","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

edit: to set a specific time change HH:mm:ss to the time of day(24 hour clock) in the output section. If you wanted the notification 45 days later @ 1:00pm use

image

1 Like

@Mikefila

I think that’ll work! Thanks!

1 Like

@Mikefila

I am getting back around to truly implementing this and I am getting some odd results in testing. Half the time I try to add 1 day to the datetime, the state node errors out. And my node red debug is saying the date is in the past. But if you look at the date time helper, it is NOT in the past. It is one day in the future.

But then if I just change the state node to some other entity, then back to the right one, it will update and say the date that it just told me was in the past, is now actually, rightfully, in the future.

See video below. And here is the code. (the comment on the nodes say 30 days, but its currently set to 1 day just because i was testing)

[{"id":"941be3a5d2e1fdb5","type":"tab","label":"Flow 2","disabled":false,"info":"","env":[]},{"id":"57364d8579367e03","type":"inject","z":"941be3a5d2e1fdb5","name":"Simulate button press","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"iso","payloadType":"date","x":135,"y":270,"wires":[["101f9485bd171a33"]]},{"id":"101f9485bd171a33","type":"moment","z":"941be3a5d2e1fdb5","name":"add 30 days","topic":"","input":"payload","inputType":"msg","inTz":"ETC/UTC","adjAmount":"1","adjType":"days","adjDir":"add","format":"YYYY-MM-DD HH:mm:ss","locale":"En","output":"payload","outputType":"msg","outTz":"America/Chicago","x":350,"y":270,"wires":[["60c551cde377a1fc"]]},{"id":"60c551cde377a1fc","type":"api-call-service","z":"941be3a5d2e1fdb5","name":"Set filter due date time","server":"ab3e2c53.e3491","version":7,"debugenabled":false,"action":"input_datetime.set_datetime","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_datetime.hvac_filter_change_due_date","input_datetime.hvac_filter_change_due_date_nag"],"labelId":[],"data":"{\"datetime\": payload }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":false,"domain":"input_datetime","service":"set_datetime","x":560,"y":270,"wires":[[]]},{"id":"50e034759879a719","type":"ha-time","z":"941be3a5d2e1fdb5","name":"Trigger on HVAC filter change due date","server":"ab3e2c53.e3491","version":3,"exposeAsEntityConfig":"","entityId":"input_datetime.hvac_filter_change_due_date","property":"state","offset":"0","offsetType":"num","offsetUnits":"minutes","randomOffset":false,"repeatDaily":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"date"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"sunday":true,"monday":true,"tuesday":true,"wednesday":true,"thursday":true,"friday":true,"saturday":true,"x":165,"y":405,"wires":[["36274334734556bf"]]},{"id":"36274334734556bf","type":"api-call-service","z":"941be3a5d2e1fdb5","name":"Alert Jims phone","server":"ab3e2c53.e3491","version":7,"debugenabled":false,"action":"notify.notify_jim","floorId":[],"areaId":[],"deviceId":[],"entityId":[],"labelId":[],"data":"{\"title\":\"\",\"message\":\"Change HVAC filter\",\"data\":{\"sticky\":\"false\",\"priority\":\"Normal\",\"ttl\":0,\"tag\":\"HVAC filter\",\"color\":\"blue\",\"notification_icon\":\"mdi:air-filter\"}}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"date"}],"queue":"none","blockInputOverrides":false,"domain":"notify","service":"notify_jim","x":450,"y":405,"wires":[["561048dd42d86ec0"]]},{"id":"561048dd42d86ec0","type":"moment","z":"941be3a5d2e1fdb5","name":"Add 12 hours","topic":"","input":"payload","inputType":"msg","inTz":"ETC/UTC","adjAmount":"1","adjType":"days","adjDir":"add","format":"YYYY-MM-DD HH:mm:ss","locale":"En","output":"payload","outputType":"msg","outTz":"America/Chicago","x":640,"y":405,"wires":[["43b54680bbabcd45"]]},{"id":"43b54680bbabcd45","type":"api-call-service","z":"941be3a5d2e1fdb5","name":"Set filter due date NAG time","server":"ab3e2c53.e3491","version":7,"debugenabled":false,"action":"input_datetime.set_datetime","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_datetime.hvac_filter_change_due_date_nag"],"labelId":[],"data":"{\"datetime\": payload }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":false,"domain":"input_datetime","service":"set_datetime","x":855,"y":405,"wires":[[]]},{"id":"ab3e2c53.e3491","type":"server","name":"AvilaSmartHome","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

I don’t experience that error. The only thing I can suggest is deleting that time node, deploy and drag out a new one.

Hmmm, I created new flow, new datetime helpers and I see the same thing. It work the first time, then errors out after.

Can’t you use Unix Epoch time for this?
Like storing (by a button) the Epoch value in a remanent variable/file, and substract the actual Epoch value with stored value.
You wil end with a value which represents the seconds between storage ↔ actual.

As far as I know the simpletime node can generate Epoch values (node-red-contrib-simpletime).
Also you can use A Date/Time Formatter - node for more time convertion possibilities (node-red-contrib-moment).

I hope this helps…

EDIT: FYI, I checked the inject-node which can inject milliseconds since epoch (timestamp). You can use it to inject it every second, minute etc…
EDIT2: FYI, the milliseconds since epoch is also available in the button nodes.
EDIT3: FYI, an Unix Epoch Clock.

I’ve just had something similar after updating to the latest version of NR.
Make sure all your nodes are upto date in the palette and restart Node Red.
It seemed to work for me.
HTH

No go. Still errors.

@Domoticon
I understand where your going but I wont be able to implement it.

It doesn’t solve the error but you could, instead of saving it to a datetime helper, save it to a local calendar. Then use the calendar node for a trigger.

It just so weird because once it errors out, I can inject into it as much as I want and it’ll never work again until I change the enitity to something else, then back to the right one. Then I can inject every second and it works.

FYI I posted bug report for this as it has to be a bug.

Thanks Jim - I’ve also had similar issues over the last few days which seem to have come around since updating to v4.0.2

This was fixed in node-red-contrib-home-assistant-websocket v0.73.0

@Mikefila now that I got this working for its intended purpose, I would like to use it for another purpose.

That being, using it as a timer for my garden watering system.

See the graphic and attached flow. What I have setup is a button, an input_number, and an input_datetime. What I would like it when the button is pressed, it looks at the value of input_number and then tranfers that to the date/time formatter node to be put into the input_datetime.

My question is; is there a way to have the value of the input_number be used for the date/time formatter node? So if input_number = 5, I want the date/time formatter node set the input_datetime 5 minutes in the future.

[{"id":"f49cc0858a8a347b","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"9701bcb50cf3c10a","type":"moment","z":"f49cc0858a8a347b","name":"Insert input_number as minutes","topic":"","input":"payload","inputType":"msg","inTz":"ETC/UTC","adjAmount":"10","adjType":"minutes","adjDir":"add","format":"YYYY-MM-DD HH:mm:ss","locale":"En","output":"payload","outputType":"msg","outTz":"America/Chicago","x":740,"y":240,"wires":[["291773795a92be09"]]},{"id":"6a312873eb2d38b1","type":"server-state-changed","z":"f49cc0858a8a347b","name":"Water garden button pressed","server":"ab3e2c53.e3491","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_button.start_garden_watering"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":255,"y":240,"wires":[["c7b454e98bc5703e"]]},{"id":"c7b454e98bc5703e","type":"api-current-state","z":"f49cc0858a8a347b","name":"Check minutes","server":"ab3e2c53.e3491","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_number.garden_watering_timer","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":490,"y":240,"wires":[["9701bcb50cf3c10a"]]},{"id":"291773795a92be09","type":"api-call-service","z":"f49cc0858a8a347b","name":"Set watering stop time","server":"ab3e2c53.e3491","version":7,"debugenabled":false,"action":"input_datetime.set_datetime","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_datetime.garden_watering_stop_time"],"labelId":[],"data":"{\"datetime\": payload }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":false,"domain":"input_datetime","service":"set_datetime","x":1000,"y":240,"wires":[[]]},{"id":"ab3e2c53.e3491","type":"server","name":"AvilaSmartHome","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

It doesn’t look like the node accepts a variable for the adjustment value. You can though use the moment func as jsonata. This will add days based on the number in the input helper.

$moment().add($entity().state, 'd')

edit: removed extra word