Morning Weather Notification | With daily high temperature đŸ„ș

Scenario

Every morning my spouse wants to know the specific temperature and forecast, but she typically asks me assuming I’ve already checked since I get up earlier. I’m not one who checks the weather, because where we live it is pretty consistent. January - March below freezing, June-August slightly warm, pleasant in between
 For this I used the Tomorrow.io - Home Assistant integration, but you could probably do it with the default AccuWeather, or whatever you have, as long as it provides an hourly forcast.

The Fix

I made her a notification that answers all her questions!
Why not use a weather app? Because I can do it this with Home Assistant! Wasted time? No! I learned more about Jinja namespaces. :pleading_face: Which is annoying but a good learning experience.

Updated version that doesn’t use loops or require jinja namespaces

With the help of @CentralCommand in his reply Morning Weather Notification | With daily high temperature đŸ„ș - #9 by CentralCommand the for loop is not necessary to achieve the same result.

Here is the the updated version


Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, 
feels like {{states('sensor.climacell_feels_like')}}°. 
Today 
{{ (state_attr('weather.climacell_hourly', 'forecast')
    | map(attribute="temperature")
    | list)[0:16]
    | max }}°
/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° 
{{ states('weather.climacell_daily') }} 
humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% 
with 
{%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}
   preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}
{%else%}
   no precipitation
{%endif%}.

Which shows: Currently -11°, feels like -21.44°. Today 7°/-11° sunny humidity 69% with no precipitation. for me right now.

If you want to see the old version with a for loop where I explain each part of the loop then expand this:

Explination of the loop I previously used

The raw code

{% set thetemp = state_attr('weather.climacell_hourly', 'forecast') %}
{% set hightemp = 0 %}
{% set ns = namespace (hightemp = 0) %}
{% for looper in thetemp %}
  {% if loop.index0 < 16 %}
    {% if looper.temperature > ns.hightemp %}
      {% set ns.hightemp = looper.temperature | float %}
    {% endif %}
  {% endif %}
{% endfor %}
Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, feels like {{states('sensor.climacell_feels_like')}}°. Today {{ns.hightemp}}°/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° {{ states('weather.climacell_daily') }} humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% with {%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}{%else%}no precipitation{%endif%}.

What’s with that for loop?

Getting the low temp for the day was easy, I just got the low forecasted for tomorrow, which will probably be during the night and is good enough for me.

However, I couldn’t find a daily high temperature prediction in the Accuweather or ClimaCell integrations. So I loop through the hourly forecast and find the biggest number!

Lets walk through it.

Put your weather attribute weather.climacell_hourly into a variable called thetemp

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

Create a new variable called hightemp to hold the highest temperature found in the upcoming loop.

{% set hightemp = 0 %}

This is where the :pleading_face: comes in. Jinja by default does not allow you to pass variables in and out of loops. Also known as, changing scopes. To allow this you need to use a namespace object which is what is done here.

Check out Reza Rahimi’s great explanation here: python - How can i access a jinja2 variable outside the for loop? - Stack Overflow

Basically, we’re just moving the variable hightemp into a new variable called ns.hightemp that is accessible in and out of the loop.

{% set ns = namespace (hightemp = 0) %}

Next is the floor loop with an if statement inside.

{% for looper in thetemp %}
  {% if looper.temperature > ns.hightemp %}
    {% set ns.hightemp = looper.temperature | float %}
  {% endif %}
{% endfor %}

Let’s start with the for loop. This just loops through each hour in the hourly forecast.

{% for looper in thetemp %}

{% endfor %}

Next look at the first if statement.
This limits our loop to the first 16 options. This way we don’t look for the whole 24 hour period, and only look at the next 16 hours when we will be awake.

  {% if loop.index0 < 16 %}

  {% endif %}

Next look at the second if statement.
This checks the temperature sub-attribute of weather.climacell_hourly. Go to the bottom to see my explanation on sub-attributes.

This if statement compares the temperature of every sub-attribute that we loop through to our variable ns.hightemp

  {% if looper.temperature > ns.hightemp %}

  {% endif %}

Lastly, when the if statement is true (meaning the forecasted temperature for that hour that the loop is on is greater than the item in ns.hightemp variable) then we put that hour’s temperature into the variable ns.hightemp with set ns.hightemp = looper.temperature. That is followed by | float because the number in the looper.temperature could be a decimal number like -10.5 so the | float ensures the number stays a decimal number.

    {% set ns.hightemp = looper.temperature | float %}

Whoa! All that just to find the high temperature for the day!

Yes, now we can talk about the actual notification.

The Notification

The notification template below will say:

Currently 0°, feels like -11.2°. Today 11.0°/-5° partlycloudy humidity 62% with no precipitation.

Below is what makes the notification happen. If you look closely you’ll see another if statement at the end. This is because when state_attr('weather.climacell_daily', 'forecast').precipitation_type is none it it actually just returns nothing, so there will be no words in the notification. This if statement make it so that when it is none then the notification says “no precipitation”

Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, feels like {{states('sensor.climacell_feels_like')}}°. Today {{ns.hightemp}}°/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° {{ states('weather.climacell_daily') }} humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% with {%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}{%else%}no precipitation{%endif%}.

A more readable format of the above:

Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, 

feels like {{states('sensor.climacell_feels_like')}}°. 

Today {{ns.hightemp}}°/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° 

{{ states('weather.climacell_daily') }} 

humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% 

with 
{%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}
    preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}
  {%else%}
    no precipitation
{%endif%}.

The Notification Action

I originally had the trigger be when the iPhone is unplugged in the morning, but the update frequency for the Home Assistant Companion App is pretty slow (>15 minutes) when I tested, unless you have the app open in the foreground.

Wrapping it all up into one automation you get


alias: 'Notification: Wife Wakeup Weather'
description: >-
  Send Wife's phone a custom weather message in
  the morning.
trigger:
  - platform: time
    at: '07:00:00'
condition:
  - condition: state
    entity_id: person.wife
    state: home
action:
  - service: notify.mobile_app_wife_iphone
    data:
      title: Your Daily Forecast
      message: >-
        {% set thetemp = state_attr('weather.climacell_hourly', 'forecast') %}
        {% set hightemp = 0 %}
        {% set ns = namespace (hightemp = 0) %}
        {% for looper in thetemp %}
          {% if loop.index0 < 12 %}
            {% if looper.temperature > ns.hightemp %}
              {% set ns.hightemp = looper.temperature | float %}
            {% endif %}
          {% endif %}
        {% endfor %}
        Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, feels like {states('sensor.climacell_feels_like')}}°. Today {{ns.hightemp}}°/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° {{ states('weather.climacell_daily') }} humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% with {%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}{%else%}no precipitation{%endif%}.
mode: single

END OF OLD VERSION WITH LOOP EXPLINATION

Below is what makes the notification happen. If you look closely you’ll see another if statement at the end. This is because when state_attr('weather.climacell_daily', 'forecast').precipitation_type is none it it actually just returns nothing, so there will be no words in the notification. This if statement make it so that when it is none then the notification says “no precipitation”

Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, feels like {{states('sensor.climacell_feels_like')}}°. Today {{ (state_attr('weather.climacell_hourly', 'forecast') | map(attribute="temperature") | list)[0:16] | max }}°/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° {{ states('weather.climacell_daily') }} humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% with {%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}{%else%}no precipitation{%endif%}.

The Notification Action

I originally had the trigger be when the iPhone is unplugged in the morning, but the update frequency for the Home Assistant Companion App is pretty slow (>15 minutes) when I tested, unless you have the app open in the foreground.

Wrapping it all up into one automation you get


alias: 'Notification: Wife Wakeup Weather'
description: >-
  Send Wife's phone a custom weather message in
  the morning.
trigger:
  - platform: time
    at: '07:00:00'
condition:
  - condition: state
    entity_id: person.wife
    state: home
action:
  - service: notify.mobile_app_wife_iphone
    data:
      title: Your Daily Forecast
      message: >-
        Currently {{state_attr('weather.climacell_nowcast','temperature')}}°, feels like {{states('sensor.climacell_feels_like')}}°. Today {{ (state_attr('weather.climacell_hourly', 'forecast') | map(attribute="temperature") | list)[0:16] | max }}°/{{ state_attr('weather.climacell_daily', 'forecast')[0].templow }}° {{ states('weather.climacell_daily') }} humidity {{ state_attr('weather.climacell_daily', 'humidity') }}% with {%if state_attr('weather.climacell_daily', 'forecast').precipitation_type is none %}preciptation of {{ state_attr('weather.climacell_daily', 'forecast').precipitation_type }}{%else%}no precipitation{%endif%}.
mode: single

weather.climacell_hourly sub-attribute explanation

I don’t know if ‘sub-attribute’ is the right word, but that’s what I’ll use until someone corrects me in the comments.

See how weather.climacell_hourly has a bunch of data under forecast attribute? See how it is repeating? You can access those like this: {{ state_attr('weather.climacell_hourly','forecast')[0].temperature }} This returns the first [0] (because an index starts at 0) set of sub-attributes for forecast, then .temperature gives you only the temperature of that first sub-attribute. So in my case it returns -4 (that’s Fahrenheit by the way).

With this knowledge you can access any hour’s attributes.

6 Likes

I have a similar setup, but instead of a loop I have a min/max sensor that tells me who is lowest and who is highest and just announce whatever that sensor calculated, including the average of the sensors if I want.

This is fantastic and perfectly timed for me, I have however a question if you dont mind


I have spent a few hours making this suit my use case but the only part I cant get right is the templow
as I see no reference to it in the loop or anywhere above.

I suspect I am being an idiot here.

Thanks in advance.

EDIT:
This is what I have so far:

{% set thetemp = state_attr('weather.met_office_daily', 'forecast') %}
{% set hightemp = 0 %}
{% set ns = namespace (hightemp = 0) %}
{% for looper in thetemp %}
  {% if loop.index0 < 16 %}
    {% if looper.temperature > ns.hightemp %}
      {% set ns.hightemp = looper.temperature | float %}
    {% endif %}
  {% endif %}
{% endfor %}

The weather report today for princess is as follows.
It is currently {{states('weather.met_office_daily')}},
with a temperature of {{state_attr('weather.met_office_daily','temperature')}}°, 
but with a feels like of {{states('sensor.feels_like_temperature_daily')}}°. 
With an estimated high of {{ns.hightemp}}°
The humidity is {{ state_attr('weather.met_office_daily', 'humidity') }}%,
with a {{states('sensor.probability_of_precipitation_daily') }}% chance of rain.

All of the above works perfectly (note I have removed the location from the above entities)

However the following renders nothing and I am not sure why?

{{ state_attr(‘weather.met_office_daily’, ‘forecast’)[0].templow }}°

Hi @rossk, it may be that your met office integration does not have that attribute?
I’m using the ClimaCell - Home Assistant integration and that does have a temp low sub-attribute.

Could you post a screenshot of your weather.met_office_daily entity? You might also check the other met office entities and see if they have a lowtemp (or similar) attribute/sub-attribute.

Just FYI, you actually don’t need a for loop or namespaces for your high temperature use case. Jinja has a built-in filter called max you can use here, it takes in a list and returns you the largest value in that list. So your template to get the high temperature can just be this:

{{ state_attr('weather.climacell_hourly', 'forecast')
  | map(attribute="temperature")
  | list | max }}

Here’s the result on mine. I tweaked it slightly so you could see both the list of temperatures and the final max result to show how it worked:

Part of the reason namespaces and loops are so awkward in Jinja is because generally you don’t need them. Jinja tries to push you to use its streaming filters for handling arrays like map, select, selectattr, max, etc. because they are more performant and lead to cleaner easier to read templates. If you haven’t had a chance to read through their doc I would recommend it. It does a good job and shows all the built-in stuff they make available for you.

2 Likes

Oh nice, thank you @CentralCommand ! Yea, I knew there was the max filter, but did not know how to put the results in to a list that the max filter could iterate through. I’ll give it a try and update.

1 Like

Thanks @Noblewolf for the reply, after much time tweaking I discovered pretty much this, my weather integrations did not have a high and low state, simply an hourly temp reading forecast.

I ended up using two different weather integrations and also made an additional loop to populate the lowest temp over the first 16 hours blocks.

Thanks for the inspiration :+1:t2:

There’s me thinking I had finished my weather project and now I feel the need to re-write it :joy:

Question though in @Noblewolf example his loop would only loop through the first 16, is your code adaptable easily to achieve the same?

Thanks

1 Like

Oh, huh, I missed that, whoops. Yea easy enough, you can just use the python syntax for getting a subarray. So like this:

{{ (state_attr('weather.climacell_hourly', 'forecast')
  | map(attribute="temperature")
  | list)[0:16]
  | max }}

Admittedly its not quite as pretty anymore since there’s no filter to pipe into for that task but it does the job.

As a rule of thumb when you aren’t sure how to do something in Jinja, google how to do it in python. Whatever you find will probably also work in Jinja as long as it doesn’t try to mutate the state of an object or require importing a library.

2 Likes

Thanks @rossk for pointing out the limit to the first 16 results. The reason I had that was because if you loop through the entire 24 hours you’ll probably get the high temperature for the next day (if the next day is warmer). So the 16 item (or hour) limit is to only get the current day’s high temperature.

Thank you @CentralCommand for the great explanations. I’ve updated my original post with your version of the max temperature filter!

1 Like

Just wanted to give a quick update. 2.5 months on and my spouse is still loving the notifications as built. She hadn’t mentioned it in a while until she looked at her phone before 7am (automation trigger is 07:00) and mentioned it wasn’t there yet.

I considered running the notification whenever she unplugs her phone in a time range 05:00-12:00, but the iOS version of the HA Companion app doesn’t update that charging sensor often (like ever 15 minutes) so it isn’t a viable trigger. If anyone has an idea of a faster “I’m awake because my phone did x” trigger I’d love to hear it.

The way I do it is all through HA and motion sensors to know if one of us is awake. After 5am if the bedroom detects motion by the door (indicating someone is leaving the room) then HA waits to see if the other room gets the motion sensor triggered, indicating that someone left the bedroom and entered the next room, then it waits 10 seconds for them to close the door (so a door sensor could also be the source) and sets the house to “One Awake”. Then the next person to repeat the process triggers the house to be “Awake” (helper input), that is how I know it’s time to run my morning announcements 3 minutes later (to give you time to wake up before being inundated with stats).

I have quite a bit of automation around this concept of Awake, Napping, Sleeping, One Awake, where the HVAC kicks in, announcements are made, lights come on and other extra notifications like if it’s trash day or whatever. We rarely have to do something manually in the morning unless we wakeup before 5am and the triggering doesn’t occur then we might have to tell Siri “Good Morning” to force the house into Awake mode and start the chain of events.

Neither of us take our phones to the bedroom so that was never an option, plus I don’t know if I would like to be reliant on the fact that someone picked up their phone or took it off charge religiously when I have all these sensors around that can pretty accurately determine what’s going on.

2 Likes

That’s a good method. We both wake up at very different times, sometimes hours apart, so this combo idea is good. I’ll just have to get some more motion sensors.

Yea, it’s worked out very well and only takes two sensors to make it work. One of the reasons I did this two person style is because I like to have the temperature set to 60 for bedtime (automatic based on the “Sleep Mode” and other factors) and she felt like an Eskimo when she woke up first, so the “One Awake” setting notches the furnace up to 65 for her then 70 when the second person gets up. That’s a lot to remember when you haven’t had coffee yet, so the automation made sense. It’s also nice having that input variable because it also controls when the home automation makes announcements and where, all my automations are keyed to “If Sleep Mode is Awake”.

1 Like

Me and the better half both have iPhones and as you say the HA iOS app sensors don’t update frequently enough, so for sensors we his the ic3 integration as these trigger straight away for us.

We also use it to trigger the iPad chargers to maintain the batteries nicely.

Maybe an option to consider if using iCloud.

we just say “alexa what’s the weather” but yeah not very HA oriented.

We use the smart speaker as well, but we only have one in the living room, and she prefers to check the weather before getting dressed and going out of the bedroom. Not more than 20 steps to ask Alexa, but this is about small improvements. :slight_smile:

My whole home automation has been built on a series of small improvements over the past 25+ years of doing home automation (starting with X10). Some big ideas get implemented out of the gate, but mostly as a developer I just put in the basics and build upon it. My One Awake/All Awake system was bull on that, it just started as a way to control the thermostat but has evolved to an entire system.

The automatic morning announcement is a pretty beefy Jinja script with tons of conditions, like if it’s going to be over or under a certain temperature and reading the wind forecast to see when the best time to go for a run is to reading out the first scheduled task of the day - all this gets wrapped into what the house tells you about when you wake up.

This is why I love HomeAssistant, it’s so flexible to do these kinds of things. I even use different TTS voices for different things, all on the fly via scripting.

1 Like

I assume “one awake” when the (downstairs) hallway motion sensor detects movement for the 1st time after 6am. Anything before that and it assumes still “asleep”.

How does 'Napping" mode work? Does it lower volumes throughout the house and how do you trigger it?

One awake is when the bedroom motion sensor picks up motion and then the next room detects motion within 2 minutes, then one is awake. The next time the bedroom picks up motion, now it’s just fully awake, the rest of the drapes open, heat goes up, so and and so forth. Then, again, motion in the bedroom turns it off, opens the drapes and reverses the process.

Napping is easy, it turns off all the lights in the bedroom, closes the curtains, quits out of certain apps on my PC, turns off all non home automation sounds and done. All my home automation voice prompts bear the condition of “if Sleep Mode == Awake” so that it’s never talking when one or more people are sleeping or napping or whatever.

1 Like