Lovelace: Button card

The default tap_action (there is no default for hold_action on purpose) for non toggle-able entities is already more-info. If it’s not the case for you, you have an issue (cache ?)

tbh, can’t imagine, this card has been there since forever…

will take them out again and see what gives.

@petro

can you share the config please? looks great !

it uses lovelace gen and there are issues with this version regarding text. I’ve since moved away from this config so I don’t have fixes for them

Also this uses lovelace gen, so you’ll need to remove the jinja stuff and replace it with what’s required in that field.

# lovelace_gen

{% set color = color|default('var(--paper-item-icon-active-color)') %}

type: custom:button-card
aspect_ratio: 1/1
entity: {{ entity }}
size: 60%
name: {{ name }}
show_state: true
show_name: true
show_label: false
styles:
  icon:
  - height: auto
  img_cell:
  - justify-content: start
  - align-items: start
  grid:
  - grid-template-areas: '"i info" "n n" "s s" "l l"'
  - grid-template-columns: 1fr 35%
  - grid-template-rows: 1fr 0.min-content min-content min-content
  - position: relative
  card:
    - border-radius: 15px
    - padding: 10px
  name:
  - justify-self: start
  - align-self: end
  - font-weight: bold
  - font-family: Helvetica 
  - font-size: 12px
  - text-align: start
  - background-image: linear-gradient(to right, white 0%, white 80%, rgba(0,0,0,0))
  - -webkit-background-clip: text
  - -webkit-text-fill-color: transparent
  - position: relative
  - display: inline-block
  - width: 100%
  - align-content: start
  - text-align: start
  - text-overflow: unset
  state:
  - justify-self: start
  - align-self: end
  - font-weight: bold
  - font-family: Helvetica 
  - font-size: 12px
  - text-align: start
  custom_fields:
    info:
    - align-self: start
custom_fields:
  info: >
      [[[
        function capitalizeFirstLetter(string) {
          return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
        }
        
        const length = 41;
        const width = 3;

        if (entity.state === 'on' && entity.attributes.brightness) {
          const radius = length / 2;
          const brightness = Math.round(entity.attributes.brightness / 2.54);
          const circumference = radius * 2 * Math.PI;
          return `
            <svg viewBox="0 0 50 50">
              <circle cx="25" cy="25" r="${radius}" fill="none" stroke="var(--paper-item-icon-color)" opacity="0.5" stroke-width="${width}" />
              <circle style="
                  transform: rotate(-90deg);
                  transform-origin: 50% 50%;
                  stroke-dasharray: ${circumference};
                  stroke-dashoffset: ${circumference - brightness / 100 * circumference};
                "
                id="c_brightness" cx="25" cy="25" r="${radius}" stroke="var(--paper-item-icon-active-color)" stroke-width="${width}" fill="none" stroke-linecap="round" />
              <text x="50%" y="54%" fill="var(--primary-text-color)" font-size="14" text-anchor="middle" alignment-baseline="middle">${brightness}<tspan font-size="10">%</tspan>
              </text>
            </svg>
            `;
          }
        else if (entity.state === 'on' && entity.attributes.speed && entity.attributes.speed_list)  {
          const text = capitalizeFirstLetter(entity.attributes.speed);
          const gap = 5;
          const edge = (50 - length) / 2;
          const y = 50 - edge;
          var items = entity.attributes.speed_list;
          if (items.indexOf('off') !== -1){
            items.splice(items.indexOf('off'), 1);
          }
          const current = items.indexOf(entity.attributes.speed)
          var i;
          var x1 = edge;
          var ret = `<svg  viewBox="0 0 50 50">`;
          var l = (length - gap * (items.length - 1)) / items.length;
          for (i = 0; i < items.length; i++) {
            var x2 = x1 + l;
            var color = (i <= current) ? "var(--paper-item-icon-active-color)" : "var(--paper-item-icon-color)";
            var opacity = (i <= current) ? "1.0" : "0.5";
            ret += `<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="${color}" stroke-width="${width}" opacity="${opacity}" stroke-linecap="round" />`;
            x1 = x2 + gap;
          }
          ret += `<text x="50%" y="54%" fill="var(--primary-text-color)" font-size="14" text-anchor="middle" alignment-baseline="middle">${text}</text></svg>`
          return ret;
        }
      ]]]
state:
- value: 'on'
  styles:
    card:
    - opacity: 1.0
    icon:
    - color: |
        [[[
          var [domain, object_id] = entity.entity_id.split('.');
          if (domain === "light")
            return 'var(--button-card-light-color)';
          return '{{ color }}';
        ]]]
    name:
      - color: white
    state:
      - color: gray
    lock:
      - color: white 
    label:
      - color: gray
- value: 'off'
  styles:
    card:
    - opacity: 0.5
    icon:
    - color: var(--paper-item-icon-color)
    name:
    - color: var(--primary-text-color)
    state:
    - color: var(--primary-text-color)
    label:
    - color: var(--primary-text-color)
    lock:
    - color: var(--paper-item-icon-color)
- value: "unavailable"
  styles:
    card:
    - opacity: 0.2
    icon:
    - color: var(--paper-item-icon-color)')
    name:
    - color: var(--primary-text-color)
    state:
    - color: hsl(0, 100%, 50%)
    label:
    - color: var(--primary-text-color)
    lock:
    - color: var(--paper-item-icon-color)
tap_action:
  action: toggle
  haptic: light
hold_action:
  action: more-info
  haptic: heavy
2 Likes

OK, I finally got this to work. Using the hints from this StackOverflow post, the trick is to make the img_cell grid item a flexbox container and then specify how all the items of the flex box are to be aligned (in this case the only item is the icon (ha-icon)).

For some reason, the only way to get this to work was to specify a 1/1 aspect ratio. Not sure why…

Here is the code:

type: 'custom:button-card'
entity: switch.my_switch
name: My Switch
show_state: true
aspect_ratio: '1/1'
icon: mdi:fire
styles:
  grid:
    - grid-template-areas: '"i" "n" "s"'
    - grid-template-columns: 1fr
    - grid-template-rows: 1fr min-content min-content
  img_cell:
    - display: flex
    - justify-content: left
  name:
    - justify-self: start
    - padding-left: 10px
    - font-weight: bold
    - text-transform: lowercase
  state:
    - justify-self: start
    - padding-left: 10px
1 Like

thanks for your commitment, that works!
button-card has really weird behavior sometimes… and iam really a noob if it comes to css. Is there a solution for a horizontal-stack with 4 button cards and card-mod? I modded all my lovelace cards to be not so wide
with:

style: |
  ha-card {
     box-shadow: var(--card-box-shadow);
     margin: 0px 8px 15px 8px;
   }    

with button-card the right card always gets cut off.

Ah yes, the old clip the box shadow problem. This is the best solution that I have come up with. As a side note, I have also been adding outlines in various places controlled by an input_boolean that helps immensely in visualizing what is going on. Of course you can also use the browsers dev tools to figure this out, but drawing outlines is bit more efficient than always turning on dev tools.

Here is the code snippet that I use to mod horizontal-stack. You can safely ignore the border if you don’t want a border around your horizontal-stack.

        - type: custom:mod-card
          style: |
            ha-card {
              {% if is_state('input_boolean.show_outlines', 'on') %} outline: 1px solid red; {% endif %}

              /* --------------------------------------------------
                Add space inside the border of horizontal-stack.
                This adds space between the border of the horizontal-stack
                and the outside of the contained elements so that
                those elements don't butt up against the inside
                of the horizontal-stack (and there is room for the
                box-shadow
                -------------------------------------------------- */
              padding: 15px;

              border: 2px solid black;
            }
          card:

            type: horizontal-stack
            cards:

Edit: I don’t think its so much that button-card has weird behavior, its just understanding the underlying DOM structure and getting more familiar with CSS. button-card has been a fantastic way to learn how this all works. The “framework” provided along with the ability to use embedded Javascript is extremely powerful. @RomRider has created something very unique here! (Thanks man!)

you come up with some crazy tricks there haha. Thanks alot! Weird behavior only for the unkown, this is so true for many things in life!

I have these button cards with color_type: card and color is displaying only once and it’s disappearing.

I would like to have the button card stay with the color as long as that entity state is on.

Am I missing anything?

UI: (dark theme - Hulu switch state is on)

Screen Shot 2020-05-04 at 2.41.09 PM

UI: (light theme)

Screen Shot 2020-05-04 at 2.54.40 PM

Code:

          - type: custom:stack-in-card
            title: Family Room TV
            mode: vertical
            cards:
              - type: horizontal-stack
                cards:
                  - type: custom:button-card
                    entity: switch.tv_activity_hulu_switch
                    icon: mdi:hulu
                    name: Hulu
                    color_type: card
                    # color: auto
                    # color: rgb(223, 255, 97)
                    color: "#7CBE6E"
                  - type: custom:button-card
                    entity: switch.tv_activity_plex_switch
                    icon: mdi:plex
                    name: Plex
                    color_type: card
                    color: "#7CBE6E"
                  - type: custom:button-card
                    entity: switch.tv_activity_hotstar_switch
                    icon: mdi:star
                    name: Hotstar
                    color_type: card
                    color: "#7CBE6E"
              - type: horizontal-stack
                cards:
                  - type: custom:button-card
                    entity: switch.tv_activity_cnn_switch
                    icon: mdi:newspaper
                    name: CNN
                    color_type: card
                    color: "#7CBE6E"
                  - type: custom:button-card
                    entity: switch.tv_activity_disney_switch
                    icon: mdi:music-circle
                    name: Disney
                    color_type: card
                    color: "#7CBE6E"
                  - type: custom:button-card
                    entity: remote.harmony_hub
                    icon: mdi:power
                    name: Harmony
                    color_type: card
                    # color: auto
                    color: "#CE2841"

With stack-in-card the background is removed unless you specify it in the keep object. Check the documentation of stack-in-card on github you’ll see :slight_smile:

Haha great! You really cleaned that up. Now that we don’t need extra_styles we can delete id="c_brightness" and add r="${radius}" to the circle.

Bingo! I was looking at ‘vertical-stack-in-card’ documentation instead of your ‘stack-in-card’.

:tada::tada: Version 3.3.3 :tada::tada:

Bugfixes

  • Units would be shown even with show_units: false (Fix #321)
  • Card would not work properly with HA < 0.107 (Fix #322)

Is there a way to change the display name of a state on the button-card?

For example: My TV state is “Standby”, but I want it to say “Off” on the card.

Thanks so much!

You need to use the state display option with JavaScript.

Can someone assist with Templating tap actions?

Is it possible to do different actions based on the state?

please allow me this maintenance question, I might have missed an update to the action field:

is this:

hold_action:
  action: call-service
  service: automation.trigger
  service_data:
    entity_id: entity

still correct for firing an automation which is the entity of the button? I ask because it very often doesn’t work (could also be caused by the conditional styling of the card, which is animated: blink 2s ease infinite which makes it a bit more difficult to touch the button on an iPhone…

reason I am expanding on this also, is I have several ‘Alert’ buttons showing on my Home view, in a conditional card, and on their own detailed view for the specific info. Example: a GitHub update is available: ping!, Home view shows the update button.

The same button also shows on my developer view, with all other stuff on the GitHub repos I use.

What I would love to do is, depending on the view I am on, have a hold-action defined.

On my Home view, I would like the hold-action to be a navigate to the developer view,

hold_action:
  action: navigate
  navigation_path: developer

and, on the developer view, the hold action would need to be what I posted above, trigger the automation.

hold_action:
  action: call-service
  service: automation.trigger
  service_data:
    entity_id: entity

I would hope to be able to write a template for that, but of course would need to access view in the JS templates
@RomRider would you allow to do this please?

As I have about 12 alert buttons, for which this scenario would go, it would be amazing if we could:
a) use templates for the actions, that
b) see the view

in Jinja I would be able to write this in a service_template, or maybe even a subscript, but in JS I am not sure how to proceed, so if at all possible would need some help here too…

please have a look? thanks!

Hey there,

is it possible to display to notifications? The example

- type: custom:button-card
    icon: mdi:lightbulb
    aspect_ratio: 1/1
    name: Nb lights on
    styles:
      grid:
        - position: relative
      custom_fields:
        notification:
          - background-color: >
              [[[
                if (states['input_number.test'].state == 0)
                  return "green";
                return "red";
              ]]]
          - border-radius: 50%
          - position: absolute
          - left: 60%
          - top: 10%
          - height: 20px
          - width: 20px
          - font-size: 8px
          - line-height: 20px
    custom_fields:
      notification: >
        [[[ return Math.floor(states['input_number.test'].state / 10) ]]]

shows one notification in the right upper corner. I would like to show another notification in the right corner at the bottom.

Is this possible?

I have the following button set up:

type: 'custom:button-card'
entity: switch.pool_pump
tap_action:
  action: more-info
name: |
  [[[ 
    if (states["timer.clean_cycle"].state == 'idle') return states['sensor.pump_on_today'].state + " h";
    else return "."
  ]]]
show_icon: true
show_name: true
custom_fields:
  timer:
    card:
      type: entities
      entities:
        - timer.clean_cycle
      graph: line
styles:
  grid:
    - position: relative
  custom_fields:
    timer:
      - position: absolute
      - overflow: unset
      - top: 78%
      - left: 15%
      - height: 0px

It shows the state of the pool pump. There are 2 values that I would like to display as the name/label depending on what the pump is doing ie ‘Clean cycle’ or otherwise:
Screenshot 2020-05-07 at 18.25.51 Screenshot 2020-05-07 at 18.09.26

During the Clean cycle the ‘time remaining’ is displayed, otherwise the hours run for the day are displayed.

The only way I know to show a timer counting down in real time is by using an ‘Entities’ card. I therefore embedded it in the button using a custom-field.

Positioning the custom-field was fiddly as I’m not that familiar with CSS styling, but eventually got there. The problem is that the position of the value changes depending on the size of the button.

Any ideas on how to correct this or other ways to do it, would be most welcome.

I get that width: and height: are in px. But, I’m using transparent and non-transparent button-cards as overlays on picture-elements cards to drive actions. Picture-elements’ images scale with the size of the display, but not the custom:button-cards laid on them. Is there a way to scale the button-card dimensions as a percent to keep them in sync with the screen-size?