Here are some more updates for my Mushroom Media Player. These ones use extracted album art colors for the background and controls and use Node-Red to extract the colors.
Mushroom Mini Media Player & Popup:
type: custom:mushroom-media-player-card
entity: media_player.currently_playing
icon: mdi:play
use_media_info: true
icon_type: entity-picture
show_volume_level: false
media_controls:
- play_pause_stop
- next
volume_controls:
- volume_set
fill_container: false
layout: horizontal
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.popup
data:
title: Currently Playing
content:
type: custom:stack-in-card
cards:
- type: custom:mushroom-media-player-card
entity: media_player.currently_playing
icon_type: none
layout: vertical
media_controls:
- previous
- play_pause_stop
- next
volume_controls:
- volume_set
- volume_buttons
show_volume_level: false
use_media_info: true
collapsible_controls: false
card_mod:
style:
mushroom-state-info$: |
/* CSS for Mushroom Popup Media Player */
.secondary:before {
/* Add album name between song title and artist name */
{% if state_attr(config.entity, 'media_album_name') != none %}
content: "{{ state_attr(config.entity, 'media_album_name')}}\A";
{% endif %}
/* Format title to fit on seperate line */
white-space: pre;
text-overflow: ellipsis;
}
.: |
ha-card {
/* Remove border from media player */
--ha-card-border-width: 0;
/* Apply album art color to media player icon & volume bar */
--rgb-state-media-player: var(--album-art-color);
/* Disable transitions */
transition: all 0s;
}
.actions {
/* Apply album art color to media player controls */
--rgb-primary-text-color: var(--album-art-color);
--primary-text-color: rgb(var(--album-art-color));
/* Move volume button to seperate row */
display: block !important;
}
ha-card:before {
content: "";
/* Show album art above media player when active and default image when idle */
{% if is_state(config.entity, ['playing', 'paused']) %}
background: url( '{{ state_attr(config.entity, "entity_picture") }}') center no-repeat;
{% else %}
background: url('/local/idle_art.png') center no-repeat;
{% endif %}
/* Add padding around album art */
margin: 0px 4px 16px;
/* Add drop shadow to album art */
filter: drop-shadow(4px 4px 6px rgba(var(--album-art-color), 0.3));
/* Round borders of album art */
border-radius: var(--control-border-radius);
/* Adjust the album art aspect ratio based on media type */
{% set media_type = state_attr(config.entity, 'media_content_type') %}
{% if media_type == 'tvshow' %}
aspect-ratio: 16 / 9;
{% elif media_type == 'movie' %}
aspect-ratio: 2 / 3;
{% else %}
aspect-ratio: 1 / 1;
{% endif %}
/* Stretch album art to fit box. Fix for if album art is not sized correctly */
background-size: 100% 100%;
}
mushroom-button {
/* Size volume button to match other controls and center */
display: flex;
width: calc((100% / 3) - (var(--spacing) / 3) * 2);
margin: auto;
}
mushroom-media-player-media-control,
mushroom-media-player-volume-control {
/* Correct margins for volume button on second row */
display: flex;
margin-right: 0px !important;
/* Check if PLAY|STOP are supported and adjust margin accordingly */
{% if state_attr(config.entity, 'supported_features') | int | bitwise_and(20480) > 0 %}
margin-bottom: var(--spacing) !important;
{% endif %}
}
- entity: media_player.currently_playing
hide:
icon: true
name: true
runtime: true
source: true
power: true
state_label: true
volume: true
info: true
progress: false
controls: true
more_info: false
type: custom:mini-media-player
toggle_power: false
group: true
card_mod:
style:
mmp-progress$: |
paper-progress {
/* Apply album art color to progress bar when paused */
--paper-progress-container-color: rgba(var(--album-art-color), 0.2) !important;
/* Apply album art color to progress bar when playing */
--paper-progress-active-color: rgb(var(--album-art-color)) !important;
}
.: |
ha-card {
/* Move progress bar up into gap. Check if PLAY|STOP are supported */
--base-offset: calc(4 * var(--mush-spacing, 12px)
+ 1.33 * var(--mush-spacing, 12px)
+ var(--mush-card-primary-line-height, 1.5) * var(--mush-card-primary-font-size, 14px)
+ var(--mush-card-secondary-line-height, 1.5) * var(--mush-card-secondary-font-size, 12px)
+ var(--mush-control-height, 42px));
/* Check if Play (16385) or Stop (4096) are supported and add control button height if they are */
{% if state_attr(config.entity, 'supported_features') | int | bitwise_and(20480) > 0 %}
--control-offset: calc(var(--mush-spacing, 12px) + var(--mush-control-height, 42px));
{% else %}
--control-offset: 0px;
{% endif %}
/* Check if album name is present and add to height if it is */
{% set album_name = state_attr(config.entity, 'media_album_name') %}
{% if album_name == None or album_name == "" %}
--album-offset: 0px;
{% else %}
--album-offset: calc(var(--mush-card-secondary-line-height, 1.5) * var(--mush-card-secondary-font-size, 12px));
{% endif %}
bottom: calc(var(--base-offset) + var(--control-offset) + var(--album-offset));
/* Correct margins for progress bar */
margin: 0px 28px -12px;
/* Set height of card to match pregress bar height */
height: var(--mmp-progress-height);
/* Remove border outline */
--ha-card-border-width: 0;
/* Round corners of progress bar */
--mmp-border-radius: var(--control-border-radius, 12px) !important;
/* Set height of progress bar */
--mmp-progress-height: 12px !important;
/* Remove transitions to prevent progress bar floating in */
transition: all 0s;
}
card_mod:
style: |
:host {
/* Assign album art color to variable used in popup */
--album-art-color:
{% if is_state('media_player.currently_playing', ['playing', 'paused']) %}
{{ states('sensor.muted_color') }}
{% else %}
141, 117, 238
{% endif %};
/* Remove background because it is applied to popup */
--ha-card-background: none;
}
card_mod:
style:
ha-dialog$: |
.mdc-dialog__surface {
/* Apply gradient background to popup using album art colors. Set to default colors when idle */
{% if is_state('media_player.currently_playing', ['playing', 'paused']) %}
background: linear-gradient(305deg, transparent 50%, rgba({{ states('sensor.dark_vibrant_color') }}, 0.4)),
linear-gradient(55deg, transparent 50%, rgba({{ states('sensor.vibrant_color') }}, 0.2)),
linear-gradient(180deg, transparent 40%, rgba({{ states('sensor.dark_muted_color') }}, 0.3));
{% else %}
background: linear-gradient(0deg, transparent 40%, rgba(192, 127, 190, 0.3)),
linear-gradient(240deg, transparent 40%, rgba(143, 119, 237, 0.3)),
linear-gradient(120deg, transparent 40%, rgba(122, 181, 239, 0.3));
{% endif %}
}
ha-header-bar$: |
.mdc-top-app-bar {
/* Remove header background so that popup background is visible */
--mdc-theme-primary: none;
/* Reduced the gap between header and album art */
margin-bottom: -16px;
}
.: |
:host {
/* Set width of popup */
--popup-min-width: 450px;
}
card_mod:
style: |
/* CSS for Mushroom Mini Media Player */
ha-card {
/* Apply album art color to volume bar */
--rgb-state-media-player: var(--album-art-color);
/* Apply gradient background to sticky media player using album art colors */
{% if is_state('media_player.currently_playing', ['playing', 'paused']) %}
--ha-card-background: linear-gradient(135deg, rgba({{ states('sensor.dark_vibrant_color') }}, 0.3),
rgba({{ states('sensor.dark_muted_color') }}, 0.3));
{% endif %}
}
.actions {
/* Apply album art color to sticky media player controls */
--rgb-primary-text-color: var(--album-art-color);
--primary-text-color: rgb(var(--album-art-color));
/* Allow the album info to display more text */
max-width: fit-content;
min-width: calc(2 * var(--mush-spacing, 12px) + 3 * var(--mush-control-height, 42px));
}
mushroom-media-player-volume-control {
/* Fix the width of the volume bar */
width: 200px;
}
:host {
/* Add background to host to prevent transparent card */
border-radius: var(--ha-card-border-radius, 12px);
background: var(--card-background-color);
/* Assign album art color to variable used in sticky media player */
{% if is_state('media_player.currently_playing', ['playing', 'paused']) %}
--album-art-color: {{ states('sensor.muted_color') }};
{% else %}
/* Hide the sticky media player when it is idle */
display: none !important;
{% endif %}
/* Make the media player sticky at the bottom of the page */
position: sticky;
bottom: 12px;
}
Mushroom Media Player:
type: custom:stack-in-card
cards:
- type: custom:mushroom-media-player-card
entity: media_player.office
icon: mdi:play
name: Media Player
use_media_info: true
use_media_artwork: false
show_volume_level: false
media_controls:
- play_pause_stop
- previous
- next
volume_controls:
- volume_buttons
- volume_set
fill_container: false
collapsible_controls: true
card_mod:
style:
mushroom-shape-icon:
$: |
ha-icon {
/* Animate media player icon based on media type */
{% set media_type = state_attr(config.entity, 'media_content_type') %}
--icon-animation:
{% if media_type == 'tvshow' %}
flicker 1s linear infinite alternate
{% elif media_type == 'movie' %}
spin 2s linear infinite reverse
{% elif media_type == 'music' %}
beat 1.3s ease-out infinite both
{% elif media_type == 'playlist' %}
beat 1.3s ease-out infinite both
{% endif %};
/* Stop animation while paused */
{{ '--icon-animation: none;' if not is_state(config.entity, 'playing') }}
}
@keyframes flicker {
0%, 31.98%, 32.98%, 34.98%, 36.98%, 39.98%, 67.98%, 68.98%, 95.98%, 96.98%, 97.98%, 98.98%, 100% { opacity: 1; }
32%, 33%, 35%, 36%, 37%, 40%, 68%, 69%, 96%, 97%, 98%, 99% { opacity: 0.6; }
}
@keyframes beat {
0% { transform: scale(1); }
10% { transform: scale(1.1); }
17% { transform: scale(1.05); }
33% { transform: scale(1.25); }
60% { transform: scale(1); }
}
.: |
mushroom-shape-icon {
/* Change media player icon based on media type */
{% set media_type = state_attr(config.entity, 'media_content_type') %}
--card-mod-icon:
{% if media_type == 'tvshow' %}
mdi:television-classic
{% elif media_type == 'movie' %}
mdi:movie-roll
{% elif media_type == 'music' %}
mdi:music
{% elif media_type == 'playlist' %}
mdi:music
{% else %}
mdi:play
{% endif %};
}
ha-card {
/* Remove border from media player */
--ha-card-border-width: 0;
/* Apply album art color to media player icon & volume bar */
--rgb-state-media-player: var(--album-art-color);
/* Disable transitions */
transition: all 0s;
}
.actions {
/* Apply to controls only when active */
{% if is_state(config.entity, ['playing', 'paused']) %}
/* Apply album art color to media player controls */
--rgb-primary-text-color: var(--album-art-color);
--primary-text-color: rgb(var(--album-art-color));
/* Add space for progress bar */
padding-top: 24px;
{% endif %}
}
- entity: media_player.office
hide:
icon: true
name: true
runtime: true
source: true
power: true
state_label: true
volume: true
info: true
progress: false
controls: true
more_info: false
type: custom:mini-media-player
toggle_power: false
group: true
card_mod:
style:
mmp-progress$: |
paper-progress {
/* Hide the progress bar when not playing or paused */
{{ 'display: none;' if not is_state(config.entity, ['playing', 'paused']) }}
/* Apply album art color to progress bar when paused */
--paper-progress-container-color: rgba(var(--album-art-color), 0.2) !important;
/* Apply album art color to progress bar when playing */
--paper-progress-active-color: rgb(var(--album-art-color)) !important;
}
.: |
ha-card {
/* Move progress bar up into gap */
bottom: calc(3 * var(--mush-spacing, 12px) + var(--mush-control-height, 42px));
/* Correct margins for progress bar */
margin: 0px calc(1.618 * var(--mush-spacing, 12px)) calc(-1 * var(--mush-spacing, 12px));
/* Set height of card to match pregress bar height */
height: var(--mmp-progress-height);
/* Remove border outline */
--ha-card-border-width: 0;
/* Round corners of progress bar */
--mmp-border-radius: var(--control-border-radius, 12px) !important;
/* Set height of progress bar */
--mmp-progress-height: var(--mush-spacing, 12px) !important;
/* Remove transitions to prevent progress bar floating in */
transition: all 0s;
}
card_mod:
style: |
ha-card:before {
content: "";
/* Position and size background color */
position: absolute;
height: 100%;
width: 100%;
/* Apply background to media player using album art color */
{% if is_state('media_player.office', ['playing', 'paused']) %}
background: rgba({{ states('sensor.office_dark_vibrant_color') }}, 0.3);
{% endif %}
}
ha-card {
/* Apply blended album art background */
{% if is_state('media_player.office', ['playing', 'paused']) %}
background: url( '{{ state_attr("media_player.office", "entity_picture") }}' ) no-repeat,
linear-gradient(to left, transparent, rgb(var(--rgb-card-background-color)) 50%);
/* Adjust for different aspect ratio of TV shows episode art */
{% if state_attr('media_player.office', 'media_content_type') == 'tvshow' %}
background-size: 50% 100%, cover;
{% else %}
background-size: 50% auto, cover;
{% endif %}
/* Position art on right side */
background-position: right;
/* Blend art with background using saturation */
background-blend-mode: saturation;
{% endif %}
}
:host {
/* Assign album art color to variable used in media player. Default to indigo when idle */
--album-art-color:
{% if is_state('media_player.office', ['playing', 'paused']) %}
{{ states('sensor.office_muted_color') }}
{% else %}
var(--mush-rgb-indigo, 63, 81, 181)
{% endif %};
}
Node-Red Album Art Color Flow:
Node-Red Album Art Color Flow
[{"id":"eacdc375eb4de995","type":"server-state-changed","z":"f05a616e.f2aef","name":"Currently Playing Changed","server":"84a6d09c.5336e","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"media_player.currently_playing","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"playing","halt_if_type":"re","halt_if_compare":"is","outputs":2,"output_only_on_state_change":false,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":210,"y":3670,"wires":[["bd9143fe7b314177"],[]]},{"id":"bd9143fe7b314177","type":"function","z":"f05a616e.f2aef","name":"Format Album Art URL","func":"msg.payload = 'https://**homeassistant.url**:8123' + msg.data.new_state.attributes.entity_picture\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":3670,"wires":[["c309c0f5a752ecdd"]]},{"id":"c309c0f5a752ecdd","type":"vibrant","z":"f05a616e.f2aef","name":"Extract Album Art Color","x":840,"y":3670,"wires":[["7882f420860746b9"]]},{"id":"7882f420860746b9","type":"function","z":"f05a616e.f2aef","name":"Colors","func":"var msg1 = { payload: msg.payload.Vibrant.rgb[0] + ',' + msg.payload.Vibrant.rgb[1] + ',' + msg.payload.Vibrant.rgb[2] };\nvar msg2 = { payload: msg.payload.DarkVibrant.rgb[0] + ',' + msg.payload.DarkVibrant.rgb[1] + ',' + msg.payload.DarkVibrant.rgb[2] };\nvar msg3 = { payload: msg.payload.LightVibrant.rgb[0] + ',' + msg.payload.LightVibrant.rgb[1] + ',' + msg.payload.LightVibrant.rgb[2] };\nvar msg4 = { payload: msg.payload.Muted.rgb[0] + ',' + msg.payload.Muted.rgb[1] + ',' + msg.payload.Muted.rgb[2] };\nvar msg5 = { payload: msg.payload.DarkMuted.rgb[0] + ',' + msg.payload.DarkMuted.rgb[1] + ',' + msg.payload.DarkMuted.rgb[2] };\nvar msg6 = { payload: msg.payload.LightMuted.rgb[0] + ',' + msg.payload.LightMuted.rgb[1] + ',' + msg.payload.LightMuted.rgb[2] };\n\nreturn [msg1, msg2, msg3, msg4, msg5, msg6];","outputs":6,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1090,"y":3670,"wires":[["cf6ff890cadf96ef"],["63e066959f858c54"],["6efb0dc3953b3240"],["5806b87267d02bc3"],["77b1979bc5ad11f3"],["e5b91489990a01ea"]]},{"id":"cf6ff890cadf96ef","type":"ha-sensor","z":"f05a616e.f2aef","name":"Vibrant Color","entityConfig":"dfb12e11b7a6e846","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1460,"y":3540,"wires":[[]]},{"id":"63e066959f858c54","type":"ha-sensor","z":"f05a616e.f2aef","name":"Dark Vibrant Color","entityConfig":"3e58f341fe905599","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1480,"y":3590,"wires":[[]]},{"id":"6efb0dc3953b3240","type":"ha-sensor","z":"f05a616e.f2aef","name":"Light Vibrant Color","entityConfig":"70d8791a24738ee6","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1480,"y":3640,"wires":[[]]},{"id":"5806b87267d02bc3","type":"ha-sensor","z":"f05a616e.f2aef","name":"Muted Color","entityConfig":"be0a1e0059e1713e","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1460,"y":3690,"wires":[[]]},{"id":"77b1979bc5ad11f3","type":"ha-sensor","z":"f05a616e.f2aef","name":"Dark Muted Color","entityConfig":"90991e9dde96cec6","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1480,"y":3740,"wires":[[]]},{"id":"e5b91489990a01ea","type":"ha-sensor","z":"f05a616e.f2aef","name":"Light Muted Color","entityConfig":"13f67da67efbf0ae","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1480,"y":3790,"wires":[[]]},{"id":"84a6d09c.5336e","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"dfb12e11b7a6e846","type":"ha-entity-config","server":"84a6d09c.5336e","deviceConfig":"","name":"Vibrant Color","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Vibrant Color"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"3e58f341fe905599","type":"ha-entity-config","server":"84a6d09c.5336e","deviceConfig":"","name":"Dark Vibrant Color","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Dark Vibrant Color"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"70d8791a24738ee6","type":"ha-entity-config","server":"84a6d09c.5336e","deviceConfig":"","name":"Light Vibrant Color","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Light Vibrant Color"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"be0a1e0059e1713e","type":"ha-entity-config","server":"84a6d09c.5336e","deviceConfig":"","name":"Muted Color","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Muted Color"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"90991e9dde96cec6","type":"ha-entity-config","server":"84a6d09c.5336e","deviceConfig":"","name":"Dark Muted Color","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Dark Muted Color"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"13f67da67efbf0ae","type":"ha-entity-config","server":"84a6d09c.5336e","deviceConfig":"","name":"Light Muted Color","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Light Muted Color"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false}]
You will need to replace **homeassistant.url**
with the URL for your Home Assistant instance.
In addition to Node-Red the Vibrant Node is Required: