TL;DR
Using Home Assistant and Node-RED I receive actionable notifications on my android phone for Google Home alarms and timers. The notifications show all information, they are grouped and have a countdown.
Table of contents
I'm a big fan of the Google(Nest) smart devices and have multiple Hub and Mini devices in my smart home setup. Paired with Home Assistant and Node-RED I use them to control every device and automation flow.
Alarms & Timers
The alarm and timer functions on the devices are the most frequent used by me, I use them every day.
They work fine, but I'm missing some functionality to make them awesome.
- They don't show up on my phone
- I can't control them by phone
- When the device rings and I'm not in the same room I won't notice the alarm / timer ringing
Home Assistant Google Home integration
This week I discovered a new community integration called "Google Home" which exposes the alarms and timers as sensors in HA.
leikoilja / ha-google-home
Home Assistant Google Home custom component
I installed it via HACS and it works really nice, the sensor entities for alarms and timers are created per device.
I wondered if I could use it for some new automations which would add more functionality for me, especially have them as notifications on my phone.
Goal
- Show alarms and timers as notifications on my phone
- Let them countdown to the time they fire
- When they ring the notification should show this
- When I dismiss a notification the alarm / timer should be deleted
- If they are deleted on the google device the notification should be removed
Node-RED
I use Node-RED for all my automations, I like the way it visualizes the flows. It's often described as a low code solution, as a developer I like the option to apply code logic as well.
What you see next is the end result of some trial and error programming, usually I start with just 1 sensor input and a normal flow. Which I then finetune and often convert to a reusable subflow.
What the main flow will look like:
In the end result I wanted to have just 1 sublfow which would control everything, having multiple alarm and timer sensors as input.
The subflow
The top flow handles all input.
The bottom flow handles cleared notifications.
Top flow
- Parse the input from the sensors
- Determine if it is an alarm or timer sensor
- Cache the active items (with type and sensor info) in memory
- For every active item create/update a notification message
- For every inactive item make a clear_notification message
- Send the messages to the Call service node
Let's start with the "Parse alarms & timers" function node which does all the heavy lifting.
I use the "Setup" tab of the function node to set some defaults which are used in the flow:
// Set type defaults
const defaults = {
timer: {
id: 'timer_id',
label: '⏳',
items: 'timers',
name: 'Timers',
deleteService: 'delete_timer'
},
alarm: {
id: 'alarm_id',
label: '⏰',
items: 'alarms',
name: 'Alarms',
deleteService: 'delete_alarm'
},
}
flow.set('defaults', defaults);
The "Function" tab
const defaults = flow.get('defaults');
const service = env.get('service');
const messages = [];
let type = '';
if(!msg.data) {
return null;
}
// Determine type: alarm or timer
if(typeof msg.data.new_state.attributes.timers !== 'undefined' || typeof msg.data.old_state.attributes.timers !== 'undefined' ) {
type = 'timer';
} else if (typeof msg.data.new_state.attributes.alarms !== 'undefined' || typeof msg.data.old_state.attributes.alarms !== 'undefined') {
type = 'alarm';
} else {
return null;
}
const typeValues = defaults[type];
// Create a alarm/timer item ready to be used by the service
const createItem = (item, sensor, type, device) => {
const doneTime = new Date(item.fire_time * 1000).toTimeString().substr(0, 5);
let label = `${typeValues.label}`;
let vibration = '';
let chronometer = true;
if(item.status === 'ringing') {
label = item.label ? `${label} ${item.label} RINGING` : `${label} RINGING`;
vibration = '100, 1000, 100, 1000, 100, 100, 1000, 100, 1000, 100';
chronometer = false;
} else {
label = item.label ? `${label} ${doneTime} ${item.label}` : `${label} ${doneTime}`;
}
let title = label;
if(item.duration) {
title = `${label} (${item.duration})`;
}
return {
id: item[typeValues.id],
sensor: sensor,
device: device,
type: type,
status: item.status,
data:{
title: title,
message: `${device}`,
data: {
tag: item[typeValues.id],
chronometer: chronometer,
when: item.fire_time,
sticky: 'true',
group: typeValues.name,
vibrationPattern: vibration
}
}
}
};
const mapItems = (arr) => {
return arr.map((item) => { return createItem(item, msg.data.entity_id, type, msg.data.old_state.attributes.friendly_name) })
}
const activeItems = mapItems(msg.data.new_state.attributes[typeValues.items] || []).filter(item => item.status !== 'none');
const cachedItems = flow.get('cachedItems') || new Map();
// Update or create notifications for active items
activeItems.forEach((item) => {
messages.push({
payload: {
service: service,
data: item.data
}
});
cachedItems.set(item.id, item);
})
// Clear expired/deleted notifications
cachedItems.forEach((item, id) => {
const findItem = activeItems.find(newItem => newItem.id === id);
if(!findItem && item.sensor === msg.data.entity_id) {
messages.push({
payload: {
service: service,
data: {
message: 'clear_notification',
data: {
tag: id
}
}
}
});
cachedItems.delete(id);
}
})
flow.set('cachedItems', cachedItems);
// Send notifications as a stream
messages.forEach((msg) => node.send(msg));
// All done
node.done();
Bottom flow
- Detect cleared notifications
- Look it up in the cache
- When found call the delete_alarm/timer service
- Remove the item from the cache
const cachedItems = flow.get('cachedItems');
const defaults = flow.get('defaults');
// Find cleared item
const findItem = cachedItems.get(msg.payload.event.tag);
if(!findItem) {
return null
}
node.send(
{
payload: {
service: defaults[findItem.type].deleteService,
data: {
entity_id: findItem.sensor,
[defaults[findItem.type].id]: findItem.id
}
}
}
)
// Clean up
cachedItems.delete(findItem.id);
flow.set('cachedItems', cachedItems);
node.done();
How to use
- Import the subflow in NR:
- Then edit the Environment variable for the service:
- Add the subflow and connect some alarm and/or timer sensors as
events_state
nodes. - All done, try it by creating a alarm / timer on the google home device, within a few seconds you should receive the notification on your phone.
Top comments (4)
Hello,
Thanks for your flow, I'm using it but sometimes it didn't work.
It's the line
flow.get() returns an object so the functions Map.set() and Map.forEach() aren't working and the function block stops with an error.
Just change it to e.g.
and it should work. I'll test it further and report back if I still get errors:)
I really appreciate your work
Love this but cant get it to work. I'm not exactly great with computers but I try lol.
I've changed the service to my phone id and when I set an alarm it does count down but I get no "RINGING" notification and when I dismiss the notification it doesnt stop or delete the alarm.
Any help would be great because its an awesome idea
hello,
I'm trying to use this flow for 2 mobiles phones but only the first one is working.
I've tried to use 2 subflows (with different eviroment variable) and 2 distinct flow but only the first one is working. Am I doing something wrong?
Thank you
Hi there,
I loved your scripts. It was working fine. But now I get the following errror:
node: Parse alarms & timers
function : (error)
"TypeError: Cannot convert undefined or null to object"