7 Day Weather Forecast

I wanted to create a nice looking weather forecast inside of Home Assistant that uses the mdi icons, so it looks like the rest of my home assistant sensors. Sure you can use a weather app, but this just seems nicer, and since I’m looking at my home assistant app so much anyways, it makes sense.

This uses the dark sky sensor component, but could be applied using other weather components. Dark sky assigns the suffix _2, _3, _4 for the forecast 2 days out, 3 days out, etc.

Below is an image of the end result and the code to create it.
EDIT 1/24/2018: I’ve revised the code to incorporate the improvements made by @mconway and @arsaboo .
EDIT 3/4/2018: See arsaboo’s post #173 for the updated code using the friendly name templating, which was introduced in version 0.64. If this works for you, you no longer need the python script that updates the friendly names for the forecast sensors. Some Hassio users are reporting that the friendly name templating is not working for them; if that’s the case for you, just use the python script method described in this post to update the friendly names.

Save the following as weather.yaml in a folder called packages, add packages: !include_dir_named packages under homeassistant: in the configuration.yaml file. See home assistant’s documentation on packages for more information.

sensor:
  - platform: darksky
    api_key: !secret darksky_api_key
    forecast:
      - 1
      - 2
      - 3
      - 4
      - 5
      - 6
      - 7
    monitored_conditions:
    - summary
    - temperature
    - temperature_min
    - temperature_max
    - humidity
    - precip_probability
    - daily_summary
    - icon

  - platform: season
  - platform: template
    sensors:
      weekday:
        friendly_name: 'Weekday'
        value_template: '{{ now().strftime("%A") }}'
      forecast_today:
        friendly_name: "Today's Forecast"
        value_template: >
          {{states.sensor.dark_sky_daily_high_temperature.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon", "sleet") %}
            mdi:weather-snowy-rain
          {% elif is_state("sensor.dark_sky_icon", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon", "partly-cloudy-night") %}
            mdi:weather-partlycloudy            
          {% else %}
            error
          {% endif %}    
      forecast_1:
        friendly_name: 'Tomorrow'
        value_template: >          
          {{states.sensor.dark_sky_daily_high_temperature_1.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_1.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_1.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_1", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_1", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon_1", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_1", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_1", "sleet") %}
            mdi:weather-snowy-rain
          {% elif is_state("sensor.dark_sky_icon_1", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_1", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_1", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_1", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_1", "partly-cloudy-night") %}
            mdi:weather-partlycloudy            
          {% else %}
            error
          {% endif %}
    
      forecast_2:
        friendly_name: 'Second'
        value_template: >          
          {{states.sensor.dark_sky_daily_high_temperature_2.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_2.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_2.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_2", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_2", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon_2", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_2", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_2", "sleet") %}
            mdi:weather-snowy-rain 
          {% elif is_state("sensor.dark_sky_icon_2", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_2", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_2", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_2", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_2", "partly-cloudy-night") %}
            mdi:weather-partlycloudy            
          {% else %}
            error
          {% endif %}
                 
      forecast_3:
        friendly_name: 'Third'
        value_template: >          
          {{states.sensor.dark_sky_daily_high_temperature_3.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_3.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_3.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_3", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_3", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon_3", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_3", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_3", "sleet") %}
            mdi:weather-snowy-rain 
          {% elif is_state("sensor.dark_sky_icon_3", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_3", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_3", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_3", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_3", "partly-cloudy-night") %}
            mdi:weather-partlycloudy
          {% else %}
            error
          {% endif %}
        
      forecast_4:
        friendly_name: 'Fourth'
        value_template: >          
          {{states.sensor.dark_sky_daily_high_temperature_4.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_4.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_4.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_4", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_4", "clear-night") %}
            mdi:weather-night            
          {% elif is_state("sensor.dark_sky_icon_4", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_4", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_4", "sleet") %}
            mdi:weather-snowy-rain 
          {% elif is_state("sensor.dark_sky_icon_4", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_4", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_4", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_4", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_4", "partly-cloudy-night") %}
            mdi:weather-partlycloudy
          {% else %}
            error
          {% endif %}

      forecast_5:
        friendly_name: 'Fifth'
        value_template: >
          {{states.sensor.dark_sky_daily_high_temperature_5.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_5.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_5.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_5", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_5", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon_5", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_5", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_5", "sleet") %}
            mdi:weather-snowy-rain 
          {% elif is_state("sensor.dark_sky_icon_5", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_5", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_5", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_5", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_5", "partly-cloudy-night") %}
            mdi:weather-partlycloudy
          {% else %}
            error
          {% endif %}
           
      forecast_6:
        friendly_name: 'Sixth'
        value_template: >          
            {{states.sensor.dark_sky_daily_high_temperature_6.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_6.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_6.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_6", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_6", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon_6", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_6", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_6", "sleet") %}
            mdi:weather-snowy-rain  
          {% elif is_state("sensor.dark_sky_icon_6", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_6", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_6", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_6", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_6", "partly-cloudy-night") %}
            mdi:weather-partlycloudy
          {% else %}
            error
          {% endif %}

      forecast_7:
        friendly_name: 'Seventh'
        value_template: >
            {{states.sensor.dark_sky_daily_high_temperature_7.state|round(0)}}°/{{states.sensor.dark_sky_daily_low_temperature_7.state|round(0)}}°/{{states.sensor.dark_sky_precip_probability_7.state|round(0)}}%
        icon_template: >-
          {% if is_state("sensor.dark_sky_icon_7", "clear-day") %}
            mdi:weather-sunny
          {% elif is_state("sensor.dark_sky_icon_7", "clear-night") %}
            mdi:weather-night
          {% elif is_state("sensor.dark_sky_icon_7", "cloudy") %}
            mdi:weather-cloudy   
          {% elif is_state("sensor.dark_sky_icon_7", "rain") %}
            mdi:weather-pouring        
          {% elif is_state("sensor.dark_sky_icon_7", "sleet") %}
            mdi:weather-snowy-rain 
          {% elif is_state("sensor.dark_sky_icon_7", "snow") %}
            mdi:weather-snowy
          {% elif is_state("sensor.dark_sky_icon_7", "wind") %}
            mdi:weather-windy
          {% elif is_state("sensor.dark_sky_icon_7", "fog") %}
            mdi:weather-fog
          {% elif is_state("sensor.dark_sky_icon_7", "partly-cloudy-day") %}
            mdi:weather-partlycloudy
          {% elif is_state("sensor.dark_sky_icon_7", "partly-cloudy-night") %}
            mdi:weather-partlycloudy
          {% else %}
            error
          {% endif %}

group:
  weather_card_today:
    name: Today
    view: no
    entities:
      - sensor.forecast_today
      - sensor.dark_sky_temperature
      - sensor.dark_sky_humidity
  weather_card_forecast:
    name: Forecast
    view: no
    entities:
      - sensor.dark_sky_daily_summary
      - sensor.forecast_1
      - sensor.forecast_2
      - sensor.forecast_3
      - sensor.forecast_4
      - sensor.forecast_5
      - sensor.forecast_6
      - sensor.forecast_7

automation:
  - id: update_friendly_names
    alias: "Update friendly names"
    trigger:
      - platform: state
        entity_id:
          - sensor.forecast_1
          - sensor.forecast_2
          - sensor.forecast_3
          - sensor.forecast_4
          - sensor.forecast_5
          - sensor.forecast_6
          - sensor.forecast_7
      - platform: homeassistant
        event: start
    action:
      - service: python_script.dark_sky_friendly_names
        data_template:
          entityid: '{{trigger.entity_id}}'    

Python script to update the friendly names (save as dark_sky_friendly_names.py , place in /python_scripts folder and add python_script: to your configuration.yaml file. See python scripts component for more information.) NOTE: I’ve modified lines 25, 33, 41, and 49 to use ‘icon’ instead of ‘entity_picture’ so that the mdi icons work. If you want to use custom icons, see links to @arsaboo 's code on github below.

###############################################################################
# Author:      Alok R. Saboo
# Description:  This script takes the dark_sky forecast sensors and updates
#               their friendly_name to include the date and day.
#               https://github.com/arsaboo/homeassistant-config/blob/master/packages/weather.yaml
###############################################################################
dark_sky_entities = ["sensor.forecast_1", "sensor.forecast_2", "sensor.forecast_3",
                     "sensor.forecast_4", "sensor.forecast_5", "sensor.forecast_6",
                     "sensor.forecast_7"]
days = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]

triggeredEntity = data.get('entity_id')
# logger.warning("trigger is {}".format(triggeredEntity))
now = datetime.datetime.now()
today = now.weekday()

if triggeredEntity is None:
    for entity_id in dark_sky_entities:
        # copy it's state
        state = hass.states.get(entity_id)
        newState = state.state
        forecastdays = int(entity_id.split('_')[1])
        day = datetime.timedelta(days = forecastdays)
        forecastdate = now + day
        newEntityPicture = state.attributes.get('icon')
        if today + forecastdays > 6:
            newDay = days[today + forecastdays - 7]
        else:
            newDay = days[today + forecastdays]
        # Set states
        hass.states.set(entity_id, newState, {
            'friendly_name': "{} ({}/{})".format(newDay, forecastdate.month, forecastdate.day),
            'icon': newEntityPicture,
        })
else:
    state = hass.states.get(triggeredEntity)
    newState = state.state
    forecastdays = int(triggeredEntity.split('_')[1])
    day = datetime.timedelta(days = forecastdays)
    forecastdate = now + day
    newEntityPicture = state.attributes.get('icon')
    if today + forecastdays > 6:
        newDay = days[today + forecastdays - 7]
    else:
        newDay = days[today + forecastdays]
    # Set states
    hass.states.set(triggeredEntity, newState, {
        'friendly_name': "{} ({}/{})".format(newDay, forecastdate.month, forecastdate.day),
        'icon': newEntityPicture,
    })

Links to latest files on github are below. Note that unedited, this code assumes the usage of custom icons, which you need to download per the instructions in the commented code. If you want to use mdi icons, just use the code above.
https://github.com/arsaboo/homeassistant-config/blob/master/packages/weather.yaml

29 Likes

I’ve stripped out a lot of the if statements, i’m just working on printing the day names in the forecast. Right now I have “tomorrow”,“second”,“third” etc. Either way, I was able to reduce the lines of code by about 30%. Also got rid of the dependency of having weekday_number and weekday sensors. Hopefully I can wrap up the changes soon and repost for you. I’ve also turned this into a package :slight_smile:

3 Likes

That’s great! Thanks. I knew somebody could improve on this.

Alright, here’s how far I got tonight. It’s all in a package, and the names have comments for the template that would produce the correct rolling name. I haven’t figured out how to template the names yet. This may not be perfect, but it’s a start.

1 Like

Very cool, I’m going to try out some of your modifications. Yes, templating the friendly names is one of the big issues I ran into. I don’t think I was ever able to get it working.

One comment, under forecast_fourth, the icon template sensor names still say “thursday” where they should say “fourth” (lines 163-181).

Woops, youre right! should be fixed now. Haven’t had time to look at the names more, but i’m sure there is a way.

Looks like some icons are not updated in HA. I cannot see partly-cloudy-night and clear-day.

That’s weird, they should be showing up. The code has those two icon states and their corresponding mdi icons.

I simplified everything to use entity_picture_template. Instead of using mdi icons, which are not exactly similar to the dark_sky icons, I am not using the dark_sky icons directly.

image

The updated code is available here. I am still not entirely happy with the structure and will update it soon.

1 Like

I updated the whole code to use python_scripts that update the friendly names automatically. Here’s how it looks:

image

The python_script is here.
The weather package is available here.

4 Likes

This is fantastic! I would really like to incorporate this into my setup but I have a couple of questions.

  1. I am not that familiar with including python_scripts, is that just an additional folder in my configuration setup? I saw that you have a line “python_script:” in your config file. I guess this doesn’t need an "!include . . " statement?

  2. I also see you are referencing weather icons under your local folders. What icons are you using? Any chance you have a url to where you got them from?

Nice work to everyone on this.

Thanks!

so in configuration.yaml I need to include the weather.yaml file? Where do I put the python script? I’m running hassio…

thanks - looks great.

@photo64 @DavidFW1960

Under homeassistant: in your configuration.yaml file, put packages: !include_dir_named packages then create a folder called packages in your main configuration folder, and place weather.yaml in there.

For python scripts, add python_script: to your configuration.yaml file, create a folder called python_scripts in your main configuration folder, and place dark_sky_friendly_names.py in there.

@arsaboo The python script to update the friendly names is great, nice work. One question: how do I make it so that it only updates the friendly name and leaves the icon alone? I tried commenting out lines 29 and 37, but it’s still not working, the icon is just the default eyeball. I want it to use the icon template I specify in the template sensor.

EDIT: I got it to work by replacing 'entity_picture' with 'icon' in lines 29 and 37. Not sure if there’s a better way to do it.

I’ve updated the original post to reflect the improvements to the code. Note I’ve made a few tweaks to make it work with the mdi icons as I originally wanted. Thanks @mconway @arsaboo for your improvements.

Guys…just realized that the code has some bugs that need to be fixed. I will try to fix them and, may be, create a new thread will all the necessary details.

1 Like

What are the bugs? Seems to be working fine for me. Keep the updated code in this post if possible so it’s not the same information in multiple places. I’ll update the OP with any improvements.

EDIT: Ah I found one, the friendly names change back to “second”, “third”, etc. when the state gets updated. Should just need to add a trigger to the automation to run the name update script after any state change.

The friendly_name is not retained during updates. I have updated the entire script to use triggers. I am not sure this is the final version, so instead of copying the code in the OP, please just link to this post so that readers can access the most recent version of the code.

Please the following script under python_scripts

You will also need the weather.yaml package.
https://github.com/arsaboo/homeassistant-config/blob/master/packages/weather.yaml

Thanks @arsaboo! I’ll update my configuration with this as well. I still prefer the MDI over the dark sky icons, but I’ll play with both and see which works best.

@arsaboo I like those icons. Where did you get them?