Lovelace: Button card

:tada::tada: Version 3.4.0 :tada::tada:

New Features

  • Auto expand groups with group_expand: true. This helps when your entity or any entity from triggers_update or any discovered entity is a group and you want to update the card also when child entities are updated (eg: counting the number of entities which are on in a group). This works with nested groups too. (Fix #392)

  • Support for configuring the card size for the auto-layout feature of lovelace using card_size. Default value is still 3, but you can now override it. Lovelace multiplies the value by about 50px for the auto layout feature.

    type: custom:button-card
    entity: switch.my_switch
    card_size: 42
    
  • Support for displaying a tooltip while hoovering the card for 1.5s using the tooltip parameter. It supports javascript templates. You can also apply any CSS style to the tooltip (either in the main styles config object or in any of the state entry).

    type: custom:button-card
    entity: switch.my_switch
    tooltip: |
      [[[
        return entity.state
      ]]]
    styles:
      tooltip:
        - color: '[[[ return entity.state === "on" ? "red" : "green" ]]]'
    

    or

    type: custom:button-card
    entity: switch.my_switch
    tooltip: |
      [[[
        return entity.state
      ]]]
    state:
      - value: 'on'
        styles:
          tooltip:
            - color: red
      - value: 'off'
        styles:
          tooltip:
            - color: green
    

Bugfixes

  • Don’t take brightness into account if the light is off (this is a bogus implementation of the light entity probably). Fix #378
  • Fix deprecation of --paper-card-background-color
  • Fix Timer not updating on page change (#365)

Other

  • Documentation typos (Thanks to all the contributors)
  • Some perf upgrades (probably only noticeable on slow systems)
  • Update all dependencies
2 Likes

I have been using the Button Card for awhile and just reread the documentation. I’m amazed at all the new features that have been added - thank you so much! I am now trying to “declutter” my lovelace views by using templates and variables, but have run into the following problem. I’m sure I am doing something stupid, but …

I’m simply trying to create a template for a light button.

This is the barebones template:

button_card_templates:
  light:
    variables:
      v_entity: light.fr1
      v_name: "undef"
      v_icon: mdi:lamp
    name: '[[[ return variables.v_name ]]]'
    entity: '[[[ return variables.v_entity ]]]'
    icon: '[[[ return variables.v_icon ]]]'

This is my lovelace config:

- type: custom:button-card             
  template: light
  variables:
    v_entity: light.fr2
    v_name: Fr2
    v_icon: mdi:lightbulb

The variables for v_name and v_icon are overridden as expected, but the variable for v_entity is not. In fact, the default value for v_entity does not even work. Whenever I click on the button I get the following error:

ERROR (MainThread) [frontend.js.latest.202011112] http://192.168.1.xxx:8123/hacsfiles/button-card/button-card.js:1:30589 Uncaught TypeError: Cannot read property 'state' of undefined

I get this error whether or not I specify the v_entity variable in the lovelace config. Anyone have any ideas? I need to pass the entity as a variable so I can apply some entity specific styles to the template.

Hi,
Is there any way to change the width of the “LIVING ROOM” label card to cover 4 of 6 buttons?
(as the red arrows show)
image
Thanks // Fredrik

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - color: 'rgb(44, 109, 214, 0.6)'
        color_type: label-card
        name: LIVINGROOM
        styles:
          card:
            - height: 20px
            - padding: 0px
        type: 'custom:button-card'
      - color: 'rgb(44, 109, 214, 0.6)'
        color_type: label-card
        name: UTILITY ROOM
        styles:
          card:
            - height: 20px
            - padding: 0px
        type: 'custom:button-card'
  - cards:
      - template: custom_gap_small
        type: 'custom:button-card'
      - entity: light.600040352462ab0fdc9e
        hold_action:
          action: call-service
          service: browser_mod.popup
          service_data:
            card:
              entity: light.600040352462ab0fdc9e
              type: 'custom:slider-entity-row'
            deviceID:
              - this
            title: Spotlights TV
        icon: 'mdi:spotlight'
        name: TV
        size: 40px
        template: custom_button_light_active_glow
        type: 'custom:button-card'
      - template: custom_gap_small
        type: 'custom:button-card'
      - entity: switch.25803704d8f15b012287_2
        icon: 'mdi:ceiling-light'
        name: Table
        size: 40px
        template: custom_button_light_active_glow
        type: 'custom:button-card'
      - template: custom_gap_small
        type: 'custom:button-card'
      - entity: light.321728022462ab4df00e
        hold_action:
          action: call-service
          service: browser_mod.popup
          service_data:
            card:
              entity: light.321728022462ab4df00e
              type: 'custom:slider-entity-row'
            deviceID:
              - this
            title: Dining
        icon: 'mdi:spotlight'
        name: Ceiling
        size: 40px
        template: custom_button_light_active_glow
        type: 'custom:button-card'
      - template: custom_gap_small
        type: 'custom:button-card'
      - entity: switch.25803704d8f15b012287_1
        icon: 'mdi:ceiling-light'
        name: Dining
        size: 40px
        template: custom_button_light_active_glow
        type: 'custom:button-card'
      - template: custom_gap_small
        type: 'custom:button-card'
      - entity: switch.25803704d8f15b015d79_1
        icon: 'mdi:spotlight'
        name: Ceiling
        size: 40px
        template: custom_button_light_active_glow
        type: 'custom:button-card'
      - template: custom_gap_small
        type: 'custom:button-card'
      - entity: switch.25803704d8f15b015d79_2
        icon: 'mdi:lamp'
        name: Window
        size: 40px
        template: custom_button_light_active_glow
        type: 'custom:button-card'
      - template: custom_gap_small
        type: 'custom:button-card'
    type: horizontal-stack

Entity doesn’t support template, because it’s not possible. The way to achieve that is with a configuration overload of just the entity.

- type: custom:button-card             
  template: light
  entity: light.fr2
  variables:
    v_name: Fr2
    v_icon: mdi:lightbulb

That would probably be possible with layout-card. No super easily achievable with button-card if you want to use your interface on different screen sizes.

Could you explain what you mean by “configuration overload of just the entity”, or better yet, point me in the right direction to docs about what this is?

I am trying to add a % label in the template as follows:

        label: >
          [[[
            var b = states['light.fr1'].attributes.brightness;
            return parseInt(b ? b/2.55 : '0') + '%';
          ]]]

Is something like this possible? Is there a way to reference the entity_id from within the template? I saw that the decluttering card can handle this, but am having problems using defaults in variables.

I am having a strange behaviour with the styling of a button card.
Having this part of light template:

    styles:
      card:
        - width: 105px
        - height: 105px
        - border: solid 1px yellow
        - border-radius: 30px
        - margin: 5px 5px 5px 5px
        - padding: 10px 0px
        - font-family: 'Georgia'
        - filter: drop-shadow(0 0 0.75rem crimson)
        - -webkit-box-shadow: 0px 0px 9px 3px var(--button-card-light-color)
        - box-shadow: 0px 0px 9px 3px var(--button-card-light-color)

I am expecting a border-radius like i configured it, but in the frontend i get the card without border-radius (or like it was set to 0px).
When i go to turn on the light the border-radius is shown as i set it in the template, but after a refresh it go again to 0px.

This is a screenshot of a light turned on and all the others turned off and all the button use the same template:

Schermata 2020-12-04 alle 11.58.08

How to have the correct border-radius as it was set in the template?

Unfortunately I have been unable to work on HA for some weeks (and that will persist for several more) so all I have been doing is keep my system up to date with HA versions and other custom component updates.

I have just noticed this error but I can’t see anything wrong with the code which has always worked in the past. Has anything changed that I missed?

        name: >
          [[[
            if (states['input_boolean.irrigation_master_control_switch'].state == 'off')
              return 'SYSTEM IS OFF';
            else
          ...etc...
          ]]]


To pre-empt the obvious questions here is the entire section of yaml.

        name: >
          [[[
            if (states['input_boolean.irrigation_master_control_switch'].state == 'off')
              return 'SYSTEM IS OFF';
            else
              var today = new Date();
              var weekday = new Array(7);
              weekday[0] = "sun";
              weekday[1] = "mon";
              weekday[2] = "tue";
              weekday[3] = "wed";
              weekday[4] = "thu";
              weekday[5] = "fri";
              weekday[6] = "sat";

              var day_today = weekday[today.getDay()];
              var day_tomorrow = weekday[(today.getDay() + 1) % 7];

              if (states['group.irrigation_group_cycle1_every_day'].state == 'on' ||
                  states['group.irrigation_group_cycle1_' + day_today].state == 'on')
                var cycle1_runs_today = true;
              else
                var cycle1_runs_today = false;

              if (states['group.irrigation_group_cycle2_every_day'].state == 'on' ||
                  states['group.irrigation_group_cycle2_' + day_today].state == 'on')
                var cycle2_runs_today = true;
              else
                var cycle2_runs_today = false;

              if (states['group.irrigation_group_cycle1_every_day'].state == 'on' ||
                  states['group.irrigation_group_cycle1_' + day_tomorrow].state == 'on')
                var cycle1_runs_tomorrow = true;
              else
                var cycle1_runs_tomorrow = false;

              if (states['group.irrigation_group_cycle2_every_day'].state == 'on' ||
                  states['group.irrigation_group_cycle2_' + day_tomorrow].state == 'on')
                var cycle2_runs_tomorrow = true;
              else
                var cycle2_runs_tomorrow = false;

              var time_now = states['sensor.time'].state;
              var cycle1_schedule_enabled = states['input_boolean.irrigation_cycle1_schedule_enabled'].state;
              var cycle2_schedule_enabled = states['input_boolean.irrigation_cycle2_schedule_enabled'].state;
              var cycle1_start_time = states['input_datetime.irrigation_cycle1_start_time'].state.substr(0, 5);
              var cycle2_start_time = states['input_datetime.irrigation_cycle2_start_time'].state.substr(0, 5);
              var cycle1_name = states['input_text.irrigation_cycle1_name'].state;
              var cycle2_name = states['input_text.irrigation_cycle2_name'].state;
              var cycle3_name = states['input_text.irrigation_cycle3_name'].state;

              var cycle1_running = states['input_boolean.irrigation_cycle1_running'].state;
              var cycle2_running = states['input_boolean.irrigation_cycle2_running'].state;
              var cycle3_running = states['input_boolean.irrigation_cycle3_running'].state;

              if (cycle1_running == 'on')
                return cycle1_name + ' cycle';
              else if (cycle2_running == 'on')
                return cycle2_name + ' cycle';
              else if (cycle3_running == 'on')
                return cycle3_name + ' cycle';
              else if (cycle1_schedule_enabled == 'on' && cycle1_runs_today && cycle1_start_time > time_now)
                  return cycle1_name + ' cycle at ' + cycle1_start_time;
              else if (cycle2_schedule_enabled == 'on' && cycle2_runs_today && cycle2_start_time > time_now)
                  return cycle2_name + ' cycle at ' + cycle2_start_time;
              else if (cycle1_schedule_enabled == 'on' && cycle1_runs_tomorrow)
                  return 'Tomorrow at ' + cycle1_start_time;
              else if (cycle2_schedule_enabled == 'on' && cycle2_runs_tomorrow)
                  return 'Tomorrow at ' + cycle2_start_time;
              else return 'NOTHING SCHEDULED';
          ]]]

reference the entity through entity. I recommend looking at the examples in the docs. This is covered in them under javascript template section. It describes all available built in globals.

The error claims that some states[] entity doesn’t exist. Revist all your entities and verify they exist in the system.

FWIW, it’s always best to make sure something exists.

Example, you have:

if (states['group.irrigation_group_cycle1_every_day'].state == 'on' ...

If states[‘group.irrigation_group_cycle1_every_day’] doesn’t exist, you’ll get an exception. This is what’s happening. So before you grab states from a potentially non existing object… do this:

var my_entity = states['group.irrigation_group_cycle1_every_day'];
if (my_entity && my_entity.state =='on' ...

if my_entity doesn’t exist, it will not resolve the second part of the if statement.

Also, when you assign it to a var like that, you can add console.log(my_entity); to verify that these things exist. That will print out info to the area where you saw the exception.

Ah HA! Thank you! I think you have given me the clue I need.

I have some automations that run on HA start to set some stuff up in this package. If the system is ‘off’ those automations are also turned off :man_facepalming:

No time to investigate fully now but I am pretty sure that will be the answer.

Thanks again for the nudge and the advice about pre-checking the existence of objects.

I’ll add it to the ever growing to-do list for when I finally get back to working on HA.

Thanks again (as always).


EDIT: Just did a restart with system ‘on’ so all automations ran and everything is as it should be. Thanks.

Thank you so much for your help - I think I’m starting to understand this a little better. If you don’t mind, I have one other question: What is the correct syntax to reference the entity variable in the following template excerpt?

    label: >
      [[[
        var b = states['light.fr1'].attributes.brightness;
        return parseInt(b ? b/2.55 : '0') + '%';
      ]]]

where I want to substitute the variable entity in for ‘light.fr1’. I tried all sorts of syntax but always get an error. I tried the following but none worked.

var b = states['entity'].attributes.brightness;
var b = states[entity].attributes.brightness;
var b = states[(entity)].attributes.brightness;
var b = states(entity).attributes.brightness;

simply

var b = entity.attributes.brightness;

also, attributes are always strongly typed so brightness is always an int. Also, you should build safety into your system if you accidentally add a entity without brightness or with the wrong entity_id.

label: >
  [[[
    return (entity && entity.attributes.brightness != undefined) 
      ? `${entity.attributes.brightness / 2.55}%` 
      : 'error';
  ]]]

Does the entity field support templates?

I am creating a media button that i would like to dynamically change based on which speaker is playing.

entity: >
  [[[   if (states["media_player.all_speakers"].state == "playing") return media_player.all_speakers;   
else if (states["media_player.upstairs_speaker"].state == "playing") return media_player.upstairs_speakers;  
else return media_player.dining_room_speaker;   ]]]

it does not support templates

You can use variables for that and use the result in all the other fields where it’s needed.

I have a problem with templates and layout-card.
I am using this button templates for my lights:

  light_button:
    label: >
      [[[ var bri = Math.round(entity.attributes.brightness / 2.55);  if
      (entity.state === 'on') return (bri ? (bri+"%") : '') ]]]
    layout: icon_label
    show_label: true
    show_name: true
    size: 40%
    state:
      - value: 'on'
        styles:
          name:
            - text-shadow: 2px 2px 5px red
          card:
            - background-color: green
      - value: 'off'
        styles:
          card:
            - background-color: lightgray
          name:
            - color: black
    styles:
      card:
        - width: 105px
        - height: 105px
        - border: solid 1px
        - margin: 5px 5px 5px 5px
        - padding: 10px 0px
        - font-family: 'Georgia'
        - border-radius: 0px
        - filter: drop-shadow(0 0 0.75rem crimson)
        - -webkit-box-shadow: 0px 0px 9px 3px var(--button-card-light-color)
        - box-shadow: 0px 0px 9px 3px var(--button-card-light-color)
        - outline-style: double
      grid:
        - grid-template: '"l" "i" "n"'
        - grid-template-rows: min-content 1fr min-content
        - grid-template-columns: 1fr
      icon:
        - width: 40px
        - color: var(--button-card-light-color)
      label:
        - font-size: 12px
        - font-weight: normal
        - color: white
      name:
        - justify-self: center
        - align-self: end
        - padding: 10px 10px
        - font-size: 13px
        - font-weight: normal
        - color: white
    tap_action:
      action: toggle
      haptic: light

If i use it for button cards inside a stack-in-card and all the view is with layout-card the lights doesn’t follow some of the options of the template (especially the ones for background card and text options).
Instead, using the same template without stack-card and layout card all is working as expected.

These are some examples.
This is without stack-card and layout card

This with them

Schermata 2020-12-04 alle 19.33.56

How to have the template working correctly in both the situations?

If you read the documentation of stack-in-card you’ll see there are options to keep the background / border radius of stacked cards etc… :wink:

1 Like

Interesting - i wonder how that works then as this doesnt work for me?

variables:
  var_name: "media_player.all_speakers"
entity: '[[[ return variables.var_name ]]]'

I have also thought if i created an automation to fill in a text input of the entity name which is playing i could then use the value of the text input as the entity on the button card… but im not sure that would work either.

entity still doesn’t support templates but you can reference this variable in the other fields like name, label, or any other field which supports javascript templates.
You don’t need an entity for the card to work.