Music Assistant Playlist Slider Card (Tutorial)

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: ""

6 Likes

This looks great and maybe what I am looking for. Thank you for sharing and I’ll definately give it a try.