A Flexible Multi-Player / Multi-Device Music Interface

Hi all,

I have been working on a small control panel to easily select the music you would like.
Basically, the interface has two modes:

  1. When no music is playing, a block gives the opportunity to select which music app you want to use (in my case, Plex, Web Radio, or Spotify) with for each a list of favorite playlists/Channels and on which room (Chromecast Speaker Groups).
    image

  2. When music is playing, a simple view of what’s playing, the opportunity to change sound level, stop or mute.
    image
    In this one, I had to “reproduce” a basic media player as custom button because media player cannot be dynamic. As I am neither an artist, nor a CSS pro, that “player” has visual room for improvement.

The challenge (fun) was to simply combine many options in an easy interface.

The add-ons/integration used in this project are:

  • Custom button cards in the interface
  • Spocast to launch spotify music on sleeping Google Home speakers
  • The Plex Integration for local media
  • The URL’s of web radio come from this site, but Google can help if you are looking for different ones

The full code has been centralized in one package:

###################################################################################################
#                                                                                                 #
#                           Music Player - Configuration & Selection                              #
#                                                                                                 #
###################################################################################################

###################################################################################################
# Music Player - Audio Selection
###################################################################################################

input_select:

  audio_media_player_select:
    name: Choix du media audio
    options:
      - Plex
      - Spotify
      - Webradio
    initial: Plex
    icon: mdi:music-box-multiple

  audio_media_device_select:
    name: Choix des hauts-parleurs
    options:
      - Cuisine
      - Rez-de-chaussée
      - Maison entière
    initial: Cuisine
    icon: mdi:cast-audio
    
  plex_playlist_select:
    name: Playlist Plex à jouer
    options:
      - All Music
      - Cinéma & Télévision
      - Dinner Classics
      - Français
      - International
      - Madeleine & Lucien
      - Musique Classique
      - Noël
    initial: All Music
    icon: mdi:music

  spotify_playlist_select:
    name: Playlist Spotify à jouer
    options:
      - Hits Français
      - Culture Tubes
      - Noël Top 100
      - La Vie est Belle
      - Life Sucks
      - Dinner with Friends
      - Soft Pop
      - Classical Movie Music
    initial: La Vie est Belle
    icon: mdi:spotify

  webradio_select:
    name: Choix Webradio
    options:
      - Joe FM
      - La Première
      - Nostalgie 80
      - Nostalgie Cinéma
      - Q-Music
      - Q Foute Radio
      - RTL 2
    initial: Q-Music
    icon: mdi:radio

###################################################################################################
# Music Player - Selected Audio Transformation
###################################################################################################

template:

  - sensor:

      - unique_id: "selected_plex_playlist_url" 
        icon: "mdi:music"
        state: >-
          {% if is_state("input_select.plex_playlist_select", "All Music") %} plex://{ "library_name": "Musique", "playlist_name": "All Music", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "Cinéma & Télévision") %} plex://{ "library_name": "Musique", "playlist_name": "Cinéma & Télévision", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "Dinner Classics") %} plex://{ "library_name": "Musique", "playlist_name": "Dinner Classics", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "Français") %} plex://{ "library_name": "Musique", "playlist_name": "Français", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "International") %} plex://{ "library_name": "Musique", "playlist_name": "International", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "Madeleine & Lucien") %} plex://{ "library_name": "Musique", "playlist_name": "Madeleine & Lucien", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "Musique Classique") %} plex://{ "library_name": "Musique", "playlist_name": "Musique Classique", "shuffle":"1" }
          {% elif is_state("input_select.plex_playlist_select", "Noël") %} plex://{ "library_name": "Musique", "playlist_name": "Noël", "shuffle":"1" }
          {% endif %}
        attributes:
          friendly_name: "URL Playlist Plex Choisie"
          filter_key: "Music_Player"

      - unique_id: "selected_webradio_url" 
        icon: "mdi:radio"
        state: >-
          {% if is_state("input_select.webradio_select", "Q-Music") %}
          http://icecast-qmusic.cdp.triple-it.nl/Qmusic_be_live_128.mp3
          {% elif is_state("input_select.webradio_select", "Joe FM") %}
          http://icecast-qmusic.cdp.triple-it.nl/JOEfm_be_live_128.mp3
          {% elif is_state("input_select.webradio_select", "Q Foute Radio") %}  http://playerservices.streamtheworld.com/api/livestream-redirect/QFOUTERADIO.mp3
          {% elif is_state("input_select.webradio_select", "RTL 2") %} http://streaming.radio.rtl2.fr/rtl2-1-44-128?listen=webCwsBCggNCQgLDQUGBAcGBg
          {% elif is_state("input_select.webradio_select", "Nostalgie 80") %} http://cdn.nrjaudio.fm/adwz1/fr/30605/mp3_128.mp3?origine=fluxradios
          {% elif is_state("input_select.webradio_select", "Nostalgie Cinéma") %}
          http://streamingp.shoutcast.com/NostalgieCinema
          {% elif is_state("input_select.webradio_select", "La Première") %}
          http://radios.rtbf.be/laprem1erebxl-128.mp3
          {% endif %}
        picture: >-
          {% if is_state("input_select.webradio_select", "Q-Music") %} 
          /local/radio-logo/qmusic.png
          {% elif is_state("input_select.webradio_select", "Joe FM") %}
          /local/radio-logo/joefm.png
          {% elif is_state("input_select.webradio_select", "Q Foute Radio") %}  
          /local/radio-logo/qfout.png
          {% elif is_state("input_select.webradio_select", "RTL 2") %}
          /local/radio-logo/rtl2.png
          {% elif is_state("input_select.webradio_select", "Nostalgie 80") %}
          /local/radio-logo/nostalgie80.png
          {% elif is_state("input_select.webradio_select", "Nostalgie Cinéma") %}
          /local/radio-logo/nostalgie_cinema.png
          {% elif is_state("input_select.webradio_select", "La Première") %}
          /local/radio-logo/premiere.png
          {% endif %} 
        attributes:
          friendly_name: "URL Webradio Choisie"
          filter_key: "Music_Player"
          
      - unique_id: "selected_spotify_playlist_uri"
        state: >-
          {% if is_state("input_select.spotify_playlist_select", "Hits Français") %}
          spotify:playlist:37i9dQZF1DXcSPhLAnCjoM
          {% elif is_state("input_select.spotify_playlist_select", "Culture Tubes") %}
          spotify:playlist:37i9dQZF1DXd0Y4aXXQXWv 
          {% elif is_state("input_select.spotify_playlist_select", "Noël Top 100") %}
          spotify:playlist:2wlZ9OIdKnLrSLRqIpBPax
          {% elif is_state("input_select.spotify_playlist_select", "Soft Pop") %}
          spotify:playlist:37i9dQZF1DWTwnEm1IYyoj
          {% elif is_state("input_select.spotify_playlist_select", "La Vie est Belle") %}
          spotify:playlist:37i9dQZF1DXdrln2UyZD7F
          {% elif is_state("input_select.spotify_playlist_select", "Life Sucks") %}
          spotify:playlist:37i9dQZF1DX3YSRoSdA634
          {% elif is_state("input_select.spotify_playlist_select", "Dinner with Friends") %}
          spotify:playlist:37i9dQZF1DX4xuWVBs4FgJ
          {% elif is_state("input_select.spotify_playlist_select", "Classical Movie Music") %}
          spotify:playlist:1UdKFAMBjM3prK7hieeqjC
          {% endif %}
        icon: mdi:spotify
        attributes:
          friendly_name: "URI Playlist Spotify Choisie"
          filter_key: "Music_Player"

      - unique_id: "selected_audio_media_type" 
        state: >-
          {% if is_state("input_select.audio_media_player_select", "Plex") %} PLAYLIST
          {% elif is_state("input_select.audio_media_player_select", "Webradio") %} audio/mp4
          {% elif is_state("input_select.audio_media_player_select", "Spotify") %} playlist
          {% endif %} 
        attributes:
          friendly_name: "Catégorie Media Choisi"
          filter_key: "Music_Player"
          
      - unique_id: "selected_audio_media_device" 
        icon: "mdi:cast-audio"
        state: >-
          {% if is_state("input_select.audio_media_device_select", "Cuisine") %} media_player.paire_cuisine
          {% elif is_state("input_select.audio_media_device_select", "Rez-de-chaussée") %} media_player.rez_de_chaussee
          {% elif is_state("input_select.audio_media_device_select", "Maison entière") %} media_player.maison
          {% endif %}
        attributes:
          friendly_name: "Groupe de Hauts-Parleurs choisi"
          filter_key: "Music_Player"
          plexid: >-
            {% if is_state("input_select.audio_media_device_select", "Cuisine") %} media_player.plex_chromecast_paire_cuisine
            {% elif is_state("input_select.audio_media_device_select", "Rez-de-chaussée") %} media_player.plex_chromecast_rez_de_chaussee
            {% elif is_state("input_select.audio_media_device_select", "Maison entière") %} media_player.plex_chromecast_maison
            {% endif %}          

      - unique_id: "selected_audio_media_device_state" 
        icon: "mdi:cast-audio-variant"
        state: >-
          {{ states(states("sensor.selected_audio_media_device")) }}
        attributes:
          friendly_name: "Groupe de Hauts-Parleurs choisi - Etat"
          filter_key: "Music_Player"

      - unique_id: "selected_audio_media_device_muted" 
        icon: "mdi:volume-mute"
        state: >-
          {{ (state_attr(states('sensor.selected_audio_media_device'), 'is_volume_muted')) }}
        attributes:
          friendly_name: "Groupe de Hauts-Parleurs choisi - Mute/Unmute"
          filter_key: "Music_Player"
          
      - unique_id: "selected_audio_media_device_picture" 
        icon: "mdi:panorama-variant"
        state: >-
          {% if is_state('input_select.audio_media_player_select', 'Plex') %}
          {{ (state_attr(state_attr('sensor.selected_audio_media_device', 'plexid'), 'entity_picture')) }}
          {% elif is_state('input_select.audio_media_player_select', 'Webradio') %}
          {{ (state_attr('sensor.selected_webradio_url', 'entity_picture')) }}
          {% elif is_state('input_select.audio_media_player_select', 'Spotify') %}
          {{ (state_attr(states('sensor.selected_audio_media_device'), 'entity_picture_local'))}}
          {% endif %} 
        attributes:
          friendly_name: "Groupe de Hauts-Parleurs choisi - Image"
          filter_key: "Music_Player"
          
###################################################################################################
# Music Player - Sound Volume Management
###################################################################################################

      - unique_id: "sound_volume_selected_audio_device"
        state: >-
          {% if is_state("input_select.audio_media_device_select", "Cuisine") %}
          {{ (state_attr('media_player.paire_cuisine', 'volume_level') | float *100) |int }}
          {% elif is_state("input_select.audio_media_device_select", "Rez-de-chaussée") %}
          {{ (state_attr('media_player.rez_de_chaussee', 'volume_level') | float *100) |int }}
          {% elif is_state("input_select.audio_media_device_select", "Maison entière") %}
          {{ (state_attr('media_player.maison', 'volume_level') | float *100) |int }}
          {% endif %}
        icon: >-
          {% if states("sensor.sound_volume_selected_audio_device")|float(0) >37 %} mdi:volume-high
          {% elif states("sensor.sound_volume_selected_audio_device")|float(0) >17 %} mdi:volume-medium
          {% elif states("sensor.sound_volume_selected_audio_device")|float(0) >0 %} mdi:volume-low
          {% else %} mdi:speaker-off
          {% endif %}
        attributes:
          friendly_name: "Groupe de Hauts-Parleurs choisi - Volume"
          filter_key: "Music_Player"
          
###################################################################################################
# Music Player - Artist & Title Information Extract
###################################################################################################

      - unique_id: "artist_playing"
        state: >-
          {% if is_state("input_select.audio_media_player_select", "Webradio") %}
          {{ states("input_select.webradio_select") }}
          {% else %} 
          {{ (state_attr(states('sensor.selected_audio_media_device'), 'media_artist')) }}
          {% endif %} 
        icon: mdi:account-music
        attributes:
          friendly_name: "Interprète de la chanson en cours"
          filter_key: "Music_Player"
          
      - unique_id: "title_playing"
        state: >-
          {% if is_state("input_select.audio_media_player_select", "Webradio") %}
          {{ states("input_select.webradio_select") }}
          {% else %} 
          {{ (state_attr(states('sensor.selected_audio_media_device'), 'media_title')) }}
          {% endif %} 
        icon: mdi:file-music
        attributes:
          friendly_name: "Titre de la chanson en cours"
          filter_key: "Music_Player"
          
###################################################################################################
# Music Player - Scripts
###################################################################################################    
script:

# Music Player - Scripts dynamiques

    music_control_play_music:
      alias: Musique Dynamique - Jouer de la musique
      sequence:
      - choose:
        - conditions:
          - condition: state
            entity_id: input_select.audio_media_player_select
            state: Spotify
          sequence:
          - service: spotcast.start
            data:
              random_song: true
              shuffle: true
              entity_id: >-
                {{ states("sensor.selected_audio_media_device")}}
              uri: '{{ (states(''sensor.selected_spotify_playlist_uri'')) }}'
              account: quizmaster
        default:
        - service: media_player.play_media
          data:
            media_content_type: '{{ states("sensor.selected_audio_media_type")}}'
            media_content_id: >-
              {% if is_state("input_select.audio_media_player_select", "Plex") %}
                {{ states("sensor.selected_plex_playlist_url") }}
              {% elif is_state("input_select.audio_media_player_select", "Webradio") %}
                {{ states("sensor.selected_webradio_url")}}
              {% endif %}
          target:
            entity_id: '{{ states("sensor.selected_audio_media_device")}}'
      mode: single
      icon: mdi:folder-music-outline
      
    music_control_stop_music:
      alias: Musique Dynamique - Arrêter la musique
      sequence:
      - service: media_player.media_stop
        target:
          entity_id: '{{ states("sensor.selected_audio_media_device")}}'
      mode: single
      icon: mdi:volume-off
      
    music_control_decrease_sound_level:
      alias: Musique Dynamique - Augmenter le volume
      sequence:
      - service: media_player.volume_set
        target:
          entity_id: '{{ states("sensor.selected_audio_media_device") }}'
        data:
          volume_level: >-
            {{ ((((states("sensor.sound_volume_selected_audio_device")|float(0))
            / 500)|round(2) * 5) - 0.05) |round(2) |float(0.05) }}
      mode: single
      icon: mdi:volume-minus
      
    music_control_increase_sound_level:
      alias: Musique Dynamique - Augmenter le volume
      sequence:
      - service: media_player.volume_set
        target:
          entity_id: '{{ states("sensor.selected_audio_media_device") }}'
        data:
          volume_level: >-
            {{ ((((states("sensor.sound_volume_selected_audio_device")|float(0))
            / 500)|round(2) * 5) + 0.05) |round(2) |float(0.05) }}
      mode: single
      icon: mdi:volume-plus

    music_control_mute_unmute_music:
      alias: Musique Dynamique - Mute/Unmute Speakers
      sequence:
      - service: media_player.volume_mute
        target:
          entity_id: '{{ states("sensor.selected_audio_media_device")}}'
        data:
          is_volume_muted: >-
            {% if is_state_attr(states("sensor.selected_audio_media_device"),'is_volume_muted', true) %}
              false
            {% else %}
              true
            {% endif %}
      mode: single
      icon: mdi:volume-off

    music_control_next_track:
      alias: Musique Dynamique - Morceau suivant si API le permets
      sequence:
      - service: media_player.media_next_track
        target:
          entity_id: '{{ states("sensor.selected_audio_media_device")}}'
      mode: single
      icon: mdi:skip-next

As you can see I have created some simple templates and scripts, to keep the conditional part in the lovelace interface reasonable. (see first reply for lovelace code)

Anyway, it’s there to inspire anyone who would like to make something similar.
Comments, suggestions for improvement, feedback welcome.

3 Likes

The whole lovelace has been put in a vertical stack so that it can easily be added to existing (mobile) dashboards.

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - type: conditional
        conditions:
          - entity: sensor.selected_audio_media_device_state
            state_not: playing
        card:
          type: horizontal-stack
          cards:
            - type: custom:button-card
              entity: input_select.audio_media_player_select
              show_state: false
              name: Local Music
              icon: mdi:music
              tap_action:
                action: call-service
                service: input_select.select_option
                service_data:
                  option: Plex
                  entity_id: input_select.audio_media_player_select
              styles:
                name:
                  - justify-self: middle
                  - font-weight: bold
                  - font-size: 14px
                  - color: |
                      [[[
                        if (entity.state == 'Plex')
                          return '#dddddd';
                        else 
                          return '#56819F';
                      ]]]
                card:
                  - height: 70px
                  - background-color: |
                      [[[
                        if (entity.state == 'Plex')
                          return '#56819F';
                        else 
                          return '';
                      ]]]
                icon:
                  - color: |
                      [[[
                        if (entity.state == 'Plex')
                          return '#dddddd';
                        else 
                          return '#56819F';
                      ]]]
            - type: custom:button-card
              entity: input_select.audio_media_player_select
              show_state: false
              name: Online Radio
              icon: mdi:radio
              tap_action:
                action: call-service
                service: input_select.select_option
                service_data:
                  option: Webradio
                  entity_id: input_select.audio_media_player_select
              styles:
                name:
                  - justify-self: middle
                  - font-weight: bold
                  - font-size: 14px
                  - color: |
                      [[[
                        if (entity.state == 'Webradio')
                          return '#dddddd';
                        else 
                          return '#56819F';
                      ]]]
                card:
                  - height: 70px
                  - background-color: |
                      [[[
                        if (entity.state == 'Webradio')
                          return '#56819F';
                        else 
                          return '';
                      ]]]
                icon:
                  - color: |
                      [[[
                        if (entity.state == 'Webradio')
                          return '#dddddd';
                        else 
                          return '#56819F';
                      ]]]
            - type: custom:button-card
              entity: input_select.audio_media_player_select
              show_state: false
              name: Spotify
              icon: mdi:spotify
              tap_action:
                action: call-service
                service: input_select.select_option
                service_data:
                  option: Spotify
                  entity_id: input_select.audio_media_player_select
              styles:
                name:
                  - justify-self: middle
                  - font-weight: bold
                  - font-size: 14px
                  - color: |
                      [[[
                        if (entity.state == 'Spotify')
                          return '#dddddd';
                        else 
                          return '#56819F';
                      ]]]
                card:
                  - height: 70px
                  - background-color: |
                      [[[
                        if (entity.state == 'Spotify')
                          return '#56819F';
                        else 
                          return '';
                      ]]]
                icon:
                  - color: |
                      [[[
                        if (entity.state == 'Spotify')
                          return '#dddddd';
                        else 
                          return '#56819F';
                      ]]]
  - type: conditional
    conditions:
      - entity: input_select.audio_media_player_select
        state: Plex
      - entity: sensor.selected_audio_media_device_state
        state_not: playing
    card:
      type: entities
      entities:
        - entity: input_select.plex_playlist_select
          name: Choose Playlist
  - type: conditional
    conditions:
      - entity: input_select.audio_media_player_select
        state: Webradio
      - entity: sensor.selected_audio_media_device_state
        state_not: playing
    card:
      type: entities
      entities:
        - entity: input_select.webradio_select
          name: Choose Station
  - type: conditional
    conditions:
      - entity: input_select.audio_media_player_select
        state: Spotify
      - entity: sensor.selected_audio_media_device_state
        state_not: playing
    card:
      type: entities
      entities:
        - entity: input_select.spotify_playlist_select
          name: Choose Playlist
  - type: conditional
    conditions:
      - entity: sensor.selected_audio_media_device_state
        state_not: playing
    card:
      type: horizontal-stack
      cards:
        - type: custom:button-card
          entity: input_select.audio_media_device_select
          show_state: false
          name: Cuisine
          icon: mdi:silverware
          tap_action:
            action: call-service
            service: input_select.select_option
            service_data:
              option: Cuisine
              entity_id: input_select.audio_media_device_select
          styles:
            name:
              - justify-self: middle
              - font-weight: bold
              - font-size: 14px
              - color: |
                  [[[
                    if (entity.state == 'Cuisine')
                      return '#dddddd';
                    else 
                      return '#56819F';
                  ]]]
            card:
              - height: 70px
              - background-color: |
                  [[[
                    if (entity.state == 'Cuisine')
                      return '#56819F';
                    else 
                      return '';
                  ]]]
            icon:
              - color: |
                  [[[
                    if (entity.state == 'Cuisine')
                      return '#dddddd';
                    else 
                      return '#56819F';
                  ]]]
        - type: custom:button-card
          entity: input_select.audio_media_device_select
          show_state: false
          name: Rez-de-chaussée
          icon: mdi:home-floor-0
          tap_action:
            action: call-service
            service: input_select.select_option
            service_data:
              option: Rez-de-chaussée
              entity_id: input_select.audio_media_device_select
          styles:
            name:
              - justify-self: middle
              - font-weight: bold
              - font-size: 14px
              - color: |
                  [[[
                    if (entity.state == 'Rez-de-chaussée')
                      return '#dddddd';
                    else 
                      return '#56819F';
                  ]]]
            card:
              - height: 70px
              - background-color: |
                  [[[
                    if (entity.state == 'Rez-de-chaussée')
                      return '#56819F';
                    else 
                      return '';
                  ]]]
            icon:
              - color: |
                  [[[
                    if (entity.state == 'Rez-de-chaussée')
                      return '#dddddd';
                    else 
                      return '#56819F';
                  ]]]
        - type: custom:button-card
          entity: input_select.audio_media_device_select
          show_state: false
          name: Maison entière
          icon: mdi:home
          tap_action:
            action: call-service
            service: input_select.select_option
            service_data:
              option: Maison entière
              entity_id: input_select.audio_media_device_select
          styles:
            name:
              - justify-self: middle
              - font-weight: bold
              - font-size: 14px
              - color: |
                  [[[
                    if (entity.state == 'Maison entière')
                      return '#dddddd';
                    else 
                      return '#56819F';
                  ]]]
            card:
              - height: 70px
              - background-color: |
                  [[[
                    if (entity.state == 'Maison entière')
                      return '#56819F';
                    else 
                      return '';
                  ]]]
            icon:
              - color: |
                  [[[
                    if (entity.state == 'Maison entière')
                      return '#dddddd';
                    else 
                      return '#56819F';
                  ]]]
  - type: horizontal-stack
    cards:
      - type: custom:button-card
        name: Volume Down
        icon: mdi:volume-minus
        color: '#56819F'
        show_state: true
        tap_action:
          action: call-service
          service: script.music_control_decrease_sound_level
        styles:
          card:
            - height: 70px
          name:
            - justify-self: middle
            - font-size: 14px
      - type: custom:button-card
        entity: input_select.audio_media_device_select
        show_state: false
        name: |
          [[[
            if (states['sensor.selected_audio_media_device_state'].state == 'playing')
              return 'Stop';
            else 
              return 'Play';
          ]]]
        icon: |
          [[[
            if (states['sensor.selected_audio_media_device_state'].state == 'playing')
              return 'mdi:stop';
            else 
              return 'mdi:play';
          ]]]
        tap_action:
          action: call-service
          service: |
            [[[
              if (states['sensor.selected_audio_media_device_state'].state == 'playing')
                return 'script.music_control_stop_music';
              else 
                return 'script.music_control_play_music';
            ]]]
        styles:
          name:
            - justify-self: middle
            - font-size: 14px
          card:
            - height: 70px
          icon:
            - color: |
                [[[
                  if (entity.state == 'Plex')
                    return '#dddddd';
                  else 
                    return '#56819F';
                ]]]
      - type: custom:button-card
        name: Volume Up
        icon: mdi:volume-plus
        color: '#56819F'
        show_state: false
        tap_action:
          action: call-service
          service: script.music_control_increase_sound_level
        styles:
          card:
            - height: 70px
          name:
            - justify-self: middle
            - font-size: 14px
      - type: custom:button-card
        entity: sensor.sound_volume_selected_audio_device
        show_name: false
        show_state: true
        icon: |
          [[[
            if (states['sensor.template_selected_audio_media_device_muted'].state == 'True')
              return 'mdi:volume-mute';
            if (states['sensor.sound_volume_selected_audio_device'].state > 37)
              return 'mdi:volume-high'
            if (states['sensor.sound_volume_selected_audio_device'].state > 17)
              return 'mdi:volume-medium'
            if (states['sensor.sound_volume_selected_audio_device'].state > 0)
              return 'mdi:volume-low'
            else 
              return 'mdi:speaker-off';
          ]]]
        tap_action:
          action: call-service
          service: script.music_control_mute_unmute_music
        styles:
          card:
            - height: 70px
            - background-color: '#56819F'
          state:
            - justify-self: middle
            - font-size: 14px
            - color: '#dddddd'
          icon:
            - color: '#dddddd'
  - type: conditional
    conditions:
      - entity: sensor.selected_audio_media_device_state
        state: playing
    card:
      type: horizontal-stack
      cards:
        - type: custom:button-card
          icon: mdi:speaker
          show_icon: false
          show_state: true
          custom_fields:
            device: |
              [[[
                return `${states['input_select.audio_media_device_select'].state}`
              ]]]
            player: |
              [[[
                return `${states['input_select.audio_media_player_select'].state}`
              ]]]
            artist: |
              [[[
                return `${states['sensor.artist_playing'].state}`
              ]]]
            title: |
              [[[
                return `${states['sensor.title_playing'].state}`
              ]]]
          styles:
            card:
              - border-radius: 5px
              - padding: 15px
              - height: 120px
              - color: ivory
              - font-size: 18px
              - text-shadow: 2px 0px 2px black
              - background-repeat: no-repeat
              - background-size: 150px
              - background-color: '#56819F'
              - background-image: |-
                  [[[
                    return `url('${states['sensor.selected_audio_media_device_picture'].state}')`
                  ]]]
              - background-position: right
            grid:
              - grid-template-areas: '"player device ." "artist . ." "artist . ." "title . ."'
              - grid-template-columns: min-content 1fr 1fr
              - grid-template-rows: 1fr 1fr 1fr 1fr
            custom_fields:
              artist:
                - align-self: middle
                - justify-self: start
                - font-size: 22px
              title:
                - align-self: middle
                - justify-self: start
                - font-size: 18px
              player:
                - align-self: top
                - justify-self: start
                - font-size: 14px
              device:
                - align-self: top
                - justify-self: start
                - font-size: 14px
        - type: custom:button-card
          icon: mdi:skip-next
          color: '#56819F'
          size: 40px
          show_state: false
          show_name: false
          tap_action:
            action: call-service
            service: |
              [[[
                if (states['input_select.audio_media_player_select'].state == 'Spotify')
                  return 'script.music_control_next_track';
                else 
                  return 'script.music_control_play_music';
              ]]]
          styles:
            card:
              - width: 60px
              - height: 120px
5 Likes

Hi. I am trying to get spotify to play over my amazon echo. I’ve copied your codes to the best I can changing what I need to fit in my environment. When I press play, I get error saying cannot find the echo. Perhaps I am missing a step somewhere. My echo entity id is media_player.ai_4

partial template sensor.

      - unique_id: "selected_audio_media_device" 
        icon: "mdi:cast-audio"
        state: >-
          {% if is_state("input_select.audio_media_device_select", "Cuisine") %} media_player.ai_4
          {% elif is_state("input_select.audio_media_device_select", "Rez-de-chaussée") %} media_player.ai_1
          {% elif is_state("input_select.audio_media_device_select", "Maison entière") %} media_player.plex_plex_web_chrome_windows
          {% endif %}

error

[547305089648] Could not find device with name Ai.4
8:22:06 AM – (ERROR) Home Assistant WebSocket API (custom integration) - message first occurred at 8:02:36 AM and shows up 9 times

Dynamic Music - Play Music: Error executing script. Error for choose at pos 1: Could not find device with name Ai.4
8:22:06 AM – (ERROR) Script - message first occurred at 8:02:36 AM and shows up 22 times

Could not find device Ai.4 from hass.data
8:22:06 AM – (ERROR) Spotcast (custom integration) - message first occurred at 8:02:36 AM and shows up 11 times

First I would try to isolate the issue.
Does it work on other speakers/devices?
Can you play Spotify on your Echo speaker with other scripts / basic service calls ?

Hi. Thanks for the reply. With the following script below using entity_id does not work.
entity_id: media_player.ai_4

If I change to
device_name: "Ai.4"
it plays.

script:
# Music Player - Scripts dynamiques

    music_control_play_music:
      alias: Dynamic Music - Play Music
      sequence:
      - choose:
        - conditions:
          - condition: state
            entity_id: input_select.audio_media_player_select
            state: Spotify
          sequence:
          - service: spotcast.start
            data:
              random_song: true
              shuffle: true
              entity_id: >-
                {{ states("sensor.template_selected_audio_media_device") }}
              uri: '{{ (states(''sensor.template_selected_spotify_playlist_uri'')) }}'

It says in spotcast.
Optionally you can specify the entity_id of an existing Home Assistant chromecast media-player like:

- service: spotcast.start
  data:
    entity_id: "media_player.vardagsrum"
    uri: "spotify:playlist:37i9dQZF1DX3yvAYDslnv8"

Amazon echo is not a Home Assistant Chromecast media-player??

Just rechecked the documentation. Spocast is for Google Nest/Chromecast devices.
On Amazon Echo, you might have to change the Spotify player command:

          sequence:
          - service: spotcast.start
            data:
              random_song: true
              shuffle: true
              entity_id: >-
                {{ states("sensor.selected_audio_media_device")}}
              uri: "{{ (states('sensor.selected_spotify_playlist_uri')) }}"
              account: xxx

will probably have to be replace by something like this:

    sequence:
    - service: media_player.select_source
      data:
        entity_id: media_player.spotify
        source: "{{ states('sensor.selected_audio_media_device')}}"
    - service: media_player.play_media
      data:
        entity_id: media_player.spotify
        media_content_type: "music"
        media_content_id: "{{ (states('sensor.selected_spotify_playlist_uri')) }}"

Code inspired by Play Spotify track on echo dot - #5 by Burningstone. As I have never used an Echo, I cannot guarantee it works. (You might have to repace the echo entity_id by its name at different places)

1 Like

Nope, Amazon is Amazon. Chromecast is Google.
Not the best friends in the world :wink:

ok. Thanks I will play with it some more. I have another question. With these template sensors created.

template:
  - sensor:
      - unique_id: "selected_audio_media_device" 
        icon: "mdi:cast-audio"
        state: >-
          {% if is_state("input_select.audio_media_device_select", "Cuisine") %} media_player.ai_4
          {% elif is_state("input_select.audio_media_device_select", "Rez-de-chaussée") %} media_player.ai_1
          {% elif is_state("input_select.audio_media_device_select", "Maison entière") %} media_player.plex_plex_web_chrome_windows
          {% endif %}
        attributes:
          friendly_name: "Selected Speaker Group"
          filter_key: "Music_Player"
          plexid: >-
            {% if is_state("input_select.audio_media_device_select", "Cuisine") %} media_player.plex_chromecast_ai_4
            {% elif is_state("input_select.audio_media_device_select", "Rez-de-chaussée") %} media_player.plex_chromecast_ai_1
            {% elif is_state("input_select.audio_media_device_select", "Maison entière") %} media_player.plex_chromecast_plex_plex_web_chrome_windows
            {% endif %}          

HA is creating these sensors by adding template in the front. The above sensor is seen in HA as
sensor.template_selected_audio_media_device
Is this correct??

Yes, it is.
That’s how now the new templates (Template - Home Assistant) work when you use unique_id, it adds “template_” before automatically.
You can avoid that if you use “name” instead of using “unique_id”, but I started with unique_id and stick to that for the consistency. (and UI access)

You can adapt afterwards, but might be tricky to keep scripts in line if you do.

Ok. I was just checking since I have never used template sensors until now.
I finally got Spotify to play with my Amazon echo after a few trial and error. Your base code worked and I have to figured out how to get the name of the the Amazon echo for the source:. I saw the plexid where you custom create an attribute. Not sure what that does, but I will find out when I tackle the plex and webradio next. This gave me the idea to added one attribute sourceid to get the name of my Amazon echo that Spotify needed for source.

template:
  - sensor:
      - unique_id: "selected_audio_media_device" 
        icon: "mdi:cast-audio"
        state: >-
          {% if is_state("input_select.audio_media_device_select", "Kitchen") %} media_player.ai_1
          {% elif is_state("input_select.audio_media_device_select", "Computer") %} media_player.ai_4
          {% elif is_state("input_select.audio_media_device_select", "Hallway") %} media_player.ai_2
          {% endif %}
        attributes:
          friendly_name: "Selected Speaker Group"
          filter_key: "Music_Player"
          plexid: >-
            {% if is_state("input_select.audio_media_device_select", "Kitchen") %} media_player.plex_chromecast_ai_1
            {% elif is_state("input_select.audio_media_device_select", "Computer") %} media_player.plex_chromecast_ai_4
            {% elif is_state("input_select.audio_media_device_select", "Hallway") %} media_player.plex_chromecast_ai_2
            {% endif %}
          sourceid: >-
            {% if is_state("input_select.audio_media_device_select", "Kitchen") %} Ai.1
            {% elif is_state("input_select.audio_media_device_select", "Computer") %} Ai.4
            {% elif is_state("input_select.audio_media_device_select", "Hallway") %} Ai.2
            {% endif %}

script:
    music_control_play_music:
      alias: Dynamic Music - Play Music
      sequence:
      - choose:
        - conditions:
          - condition: state
            entity_id: input_select.audio_media_player_select
            state: Spotify
          sequence:
            - service: media_player.select_source
              data:
                entity_id: media_player.spotify_duc
                source: '{{ state_attr("sensor.template_selected_audio_media_device", "sourceid") }}'
            - service: media_player.shuffle_set
              data:
                shuffle: true
                entity_id: media_player.spotify_duc
            - service: media_player.play_media
              data:
                entity_id: media_player.spotify_duc
                media_content_type: playlist
                media_content_id: '{{ (states(''sensor.template_selected_spotify_playlist_uri'')) }}'

The PlexId is used to call Plex’s api to get the background image (disc cover) of the running song from plex.
Good luck and enjoy

Hi again. Since I only had amazon echo speakers and it is not compatible for streaming mp3 and what not, I’ve ordered a I2S DAC to use with an ESP32 for audio playback to a pc speaker I have via ESPhome.

I have got it working and am able to test playback for tts and local mp3 files. What I am having trouble is playback of streaming url link. These are the settings I have tried, but it doesn’t work. No error in esphome or HA log. For the media_content_type, I have tried playlist, music, etc.

esphome log:

service: media_player.play_media
data:
  media_content_id: http://ice-the.musicradio.com/HeartBedfordshire
  media_content_type: music
target:
  entity_id: media_player.razer_leviathan_player

I think the settings are correct. Why it isn’t working I have yet to figure out. As it stands, it doesn’t playback using external url. Internal and folder dir works. These 2 links below plays the same file, but using the local url works only. The later does not.

http://192.168.1.20:8123/local/alerts/en/minecraft-theme.mp3
https://hass.externaldomain.com/local/alerts/en/minecraft-theme.mp3

I have not really tried plex and I do have plex integration installed. What is the proper settings for media_content_id? I have tried this line using playlist as the media type. Again, no errors and playback.

plex://{ "library_name": "MUSIC", "playlist_name": "All Music", "shuffle":"1" }

I have read another post of some users having the same exact issue I am facing. I think using esphome route is limited. Any idea of what to look next for troubleshooting?

Hi Frederic, I’ve been setting up my Home Assistant for some weeks and manage to play with UI oriented items. In Yaml, however, I’m an absolute beginner. So, if I copy and paste this code, where do I begin? I get all kind of error messages, of which “Custom element doesn’t exist: button-card” (x12) seems to be the most urgent:-).

I forgot to install the button-card, obviously, hence the error. Still, after the install I get a bunch of other error messages. As said, I’m a Yaml-beginner, so I wouldn’t know where to change your code and adjust it to my specific situation.

Hi
Pretty difficult to help without the exact error messages.
At first, make sure your device and entities names are correct.
Check also if your integrations (Spotcast, Plex…) Work stand alone in simple on/off scripts.
Enjoy, finding out what doesn’t work is part of the fun (at least to me)

Hello,
Thanks a lot for sharing your project !
It’s really that i want to do with radio, youtube music and local plex music.

I have a question to start this project for my personal use.

How to do a multiple instance ?
Because if i’m right, it’s only for one person right ?
How to have this type of project for more than one person ?

Hi Frédéric,

Nice work !

Sorry for the newbie question.

But how can i install the full code package ? In configuration.yalm ?

Best regards

Frédéric

You could, but I personally work with packages as it is easier to manage.
You add the following in your configuration.yaml

homeassistant:
  packages: !include_dir_named packages

then you can create the packages subdirectory in your config directory.
in that packages subdirectory you can then create as many sub-directories and yaml files as you want (filename in small letters without space or special characters) that will be read along your configuration.yaml file

I guess you need to duplicate and change your credentials per version.
Not sure what you intend to do

1 Like