spotcast: Error when start on chromecast: KeyError: 'deviceID'

Bug Ticket

Describe the bug

When I execute spotcast.start on my chromecast Cuisine, Spotify is launched, I have the logo on the screen, but after that, no music.

Troubleshooting

Make sure to validate all the elements before submitting the ticket (Exception to the steps marked as optional)

  • Using latest version of spotcast
  • Using latest stable version of Home Assistant
  • I have setup the Spotify integration in Home Assistant
  • I have renewed my sp_dc and sp_key values and restarted Home Assistant (see README)
  • (optional) I have Spotify Premium
  • (optional) I am using multiple accounts
  • (optional) I’m attaching relevant logs with level debug for component spotcast (see README)
  • (optional) I’m using entity_id in the service call and have tried device_name but the issue remains

Environment

  • Installation type: HA_OS
  • HA version: 2023.8.4
  • spotcast version: 3.7.0

Configuration

spotcast:
  sp_dc: !secret dorian_sp_dc
  sp_key: !secret dorian_sp_key

Service Call

If relevant, provide a yaml of the service call or explain the action taken to replicate the issue.

service: spotcast.start
data:
  limit: 20
  force_playback: false
  random_song: false
  repeat: "off"
  shuffle: false
  offset: 0
  ignore_fully_played: false
  device_name: Cuisine

Logs

  • normal
2023-08-28 18:28:53.826 ERROR (Thread-12) [pychromecast.socket_client] [Cuisine(192.168.2.33):8009] Exception caught while sending message to controller SpotifyController: Message urn:x-cast:com.spotify.chromecast.secure.v1 from 720cd6a6-a540-4ffb-9a6a-2f28bdab4ecd to sender-0: {'type': 'getInfoResponse', 'payload': {'version': '2.9.0', 'publicKey': 'empty', 'remoteName': 'Spotify on Cast', 'deviceType': 'cast_video', 'brandDisplayName': 'google', 'modelDisplayName': 'Chromecast_Tv', 'libraryVersion': '5.30.3', 'resolverVersion': '1', 'groupStatus': 'NONE', 'deviceAPI_isGroup': False, 'tokenType': 'accesstoken', 'clientID': 'd7df0887fb71494ea994202cb473eae7', 'productID': 0, 'scope': 'streaming', 'availability': '', 'spotifyError': 0, 'status': 101, 'statusString': 'OK'}}
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/pychromecast/socket_client.py", line 719, in _route_message
handled = handler.receive_message(message, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/spotcast/spotify_controller.py", line 47, in receive_message
self.device = data["payload"]["deviceID"]
~~~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 'deviceID'
2023-08-28 18:29:04.775 ERROR (MainThread) [homeassistant.helpers.script.websocket_api_script] websocket_api script: Error executing script. Unexpected error for call_service at pos 1: Timeout when waiting for status response from Spotify app
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 468, in _async_step
await getattr(self, handler)()
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 703, in _async_call_service_step
response_data = await self._async_run_long_action(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 665, in _async_run_long_action
return long_task.result()
^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/core.py", line 1974, in async_call
response_data = await coro
^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/core.py", line 2018, in _execute_service
return await self._hass.async_add_executor_job(target, service_call)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/spotcast/__init__.py", line 221, in start_casting
spotify_device_id = spotcast_controller.get_spotify_device_id(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/spotcast/spotcast_controller.py", line 252, in get_spotify_device_id
spotify_cast_device.startSpotifyController(access_token, expires)
File "/config/custom_components/spotcast/spotcast_controller.py", line 95, in startSpotifyController
sp.launch_app()
File "/config/custom_components/spotcast/spotify_controller.py", line 104, in launch_app
raise LaunchError(
pychromecast.error.LaunchError: Timeout when waiting for status response from Spotify app
2023-08-28 18:29:04.796 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [547346856128] Error handling message: Unknown error (unknown_error) Dorian from 192.168.1.1 (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36)
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/websocket_api/decorators.py", line 26, in _handle_async_response
await func(hass, connection, msg)
File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 716, in handle_execute_script
response = await script_obj.async_run(msg.get("variables"), context=context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 1569, in async_run
return await asyncio.shield(run.async_run())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 420, in async_run
await self._async_step(log_exceptions=False)
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 470, in _async_step
self._handle_exception(
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 493, in _handle_exception
raise exception
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 468, in _async_step
await getattr(self, handler)()
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 703, in _async_call_service_step
response_data = await self._async_run_long_action(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 665, in _async_run_long_action
return long_task.result()
^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/core.py", line 1974, in async_call
response_data = await coro
^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/core.py", line 2018, in _execute_service
return await self._hass.async_add_executor_job(target, service_call)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/spotcast/__init__.py", line 221, in start_casting
spotify_device_id = spotcast_controller.get_spotify_device_id(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/spotcast/spotcast_controller.py", line 252, in get_spotify_device_id
spotify_cast_device.startSpotifyController(access_token, expires)
File "/config/custom_components/spotcast/spotcast_controller.py", line 95, in startSpotifyController
sp.launch_app()
File "/config/custom_components/spotcast/spotify_controller.py", line 104, in launch_app
raise LaunchError(
pychromecast.error.LaunchError: Timeout when waiting for status response from Spotify app

Additional context

I opened a Spotify on web browser, ran the service; but the device didn’t appear on the device list. Only on the windows app I can view it. When pychromecast is called we can see an error at

self.device = data["payload"]["deviceID"]

I don’t know what to do now.

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Reactions: 9
  • Comments: 70 (5 by maintainers)

Most upvoted comments

I am still trying to figure out what aU(a.friendlyName) does.

Had some time to dig in a bit more and I think the friendly name gets converted to a md5 hash which is used as the deviceID after that. For example, I have a chromecast with the friendlyName Opkamer. The corresponding spotify deviceID is a405a64e9f12009792e0c862f6ebe5e0. The md5 hash of Opkamer is also a405a64e9f12009792e0c862f6ebe5e0 🤯

In the end, the aU(a.friendlyName) ends up here in the web-player.js (where e in this case is Opkamer):

return function(e) {
   return new Md5(!0).update(e)[t]()
}

Did some quick testing and it does seem to work when I also send payload with remoteName, deviceID, and deviceAPI_isGroup on getInfo.

Long story short, the deviceID seems to be the md5 hash of the friendly_name of the device. I will do some additional testing and create PR when I have some time, probably after the weekend…

Please use the subscribe button in the notification area. This helps to have a clean discussion for solving the issue. Thanks!

Created PR https://github.com/fondberg/spotcast/pull/406 which should fix the missing deviceID issue. Had no time to test it within Home Assistant yet. Feel free to do so, as I do not have much time this weekend.

@fcusson Would you be able to test these changes and see if this would fix it?

Thanks for digging into that deeper! So in the new way the web player defines the deviceID for the device and not the app on the device? Interesting!

Not sure, but I guess this way Spotify has more possibilities whenever they would like to change the way how the deviceID is constructed without a app update.

Although I’m not a maintainer, I simply don’t see how this problem gets fixed without changes to the Spotify API. Looks like devices are only shown in the API if “active” via Spotify. So even playing/casting other media doesn’t make devices show in the API.

That’s why the first step is registering the speaker to the Spotify App in Chromecast system.

I though, by error, that the new problem was due to spotify changing there API, but the change in response is actually from the pychromecast library, the device I’d missing is the Chromecast device Id, not Spotify.

I’m currently exploring step by step where the system is breaking and that seems to be in the app registration problem.

Just to tamper expectations, Im just getting back from vacawand we have a new hire I have to train at work this week, so I might not have a tone of time. Trying to debug as much as possible, but my personal coding time is currently limited.

/affected and subscribing

subscribing to follow the issue. As I’m affected as well.

Following!

It’s a combination of a few cards, input selects and input booleans, and node red code.

The top input boolean toggle allows me to “send” the variables to call the spotcast service. I have 2 accounts at home, my wife’s and mine. If she is playing music, and we want to change what it’s casting to, I need to select her account before I select a new speaker. Node red will then see it as a previously playing spotify media player and not start the playlist over and just continue playback. So if I had it trigger as soon as you made any selection it could affect that. That being said, if we have a party going on, and someone wants to change the playlist, if the input boolean is toggled on (which it stays on unless all players are idle), it sends the command right away.

I have conditional cards that display if a speaker group is selected. That way you can adjust all of the speakers in the group together. But the collapsible card will expand out the speakers currently playing in the group to adjust them automatically.

The “now playing” on the left are conditional cards, but I’d like to have this be based on what account is selected in the account input_select later. I just haven’t messed with that yet.

Dashboard YAML:

  • title: Music path: music icon: mdi:music subview: false type: panel theme: ios-dark-mode-blue-red-alternative badges: [] cards:
    • type: horizontal-stack cards:
      • type: conditional conditions:
        • entity: media_player.spotify_[ACCOUNT1] state_not: idle card: type: media-control entity: edia_player.spotify_[ACCOUNT1]
      • type: conditional conditions:
        • entity: edia_player.spotify_[ACCOUNT2] state_not: idle card: type: media-control entity: edia_player.spotify_[ACCOUNT2]
      • type: vertical-stack cards:
        • type: vertical-stack cards:
          • type: custom:mushroom-chips-card chips:
            • type: template tap_action: action: toggle entity: input_boolean.start_spotify icon: mdi:spotify icon_color: ‘{{ ‘‘green’’ if states(entity) == ‘‘on’’ else ‘‘grey’’ }}’ alignment: center
          • type: entities entities:
            • entity: input_select.speakers
            • entity: input_select.music_playlist
            • entity: input_select.spotify_account name: Spotify Account
          • type: conditional conditions:
            • entity: media_player.[Speaker_Group1] state: playing card: type: custom:mushroom-media-player-card entity: media_player.[Speaker_Group1] volume_controls:
              • volume_mute
              • volume_set
              • volume_buttons show_volume_level: true
          • type: conditional conditions:
            • entity: media_player.[Speaker_Group2] state: playing card: type: custom:mushroom-media-player-card volume_controls:
              • volume_mute
              • volume_set
              • volume_buttons show_volume_level: true entity: media_player.[Speaker_Group2]
          • type: custom:collapsable-cards title: Speaker Volumes defaultOpen: false cards:
            • type: custom:auto-entities card: type: grid columns: 1 square: false card_param: cards filter: template: |- {% for state in states.media_player -%} {%- if state.attributes.device_class == ‘speaker’ and state.state in [‘playing’, ‘paused’] and “speakers” not in state.attributes.friendly_name|lower and “Living Room TV” not in state.attributes.friendly_name -%} {{ { ‘entity’: state.entity_id, ‘type’: ‘custom:mushroom-media-player-card’, ‘primary_info’: ‘name’, ‘use_media_info’: false, ‘show_volume_level’: true, ‘volume_controls’: ‘- volume_mute’ ‘- volume_set’ ‘- volume_buttons’, ‘collapsible_controls’: true } }}, {%- endif -%} {%- endfor %} exclude: [] show_empty: true

image Data for Resuming Playback {“device_name”:device_name,“force_playback”:true,“account”:account}

Data for Start Spotcast {“device_name”:device_name,“force_playback”:true,“uri”:uri,“random_song”:true,“shuffle”:true,“account”:account}

I currently have a flow variable for “resume” which allows me to determine when I’m just changing speakers vs when it needs to start a whole new spotcast service call.

The playlist switch sets the spotify URI. (There may be a better way to do this?)

Hope this helps.

YES!!! Spotcast v3.7.1 just dropped in HACS. Updated and all is working again! WOOHOO!! Thank you all who worded to get us all back up and working.

Tracking this issue. i also effected by this.

Thank you so much for your work on this.

I use this integration many times each day (averaging about 7 hours of music play every day over this last week) and just thank you for creating and maintaining this. I am so appreciative of your efforts ✨

Thanks for fixing this! I had to redo how spotcast was being called, but got it working even better now.

image

How did you do the card on the right? Mind sharing?

Confirmed the fix is working. Thank you all!

How does this project solve it? https://github.com/music-assistant/hass-music-assistant I guess since it requires a server that is always running it is always aware of what cast Devices are available? Tested and I can cast Spotify if i use it.

Works quite well the hours I’ve tested so far. Looks like a decent and well-maintained alternative. Only thing I’m missing is multiple accounts and choosing which to use when casting. This feature is actively being developed.

Yes but I hope this project can find some inspiration there and maybe a solution to this problem.

Just found a workaround for the moment. The only part broken is the ability to find the device id based on the entity_name in Home Assistant. Which means, it is possible to make the call with the spotify_id directly.

Spotify just reinstated there api console on the developper site. It is possible to request the available devices with this link.

I tested and I am able to play media using the spotify_id directly. I’ll work on a way to repair the entity_name to device_id method. Functionality is limited. Speaker needs to be on before it plays

I’m also seeing this problem (started about 8 hours ago)

I am able to start Spotify on my chromcast devices by selecting the device in the Spotify App (but doesn’t work if I start it from HomeAssistant)

From my logs

Error executing script. Unexpected error for call_service at pos 1: Timeout when waiting for status response from Spotify app
10:01:07 AM – (ERROR) Script (custom integration) - message first occurred at 9:55:28 AM and shows up 2 times

[Everywhere(192.168.1.74):32052] Exception caught while sending message to controller SpotifyController: Message urn:x-cast:com.spotify.chromecast.secure.v1 from 8465b09f... to sender-0: {'type': 'getInfoResponse', 'payload': {'version': '2.9.0', 'publicKey': 'empty', 'remoteName': 'Spotify on Cast', 'deviceType': 'cast_audio', 'brandDisplayName': 'google', 'modelDisplayName': 'Chromecast_Audio', 'libraryVersion': '5.30.3', 'resolverVersion': '1', 'groupStatus': 'NONE', 'deviceAPI_isGroup': False, 'tokenType': 'accesstoken', 'clientID': 'd7df0887fb...', 'productID': 0, 'scope': 'streaming', 'availability': '', 'spotifyError': 0, 'status': 101, 'statusString': 'OK'}}
10:00:56 AM – (ERROR) Spotcast (custom integration) - message first occurred at 9:55:17 AM and shows up 6 times

Did some quick testing and it does seem to work when I also send payload with remoteName, deviceID, and deviceAPI_isGroup on getInfo.

Long story short, the deviceID seems to be the md5 hash of the friendly_name of the device. I will do some additional testing and create PR when I have some time, probably after the weekend…

Thanks for digging into that deeper! So in the new way the web player defines the deviceID for the device and not the app on the device? Interesting!

It suggests me, that this might be a solution to make sure the app is started on the right device and not to confuse a rouge getInfoResponse from another device with.

Digging a bit deeper, shows the deviceID is actually no longer present in the response message from Spotify API:

Can you try to compose a message to the device according the webplayer’s method? I am a bit programming handicapped… 😉

{type:"getInfo",
payload: {
remoteName:a.friendlyName,
deviceID:aU(a.friendlyName),
deviceAPI_isGroup:a.capabilities.includes(null===(n=chrome)||void 0===n||null===(i=n.cast)||void 0===i?void 0:i.Capability.MULTIZONE_GROUP)
}
}

The values for friendlyName, etc… comes from the getCastDevice function from the Cast web sender:

https://developers.google.com/cast/docs/reference/web_sender/cast.framework.CastSession#getCastDevice

I am still trying to figure out what aU(a.friendlyName) does.

Also following, experience the same issue

having this issue. i queried https://api.spotify.com/v1/me/player/devices to see what returned and it shows none of my chromecast devices at home. i believe this is related to my issue.

@chasebolt this is normal Chromecast needs to be actively connected to spotify to show up in the API, Spotify API has no awareness of your local network devices that aren’t actively in use.

Sorry but this just sounds odd to me, but maybe I don’t fully understand the functionality here. What do you mean with “actively in use”? The device obviously won’t be in use the first time we initiate playback?

@fcusson Which component is responsible for publishing the Chromecast device as an available Spotify device?

That is the whole point of Spotcast. Normally you can’t cast to idle chromecast devices because Spotify doesn’t know they exist so they have no ID. Spotcast gets them connected so Spotify can give them an ID, then gets the ID, then starts casting.

It seems that this is a problem that appears to many users, does anyone have a solution to this problem? Is the developer of the plugin in the group and reading the errors? Maybe he can respond and say if it is in treatment or not?

having this issue. i queried https://api.spotify.com/v1/me/player/devices to see what returned and it shows none of my chromecast devices at home. i believe this is related to my issue.

@chasebolt this is normal Chromecast needs to be actively connected to spotify to show up in the API, Spotify API has no awareness of your local network devices that aren’t actively in use.

You could retrieve the device id’s, using the API. But thats a wee bit more technical.

It’s just so that the API of Spotify changed. I had to update the AppStore app to be able to smoothly cast to my google cast devices again. So I’m guessing the maintainers will update the integration as well.

On a more FUN note: I use SpotCast to play my wake-up music at 20% volume to wake up. So I overslept. But that’s a risk I’m willing to take. I love the work done on SpotCast!

I noticed the same problem about 13 hours ago.

Like @DanDixon, I can select and play music on my Chromecast device via the Spotify app, but not via Spotcast from HomeAssistant.