[Solved] Check if a sensor has been inacative for specified time

I’m guessing the get history node is going to be my answer, but I’m trying to figure out a way to check if a sensor has been in a single state for a specified time. For example, I forget to arm the alarm some nights so I’d like at a specific time, say midnight, to check a few conditions (TV off, Foyer light off, and no motion for 20 minutes on Great Room Motion). The TV and foyer light are simple, but I can’t figure out how to check that GR motion was off for 20 minutes and if true continue on. Any suggestions?

I used a Change node where I set the payload to msg.data.new_state.timeSinceChangedMs. Not sure is all entities has this value, but worked for me.

Can you provide an example flow with this?

[
    {
        "id": "80233fa4.e52f9",
        "type": "tab",
        "label": "Missing status update",
        "disabled": false,
        "info": ""
    },
    {
        "id": "5b62d760.41a37",
        "type": "server-state-changed",
        "z": "80233fa4.e52f9",
        "name": "",
        "server": "80fe3d5c.28df9",
        "version": 1,
        "entityidfilter": "sensor.dor_inngang_linkquality",
        "entityidfiltertype": "exact",
        "outputinitially": true,
        "state_type": "str",
        "haltifstate": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "outputs": 1,
        "output_only_on_state_change": false,
        "x": 220,
        "y": 120,
        "wires": [
            [
                "f8c73d36.a1b"
            ]
        ]
    },
    {
        "id": "f8c73d36.a1b",
        "type": "change",
        "z": "80233fa4.e52f9",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "data.new_state.timeSinceChangedMs",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "enhet",
                "pt": "msg",
                "to": "topic",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 500,
        "y": 120,
        "wires": [
            [
                "ec9af978.078be"
            ]
        ]
    },
    {
        "id": "ec9af978.078be",
        "type": "function",
        "z": "80233fa4.e52f9",
        "name": "Convert to hours",
        "func": "msg.payload = msg.payload / 1000 / 60 / 60;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 690,
        "y": 120,
        "wires": [
            [
                "5123ff2b.1fa0d8"
            ]
        ]
    },
    {
        "id": "5123ff2b.1fa0d8",
        "type": "switch",
        "z": "80233fa4.e52f9",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gte",
                "v": "12",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 850,
        "y": 120,
        "wires": [
            [
                "a8bc810c.44ae08"
            ]
        ]
    },
    {
        "id": "a8bc810c.44ae08",
        "type": "function",
        "z": "80233fa4.e52f9",
        "name": "Linkquality 12 hours",
        "func": "msg.payload =\n{\n  \"data\": {\n    \"message\": \"\" + msg.enhet + \" has not updated in 12 hours.\",\n    \"data\": {\n      \"push\": {\n        \"category\": \"OK\"\n      }\n    }\n  }\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 1020,
        "y": 120,
        "wires": [
            [
                "47a911ea.6f728"
            ]
        ]
    },
    {
        "id": "47a911ea.6f728",
        "type": "api-call-service",
        "z": "80233fa4.e52f9",
        "name": "Hallvard ios 2.0",
        "server": "80fe3d5c.28df9",
        "version": 1,
        "service_domain": "notify",
        "service": "mobile_app_hallvard_sin_iphone",
        "entityId": "",
        "data": "",
        "dataType": "json",
        "mergecontext": "",
        "output_location": "payload",
        "output_location_type": "msg",
        "mustacheAltTags": false,
        "x": 1220,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "80fe3d5c.28df9",
        "type": "server",
        "z": "",
        "name": "Home Assistant VM",
        "legacy": false,
        "hassio": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": true
    }
]

data.new_state.timeSinceChangedMs is in milliseconds, so I use a function node to convert to hours (just to make it a bit more readable). Then I use a switch node to pass it to my iPhone when its over 12 hours since it changed.

It seems to work for me, but I’m not sure how often the value updates.

Thanks it works great for me.

History Stats is great for that. In Node Red i also use a different mechanic that works perfectly fine for me. I needed to check several items to be true (off for more than 30min) before something happend. Therefore i simply employed 30min timers and stop/start them based on the states of the sensor. So the timer only ends if the sensor has been off for 30min or more. Then if and only if all 3 timers sent their “done” message, my flow continues.

Sounds like this could work fine for your usecase as well :slight_smile:

Hi, I think I have the same challenge, only that I use one Step to check several binary sensor, after that I want to have the timer to run and check if the original state changed. So what does need to go into the function node, as well as how to setup the timer so that it worked for you @AlmostSerious.

Mine does not work at the moment

Cheers

My on/off function node looks like this:

newmsg = {};
if (msg.payload == "off")
{
    newmsg.payload = "True";
}else {
    newmsg.payload = "STOP";
    flow.set("motion", 0);
}

return newmsg;

the check if all of them are on is:

var remote = flow.get('remote');
var motion = flow.get('motion');
var home = flow.get('home');
var gaming = flow.get('gaming');
if (remote + motion + gaming == 3){
    msg.payload = "True";
} else {
    msg.payload = "False";
}

msg.motion = motion
msg.gaming = gaming
msg.remote = remote
msg.add = remote + motion + gaming

return msg;

cool thank you, I think I will go for a single check per state change. Thank you for the code, much better than mine :wink:

Hi, would you be able to share your flow on this please? many thanks.

[{"id":"f02c4b6f.26ade8","type":"server-state-changed","z":"7af83619.471ce8","name":"Gaming?","server":"2fba4297.e4145e","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_boolean.gaming","entityidfiltertype":"substring","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is_not","outputs":1,"output_only_on_state_change":true,"x":80,"y":140,"wires":[["89607b14.7a5528"]]},{"id":"f34deb25.bfa818","type":"server-state-changed","z":"7af83619.471ce8","name":"Remotes?","server":"2fba4297.e4145e","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"remote.living_room","entityidfiltertype":"substring","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is_not","outputs":1,"output_only_on_state_change":false,"x":80,"y":200,"wires":[["74e82fb.bf116d"]]},{"id":"89607b14.7a5528","type":"function","z":"7af83619.471ce8","name":"on/off?","func":"newmsg = {};\nif (msg.payload == \"off\")\n{\n    newmsg.payload = \"True\";\n}else {\n    newmsg.payload = \"STOP\";\n    flow.set(\"gaming\", 0);\n}\n\nreturn newmsg;","outputs":1,"noerr":0,"x":610,"y":140,"wires":[["8c544384.d436c"]]},{"id":"74e82fb.bf116d","type":"function","z":"7af83619.471ce8","name":"on/off?","func":"newmsg = {};\nif (msg.payload == \"off\")\n{\n    newmsg.payload = \"True\";\n}else {\n    newmsg.payload = \"STOP\";\n    flow.set(\"remote\", 0);\n}\n\nreturn newmsg;","outputs":1,"noerr":0,"x":610,"y":200,"wires":[["500830ed.c3b61"]]},{"id":"2668ac5c.bf6e14","type":"server-state-changed","z":"7af83619.471ce8","name":"Motion?","server":"2fba4297.e4145e","version":1,"entityidfilter":"group.motion","entityidfiltertype":"substring","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is_not","outputs":1,"output_only_on_state_change":false,"x":70,"y":80,"wires":[["dbdfdf7e.f482e"]]},{"id":"dbdfdf7e.f482e","type":"function","z":"7af83619.471ce8","name":"on/off?","func":"newmsg = {};\nif (msg.payload == \"off\")\n{\n    newmsg.payload = \"True\";\n}else {\n    newmsg.payload = \"STOP\";\n    flow.set(\"motion\", 0);\n}\n\nreturn newmsg;","outputs":1,"noerr":0,"x":610,"y":80,"wires":[["e4b19e32.28765"]]},{"id":"e4b19e32.28765","type":"stoptimer","z":"7af83619.471ce8","duration":"30","units":"Minute","payloadtype":"num","payloadval":"1","name":"","x":780,"y":80,"wires":[[],["aad5bf2b.ccee1"]]},{"id":"aad5bf2b.ccee1","type":"change","z":"7af83619.471ce8","name":"","rules":[{"t":"set","p":"motion","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":80,"wires":[["b9f88146.50729"]]},{"id":"8c544384.d436c","type":"stoptimer","z":"7af83619.471ce8","duration":"30","units":"Minute","payloadtype":"num","payloadval":"1","name":"","x":780,"y":140,"wires":[[],["46e5d536.2caaec"]]},{"id":"500830ed.c3b61","type":"stoptimer","z":"7af83619.471ce8","duration":"30","units":"Minute","payloadtype":"num","payloadval":"1","name":"","x":780,"y":200,"wires":[[],["5c186c96.8eebe4"]]},{"id":"46e5d536.2caaec","type":"change","z":"7af83619.471ce8","name":"","rules":[{"t":"set","p":"gaming","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":140,"wires":[["b9f88146.50729"]]},{"id":"5c186c96.8eebe4","type":"change","z":"7af83619.471ce8","name":"","rules":[{"t":"set","p":"remote","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":200,"wires":[["b9f88146.50729"]]},{"id":"b9f88146.50729","type":"function","z":"7af83619.471ce8","name":"All 3?","func":"var remote = flow.get('remote');\nvar motion = flow.get('motion');\nvar home = flow.get('home');\nvar gaming = flow.get('gaming');\nif (remote + motion + gaming == 3){\n    msg.payload = \"True\";\n} else {\n    msg.payload = \"False\";\n}\n\nmsg.motion = motion\nmsg.gaming = gaming\nmsg.remote = remote\nmsg.add = remote + motion + gaming\n\nreturn msg;","outputs":1,"noerr":0,"x":1170,"y":80,"wires":[["1fb4bbf5.889534"]]},{"id":"1fb4bbf5.889534","type":"switch","z":"7af83619.471ce8","name":"True?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"True","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":1290,"y":80,"wires":[["ad9cd7c3.4e0408"]]},{"id":"2fba4297.e4145e","type":"server","z":"","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]

There you go

1 Like

super thanks!