Windows open and doors open for X minutes

I'm creating a whole house fan module, and want the whole house fan to turn on when more than 2 window is open. I'd like to have it also add to the total count if a door is open for Y minutes.

So if I have one window open, and open the door, it will not turn on. but if the door is open for 5 minutes the open count is now 2, so the whole house fan turns on. This way if the doors open and close it doesn't count them, but if we keep the door open for a period of time (to air out the house) then it sees the door open for a longer period of time so it adds to the count.

Appreciate your guidance!

Here's an example of the code I have, and a message with what I'm trying to accomplish.

[{"id":"a607dc72.c9d2f","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"9b341da.ef03de","type":"api-current-state","z":"a607dc72.c9d2f","name":"Master Temp","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":true,"entity_id":"sensor.master_bedroom_temperature","state_type":"num","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":130,"y":180,"wires":[["d813ef79.002dc"]]},{"id":"d813ef79.002dc","type":"change","z":"a607dc72.c9d2f","name":"inside_temp","rules":[{"t":"change","p":"topic","pt":"msg","from":"sensor.master_bedroom_temperature","fromt":"str","to":"inside_temp","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":130,"y":240,"wires":[["38f1a4f.748ed5c"]]},{"id":"499ad131.2a941","type":"api-current-state","z":"a607dc72.c9d2f","name":"Outside Temp","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":true,"entity_id":"sensor.dark_sky_temperature","state_type":"num","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":140,"y":300,"wires":[["a1360990.c51ed8"]],"outputLabels":["outside_temperature"]},{"id":"a1360990.c51ed8","type":"change","z":"a607dc72.c9d2f","name":"outside_temp","rules":[{"t":"change","p":"topic","pt":"msg","from":"sensor.dark_sky_temperature","fromt":"str","to":"outside_temp","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":140,"y":360,"wires":[["38f1a4f.748ed5c"]]},{"id":"38f1a4f.748ed5c","type":"join","z":"a607dc72.c9d2f","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":330,"y":240,"wires":[["498765d7.32c73c"]]},{"id":"498765d7.32c73c","type":"function","z":"a607dc72.c9d2f","name":"Compare","func":"var outside\noutside = Number(msg.payload.outside_temp)\nvar inside\ninside = Number(msg.payload.inside_temp)\nmsg.topic = 'speed'\nif (outside < inside) \n{\n return [msg, null]\n} else \n{    \n  return [null, msg]\n}\nreturn msg;","outputs":2,"noerr":0,"initialize":"","finalize":"","x":340,"y":300,"wires":[["4d407c0f.430c84","fc73fff1.6d54b"],[]]},{"id":"4d407c0f.430c84","type":"api-current-state","z":"a607dc72.c9d2f","name":"Master Windows","server":"2ee084e7.d905bc","version":1,"outputs":2,"halt_if":"1","halt_if_type":"num","halt_if_compare":"gte","override_topic":false,"entity_id":"sensor.window_count_master","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":570,"y":180,"wires":[["bd3347dc.b91028"],[]]},{"id":"fc73fff1.6d54b","type":"api-current-state","z":"a607dc72.c9d2f","name":"Master Doors","server":"2ee084e7.d905bc","version":1,"outputs":2,"halt_if":"1","halt_if_type":"num","halt_if_compare":"gte","override_topic":false,"entity_id":"sensor.door_count_master","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":560,"y":240,"wires":[["af7bb03c.e15b1"],[]]},{"id":"af7bb03c.e15b1","type":"stoptimer","z":"a607dc72.c9d2f","duration":"5","units":"Minute","payloadtype":"num","payloadval":"","name":"","x":540,"y":320,"wires":[["3263c44d.89e73c","bd3347dc.b91028"],[]]},{"id":"bd3347dc.b91028","type":"switch","z":"a607dc72.c9d2f","name":"Open Count","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"5","vt":"str"},{"t":"eq","v":"4","vt":"str"},{"t":"gte","v":"2","vt":"str"},{"t":"eq","v":"1","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":870,"y":220,"wires":[[],[],[],[]]},{"id":"3263c44d.89e73c","type":"debug","z":"a607dc72.c9d2f","name":"Add Door count to Window Count","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":850,"y":300,"wires":[]},{"id":"c23a7b5b.0589c8","type":"inject","z":"a607dc72.c9d2f","name":"Run","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":120,"wires":[["9b341da.ef03de","499ad131.2a941"]]},{"id":"2ee084e7.d905bc","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

I've done the delayed warning processing a few different ways in the past:

Basically using cascading triggers gives me an initial warning after 2 min, another after 5 and another after 10 but only if the flow hasn't received a close notification in the meantime.

Can you share the code you used?

I haven't played much with the trigger, and the cascading trigger sounds interesting. Would love to see/understand how it works in your environment. I assume in the case you have, that if trigger 5min runs, I would then add to 1 to the count.

This is the json node to the change node in the top flow. Should be enough to show you how it works.

[{"id":"138bb6f.73fd849","type":"json","z":"d84f1dc7.1db28","name":"","x":510,"y":100,"wires":[["2a3146e2.ac6f9a","8002b126.02ed6","c22836b9.40e4c8","5216da16.f592f4"]]},{"id":"8002b126.02ed6","type":"trigger","z":"d84f1dc7.1db28","name":"","op1":"Door Opened","op2":"Door has been open for 2 minutes, please check it.","op1type":"str","op2type":"str","duration":"2","extend":false,"units":"min","reset":"Off","outputs":1,"x":450,"y":180,"wires":[["817df55b.0e6b78"]]},{"id":"c22836b9.40e4c8","type":"trigger","z":"d84f1dc7.1db28","name":"","op1":"","op2":"FREEZER DOOR HAS BEEN OPEN FOR 5 minutes, PLEASE CHECK IT NOW!","op1type":"nul","op2type":"str","duration":"5","extend":false,"units":"min","reset":"Off","outputs":1,"x":450,"y":220,"wires":[["817df55b.0e6b78"]]},{"id":"5216da16.f592f4","type":"trigger","z":"d84f1dc7.1db28","name":"","op1":"","op2":"DANGER!! THE FREEZER DOOR HAS BEEN OPEN FOR 10 minutes, PLEASE CHECK IT NOW!","op1type":"nul","op2type":"str","duration":"10","extend":false,"units":"min","reset":"Off","outputs":1,"x":450,"y":260,"wires":[["817df55b.0e6b78"]]},{"id":"817df55b.0e6b78","type":"change","z":"d84f1dc7.1db28","name":"Topic: Freezer Door Open","rules":[{"t":"set","p":"topic","pt":"msg","to":"Freezer Door Open","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":220,"wires":[["68a6bd32.cdeac4"]]}]

Appreciate you sending this over. I kinda see how its working but am not seeing how to turn it off.
IE if the door is open for (testing purposes) less then 5 seconds, it says door open, once its over 5 seconds it gives the message Door been open for 5 seconds), and then over 15 seconds Danger open over 15. When the door closes, it does not change the payload to null. It keeps the payload from the last one till the door re-opens.

[{"id":"dab6ca13.ec0618","type":"trigger","z":"d4d11aac.7a6398","name":"","op1":"","op2":"DANGER!! THE FREEZER DOOR HAS BEEN OPEN FOR 15 seconds, PLEASE CHECK IT NOW!","op1type":"nul","op2type":"str","duration":"15","extend":false,"overrideDelay":false,"units":"s","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":270,"y":380,"wires":[["518a1ef4.97692"]]}]

should I join the binary switch so that when off is submitted, to change payload to off? ultimately I'm going to use this to add to open windows. Door open X minutes, count goes up. door closes, count goes to only open windows.

In the case shared, you will note the following setting in the trigger node:

My magnetic door sensor sends an "On" msg to MQTT when open and "Off" when closed. So in this case, the trigger is cancelled once the Off signal is received.

You must have some kind of cancel/close signal of course if you are going to usefully have warnings about what is open/closed.

I think you are saying that, in your case, the sensor repeatedly sends open messages while open and nothing while closed? If that is the case, you will need a different process where the trigger node is used to send an Off/Closed msg x seconds after receiving an open msg and using the "extend delay if new message arrives" flag so htat each new open msg delays the sending of the close msg. Then you have reproduced what my sensors already do.

Here's what I did, which appears to be working. Not as clean as I had hoped for:

[{"id":"d4d11aac.7a6398","type":"tab","label":"Flow 5","disabled":false,"info":""},{"id":"c87456f8.10afa8","type":"api-current-state","z":"d4d11aac.7a6398","name":"Main Windows","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"gte","override_topic":false,"entity_id":"sensor.window_count_main","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":360,"y":220,"wires":[["e586fbba.4897c8"]]},{"id":"794fa680.3b20c8","type":"trigger","z":"d4d11aac.7a6398","name":"","op1":"Door Opened","op2":"open","op1type":"str","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"min","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":530,"y":280,"wires":[["3456d5f2.00a88a"]]},{"id":"b05df403.1c5a18","type":"trigger","z":"d4d11aac.7a6398","name":"","op1":"Door Opened","op2":"open","op1type":"str","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"min","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":530,"y":760,"wires":[["e5a10cc9.2cc6a"]]},{"id":"f15b5a70.a17df8","type":"trigger","z":"d4d11aac.7a6398","name":"","op1":"Door Opened","op2":"open","op1type":"str","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"min","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":530,"y":400,"wires":[["87d4e2ac.bed1c"]]},{"id":"79b26671.f01f08","type":"trigger","z":"d4d11aac.7a6398","name":"","op1":"Door Opened","op2":"open","op1type":"str","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"min","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":530,"y":640,"wires":[["7837bc35.0c8ed4"]]},{"id":"b57eaa7b.7a2578","type":"trigger","z":"d4d11aac.7a6398","name":"","op1":"Door Opened","op2":"open","op1type":"str","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"min","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":530,"y":520,"wires":[["62ef7b3a.b5c514"]]},{"id":"3456d5f2.00a88a","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"var opencount = opencount;\nvar runonce = runonce;\nif (runonce == \"null\")\n{\n    opencount = 0;\n    msg.payload = (opencount);\n    runonce = 1;\n}\nif (msg.payload == 'open')\n{\n    opencount = 1;\n}\nelse\n{\n    opencount = 0;\n}\nmsg.topic = 'den';\nmsg.payload = (opencount);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":520,"y":340,"wires":[["5fcc828a.c8c55c"]]},{"id":"e5a10cc9.2cc6a","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"var opencount = opencount;\nvar runonce = runonce;\nif (runonce == \"null\")\n{\n    opencount = 0;\n    msg.payload = (opencount);\n    runonce = 1;\n}\nif (msg.payload == 'open')\n{\n    opencount = 1;\n}\nelse\n{\n    opencount = 0;\n}\nmsg.topic = 'kitchen';\nmsg.payload = (opencount);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":520,"y":820,"wires":[["5fcc828a.c8c55c"]]},{"id":"87d4e2ac.bed1c","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"var opencount = opencount;\nvar runonce = runonce;\nif (runonce == \"null\")\n{\n    opencount = 0;\n    msg.payload = (opencount);\n    runonce = 1;\n}\nif (msg.payload == 'open')\n{\n    opencount = 1;\n}\nelse\n{\n    opencount = 0;\n}\nmsg.topic = 'dining';\nmsg.payload = (opencount);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":520,"y":460,"wires":[["5fcc828a.c8c55c"]]},{"id":"7837bc35.0c8ed4","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"var opencount = opencount;\nvar runonce = runonce;\nif (runonce == \"null\")\n{\n    opencount = 0;\n    msg.payload = (opencount);\n    runonce = 1;\n}\nif (msg.payload == 'open')\n{\n    opencount = 1;\n}\nelse\n{\n    opencount = 0;\n}\nmsg.topic = 'guest';\nmsg.payload = (opencount);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":520,"y":700,"wires":[["5fcc828a.c8c55c"]]},{"id":"62ef7b3a.b5c514","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"var opencount = opencount;\nvar runonce = runonce;\nif (runonce == \"null\")\n{\n    opencount = 0;\n    msg.payload = (opencount);\n    runonce = 1;\n}\nif (msg.payload == 'open')\n{\n    opencount = 1;\n}\nelse\n{\n    opencount = 0;\n}\nmsg.topic = 'front';\nmsg.payload = (opencount);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":520,"y":580,"wires":[["5fcc828a.c8c55c"]]},{"id":"5fcc828a.c8c55c","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"context.data = context.data || {};\nswitch (msg.topic) {\n    case \"den\": \n        context.data.den = msg.payload;\n        break;\n    case \"kitchen\":\n        context.data.kitchen = msg.payload;\n        break;    \n    case \"front\": \n        context.data.front = msg.payload;\n        break;    \n    case \"guest\": \n        context.data.guest = msg.payload;\n        break;      \n    case \"dining\": \n        context.data.dining = msg.payload;\n        break;  \n    case \"firstrun\": \n        context.data.firstrun = msg.payload;\n        break;  \n    default:\n        msg = null;\n        break;\n}    \nvar opencount = opencount;\n    opencount = (context.data.den + context.data.dining + context.data.front + context.data.kitchen + context.data.guest);\n    msg.payload = (opencount);\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":700,"y":580,"wires":[["161c2857.698298"]]},{"id":"161c2857.698298","type":"change","z":"d4d11aac.7a6398","name":"Door topic","rules":[{"t":"set","p":"topic","pt":"msg","to":"doors","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":520,"wires":[["2e8dc0cf.8c103"]]},{"id":"2e8dc0cf.8c103","type":"function","z":"d4d11aac.7a6398","name":"Compare","func":"context.data = context.data || {};\nswitch (msg.topic) {\n    case \"windows\": \n        context.data.windows = Number(msg.payload);\n        break;\n    case \"doors\":\n        context.data.doors = msg.payload;\n        break;    \n    case \"firstrun\": \n        context.data.firstrun = msg.payload;\n        break;  \n    default:\n        msg = null;\n        break;\n}    \nvar opencount = opencount;\nvar runonce = runonce;\nif (runonce == \"null\")\n{\n    opencount = 0;\n    msg.payload = (opencount);\n    runonce = 1;\n}\n    opencount = (context.data.windows + context.data.doors);\n    msg.payload = (opencount);\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":700,"y":360,"wires":[["fc121df3.e4f92"]]},{"id":"e586fbba.4897c8","type":"change","z":"d4d11aac.7a6398","name":"Windows topic","rules":[{"t":"set","p":"topic","pt":"msg","to":"windows","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":220,"wires":[["2e8dc0cf.8c103"]]},{"id":"d0807237.45d98","type":"server-state-changed","z":"d4d11aac.7a6398","name":"Den Door","server":"2ee084e7.d905bc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.den_door_access_control_window_door_is_open","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":340,"y":280,"wires":[["e18f077.2ead9f8"]]},{"id":"e18f077.2ead9f8","type":"api-current-state","z":"d4d11aac.7a6398","name":"Den Door","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"binary_sensor.den_door_access_control_window_door_is_open","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":340,"y":340,"wires":[["794fa680.3b20c8","3456d5f2.00a88a"]]},{"id":"126818b8.811237","type":"server-state-changed","z":"d4d11aac.7a6398","name":"Dining Door","server":"2ee084e7.d905bc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.dining_room_door_access_control_window_door_is_open","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":350,"y":400,"wires":[["33461a50.344246"]]},{"id":"33461a50.344246","type":"api-current-state","z":"d4d11aac.7a6398","name":"Dining Door","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"binary_sensor.dining_room_door_access_control_window_door_is_open","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":350,"y":460,"wires":[["f15b5a70.a17df8","87d4e2ac.bed1c"]]},{"id":"5f04f0fc.f6c04","type":"server-state-changed","z":"d4d11aac.7a6398","name":"Kitchen Door","server":"2ee084e7.d905bc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.kitchen_door_access_control_window_door_is_open","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":350,"y":760,"wires":[["c46d0550.9b4698"]]},{"id":"c46d0550.9b4698","type":"api-current-state","z":"d4d11aac.7a6398","name":"Kitchen Door","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"binary_sensor.kitchen_door_access_control_window_door_is_open","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":350,"y":820,"wires":[["e5a10cc9.2cc6a","b05df403.1c5a18"]]},{"id":"467329ba.eeaff8","type":"server-state-changed","z":"d4d11aac.7a6398","name":"Guest Door","server":"2ee084e7.d905bc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.guest_bathroom_door_access_control_window_door_is_open","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":350,"y":640,"wires":[["58d43db6.3bcc44"]]},{"id":"58d43db6.3bcc44","type":"api-current-state","z":"d4d11aac.7a6398","name":"Guest Door","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"binary_sensor.guest_bathroom_door_access_control_window_door_is_open","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":350,"y":700,"wires":[["7837bc35.0c8ed4","79b26671.f01f08"]]},{"id":"ca81cfa8.8327f","type":"server-state-changed","z":"d4d11aac.7a6398","name":"Front Door","server":"2ee084e7.d905bc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"binary_sensor.front_door","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":340,"y":520,"wires":[["f1c473b.84dc29"]]},{"id":"f1c473b.84dc29","type":"api-current-state","z":"d4d11aac.7a6398","name":"Front Door","server":"2ee084e7.d905bc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"binary_sensor.front_door","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":350,"y":580,"wires":[["b57eaa7b.7a2578","62ef7b3a.b5c514"]]},{"id":"afedd9f.bb70a28","type":"server-state-changed","z":"d4d11aac.7a6398","name":"Main Windows","server":"2ee084e7.d905bc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.window_count_main","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":360,"y":160,"wires":[["c87456f8.10afa8"]]},{"id":"fc121df3.e4f92","type":"debug","z":"d4d11aac.7a6398","name":"Total Count","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":900,"y":360,"wires":[]},{"id":"2ee084e7.d905bc","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

The logic you explained was not fully clear to me but I think I had a similar problem. I solved it that way

  1. i have a flow-variable "No_of_open_windows"
  2. Whenever a window or door is opened i add 1, and when a window/door is closed, i substract 1 to the flow variable
  3. whenever a window/door is opened I restart a timer (e.g. 5min)
  4. Whenever the timer is expired I check the flow-variable to be > 2 (i think thats your criteria) and switch on the fan

Do you mind sharing you flow? This could help clean up what I've created :wink:

My explanation above was a bit more simplified (and based on an old version of my flow). I'm now using the THING-nodes for all my sensors. the below flow is giving you the core logic

[{"id":"24b7f0b1.d3eb2","type":"Thing List","z":"dd0287b8.7a9528","nodeName":"","outputType":"array","outputValue":"thing","property":"payload","discardInput":false,"rules":[{"compare":"false","thingProp":"state.contact"},{"compare":"eq","thingProp":"type","value":"Sensor","type":"str"}],"x":310,"y":640,"wires":[["bcfd2a6c.2c3278"]]},{"id":"c84ade42.c0d9a","type":"inject","z":"dd0287b8.7a9528","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":680,"wires":[["24b7f0b1.d3eb2"]]},{"id":"bcfd2a6c.2c3278","type":"function","z":"dd0287b8.7a9528","name":"check count","func":"var count =0;\n\nfor(let i = 0; i < msg.payload.length; i++){\n    //let title = msg.payload[i].title; \n    if (msg.payload[i].state.contact==false) count++;\n\n}\nmsg.count=count;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":450,"y":640,"wires":[["fa157e95.06ae8"]]},{"id":"fa157e95.06ae8","type":"switch","z":"dd0287b8.7a9528","name":">=2","property":"count","propertyType":"msg","rules":[{"t":"gte","v":"2","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":590,"y":640,"wires":[["64365980.326de8"]]},{"id":"64365980.326de8","type":"change","z":"dd0287b8.7a9528","name":"start","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload","pt":"msg","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":640,"wires":[["57f83d9.cbd7ac4"]]},{"id":"57f83d9.cbd7ac4","type":"mytimeout","z":"dd0287b8.7a9528","name":"Duration","outtopic":"tt","outsafe":"ON","outwarning":"Warning","outunsafe":"OFF","warning":"5","timer":"20","ndebug":false,"ignoreCase":false,"repeat":false,"again":false,"x":880,"y":640,"wires":[["573785c5.84d00c"],[]]},{"id":"573785c5.84d00c","type":"switch","z":"dd0287b8.7a9528","name":"timer off?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"OFF","vt":"str"},{"t":"eq","v":"ON","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1040,"y":640,"wires":[["23cf992a.a53b56"],[]]},{"id":"23cf992a.a53b56","type":"link out","z":"dd0287b8.7a9528","name":"Fan-ON","links":[],"x":1155,"y":640,"wires":[]},{"id":"7baa074c.356748","type":"link in","z":"dd0287b8.7a9528","name":"","links":[],"x":175,"y":640,"wires":[["24b7f0b1.d3eb2"]]}]
  1. whenever a window or door is opened/closed the THING state will be updated
  2. on every THING event for Windows/doors I check the number of open windows/doors (Link-In)
  3. the THING will give me an array of all windows/doors with false contact (window or door is open)
  4. I have done a small function to count the elements in the THING array. These is the number of open windows/doors
  5. if the number of open windows/doors is >=2 than i restart my timer
  6. if the timer has expired (20sec) than another routine is called to start the fan (which has its own timer function)

you may need your own logic to count the open windows/doors .. but otherwise it should work

I'll take a look at this later and reply back. This may be able to simplify my code - although for the count I want it to only check each door independently for a time frame so that if the back door is open for 4 minutes then the front is opened the count would change. Then if the back door closed before 5 minutes (or stayed open) the 5 minute timer would have received a new value as a new door opens/closes. I only want it to check each door. But this may be able to help. Appreciate you introducing Things (and more importantly mytimeout - which may be a HUGE help!)

I had similar problems, but solved most of them with the THINGS node. For example, I have a THING for all contact related sensors (Doors, Windows). As part of the THINGS definition i have defined properties like HOUSE, BEDROOM, BATH, GARAGE and I'm now able to filter my results based on these properties just with the a single 'LIST'-node from THINGS (as you saw in the above flow)

E.G:

  • List all open windows in the Living room
  • List all open windows in the house (I use that to inform me on open windows when I leave the house)

The good thing about THINGS is that it will provide you LIST and TRIGGER support which can simplify all your flows. As said, all my sensors are now feeding into THINGS objects and it helped me a lot

For example, below flow is providing a listbox with all open windows/doors in my house and is automatically updated (via THINGS-trigger) whenever a sensor update occurs. It even shows when the window/door was opened!
image

[{"id":"e54d6de3.b31d8","type":"Thing Trigger","z":"4a94dfcf.4b415","g":"7e4c8e52.02269","nodeName":"","triggerThing":{"path":"type","type":"str","value":"Sensor"},"triggerState":"state.contact","triggerTest":null,"ignoreInit":true,"payload":{"type":"state","value":""},"x":160,"y":1200,"wires":[["f52754ed.d80bd8"]]},{"id":"f52754ed.d80bd8","type":"Thing List","z":"4a94dfcf.4b415","g":"7e4c8e52.02269","nodeName":"","outputType":"array","outputValue":"thing","property":"payload","discardInput":false,"rules":[{"compare":"false","thingProp":"state.contact"},{"compare":"eq","thingProp":"type","value":"Sensor","type":"str"}],"x":350,"y":1200,"wires":[["6d44f6fa.f04af8"]]},{"id":"6d44f6fa.f04af8","type":"function","z":"4a94dfcf.4b415","g":"7e4c8e52.02269","name":"Prep Pulldown","func":"\nmsg.options = [];//create empty array\nfor(let i = 0; i < msg.payload.length; i++){\n    //let title = msg.payload[i].title; \n    let check = msg.payload[i].name;\n    let opt = {};//make new opt object\n    opt[\"title\"] = msg.payload[i].name;// fill object for pulldown\n    var x=msg.payload[i].state.timestamp;\n    var d= new Date(x);\n    opt[\"description\"] = \"Open | \"+d.toLocaleString(\"en-GB\", {timeZone: \"UTC\",hour12: false });\n\n    if (check.includes(\"Window\") ==true) \n        opt[\"icon\"] = \"http://192.168.2.202:1880/Window01.png\";//data:image/jpeg;base64,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\";\n    else\n        opt[\"icon\"] = \"http://192.168.2.202:1880/Door01.png\";\n \n    msg.options.push(opt);//add the opt to array        \n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":540,"y":1200,"wires":[["b4d4b5c0.572378"]]},{"id":"b4d4b5c0.572378","type":"change","z":"4a94dfcf.4b415","g":"7e4c8e52.02269","name":"-> list","rules":[{"t":"move","p":"options","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":1200,"wires":[["ce907c0f.ba7cf"]]},{"id":"ce907c0f.ba7cf","type":"ui_list","z":"4a94dfcf.4b415","g":"7e4c8e52.02269","group":"74653dda.15fc94","name":"Windows","order":1,"width":6,"height":4,"lineType":"two","actionType":"click","allowHTML":true,"outputs":1,"topic":"","x":980,"y":1200,"wires":[[]]},{"id":"74653dda.15fc94","type":"ui_group","name":"Windows/Doors = OPEN","tab":"9e238849.652318","order":4,"disp":true,"width":"6","collapse":true},{"id":"9e238849.652318","type":"ui_tab","name":"Home","icon":"fa-home","order":1,"disabled":false,"hidden":false}]

I hope thats helps :wink:

This is fantastic. I love how you can see when they opened last. I'll have to play with this!