Change CSS property (e.g. badge color) based on state w/ lovelace_gen and card-mod

I’m trying to use lovelace_gen and card-mod to create a badge template that will change CSS properties for 1…n family members based on their state…building on this (How to customize the badges at the top of the page? - #11 by Olivier1974).

This template generates properly formatted .yaml from the Developer Template Tool (i.e. if I copy/paste the output back into the .yaml, the badges render properly), but not when placed in the .yaml as a lovelace_gen template:

badges:
      {%- set firstnames = [
        'firstname1','firstname2','firstname3','firstname4','firstname5'
      ]
      -%}
      {% set lastname = 'lastname' -%}
      {% for firstname in firstnames %}
      - entity: person.{{ firstname }}_{{ lastname }}
        name: {{ firstname.capitalize() }}
        style: |
          :host {
            color: white;
            {% if is_state('person.' ~ firstname ~ '_' ~ lastname,'home') -%}
            --label-badge-red: green;
            {%- else -%}
            --label-badge-red: red;
            {%- endif %}}
      {%- endfor %}

Of course, I’ve replaced the strings of my family members’ names with placeholders. The line that was giving me a problem was this one:

{% if is_state('person.' ~ firstname ~ '_' ~ lastname,'home') -%}

But, it seemed to be the only way to get the template to work (i.e. properly set the label-badge-red property). However, this does not work for the actual dashboard (Unknown error - RELOAD UI)

I’ve also tried the following, but they didn’t even result in the proper css setting in the template tool:

{% if is_state('person.{{ firstname }}_{{ lastname }}','home') -%}
{% if is_state('person.' + {{ firstname }} + '_' + {{ lastname }},'home') -%}
{% if is_state('person.' ~ {{ firstname }} ~ '_' ~ {{ lastname }},'home') -%}

What’s the right syntax?

Note - I have lovelace_gen working on other views. Also, this particular .yaml file has the requisite #lovelace_gen as the first line and it is properly !include’d from another main .yaml file.

@thomasloven Any ideas to try? I’ve scoured the Jinja documentation and multiple posts/Google to see the proper way to concatenate. This seems IAW Jinja, so I’m not sure why it’s not working.

https://github.com/thomasloven/hass-lovelace_gen#what-if-i-want-jinja-in-my-lovelace-interface

Personally, I don’t like using {% raw %} and {% endraw %} as it makes it more confusing. I like to escape the jinja code.

In your case…

{% set fmat = "{{% 'green' if is_state('person.{0}_{1}','home') else 'red' -%}}" %}

then where ever you want to use it…

            --label-badge-red: {{ fmat.format(firstname, lastname) }}

@thomasloven Thank you. I read that section multiple times on the lovelace_gen and Jinja pages, but wasn’t/still not sure exactly how to use it. Simply adding it before and after the if block like this doesn’t work in the template editor:

            {% raw -%}
            {% if is_state('person.' ~ firstname ~ '_' ~ lastname,'home') %}
            --label-badge-red: green;
            {% else %}
            --label-badge-red: red;
            {% endif %}}
            {%- endraw %}

I would like to understand how this should be used. Can you please clarify?

@petro Thank you. I also gave your suggestion a try:

    badges:
      {%- set firstnames = ['first1,'first2','first3','first4','first5']-%}
      {% set lastname = 'ourlastname' -%}
      {% for firstname in firstnames %}
      {%- set fmat = "{{% 'green' if is_state('person.{0}_{1}','home') else 'red' -%}}" -%}
      - entity: person.{{ firstname }}_{{ lastname }}
        name: {{ firstname.capitalize() }}
        style: |
          :host {
            color: white;
            --label-badge-red: {{ fmat.format(firstname, lastname) }};
      {%- endfor %}

and got this result in the template editor:

--label-badge-red: {% 'green' if is_state('person.first1_ourlastname','home') else 'red'

I researched the format method and generally understand how it’s meant to work, but not sure what I’m missing here to get the right output (in this case --label-badge-red: green;)

WIth raw, you have to put it before and after all raw syntax…

{% raw %}{% if is_state('person.{% endraw %}{{ firstname }}_{{ lastname }}{% raw %}', 'home') %}{% endraw %}
... etc

My stuff had typos

{% set fmat = "{{{{ 'green' if is_state('person.{0}_{1}','home') else 'red' }}}}" %}

and .format is a python method on strings, so you’d need to read up on it in the python docs.

Thanks, that’s what I meant by my previous comment (I researched the python format method).

Here’s what I get from the template editor using that change:

--label-badge-red: {{ 'green' if is_state('person.first1_ourlastname','home') else 'red' }};

Shouldn’t it resolve to the final css to show it’s working?

Looking at the example raw syntax, it does seem like a pain. Using either method, I’m still trying to understand how and why we need to escape at all considering it’s all meant to be Jinja2/Python that produces properly formatted yaml, right? Is this to account for Jinja2 limitations? Put another way, why can’t I just concatenate literal and expressive strings (I think I asked that right :laughing:)?

No. It should resolve to the jinja that gets executed when a state changes.

Lovelace Gen generates your dashboard & cards. You’re trying to make lovelace gen generate jinja code. You don’t want lovelace gen to execute the jinja code. Thats why you need to escape it or use raw.

Lightbulb!

So, we really have three phases of code for this:

  1. Main lovelace_gen .yaml file containing yaml, Jinja, and escaped/raw Jinja
  2. Interpreted lovelace_gen containing yaml and Jinja only
  3. (on load and at state changes) Interpreted yaml (and css) code for use by the respective dashboards/views.

Good summary?

To verify the syntax just for the current states, I ran (1) through the template editor and then copy/pasted the output (2) back into the input side of the template editor. That second output (3) was properly formatted yaml that I was able to verify works properly by overwriting the .yaml file, uploading, and reloading the page…the badges rendered properly (again, just for the current states)!

I overwrote the .yaml file with (1) and restarted HA…Unknown error RELOAD UI. Arghh!! :laughing:

Unlike a couple weeks ago when you were guiding me on the proper use of yaml and lovelace_gen, I’m confident lovelace_gen is configured properly and this .yaml is properly included and marked as #lovelace_gen. I also cleared the cache and did a couple page reloads.

Anything else I should be checking?

Look in your logs for the error. Unknown errors should produce a log entry

Here’s what I show…the last few lines seem to be the most relevant:

Logger: homeassistant.components.websocket_api.http.connection
Source: YAMLSandboxIncluded.yaml:13
Integration: Home Assistant WebSocket API ([documentation](https://www.home-assistant.io/integrations/websocket_api), [issues](https://github.com/home-assistant/home-assistant/issues?q=is%3Aissue+is%3Aopen+label%3A%22integration%3A+websocket_api%22))
First occurred: 1:17:10 PM (7 occurrences)
Last logged: 3:48:48 PM

* [547637741888] Error handling message: Unknown error (unknown_error)
* [547648166688] Error handling message: Unknown error (unknown_error)
* [547749104656] Error handling message: Unknown error (unknown_error)
* [547276557232] Error handling message: Unknown error (unknown_error)

Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/components/websocket_api/decorators.py", line 27, in _handle_async_response await func(hass, connection, msg) File "/usr/src/homeassistant/homeassistant/components/lovelace/websocket.py", line 30, in send_with_error_handling result = await func(hass, connection, msg, config) File "/usr/src/homeassistant/homeassistant/components/lovelace/websocket.py", line 72, in websocket_lovelace_config return await config.async_load(msg["force"]) File "/usr/src/homeassistant/homeassistant/components/lovelace/dashboard.py", line 188, in async_load is_updated, config = await self.hass.async_add_executor_job( File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/usr/src/homeassistant/homeassistant/components/lovelace/dashboard.py", line 207, in _load_config config = load_yaml(self.path, Secrets(Path(self.hass.config.config_dir))) File "/usr/src/homeassistant/homeassistant/util/yaml/loader.py", line 111, in load_yaml return parse_yaml(conf_file, secrets) File "/usr/src/homeassistant/homeassistant/util/yaml/loader.py", line 123, in parse_yaml yaml.load(content, Loader=lambda stream: SafeLineLoader(stream, secrets)) File "/usr/local/lib/python3.9/site-packages/yaml/__init__.py", line 81, in load return loader.get_single_data() File "/usr/local/lib/python3.9/site-packages/yaml/constructor.py", line 51, in get_single_data return self.construct_document(node) File "/usr/local/lib/python3.9/site-packages/yaml/constructor.py", line 55, in construct_document data = self.construct_object(node) File "/usr/local/lib/python3.9/site-packages/yaml/constructor.py", line 100, in construct_object data = constructor(self, node) File "/usr/src/homeassistant/homeassistant/util/yaml/loader.py", line 253, in _ordered_dict nodes = loader.construct_pairs(node) File "/usr/local/lib/python3.9/site-packages/yaml/constructor.py", line 155, in construct_pairs value = self.construct_object(value_node, deep=deep) File "/usr/local/lib/python3.9/site-packages/yaml/constructor.py", line 100, in construct_object data = constructor(self, node) File "/config/custom_components/lovelace_gen/__init__.py", line 54, in _include_yaml return loader._add_reference(load_yaml(fname, ldr.secrets, args=args), ldr, node) File "/config/custom_components/lovelace_gen/__init__.py", line 32, in load_yaml stream = io.StringIO(jinja.get_template(fname).render({**args, "_global": llgen_config})) File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 1291, in render self.environment.handle_exception() File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 926, in handle_exception raise rewrite_traceback_stack(source=source) File "/config/YAMLSandboxIncluded.yaml", line 13, in top-level template code --label-badge-red: {{ 'green' if is_state('person.first1_ourlastname','home') else 'red' }}; File "/usr/local/lib/python3.9/site-packages/jinja2/utils.py", line 83, in from_obj if hasattr(obj, "jinja_pass_arg"): jinja2.exceptions.UndefinedError: 'is_state' is undefined

it looks like you didn’t use raw or escape it properly because it’s trying to resolve it on lovelace load.

Hmmm. I’m using the corrected syntax you shared earlier. Here’s the entire file:

# lovelace_gen

  - title: Apple
    path: Apple
    icon: mdi:food-apple
    # theme: Backend-selected
    badges:
      {%- set firstnames = ['first1','first2','first3','first4','first5']-%}
      {% set lastname = 'ourlastname' -%}
      {% for firstname in firstnames %}
      {% set fmat = "{{{{ 'green' if is_state('person.{0}_{1}','home') else 'red' }}}}" -%}
      - entity: person.{{ firstname }}_{{ lastname }}
        name: {{ firstname.capitalize() }}
        style: |
          :host {
            color: white;
            --label-badge-red: {{ fmat.format(firstname, lastname) }};
      {%- endfor %}
      - entity: sun.sun
        type: state-label
        style: |
          :host {
            color: white;
            --label-badge-red: silver;
            {% if is_state('sun.sun','above_horizon') -%}
            --label-badge-background-color: yellow;
            {%- else -%}
            --label-badge-background-color: black;
            {%- endif %}}

check your code again, you’re missing a character

@petro Thanks. That’s what I get for selecting the entire line and blindly copying/pasting. The brace is back to properly close the looped host block.

Unfortunately, I’m still getting the Unknown error and the ‘jinja2.exceptions.UndefinedError: ‘is_state’ is undefined’ error in the log.

This is the lovelace_gen:

# lovelace_gen

- title: Apple
  path: Apple
  icon: mdi:food-apple
  # theme: Backend-selected
  badges:
    {%- set firstnames = ['first1','first2','first3','first4','first5'] -%}
    {% set lastname = 'ourlastname' -%}
    {% for firstname in firstnames -%}
    {% set fmat = "{{{{ 'green' if is_state('person.{0}_{1}','home') else 'red' }}}}" %}
    - entity: person.{{ firstname }}_{{ lastname }}
      name: {{ firstname.capitalize() }}
      style: |
        :host {
          color: white;
          --label-badge-red: {{ fmat.format(firstname, lastname) }};}
    {%- endfor %}
    - entity: sun.sun
      type: state-label
      style: |
        :host {
          color: white;
          --label-badge-red: silver;
          {% if is_state('sun.sun','above_horizon') -%}
          --label-badge-background-color: yellow;}
          {%- else -%}
          --label-badge-background-color: black;}
          {%- endif %}

The template editor resolves the block that seems to be the problem to this:

      style: |
        :host {
          color: white;
          --label-badge-red: {{ 'green' if is_state('person.first1_ourlastname','home') else 'red' }};}

Which part of that snippet just above would be considered raw Jinja2 that I still need to escape and how would I do that using your method?

The syntax itself resolves properly when I put it back in the template editor, but I know that’s not how lovelace_gen works:

style: |
        :host {
          color: white;
          --label-badge-red: green;}

Honestly, at this point, I know I could just use the template editor generated code and move on, but I’m trying to understand lovelace_gen as much as you’re willing to help. I’ve already created a PR to update the README.md with what I came to understand about its configuration and clarify some of my confusion.

this is one i haven’t been able to get working. i have a sensor that stores lights on count in state and list of lights that are on in an attribute called devices. here’s the structure:

someSensor
  state: 2
  devices:
    - light.hue_color_1
    - light.hue_color_2

what i want to be able to do is show light cards for the list of devices in that sensor. here lightsOn is lovelace-gen jinja2 variable that stores 'sensor.someSensor':

{% set theseLights = {% raw %}states[{% endraw %}lightsOn{% raw %}].attributes.devices{% endraw %} %}
  {% for l in range(theseLights | len) %}
    - type: light
      entity: {{ theseLights[l] }}
{% endfor %}

but it produces the following unknown error in logs:

  File "/config/myHomeDashboard.yaml", line 1638, in template
    {% set theseLights = {% raw %}states[{% endraw %}lightsOn{% raw %}].attributes.devices{% endraw %} %}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
jinja2.exceptions.TemplateSyntaxError: unexpected '%'

any help figuring this out would be much appreciated.

thank you.

the examples posted above only work in lovelace_gen

sorry i didn’t get it? i am using lovelace-gen. the lightsOn is a lovelace-gen variable.

I think you’re miss-using it then. Lovelace_gen does not have access to the state machine, so you can’t use anything from the state machien in lovelace_gen. Raw is specifically used so that you don’t render a template at all during the lovelace_gen rendering process.

right. that’s why everything’s involving state is enclosed in the raw tags.

yes, but you’re trying to generate cards with it in lovelace_gen…

You can’t iterate because it doesn’t have access to the state machine…