Confirmed by direct hardware check: a334c8ba...=Bedroom, d0166d6f...= Living Room -- the swap from two commits ago was right, my revert of it was wrong. The actual bug behind the last "Living Room played in Bedroom" report was that the wait loop before play_media only checked supported_features (is play_media possible at all), not whether select_source's target had actually taken effect -- so if some other device was already active from earlier testing, the feature-gate could pass immediately while Spotify was still routing to that old device. Now the loop also waits for the source attribute to match the requested room's ID before proceeding. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
247 lines
10 KiB
Plaintext
247 lines
10 KiB
Plaintext
- spec:
|
|
name: execute_services
|
|
description: Use this function to execute service of devices in Home Assistant.
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
list:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
domain:
|
|
type: string
|
|
description: The domain of the service
|
|
service:
|
|
type: string
|
|
description: The service to be called
|
|
service_data:
|
|
type: object
|
|
description: The service data object to indicate what to control.
|
|
properties:
|
|
entity_id:
|
|
type: string
|
|
description: The entity_id retrieved from available devices. It
|
|
must start with domain, followed by dot character.
|
|
required:
|
|
- entity_id
|
|
required:
|
|
- domain
|
|
- service
|
|
- service_data
|
|
function:
|
|
type: native
|
|
name: execute_service
|
|
|
|
|
|
# Spotify Music Control Functions
|
|
|
|
- spec:
|
|
name: search_spotify
|
|
description: >-
|
|
Search Spotify for an artist, album, track, or playlist and return the Spotify URI.
|
|
Use this before playing music to get the URI. Use this whenever the user asks to play
|
|
or queue a specific song, artist, album, or playlist — even if the name contains a
|
|
number (e.g. "Back on 74", "1979", "Blink 182"). A number that is part of a
|
|
song/artist/album name is NOT a volume level; only use control_playback's volume_set
|
|
if the user explicitly says "volume" or "percent".
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
query:
|
|
type: string
|
|
description: Artist, album, track, or playlist name to search for (e.g., "Coldplay", "Parachutes", "Yellow", "Today's Top Hits")
|
|
type:
|
|
type: string
|
|
enum: [artist, album, track, playlist]
|
|
description: Type of content to search for. Playlist searches check your personal playlists first, then fall back to public Spotify playlists.
|
|
required:
|
|
- query
|
|
- type
|
|
function:
|
|
type: script
|
|
sequence:
|
|
- service: spotify_voice_assistant.search
|
|
response_variable: _function_result
|
|
data:
|
|
query: "{{ query }}"
|
|
type: "{{ type }}"
|
|
|
|
- spec:
|
|
name: play_music
|
|
description: Play music immediately on a Spotify Connect device using a Spotify URI, replacing current playback. You must first call search_spotify to get the URI. Use this when user says "play".
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
spotify_uri:
|
|
type: string
|
|
description: Spotify URI from search_spotify (e.g., "spotify:artist:4gzpq5DPGxSnKTe4SA8HAU")
|
|
type:
|
|
type: string
|
|
enum: [artist, album, track, playlist]
|
|
description: The exact "type" value returned by search_spotify for this URI. Required — passing the wrong value causes playback to silently fail.
|
|
room:
|
|
type: string
|
|
description: Spotify Connect source/device name to play on, matching an entry in media_player.spotify_bhetherman's source_list exactly (e.g., "Living Room")
|
|
required:
|
|
- spotify_uri
|
|
- type
|
|
- room
|
|
function:
|
|
type: script
|
|
sequence:
|
|
- service: media_player.select_source
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
source: "{{ room | replace('Living Room', 'd0166d6f83796b0f9059b0adf0f6cb0d81fa6376') | replace('Bedroom', 'a334c8ba989367b52303eaa9dbc424cdd83012b5') }}"
|
|
- repeat:
|
|
while:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ repeat.index <= 8 and (
|
|
state_attr('media_player.spotify_bhetherman', 'source') !=
|
|
(room | replace('Living Room', 'd0166d6f83796b0f9059b0adf0f6cb0d81fa6376') | replace('Bedroom', 'a334c8ba989367b52303eaa9dbc424cdd83012b5'))
|
|
or (state_attr('media_player.spotify_bhetherman', 'supported_features') | int(0)
|
|
| bitwise_and(512)) != 512
|
|
) }}
|
|
sequence:
|
|
- delay: "00:00:01"
|
|
- service: media_player.play_media
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
media_content_id: "{{ spotify_uri }}"
|
|
media_content_type: "{{ type }}"
|
|
enqueue: replace
|
|
|
|
- spec:
|
|
name: queue_music
|
|
description: >-
|
|
Queue or play music using a Spotify URI from search_spotify. Use this whenever the
|
|
user says "queue" or "add to queue" — always use this function for queue requests,
|
|
regardless of whether the content is a track, album, artist, or playlist; the
|
|
correct behavior for each type is handled automatically. (Individual tracks are
|
|
added to the queue without interrupting playback; since Spotify cannot queue a whole
|
|
album/artist/playlist, those start playing immediately instead.)
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
spotify_uri:
|
|
type: string
|
|
description: Spotify URI from search_spotify (e.g., "spotify:track:4gzpq5DPGxSnKTe4SA8HAU")
|
|
type:
|
|
type: string
|
|
enum: [artist, album, track, playlist]
|
|
description: The exact "type" value returned by search_spotify for this URI.
|
|
room:
|
|
type: string
|
|
description: Spotify Connect source/device name to queue on, matching an entry in media_player.spotify_bhetherman's source_list exactly (e.g., "Living Room")
|
|
required:
|
|
- spotify_uri
|
|
- type
|
|
- room
|
|
function:
|
|
type: script
|
|
sequence:
|
|
- service: media_player.select_source
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
source: "{{ room | replace('Living Room', 'd0166d6f83796b0f9059b0adf0f6cb0d81fa6376') | replace('Bedroom', 'a334c8ba989367b52303eaa9dbc424cdd83012b5') }}"
|
|
- repeat:
|
|
while:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ repeat.index <= 8 and (
|
|
state_attr('media_player.spotify_bhetherman', 'source') !=
|
|
(room | replace('Living Room', 'd0166d6f83796b0f9059b0adf0f6cb0d81fa6376') | replace('Bedroom', 'a334c8ba989367b52303eaa9dbc424cdd83012b5'))
|
|
or (state_attr('media_player.spotify_bhetherman', 'supported_features') | int(0)
|
|
| bitwise_and(512)) != 512
|
|
) }}
|
|
sequence:
|
|
- delay: "00:00:01"
|
|
- service: media_player.play_media
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
media_content_id: "{{ spotify_uri }}"
|
|
media_content_type: "{{ type }}"
|
|
enqueue: "{{ 'add' if type == 'track' else 'replace' }}"
|
|
|
|
- spec:
|
|
name: control_playback
|
|
description: >-
|
|
Control playback of music that is ALREADY PLAYING (pause, resume, stop, next track,
|
|
previous track, set volume, shuffle) on the currently active Spotify Connect session.
|
|
Only use volume_set when the user explicitly says the word "volume" or "percent"
|
|
(e.g. "turn the volume up", "set volume to 50", "50 percent volume").
|
|
Do NOT use this function to play a requested song, artist, or album — a number in a
|
|
song/artist/album name (e.g. "Back on 74", "Blink 182", "Sunset 2005") is part of the
|
|
title, never a volume level. Use search_spotify + play_music for any request to play
|
|
or start specific music, even if it contains a number.
|
|
parameters:
|
|
type: object
|
|
properties:
|
|
action:
|
|
type: string
|
|
enum: [pause, play, stop, next_track, previous_track, volume_set, shuffle_on, shuffle_off]
|
|
description: Playback control action
|
|
volume_level:
|
|
type: number
|
|
description: Volume level (0-100) for volume_set action only. Only set this when the user said the word "volume" or "percent".
|
|
required:
|
|
- action
|
|
function:
|
|
type: script
|
|
sequence:
|
|
- choose:
|
|
- conditions: "{{ action == 'pause' }}"
|
|
sequence:
|
|
- service: media_player.media_pause
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
- conditions: "{{ action == 'play' }}"
|
|
sequence:
|
|
- service: media_player.media_play
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
- conditions: "{{ action == 'stop' }}"
|
|
sequence:
|
|
# media_player.spotify_bhetherman does not support the media_stop
|
|
# service (STOP is not in its supported_features); Spotify Connect has
|
|
# no separate stop concept, so route "stop" through pause instead.
|
|
- service: media_player.media_pause
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
- conditions: "{{ action == 'next_track' }}"
|
|
sequence:
|
|
- service: media_player.media_next_track
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
- conditions: "{{ action == 'previous_track' }}"
|
|
sequence:
|
|
- service: media_player.media_previous_track
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
- conditions: "{{ action == 'volume_set' }}"
|
|
sequence:
|
|
- service: media_player.volume_set
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
volume_level: "{{ volume_level / 100 }}"
|
|
- conditions: "{{ action == 'shuffle_on' }}"
|
|
sequence:
|
|
- service: media_player.shuffle_set
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
shuffle: true
|
|
- conditions: "{{ action == 'shuffle_off' }}"
|
|
sequence:
|
|
- service: media_player.shuffle_set
|
|
target:
|
|
entity_id: media_player.spotify_bhetherman
|
|
data:
|
|
shuffle: false |