Lovelace: Button card

I seem to recall something about templating nested variables and templates on those. Since that would require the full button to be re-rendered constantly I believe that is not supported on purpose.

Would have to look that up, but I figure you could find that in this topic, or in the GitHub repo itself

Hi, I’m very new to all of this so apologies up front if this is the wrong place to ask for help. I’m trying to make a “button” that has 2 other buttons “inside” it. I’ve gotten pretty close, but for the life of me I cannot figure out how to flip the text “Television” and the icon. I’d like the text on top. I’d also like to separate the 2 volume buttons with some space in between them and cant seem to get that to work either. Any help, suggestions or points are greatly appreciated.

image

type: custom:button-card
aspect_ratio: 2/1
custom_fields:
  - card:
      type: custom:button-card
      size: 50%
      name: Volume Down
      show_name: false
      entity: script.fireplace_tv_volume_down
  - card:
      type: custom:button-card
      size: 50%
      name: Volume Up
      show_name: false
      entity: script.fireplace_tv_volume_up
entity: script.fireplace_tv_power
name: Television
styles:
  card:
    - height: 200px
    - padding: 25px 25px 200px

What you want to achieve can be done with grid styling in the card. Have a go and don’t hesitate to ask if you’re stuck.

Thanks a lot for a reply.
I could not find in the Docs anything regarding “use a variable to define a variable” case.

As for “templating variables” - it works fine in general, like in this simple example:

  variables:
    GRAPH_SENSOR: '[[[return entity.entity_id]]]'

Here the “GRAPH_SENSOR” var is used as a sensor for an embedded graph (“custom_field”); but this sensor may be replaced for some particular button.
Only using other variables inside a template give unstable results.

In a template sensor we may use smth like this:

attributes:
  attr_1: >-
    {% if this.attributes.attr_2 is defined -%}
      {{ .... use attr_2 ... }}
    {%- else -%}
      {{ .... use smth else ... }}
    {%- endif %}

i.e. “attr_2” is used to define “attr_1” - and for me it seems rather safe & stable.
Here with button-card I am getting erratic results.

hmm, you might be right, and I think I managed to figure out is was about the triggers_update option where no templates are allowed.

though upon inspecting your earlier post:

if (variables) will only be evaluated if in fact the variables exist. so its a moot if? also, using those if’s wouldn’t it be safer to add a second if != none. that way you would prevent the condition to be true, in the scenario you describe above

Yes, I suspect some more checks should be added. Will come back with results.

Really, using var2 inside var1 gives opportunities like in object-oriented programming.

Seems that “none” is not recognized:

Isn’t the “if (variables)” the same check for “none”?

Should have suggested ‘null’ ofc, none is Jinja…
See How do I check for null values in JavaScript? - Stack Overflow

I tested with “null” too. (Java does not know what is “none”, in C/C++/Java it is “null”)
Still no success.

Only the last examples does not give an error - but anyway gives a wrong result.

Seems that “variables” is SOMETIMES unknown inside defining a variable:

hmm, seems you need the feedback of Romrider then…

meanwhile, I would be interested in a real life usecase for this. And see if that couldn’t be implemented in a better way.

Not sure where the incidental (timing?) fluke lies, but maybe you could also have a look at the triggers_update: option, which would at least take care of correct updating of the button card as a whole. (not sure if this would trickle down inside your variables issue, but worth a look)

IMHO in my simple example there is no need for triggers_update since we got only one entity…

Here is a fictional example (may contain typos).
Imagine a button-template with ONE entity (could be of any domain).

state_display: >-
  [[[
    if (entity)
    {
      var STATE = entity.state;
      if (['unavailable','unknown'].includes(STATE))
        return some_text_for_disabled;
      else
        return some_normal_state;
    }
    else
      return some_text_for_undefined;
  ]]]

Now we need a button with a bit more complex processing:

state_display: >-
  [[[
    if (entity)
    {
      var STATE = entity.state;
      if (['unavailable','unknown'].includes(STATE))
        return some_text_for_disabled;
      else
      {
        if (some_condition)
          return some_state_1;
        else
          return some_state_2;
      }
    }
    else
      return some_text_for_undefined;
  ]]]

And we need, let’s say, 10 different buttons with different processing.

It could be like this:

variables:
  CONDITIONAL_STATE: >-
    [[[
      return some_normal_state;
    ]]]
state_display: >-
  [[[
    if (entity)
    {
      var STATE = entity.state;
      if (['unavailable','unknown'].includes(STATE))
        return some_text_for_disabled;
      else
        return variables.CONDITIONAL_STATE;
    }
    else
      return some_text_for_undefined;
  ]]]

with redefined “CONDITIONAL_STATE” in “descendants”:

variables:
  CONDITIONAL_STATE: >-
    [[[
      if (some_condition)
        return some_state_1;
      else
        return some_state_2;
    ]]]

And this works fine.

But imagine that the “state_display” is not an option - it is a variable itself.
Then we have a situation described in that example.
This is a very specific situation - and this should be avoided using workarounds.

Id have to read up on those, and try to see the use for it, sorry, was a long day and extremely short night…

however I did find this in my state_display templates:

  state_display: >
    [[[
      if (entity != undefined) {
        const data = states[entity.entity_id].attributes.data;
        const number = data[1].number == undefined ? '(' + data[1].aired.split('-')[0] + ')' : data[1].number;
        return `${data[1].title} ${number}`;
      }
      return 'Unknown';
    ]]]

also, sometimes you just have to find the correct set of conditions by experimenting :grimacing:

  styles:
    icon:
      - color: |
          [[[ if (entity && entity.attributes && entity.attributes.templates) {
                const tpl = entity.attributes.templates;
                if (tpl.icon_color) {
                  return new Function('state',`'use strict';${tpl.icon_color}`).call(this, entity.state);
                }
              }
              return null; ]]]

was the only set of conditions to test for in this case even though any of the single conditions would have seems sufficient to me.

so, that if, != undefined, should at least be a valid test, and if that doesnt help, you have other problems :wink:

What you posted is smth describing your checks for some entities.
The card supports any (hope so) user-defined processing.
What I was talking about is a possibility to re-define processing in “descendant” cards.
It could be related to checks for “wrong states” or anything else.
And as we can see there are some limitations with using variables - but luckily some workarounds may be used.

Thanks for the pointer, still struggling. Do you have an example that I may be able to follow?

Try something along these lines (untested):

type: custom:button-card
aspect_ratio: 2/1
custom_fields:
  - card:
      type: custom:button-card
      size: 50%
      name: Voldown
      show_name: false
      entity: script.fireplace_tv_volume_down
  - card:
      type: custom:button-card
      size: 50%
      name: Volup
      show_name: false
      entity: script.fireplace_tv_volume_up
entity: script.fireplace_tv_power
name: Television
styles:
  card:
    - height: 200px
    - padding: 25px 25px 200px
  grid:
    - grid-template-areas: '"n Volup" "i Voldown"'
    - grid-template-columns: 1fr 30%

Basically, you define the grid columns as 30% for the volume buttons and 1fr (rest of the available space), and then you assign the content you want to the grid’s spaces:

'entire grid goes here'
'"this is one row"'
'"Column1Row1 Column2Row1 Column2Row1" "Column1Row2 Column2Row2 Column3Row2"'

Spaces within a row separate your columns, so using names with spaces in them for the custom fields is not advised, hence Volup and Voldown.

Some reading on CSS grids, since that’s what this is based on: Intro, grid container, grid item.

Hi all.

How do I check the content of an attribute that can contain null or numbers?
I need to change a svg images color based on temperature value but, in some case, the temperature attribute is null (when I manually activate “boost”)

style: |
  .svg_image {
      fill: {% if state_attr(config.entity, "temperature") == 'null' %}
               red;
            {% else %}
              green;
            {% endif %}
  }

And this is the attribute value


I don’t known if “null” is a real null or a “null” string, but I have tried different check and all fails

{% if state_attr(config.entity, "temperature") is null %}
{% if state_attr(config.entity, "temperature") == null %}
{% if state_attr(config.entity, "temperature") | int > 0 %}

When the temperature is a number, this check working

{% if state_attr(config.entity, "temperature") == '19' %}

How can I do?

Regards,
Marco

Try this:

{% set STATE = state_attr(config.entity, "temperature") %}
{% if not is_number(STATE) %}
  ... red
{% else %}
  ... may use STATE to define a color
{% endif %}

I have found the solution just now.
This work!

if state_attr(config.entity, "temperature") == none

I’d like to change the transparency of a button background based on the brightness of a light. Do we have a default method for that?

The button below is toggling a boolean to set ‘glow mode’ or pulsating mode. meaning, it turns on a set of lights, runs a few scripts so they increase and decrease their brightness.

Turning off the boolean, stops the pulsating, but doesnt turn the lights off. That is on purpose.

What Id love to do is set the background transparency/opacity in the ‘off’ state of the boolean to reflect the actual brightness of the light.kerststerren.

type: custom:button-card
entity: input_boolean.pulsate_light
variables:
  duration: >
    [[[ return states['input_number.transition_time'].state; ]]]
tooltip: >
  [[[ return entity.state === 'on'? 'Fixed lights' :'Breathing lights'; ]]]
show_name: false
size: 95%
aspect_ratio: 1/1
state:
  - value: 'on'
    icon: mdi:hexagram
    styles:
      card:
        - background-color: ivory
        - animation: >
            [[[ return 'glow ' + variables.duration +'s ease ' + variables.duration +
                              's infinite normal forwards' +
                ',border-color ' + variables.duration +'s ease ' + variables.duration +
                               's infinite normal forwards'; ]]]
#        - border: '2px dotted #B71515'
      icon:
        - color: '#B71515'
        - animation: rotate_icon 2s, flash_icon 2s 1s infinite ease
  - value: 'off'
    styles:
      card:
        - background-color: saddlebrown
        - border: 2px double green
        - opacity: some method based on states['light.kerststerren'].attributes.brightness
      icon:
        - color: >
             [[[ return (states['light.kerststerren'].state == 'on')
                 ? 'rgb(' + states['light.kerststerren'].attributes.rgb_color + ')'
                 : 'green'; ]]]
    icon: mdi:hexagram

or maybe its just a simple as dividing that by / 255.0 ?

I did test something like:

        - background-color: rgba(139, 69, 19, 0.5)

which is ok-ish, but trying to get that a number from the actual state is more of a challenge:

  - background-color: >
      [[[ return 'rgba(139, 69, 19,' + 0.1 + ')'; ]]] /* saddlebrown */ 

doesnt work, let alone

  - background-color: >
      [[[ return 'rgba(139, 69, 19,' + states['light.kerststerren'].attributes.brightness/ 255 + ')'; ]]] /* saddlebrown */ 

would appreciate a helping hand very much, thx

update

this seems to work after all:

      card:
        - background-color: >
            [[[ var bri = states['light.kerststerren'].attributes.brightness;
                return bri ? 'rgba(139, 69, 19,' + states['light.kerststerren'].attributes.brightness/ 255 + ')'
                : 'saddlebrown'; ]]]

Which can further be shortened to

      card:
        - background-color: >
            [[[ var bri = states['light.kerststerren'].attributes.brightness;
                return bri ? 'rgba(139, 69, 19,' + bri/255 + ')'
                : 'saddlebrown'; ]]]

nice. full card finally:

type: custom:button-card
entity: input_boolean.pulsate_light
variables:
  duration: >
    [[[ return states['input_number.transition_time'].state; ]]]
tooltip: >
  [[[ return entity.state === 'on'? 'Fixed lights' :'Breathing lights'; ]]]
hold_action:
    action: call-service
    service: light.toggle
    service_data:
      entity_id: light.kerststerren
show_name: false
size: 95%
aspect_ratio: 1/1
state:
  - value: 'on'
    icon: mdi:hexagram
    styles:
      card:
        - background-color: ivory
        - animation: >
            [[[ return 'glow ' + variables.duration +'s ease ' + variables.duration +
                              's infinite normal forwards' +
                ',border-color ' + variables.duration +'s ease ' + variables.duration +
                               's infinite normal forwards'; ]]]
#        - border: '2px dotted #B71515'
      icon:
        - color: '#B71515'
        - animation: rotate_icon 2s, flash_icon 2s 1s infinite ease
  - value: 'off' # glow is 'off' but lights can still be 'on', so templating is required
    styles:
      card:
        - background-color: >
            [[[ var bri = states['light.kerststerren'].attributes.brightness;
                return bri ? 'rgba(139, 69, 19,' + bri/255 + ')'
                : 'saddlebrown'; ]]]
        - border: >
            [[[ return (states['light.kerststerren'].state == 'on')
                 ? '2px double red' : '2px dotted green'; ]]]
      icon:
        - color: >
             [[[ return (states['light.kerststerren'].state == 'on')
                 ? 'rgb(' + states['light.kerststerren'].attributes.rgb_color + ')'
                 : 'green'; ]]]
    icon: mdi:hexagram
extra_styles: |
  @keyframes border-color {
    0%  {
      border: 2px dotted green;
    }
    20% {
      border: 1px dashed saddlebrown;
    }
    40% {
      border: 2px dotted #B71515;
    }
    50% {
      border: 1px dashed green;
    }
    60%  {
      border: 2px dotted saddlebrown;
    }
    70% {
      border: 1px dashed #B71515;
    }
    80% {
      border: 2px dotted green;
    }
    90% {
      border: 1px dashed saddlebrown;
    }
    100% {
      border: 2px dotted #B71515;
    }
  }
  @keyframes glow {
    0%  {
      box-shadow: 0px 0px 0px 0px #B71515;
    }
    20% {
      box-shadow: 0px 0px 2px 2px green;
    }
    40% {
      box-shadow: 0px 0px 4px 4px green;
    }
    50% {
      box-shadow: 0px 0px 6px 6px green;
    }
    60%  {
      box-shadow: 0px 0px 8px 8px #B71515;
    }
    70% {
      box-shadow: 0px 0px 6px 6px green;
    }
    80% {
      box-shadow: 0px 0px 4px 4px green;
    }
    90% {
      box-shadow: 0px 0px 2px 2px #B71515;
    }
    100% {
      box-shadow: 0px 0px 0px 0px #B71515;
    }
  }
  @keyframes flash_icon {
    0%  {
      color: green;
    }
    24% {
      color: green;
    }
    40% {
      color: green;
    }
    75% {
      color: #B71515;
    }
    100% {
      color: #B71515;
    }
  }
  @keyframes rotate_icon {
    0% {
      transform: rotate(0);
    }
    100% {
      transform: rotate(3.142rad);
    }
  }

when i used this i became an error: “bad indentation of a mapping entry”

i would like to “lock” an card, when an boolean is true.

type: custom:button-card
entity: input_boolean.test
show_name: false
lock:
  enabled: '[[[ return entity.state === 'on'; ]]]'
  unlock: hold
  duration: 2