How to create reusable JINJA code in templating?

Hello,
I’m still trying to optimize the readability of my code and its maintainability.

Then I’m asking myself if there is a way to create custom global functions for JINJA templates.

For example, in my home assistant, I’m customizing temperature badges based on a coloured scale.

Therefor I’m repeating a dozen of time (for each temp badges) the same portion of code below :

- type: state-label
  entity: sensor.my_temp
  name: MyTempSensor
  style: |
          :host {
            {% set state = states('sensor.my_temp') | float %}
            {% set text = 'white' %}
            {% if state < -23 %} {% set color = 'Magenta' %}
            {% elif state >= -23 and state  < -18 %} {% set color = 'Purple' %}
            {% elif state >= -18 and state < -12 %} {% set color = 'DarkMagenta' %}
            {% elif state >= -12 and state < -7 %} {% set color = 'Blue' %}
            {% elif state >= -7 and state  < -1 %} {% set color = 'Skyblue' %}
            {% elif state >= -1 and state < 4 %} {% set color = 'Green' %}
            {% elif state >= 4 and state < 10 %} {% set color = 'GreenYellow' %} 
            {% elif state >= 10 and state < 16 %} {% set color = 'Yellow' %} {% set text = 'black' %}
            {% elif state >= 16 and state  < 23 %} {% set color = 'Orange' %}
            {% elif state >= 23 and state < 27 %} {% set color = 'Red' %} 
            {% elif state >= 27 and state < 32 %} {% set color = 'GreenYellow' %}
            {% elif state >= 32 and state < 38 %} {% set color = 'DarkRed' %}
            {% elif state >= 38 %} {% set color = 'Maroon' %}
            {% else %} {% set color = 'grey' %}
            {% endif %} 
            color: {{ color }};
            --label-badge-red: {{ color }};
            --ha-label-badge-label-color: {{ text }};
          }

Well… it works, but it’s painful in terms of maintainability. The problem is : if now I decide to change my scale color schema, then I need to go everywhere and modify it.

Search & replace is my best friend, but I assume there should exist a better way to do that?

Anyone has a clever solution to avoid repeating the same code again and again?

Many thanks in advance.

Use card mod themes. Create the theme once, apply it to the cards you want to.

1 Like
  1. You’re using card mod. So you should use the config.entity variable and then use yaml anchors. However, the anchor will only work in the file it resides in. It will not work across !includes

      style: &temperature_sensor_style |
          :host {
                {% set state = states(config.entity) | float %}
                {% set text = 'white' %}
                {% if state < -23 %} {% set color = 'Magenta' %}
                {% elif state >= -23 and state  < -18 %} {% set color = 'Purple' %}
                {% elif state >= -18 and state < -12 %} {% set color = 'DarkMagenta' %}
                {% elif state >= -12 and state < -7 %} {% set color = 'Blue' %}
                {% elif state >= -7 and state  < -1 %} {% set color = 'Skyblue' %}
                {% elif state >= -1 and state < 4 %} {% set color = 'Green' %}
                {% elif state >= 4 and state < 10 %} {% set color = 'GreenYellow' %} 
                {% elif state >= 10 and state < 16 %} {% set color = 'Yellow' %} {% set text = 'black' %}
                {% elif state >= 16 and state  < 23 %} {% set color = 'Orange' %}
                {% elif state >= 23 and state < 27 %} {% set color = 'Red' %} 
                {% elif state >= 27 and state < 32 %} {% set color = 'GreenYellow' %}
                {% elif state >= 32 and state < 38 %} {% set color = 'DarkRed' %}
                {% elif state >= 38 %} {% set color = 'Maroon' %}
                {% else %} {% set color = 'grey' %}
                {% endif %} 
                color: {{ color }};
                --label-badge-red: {{ color }};
               --ha-label-badge-label-color: {{ text }};
          }
    

    then later in the file…

    - type: state-label
      entity: sensor.my_temp2
      name: MyTempSensor
      &temperature_sensor_style
    
  2. you don’t need to use an and for your if statements.

    This is much more readable

    {% elif -23 <= state  < -18 %}
    
  3. You’re writing your code in the least readable way. Use the spaces for readability.

                {% set state = states(config.entity) | float %}
                {% set text = 'white' %}
                {% if state < -23 %}
                  {% set color = 'Magenta' %}
                {% elif -23 <= state  < -18 %}
                  {% set color = 'Purple' %}
                ...
  1. You can refactor this pretty easily but it may not be more readable. Depends on your skill level.
            {% set state = states(config.entity) | float %}
            {% set colors = {
                -23: 'Purple',
                -18: 'DarkMagenta',
                -12: 'Blue',
                -7: 'Skyblue',
                -1: 'Green',
                4: 'GreenYellow',
                10: 'Yellow',
                16: 'Orange',
                23: 'Red',
                27: 'GreenYellow',
                32: 'DarkRed',
            } %}
            {% set colors = colors.items() | list %}
            {% set ns = namespace(found='Magenta') %}
            {% for i in range(len(colors)) %}
              {% set minimum, color = colors[i] %}
              {% set maximum = colors[i+1][0] if i+1 < len(colors) else 38 %}
              {% if minimum <= state < maximum %}
                {% set ns.found = color %}
              {% endif %}
            {% endfor %}
            {% set color = 'Maroon' if state >= 38 else ns.found %}
            color: {{ color }};
            --label-badge-red: {{ color }};
            --ha-label-badge-label-color: {{ 'white' if color != 'Yellow' else 'black' }};
  1. Lastly, you can go toms route as well for point 1. Seems more versatile than anchors
2 Likes

As I have structured my Lovelace dashboards with multiple includes rather than a big yaml, then it seems that toms solution for point 1) will better fit than the anchor if I want to keep less duplicate code as possible.

For now, I’m not familiar with the topic of the theme in card-mod, I need to go deeper into that topic, and see how I can do it, but should not be a big deal :wink:

I will first integrate your anchor suggestion, while I will read a little bit on card-mod themes possibilities to move later on to this solution (I think I could as well use it for other parts of my Lovelace dashboards)

And thank you very much for your code optimization tips, very helpful.

Here’s an example:

themes.yaml

day:

# all your normal theme variables here, then the card mod bit:

  card-mod-theme: day
  card-mod-card: |

    .card-header {
      font-size: 20px;
      font-weight: 300;
      letter-spacing: 0px;
    }

    ha-card.top-level-card {
      border: solid 1px var(--primary-text-color);
      background: linear-gradient(rgba(147, 171, 202, 1), rgba(255, 255, 255, 0.6) 56px, rgba(255, 255, 255, 0.6) 100%); #url("/local/background/card_bg_Day.png");
    }

    ha-card.top-level-card h1.card-header {
      padding-top: 0px;
      padding-bottom: 28px;
    }

And in every card I want the style applied I add these top two lines:

card_mod:
  class: top-level-card
type: entities
entities:
  - entity: switch.lounge_pc
  - entity: switch.pc_monitor
  - entity: automation.automatic_lounge_pc_sleep
  - entity: automation.automatic_lounge_pc_wake
show_header_toggle: false
title: Lounge PC

Screenshot 2021-11-13 at 23-56-10 Administration - Home Assistant

1 Like

I tried to transpose the anchor method given by petro (that works fine) to card-mod-theme based on your example, documentation (Card mod Themes · thomasloven/lovelace-card-mod Wiki · GitHub), and community discussions.

anchor in Lovelace dashboard :

- type: state-label
  entity: sensor.my_temp
  name: MyTempSensor
  style: &temperature_sensor_style |
      :host {
        {% set state = states(config.entity) | float %}
        {% set colors = 
          {
            -23: 'Purple',
            -18: 'DarkMagenta',
            -12: 'Blue',
            -7: 'Skyblue',
            -1: 'Green',
            4: 'GreenYellow',
            10: 'Yellow',
            16: 'Orange',
            23: 'Red',
            27: 'GreenYellow',
            32: 'DarkRed',
          } %}
        {% set listLen = colors | length %}
        {% set colors = colors.items() | list %}
        {% set ns = namespace(color='Magenta') %}
        {% for i in range(listLen) %}
          {% set minimum, color = colors[i] %}
          {% set maximum = colors[i+1][0] if i+1 < listLen else 38 %}
          {% if minimum <= state < maximum %}
            {% set ns.color = color %}
          {% endif %}
        {% endfor %}
        {% set color = 'Maroon' if state >= 38 else ns.color %}
        color: {{ color }};
        --label-badge-red: {{ color }};
        --ha-label-badge-label-color: {{ 'white' if color != 'Yellow' else 'black' }};
      }
- type: state-label
  entity: sensor.my_temp2
  name: MyTempSensor2
  style: *temperature_sensor_style

then below, what I tried with themes.yaml :

theme_badge_temp:
  card-mod-theme: theme_badge_temp
  card-mod-row: |
    :host (.color_full_scale) {
      {% set state = states(config.entity) | float %}
      {% set colors = 
        {
          -23: 'Purple',
          -18: 'DarkMagenta',
          -12: 'Blue',
          -7: 'Skyblue',
          -1: 'Green',
          4: 'GreenYellow',
          10: 'Yellow',
          16: 'Orange',
          23: 'Red',
          27: 'GreenYellow',
          32: 'DarkRed',
        } %}
      {% set listLen = colors | length %}
      {% set colors = colors.items() | list %}
      {% set ns = namespace(color='Magenta') %}
      {% for i in range(listLen) %}
        {% set minimum, color = colors[i] %}
        {% set maximum = colors[i+1][0] if i+1 < listLen else 38 %}
        {% if minimum <= state < maximum %}
          {% set ns.color = color %}
        {% endif %}
      {% endfor %}
      {% set color = 'Maroon' if state >= 38 else ns.color %}
      color: {{ color }};
      --label-badge-red: {{ color }};
      --ha-label-badge-label-color: {{ 'white' if color != 'Yellow' else 'black' }};
    }

and the state-label for my badge in lovelace dashboard :

- type: state-label
  entity: sensor.my_temp
  name: MyTempSensor
  card_mod:
    class: color_full_scale

But it doesn’t seem to be working

Just in case, I tried to change card-mod-row to card-mod-card / card-mod-badge-yaml.

I’m not sure if it’s possible to use “config.entity” that way in themes.yaml file, even if I have no error in my logs.

Initially, I used ( but not working) :

card-mod-row: |
      :host  {
      ...
    }

then add a class to :host, in order to have a class to pass to card-mod (and it could be useful in the future to specify different color schema for my temp sensors) :

card-mod-row: |
      :host (.color_full_scale) {
      ...
    }

I probably miss something to make the link between the themes.yaml and the card-mod in the Lovelace dashboard.

Hi Petro.

I don’t know if your still around after so long but I have to ask… Is there a book or some source I learn this from scratch? I have done pretty well pulling it together form forums, but where can I learn, chapter by chapter , how to do this stuff… I ask because this post filled in soooo many things I had just not seen in a context I could use before. Seems you might be the one to ask. Thanks

There are no books, you can probably find tutorials online. I learned jinja from reading the documentation and playing around.

For anyone else looking for a solution, look at the doc for reusing templates.

1 Like