Lovelace: Button card

Yeah, add a custom_field to your main button. Add new buttons and values into the custom_fields

Then on your main card add the grid-template-areas as your custom_fields. Example with lots of values so ignore most. I normally use templates.

aspect_ratio: 1/1
type: 'custom:button-card'
custom_fields:
  currentrate_btn:
    card:
      type: 'custom:button-card'
      aspect_ratio: 2/1
      entity: sensor.octopus_agile_current_rate
      show_name: true
      show_state: true
      name: Current Rate
      styles:
        grid:
          - grid-template-areas: '"n" "s"'
          - grid-template-rows: 50%
          - grid-template-columns: 100%
        card:
          - border-radius: 0
          - box-shadow: none
        icon:
          - width: 50%
          - color: var(--paper-item-icon-color)
        state:
          - color: var(--paper-item-icon-color)
          - font-size: 10pt
          - justify-self: start
          - margin-left: 10%
          - margin-bottom: 10px
        name:
          - justify-self: start
          - margin-left: 10%
          - font-weight: bold
          - padding: 0
          - margin-top: 10px
      state:
        - operator: '>='
          value: 30
          styles:
            card:
              - background-color: '#FF5722'
              - color: white
            icon:
              - color: white
            state:
              - color: white
        - operator: '>='
          value: 20
          styles:
            card:
              - background-color: '#FFC107'
              - color: white
            icon:
              - color: white
            state:
              - color: white
        - operator: '>='
          value: 5
          styles:
            card:
              - background-color: '#4CAF50'
              - color: white
            icon:
              - color: white
            state:
              - color: white
        - operator: '>='
          value: 0
          styles:
            card:
              - background-color: '#2196F3'
              - color: white
            icon:
              - color: white
            state:
              - color: white
  nextrate_btn:
    card:
      type: 'custom:button-card'
      entity: sensor.octopus_agile_next_rate
      aspect_ratio: 2/1
      name: Next Rate
      show_name: true
      show_state: true
      styles:
        grid:
          - grid-template-areas: '"n" "s" '
          - grid-template-rows: 50%
          - grid-template-columns: 100$
        card:
          - border-radius: 0
          - box-shadow: none
        icon:
          - width: 50%
          - color: var(--paper-item-icon-color)
        state:
          - color: var(--paper-item-icon-color)
          - font-size: 10pt
          - justify-self: start
          - margin-left: 10%
          - margin-bottom: 10px
        name:
          - justify-self: start
          - margin-left: 10%
          - font-weight: bold
          - padding: 0
          - margin-top: 10px
      state:
        - operator: '>='
          value: 30
          styles:
            card:
              - background-color: '#FF5722'
              - color: white
            icon:
              - color: white
            state:
              - color: white
        - operator: '>='
          value: 20
          styles:
            card:
              - background-color: '#FFC107'
              - color: white
            icon:
              - color: white
            state:
              - color: white
        - operator: '>='
          value: 5
          styles:
            card:
              - background-color: '#4CAF50'
              - color: white
            icon:
              - color: white
            state:
              - color: white
        - operator: '>='
          value: 0
          styles:
            card:
              - background-color: '#2196F3'
              - color: white
            icon:
              - color: white
            state:
              - color: white
styles:
  grid:
    - grid-template-areas: '"currentrate_btn" "nextrate_btn"'
    - grid-template-rows: 50% 50%
    - grid-template-columns: 100%
  card:
    - padding: 0
    - margin: 0
triggers_update:
  - sensor.octopus_agile_current_rate
  - sensor.octopus_agile_next_rate

this is a first for me, but I am trying to check an attribute without an underscore in the slug and I dont know how to do that…

this is the correct jinja

{% set change = 'ahead' if is_state_attr('sensor.dst','dst active',true)
                            else 'back' %}`

note the unquoted true which is correct…

  change: >
    [[[ return states['sensor.dst'].attributes.[dst active] == true ? 'ahead' : 'back'; ]]]

doesn’t show anything, nor does 'dst active'
please help me finding the correct syntax for this?

second question: Id like the button to spin in the related direction, mdi:update clockwise when ‘ahead’, mdi:history anti-clockwise when ‘back’

haven’t found an example doing that yet, so please let me know if that can be done?

—EDIT—

mere guessing made me find it:

[[[ return states['sensor.dst'].attributes['dst active'] == true ? 'ahead' : 'back'; ]]]

figured if we didn’t need the . between states and [, we might not need it between attributes and [ either…

ok, now where can I read about that syntax?
still searching for the spin direction :wink:

Hi everyone,

I’ve got a question regarding the sizing of the custom button card:
I try to define the the layout of my button card in a relative manner so that it scales nicely (still a work in progress):

button_template:
  variables:
    svg_icon: null
    corner:   null
  name:               "[[[ return entity.attributes.friendly_name ]]]"
  color:              auto
  color_type:         icon
  aspect_ratio:       1/1
  state:              
      - value: 'on'
        styles:
          card:
            - background-color: var(--button-card-on-background)
            - color: var(--button-card-on-text-color) 
      - value: 'unavailable'
        styles:
          icon:
            - color: var(--button-card-disabled-text-color)
          name:
            - color: var(--button-card-disabled-text-color)

  # lock:               
  #   enabled:            false
  #   duration:           5
  #   unlock:             tap
  show_last_changed:  false


  custom_fields:  
    svg_icon: "[[[ return variables.svg_icon ]]]"
    corner:   ""
    corner_2: ""
    cust_1:   "[[[ return entity.state ]]]"
    cust_2:   ""
  
  # Styles of the card
  styles:

    # Card style itself
    card:
      - padding:            10%                         # Padding between card edge and contents
      - font-size:          12px                        # Font pixel size of the card, actual fonts are based relative to this value
      - text-transform:     capitalize
      - font-family:        -apple-system, system-ui, BlinkMacSystemFont
      - border-radius:      var(--button-card-border-radius)
      - background-color:   var(--button-card-background-color)
      - color:              var(--button-card-text-color)
      - backdrop-filter:    blur(20px)
    
    icon:
      - transform:        scale(1.5,1.5)
      - top:              -20%
      - left:             2%
      - width:            35%
      - position: absolute
      - opacity: >
              [[[ return variables.svg_icon == null ? 1 : 0; ]]]

    # Properties of the entity name text
    name:
      - font-size:          1.20em    # Text size of the entity name (relative to card font size)
      - font-weight:        bold
      - top:                60%
      - left:               10.1%
      - position:           absolute
      - text-align:         left
      - width:              80%

    # Lock properties
    # lock:
    #   - color: var(--button-card-lock-color)

    # Properties of the custom fields
    custom_fields:
      svg_icon:
        - top:                  11.5%
        - left:                 11.5%
        - width:                35%
        - position:             absolute

      corner: 
        - top:                  11.5%
        - right:                8%
        - max-width:            35%     # Max width to prevent text from overlapping with icon
        - padding-left:         0.5em   # Add some space between rounded edge and the text
        - padding-right:        0.5em   # Add some space between rounded edge and the text
        - position:             absolute
        - font-size:            1em     # Font size relative to card font size
        - font-weight:          bold
        - text-align:           middle
        - color:                var(--button-card-corner-on-text-color)
        - border-radius:        1em   # Should be half the height for a round edge
        - opacity:              0     # Make it invisible by default
        - background-color:     var(--button-card-corner-background-color)
        - line-height:          2em
        - height:               2em   # Must be equal to the line-height

      corner_2:
        - top:                  25%
        - right:                8%
        - position:             absolute
        - color:                var(--button-card-corner-text-color)

      cust_1:
        - top:                  75%
        - left:                 10.1%
        - text-align:           left
        - position:             absolute
        - font-weight:          bold
        - color:                var(--button-card-cust-1-text-color)

      cust_2:
        - top:                  70%
        - right:                10.1%
        - text-align:           right
        - position:             absolute
        - color:                var(--button-card-cust-2-text-color)

But when I load it on my phone and laptop, I get different presentations:
Laptop:
Laptop

Phone:
IMG_372731A8C4C6-1

Do you have any clue how I can make the layout of these cards the same?

2 Likes

It’s not different, just the font size is and the font size is never relative to the parent element. That’s how CSS works.

So if I understand you correctly there is no way to have the buttons look the same on laptop/phone?

Or would a workaround be to size the card based on the font?

Don’t use em :

em is not an absolute unit - it is a unit that is relative to the currently chosen font size.

so, depending an a state, I either need an icon to spin, or spin anti-clockwise.

since spin is built in, this is easy. However, the anti-clockwise probably needs something like:


@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(-360deg); }
}

as it happens the button itself is already animated when the main entity is ‘on’, so, I would need to combine the 2 animations…

this is the card:

type: custom:button-card
template: button_alarm
variables:
  dst: >
    [[[ return states['input_boolean.dst'].state ]]]
  view: >
    [[[ return (window.location.pathname.split('/')[2] == 'time_settings') ]]]
  adjust: >
    [[[ return states['sensor.dst'].attributes['dst active'] == true ? 'ahead' : 'back'; ]]]

entity: sensor.dst

show_state: true
show_name: false

tap_action:
  action: call-service
  service: input_boolean.toggle
  service_data:
    entity_id: input_boolean.dst

hold_action:
  action: >
    [[[ return variables.view ? 'more-info' : 'navigate'; ]]]
  navigation_path: >
    [[[ return variables.view ? null : '/ui-data/time_settings'; ]]]
  entity: >
    [[[ return variables.view ? entity.entity_id : null; ]]]

state_display: >
  [[[ return 'Dst: ' + variables.adjust ]]]
state:
  - operator: template
    value: >
      [[[ return (variables.dst == 'on'); ]]]
    spin: true
    styles:
      icon:
        - color: maroon
      card:
        - background-color: var(--background-color-on)
        - color: maroon
        - animation: blink 2s ease infinite
  - operator: template
    value: >
      [[[ return (variables.dst == 'off'); ]]]
    styles:
      icon:
        - color: green
      card:
        - background-color: var(--background-color-off)
        - color: grey

and my guess would be to add an additional operator template:

state:
  - operator: template
    value: >
      [[[ return (variables.dst == 'on' && variables.adjust == 'ahead'); ]]]
    spin: true
    styles:
      icon:
        - color: red
      card:
        - background-color: var(--background-color-on)
        - color: maroon
        - animation: blink 2s ease infinite
  - operator: template
    value: >
      [[[ return (variables.dst == 'on' && variables.adjust == 'back'); ]]]
    # spin: true <--- howe to replace this with an anti-clockwise animation
    styles:
      icon:
        - color: green
      card:
        - background-color: var(--background-color-on)
        - color: maroon
        - animation: blink 4s ease infinite

hope this can be done, thanks for having a look
edit
Wait, this seems to do it:

style: |
  @keyframes spin-back {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(-360deg); }
  }

and

  - operator: template
    value: >
      [[[ return (variables.dst == 'on' && variables.adjust == 'back'); ]]]
    # spin: true <--- how to replace this with an anti-clockwise animation
    styles:
      icon:
        - color: green
        - animation: spin-back 4s ease infinite  # <---- like this!
      card:
        - background-color: var(--background-color-on)
        - color: maroon
        - animation: blink 4s ease infinite

Thanks for that! Had the code right, turns out it was an indentation issue stopping my button from showing up.

Final product:

Has anyone implemented any scrolling state/name/label text?

I have a button which displays the Artist - Song Title in the label text but i would like it to scroll rather than … get cut off.

Hi,
I’m trying to pass a variable to a service_data field. Something along the lines of the following MWE of a template:

  media_service:
    variables:
      name: "var_name"
      service: "var_service"
      entity: "var_entity"
      type: "var_type"
      data: "var_data"
      picture: "var_picture"
    name: '[[[ return variables.name ]]]'
    tap_action:
      action: call-service
      service: '[[[ return variables.service ]]]'
      service_data:
        entity_id: '[[[ return variables.entity ]]]'
        '[[[ return variables.type ]]]': '[[[ return variables.data ]]]'
    hold_action:
      action: none
    show_entity_picture: true
    entity_picture: '[[[ return variables.picture ]]]'
    aspect_ratio: 1/1

And the template in use:

      - type: 'custom:button-card'
        template: media_service
        variables:
          name: Bluetooth
          service: media_player.select_source
          entity: media_player.pioneer_avr
          type: source
          data: BT AUDIO
          picture: /local/ui/btns/bluetooth.png

I’m struggling specifically with the “type” variable in this example. Is this even at all possible? Or have I missed something?

Check Scrolling/fading text in Lovelace, is it possible?

1 Like

Also, im sure i have seen it but can you apply a border to the mdi icon itself i.e. not the border container but the SVG?

Im sure i saw someone do it for a TV backlight / hyperion setup?

Thanks for this link, i tried <marquee> tag but its depreciated so wanted to do it in CSS. With a bit of fiddling i used the following code:

On the button card:

extra_styles: |
  @keyframes marquee {
    0%   { transform: translate(0, 0); }
    100% { transform: translate(-100%, 0); }
  }

Then within the state_display field:

state_display: |
  [[[
    var active_speaker_state = (states[variables.active_speaker].state);
    var active_speaker_media_artist = (states[variables.active_speaker].attributes.media_artist);
    var active_speaker_media_title = (states[variables.active_speaker].attributes.media_title);
    if (active_speaker_state === 'playing') return `<div style="animation: marquee 8s linear infinite;display: inline-block;padding-left: 100%;">${active_speaker_media_title}</div>`;
    else return "&nbsp;";
  ]]]

I now have scrolling song title - this will always scroll now though even if the text fits on the card. Not sure how i implement an IF width greater than clause within the CSS.

1 Like

marquee still works, if it doesnt in your config, you’ve made a mistake:

type: custom:button-card
template: button_default_title
styles:
  card:
    - padding: 5px
name: >
  [[[ var phrase = states['sensor.count_alerts_notifying'].state == 1
                   ? 'Alert:' : 'Alerts:';
      return `<div style='display: inline-flex;'>
      <div>${states['sensor.count_alerts_notifying'].state} ${phrase}&nbsp</div>
      <marquee>
      <span style='align-items: center;font-weight:normal;font-size:18px;'>
      ${states['sensor.marquee_alerts'].state}&nbsp</span>
      </marquee>`; ]]]

or

type: custom:button-card
template: button_summary_picture
entity: sensor.next_alarm
hold_action:
  action: navigate
  navigation_path: /lovelace/alarmclock
label: >
  [[[ var caption = 'Next:&nbsp';
      var alarm = states['sensor.next_alarm_day_only'].state;
      if (entity.state != 'Not set')
      return `<div style='display: flex;
                         padding: 0px 5px 0px 5px;
                         align-items: center;
                         background: transparent;'>
      <div>${caption}</div>
      <marquee>
      <span style='color: var(--primary-color);align-items: center;'>${alarm} at ${entity.state}</span>
      </marquee>`;
      return entity.state; ]]]
entity_picture: >
  [[[ if (entity.state != 'Not set') return hass.hassUrl('/local/badges/alarm.png');
      return hass.hassUrl('/local/badges/alarm_off.png'); ]]]
styles:
  label:
    - color: >
        [[[ if (entity.state != 'Not set') return 'crimson';
            return 'green'; ]]]

work just fine, even posted that some time ago in this thread: Lovelace: Button card - #3133 by Mariusthvdb

that might be rather difficult, given the fact these buttons adapt to the device and screen size, so you’d need to consider that in the logic.

I didnt say it didnt work, it did work but its a depreciated tag. Also the tag itself has some odd margin/padding which i couldnt be bothered to fix so i went the other route after looking at your examples.

ok cool, thanks for letting me know.

hello together,

i am very new to the custom button card, but i like it a lot.
One Question i have at the moment is, is it possible to “call” a different card with double-tap for example.

What i want to achieve is the following:
tap_action: => Open or Close a Cover (that works great)
double_tap_action: => call different card to set the postion (that does not work :frowning: )
The option more-info is not the thing i like at this point :frowning:

Perhaps anybody can share how he/she has achieved something like that

thx.

You can open popups or replace the more-info dialog with:

Hi, is it possible to use a custom icon like gif or svg instead of mdi?

Is it possible to set an entity name as an var and use it in template. I got an card it already partially templated but want to move the rest over to the template side, because all the setting is the same with the exception of entity name.

############## Picture Element Main Light Entities ##############
  - type: "custom:button-card"
    entity: light.hall_way_walkway
    template: light_element_button
    style:
      left: 54.5%
      top: 33%
    ######### want to move this section into template #########
    entity_picture: |
      [[[
        if (states['light.hall_way_walkway'].state == 'off')
          return "/local/photos/lovelace_photos/general/recessed_light_off.png";
        return "/local/photos/lovelace_photos/general/recessed_light_on.png";
      ]]]
    custom_fields:
      light_bri: |
        [[[
          var bri = states['light.hall_way_walkway'].attributes.brightness;
          if (bri > 0) 
            return Math.floor(bri/255 * 100) + '%';
          return
        ]]]
    ######### want to move this section into template ######### 
############## Button Card Template ##############
##### main light button
light_element_button:
  hold_action:
      action: more-info
  show_entity_picture: true
  show_name: false
  styles:
    grid:
      - position: relative
    entity_picture:
      - width: 21px
    custom_fields:
      light_bri:
        - position: absolute
        - height: 21px
        - width: 21px
        - font-size: 8px
        - line-height: 21px
        - align-self: middle
        - justify-self: start
        - color: 'red'