I’ve recently discovered the power of custom:button-card
(GitHub - custom-cards/button-card: ❇️ Lovelace button-card for home assistant) and want to share some nice layouts I created. Be sure to read the documentation for the button-card at that link if you try to use any of these examples. I make extensive use of its templating feature.
If the image above is not animated, try clicking it to open a popup. The fans should be spinning and 2 sensors should be flashing red or blue.
There are a few things going on in that image, and everything you see is a custom button card or combination of them.
- standard and wide button variations
- a container that only needs one color setting to theme the header and body
- multi-option button
- sensors that have color animation when active
A Quick Note On Templates
The templates go in your raw dashboard config, usually at the top. To view your raw dashboard yaml, first get into edit mode in the UI, then click the same 3-dot menu icon again and choose Raw configuration editor
. For detailed info on how to define templates, there is a good section about it in the documentation for the custom button card here. In general, you define the template the same as you would if you were using the element in your dashboard, but you leave out the settings that would change (like entity). Then, when you use a custom button card in your dashboard you simply refer to your template by name in the template setting and add the unique settings for that button.
Standard & Wide Buttons
The standard and wide button variations are fairly straightforward. I have these defined as templates:
button_card_templates:
standard:
color_type: card
size: 80%
hold_action:
action: more-info
styles:
card:
- padding: 0.2em
- '--mdc-ripple-color': yellow
- '--mdc-ripple-press-opacity': 0.5
icon:
- opacity: 0.5
name:
- font-size: 0.65em
- white-space: normal
state:
- font-size: 0.65em
- white-space: normal
label:
- font-size: 0.4em
- white-space: normal
wide:
template: standard
styles:
grid:
- position: relative
- grid-template-areas: '"i n"'
- grid-template-columns: 1fr 1fr
- grid-template-rows: 1fr
I use the standard template to set my common style. The wide template builds on the standard template and just makes the icon and name side by side instead of over/under. To make the wide buttons actually wide rather than square I set the aspect_ratio, such as 2/1 or 4/1 depending on what ratio I need. I then tweak the ratio to fine-tune it to be the same height as other buttons on the same row. For example, the Garage and Upstairs buttons are 2.2/1, the Office buttons are 1.4/1 and the Game Room button is 4.5/1.
To use them in my dashboard I just supply the desired template and any other relevant properties that aren’t defined by the template or that I want to override:
- entity: switch.patio_light
template: standard
name: Patio
icon: 'mdi:outdoor-lamp'
type: 'custom:button-card'
Container with Header
This layout was inspired by a control that I used to use for the same effect, banner-card
(GitHub - nervetattoo/banner-card: A fluffy banner card for Home Assistant 🥰). I like the look of that, but I wanted more flexibility of the content. Fortunately it was quite easy to make a container with a button card using the custom_fields
config.
button_card_templates:
container:
color_type: label-card
color: dimgray
styles:
card:
- padding: 0
name:
- border-radius: 0.4em 0.4em 0 0
- padding: 0.1em
- width: 100%
- font-weight: bold
grid:
- grid-template-areas: '"i" "n" "buttons"'
- grid-template-columns: 1fr
- grid-template-rows: 1fr min-content min-content
custom_fields:
buttons:
- background-color: 'rgba(0,0,0,0.3)'
- margin: 0
- padding: 0.3em
This is a label card that contains a horizontal or vertical stack, which then contains whatever I want, such as buttons to control things. In the template above I defined a custom grid for the layout and set the styles. In the grid-template-areas
I have a grid area called buttons
that matches the buttons
custom field in custom_fields
. The custom_fields
is defined twice: once in the template styles, and once in my dashboard where I am defining the container. Below is the dashboard yaml for one of my containers in the example image.
type: 'custom:button-card'
template: container
color: '#EDE7B0'
name: Eating & Patio
custom_fields:
buttons:
card:
type: horizontal-stack
cards:
- entity: switch.ge_14291_in_wall_smart_switch_switch_2
name: Kitchen
template: standard
icon: 'mdi:wall-sconce-flat'
type: 'custom:button-card'
- entity: light.ge_14294_in_wall_smart_dimmer_level_10
name: Table
template: standard
icon: 'mdi:ceiling-light'
type: 'custom:button-card'
- entity: light.ge_14294_in_wall_smart_dimmer_level_7
name: Dining
template: standard
icon: 'mdi:ceiling-light'
type: 'custom:button-card'
- entity: switch.patio_light
template: standard
name: Patio
icon: 'mdi:outdoor-lamp'
type: 'custom:button-card'
Multi-option Button
The multi-option button is a bit more involved. It is essentially a button showing its own icon/name and 4 more buttons. In principal it works the same as the container, but it is styled differently and assumes I always want 4 options so that most of it is standard templates and reduces what I have to put in my dashboard. This also has custom javascript templates to detect value ranges when a particular option should be “on”, and it uses service calls to set the desired option value.
This uses several templates, so I will describe each one separately.
Option Button
This defines the basic look of an option button. It builds on the standard button, hides the icon, sets 1/1 aspect ratio to make it square, and sets a nice border radius.
button_card_templates:
option-button:
template: standard
show_icon: false
aspect_ratio: 1/1
styles:
card:
- border-radius: 20%
Dimmer Option, Blind Option, Fan Option
This builds on the option-button and defines the tap_action and state monitoring. You will notice that is is using the variables feature of the button-card to get some necessary values. Those are provided by the presets template that I will describe in a moment.
I am only showing the dimmer option here because they are all the same definition, but with different service type and service_data for the tap_action, and different attributes to examine in the javascript.
button_card_templates:
dimmer-option:
template: option-button
tap_action:
action: call-service
service: light.turn_on
service_data:
entity_id: entity
brightness: '[[[ return variables.set_value ]]]'
state:
- operator: default
styles:
card:
- background-color: '[[[ return variables.option_button_off_color ]]]'
- color: '[[[ return variables.option_text_off_color ]]]'
- operator: template
value: >-
[[[ return (entity.attributes.brightness||0) >= variables.range_start
&& (entity.attributes.brightness||0) <= variables.range_stop ]]]
styles:
card:
- background-color: '[[[ return variables.option_button_on_color ]]]'
- color: '[[[ return variables.option_text_on_color ]]]'
Presets Template
This is a big template. It defines the whole layout and all of the variables, with default values.
Special note on overflow: visible
: this is to allow the option button’s shadow to appear correctly.
button_card_templates:
presets:
template: standard
color_type: icon
tap_action:
action: none
styles:
card:
- background-color: 'rgba(0,0,0,0.3)'
icon:
- color: white
grid:
- grid-template-areas: '"i opt1 opt2 opt3 opt4" "n opt1 opt2 opt3 opt4"'
- grid-template-columns: 1fr 1fr 1fr 1fr 1fr
custom_fields:
opt1:
- margin: 0.1em
- overflow: visible
opt2:
- margin: 0.1em
- overflow: visible
opt3:
- margin: 0.1em
- overflow: visible
opt4:
- margin: 0.1em
- overflow: visible
variables:
option_template: dimmer-option
option_button_on_color: var(--paper-item-icon-active-color)
option_text_on_color: white
option_button_off_color: var(--paper-card-background-color)
option_text_off_color: white
option1_name: Low
option1_set_value: 51
option1_range_start: 1
option1_range_stop: 77
option2_name: Med
option2_set_value: 102
option2_range_start: 78
option2_range_stop: 170
option3_name: High
option3_set_value: 255
option3_range_start: 171
option3_range_stop: 255
option4_name: 'Off'
option4_set_value: 0
option4_range_start: 0
option4_range_stop: 0
custom_fields:
opt1:
card:
type: 'custom:button-card'
entity: '[[[ return variables.entity ]]]'
template: '[[[ return variables.option_template ]]]'
name: '[[[ return variables.option1_name ]]]'
variables:
set_value: '[[[ return variables.option1_set_value ]]]'
range_start: '[[[ return variables.option1_range_start ]]]'
range_stop: '[[[ return variables.option1_range_stop ]]]'
option_button_on_color: '[[[ return variables.option_button_on_color ]]]'
option_button_off_color: '[[[ return variables.option_button_off_color ]]]'
option_text_on_color: '[[[ return variables.option_text_on_color ]]]'
option_text_off_color: '[[[ return variables.option_text_off_color ]]]'
opt2:
card:
type: 'custom:button-card'
entity: '[[[ return variables.entity ]]]'
template: '[[[ return variables.option_template ]]]'
name: '[[[ return variables.option2_name ]]]'
variables:
set_value: '[[[ return variables.option2_set_value ]]]'
range_start: '[[[ return variables.option2_range_start ]]]'
range_stop: '[[[ return variables.option2_range_stop ]]]'
option_button_on_color: '[[[ return variables.option_button_on_color ]]]'
option_button_off_color: '[[[ return variables.option_button_off_color ]]]'
option_text_on_color: '[[[ return variables.option_text_on_color ]]]'
option_text_off_color: '[[[ return variables.option_text_off_color ]]]'
opt3:
card:
type: 'custom:button-card'
entity: '[[[ return variables.entity ]]]'
template: '[[[ return variables.option_template ]]]'
name: '[[[ return variables.option3_name ]]]'
variables:
set_value: '[[[ return variables.option3_set_value ]]]'
range_start: '[[[ return variables.option3_range_start ]]]'
range_stop: '[[[ return variables.option3_range_stop ]]]'
option_button_on_color: '[[[ return variables.option_button_on_color ]]]'
option_button_off_color: '[[[ return variables.option_button_off_color ]]]'
option_text_on_color: '[[[ return variables.option_text_on_color ]]]'
option_text_off_color: '[[[ return variables.option_text_off_color ]]]'
opt4:
card:
type: 'custom:button-card'
entity: '[[[ return variables.entity ]]]'
template: '[[[ return variables.option_template ]]]'
name: '[[[ return variables.option4_name ]]]'
variables:
set_value: '[[[ return variables.option4_set_value ]]]'
range_start: '[[[ return variables.option4_range_start ]]]'
range_stop: '[[[ return variables.option4_range_stop ]]]'
option_button_on_color: '[[[ return variables.option_button_on_color ]]]'
option_button_off_color: '[[[ return variables.option_button_off_color ]]]'
option_text_on_color: '[[[ return variables.option_text_on_color ]]]'
option_text_off_color: '[[[ return variables.option_text_off_color ]]]'
Using the multi-option button in the dashboard
For this, you can omit any variables that have defaults that match waht you want. In this example I have set a lot of them, but not all.
- entity: cover.master_bath_blinds
type: 'custom:button-card'
template: presets
name: Blinds
variables:
entity: cover.master_bath_blinds
option_template: blind-option
option1_name: ANG
option1_set_value: 12
option1_range_start: 2
option1_range_stop: 35
option2_name: OPN
option2_set_value: 50
option2_range_start: 36
option2_range_stop: 99
option3_name: CLN
option3_set_value: 100
option3_range_start: 100
option3_range_stop: 100
option4_name: CLS
option4_set_value: 0
option4_range_start: 9999
option4_range_stop: 9999
Alerting Sensors
This sensor button is all about CSS animation. I have 2 templates (alerter & alerter-dual) using extra_styles to define the animation keyframes and the styles section to define the animation using the keyframes. I have some variables and javascript templates in there to set the color and the timing to switch from one color to the other. Transitioning to a second color after a period of time requires a bit more animation handling, but I’m happy with the result.
I got the idea for this from Sam Wakefield in the Home Assistant group on Facebook: Good evening, Whilst playing around with the 'custom:button-card' I figured out a way of using additional animations instead of just 'blink' You'll find... | By SamFacebook.
button_card_templates:
alerter:
template: standard
show_last_changed: true
color_type: icon
extra_styles: |
[[[ return `
@keyframes pulse {
20% {
background-color: ${variables.color};
}
}
`]]]
variables:
color: var(--paper-item-icon-active-color)
state:
- value: 'on'
id: value_on
styles:
card:
- animation: pulse 1s infinite
alerter-dual:
template: standard
show_last_changed: true
color_type: icon
extra_styles: |
[[[ return `
@keyframes pulse1 {
20% {
background-color: ${variables.color_initial};
}
}
@keyframes pulse2 {
20% {
background-color: ${variables.color_extended};
}
}
@keyframes color {
0% {
color: unset;
opacity: 0.5
}
99% {
color: unset;
opacity: 0.5
}
100% {
color: ${variables.color_extended};
}
}
`]]]
variables:
color_initial: var(--paper-item-icon-active-color)
color_extended: 'rgba(240,52,52, 0.9)'
color_seconds: 60
state:
- value: 'on'
id: value_on
styles:
card:
- animation: >-
[[[ return `pulse1 1s ${variables.color_seconds}, pulse2 1s
${variables.color_seconds}s infinite` ]]]
icon:
- color: '[[[ return variables.color_extended ]]]'
- opacity: 1
- animation: '[[[ return `color ${variables.color_seconds}s 1` ]]]'
name:
- font-weight: bold
Here is how to use it in the dashboard:
- entity: binary_sensor.garage_door
template: alerter-dual
name: Garage
type: 'custom:button-card'
variables:
color_seconds: 120
Conclusion
I have dumped a lot of config here and fully expect there will be questions. I hope you find this useful