Another random one I’m struggling with… trying to make a single button, that shows two icons… e.g. two different states at the same time… and has a single tap_action. Possible?
Check this other post I did that might be similar to what you want to do:
Actually came up with a kinda hack… Actually made my own icons…
Now I need to figure out how to change the color of different parts of the icon…
Almost fully there now…
Basically the main icon is an SVG I am drawing with JS Templating to color different paths. The button opens a popup where i have controls for the individual doors and “dual” door functions. The issue is that when I click the main icon the “multiple” actions are being triggered… e.g. the buttons in the popup are somehow being triggered even though I clicked on the main icon…
Thoughts?
- type: custom:button-card
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.popup
data:
title: Single Garage Door
content:
type: vertical-stack
cards:
- type: custom:frigate-card
cameras:
- camera_entity: camera.garage
- type: horizontal-stack
cards:
- entity: cover.single_garage_door
name: Open
icon: "emf:garage-single-open"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.open_cover
service_data:
entity_id: "cover.single_garage_door"
- entity: cover.single_garage_door
name: Crack
icon: "emf:garage-single-cracked"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.single_crack"
- entity: cover.single_garage_door
name: Close
icon: "emf:garage-single-closed"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.close_cover
service_data:
entity_id: "cover.single_garage_door"
- type: horizontal-stack
cards:
- entity: cover.double_garage_door
name: Open
icon: "emf:garage-double-open"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.open_cover
service_data:
entity_id: "cover.double_garage_door"
- entity: cover.double_garage_door
name: Crack
icon: "emf:garage-double-cracked"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.double_crack"
- entity: cover.double_garage_door
name: Close
icon: "emf:garage-double-closed"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.close_cover
service_data:
entity_id: "cover.double_garage_door"
- type: horizontal-stack
cards:
- entity: cover.double_garage_door
name: Open
icon: "emf:garage-dual-open-open"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: nothing
multi_calls: |
[[[
hass.callService(
"cover",
"open_cover",
{ entity_id: "cover.double_garage_door"}
);
hass.callService(
"cover",
"open_cover",
{ entity_id: "cover.single_garage_door"}
);
]]]
- entity: cover.double_garage_door
name: Crack & Closed
icon: "emf:garage-dual-closed-cracked"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: nothing
multi_calls: |
[[[
hass.callService(
"cover",
"close_cover",
{ entity_id: "cover.double_garage_door"}
);
hass.callService(
"button",
"press",
{ entity_id: "button.single_crack"}
);
]]]
- entity: cover.double_garage_door
name: Close
icon: "emf:garage-dual-closed-closed"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: nothing
multi_calls: |
[[[
hass.callService(
"cover",
"close_cover",
{ entity_id: "cover.double_garage_door"}
);
hass.callService(
"cover",
"close_cover",
{ entity_id: "cover.single_garage_door"}
);
]]]
entity:
name: Garage
show_name: true
show_icon: false
size: 80%
styles:
card:
- padding: 0.2em
- "--mdc-ripple-color": yellow
- "--mdc-ripple-press-opacity": 0.5
- height: 100%
icon:
- opacity: 0.5
# - max-height: 4.5rem #tweaking the auto-size to prevent vertical inflation of the UI on my wall tablets
name:
- font-size: 0.65em
- white-space: normal
state:
- font-size: 0.65em
- white-space: normal
label:
- font-size: 0.4em
- white-space: normal
grid:
- grid-template-areas: '"info" "n"'
- grid-template-columns: 1fr
- grid-template-rows: 1fr min-content
custom_fields:
info:
- opacity: 0.5
custom_fields:
info: >
[[[
var colorSingle = '#0000FF';
var colorDouble = '#0000FF';
if ((states['cover.single_garage_door'].state == 'open') && !(states['sensor.single_garage_door_position'].state == 'Cracked')) {
colorSingle = '#00FF00';
} else if ((states['cover.single_garage_door'].state == 'open') && (states['sensor.single_garage_door_position'].state == 'Cracked')) {
colorSingle = '#FF0000';
}
if ((states['cover.double_garage_door'].state == 'open') && !(states['sensor.double_garage_door_position'].state == 'Cracked')) {
colorDouble = '#00FF00';
} else if ((states['cover.double_garage_door'].state == 'open') && (states['sensor.double_garage_door_position'].state == 'Cracked')) {
colorDouble = '#FF0000';
}
var toReturn = '<svg viewBox="0 0 24 24" version="1.1" id="svg67">';
toReturn += `<path
style="stroke-width:0.715542;fill:var(--primary-text-color)"
d="M 7.1992188,6.5 0.80078125,9.6992188 V 18.5 H 2.0800781 V 11.300781 H 12.320312 V 18.5 h 0.0039 v 0.02539 h 1.599609 v -7.199219 h 8 v 7.199219 h 1.59961 V 9.7265625 l -5.59961,-3.2011719 -4.978516,2.8457032 z"
id="path65"
inkscape:label="garage" />
<path
style="stroke-width:0.715542;fill:${colorSingle}"
d="m 14.724609,12.125 v 1.601562 H 21.125 V 12.125 Z"
id="path238"
inkscape:label="single-open" />
<path
style="stroke-width:0.715542;fill:${colorDouble}"
d="m 2.7207031,12.099609 v 1.59961 h 8.9589849 v -1.59961 z"
id="path236"
inkscape:label="double-open" />`
if (states['cover.single_garage_door'].state == 'closed') {
toReturn += `<path
style="stroke-width:0.715542;fill:${colorSingle}"
d="m 14.724609,16.925781 v 1.59961 H 21.125 v -1.59961 z"
id="path246"
inkscape:label="single-closed" />
<path
style="stroke-width:0.715542;fill:${colorSingle}"
d="M 14.724609,14.525391 V 16.125 H 21.125 v -1.599609 z"
id="path242"
inkscape:label="single-cracked" />`;
} else if ((states['cover.single_garage_door'].state == 'open') && (states['sensor.single_garage_door_position'].state == 'Cracked')) {
toReturn += `<path
style="stroke-width:0.715542;fill:${colorSingle}"
d="M 14.724609,14.525391 V 16.125 H 21.125 v -1.599609 z"
id="path242"
inkscape:label="single-cracked" />`;
};
if (states['cover.double_garage_door'].state == 'closed') {
toReturn += `<path
style="stroke-width:0.715542;fill:${colorDouble}"
d="M 2.7207031,16.900391 V 18.5 h 8.9589849 v -1.599609 z"
id="path244"
inkscape:label="double-closed" />
<path
style="stroke-width:0.715542;fill:${colorDouble}"
d="m 2.7207031,14.5 v 1.599609 H 11.679688 V 14.5 Z"
id="path240"
inkscape:label="double-cracked" />`;
} else if ((states['cover.double_garage_door'].state == 'open') && (states['sensor.double_garage_door_position'].state == 'Cracked')) {
toReturn += `<path
style="stroke-width:0.715542;fill:${colorDouble}"
d="m 2.7207031,14.5 v 1.599609 H 11.679688 V 14.5 Z"
id="path240"
inkscape:label="double-cracked" />`;
};
return toReturn + '</svg>';
]]]
Are you looking for already working custom cards?
If yes, please vote for this feature request.
For anyone interested… built a two car garage into a single button. Made custom SVGs, with states for open/closed/cracked … as well as animated states for opening/closing … The action is a popup that has buttons to control all the states as well as a frigate card from my garage camera. It’s still a work in progress. But pretty fun and cool I think.
type: custom:button-card
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.popup
data:
title: Single Garage Door
content:
type: vertical-stack
cards:
- type: custom:frigate-card
cameras:
- camera_entity: camera.garage
- type: horizontal-stack
cards:
- entity: cover.single_garage_door
name: Open
icon: "emf:garage-single-open"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.open_cover
service_data:
entity_id: "cover.single_garage_door"
- entity: cover.single_garage_door
name: Crack
icon: "emf:garage-single-cracked"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.single_crack"
- entity: cover.single_garage_door
name: Close
icon: "emf:garage-single-closed"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.close_cover
service_data:
entity_id: "cover.single_garage_door"
- type: horizontal-stack
cards:
- entity: cover.double_garage_door
name: Open
icon: "emf:garage-double-open"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.open_cover
service_data:
entity_id: "cover.double_garage_door"
- entity: cover.double_garage_door
name: Crack
icon: "emf:garage-double-cracked"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.double_crack"
- entity: cover.double_garage_door
name: Close
icon: "emf:garage-double-closed"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: cover.close_cover
service_data:
entity_id: "cover.double_garage_door"
- type: horizontal-stack
cards:
- entity: cover.double_garage_door
name: Open
icon: "emf:garage-dual-open-open"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.open_both"
- entity: cover.double_garage_door
name: Crack & Closed
icon: "emf:garage-dual-closed-cracked"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.single_crack_and_close_double"
- entity: cover.double_garage_door
name: Close
icon: "emf:garage-dual-closed-closed"
type: "custom:button-card"
aspect_ratio: 2/1
tap_action:
action: call-service
service: button.press
service_data:
entity_id: "button.close_both"
entity:
name: Garage
show_name: true
show_icon: false
size: 80%
styles:
card:
- padding: 0.2em
- "--mdc-ripple-color": yellow
- "--mdc-ripple-press-opacity": 0.5
- height: 100%
icon:
- opacity: 0.5
# - max-height: 4.5rem #tweaking the auto-size to prevent vertical inflation of the UI on my wall tablets
name:
- font-size: 0.65em
- white-space: normal
state:
- font-size: 0.65em
- white-space: normal
label:
- font-size: 0.4em
- white-space: normal
grid:
- grid-template-areas: '"info" "n"'
- grid-template-columns: 1fr
- grid-template-rows: 1fr min-content
custom_fields:
info:
- opacity: 0.5
custom_fields:
info: >
[[[
var cssStyleSheet = '';
var stateColors = {
'closed': '#0000FF',
'open': '#00FF00',
'cracked': '#FF0000',
'closing': '#FFFF00',
'opening': '#FF00FF'
};
var stateSingle = "unknown";
if ((states['cover.single_garage_door'].state == 'open') && !(states['sensor.single_garage_door_position'].state == 'Cracked')) {
stateSingle = "open";
} else if ((states['cover.single_garage_door'].state == 'open') && (states['sensor.single_garage_door_position'].state == 'Cracked')) {
stateSingle = "cracked";
} else if (states['cover.single_garage_door'].state == 'opening') {
stateSingle = "opening";
} else if (states['cover.single_garage_door'].state == 'closing') {
stateSingle = "closing";
} else if (states['cover.single_garage_door'].state == 'closed') {
stateSingle = "closed";
}
var stateDouble = "unknown";
if ((states['cover.double_garage_door'].state == 'open') && !(states['sensor.double_garage_door_position'].state == 'Cracked')) {
stateDouble = "open";
} else if ((states['cover.double_garage_door'].state == 'open') && (states['sensor.double_garage_door_position'].state == 'Cracked')) {
stateDouble = "cracked";
} else if (states['cover.double_garage_door'].state == 'opening') {
stateDouble = "opening";
} else if (states['cover.double_garage_door'].state == 'closing') {
stateDouble = "closing";
} else if (states['cover.double_garage_door'].state == 'closed') {
stateDouble = "closed";
}
var svgOutline = `<path
style="stroke-width:0.715542;fill:var(--primary-text-color)"
d="M 7.1992188,6.5 0.80078125,9.6992188 V 18.5 H 2.0800781 V 11.300781 H 12.320312 V 18.5 h 0.0039 v 0.02539 h 1.599609 v -7.199219 h 8 v 7.199219 h 1.59961 V 9.7265625 l -5.59961,-3.2011719 -4.978516,2.8457032 z"
id="path65"
inkscape:label="garage" />`;
var svgSingleTop = `<path
style="stroke-width:0.715542;fill:${stateColors[stateSingle]}"
d="m 14.724609,12.125 v 1.601562 H 21.125 V 12.125 Z"
id="svgSingleTop"
inkscape:label="single-open" />`;
var svgSingleMiddle = `<path
style="stroke-width:0.715542;fill:${stateColors[stateSingle]}"
d="M 14.724609,14.525391 V 16.125 H 21.125 v -1.599609 z"
id="svgSingleMiddle"
inkscape:label="single-cracked" />`;
var svgSingleBottom = `<path
style="stroke-width:0.715542;fill:${stateColors[stateSingle]}"
d="m 14.724609,16.925781 v 1.59961 H 21.125 v -1.59961 z"
id="svgSingleBottom"
inkscape:label="single-closed" />`;
var svgDoubleTop = `<path
style="stroke-width:0.715542;fill:${stateColors[stateDouble]}"
d="m 2.7207031,12.099609 v 1.59961 h 8.9589849 v -1.59961 z"
id="svgDoubleTop"
inkscape:label="double-open" />`;
var svgDoubleMiddle = `<path
style="stroke-width:0.715542;fill:${stateColors[stateDouble]}"
d="m 2.7207031,14.5 v 1.599609 H 11.679688 V 14.5 Z"
id="svgDoubleMiddle"
inkscape:label="double-cracked" />`;
var svgDoubleBottom = `<path
style="stroke-width:0.715542;fill:${stateColors[stateDouble]}"
d="M 2.7207031,16.900391 V 18.5 h 8.9589849 v -1.599609 z"
id="svgDoubleBottom"
inkscape:label="double-closed" />`;
var toReturn = '<svg viewBox="0 0 24 24" version="1.1" id="svg67">';
toReturn += svgOutline;
if (stateSingle === 'open') {
toReturn += svgSingleTop;
} else if (stateSingle === 'cracked') {
toReturn += svgSingleTop + svgSingleMiddle;
} else if (stateSingle === 'closed') {
toReturn += svgSingleTop + svgSingleMiddle + svgSingleBottom;
} else if (stateSingle === 'closing') {
toReturn += svgSingleTop + svgSingleMiddle + svgSingleBottom;
cssStyleSheet += `<style>
@keyframes hideshow {
0% { opacity: 1; }
25% { opacity: 1; }
50% { opacity: 0; }
75% { opacity: 0; }
100% { opacity: 0; }
}
#svgSingleTop {
animation: hideshow 4s ease infinite;
}
#svgSingleMiddle {
opacity: 0;
animation: hideshow 4s 0.5s ease infinite;
}
#svgSingleBottom {
opacity: 0;
animation: hideshow 4s 1.0s ease infinite;
}
</style>`;
} else if (stateSingle === 'opening') {
toReturn += svgSingleTop + svgSingleMiddle + svgSingleBottom;
cssStyleSheet += `<style>
@keyframes hideshow {
0% { opacity: 1; }
25% { opacity: 1; }
50% { opacity: 0; }
75% { opacity: 0; }
100% { opacity: 0; }
}
#svgSingleBottom {
animation: hideshow 4s ease infinite;
}
#svgSingleMiddle {
opacity: 0;
animation: hideshow 4s 0.5s ease infinite;
}
#svgSingleTop {
opacity: 0;
animation: hideshow 4s 1.0s ease infinite;
}
</style>`;
} else {
}
if (stateDouble === 'open') {
toReturn += svgDoubleTop;
} else if (stateDouble === 'cracked') {
toReturn += svgDoubleTop + svgDoubleMiddle;
} else if (stateDouble === 'closed') {
toReturn += svgDoubleTop + svgDoubleMiddle + svgDoubleBottom;
} else if (stateDouble === 'closing') {
toReturn += svgDoubleTop + svgDoubleMiddle + svgDoubleBottom;
cssStyleSheet += `<style>
@keyframes hideshow {
0% { opacity: 1; }
25% { opacity: 1; }
50% { opacity: 0; }
75% { opacity: 0; }
100% { opacity: 0; }
}
#svgDoubleTop {
animation: hideshow 4s ease infinite;
}
#svgDoubleMiddle {
opacity: 0;
animation: hideshow 4s 0.5s ease infinite;
}
#svgDoubleBottom {
opacity: 0;
animation: hideshow 4s 1.0s ease infinite;
}
</style>`;
} else if (stateDouble === 'opening') {
toReturn += svgDoubleTop + svgDoubleMiddle + svgDoubleBottom;
cssStyleSheet += `<style>
@keyframes hideshow {
0% { opacity: 1; }
25% { opacity: 1; }
50% { opacity: 0; }
75% { opacity: 0; }
100% { opacity: 0; }
}
#svgDoubleBottom {
animation: hideshow 4s ease infinite;
}
#svgDoubleMiddle {
opacity: 0;
animation: hideshow 4s 0.5s ease infinite;
}
#svgDoubleTop {
opacity: 0;
animation: hideshow 4s 1.0s ease infinite;
}
</style>`;
} else {
}
return toReturn + cssStyleSheet + '</svg>';
]]]
Does anyone know if there is anyway to make an icon flash two different colours?
I can use the below code to make it flash on a certain state, however this is only flashing the icon on and off. But if I wanted to flash between two colours (for example flash between red and green), is there anyway to do that?
Hope that makes sense!
Thanks
state:
- value: 1
styles:
icon:
- animation: blink 5s ease infinite
Look above, there was a reference to a dual toggle a few posts up. You can do it using keyframes
Look at the post I made above. I use them to animate an SVG.
Can some one give me a hand with the active/background’s variable?
I want to differentiate the active option from the others:
In this case the activated options are 30º, as the angle, and Normal, as the Mode; but I can’t seem to find the right way to differentiate those from the others…
type: vertical-stack
cards:
- type: entity
entity: fan.fan
state_color: true
attribute: percentage
unit: '%'
- type: horizontal-stack
cards:
- show_name: true
show_icon: true
type: custom:button-card
name: 30º
tap_action:
action: call-service
service: script.fan_30
show_state: false
color_type: card
color: rgb(66, 134, 244)
icon: ''
- show_name: true
show_icon: false
color_type: card
color: rgb(66, 134, 244)
type: custom:button-card
name: 60º
tap_action:
action: call-service
service: script.fan_60
show_state: false
icon: ''
- show_name: true
show_icon: false
type: custom:button-card
name: 90º
tap_action:
action: call-service
service: script.fan_90
show_state: false
color_type: card
color: rgb(66, 134, 244)
icon: ''
- show_name: true
show_icon: false
type: custom:button-card
name: 120º
tap_action:
action: call-service
service: script.fan_120
show_state: false
color_type: card
color: rgb(66, 134, 244)
icon: ''
- type: horizontal-stack
cards:
- show_name: true
show_icon: true
type: custom:button-card
name: Normal
tap_action:
action: call-service
service: script.fan_normal
show_state: false
icon: ''
- show_name: true
show_icon: true
type: custom:button-card
name: Breeze
tap_action:
action: call-service
service: script.fan_breeze
show_state: false
icon: ''
- type: custom:button-card
show_name: true
show_icon: true
show_state: false
entity: number.lr_hvac_tadiran_set_setpoint
name: |
[[[return ‘ok’;]]]
size: 100%
color: |
[[[ return ‘red’;]]]
not showing the red color. what is wrong?
thanks
Is there a way to display the name of the linked room on the place name? In Jinja we can use area_name but I don’t know what the js template of this is.
I have a question concerning the JavaScript templates in this card.
Configuration excerpt:
tmpl_btn_roll:
variables:
preset: open # closed, shade, blinds
set_width: 7 #6.8 #"rem" will be added later
enty: null #placeholder
entity: '[[[ return variables.enty ]]]'
size: 24px
...and so on
Further down the road I want to access an attribute of the entity.
This attribute’s name is provided via the variable preset
.
If I try
styles:
grid: # div#container.vertical.no-state
- grid-template: '"i l" auto'
card: # ha-card#card.button-card-main.type-custom-button-card
- border-style: >
[[[ var v_pos = states[variables.enty].attributes.current_position;
if (v_pos == states[variables.enty].attributes.${variables.preset}) return 'dashed';
return 'solid'; ]]]
it produces an error: ButtonCardJSTemplateError: SyntaxError: Unexpected token '{' in 'var v_pos = states[variables.enty].attributes.current_position; if (v_pos == states[variables....'
How do I access an attribute which name is changing?
Dug two hours in SO since I didn’t find the right question (I’m absolutely not into JS).
Once I asked the right question, the following answer came up:
if (v_pos == eval("states[variables.enty].attributes." + variables.preset)) return 'dashed';
works, but it uses evil eval()…
@Ghafla, try this to avoid eval:
if (v_pos == states[variables.enty].attributes[variables.preset]) return 'dashed';
What’s cool about javascript is you can access properties of an object as if the properties are an array of names. That allows us to access a property from the array by name using a variable.
@Ostracizado, take a look at this part of the documentation for the state
settings. You can have different visual settings based on state values. Each of your buttons can watch for a specific state and show itself differently.
@janiv, please edit your post to put your yaml inside triple-backticks ``` at the start and end so we can see the indentation properly. Also, I think that color
setting is the on
color and not used when the state is off
. There are other places to set color that may work better.