Lovelace: Button card

Actually, I think my case is even more complex than just wanting templated *_action.

I am using buttons to display motion, door and window sensors. I sometimes have door and window sensors in the wrong state (cheap sensors!) so I use a double tap to open a custom-popup-card which allows me to set that sensor’s state via an input_select.

I combined all these buttons to use one template but the motion sensors don’t need a dbltap_action.

I have also realised that motion, door, and window sensors also have different icons (of course!) so would need to template those as well which I don’t think is possible either???

Here is my template after combining Door and Window and before realising the icons were wrong!

EDIT: Also, yes I have still got to template my label properly…

#============================
#=== Door and Window Sensors
#===
#=== If the button card ever allows templated tap_action
#=== this is the name_template...
#===    var sensor_name = entity.attributes.friendly_name;
#===    if (sensor_name.search('PIR') === 0) {
#===      return sensor_name.split('PIR ')[1];
#===    } else if (sensor_name.split(' ')[sensor_name.split(' ').length - 1] === 'Door') {
#===      var sensor_name = sensor_name.replace(' Door', '');
#===      return sensor_name;
#===    } else {
#===      var sensor_name = sensor_name.replace(' Window', '');
#===      var sensor_name = sensor_name.replace('Left', '(L)');
#===      var sensor_name = sensor_name.replace('Centre', '(C)');
#===      var sensor_name = sensor_name.replace('Right', '(R)');
#===      return sensor_name;
#===    }
#===
#============================
door_and_window_sensors:
  name_template: >
    var sensor_name = entity.attributes.friendly_name;
    if (sensor_name.split(' ')[sensor_name.split(' ').length - 1] === 'Door') {
      var sensor_name = sensor_name.replace(' Door', '');
      return sensor_name;
    } else {
      var sensor_name = sensor_name.replace(' Window', '');
      var sensor_name = sensor_name.replace('Left', '(L)');
      var sensor_name = sensor_name.replace('Centre', '(C)');
      var sensor_name = sensor_name.replace('Right', '(R)');
      return sensor_name;
    }
  show_label: true
  label_template: >
    return entity.state === 'on' ? '-MOVEMENT-' : ''
  layout: icon_label
  aspect_ratio: 2.5/1
  size: 50%
  state:
    - value: "on"
      icon: mdi:run
      styles:
        card:
          - box-shadow: 0px 0px 8px 2px var(--secondary-text-color)
        icon:
          - color: var(--secondary-text-color)
    - value: "off"
      icon: mdi:human-handsdown #mdi:walk
  styles:
#    card:
#      - width: 110px
    name:
      - font-size: 12px
    label:
      - font-size: 12px
      - font-weight: bold
      - color: var(--secondary-text-color)
      - justify-self: start
  tap_action:
    action: none
  hold_action:
    action: none
  dbltap_action:
    action: more-info

Why not just declare a generic template for your generic settings and declare specific templates for windows or doors that inherit your generic template and set your actions?

Something like this:

generic_sensors:
  name_template: >
    var sensor_name = entity.attributes.friendly_name;
    if (sensor_name.split(' ')[sensor_name.split(' ').length - 1] === 'Door') {
      var sensor_name = sensor_name.replace(' Door', '');
      return sensor_name;
    } else {
      var sensor_name = sensor_name.replace(' Window', '');
      var sensor_name = sensor_name.replace('Left', '(L)');
      var sensor_name = sensor_name.replace('Centre', '(C)');
      var sensor_name = sensor_name.replace('Right', '(R)');
      return sensor_name;
    }
  show_label: true
  label_template: >
    return entity.state === 'on' ? '-MOVEMENT-' : ''  ## Maybe this has to be specific for motion only, if so move it in the motion one
  layout: icon_label
  aspect_ratio: 2.5/1
  size: 50%
  styles:
#    card:
#      - width: 110px
    name:
      - font-size: 12px
    label:
      - font-size: 12px
      - font-weight: bold
      - color: var(--secondary-text-color)
      - justify-self: start
  state:
    - value: "on"
      id: sensor_on
      styles:
        card:
          - box-shadow: 0px 0px 8px 2px var(--secondary-text-color)
        icon:
          - color: var(--secondary-text-color)
  tap_action:
    action: none
  hold_action:
    action: none

window_sensors:
  template: generic_sensors
  dbltap_action:
    action: more-info
  state:
    - id: sensor_on
      icon: mdi:window-open
    - value: "off"
      icon: mdi:window-closed

door_sensors:
  template: generic_sensors
  dbltap_action:
    action: more-info
  state:
    - id: sensor_on
      icon: mdi:door-open
    - value: "off"
      icon: mdi:door-closed

motion_sensors:
  template: generic_sensors
  state:
    - id: sensor_on
      icon: mdi:run
    - value: "off"
      icon: mdi:human-handsdown

And then use the appropriate template in each type of sensor you want to display:

  • motion_sensors
  • windows_sensors
  • door_sensors

The trick here is that you can inherit template from a template and overload/add anything.

2 Likes

Brilliant!

Thanks…

EDIT: That all worked perfectly, Thanks again. The more I use this card the better it gets!!

And in case anyone is interested in how it looks…

4 Likes

nice!
of course you need to show us the code that creates all of this now…

Oh, ok.
Just for you :wink:

Templates

#===========================================
#=== Motion, Door and Window Sensors Header
#===========================================
motion_door_and_window_sensors_header:
  show_name: false
  show_label: true
  show_icon: false
  layout: icon_label
  label_template: >
    var i;
    var entities = entity.attributes.entity_id;
    var count = 0;
    var entity_type = entity.attributes.friendly_name.split(' ')[1];
    for (i = 0; i < entities.length; i++) {
      var state = states[entities[i]].state;
      if (state === 'on') {
        count += 1;
      }
    }
    if (entity_type === 'PIR') {
      var label_prefix = '-- Motion';
      var label_sufix = ' Detected --';
    } else if (entity_type === 'Door') {
      var label_prefix = '-- Doors';
      var label_sufix = ' are Open --';
    } else {
      var label_prefix = '-- Windows';
      var label_sufix = ' are Open --';
    }
    return label_prefix + (count.toString() > 0 ? label_sufix : ' --');
  styles:
    card:
      - background-color: var(--paper-card-background-color) #"rgba(0, 0, 0, 0.0)"
    label:
      - justify-self: start
      - padding-left: 24px
      - font-size: 24px
      - color: var(--primary-text-color)
    grid:
      - grid-template-areas: '"l"'
      - grid-template-columns: 1fr


#================
# Motion sensors
#================
motion_sensors:
  template: generic_motion_door_and_window_sensors
  state:
    - id: sensor_on
      icon: mdi:run
    - value: "off"
      icon: mdi:human-handsdown


#==============
# Door sensors
#==============
door_sensors:
  template: generic_motion_door_and_window_sensors
  dbltap_action:
    action: more-info
  state:
    - id: sensor_on
      icon: mdi:door-open
    - value: "off"
      icon: mdi:door-closed


#================
# Window sensors
#================
window_sensors:
  template: generic_motion_door_and_window_sensors
  dbltap_action:
    action: more-info
  state:
    - id: sensor_on
      icon: mdi:window-open
    - value: "off"
      icon: mdi:window-closed

      

#============================================
#=== Generic Motion, Door and Window Sensors
#============================================
generic_motion_door_and_window_sensors:
  name_template: >
    var sensor_name = entity.attributes.friendly_name;
    if (sensor_name.search('PIR') === 0) {
      return sensor_name.split('PIR ')[1];
    } else if (sensor_name.split(' ')[sensor_name.split(' ').length - 1] === 'Door') {
      var sensor_name = sensor_name.replace(' Door', '');
      return sensor_name;
    } else {
      var sensor_name = sensor_name.replace(' Window', '');
      var sensor_name = sensor_name.replace('Left', '(L)');
      var sensor_name = sensor_name.replace('Centre', '(C)');
      var sensor_name = sensor_name.replace('Right', '(R)');
      return sensor_name;
    }
  show_label: true
  label_template: >
    var sensor_name = entity.attributes.friendly_name;
    if (sensor_name.search('PIR') === 0) {
      return entity.state === 'on' ? '-MOTION-' : '';
    } else {
      return entity.state === 'on' ? '-OPEN-' : '';
    }
  layout: icon_label
  aspect_ratio: 2.5/1
  size: 50%
  state:
    - value: "on"
      id: sensor_on
#      icon: mdi:run
      styles:
        card:
          - box-shadow: 0px 0px 8px 2px var(--secondary-text-color)
        icon:
          - color: var(--secondary-text-color)
#    - value: "off"
#      icon: mdi:human-handsdown #mdi:walk
  styles:
#    card:
#      - width: 110px
    name:
      - font-size: 12px
    label:
      - font-size: 12px
      - font-weight: bold
      - color: var(--secondary-text-color)
      - justify-self: start
  tap_action:
    action: none
  hold_action:
    action: none
  dbltap_action:
    action: more-info

Button Cards

  #====================================
  #=== Motion, Door And Window Sensors
  #====================================
  - type: vertical-stack
    cards:

      #===========
      #=== Motion
      #===========
      - type: "custom:button-card"
        entity: group.all_pir_sensors
        template: motion_door_and_window_sensors_header

      - type: horizontal-stack
        cards:
#          - type: "custom:button-card"
#            color_type: blank-card

          - type: "custom:button-card"
            entity: binary_sensor.pir_hall
            template: motion_sensors

          - type: "custom:button-card"
            entity: binary_sensor.pir_dining_room
            template: motion_sensors

          - type: "custom:button-card"
            entity: binary_sensor.pir_kitchen
            template: motion_sensors

          - type: "custom:button-card"
            color_type: blank-card

      - type: horizontal-stack
        cards:
#          - type: "custom:button-card"
#            color_type: blank-card

          - type: "custom:button-card"
            entity: binary_sensor.pir_sitting_room
            template: motion_sensors

          - type: "custom:button-card"
            entity: binary_sensor.pir_pool_room
            template: motion_sensors

          - type: "custom:button-card"
            entity: binary_sensor.pir_upstairs_hall
            template: motion_sensors

          - type: "custom:button-card"
            color_type: blank-card

      - type: "custom:button-card"
        color_type: blank-card
        styles:
          card:
            - height: 1px

      #==========
      #=== Doors
      #==========
      - type: "custom:button-card"
        entity: group.all_door_sensors
        template: motion_door_and_window_sensors_header

      - type: horizontal-stack
        cards:
#          - type: "custom:button-card"
#            color_type: blank-card

          - type: "custom:button-card"
            entity: binary_sensor.front_door
            template: door_sensors
            state:
              - value: "on"
                icon: mdi:door-open
              - value: "off"
                icon: mdi:door-closed
          
          - type: "custom:button-card"
            entity: binary_sensor.back_door
            template: door_sensors
            state:
              - value: "on"
                icon: mdi:door-open
              - value: "off"
                icon: mdi:door-closed

          - type: "custom:button-card"
            entity: binary_sensor.side_door
            template: door_sensors
            state:
              - value: "on"
                icon: mdi:door-open
              - value: "off"
                icon: mdi:door-closed

          - type: "custom:button-card"
            color_type: blank-card

      - type: horizontal-stack
        cards:
#          - type: "custom:button-card"
#            color_type: blank-card

          - type: "custom:button-card"
            entity: binary_sensor.patio_door
            template: door_sensors
            state:
              - value: "on"
                icon: mdi:door-open
              - value: "off"
                icon: mdi:door-closed


          - type: "custom:button-card"
            entity: binary_sensor.shed_door
            template: door_sensors
            state:
              - value: "on"
                icon: mdi:door-open
              - value: "off"
                icon: mdi:door-closed

          - type: "custom:button-card"
            color_type: blank-card

          - type: "custom:button-card"
            color_type: blank-card

      - type: "custom:button-card"
        color_type: blank-card
        styles:
          card:
            - height: 1px


      #============
      #=== Windows
      #============
      - type: "custom:button-card"
        entity: group.all_window_sensors
        template: motion_door_and_window_sensors_header

      - type: horizontal-stack
        cards:
          - type: "custom:button-card"
            entity: binary_sensor.pool_room_left_window
            template: window_sensors

          - type: "custom:button-card"
            entity: binary_sensor.pool_room_centre_window
            template: window_sensors

          - type: "custom:button-card"
            entity: binary_sensor.pool_room_right_window
            template: window_sensors

          - type: "custom:button-card"
            entity: binary_sensor.kitchen_window
            template: window_sensors

      - type: horizontal-stack
        cards:
          - type: "custom:button-card"
            entity: binary_sensor.dining_room_left_window
            template: window_sensors

          - type: "custom:button-card"
            entity: binary_sensor.dining_room_right_window
            template: window_sensors

          - type: "custom:button-card"
            entity: binary_sensor.shower_room_window
            template: window_sensors

          - type: "custom:button-card"
            entity: binary_sensor.bathroom_window
            template: window_sensors

      - type: "custom:button-card"
        color_type: blank-card
        styles:
          card:
            - height: 20px
2 Likes

Some publicity for another card I’ve just created :blush:

It’s a small card that helps you declutter your Lovelace config by using templates for block of cards or a simple card. Changing the template will change it everywhere where its used.
This will be helpful for those of you who embed many cards in a vertical-stack-in-card for example with only small differences like the entity.

I’m thinking about you @petro, @daphatty or @jimz011 :wink:

1 Like

ok, so have many buttons and switches using the same body.
in fact they only differ in label( template).

Ive succesfully made a template for those:

ikea_label:
  label_template: >
    function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
    }
    var id = entity.entity_id.split('.')[1].slice(11);
    return capitalizeFirstLetter(id);

switch_label:
  label_template: >
    var id = entity.entity_id.split('.')[1].slice(3, -9);
    return states['sensor.' + id + '_actueel'].state + ' Watt';

and use this in the templates for button_switch and button_switch_ikea.

Since these have the rest of the switch (body) in common, i thought Id make a template: button_body and use these with the label templates as follows:

button_switch_ikea:
  template: ikea_label
  template: button_body

and then in the entity config use that compound template

  - type: custom:button-card
    template: button_switch_ikea
    entity: switch.sub_woofer_outlet
    name: Subwoofer
    state:
      - value: 'on'
        id: on-icon
        icon: 'mdi:speaker-wireless'
      - value: 'off'
        id: off-icon
        icon: 'mdi:speaker-off'

this doesn’t work unfortunately and only the button_body is taken into account…

I’ve also tried to use the entry config as follows

  - type: custom:button-card
    template: ikea_label
    template: button_body
    entity: switch.sub_woofer_outlet
    name: Subwoofer
    state:
      - value: 'on'
        id: on-icon
        icon: 'mdi:speaker-wireless'
      - value: 'off'
        id: off-icon
        icon: 'mdi:speaker-off'

but this has the same effect in the frontend.

Why won’t this work?

You can only define one template, not multiple.
You have to think of templates as a chain. You start from your button-card config and you go all the way up to the last template defined. The chain can be as long as you want.

So what you want to achieve is possible but it has to be done like this:

main_template:
  [all your common config here]

ikea_label:
  template: main_template # this will inherit the things from main_template
  label_template: >
    function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
    }
    var id = entity.entity_id.split('.')[1].slice(11);
    return capitalizeFirstLetter(id);

switch_label:
  template: main_template  # this will inherit the things from main_template
  label_template: >
    var id = entity.entity_id.split('.')[1].slice(3, -9);
    return states['sensor.' + id + '_actueel'].state + ' Watt';

And then you use only:
switch_label or ikea_label for your buttons

1 Like

ok thanks. Will test. cool.
A bit complex it is though, and error prone.
wouldn’t you consider easing up on that, and adding the option for more than 1 template? seems the obvious way to unclutter (pun intended).
A bit like anchors really.

Hope you would have a look.

I won’t implement that sorry as it’s already possible with the solution above. It’s not meant to replicate yaml anchors (which I don’t like) :slight_smile:

I will do so, but can only share it next weekend as I’m currently on a trip. Remind me if I forget :slight_smile:

I, too, am looking forward to your post. I’ve spent the last several hours attempting to replicate your awesome layout and cannot coax layout-card to behave properly. Enjoy your trip!

Hello guys, another rookie here.

I’ve got this set up and kinda working but I’m also suspecting im doing something wrong.

Pretty much everything i configure works. But I cant seem to get “state” to change color or icon of the entity inside the custom button card.

Im doing my editing inside the “RAW config editor” within the lovelace UI, is this somehow wrong?
Also, do i need anything else to get this to work? Custom UI maybe?

Share your config so that we can have a look. And no you don’t need anything else, just this button.

Hi,

we already discussed the
option for the name field, which as you can see would really be a good thing:

31 59

I was wondering if it could somehow be automized. In the label_template we can add +
+ to the output, and have it display the second line always.
But depending on the width of the button, it would not have to be always breaking the line:

41

Would be cool if that could be done.

the buttons above show something else: the left padding isn’t always correct, and it depends on the cropping if the button. As you can see that only happens with the name field. State,label and icon are always correct.

It seems to only happen with truncated names:

42
my config is:

button_body:
  color: auto
  size: 30%
  aspect_ratio: 1/1
  show_state: true
  show_label: true
  tap_action:
    action: toggle
    haptic: light
  hold_action:
    action: more-info
    haptic: success
  styles:
    lock:
      - color: red
    card:
      - border-radius: 12px
      - background-color: ivory
      - padding-left: 5px
    name:
      - justify-self: start
#      - padding-left: 3px
      - font-weight: bold
      - font-family: Helvetica 
      - font-size: 13px
    label:
#      - color: gray
      - font-size: 11px
      - font-family: Helvetica
      - padding: 0px 10px
      - justify-self: start
#      - padding-left: 3px
    state:
      - font-size: 11px
      - font-family: Helvetica
      - padding: 0px 10px
      - justify-self: start
      - text-transform: capitalize
      - font-weight: bold
#      - padding-left: 3px
    grid:
      - grid-template-areas: '"i" "n" "s" "l"'
      - grid-template-columns: 1fr
      - grid-template-rows: 1fr min-content min-content
    img_cell:
      - justify-content: start
      - align-items: start
  state:
    - value: 'on'
      styles:
        card:
          - box-shadow: 0px 0px 1px 1px var(--paper-item-icon-active-color)
        name:
          - color: black
        state:
          - color: gray
        label:
          - color: red
      id: on-icon
    - value: 'off'
      styles:
        card:
          - opacity: 0.6
        label:
          - color: rgba(0, 0, 0, 0.0)
        icon:
          - color: black
        name:
          - color: black
        state:
          - color: black
      id: off-icon
    - value: 'unavailable'
      styles:
        card:
          - opacity: 0.4
          - color: grey
          - --paper-item-icon-color: grey
        label:
          - color: rgba(0, 0, 0, 0.0)

Unfortunately (?) I use the storage mode therefore the code is fat a little bit :slight_smile:

please allow another detail question…

I like the small boder turning the color of the light in the ‘on’ state, and therefor use:

  state:
    - value: 'on'
      styles:
        card:
          - box-shadow: 0px 0px 1px 1px var(--paper-item-icon-active-color)

this show very nice, but unfortunately takes out the 3d effect of the button (ie the shadow at the bottom and right side (and to a lesser extent the left side?))

24

can I bring that back with yet another css option?
I’ve tried it with:

    card:
      - border-radius: 12px
      - background-color: ivory
      - padding-left: 5px
      - border: 1px outset

which shows:

01

which is a bit better, but the shadow is too clearly visible…

thanks for having a look how this should be improved.

For example:

            - styles:
                card:
                  - box-shadow: 0px 0px 20px 3px var(--button-card-light-color)
                  - background-color: 'rgba(57,128,228,0.2)'
              value: 'on'
            - styles:
                card:
                  - background-color: 'rgba(0,0,0,0.6)'
              value: 'off'

that was a bit much for me… :wink:

    - value: 'on'
      styles:
        card:
          - box-shadow: 1px 1px 1px 1px var(--paper-item-icon-active-color)

shows as:

04

which comes near, but still a bit unsubtle maybe.

hello you would be kind to publish the code of the fan that turns, I succeeded in part that is I can not bring the text to the right and the fan on the left all remains in the center and overlapped …

Inkedf74a0a23bd8aa8cb049ac795c5381e328069e43a_2_517x240_LI