SpotifyPlus Integration

@witno
I know there is extra logic in the code for touch support, versus mouse support. This sounds like BOTH were detected, and it’s recognizing the touch screen (e.g. touch support) controls over the touchpad (e.g. mouse support).

Not sure there is a fix for that scenario, as some users prefer one over the other. I will look into it though - maybe it’s something I can add a configuration option for to prioritize which option it chooses when both are available?

For my needs it’s no problem. I’m mainly setting it up as part of a wall mounted tablet dashboard. And I don’t think it has anything to do with a bug in the card.
However i got me curious, so I tried to plug an external mouse into the MS Surface laptop. It turns out, that worked. But the touchpad dosn’t (not tapping it nor clicking). Suppose it’s different commands being sendt og hw malfunctin on the laptop.
Anyways. Card is SUPER!

1 Like

FYI - just released a new version of the SpotifyPlus integration

[ 1.0.65 ] - 2024/11/15

  • Updated underlying spotifywebapiPython package requirement to version 1.0.119.

I also released the SpotifyPlus Card Dashboard, which is a front-end UI to the SpotifyPlus integration media player. Check it out if you get some time.

2 Likes

Hey there Todd,

thanks for this awesome integration! Did you by chance ever try one of denons heos speakers? I guess they are similar to your bose soundtouchs. They are spotify connect only devices and seem to have a dynamic spotify device id. I just cant get them to respond reliably.
Everytime I leave home and use spotify within my car for example, home assistant completely loses the devices and the spotify integration only registers new spotify device ids, which are not translated to human readable names I gave them (e.g. kitchen, bathroom, etc).
As soon as I start to play some spotify songs on one of the speakers, the home assistant integration shows the correct names again.

I hoped, that SpotifyPlus would solve this and I think it really can.

I build a simple script that does the following:
action: spotifyplus.player_media_play_tracks
metadata: {}
data:
uris: spotify:playlist:4FhyjgN9pPxxxx
entity_id: media_player.spotifyplus_xxx
device_id: Badezimmer

However 9 out of 10 times I’m getting the following error. The 10th time it works flawlessly and the speaker starts playing:
Fehler: SAM0001E - An unhandled exception occured while processing method “GetInformation”. HTTPConnectionPool(host=‘192.168.1.32’, port=51952): Max retries exceeded with url: /zc/0?action=getInfo&version=2.7.1 (Caused by NewConnectionError(‘<urllib3.connection.HTTPConnection object at 0x7f4d98f710>: Failed to establish a new connection: [Errno 111] Connection refused’))

The good thing is: It seems to know, how to address the speaker, even if home assistants spotify integration doesnt. It correctly resolves its ip address.

Do you have any advice maybe?

@z3p337
Sorry, I do not have any experience with Denons heos speakers.

The Connection refused in the error message leads me to believe that it found the speaker at the IP address, but the Spotify Connect web service running on the speaker refused to reply to the request. This could mean a few different things: the speaker is busy servicing another request (cannot handle multiple requests); the speaker is in a deep sleep state; etc.

You might try calling the player_transfer_playback service to force activate the device, just in case it’s in sleep mode / dropped the connection due to inactivity (see example below).

Prior to calling the player_transfer_playback, you will want to make sure you have the following SpotifyPlus integration options set: LoginId, UserName, and Password. These options are required in order to re-activate a Spotify Connect device.

action: spotifyplus.player_transfer_playback
data:
  entity_id: media_player.spotifyplus_xxx
  device_id: Badezimmer
  play: true
  delay: 0.5
  refresh_device_list: true
  force_activate_device: true
2 Likes

Hi,

You meant the “SpotifyPlus: Get track recommendations” service?
Can you pls explain a little bit more how you use it? It sounds very interesting. Thank you!

Thanks so much for the quick support! :slight_smile: Tried it out. It behaves the same. Sometimes it works, sometimes it doesnt and throws the “connection refused” error.
Seems like 50:50 success rate actually.
I tried to open the speakers ip-address with the port stated in the error (eg http://badezimmer:51952) and get the same error. Chrome states “ERR_CONNECTION_REFUSED”.
The port that spotifyplus uses seems to be dynamic. Maybe denon heos only allows some of the used ports or something…
I’ll figure it out somehow! Thanks so much <3

1 Like

@z3p337
Check out the Spotify Connect Troubleshooting page for detailed instructions on how to find the port that the Denton HEOS speaker is using. This is basically the process that the SpotifyPlus player_transfer_playback uses to reconnect a device.

It could be that the speaker just needs time in its wake up / reconnect process. You might try increasing the “delay” argument from 0.5 to 7.0. This will cause the service to wait for 7 seconds instead of 500 milliseconds.

If that doesn’t work, then you will need to run some network traces (if you’re familiar with how to do that) to see how the Spotify desktop app is waking up the device. It involves tracing the Zeroconf / mDNS calls and responses.

yes, that’s the service i actually meant. I created a script for the creation of playlists with a few input fields/sliders for the different parameters of that service call, like for example the ‘seed_tracks’ or ‘max_speechiness’.

This script then gets called via an automation once a day with different settings for those parameters and automatically creates 4 playlists for different moods, for example a slow instrumenal only playlist based on a certain song.

2 Likes

Wow. Sounds nice! I hope you are happy to share the script. :grin:

sure! i realized afterwards that it would be a better approach to just delete/add tracks to the same playlists instead of recreating the whole playlist every time. Most of the script is also quite specific for my use case, but the parts with 'get track recommendations" and “add songs to playlist” should give you a good starter to create your own little playlist creator.

To make this more usable i also integrated a template which extracts the IDs from the given Spotify Playlists link(s), so it is possible to just paste the full song link into the field.

i used ai to remove some personal data in the script, just let me know if this broke something:

alias: Spotify | Create Playlist
sequence:
  - alias: Create Playlist
    sequence:
      - variables:
          mix_number: |-
            {%- if mix_title == 'Chill Mix 🌿' -%}
             1
            {%- elif mix_title == 'Happy Mix 🔆' -%}
             2
            {%- elif mix_title == 'Work Mix ☕' -%}
             3
            {%- elif mix_title == 'Psy Mix 🕉️' -%}
             4
            {%- endif -%}
        alias: Set mix_number
      - variables:
          ids_extracted: |-
            {%- set ids = '' -%} {%- for track in mix_tracks -%}
              {%- if track is string -%}
                {%- set url_parts = track.split('?')[0] -%}
                {%- if 'playlist/' in url_parts -%}
                  {%- set id = url_parts.split('playlist/')[1] -%}
                {%- elif 'track/' in url_parts -%}
                  {%- set id = url_parts.split('track/')[1] -%}
                {%- endif -%}
                {%- if id is defined -%}
                  {%- if not loop.first -%},{%- endif -%}
                  {{- id -}}
                {%- endif -%}
              {%- endif -%}
            {%- endfor -%}
        alias: Extract Spotify IDs
        enabled: true
      - action: input_text.set_value
        metadata: {}
        data:
          value: "{{ ids_extracted }}"
        target:
          entity_id: input_text.mix_{{ mix_number }}_playlist_tracks
        alias: Save Selected Tracks
      - action: spotifyplus.unfollow_playlist
        metadata: {}
        data:
          entity_id: media_player.your_spotifyplus_player
          playlist_id: >-
            {{ states('input_text.mix_' ~ mix_number ~ '_playlist_url') | 
            regex_replace(find='spotify:playlist:', replace='',
            ignorecase=False) }}
        alias: SpotifyPlus | Delete Playlist
        enabled: true
      - alias: SpotifyPlus | Get Songs
        action: spotifyplus.get_track_recommendations
        data:
          entity_id: media_player.your_spotifyplus_player
          limit: 50
          seed_tracks: |-
            {% if ids_extracted != '' %}
              {{ ids_extracted }}
            {% else %}
              {{ states('input_text.mix_' ~ mix_number ~ '_playlist_tracks') }}
            {% endif %}
          max_speechiness: "{{ max_speechiness | default (false) }}"
          min_popularity: "{{ min_popularity | default (false)  }}"
          min_liveness: "{{ min_liveness | default (false)  }}"
          min_instrumentalness: "{{ min_instrumentalness | default (false)  }}"
          min_energy: "{{ min_energy | default (false)  }}"
          min_acousticness: "{{ min_acousticness | default (false)  }}"
        enabled: true
        response_variable: track_recommendations
      - alias: SpotifyPlus | Create Playlist
        data:
          entity_id: media_player.your_spotifyplus_player
          name: "{{ mix_title }} | {{ now().strftime('%-d.%m.%y') }}"
          description: >-
            #{{ track_recommendations.result.tracks[0].artists[0].name }}, #{{
            track_recommendations.result.tracks[1].artists[0].name }}, #{{
            track_recommendations.result.tracks[2].artists[0].name }}, #{{
            track_recommendations.result.tracks[3].artists[0].name }}, #{{
            track_recommendations.result.tracks[4].artists[0].name }}, #MORE
            🧙‍♂️🪄 50 Songs, specially created for you on {{ now().strftime('%-d.%m.%y') }} at {{
            states('sensor.time') }}!
          public: false
          collaborative: false
          image_path: www/images/Playlists/mix_{{ mix_number }}.jpg
        action: spotifyplus.playlist_create
        response_variable: playlist
      - data:
          entity_id: media_player.your_spotifyplus_player
          playlist_id: >-
            {{ playlist.result.href |
            regex_replace(find='https://api.spotify.com/v1/playlists/',
            replace='', ignorecase=False) }}
          uris: >-
            {{ track_recommendations.result.tracks[0].uri }}, {{
            track_recommendations.result.tracks[1].uri }}, {{
            track_recommendations.result.tracks[2].uri }}, {{
            track_recommendations.result.tracks[3].uri }}, {{
            track_recommendations.result.tracks[4].uri }}, {{
            track_recommendations.result.tracks[5].uri }}, {{
            track_recommendations.result.tracks[6].uri }}, {{
            track_recommendations.result.tracks[7].uri }}, {{
            track_recommendations.result.tracks[8].uri }}, {{
            track_recommendations.result.tracks[9].uri }}, {{
            track_recommendations.result.tracks[10].uri }}, {{
            track_recommendations.result.tracks[11].uri }}, {{
            track_recommendations.result.tracks[12].uri }}, {{
            track_recommendations.result.tracks[13].uri }}, {{
            track_recommendations.result.tracks[14].uri }}, {{
            track_recommendations.result.tracks[15].uri }}, {{
            track_recommendations.result.tracks[16].uri }}, {{
            track_recommendations.result.tracks[17].uri }}, {{
            track_recommendations.result.tracks[18].uri }}, {{
            track_recommendations.result.tracks[19].uri }}, {{
            track_recommendations.result.tracks[20].uri }}, {{
            track_recommendations.result.tracks[21].uri }}, {{
            track_recommendations.result.tracks[22].uri }}, {{
            track_recommendations.result.tracks[23].uri }}, {{
            track_recommendations.result.tracks[24].uri }}, {{
            track_recommendations.result.tracks[25].uri }}, {{
            track_recommendations.result.tracks[26].uri }}, {{
            track_recommendations.result.tracks[27].uri }}, {{
            track_recommendations.result.tracks[28].uri }}, {{
            track_recommendations.result.tracks[29].uri }}, {{
            track_recommendations.result.tracks[30].uri }}, {{
            track_recommendations.result.tracks[31].uri }}, {{
            track_recommendations.result.tracks[32].uri }}, {{
            track_recommendations.result.tracks[33].uri }}, {{
            track_recommendations.result.tracks[34].uri }}, {{
            track_recommendations.result.tracks[35].uri }}, {{
            track_recommendations.result.tracks[36].uri }}, {{
            track_recommendations.result.tracks[37].uri }}, {{
            track_recommendations.result.tracks[38].uri }}, {{
            track_recommendations.result.tracks[39].uri }}, {{
            track_recommendations.result.tracks[40].uri }}, {{
            track_recommendations.result.tracks[41].uri }}, {{
            track_recommendations.result.tracks[42].uri }}, {{
            track_recommendations.result.tracks[43].uri }}, {{
            track_recommendations.result.tracks[44].uri }}, {{
            track_recommendations.result.tracks[45].uri }}, {{
            track_recommendations.result.tracks[46].uri }}, {{
            track_recommendations.result.tracks[47].uri }}, {{
            track_recommendations.result.tracks[48].uri }}, {{
            track_recommendations.result.tracks[49].uri }}
        action: spotifyplus.playlist_items_add
        enabled: true
        alias: SpotifyPlus | Add Songs to Playlist
      - action: input_text.set_value
        metadata: {}
        data:
          value: >-
            {{ playlist.result.href |
            regex_replace(find='https://api.spotify.com/v1/playlists/',
            replace='spotify:playlist:', ignorecase=False) }}
        target:
          entity_id: input_text.mix_{{ mix_number }}_playlist_url
        enabled: true
        alias: Playlist Sensor | Set Playlist URL
      - action: spotifyplus.playlist_cover_image_add
        metadata: {}
        data:
          playlist_id: >-
            {{ playlist.result.href |
            regex_replace(find='https://api.spotify.com/v1/playlists/',
            replace='', ignorecase=False) }}
          image_path: www/images/Playlists/mix_{{ mix_number }}.jpg
          entity_id: media_player.your_spotifyplus_player
        alias: SpotifyPlus | Add Cover Image
description: Create dynamic playlist with SpotifyPlus.
icon: mdi:spotify
fields:
  mix_title:
    selector:
      select:
        options:
          - Chill Mix 🌿
          - Happy Mix 🔆
          - Work Mix ☕
          - Psy Mix 🕉️
    name: Playlist Title
    required: true
    default: Chill Mix 🌿
  mix_tracks:
    selector:
      text:
        multiple: true
        multiline: false
    default: []
    name: "Insert Song Links:"
    required: true
  max_speechiness:
    name: Maximum Speechiness
    description: Maximum value for speech-like characteristics (0.0 to 1.0)
    selector:
      number:
        min: 0
        max: 1
        step: 0.1
  min_popularity:
    name: Minimum Popularity
    description: Minimum popularity rating (0 to 100)
    selector:
      number:
        min: 0
        max: 100
        step: 1
  min_liveness:
    name: Minimum Liveness
    description: Minimum liveness value (0.0 to 1.0)
    selector:
      number:
        min: 0
        max: 1
        step: 0.1
  min_instrumentalness:
    name: Minimum Instrumentalness
    description: Minimum instrumentalness value (0.0 to 1.0)
    selector:
      number:
        min: 0
        max: 1
        step: 0.1
  min_energy:
    name: Minimum Energy
    description: Minimum energy value (0.0 to 1.0)
    selector:
      number:
        min: 0
        max: 1
        step: 0.1
  min_acousticness:
    name: Minimum Acousticness 
    description: Minimum acousticness value (0.0 to 1.0)
    selector:
      number:
        min: 0
        max: 1
        step: 0.1
2 Likes

Cool. I will give it a try! Thank you very much for sharing!

@shpongledsummer
Just wanted to mention that it’s possible for the service to return less than 50 items in the tracks collection depending on the criteria specified. I mention it due to the hard coded array item references you are doing (eg result.tracks[n], etc). Another alternative is to use a loop.

You probably won’t have any issues if your criteria is pretty generic though.

Just wanted you to be aware though.

haha yes i know, my code is not very elegant and I should have mentioned that there will be an error if the search results is less than 50 tracks. which happens quite fast if the search parameters are too specific. thanks for pointing this out!

1 Like

Hi Todd, basically I’m Spotify addicted. I use it in my car, at work, phone+earplugs, and at home I have 3 Sonos (1 Play, 1 One, 1 One SL, 2 of them coupled) and a Linkplay WR320 connected to my A/V receiver. I’m playing with this amazing new toy-Spotify Plus, I was able to setup the new intergration.

In my dashboard I have 1 custom button card each room that I use both to start and transfer (before I used spotcast and media_player services, and with some workaround I could decently enjoy).

image

Here is my button template now:

...
  tap_action:
    haptic: medium
    service: spotifyplus.player_transfer_playback 
    service_data:
      entity_id: media_player.spotifyplus
      device_id: >
        [[[ return variables.spotify_id; ]]]
      play: true
      refresh_device_list: true
      force_activate_device: true

I have noticed 3 things:

  • When I start to play music on a device it awakes pretty quick with the transfer service (the devices stay paused if not used for a while so can awake without problems at the moment) and start playing, but my entity media_player.spotifyplus remains off, do I have to activate it manually prior to transfer or I am messing something?

  • Sometimes when I transfer, also from Sonos-to Sonos, I have weird behave and both devices keep playing.

  • I have a play/pause conditional button card that I can see only when my SpotifyPlus entity is not “off”, but I can’t use anymore the service media_player.play-pause because it’s not supported from the integration. I could do a template to choose which case is the player (pause or resume service), or do a feature request play/pause. Any advice?

Any tip how to manage my configuration in general?

@Sofa_Surfer

  • 1st thing
    The SpotifyPlus media player must be powered on in order for the media player UI to recognize it.
    You could add another service call prior to the player_transfer_playback (or create a script) to power on the player:

    action: media_player.turn_on
    target:
      entity_id: media_player.spotifyplus
    data: {}
    
  • 2nd thing
    see below for OAuth2 Token device support

  • 3rd thing
    I am about to release a SpotifyPlus v1.0.66 update, which will include the player_media_pause and player_media_resume services. I can add a player_media_play_pause service as well if that works better for you?

Have you setup the OAuth2 Token for TokenType=Authorization_Code Devices support for better support of Spotify Connect functionality on Sonos devices? If not, I highly recommend it as it provides better control of Sonos devices and corrects some of the Sonos Limitations related to Spotify Connect usage.

1 Like

FYI - just released a new version of the SpotifyPlus integration

Be sure to check out the SpotifyPlus Card Dashboard as well.

[ 1.0.66 ] - 2024/11/20

  • Added service get_playlist_cover_image to get the current image associated with a specific playlist.
  • Added service get_playlists_for_user to get a list of the playlists owned or followed by a Spotify user.
  • Added service player_media_pause to pause media play for the specified Spotify Connect device.
  • Added service player_media_resume to resume media play for the specified Spotify Connect device.
  • Added service player_media_skip_next to skip to next track in the user’s queue for the specified Spotify Connect device.
  • Added service player_media_skip_previous to skip to previous track in the user’s queue for the specified Spotify Connect device.
  • Updated service playlist_items_add to return a result property that contains a snapshot ID for the updated playlist.
  • Updated service playlist_items_clear to return a result property that contains a snapshot ID for the updated playlist.
  • Updated service playlist_items_remove to return a result property that contains a snapshot ID for the updated playlist.
  • Added service playlist_items_reorder to reorder items in a user’s playlist.
  • Added service playlist_items_replace to replace one or more items in a user’s playlist. Replacing items in a playlist will overwrite its existing items. This service can also be used to clear a playlist.
  • Added service check_users_following to check to see if the current user is following one or more users.
  • Updated service zeroconf_device_connect to add the delay argument.
  • Updated service zeroconf_device_disconnect to add the delay argument.
  • Updated underlying spotifywebapiPython package requirement to version 1.0.121.
1 Like

Thank you Todd! I’ve followed the OAuth2 Token procedure with your python script, everything looked fine and it works better. the transfer can take up to 7-8 seconds, and sometimes it still get messed, but honestly also the App on iphone sometimes it take ages to transfer or drop the attempt to connect to the next speaker.
I have also noticed that the media_player.spotifyplus entity updates every 30secs, unless the player move to the next track for example.
I’ll add the turn on service on my button, and if on next release you could add a play-pause service I think it could be useful for everyone.
Thank you again! 66 releases in 7 month it’s a shxt lot of work!!

I’ll check the card now…

@Sofa_Surfer
I have noticed the same behavior when working with my Sonos device. I wish Sonos had done a better job implementing the Spotify Connect protocol in their devices. As it stands, it’s considered a “restricted device” by the Spotify Web API … translation, I have to do a LOT of funky coding to get it to work like the other manufacturers that implemented the Spotify Connect protocol properly! LOL

The 30 second delay is normal when you initiate player commands outside of HA using the Spotify apps (desktop, mobile, web, etc). Spotify Web API requires device polling to retrieve player status updates, and the polling “window” is 30 seconds. Note that it’s near real-time when you control the Spotify player via Home Assistant UI.

Regarding the play / pause, you should just be able to call the stock HA media_player.media_play_pause service. It will call the appropriate play / pause method based on the self.state == MediaPlayerState.PLAYING status.

Example:

action: media_player.media_play_pause
data: {}
target:
  entity_id: media_player.spotifyplus_todd_l

I can’t recall if I mentioned it to you or not, but I recently released the SpotifyPlus Card Dashboard custom card, which is a media player / spotify search UI for the SpotifyPlus Integration. Check it out if you’re a Spotify junkie like me. :smiley:

2 Likes

@z3p337
Just checking in to see if you made any progress with the Spotify Connect Denon HEOS connectivity.

Are you familiar with the Wireshark network trace tool? If so, it might be worth a shot to analyze some traces between the Spotify Desktop player and the Denon HEOS. I helped another guy figure out a connectivity issue that way for his Roku TCL device.

Just throwing it out there in case you are still trying to resolve the issue.