Jinja: Is there any way to re-use template variables?

I don’t need lists or ranges, I’d just like to {% set %} some static variables and refer to them within other templates.
For example, this works great for making if statements more intuitive and reducing the amount of code:

        icon_template: >
            {% set batt_level = states.device_tracker.XXXXXX.attributes.battery_level | round() -%}

            mdi:battery
            {%- if batt_level >= 95 -%}
            {%- elif batt_level >= 85 -%}
            -90
            {%- elif batt_level >= 75 -%}
            -80
            {%- elif batt_level >= 65 -%}
            -70
            {%- elif batt_level >= 55 -%}
            -60
            {%- elif batt_level >= 45 -%}
            -50
            {%- elif batt_level >= 35 -%}
            -40
            {%- elif batt_level >= 25 -%}
            -30
            {%- elif batt_level >= 15 -%}
            -20
            {%- elif batt_level >= 5 -%}
            -10
            {%- elif batt_level < 5 -%}
            -outline
            {%- else -%}
            -unknown
            {% endif %}

But in the next template if I want to make the if statement more intuitive I have to increase the amount of code because the variable must be set again:

        value_template: >
            {% set batt_level = states.device_tracker.XXXXXX.attributes.battery_level | round() -%}

            {{ batt_level }}{{ '%' }}

So I’d like to re-use the same variable set in the icon_template:

        value_template: >
            {{ batt_level }}{{ '%' }}

My understanding is that this can be accomplished with a JavaScript to have a truly global variable, but I’m not strong enough with coding to figure out how. If there’s a simpler method that would only allow re-use within one .yaml file I’d be happy with that. Similarly, a method that only allows re-use within the same entity config would still be better than what I’m doing now.

This seems like it should be fairly simple but I haven’t found a solution yet. Hopefully I’m overlooking something obvious.

I see a very similar question was just asked here, I’m trying a suggestion…

Edit: corrected link

Short answer: No.

Long answer: The scope of a Jinja2 variable is constrained to the template in the option where it is defined. In your example above, the variable batt_level is defined in the icon_template option and that’s the limit of its scope; batt_level is undefined outside of icon_template.

very similar question was just asked here

The other question’s goal is to duplicate whole blocks of YAML code. Based on your example, you want something different. You want to refer to a Jinja2 variable outside of its normal scope (i.e. batt_level defined in one option’s template and then used in another option’s template).


FWIW, this is unrelated to your question but if you throw in some arithmetic, the example above can be streamlined:

            {% set batt_level = states.device_tracker.XXXXXX.attributes.battery_level | round() -%}

            mdi:battery
            {%- if batt_level >= 95 -%}
            {%- elif 5 <= batt_level < 95 -%} -{{ (batt_level // 5) * 5 }}
            {%- elif batt_level < 5 -%} -outline
            {%- else -%} -unknown
            {%- endif -%}
2 Likes

…and then reference them by anchor name, right? From my (admittedly novice) perspective this seems very much like setting variables. My variables couldn’t just be duplicated as anchors?

            {% set batt_level = states.device_tracker.XXXXXX.attributes.battery_level | round() -%}

            mdi:battery
            {%- if batt_level >= 95 -%}
            {%- elif 5 <= batt_level < 95 -%} -{{ (batt_level // 5) * 5 }}
            {%- elif batt_level < 5 -%} -outline
            {%- else -%} -unknown
            {%- endif -%}

This is awesome, thank you! :sunglasses:
I knew the math could be done but couldn’t wrap my head around the syntax (still having trouble even when seeing a direct comparison :sweat_smile: ).

What you’re actually asking is:

My Jinja2 variables couldn’t just be duplicated as YAML anchors?

Based on my understanding of how it works, they cannot but I encourage you to prove it to yourself.

I believe you, I was just clarifying to make sure I was understanding correctly.

-{{ (batt_level // 5) * 5 }} wouldn’t work for this specific application because it sometimes returns values ending in 5 and the icons labels are incremented by ‘-10’. But you put me on the right track to discovering that this works: -{{ batt_level | round(-1) | int }}.
Oddly, this rounds 5 down instead of up :man_shrugging:, but it’s close enough for my needs.

Thanks again for the help!

There’s no point in making a battery icon template. Use device_class: battery on your template sensor and it will fill in the rest, including the changing icon.

sensor:
  - platform: template
    sensors:
      my_battery:
        value_template: >
          {{ states.device_tracker.XXXXXX.attributes.battery_level | round() }}
        device_class: battery

The device_class battery will change the icon based on the value template and it will add the correct unit of measure.

This will round up:

{{ batt_level | round(-1, 'ceil') | int }}

However, values greater than 90 will be rounded up to 100 so the template must be adjusted accordingly … or just do what petro suggested and eliminate the template entirely by specifying a (battery) device_class.

I wish I’d known about that sooner. :sweat:
However it’s not adding the unit of measure and because it’s ‘%’ I can’t just add it on to the end as a string, and adding {{ '%' }} apparently supersedes the battery_level attribute resulting in a battery icon with a question mark.
I could just use unit_of_measurement but leading whitespace seems to be encoded (in all of them from what I’ve seen). That probably wouldn’t be a deal-breaker if I hadn’t already created an icon_template, but since I’ve got it I’ll keep using it. Thanks again for the advice though! :sunglasses:

This always rounds up, so I’d also omit all values below 10.

???

Check if the value falls within an acceptable range. If the value is outside the range, it is handled separately (that’s how the template is currently designed).

I don’t understand why you’d need the units in the state… That’s just going to make your history screwed up. It’s also the whole reason that unit_of_measure exists… Seems like you are making this difficult for no reason to be honest.

Sorry, this is what I meant to quote / was referring to:

With -{{ batt_level | round(-1) | int }} the icons change close to the halfway point of their increments: eg: 45% to 54% = mdi:battery-50, 55% to 64% = mdi:battery-60…, whereas always rounding up would result in 41% to 50% = mdi:battery-50, 51% to 60% = mdi:battery-60…

I don’t need them in the state, I just can’t display % any other way (that I know of), without leading whitespace.

I didn’t know that. The % is also displayed in my history graphs but I haven’t notice any issues. What will it screw up?

I’m enjoying experimenting and learning, I’m not sure what you’re perceiving as being difficult…?

Edit: elaborated a little more

It will not show up as a line graph, it will show up as text bar. Each battery state will have it’s own color and it will be written as text.

| 10% | 9% | 8% | etc instead of

1 Like

Hmm, OK. :unamused:
Any chance it could also be screwing up state.last_updated and state.last_changed? I can’t get those to persist through reboots and I’ve already tried deleting home-assistant_v2.db.

I wonder if the leading whitespace in unit_of_measurement is by design (necessary) or just an oversight (bug). :thinking:

They don’t persist through reboots ever. That’s by design.

Where are you even talking about this? In the interface?

Well I assumed that was the primary purpose for enabling history.

Correct. Even degree symbols (º) have an ugly space before them (85 ° instead of 85°).

Where in the UI? There are many different UI elements. Each have their own section of code. For example, this is what a card looks like for me. Notice there is no space between my units and the value.

image

So please, be explicit. Where in the UI are you talking about. Take a screenshot if necessary.

Your’s do appear to have space but your font seems to make them less apparent than mine.

This is on a picture-elements card as state-label.

With unit_of_measurement: '°' in sensor config:

With suffix: '°' in card config:

It’s been a long time since I set this up so I assumed I’d missed something back in the early days of using HA. But if I do as you said:

sensor:
  - platform: template
    sensors:
      phone_battery:
        value_template: >
          {{ state_attr('device_tracker.life360', 'battery') | default(50) | int }}
        device_class: battery

I get this:
image
But using this:

sensor:
  - platform: template
    sensors:
      phone_battery:
        unit_of_measurement: "%"
        value_template: >
          {{ state_attr('device_tracker.life360', 'battery') | default(50) | int }}
        icon_template: >
          {% if state_attr('device_tracker.life360', 'battery_charging') %}
            {% set charging = 'charging-' %}
          {% endif %}  
          {% set battery_level = state_attr('device_tracker.life360', 'battery') | default(50) | int %}
          {% set battery_round = (battery_level / 10) | int * 10 %}
          {% if battery_round >= 100 %}
            mdi:battery
          {% elif battery_round > 0 %}
            mdi:battery-{{ charging }}{{ battery_round }}
          {% else %}
            mdi:battery-alert
          {% endif %}

I get this:
image

Did you clear you cache? It should be working, however I don’t believe it will add a ‘charging’ version of the icon.