Lets see if I can figure out how to share this so that someone else can use it. Exporting from node-red feels unpredictable to me.
This is a subflow which does zone activity aggregation. Notably it will take regular contact switches to trigger zone activity as well as the traditional motion sensors. In my case, doors are placed at 90º angles to the room so covering them with a motion sensor is difficult without adding a dedicated motion sensor. The contact event will auto-reset similar to the way a motion sensor does.
The subflow takes configuration values for contact sensor reset and zone activity reset via topic:config messages, which means that the parameters can be reconfigured depending on the time of day... or whatever you want.
The motion aggregation component is in an unsafe function node, and is mostly code from someone else on this forum/thread. I removed a couple features and implemented them as node-red sequences, and I try to use as few third-party nodes as possible.
[{"id":"4c7c8cd1.6a2a1c","type":"subflow","name":"My Zone Activity Aggregator","info":"","category":"","in":[{"x":100,"y":200,"wires":[{"id":"28720ce3.415504"}]}],"out":[{"x":700,"y":520,"wires":[{"id":"f95fc416.12cdb8","port":"0"}]},{"x":1120,"y":620,"wires":[{"id":"f9fb7202.670248","port":0}]}],"env":[{"name":"Zone Name","type":"str","value":"Motion zone"}],"color":"#DDAA99","status":{"x":1060,"y":840,"wires":[{"id":"baa794b2.a49bd","port":0},{"id":"3adf6856.200d98","port":0},{"id":"6214c0ab.73eb08","port":0}]}},{"id":"37082482.1d726c","type":"unsafe-function","z":"4c7c8cd1.6a2a1c","name":"Motion Aggregator","func":"let motionStatus = null;\nlet activeCount = 0;\n\nlet sensors = context.get(\"sensors\") || {};\nlet wasActive = context.get(\"active\") || false;\nlet deviceId\n\nif (typeof msg.payload != \"undefined\" && msg.payload !== null) {\n deviceId = msg.payload.deviceId;\n}\n\nif (typeof deviceId != \"undefined\") {\n\n // If we didn't receive an event, the process was triggered by something else (eg. enabling the automation)\n motionStatus = msg.payload.value\n\n if (typeof sensors[deviceId] == \"undefined\") {\n sensors[deviceId] = {}\n }\n\n let deviceObject = sensors[deviceId]\n\n deviceObject.state = motionStatus\n deviceObject.displayName = msg.topic\n\n context.set(\"sensors\", sensors)\n}\n\n\nfor (let [deviceId, values] of Object.entries(sensors)) {\n if ( values.state == 'active' ) {\n activeCount++\n }\n}\n\nconst active = activeCount > 0\n\nnewMessage = {\n topic: env.get(\"Zone Name\"),\n payload: active ? \"active\" : \"inactive\",\n count: activeCount\n}\n\n context.set(\"active\", active)\n return newMessage\n","outputs":1,"noerr":0,"x":990,"y":160,"wires":[["1135a6a5.8ffeb9"]]},{"id":"baa794b2.a49bd","type":"change","z":"4c7c8cd1.6a2a1c","name":"Active sensor count","rules":[{"t":"set","p":"payload","pt":"msg","to":"count & \" \" & payload & \" sensors\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":920,"y":880,"wires":[[]]},{"id":"f9fb7202.670248","type":"trigger","z":"4c7c8cd1.6a2a1c","name":"Activity off delay","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"5","extend":true,"overrideDelay":true,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":1000,"y":620,"wires":[[]]},{"id":"f95fc416.12cdb8","type":"switch","z":"4c7c8cd1.6a2a1c","name":"is active?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"active","vt":"str"},{"t":"eq","v":"inactive","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":600,"y":600,"wires":[["bf8561c.27047a"],["4c35d83e.70c12"]]},{"id":"d1fef5e9.1faa","type":"switch","z":"4c7c8cd1.6a2a1c","name":"is active?","property":"count","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":720,"y":860,"wires":[["3adf6856.200d98"],["baa794b2.a49bd"]]},{"id":"3adf6856.200d98","type":"change","z":"4c7c8cd1.6a2a1c","name":"Inactive","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"inactive\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":880,"y":840,"wires":[[]]},{"id":"28720ce3.415504","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Separate config messages","property":"topic","propertyType":"msg","rules":[{"t":"else"},{"t":"eq","v":"config","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":280,"y":200,"wires":[["5e909849.a5ede"],["ab2b2200.580fe"]]},{"id":"bf8561c.27047a","type":"change","z":"4c7c8cd1.6a2a1c","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":580,"wires":[["f9fb7202.670248"]]},{"id":"5e909849.a5ede","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Separate motion from contact","property":"payload.name","propertyType":"msg","rules":[{"t":"eq","v":"motion","vt":"str"},{"t":"eq","v":"contact","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":690,"y":200,"wires":[["37082482.1d726c"],["ee67eca9.7c9548","83dac1a6.f49bb"]]},{"id":"ee67eca9.7c9548","type":"change","z":"4c7c8cd1.6a2a1c","name":"Active-true","rules":[{"t":"set","p":"payload","pt":"msg","to":"active","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":220,"wires":[["de4555d9.03c9f8"]]},{"id":"9dec185d.0fa0b","type":"change","z":"4c7c8cd1.6a2a1c","name":"Active-false","rules":[{"t":"set","p":"payload","pt":"msg","to":"inactive","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1350,"y":260,"wires":[["de4555d9.03c9f8"]]},{"id":"979d8215.69ba18","type":"link out","z":"4c7c8cd1.6a2a1c","name":"Switch Event","links":["b4f2e905.4ec488","c551054e.c3663","db637502.ed7b1"],"x":1535,"y":220,"wires":[]},{"id":"1135a6a5.8ffeb9","type":"link out","z":"4c7c8cd1.6a2a1c","name":"Motion Event","links":["b4f2e905.4ec488","c551054e.c3663"],"x":1195,"y":160,"wires":[]},{"id":"c551054e.c3663","type":"link in","z":"4c7c8cd1.6a2a1c","name":"","links":["1135a6a5.8ffeb9","979d8215.69ba18"],"x":295,"y":840,"wires":[["2d4ae563.a0394a"]]},{"id":"b4f2e905.4ec488","type":"link in","z":"4c7c8cd1.6a2a1c","name":"","links":["1135a6a5.8ffeb9","979d8215.69ba18"],"x":295,"y":600,"wires":[["a6ce7a5b.5718f8"]]},{"id":"ab2b2200.580fe","type":"link out","z":"4c7c8cd1.6a2a1c","name":"Config","links":["e1a2c208.c8ace8"],"x":575,"y":260,"wires":[]},{"id":"e1a2c208.c8ace8","type":"link in","z":"4c7c8cd1.6a2a1c","name":"","links":["ab2b2200.580fe"],"x":295,"y":1120,"wires":[["c4696ddd.05a93"]]},{"id":"7878ae9d.d67828","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Config","info":"","x":650,"y":260,"wires":[]},{"id":"cb4ad9ea.ed3e38","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Motion event","info":"","x":1310,"y":160,"wires":[]},{"id":"acfeb2ce.da0e98","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Sensor event","info":"","x":1650,"y":220,"wires":[]},{"id":"c4696ddd.05a93","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Set config parameter","property":"payload.name","propertyType":"msg","rules":[{"t":"eq","v":"sensor_reset_delay","vt":"str"},{"t":"eq","v":"active_reset_delay","vt":"str"},{"t":"eq","v":"zone_active","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":480,"y":1120,"wires":[["3f0f1369.4b070c"],["b127ab0f.e7777"],["49b85432.3fb394"]]},{"id":"3f0f1369.4b070c","type":"change","z":"4c7c8cd1.6a2a1c","name":"","rules":[{"t":"set","p":"sensor_reset_delay","pt":"flow","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":1060,"wires":[[]]},{"id":"b127ab0f.e7777","type":"change","z":"4c7c8cd1.6a2a1c","name":"","rules":[{"t":"set","p":"activity_reset_delay","pt":"flow","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":1120,"wires":[[]]},{"id":"83dac1a6.f49bb","type":"change","z":"4c7c8cd1.6a2a1c","name":"Sensor reset delay","rules":[{"t":"set","p":"delay","pt":"msg","to":"sensor_reset_delay","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":260,"wires":[["b5799027.c31f78","1f831024.572ef8"]]},{"id":"b5799027.c31f78","type":"debug","z":"4c7c8cd1.6a2a1c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1170,"y":320,"wires":[]},{"id":"1f831024.572ef8","type":"trigger","z":"4c7c8cd1.6a2a1c","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"5","extend":true,"overrideDelay":true,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":1180,"y":260,"wires":[["9dec185d.0fa0b"]]},{"id":"4c35d83e.70c12","type":"change","z":"4c7c8cd1.6a2a1c","name":"Activity reset delay","rules":[{"t":"set","p":"delay","pt":"msg","to":"activity_reset_delay","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":620,"wires":[["f9fb7202.670248"]]},{"id":"7a24866e.db567","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Zone Active","info":"","x":810,"y":520,"wires":[]},{"id":"268891b5.f0e4f6","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Zone Inactive","info":"","x":1070,"y":560,"wires":[]},{"id":"de4555d9.03c9f8","type":"rbe","z":"4c7c8cd1.6a2a1c","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":1475,"y":220,"wires":[["979d8215.69ba18"]],"l":false},{"id":"28fb421d.2d7a2e","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Status text","info":"","x":340,"y":780,"wires":[]},{"id":"3dc117c7.63b51","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Config Parameters","info":"","x":370,"y":1060,"wires":[]},{"id":"2d4ae563.a0394a","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Motion zone active","property":"zone_active","propertyType":"flow","rules":[{"t":"else"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":470,"y":840,"wires":[["6214c0ab.73eb08"],["d1fef5e9.1faa"]]},{"id":"6214c0ab.73eb08","type":"change","z":"4c7c8cd1.6a2a1c","name":"Disabled","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"Disabled\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":880,"y":800,"wires":[[]]},{"id":"49b85432.3fb394","type":"change","z":"4c7c8cd1.6a2a1c","name":"","rules":[{"t":"set","p":"zone_active","pt":"flow","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":1180,"wires":[[]]},{"id":"a6ce7a5b.5718f8","type":"switch","z":"4c7c8cd1.6a2a1c","name":"Zone enabled?","property":"zone_active","propertyType":"flow","rules":[{"t":"true"}],"checkall":"false","repair":false,"outputs":1,"x":440,"y":600,"wires":[["f95fc416.12cdb8"]]},{"id":"c90679d.51c6188","type":"comment","z":"4c7c8cd1.6a2a1c","name":"Outputs","info":"","x":330,"y":540,"wires":[]}]
I'm not sure if this picture will be helpful or not, but here it is visually: