I had a need to be able to check if a mobile app is responding and came up with the below sub-flow
Essentially it sends a notification to the notify service of a device you want to ping with the confirmation property set to 1, tag set to a random guid, and channel set to “ping”, with a 1 second timeout
in the sub-flow settings, you set the notify service to use (just the part after “notify.”) and the time you want to wait for a device to check in in miliseconds. I choose 600000 for 20 minutes
When you send a message thru the subflow, it will queue until the mobile_app_notification_received event with the matching tag is seen. If the timeout specified expires, the message is sent to output1
If your phone responds before the timeout, the message is sent to output2
You can then perform actions with a high degree of certainty that your device is either powered off or not reachable for another reason. (no network.cellular coverage)
you can pickup a payload sent to the sub-flow as msg.message on the other side at either output
In my example, I use the pyton_script.set_service to change the device_tracker state for my mobile devices to null, so that dead devices don’t stick to a zone, and I use an inject node to send this every 90 minutes.
Another use is I want to send an emergency message to a satellite communicator, but the messages are $0.50 a pop, so if my phone is available I want to get the message there. So I send a payload to the subflow, and if my phone responds to the notification ping, I get the HomeAssistant notification, but if it doesn’t respond in 20 minutes, it will send the message to my satPhone via a command_line notify service
[
{
"id": "afd780a3aaac4bcf",
"type": "subflow",
"name": "Mobile App Ping",
"info": "",
"category": "",
"in": [
{
"x": 260,
"y": 240,
"wires": [
{
"id": "51a2a04b6bbd9a23"
}
]
}
],
"out": [
{
"x": 1100,
"y": 180,
"wires": [
{
"id": "4597e138d443ff83",
"port": 0
}
]
},
{
"x": 1100,
"y": 260,
"wires": [
{
"id": "4597e138d443ff83",
"port": 1
}
]
}
],
"env": [
{
"name": "service",
"type": "str",
"value": "",
"ui": {
"label": {
"en-US": "Notify Service"
},
"type": "input",
"opts": {
"types": [
"str"
]
}
}
},
{
"name": "delayTime",
"type": "num",
"value": "",
"ui": {
"label": {
"en-US": "Delay Time"
},
"type": "input",
"opts": {
"types": [
"num"
]
}
}
},
{
"name": "debug",
"type": "bool",
"value": "false"
}
],
"meta": {},
"color": "#DDAA99"
},
{
"id": "0e35dcf6e5b6519c",
"type": "change",
"z": "afd780a3aaac4bcf",
"name": "",
"rules": [
{
"t": "set",
"p": "release",
"pt": "msg",
"to": "true",
"tot": "bool"
},
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "delete",
"p": "data",
"pt": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 700,
"y": 340,
"wires": [
[
"4597e138d443ff83"
]
]
},
{
"id": "4cc227be40f4d585",
"type": "switch",
"z": "afd780a3aaac4bcf",
"name": "",
"property": "data.event.tag",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "pingTag",
"vt": "flow"
},
{
"t": "else"
}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 470,
"y": 340,
"wires": [
[
"0e35dcf6e5b6519c"
],
[]
]
},
{
"id": "3a51d7434cbd50fb",
"type": "api-call-service",
"z": "afd780a3aaac4bcf",
"name": "",
"server": "YOUR HA CONF NODE",
"version": 5,
"debugenabled": false,
"domain": "notify",
"service": "",
"areaId": [],
"deviceId": [],
"entityId": [],
"data": "",
"dataType": "jsonata",
"mergeContext": "callServiceData",
"mustacheAltTags": false,
"outputProperties": [
{
"property": "message",
"propertyType": "msg",
"value": "message",
"valueType": "flow"
}
],
"queue": "none",
"x": 700,
"y": 200,
"wires": [
[
"4597e138d443ff83"
]
]
},
{
"id": "51a2a04b6bbd9a23",
"type": "function",
"z": "afd780a3aaac4bcf",
"name": "create service call",
"func": "function generateGUID() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0,\n v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n// Set the generated GUID as flow.ping_tag\nflow.set('pingTag', generateGUID());\nflow.set('message', msg.payload);\n\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}\nconst messages = env.get('message');\nif(!messages) {\n node.status({\n text: 'no message 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: \"ping\",\n data: {\n tag: flow.get('pingTag'),\n channel: 'ping',\n persistent: false,\n confirmation: true,\n timeout: 1,\n ttl: 0,\n priority: 'high'\n }\n }\n };\n node.send(msg);\n});\n\nnode.done();",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 430,
"y": 240,
"wires": [
[
"3a51d7434cbd50fb"
]
]
},
{
"id": "f316bbcae24b7ce8",
"type": "server-events",
"z": "afd780a3aaac4bcf",
"name": "",
"server": "YOUR HA CONF NODE",
"version": 3,
"exposeAsEntityConfig": "",
"eventType": "mobile_app_notification_received",
"eventData": "",
"waitForRunning": true,
"outputProperties": [
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
}
],
"x": 200,
"y": 340,
"wires": [
[
"4cc227be40f4d585"
]
]
},
{
"id": "4597e138d443ff83",
"type": "function",
"z": "afd780a3aaac4bcf",
"name": "Delay Messages",
"func": "// Get the delay time from the environment variable\nvar delayTime = env.get('delayTime') || 1000; // Default to 1000ms if not set\n\n// Initialize a queue to hold delayed messages\ncontext.queue = context.queue || [];\n\n// Variable to control the queue gate\nvar gateStatus = false;\n\n// Function to display debug messages based on the debug environment variable\nfunction debug(message) {\n if (env.get('debug') === true) {\n node.warn(message);\n }\n}\n\n// Main function to handle incoming messages\nfunction handleMessage(msg) {\n debug(\"Entering handleMessage\");\n\n // Check if the message has the release flag\n if (msg.release === true || msg.release === \"true\") {\n debug(\"msg.release = true\");\n\n // Set the gate status to open\n gateStatus = true;\n\n // Release all queued messages immediately\n while (context.queue.length > 0) {\n var queuedMsg = context.queue.shift();\n debug(\"Message released to output2 with delayed=true due to msg.release = true\");\n node.send([null, queuedMsg]); // Send to output2\n }\n } else {\n debug(\"msg.release = false\");\n\n // Process the message based on the gate status\n if (gateStatus) {\n // If the gate is open, route the message directly\n debug(\"Gate is open, routing message directly to output2 with delayed=false\");\n msg.delayed = false;\n msg.delayTimer = 0;\n node.send([null, msg]); // Send to output2\n } else {\n // If the gate is closed, add the message to the queue\n debug(\"Queue gate is closed, adding message to the queue with delayed=true\");\n context.queue.push({\n msg: msg,\n delayed: true,\n delayTimer: delayTime\n });\n\n // Process the queued messages when the delay timer expires\n setTimeout(function () {\n if (context.queue.length > 0) {\n var delayedMsg = context.queue.shift();\n debug(\"Message released to output1 with delayed=true after delayTimer expiration\");\n node.send([delayedMsg]); // Send to output1\n }\n }, delayTime);\n }\n }\n}\n\n// Process the incoming message\nhandleMessage(msg);\n",
"outputs": 2,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 930,
"y": 200,
"wires": [
[],
[]
]
},
{
"id": "1963a8ef6f363238",
"type": "debug",
"z": "afd780a3aaac4bcf",
"name": "debug 4",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 900,
"y": 380,
"wires": []
},
{
"id": "YOUR HA CONF NODE",
"type": "server",
"name": "Home Assistant",
"addon": true,
"rejectUnauthorizedCerts": true,
"ha_boolean": "",
"connectionDelay": true,
"cacheJson": false,
"heartbeat": true,
"heartbeatInterval": "30",
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": "at:",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "h23",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": false
},
{
"id": "5fe8c7da2fc251e7",
"type": "subflow:afd780a3aaac4bcf",
"z": "a9cede48295fbf38",
"name": "",
"x": 220,
"y": 1300,
"wires": [
[],
[]
]
}
]