well yeah, if you add a random card it’ll still have a border unless you use card_mod
When using a Light entity with the template light
, the brightness can be adapted by swiping across the brightness in the circle. Where can I find the code for this action? I would like to create a template for a cover/roller shutter and use swipe actions to open, or close the roller shutter.
Hi,
Using the mini-graph layout that came by earlier.
But I want to use a different entity displaying the temperature in the circle.
I use this code:
- type: custom:button-card
entity: sensor.woonkamer_humidity
name: Vochtigheid
template:
- base
- circle
- graph
variables:
circle_input: >
[[[ return states['sensor.woonkamer_temperature'].state; ]]]
circle_input_unit: "°C"
graph_entity: sensor.woonkamer_humidity
But no circle with the temperature is showing… Who can guide me?
Hi guys,
Great way to learn all in’s and out’s of home assistant. Been tweaking for weeks now but still stuck with the media card.
I would like to have a static image and/or recently added plex thumbnail but can’t get it work yet.
Bigger question when spotify is player the card should change to the spotify album cover and when it stops it should return back to the image/plex thumbail.
What am I missing?
#################################################
# #
# MEDIA #
# #
#################################################
- type: grid
title: Media
view_layout:
grid-area: media
columns: 1
cards:
- type: custom:swipe-card
parameters:
roundLengths: true
effect: coverflow
speed: 650
spaceBetween: 20
threshold: 7
coverflowEffect:
rotate: 80
depth: 300
cards:
- type: horizontal-stack
cards:
- type: conditional
conditions:
- entity: select.conditional_media
state_not: Spotify_D
- entity: select.conditional_media
state_not: Spotify_L
- entity: select.conditional_media
state_not: PLEX
card:
type: custom:button-card
entity: ### Cannot get this part to work // or can I add a random picture?
name: I dont get this part
tap_action:
action: none
template:
- conditional_media
- icon_plex
- type: conditional
conditions:
- entity: select.conditional_media
state: Spotify_D
card:
type: custom:button-card
entity: media_player.spotify_dennis
template:
- conditional_media
- icon_spotify
- type: conditional
conditions:
- entity: select.conditional_media
state: Spotify_L
card:
type: custom:button-card
entity: media_player.spotify_lillian
template:
- conditional_media
- icon_spotify
- type: conditional
conditions:
- entity: select.conditional_media
state: PLEX
card:
type: custom:button-card
entity: media_player.plex_dennis_plex_for_lg_lg_oled48cx6lb
template:
- conditional_media
- icon_plex
# SWIPE CARD - 2nd PAGE
- type: grid
Does anyone have a guide on how to make animated SVG icons? I would like to make a couple including a garage door opening and closing, a ceiling fan/light combo lighting up/spinning, an XBOX logo turning green, a plug being powered on/off, and probably more down the road.
I would also like to move the people from cards to the sidebar. Maybe in color when they are detected home and in greyscale when they are not. I would still like to be able to tap on them to get more information. I have seen it done before, but I don’t think the configurations have been shared for it.
Can you link to the " mini-graph layout that came by earlier." I do not know what you are referring to.
the circle_input looks fine to me circle_input_unit should be ’ not " but I don’t think that is the issue
try adding a console.log and see what value is in states[‘sensor.woonkamer_temperature’].state.
circle_input: >
[[[
console.log(states['sensor.woonkamer_temperature'].state)
return states['sensor.woonkamer_temperature'].state;
]]]
conditional_media in my opinion was the hardest thing to wrap my head around.
Based on the info you provided, you have not updated the conditional_media template.
This template has 2 templates inside out it one for the possible options and one for the selected option that should be the name of the entity you would like to display
This is mine, it might help for you to see what I have changed.
---
unique_id: conditional_media
name: conditional_media
state: >
{% set recently_added = 'Last added' %}
{% set paused_timeout_minutes = 15 %}
{% set media_players = [
states.media_player.plex_prologue_mason,
states.media_player.spotify,
states.media_player.living_room_tv,
states.media_player.plex_plex_for_lg_lg_oled65cxpta ] %}
{% macro media(state) %}
{% set state = media_players | selectattr('state','eq',state) | list %}
{% set last_changed = recently_added if state | length == 0 else state | map(attribute='last_changed') | list | max %}
{{ state | selectattr('last_changed','eq', last_changed) | map(attribute='name') | list | join }}
{% endmacro %}
{% set on = media_players | selectattr('state','eq','on')| list %}
{% set timeout_playing = False if on | length == 0 else
(as_timestamp(now()) - as_timestamp(on | map(attribute='last_changed') | list | max)) < paused_timeout_minutes * 60 %}
{% set playing = media_players | selectattr('state','eq','playing') | list %}
{% set timeout_playing = False if playing | length == 0 else
(as_timestamp(now()) - as_timestamp(playing | map(attribute='last_changed') | list | max)) < paused_timeout_minutes * 60 %}
{% set paused = media_players | selectattr('state','eq','paused') | list %}
{% set timeout_paused = False if paused | length == 0 else
(as_timestamp(now()) - as_timestamp(paused | map(attribute='last_changed') | list | max)) < paused_timeout_minutes * 60 %}
{% if playing %}
{{ media('playing') if timeout_playing else media('paused') if timeout_paused else media('playing') }}
{% elif on %}
{{ media('on')}}
{% elif paused %}
{{ media('paused') if timeout_paused else recently_added }}
{% else %}
{{ recently_added }}
{% endif %}
options: >
{% set recently_added = ['Last added'] %}
{% set media_players = [
states.media_player.living_room_tv,
states.media_player.plex_prologue_mason,
states.media_player.spotify,
states.media_player.plex_plex_for_lg_lg_oled65cxpta ] %}
{{ recently_added + media_players | map(attribute='name') | list }}
select_option:
service: select.select_option
target:
entity_id: select.conditional_media
data:
option: >
{{ option }}
I started to learn it by myself yesterday this video helped me a lot to get started. Make Awesome SVG Animations with CSS // 7 Useful Techniques - YouTube
Graph part came from post #3181
Nevertheless, this is the slightly modified template code I’m using
graph:
custom_fields:
graph:
card:
type: "custom:mini-graph-card"
height: 200
entities:
- entity: "[[[ return variables.graph_entity ]]]"
hours_to_show: 48
points_per_hour: 1
line_width: 8
font_size: 75
decimals: 0
show:
name: false
icon: false
state: false
legend: false
labels: false
labels_secondary: false
points: false
color_thresholds:
- value: 40
color: "#276696"
- value: 50
color: "#228C22"
- value: 55
color: "#d35400"
- value: 60
color: "#c0392b"
styles:
card:
- border-width: 0
custom_fields:
card:
- border-width: 0
graph: [bottom: 0%, left: 0%, width: 125%, position: absolute, margin: 0% 0% 0% 0% ] #-13% -14%]
card_mod:
style: |
:host{
--accent-color: #039be5;
--ha-card-border-width: 0px;
}
But it should not interfere with the circle.
the console.log gives the actual value
(function anonymous(states,entity,user,hass,variables,html
) {
'use strict';
console.log(states['sensor.woonkamer_temperature'].state)
return states['sensor.woonkamer_temperature'].state;
})
18.76
Hi , thank you for the reply. unfortunately I can’t solve the puzzle.
Static image until a player is activated would be enough.
#################################################
# #
# BASE MEDIA #
# #
#################################################
base_media:
variables:
media_on: >
[[[ return !entity || ['playing', 'paused'].indexOf(entity.state) !== -1; ]]]
media_off: >
[[[ return !entity || ['off', 'idle', 'standby', 'unknown', 'unavailable'].indexOf(entity.state) !== -1; ]]]
tap_action:
action: >
[[[
return variables.media_on
? 'call-service'
: 'none';
]]]
service: media_player.media_play_pause
service_data:
entity_id: >
[[[
return variables.entity_id;
]]]
double_tap_action:
action: more-info
styles:
card:
- color: >
[[[
let entity_picture = entity === undefined || entity.attributes.entity_picture;
if (variables.state === 'off' || variables.state === 'idle' || variables.state === 'standby' ||
variables.state === 'unknown' || variables.state === 'unavailable' || entity === undefined) {
return 'rgba(255, 255, 255, 0.3)';
}
return (variables.state != 'off' && variables.state != 'idle' && variables.state != 'standby') && (entity_picture == null) ? 'rgba(0, 0, 0, 0.6)' : '#efefef';
]]]
- text-shadow: >
[[[
let entity_picture = entity === undefined ? null : entity.attributes.entity_picture;
if (variables.state === 'off' || variables.state === 'idle' || variables.state === 'standby' || variables.state === 'unknown' || variables.state === 'unavailable') {
return entity_picture == null ? 'none' : '1px 1px 5px rgba(18, 22, 23, 0)';
}
return entity_picture == null ? 'none' : '1px 1px 5px rgba(18, 22, 23, 0.9)';
]]]
#################################################
# #
# MEDIA #
# #
#################################################
media:
template:
- base
- base_media
styles:
custom_fields:
icon:
- width: 70%
- margin-left: 2%
- fill: '#9da0a2'
- display: >
[[[
return variables.media_off || variables.entity_picture === undefined
? 'initial'
: 'none';
]]]
card:
- background-color: none
- background-size: cover
- background-position: center
- background-image: >
[[[
let entity_picture = entity === undefined || entity.attributes.entity_picture;
if (variables.state === 'off' || variables.state === 'idle' || variables.state === 'standby' ||
variables.state === 'unknown' || variables.state === 'unavailable' || entity === undefined) {
return 'linear-gradient(0deg, rgba(115, 115, 115, 0.2) 0%, rgba(115, 115, 115, 0.2) 100%)';
}
return (variables.state != 'off' && variables.state != 'idle' && variables.state != 'standby') && (entity_picture == null) ?
'linear-gradient(0deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 100%)' :
'linear-gradient(0deg, rgba(0,0,0,.8) 0%, rgba(0,0,0,0) 100%), url(' + entity_picture + ')';
]]]
#################################################
# #
# CONDITIONAL MEDIA #
# #
#################################################
conditional_media:
aspect_ratio: 1000/996
template:
- base
- base_media
- icon_play_pause
variables:
i: >
[[[
if (entity) {
let data = entity.attributes.data;
return data === undefined || Math.floor(Math.random() * (data.length - 1)) + 1;
}
]]]
state_display: >
[[[
if (entity) {
let elt = this.shadowRoot,
await = setTimeout(marquee, 100),
data = entity.attributes.data,
artist = entity.attributes.media_artist,
title = entity.attributes.media_title;
if (data !== undefined) {
var number = data[variables.i].number === undefined && data[variables.i].aired !== undefined
? `(${data[variables.i].aired.split("-")[0]})`
: data[variables.i].number === undefined && data[variables.i].aired === undefined
? ' '
: data[variables.i].number,
output = `${data[variables.i].title} ${number}`;
} else {
var output = artist === undefined && title !== undefined
? title
: title === undefined && artist !== undefined
? artist
: title !== undefined && artist !== undefined
? `${artist} - ${title}`
: variables.translate_idle;
}
function marquee() {
let state = elt.getElementById("state"),
container = elt.getElementById("container");
if (state && container) {
state.innerHTML = output;
let ro = new ResizeObserver(entries => {
let spacer = " ".repeat(3),
s = entries[0],
c = entries[1],
r = s && s.contentRect &&
c && c.contentRect &&
s.contentRect.width !== 0 &&
c.contentRect.width !== 0;
if (r && s.contentRect.width < c.contentRect.width) {
state.classList.remove("marquee");
}
else if (r && s.contentRect.width >= c.contentRect.width) {
state.innerHTML = `${output} ${spacer} ${output} ${spacer} `;
state.classList.add("marquee");
}
});
ro.observe(state);
ro.observe(container);
}
}
return output;
}
return variables.translate_unknown;
]]]
tap_action:
action: call-service
service: media_player.media_play_pause
service_data:
entity_id: >
[[[ return variables.entity_id; ]]]
styles:
grid:
- gap: 0.65%
name:
- padding: 0.2vw
- margin: -0.2vw
state:
- padding-bottom: 5.25%
- max-width: unset
- overflow: visible
card:
- padding: 5.75% 5.25% 0 5.75%
- border-radius: calc(var(--button-card-border-radius) / 2)
- background: rgba(115, 115, 115, 0.2) center center/cover no-repeat
- background-image: &media_background_image >
[[[
if (entity) {
let data = entity.attributes.data;
return data && (data[variables.i].fanart || data[variables.i].poster)
? `url("${data[variables.i].fanart}"), url("${data[variables.i].poster}")`
: `url("${variables.entity_picture}")`;
}
]]]
- color: >
[[[
return entity === undefined
? '#97989c'
: '#efefef';
]]]
- text-shadow: >
[[[
return entity === undefined
? 'none'
: '1px 1px 5px rgba(18, 22, 23, 0.9)';
]]]
custom_fields:
icon:
- width: 30%
- fill: >
[[[
return entity && variables.media_on
? 'rgba(255, 255, 255, 0.8)'
: '#9da0a2';
]]]
blur_overlay:
- display: block
- position: absolute
- width: 103.1%
- height: 103.1%
- filter: var(--blur-intensity)
- clip-path: >
inset(74.5% 1.45% 1.45% 1.45% round 0 0 calc(var(--button-card-border-radius) / 2) calc(var(--button-card-border-radius) / 2))
- background: center center/cover no-repeat
- background-image: *media_background_image
- left: -1.5%
- bottom: -1.6%
custom_fields:
blur_overlay: >
[[[
setTimeout(() => {
let elt = this.shadowRoot,
card = elt.getElementById('card'),
container = elt.getElementById('container'),
blur_overlay = elt.getElementById('blur_overlay');
if (elt && card && container && blur_overlay) {
card.insertBefore(blur_overlay, container);
}
}, 0);
return ' ';
]]]
#################################################
# #
# MEDIA #
# #
#################################################
- type: grid
title: Media
view_layout:
grid-area: media
columns: 1
cards:
- type: custom:swipe-card
parameters:
roundLengths: true
effect: coverflow
speed: 650
spaceBetween: 20
threshold: 7
coverflowEffect:
rotate: 80
depth: 300
cards:
- type: horizontal-stack
cards:
- type: conditional
conditions:
- entity: select.conditional_media
state_not: Spotify_D
- entity: select.conditional_media
state_not: Spotify_L
- entity: select.conditional_media
state_not: PLEX
card:
type: custom:button-card
entity: media_player.spotify_dennis
name: Currently playing
tap_action:
action: none
template:
- conditional_media
- icon_spotify
- type: conditional
conditions:
- entity: select.conditional_media
state: Spotify_D
card:
type: custom:button-card
entity: media_player.spotify_dennis
template:
- conditional_media
- icon_spotify
- type: conditional
conditions:
- entity: select.conditional_media
state: Spotify_L
card:
type: custom:button-card
entity: media_player.spotify_lillian
template:
- conditional_media
- icon_spotify
- type: conditional
conditions:
- entity: select.conditional_media
state: PLEX
card:
type: custom:button-card
entity: media_player.plex_dennis_plex_for_lg_lg_oled48cx6lb
template:
- conditional_media
- icon_plex
# SWIPE CARD - 2nd PAGE
- type: grid
columns: 1
cards:
what values are in the select.conditional_media entity?
have you modified the code in the select template sensor for select.conditional_media?
thanks with that I was able to test and I found the issue, you have 2 options to solve this
ignore the broken line that should just be an issue on my end.
option 1: update the circle template
in button_card_templates/circle.yaml
find this section of code
/* * * * * * * * * * * * * * * * * *
* *
* OTHER *
* *
* * * * * * * * * * * * * * * * * */
else if (variables.state_on) {
return circle(state, input, unit);
}
and replace it with this
/* * * * * * * * * * * * * * * * * *
* *
* SENSOR *
* *
* * * * * * * * * * * * * * * * * */
else if (domain === 'sensor') {
return circle(state, input, unit);
}
/* * * * * * * * * * * * * * * * * *
* *
* OTHER *
* *
* * * * * * * * * * * * * * * * * */
else if (variables.state_on) {
return circle(state, input, unit);
}
changes to the base template like this can result in some issues and when you update you will need to remember to not remove that change
option 2: override the custom_fields
add the code to draw the circle to your custom template
template
graph:
template:
- base
- circle
custom_fields:
circle: >
[[[
let r = 22.1,
c = r * 2 * Math.PI,
tspan = '<tspan dx=".2" dy="-.4">',
domain = entity.entity_id.split('.')[0],
state = variables.state_on,
input = variables.circle_input || ' ',
unit = variables.circle_input_unit || ' ';
return `
<svg viewBox="0 0 50 50">
<style>
circle {
transform: rotate(-90deg);
transform-origin: 50% 50%;
stroke-dasharray: ${c};
stroke-dashoffset: ${typeof input === 'number' && c - input / 100 * c};
stroke-width: var(--c-stroke-width);
stroke: ${state ? 'var(--c-stroke-color-on)' : 'var(--c-stroke-color-off)'};
fill: ${state ? 'var(--c-fill-color-on)' : 'var(--c-fill-color-off)'};
}
text {
font-size: var(--c-font-size);
font-weight: var(--c-font-weight);
letter-spacing: var(--c-letter-spacing);
fill: var(--c-font-color);
}
tspan {
font-size: var(--c-unit-font-size);
}
#circle_value, tspan {
text-anchor: middle;
dominant-baseline: central;
}
</style>
<circle id="circle_stroke" cx="25" cy="25" r="${r}"/>
<text id="circle_value" x="50%" y="52%">${input}${tspan}${unit}</tspan></text>
</svg>
${domain === 'light' && `
<input id="circle_slider" type="range" min="0" max="100" value="${input}">
`}
`;
]]]
graph:
card:
type: "custom:mini-graph-card"
height: 200
entities:
- entity: "[[[ return variables.graph_entity ]]]"
hours_to_show: 48
points_per_hour: 1
line_width: 8
font_size: 75
decimals: 0
show:
name: false
icon: false
state: false
legend: false
labels: false
labels_secondary: false
points: false
color_thresholds:
- value: 40
color: "#276696"
- value: 50
color: "#228C22"
- value: 55
color: "#d35400"
- value: 60
color: "#c0392b"
styles:
card:
- border-width: 0
custom_fields:
card:
- border-width: 0
graph: [bottom: 0%, left: 0%, width: 125%, position: absolute, margin: 0% 0% 0% 0% ] #-13% -14%]
card_mod:
style: |
:host{
--accent-color: #039be5;
--ha-card-border-width: 0px;
}
use like this now
- type: custom:button-card
entity: sensor.woonkamer_humidity
name: Vochtigheid
template:
- graph
variables:
circle_input: >
[[[ return states['sensor.woonkamer_temperature'].state; ]]]
circle_input_unit: "°C"
graph_entity: sensor.woonkamer_humidity
Hi Mason,
Thank you for the homerun. Found the problem in a file called: tv_media.yaml
Used your previous posted conditional_media code and states are now correct and updating.
Will work on the Plex thumbnails or static image now when no players are active.
Many thanks.
sweet that was the issue that then, it’s hard to know what words to use sometimes but im happy we got there in the end.
the code I provided is custom to my situation, I would recommend you modify the code from @Mattias_Persson to fit your situation, that way you can modify it when needed.
you can use the template section of the dev tools to debug the template until you get it just right
Thank you. I didn’t know that it was basically just using an SVG and CSS animations to do this. I have been out of touch with web design for quite a while.
I have been working on the garage door icon and can not seem to get it implemented properly. I have created a gist of my current coding and what is happening with what I think is relevant information at the top of the gist. If anyone would be willing to help diagnose what is going on, please leave comments in the gist so that it is not cluttering this thread. Any help is appreciated.
Garage Door SVG Animation (Not working) · GitHub
Side thought: Has anyone made a database of custom icons that are being used?
#________________xbox_animado
icon_xbox_animado:
template:
- base
- loader
styles:
custom_fields:
icon:
#- clip-path: circle()
- width: 79%
- pointer-events: none
- display: grid
- margin-left: -10%
- fill: >
[[[
return (variables.state === 'on' || variables.state === 'playing') ? '#107b10' : '#9da0a2';
]]]
custom_fields:
icon: >
[[[
let state;
if (variables.state === 'on' && variables.timeout < 2000) {
state = 'on';
}
if (variables.state === 'off' && variables.timeout < 2000) {
state = 'off';
}
if (variables.state === 'on' && variables.timeout > 2000) {
state = 'on_timeout';
}
return `
<svg viewBox="0 0 1331.67 1333.33">
<style>
@keyframes on {
0% {
transform: rotate(0deg) translate(0%, 0%);
}
100% {
transform: rotate(-360deg) translate(-1.5%, 0%);
}
}
@keyframes off {
0% {
transform: rotateY(-360deg) translate(-1.5%, 0%);
}
45% {
transform: rotateY(-40deg);
}
55% {
transform: rotateY(0deg);
}
65% {
transform: rotateY(-15deg);
}
75% {
transform: rotateY(0deg);
}
85% {
transform: rotateY(-5deg);
}
95% {
transform: rotateY(0deg);
}
}
.on {
animation: on 1.1s;
animation-fill-mode: forwards;
transform-origin: 50% 50%;
transition-timing-function: cubic-bezier(0.85, 0, 0.15, 1);
}
.off {
animation: off 1.1s linear;
animation-delay: 0.05s;
animation-fill-mode: both;
transform-origin: 45% 41%;
}
</style>
<path class="${state}" d="M665.83 534.66s1.66 0 0 0c200.91 152.76 541.3 528.02 438.35 634.29-117.89 102.95-270.65 164.39-438.35 164.39-167.7 0-322.13-61.44-438.35-164.39-104.61-106.27 237.44-481.53 436.69-632.63 0-1.66 1.66-1.66 1.66-1.66zm347.03-436.7C911.57 36.52 800.32-.01 665.83-.01c-134.5 0-245.74 36.53-347.03 97.97-1.66 0-1.66 1.66-1.66 3.32s1.66 1.66 3.32 1.66c129.51-28.23 325.44 83.02 343.71 94.65h3.32c18.26-11.62 214.2-122.87 343.71-94.65 1.66 0 3.32 0 3.32-1.66s0-3.32-1.66-3.32zm-813.61 92.98c-1.66 0-1.66 1.66-3.32 1.66C74.72 313.81 0 481.52 0 665.82c0 151.1 51.48 292.24 136.16 403.49 0 1.66 1.66 1.66 3.32 1.66s1.66-1.66 0-3.32C88 909.91 348.69 529.67 483.19 370.26l1.66-1.66c0-1.66 0-1.66-1.66-1.66-204.23-202.57-272.31-180.99-283.93-176.01zm649.23 174.35l-1.66 1.66s0 1.66 1.66 1.66C982.98 528.01 1242 908.26 1192.19 1066v3.32c1.66 0 3.32 0 3.32-1.66 84.68-111.25 136.16-252.39 136.16-403.49 0-184.31-74.72-352.01-197.59-473.22-1.66-1.66-1.66-1.66-3.32-1.66-9.96-3.32-78.04-24.91-282.27 176.01z"/>
</svg>
`;
]]]
It did not, will do this tomorrow.
Many thanks for getting back to me! Appreciated!!
this is the result… how to fix the graph?
As I said in my post that issue was there before I made any modification to the code I was provided. @DevilsAdjutant might know more.