Get Google Home alarms & timers as notifications
Tags: homeassistant, nodered, automation, smarthome
Published: 2021-04-12 22:37:52

> **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
1. [Alarms & Timers](#alarms-amp-timers)
2. [Google Home integration](#home-Assistant-google-home-integration)
3. [Goal](#goal)
4. [Node-RED](#node-red)
  1. [The subflow](#the-subflow)
  2. [Top flow](#top-flow)
  3. [Bottom flow](#bottom-flow)
  4. [How to use](#how-to-use)

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.
{% github leikoilja/ha-google-home no-readme %}
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
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wo0pjgsrmo2kjppyxjvg.gif)

* 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:  
![The flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvhhkr2jrnhikq749nj6.png)

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
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlv7eode0wtwnz4ae84a.png)
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 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vzv7beilqdp5s411rk96.png)

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:

``` javascript
// 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  



``` javascript
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
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdfmlen09i405gbrho7s.png)
``` javascript
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
1. Import the subflow in NR:
{% gist https://gist.github.com/mattieha/54bb65db8ddc47ce78d5d3695d5b0f0b %} 
2. Then edit the Environment variable for the service:![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xu95yuuololgswqmgm3q.png)
3. Add the subflow and connect some alarm and/or timer sensors as `events_state` nodes.
![The flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvhhkr2jrnhikq749nj6.png)
4. 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.