Get a “Morning” Briefing from your Smart Home

YouTube Video Here

Using Jinja2 Templating and Text To Speech.

On one of my much older videos – I think Maybe the video about Notifications – I had a comment asking about creating a Text-To-Speech Briefing from Home Assistant.
At the time I thought to myself “What a great Idea! This will be easy!”

Fast-forward to now and I couldn’t be more wrong.
In order to present the information I wanted in my daily briefing, I had to add a LOT more logic than I’d anticipated, but looking on the bright side, I learned a TON about Ninja Templating in Home Assistant and had a ton of fun at the same time.

What to include?

The First thing to do is figure out what we want to include in our daily briefing. This is very much a “choose your own adventure” situation.
I can’t tell you what your smart home briefing should include, but here’s what I decided I wanted to know from a briefing:

  • The current time:
    • Including a “Time Appropriate” greeting
      (Good Morning,
      Good Afternoon,
      Good Evening)
  • Weather Details:
    • Current Conditions
    • Current Temperature
    • Forecast High
    • Forecast Low
    • Forecast Rain
  • More Weather Details
    • Current Wind Speed
    • Direction the Wind is blowing
  • Something Fun
    • Random Quote of the Day
    • Random Dad Joke
    • Random Fact
  • Pending Updates
    • Home Assistant OS
    • Home Assistant Core
    • ESPHome

Abstraction

I think I’m using that right?!?
The concept I’m going for here is breaking down the announcements into scripts that can run separately. This is a common practice in Programming to make code easier to maintain, and less monolithic. These features sound good to me.
There’s a downside though: Inbetween each script running, there’s a short delay. It sounds okay with the delay.

So – we’re breaking each section into a single script in Home Assistant, and then we’ll create an Automation to trigger each of the Scripts in the desired order.

Building the First part

So let’s start with the time.

You’ll need a sensor.time in your Home Assistant Setup.
To get this to work, simply add:

  - platform: time_date
    display_options:
      - "time"
      - "date"

to your `configuration.yaml file, Save the change, and restart your Home Assistant instance.

Once your Home Assistant comes back up, you’ll now have a sensor.time entity in Home Assistant. So now we can build a Templated script for the Text to Speech engine to read out.
Let’s start with the Template

Get Current Time from Home Assistant

So we’re going to pull the current time into a variable in our template.

The problem we’re going to have is, it’s in 24-hour time. While there’s nothing inherently wrong with that, it sounds weird when the Text-To-Speech Engine reads it out. We’ll fix that later

{% set current_time = states('sensor.time') %}

Extract the current hour and minute into seperate variables

We need to extract the Hour and minute into seperate variables.
We need to do some calculations on the Hour, and we’ll want the minute for the Announcement.

{% set hour = current_time.split(':')[0] | int %}
{% set minute = current_time.split(':')[1] %}

Say a time appropriate greeting

So we’re using the hour variable to calculate a time appropriate greeting.

The If statements here will say “Good Morning” if it’s between 6AM and midday, “Good Afternoon” if it’s between midday and 6PM, and “Good Evening” if it’s between 6PM and 6AM

{% if hour >= 6 and hour < 12 %}
  Good morning!
{% elif hour >= 12 and hour < 18 %}
  Good afternoon!
{% elif hour >= 18 or hour < 6 %}
  Good evening!
{% endif %}

Add AM or PM Suffix for announcement

We’re then using the hour variable to calculate if it’s AM or PM.
This is pretty simple. If the hour is less-than 12, we set the suffix variable to ‘AM’, otherwise, we set the suffix variable to ‘PM’

{% set suffix = 'AM' if hour < 12 else 'PM' %}

Convert 24 hour time to 12 hour time

To make the announcement sound less weird, we’ll convert the 24 hour hour variable into 12-hour time.

Again, this is pretty simple. If our hour variable is less than 12, we just set the hour_12 variable to the same as hour. If it’s greater than 12, we set the hour_12 variable to hour - 12. This way, for example, 17:00 becomes 5:00

{% set hour_12 = hour if hour <= 12 else hour - 12 %}

Announce the current Time

Finally, we can announce the time

It's currently {{ hour_12 }}:{{ minute }} {{ suffix }}.

Put it all together:

So – All together it looks like this:

# get Current Time from Home Assistant
{% set current_time = states('sensor.time') %}

# Split time into hour and minute
{% set hour = current_time.split(':')[0] | int %}
{% set minute = current_time.split(':')[1] %}

# Say a time appropriate greeting
{% if hour >= 6 and hour < 12 %}
  Good morning!
{% elif hour >= 12 and hour < 18 %}
  Good afternoon!
{% elif hour >= 18 or hour < 6 %}
  Good evening!
{% endif %}

# Add AM or PM Suffix
{% set suffix = 'AM' if hour < 12 else 'PM' %}
{% set hour_12 = hour if hour <= 12 else hour - 12 %}
# Announce the current Time
It's currently {{ hour_12 }}:{{ minute }} {{ suffix }}.

Add it to a Script

So now we can add our template to a Home Assistant Script.

The easiest way to do this is to go to “Settings” -> “Automations & Scenes” -> “Scripts” then click “Add Script”. On the Script Creation screen, click the 3-Dot stack in the top right corner, and then click “Edit in YAML”

The Script I’ve created makes use of the notify.alexa_media service using the “Alexa Media Player” integration from HACS.

The Target when we call the service is media_player.dining_room_echo_plus in my case. Yours will be different…. obviously.

You could also use media_player.cloud_say as the service and set the entity as needed.
A nice way to do this is use the UI to create the parts of the script like calling the service, and the “entities you want to call the service against, then switching to YAML editing mode and pasting in your Template.

My script looks like this:

announce_time:
  alias: Announce Time
  sequence:
  - service: notify.alexa_media
    data:
      message: " 
        {% set current_time = states('sensor.time') %} 
        {% set hour = current_time.split(':')[0] | int %} 
        {% set minute = current_time.split(':')[1] %}

        {% if hour >= 6 and hour < 12 %} 
            Good morning! 
        {% elif hour >= 12 and hour < 18 %} 
            Good afternoon! 
        {% elif hour >= 18 or hour < 6 %} 
            Good evening! 
        {% endif %}

        {% set suffix = ''AM'' if hour < 12 else 'PM' %} 
        {% set hour_12 = hour if hour <= 12 else hour - 12 %}

        It's currently {{ hour_12 }}:{{ minute }} {{ suffix }}.
        "
      target:
      - media_player.dining_room_echo_plus
  mode: single

Weather – Part 1

Next up, I want some weather details announced.

There’s 2 scripts we’re going to work with here, for the first, we’re going to announce some details about Temperatures and conditions, and forecast temperatures and conditions.

For all the weather data, we’re making use of the default Home Assistant Weather integration – Meteorologisk institutt (Met.no) – This weather integration is added by default when you setup Home Assistant the first time.

Get the current Temperature from Home Assistant

This part is easy enough. we use the State Attributes to extract the temperature attribute from the weather.home entity.

{% set current_temp = state_attr ('weather.home','temperature') | float %}

Get the current weather condition from Home Assistant

The weather.home entity stores the current condition as its state. There’s no need to pull an attribute from the entity.

{% set current_condition = states('weather.home') %}

Fix the current Condition

When the Current weather condition is “Partly Cloudy” the default weather integration returns `partlycloudy` and it sounds odd when read out by a Text to Speech engine.

To fix it I added the following section to my template

{% if current_condition == 'partlycloudy' %}
  {% set current_condition = 'Partly Cloudy' %}
{% endif %}

At this point, we can announce the current condition and temperature.

Outside, It's {{ current_condition }} and {{ current_temp }} °C

Figure out the Highs and Lows from the forecast

For weather information, I want to know the High and Low temperatures.
This data is strangely not an attribute by default in the default weather integration, so we have to figure it out ourselves.

Start by Extracting the `forecast` array data into a variable and instantiate a “High Temp” and “Low Temp” variable

The weather entity stores forecast data in the forecast attribute as an array of forecasts.
The forecast array looks a little like this.
Now it’s only in this writeup that I’ve made the realisation that I got some fundamental concepts in my video wrong.
Each item in the “forecast” array is actually for a “Day” not an hour….. OOPS!.

forecast: 
- condition: partlycloudy
  datetime: '2023-06-05T02:00:00+00:00'
  wind_bearing: 118.2
  temperature: 17.7
  templow: 8.5
  wind_speed: 18.4
  precipitation: 2.7
- condition: partlycloudy
  datetime: '2023-06-06T02:00:00+00:00'
  wind_bearing: 14.2
  temperature: 17.8
  templow: 11.8
  wind_speed: 21.2
  precipitation: 0.1
- condition: rainy
  datetime: '2023-06-07T02:00:00+00:00'
  wind_bearing: 20.6
  temperature: 17.3
  templow: 12.7
  wind_speed: 18.7
  precipitation: 23.9
- condition: partlycloudy
  datetime: '2023-06-08T02:00:00+00:00'
  wind_bearing: 277.1
  temperature: 14.6
  templow: 10.3
  wind_speed: 15.8
  precipitation: 0.6
- condition: partlycloudy
  datetime: '2023-06-09T02:00:00+00:00'
  wind_bearing: 281.9
  temperature: 13.8
  templow: 10.4
  wind_speed: 20.2
  precipitation: 0.4

So what we can do here is put the forecast into it’s own variable, and we’ll set a high and low-temp variable from the 0th item in the forecast data, knowing that that’s the high and low temperature details for tomorrow, and I’ll grab the Forecast condition while we’re at it.

{% set weather = state_attr('weather.home', 'forecast') %}

{% set high_temp = weather[0].temperature %}
{% set low_temp = weather[0].templow %}

{% set forecast_condition = weather[0].condition %}

Some Qualitative statements about the `high_temp`

This code block looks at the `high_temp` variable and runs the if statement below to make a qualitative statement about it.

These have been built for °C and I pulled the qualitative statements out of my head.

Change them as you need to

  • Above 30°C say “It’s going to be a hot one tomorrow”
  • between 20°C and 30°C say “It should be a warm day tomorrow”
  • between 15°C and 20°C say “It’s going to be a pretty mild temperature tomorrow”
  • between 10°C and 15°C say “It seems like it’ll be a bit chilly tomorrow”
  • between 0°C and 10°C say “It’s going to be a cold day tomorrow”
  • < 0°C say “It’s going to be freezing tomorrow”
{% if high_temp > 30 %}
  It's going to be a hot one tomorrow
{% elif 20 < high_temp < 30 %}
  It should be a warm day tomorrow
{% elif 15 < high_temp < 20 %}
  It's going to be a pretty mild temperature tomorrow
{% elif 10 < high_temp < 15 %}
  It seems like it'll be a bit chilly tomorrow
{% elif 0 < high_temp < 10 %}
  It's going to be a cold day tomorrow
{% else %}
  It's going to be freezing tomorrow
{% endif %}

Announce the `high_temp` and `low_temp` values

Again, built for °C

The forecast high for tomorrow is {{ high_temp }} °C.
{{ forecast_condition }} With a Low of {{ low_temp }} °C .

Some qualitative statements about the `low_temp`

Again we’re looking at °C and the statements and ranges are arbitrary, and can be adjusted to your tastes

Remember these are for the `low_temp` variable, so it’s not going to get colder than these

  • Above 30°C say “Wow. That’s hot!”
  • between 20°C and 30°C say “it’s not going to get below 20°C tomorrow”
  • between 10°C and 20°C say “it might get a little bit chilly tomorrow.”
  • between 0°C and 10°C say “That’s cold.”
  • < 0°C say “Absolutely freezing!”

Puntuation like `.`, `!`, `?` and `,` is important in the text to speech statments so the timing sounds more “natural”

{% if low_temp > 30 %}
  Wow. That's hot!
{% elif 20 < low_temp < 30 %}
  it's not going to go below 20°C tomorrow.
{% elif 10 < low_temp < 20 %}
  it might get a little bit chilly tomorrow.
{% elif 0 < low_temp < 10 %}
  That's cold.
{% else %}
  Absolutely freezing!
{% endif %}

Now we can put it all together:

{% set current_temp = state_attr ('weather.home','temperature') | float %}
{% set current_condition = states('weather.home') %}

{% if current_condition == 'partlycloudy' %}
  {% set current_condition = 'Partly Cloudy' %}
{% endif %}

Outside, It's {{ current_condition }} and {{ current_temp }} degrees Celsius.

{% set weather = state_attr('weather.home', 'forecast') %}

{% set high_temp = weather[0].temperature %}

{% set low_temp = weather[0].templow %}

{% set forecast_condition = weather[0].condition %}

{% if high_temp > 30 %}
  It's going to be a hot one tomorrow
{% elif 20 < high_temp < 30 %}
  It should be a warm day tomorrow
{% elif 15 < high_temp < 20 %}
  It's going to be a pretty mild temperature tomorrow
{% elif 10 < high_temp < 15 %}
  It seems like it'll be a bit chilly tomorrow
{% elif 0 < high_temp < 10 %}
  It's going to be a cold day tomorrow
{% else %}
  It's going to be freezing tomorrow
{% endif %}

The forecast high for tomorrow is {{ high_temp }} °C.
{{ forecast_condition }} With a Low of {{ low_temp }} °C .


{% if low_temp > 30 %}
  Wow. That's hot!
{% elif 20 < low_temp < 30 %}
  Pretty warm
{% elif 10 < low_temp < 20 %}
  it might get a little bit chilly.
{% elif 0 < low_temp < 10 %}
  That's cold.
{% else %}
  Absolutely freezing!
{% endif %}

And put it into a script like with the Time

alias: Announce Weather Details
  sequence:
  - service: notify.alexa_media
    data:
      message: >_
      {% set current_temp = state_attr ('weather.home','temperature') | float %}
      {% set current_condition = states('weather.home') %}

      {% if current_condition == 'partlycloudy' %}
        {% set current_condition = 'Partly Cloudy' %}
      {% endif %}

      Outside, It's {{ current_condition }} and {{ current_temp }} degrees Celsius.

      {% set weather = state_attr('weather.home', 'forecast') %}
      {% set high_temp = weather[0].temperature %}
      {% set low_temp = weather[0].templow %}

      {% set forecast_condition = weather[0].condition %}

      {% if high_temp > 30 %}
        It's going to be a hot one tomorrow
      {% elif 20 < high_temp < 30 %}
        It should be a warm day tomorrow
      {% elif 15 < high_temp < 20 %}
        It's going to be a pretty mild temperature tomorrow
      {% elif 10 < high_temp < 15 %}
        It seems like it'll be a bit chilly tomorrow
      {% elif 0 < high_temp < 10 %}
        It's going to be a cold day tomorrow
      {% else %}
        It's going to be freezing tomorrow
      {% endif %}

      The forecast high for tomorrow is {{ high_temp }} °C.
      {{ forecast_condition }} With a Low of {{ low_temp }} °C .


      {% if low_temp > 30 %}
        Wow. That's hot!
      {% elif 20 < low_temp < 30 %}
        Pretty warm
      {% elif 10 < low_temp < 20 %}
        it might get a little bit chilly.
      {% elif 0 < low_temp < 10 %}
        That's cold.
      {% else %}
        Absolutely freezing!
      {% endif %}

    - media_player.dining_room_echo_plus
  mode: single

Weather – Part 2

For the second part of the Weather Announcement, I want to look at details of the wind.

We’ve got some good data like the wind-speed and the bearing, so I want that read out to me. The problem is that a bearing in degrees isn’t super helpful, so I need to change that into a compass or “Cardinal” Direction.

We’ll start by extracting the bearing and speed from Home Assistant.

Get the current wind speed and wind bearing in degrees from Home Assistant

{% set wind_bearing = state_attr('weather.home', 'wind_bearing') | float %}

{% set wind_speed = state_attr('weather.home','wind_speed') | float

Next, I’m creating an Array of Dictionaries, where the key value is a tuple containing the degree values the bearing needs to fall between to be considered that cardinal direction

Admittedly, there’s probably way more resolution than is needed here and you could probably remove the ‘in-between’ directions.

{% set directions = {
    (0.0, 11.25): 'North',
    (11.25, 33.75): 'North North East',
    (33.75, 56.25): 'North East',
    (56.25, 78.75): 'East North East',
    (78.75, 101.25): 'East',
    (101.25, 123.75): 'East South East',
    (123.75, 146.25): 'South Eeast',
    (146.25, 168.75): 'South South East',
    (168.75, 191.25): 'South',
    (191.25, 213.75): 'South South West',
    (213.75, 236.25): 'South West',
    (236.25, 258.75): 'West South West',
    (258.75, 281.25): 'West',
    (281.25, 303.75): 'West North West',
    (303.75, 326.25): 'North West',
    (326.25, 348.75): 'North Nort West',
    (348.75, 360.0): 'North'
} %}

## Announce a qualitative description of wind speed

Next – I want a qualitative assessment of the Wind Speed.
It’s worth mentioning that this is the first point in this script our Text to Speech output will have anything to say.

{% if wind_speed > 50 %}
  Its very windy outside.
{% elif 40 < wind_speed < 50 %}
  It's pretty windy at the moment
{% elif 30 < wind_speed < 40 %}
  Theres a strong breeze right now
{% elif 20 < wind_speed < 30 %}
  It's a bit breezy outside
{% elif 10 < wind_speed < 20 %}
  There's a light breeze
{% else %}
  it's quite still
{% endif %}

Announce the Cardinal Direction and Wind Speed

Then we’ll announce the direction and speed of the windy iterating through the above array to determine the cardinal direction of the wind.
This works by testing that the wind_bearing value is greater than the first number in the tuple, and less than the second number in the tuple as it iterates through the array.
Only when the wind_bearing value is both greater than the item at 0 and less than the item at 1, will it read out “the wind is currently blowing from the {{ direction }} direction, at {{ wind_speed}} kilometres per hour” part of this if statement nested inside the for loop

{% for rng, direction in directions.items() %}
  {% if wind_bearing >= rng[0] and wind_bearing < rng[1] %}
    The wind is currently blowing from the {{ direction }} direction, 
    at {{ wind_speed }} kilometres per hour
  {% endif %}
{% endfor %}

Put it together

{% set wind_bearing = state_attr('weather.home', 'wind_bearing') | float %}
{% set wind_speed = state_attr('weather.home','wind_speed') | float %}

{% set directions = {
    (0.0, 11.25): 'North',
    (11.25, 33.75): 'North North East',
    (33.75, 56.25): 'North East',
    (56.25, 78.75): 'East North East',
    (78.75, 101.25): 'East',
    (101.25, 123.75): 'East South East',
    (123.75, 146.25): 'South Eeast',
    (146.25, 168.75): 'South South East',
    (168.75, 191.25): 'South',
    (191.25, 213.75): 'South South West',
    (213.75, 236.25): 'South West',
    (236.25, 258.75): 'West South West',
    (258.75, 281.25): 'West',
    (281.25, 303.75): 'West North West',
    (303.75, 326.25): 'North West',
    (326.25, 348.75): 'North Nort West',
    (348.75, 360.0): 'North'
} %}


{% if wind_speed > 50 %}
  Its very windy outside.
{% elif 40 < wind_speed < 50 %}
  It's pretty windy at the moment
{% elif 30 < wind_speed < 40 %}
  Theres a strong breeze right now
{% elif 20 < wind_speed < 30 %}
  It's a bit breezy outside
{% elif 10 < wind_speed < 20 %}
  There's a light breeze
{% else %}
  it's quite still
{% endif %}

{% for rng, direction in directions.items() %}
  {% if wind_bearing >= rng[0] and wind_bearing < rng[1] %}
    The wind is currently blowing from the {{ direction }} direction, at {{ wind_speed }} kilometres per hour
  {% endif %}
{% endfor %}

And Lastly, add a Script to Home Assistant.

alias: Announce Wind Details
  sequence:
  - service: notify.alexa_media
    data:
      message: >_
      {% set wind_bearing = state_attr('weather.home', 'wind_bearing') | float %}
      {% set wind_speed = state_attr('weather.home','wind_speed') | float %}

      {% set directions = {
          (0.0, 11.25): 'North',
          (11.25, 33.75): 'North North East',
          (33.75, 56.25): 'North East',
          (56.25, 78.75): 'East North East',
          (78.75, 101.25): 'East',
          (101.25, 123.75): 'East South East',
          (123.75, 146.25): 'South Eeast',
          (146.25, 168.75): 'South South East',
          (168.75, 191.25): 'South',
          (191.25, 213.75): 'South South West',
          (213.75, 236.25): 'South West',
          (236.25, 258.75): 'West South West',
          (258.75, 281.25): 'West',
          (281.25, 303.75): 'West North West',
          (303.75, 326.25): 'North West',
          (326.25, 348.75): 'North Nort West',
          (348.75, 360.0): 'North'
      } %}


      {% if wind_speed > 50 %}
        Its very windy outside.
      {% elif 40 < wind_speed < 50 %}
        It's pretty windy at the moment
      {% elif 30 < wind_speed < 40 %}
        Theres a strong breeze right now
      {% elif 20 < wind_speed < 30 %}
        It's a bit breezy outside
      {% elif 10 < wind_speed < 20 %}
        There's a light breeze
      {% else %}
        it's quite still
      {% endif %}

      {% for rng, direction in directions.items() %}
        {% if wind_bearing >= rng[0] and wind_bearing < rng[1] %}
          The wind is currently blowing from the {{ direction }} direction, at {{ wind_speed }} kilometres per hour
        {% endif %}
      {% endfor %}
    - media_player.dining_room_echo_plus
  mode: single

Inside Temperature Sensors

So we’ve heard about the outside. Now I want to get briefed on the temperatures INSIDE the house.
I want a report from the Dining Room, the Lounge, The Office, and the Master Bedroom.

We’ve got Temperature sensors all over the house, some of them are Aqara brand Temperature and Humidity Sensors (affiliate link). We use those in the Office and Dining Room.
In the Lounge Room, there’s a Temperature Sensor passed through by the Air Conditioner
and in the Master Bedroom, The Termperature is measured by the XiaoMi Air Purifier in there.

So, there’s an easy way to do this and a hard way.
The easy way looks like this:

Inside it's currently {{ states('sensor.temperature_dining') }} °C in the Dining Room, 
{{ states('sensor.lounge_ac_inside_temperature') }} °C in the Lounge, 
{{ states('sensor.temperature_office')}} °C in the office,
and {{ states('sensor.master_bedroom_purifier_temperature') }} °C in the Master Bedroom.

We’ve just written a single, monolithic block of template to stay what we want it to say.
There’s nothing wrong with this approach, unless and until you want to add or remove items from the announcement.

There’s a “better” way

The “hard” way is theoretically better as it means that changes that we want or need to make later will be easier to implement.

By creating an array of entities, along with a “Room Name”, we can iterate through the array and read out the temperatures for each item. Any changes we want to make to the items we’re announcing are then simplified by only needing to add or remove the relevant items from the array.

Here’s the code:

{% set inside_temps = { 
    'sensor.temperature_dining': 'Dining Room',
    'sensor.lounge_ac_inside_temperature': 'Lounge Room',
    'sensor.temperature_office': 'Office, and',
    'sensor.master_bedroom_purifier_temperature': 'Master Bedroom'

} %}

Inside it's currently 
{% for sensor, room in inside_temps.items() %}
    {{ states(sensor) }} °C in the {{ room }}
{% endfor %}

Into a Script

alias: Announce Inside Temperatures
  sequence:
  - service: notify.alexa_media
    data:
      message: >_
    {% set inside_temps = { 
        'sensor.temperature_dining': 'Dining Room',
        'sensor.lounge_ac_inside_temperature': 'Lounge Room',
        'sensor.temperature_office': 'Office, and',
        'sensor.master_bedroom_purifier_temperature': 'Master Bedroom'

    } %}

    Inside it's currently 
    {% for sensor, room in inside_temps.items() %}
        {{ states(sensor) }} °C in the {{ room }}
    {% endfor %}
    - media_player.dining_room_echo_plus
  mode: single

Fun Stuff!

I wanted to add some fun things to the Announcement, So I went in search of different ways to do that.

I landed on adding:

  • A random quote
  • a random fact

and

  • a random Dad Joke

The first hurdle is getting the data into Home Assistant to then be able to use it in the templates.

API Ninjas have free API’s to pull quotes, Dad Jokes and Random Facts, plus MANY MANY more free to use APIs.

The API documentations are:

There’s plenty more API’s that might be useful so I recommend checking out API Ninjas. You’ll need to sign up for an API Key, but it’s free

Restful Sensor integration

So to get the data into Home Assistant, we’re going to use the RESTful Sensor integration, this involves modifying your configuration.yaml file.

I also make use of the `secrets.yaml` file to store the APINinjas API key.

your secrets.yaml file entry should look like this:

ninjas_api: <YOUR_API_KEY>

Obviously, replace <YOUR_API_KEY> with your actual API key

Quote of The Day

From an integration standpoint, the most obvious I came up with was to make use of a REST API, and use the Restful Sensor Integration in Home Assistant to pull the data into an entity.

The configuration.yaml entry I came up with for the Quote Sensor looks like this

sensor:
  - platform: rest
    name: Quote Sensor
    unique_id: quote_sensor
    resource: https://api.api-ninjas.com/v1/quotes?
    method: GET
    scan_interval: 7200
    headers:
      X-Api-Key: !secret ninjas_api
    value_template: "{{ value_json[0].quote }}"
    json_attributes_path: "$.[0]"
    json_attributes:
      - "author"
      - "category"

The json_attributes item in this configuration adds the author and category attributes to the sensor. The sensor state will contain the quote.

The scan_interval is simply the amount of time – in seconds – the sensor will stay the same before refreshing.

Dad Joke of the Day

For the Dad Joke, It’s a bit simpler. We’re just creating an entity with the joke as its state. We don’t need any attributes

sensor:
  - platform: rest
    name: Dad Joke Of the Hour
    unique_id: dad_joke
    resource: https://api.api-ninjas.com/v1/dadjokes?limit=1
    method: GET
    scan_interval: 7200
    headers:
      X-Api-Key: !secret ninjas_api
    value_template: "{{ value_json[0].joke }}"

Random Fact of the Day

And again, very simple for the Random Fact

sensor:
  - platform: rest
    name: Random Fact
    unique_id: random_fact
    resource: https://api.api-ninjas.com/v1/facts?limit=1
    method: GET
    scan_interval: 7200
    headers:
      X-Api-Key: !secret ninjas_api
    value_template: "{{ value_json.articles[0].title }}"

The Template

So Now, for the template that read all of this out.

The Quote

First, we’re going to read out the quote, and I want to know the Category and the author of the quote when we get this information, so the template looks like this:

The {{ state_attr( 'sensor.quote_sensor', 'category' ) }} 
Quote of the day 
is by {{ state_attr( 'sensor.quote_sensor', 'author' ) }}. 
They said. {{ states('sensor.quote_sensor') }}

quite simply we’re reading directly from the entity attributes for the category and author, and the state for the quote.

The Dad Joke

This one is very easy, as we only need the state of the dad_joke sensor

Here is a Dad Joke.. {{ states('sensor.dad_joke_of_the_hour') }}

The double full stop in this template serves to slow down the text to speech engine, putting a slight pause between “Here is a Dad Joke” and the actual joke

The Random Fact

Again, this one is pretty simple

And a random fact.. {{ states('sensor.random_fact') }}
All Together
The {{ state_attr( 'sensor.quote_sensor', 'category' ) }} Quote of the day is by {{ state_attr( 'sensor.quote_sensor', 'author' ) }}. 
They said. {{ states('sensor.quote_sensor') }}

Here is a Dad Joke.. {{ states('sensor.dad_joke_of_the_hour') }}

And a random fact.. {{ states('sensor.random_fact') }}
And Finally, in a script
alias: Announce Wind Details
  sequence:
  - service: notify.alexa_media
    data:
      message: >_
      The {{ state_attr( 'sensor.quote_sensor', 'category' ) }} Quote of the day is by {{ state_attr( 'sensor.quote_sensor', 'author' ) }}. 
      They said. {{ states('sensor.quote_sensor') }}

      Here is a Dad Joke.. {{ states('sensor.dad_joke_of_the_hour') }}

      And a random fact.. {{ states('sensor.random_fact') }}
    - media_player.dining_room_echo_plus
  mode: single

Pending Updates

So the last thing I wanted to add to my briefing is an announcement about any pending updates.

Like with the inside temperatures, there’s an easy way and a hard way, where the easy way can get very messy the more items you want to read out, and the hard way makes managing the items you want to read out a little easier in the long run. A little extra work now can save a lot of pain and suffering later.

So there’s 3 updates I want to manage using this method.

  • Home Assistant OS
  • Home Assistant Core
  • ESP Home

I could decide later to add more, but these are the 3 I want for now.

The easy way is to simply write them all out in one script, with one block of template per update we’re monitoring like this:

{% if states('update.home_assistant_operating_system_update') == 'on' %}
There's a Home Assistant O S Update pending. 
The Installed version is {{ state_attr('update.home_assistant_operating_system_update', 'installed_version') }} 
The Available version is {{ state_attr('update.home_assistant_operating_system_update', 'latest_version') }} 
{% endif %}

{% if states('update.home_assistant_core_update') == 'on' %}
There's a Home Assistant Core Update pending. 
The Installed version is {{ state_attr('update.home_assistant_core_update', 'installed_version') }} 
The Available version is {{ state_attr('update.home_assistant_core_update', 'latest_version') }} 
{% endif %}

{% if states('update.esphome_update') == 'on' %} 
There's an E S P Home Update pending. 
The Installed version is {{ state_attr('update.esphome_update', 'installed_version') }} 
The Available version is {{ state_attr('update.esphome_update', 'latest_version') }} 
{% endif %}

But like with the Inside temperature sensors, we’re going to do better, by creating an array, and iterating through it.

Our Array needs the sensor entity as the key, and the “name” we want read out as the Value.

Here’s the more streamlined code with the for loop:

{% set entities = {
    
    'update.home_assistant_operating_system_update': 'Home Assistant OS',
    'update.home_assistant_core_update': 'Home Assistant Core',
    'update.esphome_update': 'E S P Home',

} %}

{% for entity, name in entities.items() %}
  {% if states(entity) == 'on' %}
  There's a {{ name }} update pending. 
  The Installed version is {{ state_attr( entity, 'installed_version') }} 
  The Available version is {{ state_attr(entity, 'latest_version') }} 
  {% endif %}
{% endfor %}
Putting it into a script
alias: Announce Pending Updates
sequence:
  - service: notify.alexa_media
    data:
      message: >-
        {% set entities = {
            'update.home_assistant_operating_system_update': 'Home Assistant OS',
            'update.home_assistant_core_update': 'Home Assistant Core',
            'update.esphome_update': 'E S P Home',

        } %}

        {% for entity, name in entities.items() %}
          {% if states(entity) == 'on' %}
          There's a {{ name }} Update pending. 
          The Installed version is {{ state_attr( entity, 'installed_version') }} 
          The Available version is {{ state_attr(entity, 'latest_version') }} 
          {% endif %}
        {% endfor %}
      target:
        - media_player.dining_room_echo_plus
mode: single
icon: mdi:update

This way, the Text to speech engine will always be consistent when it reads out the update information, provided the attributes “installed_version” and “latest_version” are in the entity we’re querying.

Finishing up

So that’s pretty much it, but it’d seem a bit strange if the Announcement just ended there, so I added a final script to the lineup that just says “That’s all I’ve got for now”

Putting it all into a single automation

So the last thing we need to do is get it all together and trigger each script to run from an automation.

I created an input_button helper in my production instance called input_button.daily_briefing

For your home, you could trigger this at a particular time of day, use a physical signee button to trigger it, or use a Voice Assistant to trigger the automation.

So, my Automation looks a little something like this:

- id: daily_briefing
  alias: BRIEFING - Daily Briefing
  description: ''
  trigger:
  - platform: state
    entity_id:
    - input_button.daily_briefing
  condition: []
  action:
  - service: script.announce_time
    data: {}
  - service: script.announce_weather_details
    data: {}
  - service: script.announce_wind_details
    data: {}
  - service: script.announce_inside_temperatures
    data: {}
  - service: script.announce_quote_of_the_day
    data: {}
  - service: script.announce_dad_joke
    data: {}
  - service: script.announce_random_fact
    data: {}
  - service: script.announce_pending_updates
    data: {}
  - service: script.announce_end_of_briefing
    data: {}
  mode: single

That’s All!

So that’s how I created a Daily briefing from my Smart Home.

I hope this is helpful in some way to you, and let me know what you think in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *