Spotify Select Player

Blueprint Script to auto select a Spotify Account for a specific source/player to start playing music on.

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

(My) Purpose It’s really annoying to be out wondering around and jamming to some music through my phone on Spotify, but when a person back at home uses the (previous) script automations, it ‘steals’ my account and sets the device as the main-room (rather than my headphones) & with different music. so the ‘simple’ way around this would be use use one of the other Family accounts (in my subscription) to be dedicated to playing music in that location, however there are times when all the accounts need to be playing elsewhere. (and none-of the accounts are unused)

Solution This blueprint which performs the simple function of setting the source to either (of the following) and playing the specified playlist (stored in an input_text object)

  1. the account which is already playing in the main-room
  2. the first account (if there is one), which is not playing, and has the main-room source avaliable

Limitation
There is a big on here (and I think it’s reasonably well known, but is still important): Spotify must be logged into a source/player, or a computer on the local network must have the Spotify app open and be able to see the specified source for HA (and indeed the Sportify Web-API) to ‘see’ that source in the source_list.

  • my current work around is that the main-room is a RiPi3 with docker-raspotify running multiple times, each with it’s own sign-in for all the linked accounts in HA. I hope to post the docker-compose.yaml file later to its GitHub page.

Source
(version 1)

# Copyright (c) 2022,  Matthew Tilney
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:

# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.

# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.

# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
blueprint:
  name: Spotify Select Player
  description: > 
    Allows for automatic selection of a spotify account to be used with the specified source:
    a) selects the current Spotify account already playing on the source,
    b) select an account currently not playing from the (ordered) Spotify Accounts list
    Dependencies: HA Spotify Intergration for at least 1 account (but mutiple supported, this is the whole point)
    Issues: Sources are only avaliable in Home Assistant (viat the Spotify Intergration & Web-API) if:
      1. The account is signed into the specific Spotify account on the source
      2. A local App is currently open (and internet accessable) on a device on the same network as the source.
    - Workaround on RiPi devices (yet to be posted - May2022), run mutiple raspotify docker containers on the same devices, with the same name, each signed in to a different account
  domain: script
  input:
    source_name:
      name: Source Name
      description: Name of the source (must be avaliable in the source_list for each spotify player to work on that account)
      selector:
        text:
    media_list:
      name: Spotify Accounts
      description: Spotify Accounts list (ordere list, 1st one is checked first) for Spotify which could use for playing music
      default: {entity_id: []}
      selector:
        target:
          entity:
            integration: spotify
    media_content:
      name: Media Content as Input Text
      description: The media content url for spotify, can be playlist album, or track fron a previously populated input_text helper
      selector:
        entity:
          domain: input_text
    test_mode_enabled:
      name: Test Mode
      description: Some actions (specificall the source select and play media) are skipped, and output of which entity would be used is written to the log (WARN level)
      default: false
      selector:
        boolean:
variables:
  spotify_accounts: !input 'media_list'
  output_source: !input 'source_name'
  media_content: !input 'media_content'
  test_mode: !input 'test_mode_enabled'
alias: Spotify Select Player  (blueprint)
sequence:
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: >
        {% if spotify_accounts is not defined %}{% set spotify_accounts = namespace(entity_id = integration_entities("spotify") ) %}{% endif %} {% set
        output_source = 'spotify-player'%} {%- set spotifies = namespace(entities=[])
        -%} {% for entity in states.media_player %}
          {% if entity.entity_id in spotify_accounts.entity_id %}
          {%- set spotifies.entities = spotifies.entities + [entity] -%}
          {% set entities = spotifies.entities %}
          {% endif %}
        {% endfor %} {{ spotifies.entities | map(attribute='entity_id') | list
        }}
  - delay:
      hours: 0
      minutes: 0
      seconds: 1
      milliseconds: 0
  - if:
      - condition: not
        conditions:
          - condition: template
            value_template: !input 'test_mode_enabled'
    then:
      - service: media_player.select_source
        target:
          entity_id: >
            {# Determin which media play can be used for the current sctip#} {#
            Convert input entity_id list into full spotify entites in the HA
            setup #} {% set spotifies = namespace(entities=[]) -%} {% set
            potential_players = namespace(entities=[])%} {% set spotify_list =
            [] -%} {% for player in spotify_accounts.entity_id %} {% if player
            in integration_entities("spotify")%} {# I don't like having to use a
            2nd loop, when a way of getting full entity from entity_id as a
            command is possiable, this bit will no longer be rquired #} {% for
            player_entity in states.media_player %}{% if player_entity.entity_id
            == player %} {% set spotifies.entities = spotifies.entities +
            [player_entity] -%} {% set entities = spotifies.entities %}  {%
            endif %}{% endfor %}{% endif %}{% endfor %} {# Perform the specific
            selections, 1st if the device is already playing on the source, 2nd
            and spotify account not playing that also has acccess to the source
            #} {% for player in (spotifies.entities |
            selectattr('state','ne','idle') | selectattr('attributes.source',
            'search', output_source) | list )%} {%- set
            potential_players.entities = potential_players.entities + [player]
            -%} {% endfor %}  {% for player in (spotifies.entities |
            selectattr('state','ne','playing') |
            selectattr('attributes.source_list','search',output_source) | list
            )%} {%- set potential_players.entities = potential_players.entities
            + [player] -%} {% endfor %} {# Return the 1st spotify entity_id from
            the found list#} {{ (potential_players.entities |
            map(attribute='entity_id') | list)[0] }}
        data:
          source: !input 'source_name'
      - delay:
          hours: 0
          minutes: 0
          seconds: 2
          milliseconds: 500
      - service: media_player.play_media
        data:
          media_content_id: >
            {{ states(media_content) }}
          media_content_type: playlist
        target:
          entity_id: >
            {# Determin which media play can be used for the current sctip#} {#
            Convert input entity_id list into full spotify entites in the HA
            setup #} {% set spotifies = namespace(entities=[]) -%} {% set
            potential_players = namespace(entities=[])%} {% set spotify_list =
            [] -%} {% for player in spotify_accounts.entity_id %} {% if player
            in integration_entities("spotify")%} {# I don't like having to use a
            2nd loop, when a way of getting full entity from entity_id as a
            command is possiable, this bit will no longer be rquired #} {% for
            player_entity in states.media_player %}{% if player_entity.entity_id
            == player %} {% set spotifies.entities = spotifies.entities +
            [player_entity] -%} {% set entities = spotifies.entities %}  {%
            endif %}{% endfor %}{% endif %}{% endfor %} {# Perform the specific
            selections, 1st if the device is already playing on the source, 2nd
            and spotify account not playing that also has acccess to the source
            #} {% for player in (spotifies.entities |
            selectattr('state','ne','idle') | selectattr('attributes.source',
            'search', output_source) | list )%} {%- set
            potential_players.entities = potential_players.entities + [player]
            -%} {% endfor %}  {% for player in (spotifies.entities |
            selectattr('state','ne','playing') |
            selectattr('attributes.source_list','search',output_source) | list
            )%} {%- set potential_players.entities = potential_players.entities
            + [player] -%} {% endfor %} {# Return the 1st spotify entity_id from
            the found list#} {{ (potential_players.entities |
            map(attribute='entity_id') | list)[0] }}
    else:
      - service: system_log.write
        data:
          level: warning
          message: >
            {# Determin which media play can be used for the current sctip#} {#
            Convert input entity_id list into full spotify entites in the HA
            setup #} {% set spotifies = namespace(entities=[]) -%} {% set
            potential_players = namespace(entities=[])%} {% set spotify_list =
            [] -%} {% for player in spotify_accounts.entity_id %} {% if player
            in integration_entities("spotify")%} {# I don't like having to use a
            2nd loop, when a way of getting full entity from entity_id as a
            command is possiable, this bit will no longer be rquired #} {% for
            player_entity in states.media_player %}{% if player_entity.entity_id
            == player %} {% set spotifies.entities = spotifies.entities +
            [player_entity] -%} {% set entities = spotifies.entities %}  {%
            endif %}{% endfor %}{% endif %}{% endfor %} {# Perform the specific
            selections, 1st if the device is already playing on the source, 2nd
            and spotify account not playing that also has acccess to the source
            #} {% for player in (spotifies.entities |
            selectattr('state','ne','idle') | selectattr('attributes.source',
            'search', output_source) | list )%} {%- set
            potential_players.entities = potential_players.entities + [player]
            -%} {% endfor %}  {% for player in (spotifies.entities |
            selectattr('state','ne','playing') |
            selectattr('attributes.source_list','search',output_source) | list
            )%} {%- set potential_players.entities = potential_players.entities
            + [player] -%} {% endfor %} {# Write out to log file (for enabled
            mode) #} List of spotify accounts (1st will be selected)
            {{potential_players.entities | map(attribute='entity_id') | list }};
            with the source set as: {{ output_source }}; the content will be {{states(media_content)}}- Testmode: {{ test_mode }}
          logger: scripts.log
mode: single
1 Like

Just realised that currently the media_content_id is controlled from an input_text file (which you select), this was so that I was easily able to manipulate such field from another script & call this one for different music.
The main reason I didn’t specify a text file in the blueprint instead is that I didn’t want to configure this specific script multiple times (as tall the source and spority_accounts would be the same), however if anyone happens to be interested, I could easily publish a 2nd version, but with the input for the media beinga simple text file that you directly apply the spotify content url to.

1 Like