home-assistant / core

:house_with_garden: Open source home automation that puts local control and privacy first.
https://www.home-assistant.io
Apache License 2.0
71.03k stars 29.68k forks source link

Using multiple Spotify accounts - No active playback device found #95397

Closed cpttater closed 1 year ago

cpttater commented 1 year ago

The problem

Hello HA Team,

I have recently followed the Spotify Integration Configuration Steps and have successfully added multiple accounts on my premium family plan. After adding the accounts I am unable to control each individually without first logging out of my active Spotify account via web browser and then logging into the secondary accounts. It presents me with the following error when trying to control a Spotify account that I am not actively logged into via the web browser:

"Failed to call service media_player/media_play. No active playback device found"

What version of Home Assistant Core has the issue?

core-2023.6.1

What was the last working version of Home Assistant Core?

No response

What type of installation are you running?

Home Assistant OS

Integration causing the issue

Spotify

Link to integration documentation on our website

https://www.home-assistant.io/integrations/spotify/#using-multiple-spotify-accounts

Diagnostics information

2023-06-27 13:14:35.944 DEBUG (SyncWorker_7) [spotipy.client] Sending GET to https://api.spotify.com/v1/me/player with Params: {'market': None, 'additional_types': [<MediaType.EPISODE: 'episode'>]} Headers: {'Authorization': 'Bearer *Redacted*', 'Content-Type': 'application/json'} and Body: None 
2023-06-27 13:14:36.015 DEBUG (SyncWorker_7) [spotipy.client] RESULTS: None
2023-06-27 13:14:38.562 DEBUG (SyncWorker_1) [spotipy.client] Sending PUT to https://api.spotify.com/v1/me/player/play with Params: {} Headers: {'Authorization': 'Bearer **Redacted*', 'Content-Type': 'application/json'} and Body: None 
2023-06-27 13:14:38.645 ERROR (SyncWorker_1) [spotipy.client] HTTP Error for PUT to https://api.spotify.com/v1/me/player/play with Params: {} returned 404 due to Player command failed: No active device found
2023-06-27 13:14:38.648 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [139787120035984] No active playback device found
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 205, in handle_call_service
    await hass.services.async_call(
  File "/usr/src/homeassistant/homeassistant/core.py", line 1910, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1950, in _execute_service
    await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 226, in handle_service
    await service.entity_service_call(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 811, in entity_service_call
    future.result()  # pop exception if have
    ^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 1034, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 851, in _handle_entity_call
    await result
  File "/usr/src/homeassistant/homeassistant/components/media_player/__init__.py", line 737, in async_media_play
    await self.hass.async_add_executor_job(self.media_play)
  File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/spotify/media_player.py", line 97, in wrapper
    raise HomeAssistantError("No active playback device found") from None
homeassistant.exceptions.HomeAssistantError: No active playback device found

Example YAML snippet

No response

Anything in the logs that might be useful for us?

No response

Additional information

Works fine when only using one account in the multi account configuration. Spotcast is able to control correctly using only one account as well.

home-assistant[bot] commented 1 year ago

Hey there @frenck, mind taking a look at this issue as it has been labeled with an integration (spotify) you are listed as a code owner for? Thanks!

Code owner commands Code owners of `spotify` can trigger bot actions by commenting: - `@home-assistant close` Closes the issue. - `@home-assistant rename Awesome new title` Renames the issue. - `@home-assistant reopen` Reopen the issue. - `@home-assistant unassign spotify` Removes the current integration label and assignees on the issue, add the integration domain after the command.

(message by CodeOwnersMention)


spotify documentation spotify source (message by IssueLinks)

philipflesher commented 1 year ago

Also getting this issue. In doing some research, I found that the spotipy client's start_playback can take a device_id, but also saw that the integration is not passing anything to that method. This makes some sense, as HA assumes that a media player has its source set separately from the play function. Spotify just doesn't see it that way.

So, one possible solution: upon setting a source/device, cache that device_id in the local state, and pass it to start_playback...?

EDIT: Incorrect analysis on my part. The integration has two places it calls start_playback, and it appears that the "media play" path conditionally passes device_id. But what is not clear to me, yet, is the specific conditions under which it does not pass device_id, which does appear to be the root of the problem in my limited testing so far.

EDIT: format fix, and including URL to spotipy docs: Link

philipflesher commented 1 year ago

See also this comment in a different repo, where this issue was resolved by a user who explicitly passed the device_id as stated above:

https://github.com/spotify/web-api/issues/1325#issuecomment-639071348

joostlek commented 1 year ago

I just took a look at the code, and this is about the media_play function. The reason why it throws this error is indeed that there is no active session as the message implies.

The integration keeps track of the devices that are available and updates this list every 5 minutes. Like, yes we know that there is an active device, yes we know that we can use that to directly play media on when we click the play button. But I can also imagine that just picking a random audio device from a list and play the latest playlist you played sounds like the worst idea.

In my PR (#96914) I proposed to disable the playback controls when there is no active session. This way the UI is at least consistent with the workings of the integration. All features are disabled, except for Select source. So what this means is that you can start an active session when you select a device and then you can click play and it will play on that device.

philipflesher commented 1 year ago

@joostlek -- understood that the media_play function fails as the code currently stands, but this is only because there's no facility for HA telling Spotify, instead, "play on this device ID." Selecting the source first doesn't work, because the integration doesn't actually activate a device -- it merely transfers ongoing playback to a device, and thus also fails if no playback is underway.

The most straightforward thing I can imagine: when a source is selected, cache that selected device ID to pass to a subsequent call to any other method. I've already figured out that this would require a significant rewrite of this integration, because state fetched async from the Spotify API is constantly overwriting all state, including the source. It isn't really that clean, but it kind of forces the integration to get as close as possible to how Spotify actually works.

A more aggressive option: HA core media_player functionality could be modified to support a different kind of "play," which is "play on this device." This could be further extended to "repeat this device," "shuffle this device," etc. UI would have to allow for selection of device alongside the play, etc. so that they can be submitted at the same time. There would be separate data stored for "the source that is currently selected" and "the source that HA should use for its next command."

joostlek commented 1 year ago

I think also one thing to keep in mind is that Spotify itself also isn't able to activate a device. I had to start Spotify on my desktop to actually start playing on it for example. image So even if I for example cached the key of my phone or desktop, it would not be able to reach it without starting spotify.

And I think we should actually think about the use of media_play, as you have play media and media play. One is just saying "Continue what you were doing" (which is strange when you're doing nothing) and the other one is like play X on Y.

philipflesher commented 1 year ago

@frenck - is there another issue open to capture/track the ideas about how to modify media_player to allow for integrations such as Spotify to operate cleanly within HA? My prior comment, I think, is in-line with that kind of discussions, and I'd like to join in on that if I can. Thank you!