styles:
icon:
- color: >
[[[
let rgb = states['light.wz_hue_go'].state.attributes.rgb_color || [0, 0, 0]; // [0, 0, 0] will be the default color if the attribute doesn't exist.
return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
]]]
That’s not possible, you’ll have to do it based on the state of an entity.
for that:
Good luck
- Instantiate the card:
- type: custom:button-card
template: square_media_player
entity: media_player.sonos_office_stereo
variables:
name: Sonos Office
default_background: /local/sonos_one_big.png
sonos_master: media_player.sonos_office_stereo
- Button-card templates:
square_defaults:
aspect_ratio: 1/1
styles:
card:
- padding: 9.3px
- border-radius: var(--sq-border-radius)
- font-size: 14px
- font-weight: 400
- background-clip: padding-box
- --sq-background-color: var(--sq-background-off)
- --sq-text-color: var(--sq-text-off)
- background: var(--sq-background-color)
- --sq-info-color: var(--sq-text-color)
- --sq-icon-color: var(--sq-icon-off)
- --mdc-ripple-color: var(--sq-background-on)
lock:
- justify-content: flex-end
- align-items: flex-end
icon:
- color: var(--sq-icon-color)
square_media:
template:
- square_defaults
variables:
background: |
[[[
if (entity && entity.attributes && entity.attributes.entity_picture != null) {
return entity.attributes.entity_picture;
}
]]]
default_background: /local/hifi.jpg
name: "No Name Defined"
label: "No Label Defined"
show_icon: false
show_name: true
show_state: false
extra_styles: |
span.fade {
background-image: linear-gradient(to right, var(--sq-text-color) 0%, var(--sq-text-color) 60%, rgba(0,0,0,0));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
position:relative;
display:inline-block;
width: 100%;
}
name: |
[[[
let sonos_group = "";
if (entity && entity.attributes && entity.attributes.sonos_group && entity.attributes.sonos_group.length != 1) {
sonos_group = ` (+${entity.attributes.sonos_group.length - 1})`
}
if (entity && entity.entity_id === 'media_player.sonos_universal' && states['media_player.sonos_connect'].attributes.sonos_group.length != 1) {
sonos_group = ` (+${states['media_player.sonos_connect'].attributes.sonos_group.length - 1})`
}
return `
<div style="width: auto; padding: 9.3px; background: var(--name-background);">
<span class="fade" style="text-transform: lowercase;">${variables.name}${sonos_group}</span>
<br />
<span class="fade" style="font-weight: initial;">${variables.label}</span>
</div>
`;
]]]
custom_fields:
fade: ''
media_image: ''
styles:
custom_fields:
fade:
- width: 100%
- height: 100%
- position: absolute
- z-index: 2
- border-radius: var(--sq-border-radius)
media_image:
- border-radius: var(--sq-border-radius)
- background-clip: padding-box
- top: 0
- left: 0
- position: absolute
- height: 100%
- width: 100%
- transition: background-image 1s ease-in-out, filter 1s ease-in-out
- background-size: cover
- background-position: center center
- background-repeat: no-repeat
- background-image: '[[[ return `url(${variables.background ? variables.background : variables.default_background})` ]]]'
- -webkit-transform-origin-z: 0px
- -webkit-backface-visibility: hidden
icon_image:
- position: absolute
- top: 9.3px
- left: 9.3px
- z-index: 2
- width: 14%
card:
- padding: 0
- background-clip: padding-box
- transition: 1s ease
- --name-background: linear-gradient(to top, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.6) 80%, transparent)
- -webkit-backface-visibility: hidden
name:
- text-align: start
- align-self: end
- font-weight: bold
- z-index: 3
- transition: all 1s ease
- width: 100%
square_media_player:
template: square_media
triggers_update:
- sensor.date_time
variables:
default_background: /local/hifi.jpg
is_idle: |
[[[
if (!entity.attributes.media_position_updated_at || entity.state === "playing") return false;
const diff = (Date.now() - new Date(entity.attributes.media_position_updated_at).getTime()) / 1000;
return diff > 5 * 60;
]]]
is_playing_paused: |
[[[
if (entity.state === 'playing' || entity.state === 'paused')
return true;
return false;
]]]
display_big_icon: |
[[[
if (entity.attributes.is_volume_muted)
return true;
else if (entity.attributes.media_position_updated_at) {
if (entity.state === 'playing') return false;
const diff = (Date.now() - new Date(entity.attributes.media_position_updated_at).getTime()) / 1000;
const idle = diff > 5 * 60;
if (idle)
return false;
return entity.state === "paused";
} else {
return entity.state === "paused";
}
]]]
label: |
[[[
let state = entity.state.charAt(0).toUpperCase() + entity.state.substring(1);;
let idle = false;
if (entity.attributes.media_position_updated_at && entity.state !== "playing") {
const diff = (Date.now() - new Date(entity.attributes.media_position_updated_at).getTime()) / 1000;
idle = diff > 5 * 60;
}
if (!idle && (entity.state === 'playing' || entity.state === 'paused')) {
const attr = entity.attributes;
if (attr.media_artist && attr.media_title && attr.media_album_name)
state = `${attr.media_artist} - ${attr.media_title} [${attr.media_album_name}]`
else if (attr.media_artist && attr.media_title)
state = `${attr.media_artist} - ${attr.media_title}`
else if (attr.media_title) {
// plex
if (attr.media_series_title && attr.media_season && attr.media_episode)
state = `${attr.media_series_title} - S${attr.media_season}E${attr.media_episode} - ${attr.media_title}`
else
state = `${attr.media_title}`
}
}
return state;
]]]
big_icon: |
[[[
if (entity.attributes.is_volume_muted) return "mdi:volume-off";
return "mdi:pause";
]]]
custom_fields:
big_icon: '[[[ return `<ha-icon icon="${variables.big_icon}" style="height: 100%; width: 100%"/>` ]]]'
shade: ''
styles:
custom_fields:
shade:
- border-radius: var(--sq-border-radius)
- position: absolute
- top: 0
- left: 0
- width: 100%
- height: 100%
- z-index: 1
- transition: all 1s ease
- background: |
[[[
if (variables.display_big_icon)
return "rgba(0, 0, 0, 0.6)";
return "none";
]]]
big_icon:
- position: absolute
- transition: opacity 1s ease-in-out
- top: 50%
- left: 50%
- transform: translate(-50%, -50%)
- z-index: 50
- opacity: '[[[ return variables.display_big_icon ? 1 : 0 ]]]'
- color: var(--sq-background-on)
media_image:
- background-image: |
[[[
function image(variables) {
const image = !variables.is_idle && variables.is_playing_paused ? variables.background : variables.default_background;
return image;
}
if (!variables.is_idle && variables.is_playing_paused) {
async function myFetch(elt, url, variables, hass) {
try {
await fetch(url);
new Image().src = hass.hassUrl(url);
elt.shadowRoot.getElementById('media_image').style.backgroundImage = `url(${url})`
} catch(e) {
console.log("error");
window.setTimeout(myFetch(elt, url, variables, hass), 500);
}
}
// Sometimes the image wouldn't load without all this shit below...
let url = image(variables);
if (!url) return;
myFetch(this, url, variables, hass);
return;
} else {
return `url(${variables.default_background})`
}
]]]
- filter: '[[[ if (variables.display_big_icon) return "blur(4px)"; else return null ]]]'
tap_action:
action: call-service
service: media_player.media_play_pause
service_data:
entity_id: entity
double_tap_action:
action: call-service
service: media_player.media_next_track
service_data:
entity_id: entity
- CSS Stuff included in lovelace:
html {
--sq-text-on: rgba(0, 0, 0, 0.6);
--sq-text-off: var(--paper-item-icon-color);
--sq-background-on: rgba(255, 255, 255, 0.8);
--sq-background-off: var(--paper-card-background-color);
--sq-icon-off: var(--paper-item-icon-color);
--sq-icon-on: var(--paper-item-icon-active-color);
--sq-border-radius: 10px;
}
And you’d include it like this:
lovelace:
resources:
- url: /local/square_theme.css
type: css
That line entity: "[[entity]]"
needs to be changed to entity: "[[[ return entity.entity_id ]]]"
It is only possible if you use a custom_field and build that yourself.
That is hard to achieve, using the card part. A filter will apply to the whole card and make the content also brighter or darker.
An easier way it to declare a css var which is more bright and use that variable.
Usign the fill
attribute should work if your svg supports it:
I might look into this, but I’m not sure this is a good idea.
This is how I tackled it (which is a hack )
Include this as a variable in your button (it will expand the group and the button will update if a nested entity is updated). This variable also contains the number of total entities in the group (works with nested groups also).
max: |
[[[
if (!entity) return 0;
const loop = (elt, list, max) => {
list.forEach(entity => {
if (states[entity])
if (entity.split('.')[0] === "group")
max = loop(elt, states[entity].attributes.entity_id, max);
else {
max++
if (!elt._entities.includes(entity)) elt._entities.push(entity); // This is the hack ;)
}
});
return max;
}
return loop(this, entity.attributes.entity_id, 0);
Use the timer on the button instead of the light. And adapt the button to turn on or off the light.