Template sensor: Declare variable in value_template to use in attribute_templates?

Is it possible to set a variable in the value_template of a template sensor for use in an attributes_template of the same template sensor? Or is there an altogether better way to achieve what I’m after? Thanks!

In the YAML below, I would like to use the variable type to form part of the entity name in the scheduled attributes template. With this code the attribute does not get defined.

- platform: template
  sensors:
    next_waste_collection:
      friendly_name: Waste Collection
      value_template: >-
        {% if states.sensor.recycling_black_bin.attributes.next_date < states.sensor.recycling_garden.attributes.next_date  %}
          Recycling + Black Bin
          {% set type = "black_bin" %}
        {% else %}
          Recycling + Garden Waste
          {% set type = "garden" %}
        {% endif %}
      attribute_templates:
        scheduled: >-
          {% if (as_timestamp(now()) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Today
          {% elif (as_timestamp(now() + timedelta(days=1)) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Tomorrow
          {% else %}
            {{ as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date')) | timestamp_custom('%A') }}
          {% endif %}

If I set the variable directly in the attributes_template as below, the attribute gets defined correctly of course.

      attribute_templates:
        scheduled: >-
          {% set type = "garden" %}
          {% if (as_timestamp(now()) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Today
          {% elif (as_timestamp(now() + timedelta(days=1)) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Tomorrow
          {% else %}
            {{ as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date')) | timestamp_custom('%A') }}
          {% endif %}

Also, in Dev Tools/Templates…

{% if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date')  %}
  Recycling + Black Bin
  {% set type = 'black_bin' %}
{% else %}
  Recycling + Garden Waste
  {% set type = 'garden' %}
{% endif %}

{% if (as_timestamp(now()) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
  Today
{% elif (as_timestamp(now() + timedelta(days=1)) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
  Tomorrow
{% else %}
  {{ as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date')) | timestamp_custom('%A') }}
{% endif %}

…gives the result I’m after:

Recycling + Garden Waste
Tuesday

So, I assume that the variable definition is not shared between main value_template and attribute_templates when setting up a template sensor as it is between the two blocks in Dev Tools/Templates.

Correct. A Jinja variable’s scope is limited to the option in which it is defined. In other words, you defined type within the option called value_template and that’s the only option where that variable exists.

For it to exist elsewhere (i.e. defined within one option and accessible to templates in other options within the same entity), it would have to be defined as a Home Assistant variable but that is only available to automations and scripts.

Thanks a lot for the quick response. I suppose that I’ll just make two separate sensors–or do you have another suggestion?

I have not tested what I am about to suggest so you’ll need to put it through its paces to confirm it works. If it does, it’s a simple hack that will achieve your goal.

All I’ve done is add this line to the scheduled option’s template:

{% set type = 'black_bin' if 'Black' in states('sensor.next_waste_collection') else 'garden' %}

It refers to the sensor’s own state value and checks if it contains the word “Black”. If it does then it sets type to black_bin otherwise to garden.

I’m making an assumption that value_template will be evaluated before scheduled is evaluated. In other words, the sensor’s new state value will be computed and available for use by schedule's template. Remember, it’s just an assumption so be prepared for it to produce incorrect results; it may not get the sensor’s newly computed value but its previous one (or worse, it may be inconsistent).

- platform: template
  sensors:
    next_waste_collection:
      friendly_name: Waste Collection
      value_template: >-
        {% if states.sensor.recycling_black_bin.attributes.next_date < states.sensor.recycling_garden.attributes.next_date  %}
          Recycling + Black Bin
          {% set type = "black_bin" %}
        {% else %}
          Recycling + Garden Waste
          {% set type = "garden" %}
        {% endif %}
      attribute_templates:
        scheduled: >-
          {% set type = 'black_bin' if 'Black' in states('sensor.next_waste_collection') else 'garden' %}
          {% if (as_timestamp(now()) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Today
          {% elif (as_timestamp(now() + timedelta(days=1)) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Tomorrow
          {% else %}
            {{ as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date')) | timestamp_custom('%A') }}
          {% endif %}

Thanks a lot. That works initially at least. Your suggestion made me think that I could also just evaluate on the original input to the sensor again:

 {% set type = "black_bin" if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date') else 'garden' %}

Both appear to work. I’ve set up the second as next_waste_collection_2. I’ll keep them both going through the next sensor update to see how they perform.

Really appreciate the quick help.

I can confirm that both value_template variations work. They both correctly set the day of the week, then tomorrow and then today, then changed back to the day of the week. Thanks again.

For anyone else looking to do something similar, here are the complete configs.

Using the newly evaluated state of the template sensor to determine the attribute value:

# Next waste collection - Uses custom integration Garbage Collection
- platform: template
  sensors:
    next_waste_collection:
      friendly_name: Waste Collection
      value_template: >-
        {% if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date')  %}
          Recycling + Black Bin
        {% else %}
          Recycling + Garden
        {% endif %}
      icon_template: >-
        {% if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date')  %}
          mdi:delete
        {% else %}
          mdi:flower
        {% endif %}
      attribute_templates:
        scheduled: >-
          {% set type = 'black_bin' if 'Black' in states('sensor.next_waste_collection') else 'garden' %}
          {% if (as_timestamp(now()) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Today
          {% elif (as_timestamp(now() + timedelta(days=1)) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Tomorrow
          {% else %}
            {{ as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date')) | timestamp_custom('%A') }}
          {% endif %}

Evaluating the state of the original sensors to determine the attribute value.

# Next waste collection - Uses custom integration Garbage Collection
- platform: template
  sensors:
    next_waste_collection_2:
      friendly_name: Waste Collection 2
      value_template: >-
        {% if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date')  %}
          Recycling + Black Bin
        {% else %}
          Recycling + Garden
        {% endif %}
      icon_template: >-
        {% if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date')  %}
          mdi:delete
        {% else %}
          mdi:flower
        {% endif %}
      attribute_templates:
        scheduled: >-
          {% set type = "black_bin" if state_attr('sensor.recycling_black_bin', 'next_date') < state_attr('sensor.recycling_garden', 'next_date') else 'garden' %}
          {% if (as_timestamp(now()) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Today
          {% elif (as_timestamp(now() + timedelta(days=1)) | timestamp_custom('%D')) == ((as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date'))  | timestamp_custom('%D'))) %}
            Tomorrow
          {% else %}
            {{ as_timestamp(state_attr('sensor.recycling_' ~ type, 'next_date')) | timestamp_custom('%A') }}
          {% endif %}
1 Like

If we could have variables in sensor template, it would be more readable and i guess faster to execute

You may wish to vote for the following Feature Request:

until the FR will be implemented, I believe the safest (probably also easiest) way is to write ‘base’ template sensors for your variable, and reference the state of that base sensor in the bigger template sensors, (in which state: field you can now also use the base sensor)

Main and very important advantage of this construct is that the base sensor is called only once, and all template derived fields are updated based on that exact same value.

1 Like