Skip to content

Commit

Permalink
save conf room node red flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bradjc committed May 22, 2016
1 parent e303fba commit b35ff6e
Showing 1 changed file with 1 addition and 0 deletions.
1 change: 1 addition & 0 deletions node-red/flows/4901occupancy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"type":"tab","id":"74fa93c2.8b056c","label":"4901 Occupancy"},{"id":"5192fc22.ae6d04","type":"mqtt-broker","z":"74fa93c2.8b056c","broker":"localhost","port":"1883","clientid":"","usetls":false,"verifyservercert":true,"compatmode":true,"keepalive":"15","cleansession":true,"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":""},{"id":"cbb314fa.344ce8","type":"wemo out","z":"74fa93c2.8b056c","ipaddr":"141.212.11.101","name":"Sconce WeMo","x":1686,"y":389,"wires":[]},{"id":"4735cc91.b8ca34","type":"mqtt in","z":"74fa93c2.8b056c","name":"Projector PowerBlade","topic":"device/PowerBlade/c098e570002e","broker":"5192fc22.ae6d04","x":311,"y":273,"wires":[["141b66e9.ebe499"]]},{"id":"c53fd57.f3ac028","type":"function","z":"74fa93c2.8b056c","name":"Projector Power Threshold","func":"var threshold = 100; // above 100 watts we assume projector is on\n\nvar power = parseFloat(msg.payload.power);\n\nif (power > threshold) {\n msg.payload = true;\n} else {\n msg.payload = false;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":741,"y":273,"wires":[["cfc59ad3.303a68"]]},{"id":"141b66e9.ebe499","type":"json","z":"74fa93c2.8b056c","name":"","x":505,"y":273,"wires":[["c53fd57.f3ac028"]]},{"id":"8fed47a.f7012b8","type":"rbe","z":"74fa93c2.8b056c","name":"","func":"rbe","gap":"","x":1493,"y":390,"wires":[["cbb314fa.344ce8"]]},{"id":"e18f1c39.1e70e","type":"mqtt in","z":"74fa93c2.8b056c","name":"Squall PIR projector case","topic":"device/Blink/c098e5900089","broker":"5192fc22.ae6d04","x":300,"y":518,"wires":[["1a331115.e5ccef"]]},{"id":"1a331115.e5ccef","type":"json","z":"74fa93c2.8b056c","name":"","x":506,"y":519,"wires":[["71f8e189.8e072"]]},{"id":"71f8e189.8e072","type":"function","z":"74fa93c2.8b056c","name":"get projector case last minute","func":"var out = msg.payload.motion_last_minute;\nmsg.payload = out;\nreturn msg;\n","outputs":1,"noerr":0,"x":739,"y":519,"wires":[["cfc59ad3.303a68"]]},{"id":"cfc59ad3.303a68","type":"function","z":"74fa93c2.8b056c","name":"JOIN","func":"var join = context.join || {};\n\nvar now = Date.now();\n\njoin[msg.topic] = [msg.payload, now];\ncontext.join = join;\n\nreturn {payload: join};","outputs":1,"noerr":0,"x":1061,"y":398,"wires":[["4400afc4.bbff5","cf3e5592.30c1a8","c3f1553e.3c0ea8"]]},{"id":"4400afc4.bbff5","type":"function","z":"74fa93c2.8b056c","name":"Occupancy Detection","func":"var state = context.state || 'UNOCCUPIED';\nvar entered_unsure_timestamp = context.entered_unsure_timestamp || 0;\n\nvar now = Date.now();\n\n// Get some easy to use variables. True == sensed something\nvar p = msg.payload['device/PowerBlade/c098e570002e'] || [false, 0];\nvar dp = msg.payload['device/PowerBlade/c098e5700045'] || [false, 0];\nvar rm = msg.payload['device/Blink/c098e5900089'] || [false, 0];\nvar dm = msg.payload['device/Blink/c098e5900004'] || [false, 0];\n\nvar projector = p[0];\nvar desk_power = dp[0];\nvar room_motion = rm[0];\nvar door_motion = dm[0];\n\nvar projector_time = p[1];\nvar desk_power_time = dp[1];\nvar room_motion_time = rm[1];\nvar door_motion_time = dm[1];\n\n// State machine\nif (state == 'UNOCCUPIED') {\n // On any detection, we move to occupied\n if (projector || room_motion || door_motion || desk_power) {\n state = 'OCCUPIED';\n }\n\n} else if (state == 'OCCUPIED') {\n // We stay here unless we see door. Then we are not sure if\n // everyone just left.\n if (door_motion) {\n state = 'UNSURE';\n entered_unsure_timestamp = now;\n }\n\n} else if (state == 'UNSURE') {\n // If door motion is still here, we still don't know much.\n // We stay in this state because someone must be at least\n // near the room.\n if (door_motion) {\n // stay\n entered_unsure_timestamp = now;\n } else if (!projector && !room_motion && !desk_power) {\n // Nothing is happening. This looks like the room\n // is empty.\n state = 'UNOCCUPIED';\n } else {\n // This is where things get tricky. We need to decide whether\n // the door was because the last person left, or if someone\n // entered.\n // As a heuristic, we need to see something happen in the\n // room 15 seconds after anything happened near the door\n // in order to say there are people in the room. This handles\n // any sensor weirdness and dropped packets.\n var threshold_timestamp = entered_unsure_timestamp + (15*1000);\n if ((projector && projector_time >= threshold_timestamp) ||\n (room_motion && room_motion_time >= threshold_timestamp) ||\n (desk_power && desk_power_time >= threshold_timestamp)) {\n // We saw some indication after the door that the room is still occupied.\n state = 'OCCUPIED';\n }\n }\n}\n\n// Save state\ncontext.state = state;\ncontext.entered_unsure_timestamp = entered_unsure_timestamp;\n\n// Convert to determination of occupancy\nvar occupancy = false;\nif (state == 'OCCUPIED' || state == 'UNSURE') {\n occupancy = true;\n}\n\nreturn [{payload: occupancy}, {payload: state}];\n","outputs":"2","noerr":0,"x":1290,"y":396,"wires":[["8fed47a.f7012b8"],["c55442c1.3aabc"]]},{"id":"7129aeba.8ed65","type":"debug","z":"74fa93c2.8b056c","name":"current state of sensors","active":true,"console":"false","complete":"payload","x":1658,"y":285,"wires":[]},{"id":"a268e62d.5d9718","type":"mqtt in","z":"74fa93c2.8b056c","name":"Squall PIR door","topic":"device/Blink/c098e5900004","broker":"5192fc22.ae6d04","x":291,"y":631,"wires":[["75730ec6.8a8cf"]]},{"id":"75730ec6.8a8cf","type":"json","z":"74fa93c2.8b056c","name":"","x":502,"y":630,"wires":[["1618f10e.e9e70f"]]},{"id":"1618f10e.e9e70f","type":"function","z":"74fa93c2.8b056c","name":"get projector case last minute","func":"var out = msg.payload.motion_last_minute;\nmsg.payload = out;\nreturn msg;\n","outputs":1,"noerr":0,"x":723,"y":630,"wires":[["cfc59ad3.303a68"]]},{"id":"2ea0c713.d15f38","type":"debug","z":"74fa93c2.8b056c","name":"Room state","active":true,"console":"false","complete":"payload","x":1562,"y":521,"wires":[]},{"id":"ced4dec5.312b2","type":"mqtt in","z":"74fa93c2.8b056c","name":"Desk in 4901 PowerBlade","topic":"device/PowerBlade/c098e5700045","broker":"5192fc22.ae6d04","x":301,"y":395,"wires":[["e3eb9f19.1c146"]]},{"id":"e3eb9f19.1c146","type":"json","z":"74fa93c2.8b056c","name":"","x":513,"y":396,"wires":[["69e0ceeb.961f3"]]},{"id":"69e0ceeb.961f3","type":"function","z":"74fa93c2.8b056c","name":"Desk Power Threshold","func":"var threshold = 10; // above 100 watts we assume projector is on\n\nvar power = parseFloat(msg.payload.power);\n\nif (power > threshold) {\n msg.payload = true;\n} else {\n msg.payload = false;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":740,"y":397,"wires":[["cfc59ad3.303a68"]]},{"id":"c55442c1.3aabc","type":"rbe","z":"74fa93c2.8b056c","name":"","func":"rbe","gap":"","x":1379,"y":535,"wires":[["2ea0c713.d15f38","6c6e782e.939188"]]},{"id":"cf3e5592.30c1a8","type":"function","z":"74fa93c2.8b056c","name":"dedup obj","func":"var last = context.last || {}\n\nvar different = false;\nfor (var key in msg.payload) {\n if (key in last) {\n if (msg.payload[key][0] != last[key][0]) {\n different = true;\n break;\n }\n } else {\n different = true;\n break;\n }\n}\n\ncontext.last = msg.payload;\n\nif (different) {\n return msg;\n}","outputs":1,"noerr":0,"x":1450,"y":285,"wires":[["7129aeba.8ed65"]]},{"id":"ee1a2396.11e5e","type":"http request","z":"74fa93c2.8b056c","name":"","method":"POST","ret":"txt","url":"http://141.212.11.163/status/update?group={{{payload.group}}}&key={{{payload.key}}}&value={{{payload.value}}}","x":1788,"y":579,"wires":[[]]},{"id":"6c6e782e.939188","type":"function","z":"74fa93c2.8b056c","name":"Create status blob","func":"var state = msg.payload;\n\nmsg.payload = {\n group: 'Conference Rooms',\n key: '4901',\n value: state\n};\n\nreturn msg;","outputs":1,"noerr":0,"x":1584,"y":579,"wires":[["ee1a2396.11e5e"]]},{"id":"a20693c9.5df97","type":"comment","z":"74fa93c2.8b056c","name":"Input from MQTT based sensor streams","info":"","x":371,"y":211,"wires":[]},{"id":"ae58869b.51a778","type":"comment","z":"74fa93c2.8b056c","name":"All sensors are converted to booleans","info":"","x":725,"y":677,"wires":[]},{"id":"5d95a570.a26a5c","type":"comment","z":"74fa93c2.8b056c","name":"PowerBlades are thresholded based on power","info":"","x":755,"y":714,"wires":[]},{"id":"c3fd135b.3c02f","type":"comment","z":"74fa93c2.8b056c","name":"Blink (PIR) sensors use the \"motion in last minute field\"","info":"","x":775,"y":746,"wires":[]},{"id":"ecd9a49a.132658","type":"comment","z":"74fa93c2.8b056c","name":"\"JOIN\" synchronizes the data streams","info":"","x":1102,"y":310,"wires":[]},{"id":"85ba62c.f7a45a","type":"comment","z":"74fa93c2.8b056c","name":"Occupancy block is the application state machine","info":"","x":1292,"y":445,"wires":[]},{"id":"a8a7c1f9.57584","type":"comment","z":"74fa93c2.8b056c","name":"A light bulb is lit when the room is occupied","info":"","x":1774,"y":429,"wires":[]},{"id":"98e1206c.671ee","type":"comment","z":"74fa93c2.8b056c","name":"Room occupancy is posted to a generic \"status\" webpage","info":"","x":1680,"y":626,"wires":[]},{"id":"61dd532f.9e22ac","type":"comment","z":"74fa93c2.8b056c","name":"Debugging output","info":"","x":1644,"y":235,"wires":[]},{"id":"c3f1553e.3c0ea8","type":"debug","z":"74fa93c2.8b056c","name":"","active":true,"console":"false","complete":"false","x":1336,"y":199,"wires":[]}]

0 comments on commit b35ff6e

Please sign in to comment.