node-red-contrib-home-assistant-websocket
Version:
Node-RED integration with Home Assistant through websocket and REST API
2 lines (1 loc) • 17.2 kB
JSON
[{"id":"10f78dfbdda44e3b","type":"group","z":"bd764bbe2981bf8f","name":"use-case-01","style":{"label":true},"nodes":["bec57b08.0a8f08","31d5412.74a31be","587bab85.857fe4","e85a9609.74ddd8","1d0b3f5b.37aab1","8c90dfc.28f2f2","df484d95.35a35","6566fa4.a832004","88fd9ca4.3ea69","9e5e4c22.4d925","13c8aaf9.466e05","8f5a5cc3.fb235","5485b497.b4d90c","1b96dc9e.4f1fe3","5b765a47a35c3f91"],"x":22,"y":439,"w":1368,"h":210},{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"Android actionable notification v1.0.1\n\n[Documentation](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/actionable-notifications-subflow-for-android.html)\n","category":"HA Actions","in":[{"x":84,"y":80,"wires":[{"id":"9d85d137.fe487"}]}],"out":[{"x":1188,"y":128,"wires":[{"id":"974bd48d.c253e8","port":0}]},{"x":1188,"y":176,"wires":[{"id":"974bd48d.c253e8","port":1}]},{"x":1188,"y":224,"wires":[{"id":"974bd48d.c253e8","port":2}]},{"x":964,"y":240,"wires":[{"id":"5bc7345c.07b1cc","port":1}]}],"env":[{"name":"service","type":"str","value":"mobile_app_jason","ui":{"label":{"en-US":"Notify Service"},"type":"input","opts":{"types":["str"]}}},{"name":"title","type":"str","value":"","ui":{"label":{"en-US":"Title"},"type":"input","opts":{"types":["str"]}}},{"name":"message","type":"str","value":"","ui":{"label":{"en-US":"Message"},"type":"input","opts":{"types":["str"]}}},{"name":"action1Title","type":"str","value":"","ui":{"label":{"en-US":"Action 1 Title"},"type":"input","opts":{"types":["str"]}}},{"name":"action1Uri","type":"str","value":"","ui":{"label":{"en-US":"Action 1 URI (optional)"},"type":"input","opts":{"types":["str"]}}},{"name":"action2Title","type":"str","value":"","ui":{"label":{"en-US":"Action 2 Title"},"type":"input","opts":{"types":["str"]}}},{"name":"action2Uri","type":"str","value":"","ui":{"label":{"en-US":"Action 2 URI (optional)"},"type":"input","opts":{"types":["str"]}}},{"name":"action3Title","type":"str","value":"","ui":{"label":{"en-US":"Action 3 Title"},"type":"input","opts":{"types":["str"]}}},{"name":"action3Uri","type":"str","value":"","ui":{"label":{"en-US":"Action 3 URI (optional)"},"type":"input","opts":{"types":["str"]}}},{"name":"userInfo","type":"bool","value":"false","ui":{"label":{"en-US":"Populate User Information"},"type":"checkbox"}},{"name":"sticky","type":"bool","value":"false","ui":{"label":{"en-US":"Sticky"},"type":"checkbox"}},{"name":"group","type":"str","value":"None","ui":{"label":{"en-US":"Group"},"type":"select","opts":{"opts":[{"l":{"en-US":"None"},"v":""},{"l":{"en-US":"Cameras"},"v":"camera"},{"l":{"en-US":"Security"},"v":"security"},{"l":{"en-US":"Garage"},"v":"garage"},{"l":{"en-US":"Laundry Room"},"v":"laundry_room"}]}}},{"name":"color","type":"str","value":"","ui":{"label":{"en-US":"Color"},"type":"input","opts":{"types":["str"]}}},{"name":"timeout","type":"num","value":"","ui":{"label":{"en-US":"Timeout"},"type":"input","opts":{"types":["num"]}}},{"name":"icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#46B1EF","outputLabels":["Action 1","Action 2","Action 3","Cleared"],"icon":"font-awesome/fa-mobile-phone","status":{"x":244,"y":272,"wires":[{"id":"204dbcfc.144ae4","port":0}]}},{"id":"f9e57204.71076","type":"function","z":"6dc0247c.d7210c","name":"create service call","func":"msg._originalPayload = msg.payload;\nflow.set('latestMessage', msg);\n\nconst services = env.get('service');\nif(!services) {\n node.status({\n text: 'no services defined',\n shape: 'ring',\n fill: 'red'\n });\n return; \n}\n\nconst actions = [1, 2, 3].reduce((acc, i) => {\n const name = `action${i}`\n const id = flow.get(`${name}Id`);\n const title = getActionProperty(i, \"title\") ?? env.get(`${name}Title`);\n const uri = getActionProperty(i, \"uri\") ?? env.get(`${name}Uri`);\n const action = uri.length ? 'URI' : title ? id : undefined;\n\n acc.push({ action, title, uri });\n\n return acc;\n}, []);\n\nconst tag = flow.get('notificationTag');\nconst data = mergeDeep({\n title: env.get('title'),\n message: env.get('message'),\n data: {\n tag,\n color: env.get(\"color\"),\n group: env.get(\"group\"),\n sticky: env.get(\"sticky\"),\n timeout: env.get(\"timeout\"),\n icon: env.get(\"icon\")\n },\n }, \n msg.actionable, \n {data: {actions}},\n);\n\nif(tag !== data?.data?.tag) {\n flow.set('notificationTag', data?.data?.tag);\n}\n\nservices.trim().split(/,\\s*/).forEach(service => {\n if(!service) return;\n \n msg.payload = {\n action: `notify.${service}`,\n data,\n };\n node.send(msg);\n});\n\nnode.done();\n\nfunction getActionProperty(index, prop) {\n const i = index - 1;\n\n return msg?.actionable?.data?.actions?.[i]?.[prop];\n}\n\n/**\n * Simple object check.\n * @param item\n * @returns {boolean}\n */\nfunction isObject(item) {\n return (item && typeof item === 'object' && !Array.isArray(item));\n}\n\n/**\n * Deep merge two objects.\n * @param target\n * @param sources\n */\nfunction mergeDeep(target, ...sources) {\n if (!sources.length) return target;\n const source = sources.shift();\n\n if (isObject(target) && isObject(source)) {\n for (const key in source) {\n if (isObject(source[key])) {\n if (!target[key]) Object.assign(target, { [key]: {} });\n mergeDeep(target[key], source[key]);\n } else {\n Object.assign(target, { [key]: source[key] });\n }\n }\n }\n\n return mergeDeep(target, ...sources);\n}\n","outputs":1,"timeout":"","noerr":0,"initialize":"const randomId = () => Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);\n\n[1,2,3].forEach(i => {\n flow.set(`action${i}Id`, `action${i}_${randomId()}`);\n})\n\n\nflow.set('notificationTag', `${env.get('title')}_${randomId()}`);","finalize":"","libs":[],"x":298,"y":80,"wires":[["368c9723.5876f8"]]},{"id":"974bd48d.c253e8","type":"switch","z":"6dc0247c.d7210c","name":"which action?","property":"eventData.event.action","propertyType":"msg","rules":[{"t":"eq","v":"action1Id","vt":"flow"},{"t":"eq","v":"action2Id","vt":"flow"},{"t":"eq","v":"action3Id","vt":"flow"}],"checkall":"true","repair":false,"outputs":3,"x":1024,"y":176,"wires":[[],[],[]]},{"id":"204dbcfc.144ae4","type":"status","z":"6dc0247c.d7210c","name":"","scope":["f9e57204.71076","5bc7345c.07b1cc","a622c92a.2d9898","368c9723.5876f8"],"x":124,"y":272,"wires":[[]]},{"id":"5bc7345c.07b1cc","type":"function","z":"6dc0247c.d7210c","name":"build message","func":"const latestMessage = flow.get('latestMessage');\nconst event = msg.payload.event;\n\nlatestMessage.eventData = msg.payload;\nlatestMessage.payload = latestMessage._originalPayload;\ndelete latestMessage._originalPayload;\ndelete latestMessage.actionable;\n\nif(env.get('userInfo')) {\n const userData = msg.userData.find(u => u.id === msg.payload.context.user_id);\n latestMessage.userData = userData;\n}\n\nif(msg.event_type === 'mobile_app_notification_cleared') {\n node.status({\n text: `cleared at: ${getPrettyDate()}`,\n shape: 'dot',\n fill: 'blue'\n });\n \n return [null, latestMessage];\n}\n\nconst index = [1,2,3].find(i => event[`action_${i}_key`] === event.action);\nnode.status({\n text: `${event[`action_${index}_title`]} at: ${getPrettyDate()}`,\n shape: 'dot',\n fill: 'green'\n});\n\nreturn latestMessage;\n\n\nfunction getPrettyDate() {\n return new Date().toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n hour12: false,\n hour: 'numeric',\n minute: 'numeric',\n });\n}","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":832,"y":176,"wires":[["974bd48d.c253e8"],[]]},{"id":"8d3bdc0c.37493","type":"switch","z":"6dc0247c.d7210c","name":"belongs here?","property":"payload.event.tag","propertyType":"msg","rules":[{"t":"eq","v":"notificationTag","vt":"flow"}],"checkall":"true","repair":false,"outputs":1,"x":432,"y":176,"wires":[["83ad2004.d04d"]]},{"id":"271e4479.b9249c","type":"ha-api","z":"6dc0247c.d7210c","name":"get user info","server":"","version":1,"debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\": \"config/auth/list\"}","dataType":"json","responseType":"json","outputProperties":[{"property":"userData","propertyType":"msg","value":"","valueType":"results"}],"x":822,"y":128,"wires":[["5bc7345c.07b1cc"]]},{"id":"3618f055.6909a","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_cleared","server":"","version":3,"exposeAsEntityConfig":"","eventType":"mobile_app_notification_cleared","eventData":"","waitForRunning":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"},{"property":"event_type","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":194,"y":224,"wires":[["8d3bdc0c.37493"]]},{"id":"83ad2004.d04d","type":"switch","z":"6dc0247c.d7210c","name":"fetch user info?","property":"userInfo","propertyType":"env","rules":[{"t":"true"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":624,"y":176,"wires":[["271e4479.b9249c"],["5bc7345c.07b1cc"]]},{"id":"9d85d137.fe487","type":"switch","z":"6dc0247c.d7210c","name":"","property":"clear_notification","propertyType":"msg","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","repair":false,"outputs":2,"x":143,"y":80,"wires":[["f9e57204.71076"],["a622c92a.2d9898"]],"l":false},{"id":"a622c92a.2d9898","type":"function","z":"6dc0247c.d7210c","name":"create clear notification","func":"const services = env.get('service');\nif(!services) {\n node.status({\n text: 'no services defined',\n shape: 'ring',\n fill: 'red'\n });\n return; \n}\n\nservices.trim().split(/,\\s*/).forEach(service => {\n if(!service) return;\n \n msg.payload = {\n service,\n data: {\n message: \"clear_notification\",\n data: {\n tag: flow.get('notificationTag'),\n }\n }\n };\n node.send(msg);\n});\n\nnode.status({text: \"cleared\"});\nnode.done();","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":318,"y":128,"wires":[["368c9723.5876f8"]]},{"id":"9bfe567c.3d10c8","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_action","server":"","version":3,"exposeAsEntityConfig":"","eventType":"mobile_app_notification_action","eventData":"","waitForRunning":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"},{"property":"event_type","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":194,"y":176,"wires":[["8d3bdc0c.37493"]]},{"id":"368c9723.5876f8","type":"api-call-service","z":"6dc0247c.d7210c","name":"","server":"","version":6,"debugenabled":false,"action":"","floorId":[],"areaId":[],"deviceId":[],"entityId":[],"labelId":[],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"all","domain":"notify","x":550,"y":80,"wires":[[]]},{"id":"bec57b08.0a8f08","type":"time-range-switch","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","lat":"","lon":"","startTime":"sunsetStart","endTime":"6:00","startOffset":"-15","endOffset":0,"x":490,"y":496,"wires":[["1b96dc9e.4f1fe3"],[]]},{"id":"31d5412.74a31be","type":"poll-state","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"garage door open?","server":"","version":3,"exposeAsEntityConfig":"","updateInterval":"30","updateIntervalType":"num","updateIntervalUnits":"seconds","outputInitially":true,"outputOnChanged":true,"entityId":"cover.garage_door","stateType":"str","ifState":"open","ifStateType":"str","ifStateOperator":"is","outputs":2,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":138,"y":528,"wires":[["6566fa4.a832004"],["5485b497.b4d90c"]]},{"id":"587bab85.857fe4","type":"subflow:6dc0247c.d7210c","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"close garage door?","env":[{"name":"service","value":"mobile_app_jason","type":"str"},{"name":"title","value":"Garage Door","type":"str"},{"name":"message","value":"Garage Door is Open","type":"str"},{"name":"action1Title","value":"Close Door","type":"str"},{"name":"action2Title","value":"Ignore 1 hour","type":"str"},{"name":"action3Title","value":"Ignore 2 hours","type":"str"},{"name":"group","value":"","type":"str"}],"x":1066,"y":544,"wires":[["e85a9609.74ddd8"],["8c90dfc.28f2f2"],["df484d95.35a35"],["13c8aaf9.466e05"]],"icon":"font-awesome/fa-car"},{"id":"e85a9609.74ddd8","type":"api-call-service","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"close garage door","server":"","version":6,"debugenabled":false,"action":"cover.close_cover","floorId":[],"areaId":[],"deviceId":[],"entityId":["cover.garage_door"],"labelId":[],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","domain":"cover","service":"close_cover","output_location":"","output_location_type":"none","x":1274,"y":496,"wires":[[]]},{"id":"1d0b3f5b.37aab1","type":"api-current-state","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"not home?","server":"","version":3,"outputs":2,"halt_if":"home","halt_if_type":"str","halt_if_compare":"is_not","entity_id":"person.jason","state_type":"str","blockInputOverrides":false,"outputProperties":[],"for":0,"forType":"num","forUnits":"minutes","x":678,"y":544,"wires":[["5b765a47a35c3f91"],[]]},{"id":"8c90dfc.28f2f2","type":"change","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","rules":[{"t":"set","p":"ignore","pt":"msg","to":"60","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1199,"y":544,"wires":[["88fd9ca4.3ea69"]],"l":false},{"id":"df484d95.35a35","type":"change","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","rules":[{"t":"set","p":"ignore","pt":"msg","to":"120","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1199,"y":576,"wires":[["88fd9ca4.3ea69"]],"l":false},{"id":"6566fa4.a832004","type":"function","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"ignore?","func":"if(msg.ignore) {\n const timeout = Date.now() + msg.ignore * 60000;\n const d = new Date(timeout);\n context.set('timeout', timeout)\n node.status({text: `Ignoring until ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`, fill: \"red\"});\n return;\n}\n\nconst timeout = context.get('timeout') || 0;\n\nif(timeout > Date.now()) return;\n\nnode.status({});\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":316,"y":528,"wires":[["bec57b08.0a8f08","8f5a5cc3.fb235"]]},{"id":"88fd9ca4.3ea69","type":"link out","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"Ignore","links":["9e5e4c22.4d925"],"x":1314,"y":544,"wires":[],"l":true},{"id":"9e5e4c22.4d925","type":"link in","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","links":["88fd9ca4.3ea69"],"x":207,"y":480,"wires":[["6566fa4.a832004"]]},{"id":"13c8aaf9.466e05","type":"change","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","rules":[{"t":"set","p":"ignore","pt":"msg","to":"30","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1199,"y":608,"wires":[["88fd9ca4.3ea69"]],"l":false},{"id":"8f5a5cc3.fb235","type":"trigger","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"2","extend":false,"overrideDelay":false,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":470,"y":544,"wires":[["1d0b3f5b.37aab1"]]},{"id":"5485b497.b4d90c","type":"change","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":306,"y":576,"wires":[["8f5a5cc3.fb235","1b96dc9e.4f1fe3"]]},{"id":"1b96dc9e.4f1fe3","type":"trigger","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"15","extend":false,"overrideDelay":false,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":678,"y":496,"wires":[["5b765a47a35c3f91"]]},{"id":"5b765a47a35c3f91","type":"delay","z":"bd764bbe2981bf8f","g":"10f78dfbdda44e3b","name":"Once / 30 mins","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"30","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":864,"y":544,"wires":[["587bab85.857fe4"]]}]