Spotify search

I would like the ability to search spotify and select a device to play music from within HA. Most demos I see show playlists and not the ability to search.

Can I achieve this by leveraging something like forked daapd, SnapCast, Squeezebox or Mopidy?

The ha squeezebox integration supports search, but I haven’t tried it with spotify.

Yea, I see you can post JSON to Squeezebox through the integration to search. As you stated, not sure that will work with the Spotify integration. You would also need to parse the JSON and display into in a manner that would allow action such as playing the track to occur.

That sort of thing is why it is so much easier to use spotify on your phone and play to lms from there.

I’m just a fan of one interface, not 50. :slight_smile:

1 Like

Me too, develop away :slight_smile:

Look into the spotcast integration. I have built my own spotify player using that. It let you search spotify and then it plays the closest result (song, playlist or artist).

1 Like

Does it let you select a song from the search results or just select the closest match?

No unfortuanly not. Closets match. But i works good. With spotcast you can also start playlist on and transfer music between different media players.

4 Likes

Would you mind sharing this config too… looks awesome

I second that, it’s beautiful man.

Hmm yes i can share it but it is not completely finsihed yet and it depends on alot of scripts. I will share it when it is completed :slight_smile:

2 Likes

any news on that code, @danieljarhult? I don’t mean to be pushy, but what you did is exactly what I’m looking for. Could you share the specific part you use for the search even if it is imcomplete?

I dont work on this project anymore, i figure it is way more easy to just opens up the spotify app :smiley:
but i can give you what i have i guess :stuck_out_tongue:

You will need to create one input.text for the search field. I also use two input booleans to switch between to different rooms/players. I will give you all the code but i cant give any support right now.

the player itself

type: vertical-stack
cards:
  - type: custom:button-card
    icon: ios:magnifyingglass-circle
    triggers_update: all
    tap_action:
      action: call-service
      service: script.turn_on
      service_data:
        entity_id: script.1641817924904
    show_icon: true
    styles:
      card:
        - background: transparent
        - box-shadow: none
        - height: 75px
      icon:
        - color: white
        - width: 23px
        - left: 140px
        - top: 7px
        - position: absolute
      custom_fields:
        search:
          - width: 100px
          - left: '-48px'
          - top: '-5px'
          - position: absolute
        denon:
          - width: 130px
          - left: 173px
          - top: 8px
          - position: absolute
        nest:
          - width: 150px
          - left: 285px
          - top: 8px
          - position: absolute
    custom_fields:
      search:
        card:
          type: entities
          style: |
            ha-card {
              background: transparent;
              box-shadow: none; 
            }
          entities:
            - entity: input_text.text1
              icon: none
              style:
                hui-generic-entity-row:
                  $: |
                    .info {
                     display: none;
                    }
                  paper-input:
                    $: |
                      paper-input-container iron-input {
                        --paper-input-container-shared-input-style_-_width: 120px;
                      }
                      paper-input-container iron-input {
                        color: white;
                      }
      denon:
        card:
          type: custom:button-card
          show_icon: false
          show_name: true
          entity: input_boolean.dummy_button5
          tap_action:
            action: call-service
            service: input_boolean.turn_on
            service_data:
              entity_id: input_boolean.dummy_button5
          layout: icon_name_state2nd
          name: Vardagsrum
          show_state: true
          state_display: |
            [[[
              if (states["media_player.denon_2"].state == "playing")
                return 'Spelar';
              else if (states["media_player.denon_2"].state == "paused")
                return 'Pausad';
              else if (states["media_player.denon_2"].state == "idle")
                return 'Denon';
              else
                return 'Denon';
            ]]]
          show_entity_picture: true
          entity_picture: /local/speaker1.png/
          styles:
            card:
              - border-radius: 0px
              - box-shadow: none
              - background: transparent
            icon:
              - width: 23px
            img_cell:
              - background: transparent
              - width: 40px
              - height: 40px
              - border-radius: 50%
            name:
              - font-size: 13px
              - z-index: 2
              - justify-self: start
              - left: 50%
              - color: rgb(255,255,255,0.5)
            state:
              - font-size: 13px
              - justify-self: start
              - z-index: 2
              - color: rgb(255,255,255,0.8)
          state:
            - value: 'on'
              styles:
                img_cell:
                  - background: rgb(0,0,0,0.3)
      nest:
        card:
          type: custom:button-card
          show_icon: false
          show_name: true
          entity: input_boolean.dummy_button6
          tap_action:
            action: call-service
            service: input_boolean.turn_on
            service_data:
              entity_id: input_boolean.dummy_button6
          layout: icon_name_state2nd
          state_display: |
            [[[
              if (states["media_player.kok"].state == "playing")
                return 'Spelar';
              else if (states["media_player.kok"].state == "paused")
                return 'Pausad';
              else if (states["media_player.kok"].state == "idle")
                return 'Vilar';
             else
                return 'Nest Hub';
            ]]]
          name: Kök
          show_state: true
          show_entity_picture: true
          entity_picture: /local/tablet1.png/
          styles:
            card:
              - border-radius: 0px
              - box-shadow: none
              - background: transparent
            icon:
              - width: 23px
            img_cell:
              - background: transparent
              - width: 40px
              - height: 40px
              - border-radius: 50%
            name:
              - font-size: 13px
              - justify-self: start
              - z-index: 2
              - left: 50%
              - color: rgb(255,255,255,0.5)
            state:
              - font-size: 13px
              - justify-self: start
              - z-index: 2
              - color: rgb(255,255,255,0.8)
          state:
            - value: 'on'
              styles:
                img_cell:
                  - background: rgb(0,0,0,0.3)
  - type: custom:button-card
    triggers_update: all
    view_layout:
      grid-area: sidebar
    show_icon: false
    show_name: true
    name: |
      [[[
        if (states["media_player.spotify_daniel"].state != "idle")
          return states['media_player.spotify_daniel'].attributes.media_artist + " - " + states['media_player.spotify_daniel'].attributes.media_title;
        else if (states["media_player.spotify_daniel"].state == "idle")
          return "";
      ]]]
    show_state: false
    entity: media_player.spotify_daniel
    styles:
      card:
        - height: 340px
        - min-width: 300px
        - border-radius: 0px
        - box-shadow: none
        - background: transparent
      state:
        - font-size: 100px
        - position: absolute
        - top: 40px
        - font-weight: 300
        - transform: translate(-50%, 0%)
        - z-index: 2
        - left: 50%
        - color: rgb(255,255,255,0.9)
        - text-shadow: 2px 2px 3px rgb(0,0,0,0.2)
      name:
        - font-size: 15px
        - position: absolute
        - top: 310px
        - transform: translate(-50%, 0%)
        - z-index: 2
        - left: 50%
        - color: rgb(255,255,255,0.9)
        - text-shadow: 1px 1px 1px rgb(0,0,0,0.4)
      custom_fields:
        play:
          - z-index: 2
          - position: absolute
          - top: 10px
          - left: 60px
        backward:
          - z-index: 2
          - position: absolute
          - top: 10px
          - left: 10px
        forward:
          - z-index: 2
          - position: absolute
          - top: 10px
          - left: 110px
        slider:
          - position: absolute
          - left: 180px
          - top: '-10px'
          - height: 20px
          - width: 50%
        vol:
          - z-index: 2
          - position: absolute
          - top: 13px
          - left: 170px
        shuffle:
          - z-index: 2
          - position: absolute
          - top: 13px
          - right: 4%
        media_image:
          - z-index: 1
          - transform: translate(-50%, 0%)
          - top: 85px
          - left: 50%
          - width: 200px
          - height: 200px
          - box-shadow: 0px 0px 20px rgb(0,0,0,0.5)
          - position: absolute
          - border-radius: 50%
          - background-size: cover
          - background-image: |
              [[[ 
                return states['media_player.spotify_daniel'].attributes.entity_picture === undefined ? 'url("/local/bg1.jpg")' : `url(${states["media_player.spotify_daniel"].attributes.entity_picture})`;
              ]]]
    custom_fields:
      media_image: |
        <div> </div>
      play:
        card:
          type: custom:button-card
          tap_action:
            action: call-service
            service: media_player.media_play_pause
            service_data:
              entity_id: media_player.spotify_daniel
          show_entity_picture: true
          entity_picture: |
            [[[
             if (states["media_player.spotify_daniel"].state == "playing")
                   return '/local/play.png';
             else if (states["media_player.spotify_daniel"].state == "paused")
               return '/local/pause1.png';
             else
               return '/local/play.png';
            ]]]
          styles:
            card:
              - background: transparent
              - box-shadow: none
            icon:
              - width: 25px
      forward:
        card:
          type: custom:button-card
          tap_action:
            action: call-service
            service: media_player.media_next_track
            service_data:
              entity_id: media_player.spotify_daniel
          show_entity_picture: true
          entity_picture: /local/forward.png
          styles:
            card:
              - background: transparent
              - box-shadow: none
            icon:
              - width: 25px
      backward:
        card:
          type: custom:button-card
          tap_action:
            action: call-service
            service: media_player.media_previous_track
            service_data:
              entity_id: media_player.spotify_daniel
          show_entity_picture: true
          entity_picture: /local/rewind.png
          styles:
            card:
              - background: transparent
              - box-shadow: none
            icon:
              - width: 25px
      vol:
        card:
          type: custom:button-card
          show_entity_picture: true
          entity_picture: /local/volume.png
          tap_action:
            action: none
          styles:
            card:
              - background: transparent
              - box-shadow: none
            icon:
              - width: 20px
      shuffle:
        card:
          type: custom:button-card
          entity: input_boolean.dummy_button6
          show_entity_picture: true
          show_name: false
          entity_picture: |
            [[[
              if (states["media_player.spotify_daniel"].attributes.shuffle == false)
                return '/local/shuffleoff.png';
              else if (states["media_player.spotify_daniel"].attributes.shuffle == true)
                return '/local/shuffle.png';
            ]]]
          tap_action:
            action: toggle
          styles:
            card:
              - background: transparent
              - box-shadow: none
            icon:
              - width: 20px
      slider:
        card:
          type: entities
          style: |
            ha-card {
            --ha-card-background: transparent;
            box-shadow: none; 
            border-radius: 10px;}
            }
          entities:
            - type: custom:slider-entity-row
              entity: media_player.spotify_daniel
              full_row: true
              hide_state: true
              card_mod:
                style:
                  ha-slider$ paper-progress$: |
                    div#progressContainer {
                      background-image: linear-gradient(to right,rgb(0,0,0),rgb(0,5,5),rgb(255,255,255));
                      --paper-progress-active-color: none;
                    }
  - type: custom:swipe-card
    parameters:
      grabCursor: true
      centeredSlides: true
      slidesPerView: auto
      pagination:
        type: bullets
    cards:
      - type: horizontal-stack
        cards:
          - type: picture
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            image: local/time.jpg
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.spotifytimecapsule
            hold_action:
              action: none
          - type: picture
            image: local/repeat.jpg
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.spotifyrepeat
            hold_action:
              action: none
          - type: picture
            image: local/daily.jpg
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.spotifydaily1
            hold_action:
              action: none
          - type: picture
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            image: local/grunge.jpg
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.1641907780998
            hold_action:
              action: none
      - type: horizontal-stack
        cards:
          - type: picture
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            image: local/onehit.jpg
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.spotifyonehit
            hold_action:
              action: none
          - type: picture
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            image: local/pearl.jpg
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.1641909838007
            hold_action:
              action: none
          - type: picture
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            image: local/fleet.jpg
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.spotifyfleetwood
            hold_action:
              action: none
          - type: picture
            style: |
              ha-card {
                border-radius: 15px;
                box-shadow: none;
                }
            image: local/john.jpg
            tap_action:
              action: call-service
              service: script.turn_on
              service_data:
                entity_id: script.spotifyjohnmayer
            hold_action:
              action: none

Search script:

alias: shuffle
sequence:
  - service: media_player.shuffle_set
    target:
      entity_id: media_player.spotify_daniel
    data:
      shuffle: true
mode: single

Shuffle off script:

alias: shuffleoff
sequence:
  - service: media_player.shuffle_set
    target:
      entity_id: media_player.spotify_daniel
    data:
      shuffle: false
mode: single

Move sound to kitchen script

alias: movespotifytokok
sequence:
  - service: spotcast.start
    data:
      force_playback: true
      start_volume: 20
      device_name: Kök
mode: single

Move sound to kitchen automation:

alias: movetokok
description: ''
trigger:
  - platform: state
    entity_id: input_boolean.dummy_button6
    to: 'on'
condition:
  - condition: state
    entity_id: media_player.spotify_daniel
    state: playing
action:
  - service: script.turn_on
    target:
      entity_id: script.movespotify
    data: {}
mode: single

Start playlist on the device you have choosed in the player:

alias: spotifyonehit
sequence:
  - service: spotcast.start
    data:
      uri: >-
        https://open.spotify.com/playlist/37i9dQZF1DX0Ew6u9sRtTY?si=81261d91ac4f412c
      device_name: >-
        {% if is_state('input_boolean.dummy_button5', 'on') %}Denon{% elif
        is_state('input_boolean.dummy_button6', 'on')%}Kök{% endif %}
mode: single

and some automations to turn on and off the players

alias: input5turnsoffinput6
description: ''
trigger:
  - platform: state
    entity_id: input_boolean.dummy_button5
    to: 'on'
condition: []
action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.dummy_button6
    data: {}
mode: single
alias: input5turnsoffinput6
description: ''
trigger:
  - platform: state
    entity_id: input_boolean.dummy_button5
    to: 'on'
condition: []
action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.dummy_button6
    data: {}
mode: single
alias: input5turnsoffinput6 
description: ''
mode: single
trigger:
  - platform: state
    entity_id: input_boolean.dummy_button5
    to: 'on'
condition: []
action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.dummy_button6
    data: {}

alias: input6turnsoffinput5
description: ''
trigger:
  - platform: state
    entity_id: input_boolean.dummy_button6
    to: 'on'
condition: []
action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.dummy_button5
    data: {}
mode: single

See above :+1:

@danieljarhult Thank you so much! I’ve had a couple of crazy weeks at work but I’m looking at some days off ahead, so I’ll see what I can do, although you’re absolutely right that it is in fact easier to just open up Spotify, also, I ended up installing LSM add-on and a handful of squeezeplayers on corepis for multiroom, they work great and the LMS spotty add-on does include searching. I thought you might want to take a look into that too.

Cheers!

@mrsaatci @danieljarhult @chezpaul2 @nickrout
I know it’s been awhile, but in case you guys are still looking for Spotify search capability in Home Assistant …

I have developed a new SpotifyPlus Card Home Assistant custom component dashboard that adds extended support for Spotify premium account users.

This card utilizes services and features unique to the SpotifyPlus custom integration.

Note - this card will NOT work with the built-in HA Spotify integration; the SpotifyPlus custom integration is required.

Features

  • Spotify Media player interface with customizable controls and information display.
  • Search Spotify catalog for all media types (tracks, playlists, albums, artists, shows, audiobooks, episodes, categories, etc).
  • Display / Select your Spotify favorites: Albums, Artists, Audiobooks, Episodes, Shows, Tracks.
  • Display / Select Spotify Connect device outputs.
  • User-defined media item presets (both file and code edited supported).
  • User-defined recommendation presets; play dynamically generated content based on user-defined criteria (e.g. energy, loudness, danceability, etc).
  • Favorite status / add / remove support for all media types.
  • View Player Queue information.
  • Card Configuration Editor User-Interface for changing options.

More Information

Check out the following links for more information:

3 Likes

Well Done!
Great Card :slight_smile:

1 Like