What this does
I wanted a compact card to show playlists but couldn’t find one. This card is basically a simple slider that lists playlists from Music Assistant. Together with a trigger template sensor, it displays six playlists dynamically. Tap any of them to start playback on a media player.
This is a very simplified setup so you can easily adapt it to your own use case. Think of it as a starting point.
What you need
- paper-buttons-row (from HACS)
- A template trigger sensor that holds all playlist info in its attributes
- A script to play a playlist, since Home Assistant doesn’t allow templates directly in tap actions
The sensor
This part took me a while to figure out and there are probably cleaner ways to code it. Change the ID to your own entry ID and the “time_pattern” trigger and the action data to fit your specific usecase if needed.
# Music Assistant Playlist Sensor
- triggers:
- platform: time_pattern
minutes: "/30"
actions:
- action: music_assistant.get_library
data:
config_entry_id: MUSIC_ASSISTANT_ENTRY_ID
media_type: playlist
limit: 6
order_by: timestamp_added_desc
response_variable: result
sensor:
- name: "Music Assistant Playlists"
unique_id: music_assistant_playlists
icon: mdi:playlist-music
state: >-
{{ now().isoformat() }}
attributes:
last_updated: >-
{{ now().isoformat() }}
playlist_1_name: >-
{{ result['items'][0]['name'] if result is defined and 'items' in result and result['items']|length > 0 else '' }}
playlist_1_uri: >-
{{ result['items'][0]['uri'] if result is defined and 'items' in result and result['items']|length > 0 else '' }}
playlist_1_image: >-
{{ result['items'][0]['image'] if result is defined and 'items' in result and result['items']|length > 0 else '' }}
playlist_2_name: >-
{{ result['items'][1]['name'] if result is defined and 'items' in result and result['items']|length > 1 else '' }}
playlist_2_uri: >-
{{ result['items'][1]['uri'] if result is defined and 'items' in result and result['items']|length > 1 else '' }}
playlist_2_image: >-
{{ result['items'][1]['image'] if result is defined and 'items' in result and result['items']|length > 1 else '' }}
playlist_3_name: >-
{{ result['items'][2]['name'] if result is defined and 'items' in result and result['items']|length > 2 else '' }}
playlist_3_uri: >-
{{ result['items'][2]['uri'] if result is defined and 'items' in result and result['items']|length > 2 else '' }}
playlist_3_image: >-
{{ result['items'][2]['image'] if result is defined and 'items' in result and result['items']|length > 2 else '' }}
playlist_4_name: >-
{{ result['items'][3]['name'] if result is defined and 'items' in result and result['items']|length > 3 else '' }}
playlist_4_uri: >-
{{ result['items'][3]['uri'] if result is defined and 'items' in result and result['items']|length > 3 else '' }}
playlist_4_image: >-
{{ result['items'][3]['image'] if result is defined and 'items' in result and result['items']|length > 3 else '' }}
playlist_5_name: >-
{{ result['items'][4]['name'] if result is defined and 'items' in result and result['items']|length > 4 else '' }}
playlist_5_uri: >-
{{ result['items'][4]['uri'] if result is defined and 'items' in result and result['items']|length > 4 else '' }}
playlist_5_image: >-
{{ result['items'][4]['image'] if result is defined and 'items' in result and result['items']|length > 4 else '' }}
playlist_6_name: >-
{{ result['items'][5]['name'] if result is defined and 'items' in result and result['items']|length > 5 else '' }}
playlist_6_uri: >-
{{ result['items'][5]['uri'] if result is defined and 'items' in result and result['items']|length > 5 else '' }}
playlist_6_image: >-
{{ result['items'][5]['image'] if result is defined and 'items' in result and result['items']|length > 5 else '' }}
The paper button row card
type: custom:paper-buttons-row
styles:
gap: 12px
justify-content: flex-start
margin: "-24px"
padding: 24px
overflow-x: scroll
overflow-y: visible
scrollbar-width: none
scroll-padding-block-start: 0px
overscroll-behavior-x: contain
scroll-snap-type: x mandatory
scroll-timeline: "--scroll-timeline x"
mask-image: >-
linear-gradient(to right, transparent 0%, black 24px, black calc(100% -
24px), transparent 100%)
base_config:
entity: sensor.music_assistant_playlists
layout: state
styles:
button:
width: 90px
height: 90px
scroll-snap-align: start
scroll-margin-left: 24px
padding: 8px
justify-content: flex-start
align-items: flex-end
border-radius: 20px
background-color: rgba(255,255,255, 0.8)
background-size: 120%
background-position: center
transition: 0.3s all ease-in-out
state:
font-size: 12px
font-weight: 700
padding: 6px 10px
white-space: nowrap
overflow: hidden
display: block
max-width: 110px
text-overflow: ellipsis
"-webkit-line-clamp": 1
background-color: rgba(255,255,255, 0.8)
backdrop-filter: blur(10px)
transition: 0.3s all ease-in-out
border-radius: 12px
buttons:
- state:
attribute: playlist_1_name
tap_action:
action: call-service
service: script.music_assistant_dynamic_playlist
service_data:
playlist_number: 1
styles:
button:
background-image: >-
url("{{ state_attr('sensor.music_assistant_playlists',
'playlist_1_image') }}"
- state:
attribute: playlist_2_name
tap_action:
action: call-service
service: script.music_assistant_dynamic_playlist
service_data:
playlist_number: 2
styles:
button:
background-image: >-
url("{{ state_attr('sensor.music_assistant_playlists',
'playlist_2_image') }}"
- state:
attribute: playlist_3_name
tap_action:
action: call-service
service: script.music_assistant_dynamic_playlist
service_data:
playlist_number: 3
styles:
button:
background-image: >-
url("{{ state_attr('sensor.music_assistant_playlists',
'playlist_3_image') }}"
- state:
attribute: playlist_4_name
tap_action:
action: call-service
service: script.music_assistant_dynamic_playlist
service_data:
playlist_number: 4
styles:
button:
background-image: >-
url("{{ state_attr('sensor.music_assistant_playlists',
'playlist_4_image') }}"
- state:
attribute: playlist_5_name
tap_action:
action: call-service
service: script.music_assistant_dynamic_playlist
service_data:
playlist_number: 5
styles:
button:
background-image: >-
url("{{ state_attr('sensor.music_assistant_playlists',
'playlist_5_image') }}"
- state:
attribute: playlist_6_name
tap_action:
action: call-service
service: script.music_assistant_dynamic_playlist
service_data:
playlist_number: 6
styles:
button:
background-image: >-
url("{{ state_attr('sensor.music_assistant_playlists',
'playlist_6_image') }}"
… and the script
Replace “MASS_PLAYER_ENTITY_ID” with your actual media player.
sequence:
- alias: Start Playlist
action: media_player.play_media
data:
media:
media_content_id: >-
{{ state_attr('sensor.music_assistant_playlists', 'playlist_' ~
playlist_number ~ '_uri') }}
media_content_type: playlist
metadata:
media_class: playlist
children_media_class: track
navigateIds:
- media_content_type: music_assistant
media_content_id: playlists
- media_content_type: music
media_content_id: >-
{{ state_attr('sensor.music_assistant_playlists', 'playlist_' ~
playlist_number ~ '_uri') }}
browse_entity_id: MASS_PLAYER_ENTITY_ID
enqueue: replace
target:
entity_id: MASS_PLAYER_ENTITY_ID
alias: Music Assistant | Dynamic Playlist
description: ""
