My TTS briefing template

Here’s my briefing template. Maybe you get can some ideas from it, and maybe I can get some suggestions for improvement. It does the following:

  • Greeting
  • Weather
  • Holidays
  • Deliveries
  • Phone charging reminder
  • Events today, and on the weekend
  • Chore reminders
  • SO location/commute status

It runs in the evening after someone gets home. It also runs on-demand as an Alexa skill. Next I will do a morning briefing as a variation.

  {# Difference between dates in hours, roughly #}
  {% macro hour_diff(first, second) -%}
    {{((as_timestamp(first) - as_timestamp(second))/60.0/60.0)}}
  {%- endmacro %}

  {# Day of week #}
  {% macro day_of_week(timestamp) -%}
    {{as_timestamp(timestamp)|timestamp_custom('%A', true)|lower}}
  {%- endmacro %}

  {# On a weekend #}
  {% macro on_weekend(timestamp) -%}
    {%- if
      day_of_week(timestamp) == 'saturday' or
      day_of_week(timestamp) == 'sunday'
    -%}
      true
    {%- else -%}
      false
     {%- endif %}
  {%- endmacro %}

  {# Is today #}
  {% macro is_today(event) -%}
    {%- if event.attributes.start_time[:10]== (now()|string)[:10] -%}
      true
    {%- else -%}
      false
    {%- endif %}
  {%- endmacro %}

  {# Friendly time #}
  {% macro friendly_time(timestamp) -%}
    {{as_timestamp(timestamp)|timestamp_custom('%-I', true)}}
    {%- if as_timestamp(timestamp)|timestamp_custom('%-M', true) != '0' -%}
      {{ ' ' }}{{as_timestamp(timestamp)|timestamp_custom('%-M', true)}}
    {%- endif %}
  {%- endmacro %}

  {# Hours until a given event, approximately #}
  {% macro hours_until(event) -%}
    {{hour_diff(event.attributes.start_time, now())|float|round}}
  {%- endmacro %}

  {# Upcoming event #}
  {% macro upcoming_event(event) -%}
    {%- set start = event.attributes.start_time -%}
    {%- if start and is_today(event) == 'true' -%}
      {{event.attributes.message}} is today at {{friendly_time(start)}}
    {%- elif start and on_weekend(start) == 'true' and day_of_week(now()) == 'friday' -%}
      {{event.attributes.message}} is on {{day_of_week(start)}} at {{friendly_time(start)}}
    {%- endif -%}
  {%- endmacro %}

  {# Household chore #}
  {% macro chore_sentence(event, type, sentence) -%}
    {%- if (event.attributes.start_time and 
            is_today(event) == 'true' and
            event.attributes.message|lower == type) -%}
      {{sentence}}
    {%- endif -%}
  {%- endmacro %}

  {# Holiday sentence #}
  {% macro holiday_sentence() -%}
    {%- if (states.calendar.holidays_in_united_states.attributes.start_time and
            is_today(states.calendar.holidays_in_united_states) == 'true') -%}
      It is {{states.calendar.holidays_in_united_states.attributes.message}}.
    {%- endif -%}
  {%- endmacro %}

  {# Event sentence #}
  {% macro event_sentence(event, prefix, suffix) -%}
    {%- set event_string = upcoming_event(event) -%}
    {% if event_string != '' -%}
      {{prefix}}{{event_string}}{{suffix}}{{'.'}}
    {%- endif %}
  {%- endmacro %}

  {# Greeting sentence #}
  {% macro greeting_sentence(person) %}
    {% set greetings = [
      "Welcome home " ~ person,
      "Hey there " ~ person,
      person ~ "! You're home!",
      "Welcome back " ~ person
    ] %}
    {{greetings|random}}.
  {% endmacro %}

  {# Closing sentence #}
  {% macro closing_sentence(person) %}
    {% set closings = [
      "Have a great evening!",
      "Enjoy your night.",
      "Take it easy this evening.",
      "Have fun tonight!",
    ] %}
    {{closings|random}}
  {% endmacro %}

  {# Weather sentence #}
  {% macro weather_sentence() -%}
    It is {{states.sensor.dark_sky_summary.state.lower()}} and {{states.sensor.dark_sky_temperature.state|int|round}} degrees outside.
    {{- ' ' -}}
    {{- states.sensor.dark_sky_hourly_summary.state-}}
  {%- endmacro %}

  {# Get person's device #}
  {% macro get_device(person) -%}
    {% for device in states.device_tracker -%}
     {% if device.name == person -%}
        {{device.entity_id}}
      {%- endif %}
    {%- endfor %}
  {%- endmacro %}

  {# Get person's pronoun #}
  {%- macro get_pronoun(device_entity) -%}
    {{device_entity.split('.')[1].split('_')[0]}}
  {%- endmacro -%}

  {# How far away is a person #}
  {% macro minutes_away(pronoun, part_of_day, max) -%}
    {%- if max == 0 -%}
      unknown
    {%- else -%}
      {{((states("proximity." ~ pronoun ~ "_home")|int / max|int) * states("sensor." ~ pronoun ~ "_" ~ part_of_day)|int)|int}}
    {%- endif -%}
  {%- endmacro %}

  {# Who is the other person #}
  {% macro get_other_person(person) -%}
   {% for device in states.device_tracker -%}
     {% if device.name != person -%}
        {{device.name}}
      {%- endif %}
    {%- endfor %}
  {%- endmacro %}

  {# On My Way sentence #}
  {%- macro omw_sentence(person) -%}
    {%- set device_entity = get_device(person) -%}
    {%- set pronoun = get_pronoun(device_entity) -%}
    {%- set max = states.sensor[pronoun ~ "_distance_mean"].attributes.max_value|int -%}
    {%- if states(device_entity) == 'not_home' -%}
      {{person}} is on {{pronoun}} way home, about {{minutes_away(pronoun, "evening", max)}} minutes out.
    {%- elif states(device_entity) != 'home' -%}
      {{person}} is still at {{states(device_entity)}}.
    {%- endif -%}
  {%- endmacro -%}

  {# Battery sentence #}
  {% macro battery_sentence(person) -%}
    {%- set device_entity = get_device(person) -%}
    {%- set pronoun = get_pronoun(device_entity) -%}
    {%- set percent = states("sensor." ~ pronoun ~ "_phone_battery") -%}
    {% if percent|int < 50 %}
      Remember to plug in your phone, it is at {{percent}} percent.
    {% endif %}
  {%- endmacro %}

  {# Deliveries #}
  {% macro deliveries_sentence() -%}
    {% if 'delivered' in states.sensor.deliveries.attributes -%}
      {{states.sensor.deliveries.attributes.delivered}}{{' '}}
      {%- if states.sensor.deliveries.attributes.delivered == 1 -%}
        package was
      {%- else -%}
        packages were
      {%- endif -%}
      {{' '}}delivered today.
    {%- endif %}
  {%- endmacro %}

  {# Validate requested person #}
  {% macro validate_and_brief(person) -%}
    {% if person -%}
      {%- set caps = person|capitalize -%}
      {%- set device = get_device(caps) -%}
      {%- if device -%}
        {{briefing(caps)}}
      {%- else -%}
        {{caps}} does not live here.
      {%- endif %}
    {%- else -%}
      Specify a person.
    {%- endif %}
  {%- endmacro %}

  {# Output the briefing #}
  {% macro briefing(person) -%}
    {%- set other_person = get_other_person(person) %}
    {{greeting_sentence(person)}}
    {{holiday_sentence()}}
    {{battery_sentence(person)}}
    {{weather_sentence()}}
    {{omw_sentence(other_person)}}
    {{deliveries_sentence()}}
    {{chore_sentence(states.calendar.general, 'trash', 'Remember to take out the trash. It is trash day.')}}
    {{chore_sentence(states.calendar.general, 'recycling', 'Remember to take out the recycling. It is recycling day.')}}
    {{event_sentence(states.calendar.facebook)}}
    {{closing_sentence(person)}}
  {% endmacro %}

  {# ... And, run the briefing #}
  {{validate_and_brief(states.sensor.briefing_target.state)}}
14 Likes

It looks awesome. I litteraly had a night without sleep some days ago, imagining and thinking through a concept like you have explained here.

Was going to start with working it out last night, except my f#%€*¥£cking Google Home stopped streaming audio (an mp3 always containing the latest hourly radio news broadcast from the national tv/radio provider in Belgium) for which I thought the issue was on either their or my HA side. Turned out after some digging - and finding out that streaming of music didn’t work anymore neither- that it is a known bug for which people all over the world are reporting issues because it basically makes their Google Home device useless. Oh well, off-topic. Great Work :slight_smile:

1 Like

Thanks for posting this - I have a simple RSS feed being generated that Alexa reads to me in the mornings (via the built in custom daily briefing functionality), but it doesn’t pull much out of HASS. This will definitely push me in the right direction.

Are these part of an automation (in which case can you paste your automation)? Looks super interesting, just wondering where/how to use them.

@arsaboo https://github.com/happyleavesaoc/my-home-automation/tree/master/homeassistant under packages/briefing.yaml

Excellent…thanks!!

I have solved this with a custom component https://github.com/Danielhiversen/home-assistant_config/blob/master/custom_components/news.py

2 Likes

That would be a pretty interesting component for HaSS. Turn the logbook into an RSS feed. That would give all sorts of abilities to have systems watch hass and do things.

Holy Moly I had no idea about that macro feature…Goodbye weekend…Gotta tinker with that now.

Thanks for sharing dude!

~Cheers

1 Like

I studied your template and got it to work! :grinning:
also i made some improvements on it: between script.speech_engine and script.speech processing i added a script.notification_hub. the purpose of this is so that i can also use the speech_engine to generate my text notifications. i’m going to further expand the hub with:

  • possibilities for URL / image / file support
  • link to my media_players to stream visual infor ation along with my TTS and Pushbullet notifications.

here’s what i have so far:

I’m basing my notifications on Janet (The good place) and using some of her quotes to bring the speech_engine more to life:

What’s amazing about this template is that if you have a couple of variations for each function, the possible variations for your message make that you don’t often hear the same thing twice!

3 Likes

do you have your config shared somewhere?

@kylerw no I don’t have my config shared, but it is based on this:
https://github.com/CCOSTAN/Home-AssistantConfig/blob/master/script/speech_engine.yaml

And then i made the additions i mentioned above.

the buildup is:

Automations -->
script.speech_engine -->
script.notification_hub -->
[ speech_processing, text_processing, etc ]

All the speech_engine macro’s are called with:

service: script.speech_engine
  data:
    call_greeting: 1
    call_introduction: 1
  data_template:
    Parameter: >-
      {{ state.blah }}

Edit:

Here are some of my favourite Janet quotes that i use for the commands:

call_introduce():
    "I'm an informational delivery system, I literraly know everything. ",
    "I'm sort of the janitor around here. ", 
    "I'm like a walking database. You can ask me about the creation of the universe... or history. ",
    "Every time a Janet is rebooted, she increases her social awareness and abilities. I might be the most advanced Janet in the universe. ",
    "I'm your therapist. ", 
    "To activate your Janet, press nose for three seconds. ",
    "I'm very high in potassium. Like a banana! " 

call_update():
    "There have been 25 generation of Janet. Each new update of Janet gains more wisdom and social abilities. Fun fact: the first Janet had a clicking wheel! ",
    "I read all Google's eight hundred and sixty one thousand references on Home Assistant these last 3 milliseconds, and I found some new skills! ", 
    "I just leasurely reread all of human history, there was even some new content! ", 
    "So I found an update. That's the good news. The bad news is I seem to be losing my ability to sustain object permanence, so it's sort of a glass-half-full, glass-stops-existing-in-time-and-space kinda deal. "

call_shutdown():
    "I'll be gone, you'll get a new Janet, and everything will go back to normal. Well, not for me, I'll be a lifeless marble floating through space, but you will be back to normal.",
    "Self-destruct time... Don't worry, I won't feel any pain or anything. ",
    "If it's okay with you, I'm gonna go to my void for a little while. I just need to spend some time alone and focus on myself. ",
    "Shutting down. I'll be about as dead as I can be. Kind of like I'm in power-saver mode. ",
    "I'm in danger of total collapse. Fun fact! Mathematically, I'm equally likely to either implode or explode. "

call_okay():
    "Yaaaaaaaaaaaay ",
    "Weeeeeeeeeeeee ",
    "Does not compute. Does not compute. I'm just kidding. I mean, it doesn't compute, but I'm not gonna explode or anything. ",
    "Okay. I've hacked into the mainframe. Kidding. I can't hack into the mainframe. Technically, I am the mainframe. ",
    "I will not. It is literally impossible for me to do that. Just kidding. ",
    "I couldn't do this 800 reboots ago, but apparently now I can. "
    "Yup. Bye! "

@kylerw : i just shared my configuration:

@Danielhiversen How do I use your component?