Templates with variables - passing sensor state works but not attribute

There are a few topics that seem to be similar to this, but none of them are exactly answering my question.

Background: I have a bunch of sensors (and attributes) that represent text description of an icon. These icons are used on an lvgl weather display. I am running into memory issues in ESPHome so I want to offload the icon selection code to HA. I’ve successfully done this where the descriptor is a sensor state, but it fails when the descriptor is an attribute of a sensor.

For example this works:

      - unique_id: weather_display_icon1
        variables:
          descriptor: "{{ states('sensor.rutherglen_icon_descriptor_1') }}"
        state: &selecticon >
            {% set icons =  [ 
              { "condition": "clear", "icon": "\uF00D" }, 
              { "condition": "cloudy", "icon": "\uF013" }, 
              { "condition": "cyclone", "icon": "\uF0C1" },
              { "condition": "dust", "icon": "\uF082" }, 
              { "condition": "dusty", "icon": "\uF082" }, 
              { "condition": "fog", "icon": "\uF014" }, 
              { "condition": "frost", "icon": "\uF076" }, 
              { "condition": "haze", "icon": "\uF021" }, 
              { "condition": "hazy", "icon": "\uF021" }, 
              { "condition": "heavy_shower", "icon": "\uF008" }, 
              { "condition": "heavy_showers", "icon": "\uF008" }, 
              { "condition": "light_rain", "icon": "\uF01A" }, 
              { "condition": "light_shower", "icon": "\uF009" }, 
              { "condition": "light_showers", "icon": "\uF00A" }, 
              { "condition": "mostly_sunny", "icon": "\uF00C" }, 
              { "condition": "partly_cloudy", "icon": "\uF002" }, 
              { "condition": "rain", "icon": "\uF019" }, 
              { "condition": "rainy", "icon": "\uF019" }, 
              { "condition": "shower", "icon": "\uF009" }, 
              { "condition": "showers", "icon": "\uF009" }, 
              { "condition": "snow", "icon": "\uF01B" }, 
              { "condition": "snowy", "icon": "\uF01B" }, 
              { "condition": "storm", "icon": "\uF01E" }, 
              { "condition": "storms", "icon": "\uF01E" }, 
              { "condition": "sunny", "icon": "\uF00D" }, 
              { "condition": "tropical_cyclone", "icon": "\uF073" }, 
              { "condition": "wind", "icon": "\uF050" }, 
              { "condition": "windy", "icon": "\uF050" } 
            ]  %}
            {% set weather_icons = icons | selectattr("condition", "equalto", descriptor) | list | first %}
            {{ weather_icons.icon }}
            
      - unique_id: weather_display_icon2
        variables:
          descriptor: "{{ states('sensor.rutherglen_icon_descriptor_2') }}"
        state: *selecticon

This returns “unavailable”:

      - unique_id: weather_display_hourly_icon0
        variables:
          descriptor: "{{ state_attr('sensor.weather_data', 'condition_0') }}"
        state: &selectdaynight >
            {% set icons =  [ 
              { "condition": "clear", "icon": "\uF00D" }, 
              { "condition": "cloudy", "icon": "\uF013" }, 
              { "condition": "cyclone", "icon": "\uF0C1" },
              { "condition": "dust", "icon": "\uF082" }, 
              { "condition": "dusty", "icon": "\uF082" }, 
              { "condition": "fog", "icon": "\uF014" }, 
              { "condition": "frost", "icon": "\uF076" }, 
              { "condition": "haze", "icon": "\uF021" }, 
              { "condition": "hazy", "icon": "\uF021" }, 
              { "condition": "heavy_shower", "icon": "\uF008" }, 
              { "condition": "heavy_showers", "icon": "\uF008" }, 
              { "condition": "light_rain", "icon": "\uF01A" }, 
              { "condition": "light_shower", "icon": "\uF009" }, 
              { "condition": "light_showers", "icon": "\uF00A" }, 
              { "condition": "mostly_sunny", "icon": "\uF00C" }, 
              { "condition": "partly_cloudy", "icon": "\uF002" }, 
              { "condition": "rain", "icon": "\uF019" }, 
              { "condition": "rainy", "icon": "\uF019" }, 
              { "condition": "shower", "icon": "\uF009" }, 
              { "condition": "showers", "icon": "\uF009" }, 
              { "condition": "snow", "icon": "\uF01B" }, 
              { "condition": "snowy", "icon": "\uF01B" }, 
              { "condition": "storm", "icon": "\uF01E" }, 
              { "condition": "storms", "icon": "\uF01E" }, 
              { "condition": "sunny", "icon": "\uF00D" }, 
              { "condition": "tropical_cyclone", "icon": "\uF073" }, 
              { "condition": "wind", "icon": "\uF050" }, 
              { "condition": "windy", "icon": "\uF050" } 
            ]  %}
            {% set weather_icons = icons | selectattr("condition", "equalto", descriptor) | list | first %}
            {{ weather_icons.icon }}

Replacing the variable with the state_attr works and returns the correct icon:

            ]  %}
            {% set weather_icons = icons | selectattr("condition", "equalto", state_attr('sensor.weather_data', 'condition_0') ) | list | first %}
            {{ weather_icons.icon }}

I assume this is something in how Python is handling these values in the background - any advice would be appreciated. I can workaround by templating the attributes into individual sensors but I thought I would ask first. :slight_smile:

are these template entities? If yes, variables for template entities only get resolved on configuration load, they do not update live before resolution of the state. Which is why it only works when you hardcode the value.

With that being said, do this instead:

      - unique_id: weather_display_hourly_icon0
        variables:
          <<: &conditions
            fallback:
              icon: "\uF00D"
            conditions:
              clear: 
                icon: "\uF00D"
              cloudy: 
                icon: "\uF013"
              cyclone: 
                icon: "\uF0C1"
              dust: 
                icon: "\uF082"
              dusty: 
                icon: "\uF082"
              fog: 
                icon: "\uF014"
              frost: 
                icon: "\uF076"
              haze: 
                icon: "\uF021"
              hazy: 
                icon: "\uF021"
              heavy_shower: 
                icon: "\uF008"
              heavy_showers: 
                icon: "\uF008"
              light_rain: 
                icon: "\uF01A"
              light_shower: 
                icon: "\uF009"
              light_showers: 
                icon: "\uF00A"
              mostly_sunny: 
                icon: "\uF00C"
              partly_cloudy: 
                icon: "\uF002"
              rain: 
                icon: "\uF019"
              rainy: 
                icon: "\uF019"
              shower: 
                icon: "\uF009"
              showers: 
                icon: "\uF009"
              snow: 
                icon: "\uF01B"
              snowy: 
                icon: "\uF01B"
              storm: 
                icon: "\uF01E"
              storms: 
                icon: "\uF01E"
              sunny: 
                icon: "\uF00D"
              tropical_cyclone: 
                icon: "\uF073"
              wind: 
                icon: "\uF050"
              windy: 
                icon: "\uF050"
        state: >
          {% set descriptor = states('sensor.rutherglen_icon_descriptor_0') %}
          {{ conditions.get(descriptor, fallback).icon }}

Then using it…

      - unique_id: weather_display_hourly_icon1
        variables:
          <<: *conditions
        state: >
          {% set descriptor = states('sensor.rutherglen_icon_descriptor_1') %}
          {{ conditions.get(descriptor, fallback).icon }}

AH - that’s more concise as well - although there is some logic I will need to consider for when I have a different set of icons for day and night. I have a try tomorrow - thanks.

Just make 2 sets of icons in that area. You can even just do a day/night key instead of icon and then conditionally choose the set.

E.g.

{% set time_of_day = 'day' if is_state('sun.sun', 'above_horizon') else 'night' %}
{{ conditions.get(descriptor, fallback).get(time_of_day, 'day') }}
1 Like

Hmm - ok… so it was never going to work as I expected anyway? Why does it work for states but not attributes?

I still think your suggested solution will be the way to go, or I may just use macros.

The variable is resolved at configuration load. So what you’re seeing just boils down to luck.

The variables resolve templates when the configuration is loaded. If the attribute doesn’t exist during the load process, it will be null or result in an exception. Which would happen when you reload templates if the entity in question is a template entity.

Below is an extract from the final config for icon lookup - using anchors and macros both. Many thanks to @petro for making me learn something. :smiley:

And yes - only the get_hourly_icon macro saves me some typing - I did the others as macros just for the exercise…

{% macro get_hourly_icon(attribute_id, datetime_id, fallback, conditions, night_conditions) %}

  {% set descriptor = state_attr('sensor.weather_data', attribute_id) %}
  {% set datetime = state_attr('sensor.weather_data', datetime_id) | int %}
  {% set sunrise0 = states('sensor.sunrise0') | int %}
  {% set sunset0 = states('sensor.sunset0') | int %}
  {% set sunrise1 = states('sensor.sunrise1') | int %}
  {% set sunset1 = states('sensor.sunset1') | int %}
  {% if (datetime >= sunrise0 and datetime <= sunset0) or (datetime >= sunrise1 and datetime <= sunset1) %}
    {{ conditions.get(descriptor, fallback).icon }}
  {% else %}
    {{ night_conditions.get(descriptor, fallback).icon }}     
  {% endif %}

{% endmacro %}

{% macro get_today_icon(entity_id, fallback, conditions, night_conditions) %}

  {% set descriptor = states(entity_id) %}
  {% if states('sun.sun') == "above_horizon" %}
    {{ conditions.get(descriptor, fallback).icon }}
  {% else %}
    {{ night_conditions.get(descriptor, fallback).icon }}     
  {% endif %}

{% endmacro %}

{% macro get_dayx_icon(entity_id, fallback, conditions) %}

  {% set descriptor = states(entity_id) %}
  {{ conditions.get(descriptor, fallback).icon }}

{% endmacro %}
     - unique_id: weather_display_hourly_icon0
        variables:
          <<: &conditions
            fallback:
              icon: "\uF00D"
            conditions:
              clear: 
                icon: "\uF00D"
              cloudy: 
                icon: "\uF013"
              cyclone: 
                icon: "\uF0C1"
              dust: 
                icon: "\uF082"
              dusty: 
                icon: "\uF082"
              fog: 
                icon: "\uF014"
              frost: 
                icon: "\uF076"
              haze: 
                icon: "\uF021"
              hazy: 
                icon: "\uF021"
              heavy_shower: 
                icon: "\uF008"
              heavy_showers: 
                icon: "\uF008"
              light_rain: 
                icon: "\uF01A"
              light_shower: 
                icon: "\uF009"
              light_showers: 
                icon: "\uF00A"
              mostly_sunny: 
                icon: "\uF00C"
              partly_cloudy: 
                icon: "\uF002"
              rain: 
                icon: "\uF019"
              rainy: 
                icon: "\uF019"
              shower: 
                icon: "\uF009"
              showers: 
                icon: "\uF009"
              snow: 
                icon: "\uF01B"
              snowy: 
                icon: "\uF01B"
              storm: 
                icon: "\uF01E"
              storms: 
                icon: "\uF01E"
              sunny: 
                icon: "\uF00D"
              tropical_cyclone: 
                icon: "\uF073"
              wind: 
                icon: "\uF050"
              windy: 
                icon: "\uF050"
            night_conditions:
              clear: 
                icon: "\uF02E"
              cloudy: 
                icon: "\uF013"
              cyclone: 
                icon: "\uF0C1"
              dust: 
                icon: "\uF082"
              dusty: 
                icon: "\uF082"
              fog: 
                icon: "\uF014"
              frost: 
                icon: "\uF076"
              haze: 
                icon: "\uF021"
              hazy: 
                icon: "\uF021"
              heavy_shower: 
                icon: "\uF028"
              heavy_showers: 
                icon: "\uF028"
              light_rain: 
                icon: "\uF01A"
              light_shower: 
                icon: "\uF029"
              light_showers: 
                icon: "\uF029"
              mostly_sunny: 
                icon: "\uF086"
              partly_cloudy: 
                icon: "\uF086"
              rain: 
                icon: "\uF019"
              rainy: 
                icon: "\uF019"
              shower: 
                icon: "\uF029"
              showers: 
                icon: "\uF029"
              snow: 
                icon: "\uF02A"
              snowy: 
                icon: "\uF02A"
              storm: 
                icon: "\uF01E"
              storms: 
                icon: "\uF01E"
              sunny: 
                icon: "\uF02E"
              tropical_cyclone: 
                icon: "\uF073"
              wind: 
                icon: "\uF050"
              windy: 
                icon: "\uF050"
        state: >
          {% from 'tools.jinja' import get_hourly_icon %}
          {{ get_hourly_icon('condition_0', 'datetime_0', fallback, conditions, night_conditions) }}
          
      - unique_id: weather_display_hourly_icon1
        variables:
          <<: *conditions
        state: >
          {% from 'tools.jinja' import get_hourly_icon %}
          {{ get_hourly_icon('condition_1', 'datetime_1', fallback, conditions, night_conditions) }}
              
      - unique_id: weather_display_icon0
        variables:
          <<: *conditions
        state: >
          {% from 'tools.jinja' import get_today_icon %}
          {{ get_today_icon('sensor.rutherglen_icon_descriptor_0', fallback, conditions, night_conditions) }}
                        
      - unique_id: weather_display_icon1
        variables:
          <<: *conditions
        state: >
          {% from 'tools.jinja' import get_dayx_icon %}
          {{ get_dayx_icon('sensor.rutherglen_icon_descriptor_1', fallback, conditions) }}
            
      - unique_id: weather_display_icon2
        variables:
          <<: *conditions
        state: >
          {% from 'tools.jinja' import get_dayx_icon %}
          {{ get_dayx_icon('sensor.rutherglen_icon_descriptor_2', fallback, conditions) }}