At least on Android, you can make TTS go through your alarm channel on your phone at max volume, to really make sure you don't miss it. I do this for my alarm and doorbell (only when I'm at home).
homeassistant
Home Assistant is open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server. Available for free at home-assistant.io
I have done this for my leak detectors.
Almost everything else obeys DND, but water? That's gonna wake me up.
I do the same (except on Android instead of iOS) and it works great. I have the alerts go to all our TVs (via our Nvidia Shield boxes and https://www.home-assistant.io/integrations/nfandroidtv/) and Google speakers too.
Recently had one of the leak detectors detect a leak and the alerting worked great. Turns out the leak was a bottle of distilled water dripping out the back of the bottle.
I've struggled with getting my TV notifications to show an image because I can't seem to get the URLs right. Is that an issue you've had? Any ideas on a solution?
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:
- Listens for MQTT events from Blue Iris
- Builds a message based on the AI detection result and camera name (e.g. "Person detected at Front")
- Write image file from MQTT event to
media/alerts/
directory - Send notification to phones and TV (currently just goes to one Shield - I need to set up the second one eventually)
- If a person is detected, send notification to Google speakers
Here'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"
}
]
I did not know that you could send notifications to an Nvidia Shield. I’ll have to look into that this evening. Thanks!
It works well, and the TV app is free: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google&hl=en_US&gl=US, you just need to download it from the Play Store on the Shield. There's a companion app for phones that costs money, but you don't need that one - it's only for sending messages from your phone to the TV.
Set it up last night. Can confirm it was quick and easy. Thanks!