thlucas1 / homeassistantcomponent_spotifyplus

Home Assistant integration for Spotify Player control, services, and soundtouchplus integration support.
MIT License
45 stars 3 forks source link

No longer able to activate any of my Spotify Connect devices #38

Open milnivlek opened 3 weeks ago

milnivlek commented 3 weeks ago

System Health details

System Information

version core-2024.7.1
installation_type Home Assistant Container
dev false
hassio false
docker true
user root
virtualenv false
python_version 3.12.4
os_name Linux
os_version 6.6.31+rpt-rpi-v8
arch aarch64
timezone America/Los_Angeles
config_dir /config
Home Assistant Community Store GitHub API | ok -- | -- GitHub Content | ok GitHub Web | ok GitHub API Calls Remaining | 4982 Installed Version | 1.34.0 Stage | running Available Repositories | 1392 Downloaded Repositories | 36 HACS Data | ok
AccuWeather can_reach_server | ok -- | -- remaining_requests | 12
Home Assistant Cloud logged_in | false -- | -- can_reach_cert_server | ok can_reach_cloud_auth | ok can_reach_cloud | ok
Dashboards dashboards | 3 -- | -- resources | 26 views | 5 mode | storage
Recorder oldest_recorder_run | August 16, 2024 at 5:16 AM -- | -- current_recorder_run | August 17, 2024 at 7:11 PM estimated_db_size | 237.19 MiB database_engine | sqlite database_version | 3.45.3
SpotifyPlus integration_version | v1.0.49 -- | -- clients_configured | 1: K&M (premium) api_endpoint_reachable | ok

Checklist

Describe the issue

Since a few days ago, all my scripts stopped being able to activate my Spotify Connect devices.

Specifically, when I call this:

service: spotifyplus.player_resolve_device_id
data:
  device_value: 1st Floor Speakers
  entity_id: media_player.spotifyplus
  verify_user_context: true

I get the expected response with a device ID, e.g.

result: 26195de030cfc972b3435a8c25026022988eb5e3

However, this device ID doesn't actually work. When I attempt to start a playback with this device ID, it doesn't recognize the device ID. Instead, I get this error:

Player command failed: No active device found

Moreover, when I go to https://developer.spotify.com/documentation/web-api/reference/get-a-users-available-devices and run the Get Available Devices API request, I don't see my device in the returned list. It looks like player_resolve_device_id didn't login my account on the device at all.

I also tried calling spotifyplus.get_spotify_connect_device instead (with verify_user_context: true), but the outcome was the same. I even tried calling spotifyplus.zeroconf_device_connect directly with all the discovered device details. Still no luck.

I checked my Spotify Connect username and password in the SpotifyPlus integration options, and they are still correct. I confirmed that I can still log in via the Spotify desktop web UI with those same credentials. It's just not working through SpotifyPlus.

Not sure if this is relevant, but I did some web searches and noticed this thread on the librespot project which indicated that Spotify just stopped supporting username/password based authentication for ZeroConf: https://github.com/librespot-org/librespot/issues/1308#issuecomment-2276196094. Could SpotifyPlus be affected by the same issue?

Reproduction steps

See above write-up for steps.

Debug logs

n/a

Diagnostics dump

No response

thlucas1 commented 3 weeks ago

@milnivlek I do believe it is related to Spotify making some changes to their login processes, which is also affecting librespot. But before we go there, let's see what is registered in ZeroConf.

What do you see when you issue the following ZeroConf mDNS query from a DOS prompt / terminal window? dns-sd -B _spotify-connect._tcp. local.

Should look something like this (me environment example), listing all of your Spotify Connect devices that are known to Zeroconf:

C:\Users\thluc>dns-sd -B _spotify-connect._tcp. local.
Browsing for _spotify-connect._tcp..local.
Timestamp     A/R Flags if Domain                    Service Type              Instance Name
13:54:33.369  Add     3  5 local.                    _spotify-connect._tcp.    Bose-ST300
13:54:33.369  Add     3  5 local.                    _spotify-connect._tcp.    Bose-ST10-1 + Bose-ST10-2 (L+R)
13:54:33.369  Add     3  5 local.                    _spotify-connect._tcp.    Bose-ST10-2
13:54:33.369  Add     2  5 local.                    _spotify-connect._tcp.    Bose-ST10-1
13:54:33.417  Add     2  5 local.                    _spotify-connect._tcp.    HAVM-SpotifyConnect

Also, do you have the Spotify LoginId value set (as well as userid and password) in your SpotifyPlus configuration options?

milnivlek commented 3 weeks ago

Thanks thlucas1 for your quick follow-up.

Here's what I see when I ran the command you gave:

$ dns-sd -B _spotify-connect._tcp. local.
Browsing for _spotify-connect._tcp..local.
DATE: ---Sat 17 Aug 2024---
22:50:55.202  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Attic Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Master Bedroom Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Kitchen Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Living Room Soundbar
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Dining Room Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. All Speakers
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Dining & Kitchen
22:50:55.328  Add        2  15 local.               _spotify-connect._tcp. 1st Floor Speakers
22:50:55.540  Add        2  15 local.               _spotify-connect._tcp. f84a989346fb84a24fa545fde1a3102645d1ffca
22:50:55.598  Add        2  15 local.               _spotify-connect._tcp. cde82ced6ff067791e7bf8e70a58e9e40092887b
22:50:56.165  Add        2  15 local.               _spotify-connect._tcp. SpZc-A6E9C4

And yes, I do have all these fields set: canonical loginid, username, password.

Hope this helps!

thlucas1 commented 3 weeks ago

I just realized from this discussion thread that you are utilizing SnapCast to control your devices. I believe there are still ongoing librespot issues related to Spotify Authentication that is preventing Spotify Connect from "seeing" your devices as noted in this librespot issue thread. Not sure on that though - you might reach out to the librespot folks to confirm. Note that SpotifyPlus does not use librespot or SpotCast server to do its thing.

With that said ...

Questions: Are these ChromeCast / GoogleCast devices? What do you get for the following service call:

action: spotifyplus.get_spotify_connect_device
data:
  entity_id: media_player.spotifyplus
  device_value: "1st Floor Speakers"
  refresh_device_list: true
  activate_device: false

Should look something like this (note these results are for one of my Bose SoundTouch devices):

user_profile:
  country: US
  display_name: Todd L
  email: thl... redacted ...
  id: 31l77... redacted ...
  product: premium
  type: user
  uri: spotify:user:31l77... redacted ...
result:
  Id: 30fbc80e35598f3c242f2120413c943dfd9715fe
  Name: Bose-ST10-1
  WasReConnected: false
  DeviceInfo:
    SpotifyError: 0
    Status: 101
    StatusString: OK
    ResponseSource: null
    AccountReq: DONTCARE
    ActiveUser: ... redacted ...
    Aliases: []
    Availability: ""
    BrandDisplayName: Bose
    ClientId: 79ebcb21... redacted ...
    DeviceId: 30fbc80e35598f3c242f2120413c943dfd9715fe
    DeviceType: SPEAKER
    GroupStatus: NONE
    LibraryVersion: 3.88.29-gc4d4bb01
    ModelDisplayName: Soundtouch
    ProductId: 70001
    PublicKey: eDzNVMgBm... redacted ...
    RemoteName: Bose-ST10-1
    ResolverVersion: "0"
    Scope: streaming
    SupportedCapabilities: null
    SupportedDrmMediaFormats: []
    TokenType: accesstoken
    Version: 2.7.1
    VoiceSupport: "YES"
    IsActiveDevice: false
    IsInDeviceList: true
  DiscoveryResult:
    DeviceName: Bose-ST10-1
    Domain: .local
    HostIpAddress: 192.168.1.81
    HostIpAddresses:
      - 192.168.1.81
    HostIpPort: 8200
    HostTTL: 120
    Key: bose-st10-1._spotify-connect._tcp.local.
    Name: Bose-ST10-1._spotify-connect._tcp.local.
    Priority: 0
    OtherTTL: 4500
    Server: Bose-SM2-341513fbeeae.local.
    ServerKey: bose-sm2-341513fbeeae.local.
    ServiceType: _spotify-connect._tcp.local.
    Weight: 0
    Properties:
      - Name: CPath
        Value: /zc
      - Name: VERSION
        Value: "1.0"
    SpotifyConnectCPath: /zc
    SpotifyConnectIsInDeviceList: false
    SpotifyConnectVersion: "1.0"
    ZeroconfApiEndpointAddUser: http://192.168.1.81:8200/zc?action=addUser&version=1.0
    ZeroconfApiEndpointGetInformation: http://192.168.1.81:8200/zc?action=getInfo&version=1.0
    ZeroconfApiEndpointResetUsers: http://192.168.1.81:8200/zc?action=resetUsers&version=1.0
milnivlek commented 3 weeks ago

Oops, I realized I made a typo in that other discussion thread. I use Snapcast, not Spotcast. In short, my Spotify Connect devices are Snapcast services that represent various groupings of my actual physical speakers. It's like Sonos groups, basically. But I do my playback via buttons on the Home Assistant UI using your SpotifyPlus integration. Or via the Spotify app (which has always worked fine with my Snapcast devices).

Sorry for the confusion. I just corrected my typo in the other thread.

As for your other question: here's the output of get_spotify_connect_device:

user_profile:
  country: US
  display_name: xxxxxxx
  email: xxxxxxx
  id: xxxxxxx
  product: premium
  type: user
  uri: spotify:user:xxxxxxx
result:
  Id: 26195de030cfc972b3435a8c25026022988eb5e3
  Name: 1st Floor Speakers
  WasReConnected: false
  DeviceInfo:
    SpotifyError: 0
    Status: 101
    StatusString: ERROR-OK
    ResponseSource: null
    AccountReq: PREMIUM
    ActiveUser: ""
    Aliases: []
    Availability: ""
    BrandDisplayName: librespot
    ClientId: null
    DeviceId: 26195de030cfc972b3435a8c25026022988eb5e3
    DeviceType: Speaker
    GroupStatus: NONE
    LibraryVersion: 0.4.2
    ModelDisplayName: librespot
    ProductId: null
    PublicKey: xxxxxxx
    RemoteName: 1st Floor Speakers
    ResolverVersion: "0"
    Scope: null
    SupportedCapabilities: null
    SupportedDrmMediaFormats: []
    TokenType: null
    Version: 2.7.1
    VoiceSupport: "NO"
    IsActiveDevice: true
    IsInDeviceList: true
  DiscoveryResult:
    DeviceName: 1st Floor Speakers
    Domain: .local
    HostIpAddress: 192.168.1.43
    HostIpAddresses:
      - 172.18.0.1
      - 172.17.0.1
      - 192.168.1.43
    HostIpPort: 34723
    HostTTL: 120
    Key: 1st floor speakers._spotify-connect._tcp.local.
    Name: 1st Floor Speakers._spotify-connect._tcp.local.
    Priority: 0
    OtherTTL: 4500
    Server: home.local.
    ServerKey: home.local.
    ServiceType: _spotify-connect._tcp.local.
    Weight: 0
    Properties:
      - Name: VERSION
        Value: "1.0"
      - Name: CPath
        Value: /
    SpotifyConnectCPath: /
    SpotifyConnectIsInDeviceList: false
    SpotifyConnectVersion: "1.0"
    ZeroconfApiEndpointAddUser: http://192.168.1.43:34723/?action=addUser&version=1.0
    ZeroconfApiEndpointGetInformation: http://192.168.1.43:34723/?action=getInfo&version=1.0
    ZeroconfApiEndpointResetUsers: http://192.168.1.43:34723/?action=resetUsers&version=1.0
thlucas1 commented 3 weeks ago

The ModelDisplayName: librespot indicates that these Spotify Connect device entries are being defined by librespot. I was hoping the device itself was running the Spotify Connect Zeroconf interface, but it does not appear that is the case. Basically, librespot has registered the device to Spotify Connect Zeroconf, so librespot will control when / how the device connects to the Spotify backend to play content.

Are you seeing any of the following messages in your HA System Log? or in a librespot log maybe? ERROR librespot] Connection failed: Login failed with reason: Bad credentials These errors are mentioned in a librespot Authentication Issues thread thread.

There is also this issue on the SnapCast issues page that talks about Bad Credentials errors (due to underlying librespot functionality).

At this point you probably need to reach out to either the librespot team or SnapCast team to troubleshoot why the devices are able to connect. Once you figure that out, the SpotifyPlus and Spotify integrations should start working again.

Wish I could help more, but I don't use / know librespot functionality.

milnivlek commented 3 weeks ago

I really appreciate you trying your best to help, thlucas1. Thanks for all your hard work in supporting me and others.

One thing to clarify though: Snapcast/librespot provides two login mechanisms:

I can still use the Spotify app to switch between different accounts on my Snapcast device. So the device itself is able to login with the credentials provided by the Spotify app. This gives me hope that this can be fixed solely on the client side.

While I'm obviously not an expert (unlike you), I suspect the key issue here is that your Spotify API client library is constructing the addUser credentials blob using the username and password: https://github.com/thlucas1/SpotifyWebApiPython/blob/57143f66d5cb5cb7ac17beaf7fb41a288a4dcb6c/spotifywebapipython/zeroconfapi/zeroconfconnect.py#L548-L549. This is the part that's no longer supported by Spotify, I think(?). Instead, I think you now need to construct the credentials blob differently--probably using some output from the OAuth process. Unfortunately I have no idea what this other blob format needs to be. But maybe that's the part you need to figure out...?

Just out of curiosity, are you still able to login on your own Spotify Connect device? That is, if you switch your device to another user (e.g. via the Spotify app), is SpotifyPlus able to switch it back to the original user?

Thank you once again!

milnivlek commented 3 weeks ago

Actually - I just realized that you recently implemented sending the OAuth token in addUser for Sonos devices. Is there any way I can try enabling that for my device too?

EDIT: Just to test it out, I made a local change to disable the condition on this line. I went through your instructions for generating the token file, and copied it to ./config/.storage/spotifyplus_tokens.json. However, I keep getting this error:

Error: Spotify Desktop Client Application access token was not authorized

even though I supposedly do have a valid token:

# cat /config/.storage/spotifyplus_tokens.json
{
    "SpotifyWebApiAuthCodePkce/65b708073fc0480ea92a077233ca87bd/<<redacted>>": {
        "access_token": "BQA_qXg<<redacted>>",
        "expires_at": 1724140428.8688881,
        "expires_in": 3600,
        "refresh_token": "AQCAwVcg<<redacted>>",
        "scope": [
            "streaming"
        ],
        "token_type": "Bearer"
    }
}

Not sure what I did wrong here. I can't use Smart Inspect since I don't have a Windows machine (only Mac and Linux).

Oh well. Unfortunately I'll be out of town starting tomorrow, so I won't be able to try anything else for a bit. Hopefully you can reproduce the problem too on your end.

thlucas1 commented 3 weeks ago

Do you have the latest v1.0.52 version installed? I had to move the token cache file to the ./config/.storage/spotifyplus_tokens.json folder with that release, as it used to be under the /config/custom_components/spotifyplus/data/tokens.json location and was getting deleted when the SpotifyPlus version was upgraded. In other words, v1.0.51 and prior is still looking in the old location for the token cache file. To be sure, I would upgrade to the latest SpotifyPlus version, which should then find your token in the ./config/.storage/spotifyplus_tokens.json folder.

My Bose SoundTouch devices still function fine using the user-id and password credentials, but I believe that is because the Bose cloud is doing something to convert them into Zeroconf credentials. No problems yet with those devices. My Sonos Symfonisk is working fine with the SpotifyPlus OAuth2 token logic. The Spotify Web and Mobile players of course work fine - I believe they are using the same OAuth2 process to login to the devices with. In fact, the AuthTokenGenerator.py script that you ran to create the token simply specifies the Spotify Desktop application client id and prompts you to authorize that client id for your Spotify user-id.

If you have Sonos devices, then you should be able to control them directly from SpotifyPlus without the need for SnapCast. I am not sure how that works though in your case, as I did not see any sonosRINCON... entries in your Zeroconf mDNS output from earlier:

$ dns-sd -B _spotify-connect._tcp. local.
Browsing for _spotify-connect._tcp..local.
DATE: ---Sat 17 Aug 2024---
22:50:55.202  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Attic Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Master Bedroom Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Kitchen Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Living Room Soundbar
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Dining Room Speaker
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. All Speakers
22:50:55.328  Add        3  15 local.               _spotify-connect._tcp. Dining & Kitchen
22:50:55.328  Add        2  15 local.               _spotify-connect._tcp. 1st Floor Speakers
22:50:55.540  Add        2  15 local.               _spotify-connect._tcp. f84a989346fb84a24fa545fde1a3102645d1ffca
22:50:55.598  Add        2  15 local.               _spotify-connect._tcp. cde82ced6ff067791e7bf8e70a58e9e40092887b
22:50:56.165  Add        2  15 local.               _spotify-connect._tcp. SpZc-A6E9C4

This is what I see in my dns-sd -B _spotify-connect._tcp. local. output for my Sonos device:

C:\Users\thluc>dns-sd -B _spotify-connect._tcp. local.
Browsing for _spotify-connect._tcp..local.
Timestamp     A/R Flags if Domain                    Service Type              Instance Name
 8:18:29.499  Add     3  5 local.                    _spotify-connect._tcp.    sonosRINCON_38420B909DC801400

I wonder if SnapCast (or librespot) is doing something to unregister the sonosRINCON... entries in your Zeroconf mDNS registration, and replacing them with the friendly names (e.g. Living Room Soundbar, etc). That would not make sense though, as the Sonos device should be registering its presence to Zeroconf / mDNS.

Anyhow, you should be able to query the Sonos device directly using the zeroconf_device_getinfo with it's IP, Port, and CPath info - like so for my Sonos Symfonisk:

action: spotifyplus.zeroconf_device_getinfo
data:
  entity_id: media_player.spotifyplus_todd_l
  host_ipv4_address: 192.168.1.91
  host_ip_port: 1400
  cpath: /spotifyzc

Receiving output like this (abbreviated and redacted):

user_profile:
  display_name: Todd L
  ...
result:
  ...
  BrandDisplayName: Sonos
  ClientId: 9b377073ea334637b1406f329ce005de
  DeviceId: f87342b3ad455375317d5af8aabde64a28bd49d0
  DeviceType: SPEAKER
  GroupStatus: NONE
  ModelDisplayName: Bookshelf
  ProductId: 1233
  RemoteName: Office
  Scope: streaming
  SupportedCapabilities: 3
  TokenType: authorization_code

Note the BrandDisplayName: Sonos indicates this is a Sonos device.

The brand value differs from your SnapCast BrandDisplayName: librespot definition, which indicates that SnapCast / librespot is defining the device for you and registering it with Zeroconf so that Spotify players can find it. At that point, SnapCast / librespot have control of the device.

Hope that makes sense.