I just got a large batch of water leak sensors and was setting an automation to send a notification to my phone and I was worried that I would miss the notification. I found this page in the documentation for doing critical alerts. On iOS the notification stays persistent, even after unlocking the device. What’s really nice is that if you have an Apple Watch, the notification will also show up there and play a sound if the watch is on silent mode.
Even though I hope I never get one of these notifications, I’m really glad that this functionality exists within Home Assistant.
Just thought that was really neat.
Put the image in the
media
directory in your Home Assistant config directory then use /media/… as the URL. This works for me.For my security cameras, I have a Node-RED flow:
It:
media/alerts/
directoryHere’s the JSON for the flow:
JSON
[ { "id": "329ba4b7632a95c8", "type": "tab", "label": "Camera", "disabled": false, "info": "", "env": [] }, { "id": "f64fc2c05427d1ff", "type": "mqtt in", "z": "329ba4b7632a95c8", "name": "BlueIris/+/alert-image-b64", "topic": "BlueIris/+/alert-image-b64", "qos": "2", "datatype": "json", "broker": "e811c8eb637d26a5", "nl": false, "rap": true, "rh": 0, "inputs": 0, "x": 130, "y": 40, "wires": [ [ "5d5d6c7623201f34" ] ] }, { "id": "5d5d6c7623201f34", "type": "function", "z": "329ba4b7632a95c8", "name": "Process payload and build message", "func": "const {img, ...otherPayloadFields} = msg.payload;\n// TODO: Filter these out in Blue Iris AI config\nconst objectsToFilter = new Set([\n 'ball',\n 'sports ball',\n 'horse',\n 'book',\n 'suitcase',\n]);\n\nlet formattedObjectNames = 'Nothing';\nconst objectsData = (otherPayloadFields.json || [])\n .find(x => x.api === 'objects');\n// Would normally use `objectData?.found?.success` but Node-RED doesn't support it\nconst hasObjects = objectsData && objectsData.found && objectsData.found.success;\nif (hasObjects) {\n // Using Array.from(new Set(...)) to dedupe object names\n const objectNames = Array.from(new Set(\n (objectsData.found.predictions || [])\n .sort(x => -x.confidence)\n .map(x => x.label)\n .filter(x => !objectsToFilter.has(x))\n ));\n if (objectNames.length > 0) {\n // Capitalise first letter of first object\n objectNames[0] = objectNames[0][0].toUpperCase() + objectNames[0].substring(1)\n formattedObjectNames = objectNames.join(' and ');\n }\n}\n\nconst message = formattedObjectNames + ' detected at ' + otherPayloadFields.name;\nreturn {\n image_filename: Date.now() + '-' + otherPayloadFields.id + '.jpg',\n message,\n object_names: formattedObjectNames,\n payload: Buffer.from(img, 'base64'),\n original_payload: otherPayloadFields,\n};", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 40, "wires": [ [ "31138938746cf6fd", "53f4a59537f2893e" ] ] }, { "id": "31138938746cf6fd", "type": "file", "z": "329ba4b7632a95c8", "name": "write image file", "filename": "\"/media/alerts/\" & image_filename", "filenameType": "jsonata", "appendNewline": false, "createDir": false, "overwriteFile": "true", "encoding": "none", "x": 700, "y": 40, "wires": [ [ "6490fa5f55043f2a", "1ca18fe555c917fa" ] ] }, { "id": "6490fa5f55043f2a", "type": "api-call-service", "z": "329ba4b7632a95c8", "name": "Notify phones", "server": "b6f128b45d1d76f7", "version": 5, "debugenabled": false, "domain": "notify", "service": "all_phones", "areaId": [], "deviceId": [], "entityId": [], "data": "{\t \"message\": message,\t \"data\": {\t \"clickAction\": \"http://cameras.vpn.d.sb:81/ui3.htm?maximize=1&tab=alerts&cam=\" & original_payload.camera & \"&rec=\" & original_payload.id,\t \"image\": \"/media/local/alerts/\" & image_filename,\t \"actions\": [{\t \"action\": \"URI\",\t \"title\": \"Live View\",\t \"uri\": \"http://cameras.vpn.d.sb:81/ui3.htm?maximize=1&cam=\" & original_payload.camera\t }, {\t \"action\": \"URI\",\t \"title\": \"View Clip\",\t \"uri\": \"http://cameras.vpn.d.sb:81/ui3.htm?maximize=1&tab=alerts&cam=\" & original_payload.camera & \"&rec=\" & original_payload.id\t }],\t \t \"channel\": \"Camera\",\t \"ttl\": 0,\t \"priority\": \"high\",\t \"importance\": \"high\"\t }\t}", "dataType": "jsonata", "mergeContext": "", "mustacheAltTags": false, "outputProperties": [], "queue": "none", "x": 900, "y": 40, "wires": [ [] ] }, { "id": "1ca18fe555c917fa", "type": "api-call-service", "z": "329ba4b7632a95c8", "name": "Notify TV", "server": "b6f128b45d1d76f7", "version": 5, "debugenabled": false, "domain": "notify", "service": "shield", "areaId": [], "deviceId": [], "entityId": [], "data": "{\t \"message\": message,\t \"data\": {\t \"image\": {\t \"path\": \"/media/alerts/\" & image_filename\t }\t }\t}", "dataType": "jsonata", "mergeContext": "", "mustacheAltTags": false, "outputProperties": [], "queue": "none", "x": 880, "y": 100, "wires": [ [] ] }, { "id": "53f4a59537f2893e", "type": "switch", "z": "329ba4b7632a95c8", "name": "Is person?", "property": "message", "propertyType": "msg", "rules": [ { "t": "cont", "v": "person", "vt": "str" }, { "t": "cont", "v": "Person", "vt": "str" } ], "checkall": "false", "repair": false, "outputs": 2, "x": 710, "y": 160, "wires": [ [ "31ec03e4d62909c3" ], [ "31ec03e4d62909c3" ] ] }, { "id": "31ec03e4d62909c3", "type": "api-call-service", "z": "329ba4b7632a95c8", "name": "Notify Google Speakers", "server": "b6f128b45d1d76f7", "version": 3, "debugenabled": false, "service": "google_assistant_sdk", "entityId": "", "data": "{\"message\": message}", "dataType": "jsonata", "mustacheAltTags": false, "outputProperties": [], "queue": "none", "service_domain": "notify", "mergecontext": "", "x": 930, "y": 160, "wires": [ [] ] }, { "id": "e811c8eb637d26a5", "type": "mqtt-broker", "name": "MQTT", "broker": "mqtt.int.d.sb", "port": "1883", "clientid": "", "autoConnect": true, "usetls": false, "protocolVersion": "4", "keepalive": "60", "cleansession": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closeQos": "0", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "userProps": "", "sessionExpiry": "" }, { "id": "b6f128b45d1d76f7", "type": "server", "name": "Home Assistant", "version": 2, "addon": false, "rejectUnauthorizedCerts": true, "ha_boolean": "y|yes|true|on|home|open", "connectionDelay": true, "cacheJson": true, "heartbeat": false, "heartbeatInterval": "30" } ]