Spotcast - custom component to start playback on an idle chromecast device

Hope I did this right:

2020-07-02 23:55:50 DEBUG (SyncWorker_5) [custom_components.spotcast] setting up with  account default
2020-07-02 23:55:52 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.140397902671056] Exceeded 30 redirects.
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 130, in handle_call_service
    connection.context(msg),
  File "/usr/src/homeassistant/homeassistant/core.py", line 1260, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1299, in _execute_service
    await self._hass.async_add_executor_job(handler.func, service_call)
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/spotcast/__init__.py", line 199, in start_casting
    access_token, expires = get_token_instance(account).get_spotify_token()
  File "/config/custom_components/spotcast/__init__.py", line 262, in get_spotify_token
    self._access_token, self._token_expires = st.start_session(self.sp_dc, self.sp_key)
  File "/usr/local/lib/python3.7/site-packages/spotify_token.py", line 19, in start_session
    headers=headers, cookies=cookies)
  File "/usr/local/lib/python3.7/site-packages/requests/sessions.py", line 543, in get
    return self.request('GET', url, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/requests/sessions.py", line 530, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python3.7/site-packages/requests/sessions.py", line 665, in send
    history = [resp for resp in gen]
  File "/usr/local/lib/python3.7/site-packages/requests/sessions.py", line 665, in <listcomp>
    history = [resp for resp in gen]
  File "/usr/local/lib/python3.7/site-packages/requests/sessions.py", line 166, in resolve_redirects
    raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
requests.exceptions.TooManyRedirects: Exceeded 30 redirects.

Yes, I’ve a premium account
For some reason I’m not able to upload the screen shot, but this is what I sent.
Service: spotcast.start
Entity: media_player.onkyo (a Chromecast audio attacht to my Onkyo receiver)
Service data
1 entity_id: media_player.onkyo
2 uri: spotify:playlist:7tVLJrVZyEexwsnDECaDQo
Hope this helps

Seems ypur credentials are wrong. See readme in the repo on how to obtain new ones

Hi,

Awesome component, works great when I try the service by itself on the Developer tools, however it does throw a fit when I try to script with it.

My script is as follows:

spotify_test2:
  alias: "Spotify test2"
  sequence:
  - service: spotcast.start
    data_template:
      device_name: >
        {% if is_state("input_select.spotify_source","Grupo del Hogar (synced)") %} Grupo del Hogar
        {% elif is_state("input_select.spotify_source","Comedor") %} Comedor
        {% elif is_state("input_select.spotify_source","Pasillo Principal") %} Pasillo Principal
        {% elif is_state("input_select.spotify_source","Recamara Principal Ducha") %} Recamara Principal Ducha
        {% elif is_state("input_select.spotify_source","Recamara Principal") %} Recamara Principal
        {% elif is_state("input_select.spotify_source","Recamara Principal Buro") %} Recamara Principal Buro
        {% elif is_state("input_select.spotify_source","Grupo de Recamara Principal") %} Grupo de Recamara Principal
        {% elif is_state("input_select.spotify_source","Recamara de Visitas") %} Recamara de Visitas
        {% elif is_state("input_select.spotify_source","Oficina") %} Oficina
        {% elif is_state("input_select.spotify_source","Grupo de Limpieza") %} Grupo de Limpieza
        {% elif is_state("input_select.spotify_source","Solo Bocinas") %} Solo Bocinas
        {% elif is_state("input_select.spotify_source","Solo Bocinas Inteligentes") %} Solo Bocinas Inteligentes
        {% endif %}            
      uri: >
        {% if is_state("input_select.spotify_playlist","Sueño Profundo") %}spotify:playlist:37i9dQZF1DX7aAuYd7Jogj
        {% elif is_state("input_select.spotify_playlist","Solo Bocinas Inteligentes") %}
        {% endif %}
      random_song: true   

I tested the if statements on the developer tools template section and they respond with the correct data.

If Statement 1 returns: Oficina
If Statement 1 returns: spotify:playlist:37i9dQZF1DX7aAuYd7Jogj

However the component complains that the ‘Spotify’ object has no attribute 'playlist_tracks’

2020-07-07 18:14:47 ERROR (MainThread) [homeassistant.core] Error executing service: <ServiceCall script.spotify_test2 (c:cb11f265f11640dd9676a86393c40dc5)>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/core.py", line 1276, in catch_exceptions
    await coro_or_task
  File "/usr/src/homeassistant/homeassistant/core.py", line 1295, in _execute_service
    await handler.func(service_call)
  File "/usr/src/homeassistant/homeassistant/components/script/__init__.py", line 204, in service_handler
    await script.async_turn_on(variables=service.data, context=service.context)
  File "/usr/src/homeassistant/homeassistant/components/script/__init__.py", line 280, in async_turn_on
    await self.script.async_run(kwargs.get(ATTR_VARIABLES), context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 831, in async_run
    await run.async_run()
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 555, in async_run
    await self._async_run()
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 572, in _async_run
    await self._async_step(log_exceptions=not propagate_exceptions)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 153, in _async_step
    self, f"_async_{cv.determine_script_action(self._action)}_step"
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 656, in _async_call_service_step
    *self._prep_call_service_step(), blocking=True, context=self._context
  File "/usr/src/homeassistant/homeassistant/core.py", line 1260, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1299, in _execute_service
    await self._hass.async_add_executor_job(handler.func, service_call)
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/spotcast/__init__.py", line 242, in start_casting
    play(client, spotify_device_id, uri, random_song, repeat, shuffle, position)
  File "/config/custom_components/spotcast/__init__.py", line 172, in play
    results = client.playlist_tracks(uri)
AttributeError: 'Spotify' object has no attribute 'playlist_tracks'

Not sure if I missed something, or I’m doing something wrong. It might be all the identation spaces that are introduced in the mix when using the data_template.

Any and all help would be greatly appreciated. :slight_smile:

I never gpt scripts to work for anything in HA. If someone with knowledge ca help you it would be good seeing as it works outside a scripts.

Also I think this question with answers are posted in this thread a couple of times already

Perhaps I’m mistaken, but your playlist ‘ Solo Bocinas Inteligentes’ is missing the URI link. Basically you’re saying to play a playlist, without providing the link right?

I have a similar setup where I used BobNL’s Radio card and made a Spotify card (where I can select playlist and speakers).

My script works flawlessly: Chromecast Radio with station and player selection

1 Like

Yeah, I thought about that. But it wasn’t it. I removed that section of the elif and just left a single value (which when executed on the development tools template editor did return a valid spotify URI and it would still fail.

I did remove the random_song: true parameter from my script and now it does play the playlist on the selected speaker as expected, but it will still complain on the homeassistant.log about the AttributeError: ‘Spotify’ object has no attribute ‘playlist’ dunno what is that about, I believe that is inside the spotify component (/usr/src/homeassistant/homeassistant/components/spotify/media_player.py)

I noticed in your script you used random_song: ‘true’ and shuffle: ‘true’ with single quotes, maybe I was missing that.

@fondberg, can you please explain how to use the new websocket methods from home assistant

I’m no HASS scripting expert and have had very limited success with my scripts for other things. I can therefor not help. sorry

Hi,
This is my working script for Spotcast, I hope it will get you started:

spotify_music:
  sequence:
  - data_template:
      entity_id: '{% if is_state("input_select.spotify_source", "All Speakers") %}
        media_player.all_speakers {% elif is_state("input_select.spotify_source",
        "Kitchen") %} media_player.kitchen {% elif is_state("input_select.spotify_source",
        "Orins Room") %} media_player.orins_room {% elif is_state("input_select.spotify_source",
        "Living Room") %} media_player.living_room {% elif is_state("input_select.spotify_source",
        "Master Bedroom") %} media_player.master_bedroom {% elif is_state("input_select.spotify_source",
        "Lians Room") %} media_player.lians_room {% elif is_state("input_select.spotify_source",
        "Google Outdoor") %} media_player.google_outdoor {% elif is_state("input_select.spotify_source",
        "Bathroom") %} media_player.bathroom {% endif%}

        '
      random_song: true
      uri: '{% if is_state("input_select.spotify_playlist", "Summer Hits") %} spotify:user:spotify:playlist:37i9dQZF1DWZKxDdpAkEjR
        {% elif is_state("input_select.spotify_playlist", "80s Dance") %} spotify:playlist:2rdqJc9UxEbt5Ff8qrS5pB
        {% elif is_state("input_select.spotify_playlist", "Bob Marley") %} spotify:playlist:0NnnXJtgjnzkpdT9BNRlY3
        {% elif is_state("input_select.spotify_playlist", "Julio Iglesias") %} spotify:playlist:0gpDsLoGK0ylw3ColHhx5i
        {% elif is_state("input_select.spotify_playlist", "Piano in the Background")%}
        spotify:user:spotify:playlist:37i9dQZF1DX7K31D69s4M1 {% elif is_state("input_select.spotify_playlist",
        "Happy Hits") %} spotify:user:spotify:playlist:37i9dQZF1DXdPec7aLTmlC {% endif
        %}

        '
    service: spotcast.start
  alias: Play Music

You can see how it looks like in my github:
https://github.com/tomerbs/home-assistant-config/blob/745e834fdfa81428b63c1380fae82655ff6df7a9/README.md

3 Likes

Those APIs are only exposed to code like lovelace cards, python scripts or custom components

What is your use case?

Nice!! Def want my HA to look like that. I haven’t fixed a nice UI even after using HA for 1.5 years

Nice! I have that as well (I used Bob_NL’s Radio as an example for the codes), but I combined it with multiple conditional mini media player cards inside a vertical stack-in-card (depending on what speaker is on) and use it as a popup card for Spotify on my Lovelace view :grinning::+1: (Also wrapped all the mini cards inside a swipe card so I can swipe media players in case different sources are on). Pressing the Spotify button (added a button-card inside the vertical stack-in-card between the dropdown menu and mini media player cards) starts the script.

4 Likes

Hey, are you able to share your final config for this, looks awesome and would love to use it myself!

I really like it, do you maybe have the popup card code yaml also? I can’t get the input selectors in a nice popup card style like yours… Thanks a lot @tomerbs!

This has been thought out really nice. Are you willing to share the gui-yaml code of the popup?

Thanks a lot!

@martheijnen and @mitch Hi, thank you! It’s impossibe for me to share that for the time being. I have split my configuration in to multiple yaml files (like more than 40 files). I don’t have the time to search them all an put them in a file or to share my entire setup. I can share the code of what you see there, but you’ll be missing a lot of info and files. It’ll give you an idea how I created that and should help you on your way though!

    card:
      layout: vertical
      type: 'custom:layout-card'
      column_width: 96%
      cards:
        - content: |
            # Maak je keuzes en tik op het Spotify icoontje.
          style: !include /config/templates/smartphone/markdown-sub-style.yaml
          type: markdown      
        - cards:    
            - entities:
                - entity: input_select.spotify_playlist
                - entity: input_select.google_cast
              type: entities
              style: |
                ha-card {
                  --paper-card-background-color: var(--background-color) !important;
                  box-shadow: none !important;
                }            
            - deep_press: true
              entity: media_player.sony_cast
              hold_action:
                action: more-info
                haptic: heavy
              icon: 'mdi:spotify'
              label: Spotify
              name: Tik hier om te starten!
              aspect_ratio: 4/1
              size: 100%
              show_icon: true
              show_label: false
              show_last_changed: false
              show_name: false
              show_state: false
              state:
              styles:
                card:
                  - font-size: 18px
                  - background-color: var(--background-color)
                name:
                  - font-weight: bold                  
                  - font-family: Helvetica                
                label:
                  - font-size: 10px      
                  - filter: opacity(0%)
              tap_action:
                action: call-service
                haptic: medium
                service: script.turn_on
                service_data:
                  entity_id: script.spotify
              type: 'custom:button-card'                
            - !include /config/templates/mediaplayers/card_sony_cast.yaml
          mode: vertical
          keep:
            background: true
            border_radius: false
            margin: false        
            outer_padding: false
          type: 'custom:stack-in-card'
              
    deviceID:
      - this                      
    style:
      align-items: center
      background: var(--background-popcard)
      icon: 'mdi:apple'
      justify-content: center
      width: 400px
      box-shadow: none
    title: Spotify

And if you have multiple media players you want to swipe between just wrap those cards inside a swiper-card (install the plugin first through HACS). I removed that from the code above, because it would be confusing (since I also deleted other media players to showcase the code more clearly)

- cards:
    - INSERT CARDS HERE
    - INSERT CARDS HERE
    - INSERT CARDS HERE
  parameters:
    spaceBetween: 20 
    pagination:
      type: bullets
  type: 'custom:swipe-card'    

Make sure to view the post of Rob_NL I mentioned above to have the menu/script actually functional. I created my own script using his example: Chromecast Radio with station and player selection

1 Like

Thanks, I managed to get the script working but now want the simple entity card i have in my ‘homekit style’ layout. It’s not that easy to get that done :’)

thanks, hope to can get something of your code working

Ah ok, yeah I don’t use the Homekit Syle card (you mean the one from DBuit right?)

Yes! correct. With my own additions onto it ofc