Actionable Notifications Subflow for Android

Questions and Discussion

Post questions and follow the discussion about this recipe hereopen in new window

screenshot

v1.0.0 - Requires Nodejs v12

Changes

  • Any setting can be set or overwritten using msg.actionable to pass a notification object into the node.
[{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"Android actionable notification v1.0.0\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        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,"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":1,"event_type":"mobile_app_notification_cleared","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"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":1,"event_type":"mobile_app_notification_action","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"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":4,"debugenabled":false,"domain":"notify","target":{"areaId":[],"deviceId":[],"entityId":[]},"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"all","x":550,"y":80,"wires":[[]]},{"id":"8a2ecff38edf7efd","type":"subflow:6dc0247c.d7210c","z":"a38123c1.2fd3f","name":"","x":516,"y":544,"wires":[[],[],[],[]]}]
Old versions

v0.0.0

[{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"[Documentation](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/actionable-notifications-subflow-for-android.html)\n","category":"","in":[{"x":84,"y":80,"wires":[{"id":"9d85d137.fe487"}]}],"out":[{"x":1172,"y":128,"wires":[{"id":"974bd48d.c253e8","port":0}]},{"x":1172,"y":176,"wires":[{"id":"974bd48d.c253e8","port":1}]},{"x":1172,"y":224,"wires":[{"id":"974bd48d.c253e8","port":2}]},{"x":964,"y":240,"wires":[{"id":"5bc7345c.07b1cc","port":1}]}],"env":[{"name":"service","type":"str","value":"","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"]}}}],"color":"#DDAA99","outputLabels":["Action 1","Action 2","Action 3","Cleared"],"status":{"x":244,"y":272,"wires":[{"id":"204dbcfc.144ae4","port":0}]}},{"id":"f9e57204.71076","type":"function","z":"6dc0247c.d7210c","name":"create service call","func":"const actions = [];\n[1,2,3].forEach(i => {\n    const name = `action${i}`\n    const id = flow.get(`${name}Id`);\n    const title = env.get(`${name}Title`);\n    const uri = env.get(`${name}Uri`);\n    const action = !!uri.length ? 'URI' : title ? flow.get(`${name}Id`) : undefined;\n    \n    actions.push({\n        action,\n        title,\n        uri\n    });\n});\n\nmsg._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\nservices.trim().split(/,\\s*/).forEach(service => {\n    if(!service) return;\n    \n    msg.payload = {\n        service,\n        data: {\n            title: env.get('title'),\n            message: env.get('message'),\n            data: {\n                tag: flow.get('notificationTag'),\n                actions,\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    };\n    node.send(msg);\n});\n\nnode.done();","outputs":1,"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":"","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;\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":"","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","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\": \"config/auth/list\"}","dataType":"json","location":"userData","locationType":"msg","responseType":"json","x":822,"y":128,"wires":[["5bc7345c.07b1cc"]]},{"id":"3618f055.6909a","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_cleared","event_type":"mobile_app_notification_cleared","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"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.done();","outputs":1,"noerr":0,"initialize":"","finalize":"","x":318,"y":128,"wires":[["368c9723.5876f8"]]},{"id":"9bfe567c.3d10c8","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_action","event_type":"mobile_app_notification_action","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"x":194,"y":176,"wires":[["8d3bdc0c.37493"]]},{"id":"368c9723.5876f8","type":"api-call-service","z":"6dc0247c.d7210c","name":"","version":1,"debugenabled":false,"service_domain":"notify","service":"","entityId":"","data":"","dataType":"json","mergecontext":"callServiceData","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":550,"y":80,"wires":[[]]}]
OptionsDescriptionDocumentation
Notify ServiceCan take multiple services as a comma delimited list e.g.: mobile_app_username, mobile_app_username2
TitleTop line of textlinkopen in new window
MessageSecond line of text accepts HTML tagslinkopen in new window
Action TitleThe button titlelinkopen in new window
Action URIlovelace dashboard, https:// or app://. If URI is defined no action is returned to Node-REDlinkopen in new window
User InfoWill resolve user info in Node-RED and place it in msg.userData
StickySet whether to dismiss the notification upon selecting it or notlinkopen in new window
Groupstringlinkopen in new window
ColorColor name or the hex codelinkopen in new window
TimeoutHow long a notification will be shown on a users device before being removed automaticallylinkopen in new window
IconPath to iconlinkopen in new window

Any setting can be set or overwritten using msg.actionable to pass a notification object into the node.

Demo flow

screenshot

[{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"[Documentation](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/actionable-notifications-subflow-for-android.html)\n","category":"","in":[{"x":84,"y":80,"wires":[{"id":"9d85d137.fe487"}]}],"out":[{"x":1172,"y":128,"wires":[{"id":"974bd48d.c253e8","port":0}]},{"x":1172,"y":176,"wires":[{"id":"974bd48d.c253e8","port":1}]},{"x":1172,"y":224,"wires":[{"id":"974bd48d.c253e8","port":2}]},{"x":964,"y":240,"wires":[{"id":"5bc7345c.07b1cc","port":1}]}],"env":[{"name":"service","type":"str","value":"","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"]}}}],"color":"#DDAA99","outputLabels":["Action 1","Action 2","Action 3","Cleared"],"status":{"x":244,"y":272,"wires":[{"id":"204dbcfc.144ae4","port":0}]}},{"id":"f9e57204.71076","type":"function","z":"6dc0247c.d7210c","name":"create service call","func":"const actions = [];\n[1,2,3].forEach(i => {\n    const name = `action${i}`\n    const id = flow.get(`${name}Id`);\n    const title = env.get(`${name}Title`);\n    const uri = env.get(`${name}Uri`);\n    const action = !!uri.length ? 'URI' : title ? flow.get(`${name}Id`) : undefined;\n    \n    actions.push({\n        action,\n        title,\n        uri\n    });\n});\n\nmsg._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\nservices.trim().split(/,\\s*/).forEach(service => {\n    if(!service) return;\n    \n    msg.payload = {\n        service,\n        data: {\n            title: env.get('title'),\n            message: env.get('message'),\n            data: {\n                tag: flow.get('notificationTag'),\n                actions,\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    };\n    node.send(msg);\n});\n\nnode.done();","outputs":1,"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":"","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;\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":"","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","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\": \"config/auth/list\"}","dataType":"json","location":"userData","locationType":"msg","responseType":"json","x":822,"y":128,"wires":[["5bc7345c.07b1cc"]]},{"id":"3618f055.6909a","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_cleared","event_type":"mobile_app_notification_cleared","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"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.done();","outputs":1,"noerr":0,"initialize":"","finalize":"","x":318,"y":128,"wires":[["368c9723.5876f8"]]},{"id":"9bfe567c.3d10c8","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_action","event_type":"mobile_app_notification_action","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"x":194,"y":176,"wires":[["8d3bdc0c.37493"]]},{"id":"368c9723.5876f8","type":"api-call-service","z":"6dc0247c.d7210c","name":"","version":1,"debugenabled":false,"service_domain":"notify","service":"","entityId":"","data":"","dataType":"json","mergecontext":"callServiceData","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":550,"y":80,"wires":[[]]},{"id":"581acb2a.792564","type":"subflow:6dc0247c.d7210c","z":"31cdc.c18e83244","name":"","env":[{"name":"service","value":"mobile_app_jason","type":"str"},{"name":"title","value":"testing actionable notification","type":"str"},{"name":"action1Title","value":"action one","type":"str"},{"name":"action2Title","value":"open HA","type":"str"},{"name":"action2Uri","value":"/lovelace/0","type":"str"},{"name":"action3Title","value":"action three","type":"str"},{"name":"userInfo","type":"bool","value":"true"},{"name":"group","value":"","type":"str"}],"x":436,"y":1136,"wires":[["d2b46240.6de89"],["a63fb7bc.64af88"],["2f4904b8.568d3c"],["75334c15.956694"]]},{"id":"aa8e9e73.4957f","type":"inject","z":"31cdc.c18e83244","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"abc","payload":"default payload","payloadType":"str","x":202,"y":1136,"wires":[["581acb2a.792564"]]},{"id":"d2b46240.6de89","type":"debug","z":"31cdc.c18e83244","name":"action1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":652,"y":1088,"wires":[]},{"id":"a63fb7bc.64af88","type":"debug","z":"31cdc.c18e83244","name":"action2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":652,"y":1120,"wires":[]},{"id":"2f4904b8.568d3c","type":"debug","z":"31cdc.c18e83244","name":"action3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":652,"y":1152,"wires":[]},{"id":"75334c15.956694","type":"debug","z":"31cdc.c18e83244","name":"cleared","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":652,"y":1184,"wires":[]},{"id":"81797230.4299e","type":"inject","z":"31cdc.c18e83244","name":"clear_notification","props":[{"p":"clear_notification","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":208,"y":1184,"wires":[["581acb2a.792564"]]}]

Use Case 1: Get a notification when garage door is left open with ability to ignore the alert for an amount of time

screenshot

[{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"[Documentation](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/actionable-notifications-subflow-for-android.html)\n","category":"","in":[{"x":84,"y":80,"wires":[{"id":"9d85d137.fe487"}]}],"out":[{"x":1172,"y":128,"wires":[{"id":"974bd48d.c253e8","port":0}]},{"x":1172,"y":176,"wires":[{"id":"974bd48d.c253e8","port":1}]},{"x":1172,"y":224,"wires":[{"id":"974bd48d.c253e8","port":2}]},{"x":964,"y":240,"wires":[{"id":"5bc7345c.07b1cc","port":1}]}],"env":[{"name":"service","type":"str","value":"","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"]}}}],"color":"#DDAA99","outputLabels":["Action 1","Action 2","Action 3","Cleared"],"status":{"x":244,"y":272,"wires":[{"id":"204dbcfc.144ae4","port":0}]}},{"id":"f9e57204.71076","type":"function","z":"6dc0247c.d7210c","name":"create service call","func":"const actions = [];\n[1,2,3].forEach(i => {\n    const name = `action${i}`\n    const id = flow.get(`${name}Id`);\n    const title = env.get(`${name}Title`);\n    const uri = env.get(`${name}Uri`);\n    const action = !!uri.length ? 'URI' : title ? flow.get(`${name}Id`) : undefined;\n    \n    actions.push({\n        action,\n        title,\n        uri\n    });\n});\n\nmsg._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\nservices.trim().split(/,\\s*/).forEach(service => {\n    if(!service) return;\n    \n    msg.payload = {\n        service,\n        data: {\n            title: env.get('title'),\n            message: env.get('message'),\n            data: {\n                tag: flow.get('notificationTag'),\n                actions,\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    };\n    node.send(msg);\n});\n\nnode.done();","outputs":1,"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":"","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;\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":"","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","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\": \"config/auth/list\"}","dataType":"json","location":"userData","locationType":"msg","responseType":"json","x":822,"y":128,"wires":[["5bc7345c.07b1cc"]]},{"id":"3618f055.6909a","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_cleared","event_type":"mobile_app_notification_cleared","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"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.done();","outputs":1,"noerr":0,"initialize":"","finalize":"","x":318,"y":128,"wires":[["368c9723.5876f8"]]},{"id":"9bfe567c.3d10c8","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_action","event_type":"mobile_app_notification_action","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"x":194,"y":176,"wires":[["8d3bdc0c.37493"]]},{"id":"368c9723.5876f8","type":"api-call-service","z":"6dc0247c.d7210c","name":"","version":1,"debugenabled":false,"service_domain":"notify","service":"","entityId":"","data":"","dataType":"json","mergecontext":"callServiceData","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":550,"y":80,"wires":[[]]},{"id":"bec57b08.0a8f08","type":"time-range-switch","z":"31cdc.c18e83244","name":"","lat":"","lon":"","startTime":"sunsetStart","endTime":"6:00","startOffset":"-15","endOffset":0,"x":474,"y":1552,"wires":[["1b96dc9e.4f1fe3"],[]]},{"id":"31d5412.74a31be","type":"poll-state","z":"31cdc.c18e83244","name":"garage door open?","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"30","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":true,"entity_id":"cover.garage_door","state_type":"str","halt_if":"open","halt_if_type":"str","halt_if_compare":"is","outputs":2,"x":122,"y":1584,"wires":[["6566fa4.a832004"],["5485b497.b4d90c"]]},{"id":"765bef97.90e0e","type":"throttle","z":"31cdc.c18e83244","name":"Once / 30 mins","throttleType":"time","timeLimit":"15","timeLimitType":"minutes","countLimit":0,"blockSize":0,"locked":false,"x":848,"y":1600,"wires":[["587bab85.857fe4"]]},{"id":"587bab85.857fe4","type":"subflow:6dc0247c.d7210c","z":"31cdc.c18e83244","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":1050,"y":1600,"wires":[["e85a9609.74ddd8"],["8c90dfc.28f2f2"],["df484d95.35a35"],["13c8aaf9.466e05"]],"icon":"font-awesome/fa-car"},{"id":"e85a9609.74ddd8","type":"api-call-service","z":"31cdc.c18e83244","name":"close garage door","version":1,"debugenabled":false,"service_domain":"cover","service":"close_cover","entityId":"cover.garage_door","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1258,"y":1552,"wires":[[]]},{"id":"1d0b3f5b.37aab1","type":"api-current-state","z":"31cdc.c18e83244","name":"not home?","version":1,"outputs":2,"halt_if":"home","halt_if_type":"num","halt_if_compare":"is_not","override_topic":false,"entity_id":"person.jason","state_type":"str","state_location":"","override_payload":"none","entity_location":"","override_data":"none","blockInputOverrides":false,"x":662,"y":1600,"wires":[["765bef97.90e0e"],[]]},{"id":"8c90dfc.28f2f2","type":"change","z":"31cdc.c18e83244","name":"","rules":[{"t":"set","p":"ignore","pt":"msg","to":"60","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1183,"y":1600,"wires":[["88fd9ca4.3ea69"]],"l":false},{"id":"df484d95.35a35","type":"change","z":"31cdc.c18e83244","name":"","rules":[{"t":"set","p":"ignore","pt":"msg","to":"120","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1183,"y":1632,"wires":[["88fd9ca4.3ea69"]],"l":false},{"id":"6566fa4.a832004","type":"function","z":"31cdc.c18e83244","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":300,"y":1584,"wires":[["bec57b08.0a8f08","8f5a5cc3.fb235"]]},{"id":"88fd9ca4.3ea69","type":"link out","z":"31cdc.c18e83244","name":"Ignore","links":["9e5e4c22.4d925"],"x":1298,"y":1600,"wires":[],"l":true},{"id":"9e5e4c22.4d925","type":"link in","z":"31cdc.c18e83244","name":"","links":["88fd9ca4.3ea69"],"x":191,"y":1536,"wires":[["6566fa4.a832004"]]},{"id":"13c8aaf9.466e05","type":"change","z":"31cdc.c18e83244","name":"","rules":[{"t":"set","p":"ignore","pt":"msg","to":"30","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1183,"y":1664,"wires":[["88fd9ca4.3ea69"]],"l":false},{"id":"8f5a5cc3.fb235","type":"trigger","z":"31cdc.c18e83244","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"2","extend":false,"overrideDelay":false,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":454,"y":1600,"wires":[["1d0b3f5b.37aab1"]]},{"id":"5485b497.b4d90c","type":"change","z":"31cdc.c18e83244","name":"reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":1632,"wires":[["8f5a5cc3.fb235","1b96dc9e.4f1fe3"]]},{"id":"1b96dc9e.4f1fe3","type":"trigger","z":"31cdc.c18e83244","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"15","extend":false,"overrideDelay":false,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":662,"y":1552,"wires":[["765bef97.90e0e"]]}]

Required Nodes

Use Case 2: Ask if vacation mode should be turned on after being away for 24 hours

screenshot

[{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"[Documentation](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/actionable-notifications-subflow-for-android.html)\n","category":"","in":[{"x":84,"y":80,"wires":[{"id":"9d85d137.fe487"}]}],"out":[{"x":1172,"y":128,"wires":[{"id":"974bd48d.c253e8","port":0}]},{"x":1172,"y":176,"wires":[{"id":"974bd48d.c253e8","port":1}]},{"x":1172,"y":224,"wires":[{"id":"974bd48d.c253e8","port":2}]},{"x":964,"y":240,"wires":[{"id":"5bc7345c.07b1cc","port":1}]}],"env":[{"name":"service","type":"str","value":"","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"]}}}],"color":"#DDAA99","outputLabels":["Action 1","Action 2","Action 3","Cleared"],"status":{"x":244,"y":272,"wires":[{"id":"204dbcfc.144ae4","port":0}]}},{"id":"f9e57204.71076","type":"function","z":"6dc0247c.d7210c","name":"create service call","func":"const actions = [];\n[1,2,3].forEach(i => {\n    const name = `action${i}`\n    const id = flow.get(`${name}Id`);\n    const title = env.get(`${name}Title`);\n    const uri = env.get(`${name}Uri`);\n    const action = !!uri.length ? 'URI' : title ? flow.get(`${name}Id`) : undefined;\n    \n    actions.push({\n        action,\n        title,\n        uri\n    });\n});\n\nmsg._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\nservices.trim().split(/,\\s*/).forEach(service => {\n    if(!service) return;\n    \n    msg.payload = {\n        service,\n        data: {\n            title: env.get('title'),\n            message: env.get('message'),\n            data: {\n                tag: flow.get('notificationTag'),\n                actions,\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    };\n    node.send(msg);\n});\n\nnode.done();","outputs":1,"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":"","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;\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":"","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","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\": \"config/auth/list\"}","dataType":"json","location":"userData","locationType":"msg","responseType":"json","x":822,"y":128,"wires":[["5bc7345c.07b1cc"]]},{"id":"3618f055.6909a","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_cleared","event_type":"mobile_app_notification_cleared","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"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.done();","outputs":1,"noerr":0,"initialize":"","finalize":"","x":318,"y":128,"wires":[["368c9723.5876f8"]]},{"id":"9bfe567c.3d10c8","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_action","event_type":"mobile_app_notification_action","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"x":194,"y":176,"wires":[["8d3bdc0c.37493"]]},{"id":"368c9723.5876f8","type":"api-call-service","z":"6dc0247c.d7210c","name":"","version":1,"debugenabled":false,"service_domain":"notify","service":"","entityId":"","data":"","dataType":"json","mergecontext":"callServiceData","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":550,"y":80,"wires":[[]]},{"id":"b35b9988.72c6d8","type":"server-state-changed","z":"f6d789f6.6b5388","name":"Away?","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"person.jason","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"home","halt_if_type":"str","halt_if_compare":"is_not","outputs":2,"output_only_on_state_change":true,"for":"24","forType":"num","forUnits":"hours","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":290,"y":256,"wires":[["4587eb7f.fb86a4"],[]]},{"id":"4587eb7f.fb86a4","type":"api-current-state","z":"f6d789f6.6b5388","name":"Vacation mode off?","version":1,"outputs":2,"halt_if":"off","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_boolean.vacation_mode","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":458,"y":256,"wires":[["e5d8423c.2ccfe"],[]]},{"id":"be568091.56bbb","type":"api-call-service","z":"f6d789f6.6b5388","name":"Turn On Vacation Mode","version":1,"debugenabled":false,"service_domain":"input_boolean","service":"turn_on","entityId":"input_boolean.vacation_mode","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":942,"y":256,"wires":[[]]},{"id":"e5d8423c.2ccfe","type":"subflow:6dc0247c.d7210c","z":"f6d789f6.6b5388","name":"Turn on Vacation Mode?","env":[{"name":"service","value":"mobile_app_jason","type":"str"},{"name":"title","value":"Vacation Mode","type":"str"},{"name":"message","value":"You've been away for 24 hours.<br>Do you want to turn on vacation mode?","type":"str"},{"name":"action1Title","value":"Yes","type":"str"},{"name":"action2Title","value":"No","type":"str"},{"name":"group","value":"","type":"str"}],"x":686,"y":272,"wires":[["be568091.56bbb"],[],[],[]]}]

Also see: