How can you test if an entity attribute exists in Jinja?

This is my first attempt at a custom control in Home Assistant, and my first post.

So I’ve got this far:

The bottom left of the pair of buttons is supposed to reflect the state of the entity preset (grey = none, white = any other preset and clicking will then go back to none, and dark grey = preset doesn’t exist - one of my radiators (Porch) is different to the others).

This works in javascript

ccol: |-
        [[[ if (states[entity.entity_id].attributes.preset_mode == 'none') {
          return 'rgba(128,128,128,0.7)';
        } else if (!states[entity.entity_id].attributes.preset_mode) {
          return 'rgba(0,0,0,0.7)';
        } else {
          return 'white';
        }  ]]]

but I then want to disable the button (otherwise you get an error when clicking) - but that requires Jinja - and that’s what I can’t get to work:

Edit - I should clarify - the code ‘works’ as in there are no errors and card-mod is installed - I just can’t get the if statement right - either all buttons are disabled or none are - whereas only the Porch button should be.

style: |
                  :host {
                    {% if state_attr('entity.entity_id','preset_mode') is none %}
                      pointer-events: none;
                    {% endif %}
                  }
                tap_action:
                  action: call-service
                  service: climate.set_preset_mode
                  service_data:
                    entity_id: '[[[ return entity.entity_id ]]]'
                    preset_mode: none

Any help appreciated. Full code below.

Thanks
Andy

button_card_templates:
  container:
    color_type: label-card
    color: dimgray
    styles:
      card:
        - padding: 0
      name:
        - border-radius: 0.4em 0.4em 0 0
        - padding: 0.1em
        - width: 100%
        - font-weight: bold
      grid:
        - grid-template-areas: '"i" "n" "buttons"'
        - grid-template-columns: 1fr
        - grid-template-rows: 1fr min-content min-content
      custom_fields:
        buttons:
          - background-color: rgba(0,0,0,0.3)
          - margin: 0
          - padding: 0.3em
  thermostat:
    show_icon: false
    hold_action:
      action: none
    variables:
      bcol: |-
        [[[
        if (states[entity.entity_id].state == 'off') {
          return 'rgba(128,128,128,0.7)';
        } else {
          const t = states[entity.entity_id].attributes.temperature;
          switch (true) {
            case (t <= 15):
              return 'rgba(30,144,255,0.7)';
              break;
            case (t <= 18):
              return 'rgba(0,192,0,0.7)';
              break;
            case (t >=22):
              return 'rgba(255,0,0,0.7)';
              break;
            default:
              return 'rgba(255,165,0,0.9)';
            }
          }
        ]]]
      tcol: |-
        [[[ const t = states[entity.entity_id].attributes.current_temperature;
        switch (true) {
          case (t <= 15):
            return 'rgba(30,144,255,0.9)';
            break;
          case (t <= 18):
            return 'rgba(0,192,0,0.7)';
            break;
          case (t >=22):
            return 'rgba(255,0,0,0.7)';
            break;
          default:
            return 'rgba(255,165,0,0.9)';
          }
        ]]]
      icol: |-
        [[[ if (states[entity.entity_id].state == 'off') {
          return 'rgba(192,192,192,0.7)';
        } else {
          return 'white';
        } ]]]
      ccol: |-
        [[[ if (states[entity.entity_id].attributes.preset_mode == 'none') {
          return 'rgba(128,128,128,0.7)';
        } else if (!states[entity.entity_id].attributes.preset_mode) {
          return 'rgba(0,0,0,0.7)';
        } else {
          return 'white';
        }  ]]]
      pcol: '[[[ return ''white''; ]]]'
    styles:
      card:
        - max-width: 48px
        - padding: 0
        - background-color: inherit
        - border-radius: 0
      grid:
        - grid-template-areas: '"n" "." "current" "." "temp" "." "buttons"'
        - grid-template-columns: 1fr
        - grid-template-rows: 25px 3px min-content 3px min-content 3px min-content
      name:
        - font-size: 0.65em
        - white-space: normal
        - color: white
      custom_fields:
        temp:
          - padding: 0
          - margin: 0
        current:
          - font-size: 0.8em
          - white-space: normal
          - color: '[[[ return variables.tcol ]]]'
    style: |
      ha-card { border-width: 0px; }
    custom_fields:
      temp:
        card:
          type: custom:button-card
          show_icon: false
          show_name: false
          styles:
            card:
              - height: 80px
              - background-color: '[[[ return variables.bcol ]]]'
            grid:
              - grid-template-areas: '". up ." ". target ." ". down ."'
              - grid-template-columns: 5% 1fr 5%
              - grid-template-rows: 1fr 1fr 1fr
            custom_fields:
              target:
                - font-size: 0.8em
                - color: '[[[ return variables.icol ]]]'
          custom_fields:
            up:
              card:
                type: custom:button-card
                icon: fas:play
                show_name: false
                size: 40%
                style: |
                  ha-card { border-width: 0px; }
                styles:
                  card:
                    - background-color: inherit
                  icon:
                    - color: '[[[ return variables.icol ]]]'
                    - transform: rotate(270deg)
                entity: '[[[ return entity.entity_id ]]]'
                tap_action:
                  action: call-service
                  service: climate.set_temperature
                  service_data:
                    entity_id: '[[[ return entity.entity_id ]]]'
                    temperature: >-
                      [[[ return states[entity.entity_id].attributes.temperature
                      + 0.5 ]]]
            target: >-
              [[[ return (states[entity.entity_id].attributes.temperature) +
              "°C"; ]]]
            down:
              card:
                type: custom:button-card
                icon: fas:play
                show_name: false
                size: 40%
                style: |
                  ha-card { border-width: 0px; }
                styles:
                  card:
                    - background-color: inherit
                  icon:
                    - color: '[[[ return variables.icol ]]]'
                    - transform: rotate(270deg) rotateY(180deg)
                entity: '[[[ return entity.entity_id ]]]'
                tap_action:
                  action: call-service
                  service: climate.set_temperature
                  service_data:
                    entity_id: '[[[ return entity.entity_id ]]]'
                    temperature: >-
                      [[[ return states[entity.entity_id].attributes.temperature
                      - 0.5 ]]]               
      buttons:
        card:
          type: custom:button-card
          entity: '[[[ return entity.entity_id ]]]'
          show_icon: false
          show_name: false
          styles:
            grid:
              - grid-template-areas: '"b1 . b2"'
              - grid-template-columns: 1fr 3px 1fr
            card:
              - background-color: inherit
              - padding: 0
              - margin: 0
          style: |
            ha-card { border-width: 0px; }
          custom_fields:
            b1:
              card:
                type: custom:button-card
                entity: '[[[ return entity.entity_id ]]]'
                icon: mdi:cancel
                size: 90%
                show_name: false
                styles:
                  card:
                    - background-color: '[[[ return variables.bcol ]]]'
                    - padding: 0
                    - margin: 0
                  icon:
                    - color: '[[[ return variables.ccol ]]]'
                    - padding: 0
                    - margin-top: 0
                    - margin-left: 0
                    - margin-right: 0
                    - margin-bottom: 1px
                style: |
                  :host {
                    {% if state_attr('entity.entity_id','preset_mode') is none %}
                      pointer-events: none;
                    {% endif %}
                  }
                tap_action:
                  action: call-service
                  service: climate.set_preset_mode
                  service_data:
                    entity_id: '[[[ return entity.entity_id ]]]'
                    preset_mode: none
            b2:
              card:
                type: custom:button-card
                entity: '[[[ return entity.entity_id ]]]'
                icon: mdi:power
                size: 90%
                show_name: false
                styles:
                  card:
                    - background-color: '[[[ return variables.bcol ]]]'
                    - padding: 0
                    - margin: 0
                  icon:
                    - color: '[[[ return variables.pcol ]]]'
                    - padding: 0
                    - margin-top: 0
                    - margin-left: 0
                    - margin-right: 0
                    - margin-bottom: 1px
                tap_action:
                  action: call-service
                  service: climate.set_hvac_mode
                  service_data:
                    entity_id: '[[[ return entity.entity_id ]]]'
                    hvac_mode: |-
                      [[[
                      if (states[entity.entity_id].state == 'off') {
                        return 'heat';
                      } else {
                        return 'off';
                      }
                      ]]]
      current: >-
        [[[ return (states[entity.entity_id].attributes.current_temperature) +
        "°C"; ]]]  

To answer the topic title question, testing if preset_mode exists as an attribute:

{% if 'preset_mode' in states.entity.entity_id.attributes %}

Note that there is a difference between “is none” and “== 'none'”. The preset_mode attribute of climate entities often exists and is the string 'none'.

Many thanks for your reply.

It still doesn’t seem to work. It works in template tester with the actual names of the entities:

It just doesn’t seem to like entity.entity_id. The code now reads:

style: |
                  :host {
                    {% if not 'preset_mode' in states.entity.entity_id.attributes %}
                      pointer-events: none;
                    {% endif %}
                  }

But this disables the button for all of the radiators.

Is there a way to set a variable using JS at the start and then reference that variable in the Jinja template??

Thanks
Andy

Oh, I understand now: it’s a variable. I have no experience of that, sorry, but what about:

{% if 'preset_mode' in states[entity.entity_id].attributes %}

You can’t just shove a variable in the middle of an object reference (states.entity.entity_id.attributes), and if you include it as a string, it’ll just be that string (state_attr('entity.entity_id','foo')).

Try this:

style: |
                  :host {
                    {% if states.entity.entity_id.attributes.preset_mode is not defined %}
                      pointer-events: none;
                    {% endif %}
                  }

Thank you both. Neither work.

And combining the two comments together doesn’t work either:

{% if states[entity.entity_id].attributes.preset_mode is not defined %}

Andy

I’ve also tried using a variable - declared at the start where the colors are:

nopreset: '[[[ return !states[entity.entity_id].attributes.preset_mode; ]]]'

and then referencing as:

style: |
                  :host {
                    {% if is_state('variables.nopreset','true') %}
                      pointer-events: none;
                    {% endif %}
                  }

but that doesn’t work either!

Is this all part of the custom layout-card or some other card? Does the custom card you’re using support Jinja2 templates?

Hi

It’s a button_card and yes I believe it supports templates.

If I change it to {% if true %} or {% if false %} then it will enable or disable all the buttons so it seems to be parsing OK - it’s just how to properly reference an entity or a variable, if indeed you can.

Thanks
Andy

Which custom button card? Is it this one?

(None of Home Assistant’s standard set of cards supports Jinja2 templates except for the Markdown card.)

Yes that’s the one.

type: custom:button-card
template: container
color: '#808080'
name: Radiators
custom_fields:
  buttons:
    card:
      type: horizontal-stack
      cards:
        - type: custom:button-card
          entity: climate.dining_room
          name: Dining Room
          template: thermostat

Where in its documentation do you see it mention support for Jinja2 templates?

I only see support for JavaScript templates. I found no results when I searched the web page for strings “Jinja2” and “{% set ” and “{% if ” .

Forgive me, I’m not sure. I’ve only just started learning this so I may well have gotten this wrong.

Can I achieve the same thing in JavaScript templates??

Maybe; I don’t use any custom cards so I can’t answer your question with certainty.

What’s clear though is that the reason why what was suggested failed to work is because that custom card doesn’t support Jinja2 templates.

I managed to make it work by doing this:

tap_action:
                  action: >-
                    [[[ return !states[entity.entity_id].attributes.preset_mode
                    ? "none" : "call-service" ]]]
1 Like