Posts Voice Event Reminders with a User Interface
Post
Cancel

Voice Event Reminders with a User Interface

Disclosure: This article may contain affiliate links. If you decide to make a purchase, I'll make a small commission at no extra cost to you.

Some of my favorite “quality of life” automations are: 1.) an LED strip that turns on when anyone enters the kitchen, 2.) bedside lights that help us wake up in the morning, and 3.) Alexa voice alerts that remind us when it is about time to leave for events (school, water polo, circus class, etc).

I got caught up on the Node-Red hype train for a short while, but that ride didn’t last long. To begin the new decade I decided to retire Node-Red for good. To do so required me to move my many Event Reminders over to YAML and I wanted to really push my skills with this project.

Updates

  • 2022-01-22
    • Fix broken links to my Github repository.

Project Requirements

event-reminder1-thumbnail

  1. Schedule Alexa to alert us when an event draws near.
  2. Provide a User Interface (UI) with the following features for each event:
    • Enable/Disable button
    • “Skip Next” button
    • Days of the Week selection
    • First and Second Announcement times
    • First and Second Announcement text
    • List of Echo devices and device groups the announcement should be heard on
  3. The Automations must be reliable! The school principal doesn’t seem to understand that my kids are late because automated voice assistant didn’t remind me to take them to school. ;)

User Interface

Before I wrote the automations, I started to lay out the UI so I knew what entities I needed to create. You can see the final results of this planning in the image above. I ended up using the following six custom Lovelace plugins, all installed via the Home Assistant Community Store (HACS).

  1. custom:decluttering-card
  2. custom:text-input-row
  3. custom:button-card
  4. custom:button-entity-row
  5. custom:fold-entity-row
  6. custom:text-divider-row

I consider the first two to be necessary for this project to be feasible. The last four could be omitted if you prefer. The decluttering card makes a template for each of my (currently) 10 events, while the text card allows the text field to span the entire width of the parent card.

If you’d like to see the code behind the UI, here are the files in my repository to look at.

The detail on the UI design beyond the intended scope of this article. However, I will share that the Enable button simply enables and disables the automation directly. As always feel free to ask questions in the comment section below, and who knows I may do a follow up article detailing the UI.

Automation

Here is the base automation I wrote to trigger the Alexa voice reminders. First I trigger the automation when either the First or Second time equal the current time. This saves a lot of code duplication as you will see. Then I check if the alert is supposed to be spoken on the current day, Finally I call script.event_reminder_announce to pass the First or Second Announcement text along to be spoken.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
automation:
  - alias: event_reminder_1
    trigger:
      platform: template
      value_template: >-
        {{ is_state('sensor.event_reminder_1_1',states('sensor.time'))
        or is_state('sensor.event_reminder_1_2',states('sensor.time')) }}
    condition:
      condition: and
      conditions:
        # Is today one of the selected days?
        - condition: template
          value_template: >
            {% set   day_name = now().strftime("%A")|lower -%}
            {%- if   day_name == 'monday'    and is_state('input_boolean.event_reminder_1_mon','on') -%}
              {{true}}
            {%- elif day_name == 'tuesday'   and is_state('input_boolean.event_reminder_1_tue','on') -%}
              {{true}}
            {%- elif day_name == 'wednesday' and is_state('input_boolean.event_reminder_1_wed','on') -%}
              {{true}}
            {%- elif day_name == 'thursday'  and is_state('input_boolean.event_reminder_1_thu','on') -%}
              {{true}}
            {%- elif day_name == 'friday'    and is_state('input_boolean.event_reminder_1_fri','on') -%}
              {{true}}
            {%- elif day_name == 'saturday'  and is_state('input_boolean.event_reminder_1_sat','on') -%}
              {{true}}
            {%- elif day_name == 'sunday'    and is_state('input_boolean.event_reminder_1_sun','on') -%}
              {{true}}
            {%- else -%}
              {{false}}
            {%- endif %}
    action:
      - service: script.event_reminder_announce
        data_template:
          media_player: "{{ states('input_select.event_reminder_1_echo') }}"
          message: >-
            {% if is_state('sensor.event_reminder_1_1',states('sensor.time')) %}
              {{ states('input_text.event_reminder_1_1') }}
            {% else %}
              {{ states('input_text.event_reminder_1_2') }}
            {% endif %}

There is a little more to the final code that handles when the Skip Next button is enabled. But that just complicates things a little bit.

Announcement Scripts

script.event_reminder_announce

This script replaces the friendly media player name with the entity_id of the target Echo device, and passes the message along to script.say.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
script:
  event_reminder_announce:
    sequence:
      - service: script.say
        data_template:
          message: "{{ message }}"
          media_player: >
            {%- set room = media_player|lower|replace(' ','_')|replace('[','')|replace(']','')  %}
            {%-
              set alexa = {
                "bedroom" : "media_player.master_bedroom",
                "boys_bedroom": "media_player.boys_room",
                "downstairs": "media_player.downstairs",
                "kitchen/garage": "group.alexa_welcome",
                "garage" : "media_player.garage",
                "kitchen" : "media_player.kitchen",
                "family_room" : "media_player.family_room",
                "play_room": "media_player.play_room",
                "upstairs": "media_player.upstairs",
                "upstairs_bathroom": "media_player.upstairs_bathroom"
              }
            -%}
            {{ alexa[room] }}

script.say

The following script is an ultra simplified version of my speech script, which uses the custom Alexa Media Player voice notification feature. This component is amazing and can also be installed with HACS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  say:
    sequence:
      - service: notify.alexa_media
        data_template:
          data:
            type: announce
            method: all
          title: >
            {%- if title is not string -%}
              Home Assistant
            {%- else -%}
              {{ title }}
            {%- endif -%}
          message: "{{ message }}"
          target: "{{ media_player }}"

Supporting Entities

The tedious part was creating all 15 of the input_boolean, input_datetime, input_select, input_text, and sensor entities. After creating my first two reminders, I realized it was going to take forever to do this 10 times! I am a big fan of the way @frenck organizes his configuration files (essentially, each entity gets a separate file). That’s when I realized I needed to package all of the automations, scripts, and entities together… well, in a package.

Packages

You can find all of the packages for this project in the /integrations/event_reminders folder. The newest revision of the code I shared above can be found in event_reminder_1_package.yaml. The speech scripts can be found in event_reminder_common_package.yaml.

Conclusion

After using this code reliably for a few days I am really proud of how it turned out. I tried to keep this article relatively brief so I didn’t bore you with every detail. If you enjoyed this overview of my most ambitious project to date, please leave a comment below. Questions are also welcomed on the Home Assistant Community forum, Twitter, my GitHub repo or comments on this blog.