Hey everyone!
I recently transitioned from Amazon Echo devices to Home Assistant Voice, and one of the things I missed the most was playing Song Quiz. So, I decided to recreate it in Home Assistant using Music Assistant and conversation voice commands!
This blueprint allows you to play a music trivia game with up to four players. The game plays songs from a selected era, and players have to guess the song and artist to earn points. After five rounds, the player with the highest score wins!
For now you need to use your wake word, after every questions the voice satellite asks. I want to change that in the future when assist_satellite.start_conversation becomes available.
You can change the Spotify playlist that it will use for each era or just go with my defaults.
Setup Instructions
Requirements:
- Music Assistant integration installed and configured
- A working Home Assistant Voice setup
- Four counters to track player scores:
counter.song_quiz_player_1
counter.song_quiz_player_2
counter.song_quiz_player_3
counter.song_quiz_player_4
How to Play
- Say “Start Song Quiz” to begin.
- Home Assistant will ask how many players are playing (1 player, 2 players,…)
- Choose a music era: 90s, 2000s, 2010s, or Now.
- Each player listens to a song snippet and tries to guess the song and artist.
- Players earn 25 points for guessing both correctly, 10 points for getting one right.
- After 5 rounds, the game announces the winner!
Voice Commands:
“Start Song Quiz” – Begins a new game
“Stop Song Quiz” – Ends the game
Download the Blueprint
Paste the YAML into Automations > Blueprints, or grab it from my GitHub:
GitHub Link
I’d love to hear feedback from the community! Let me know if you try it out or if you have any feature suggestions.
blueprint:
name: Song Quiz
description: >-
A voice-controlled music trivia game using Home Assistant Voice and Music Assistant. Players guess
songs based on a selected era, earning points for correct answers. The game
lasts 5 rounds, and the player with the highest score wins.
**Setup Required:** Ensure you have the following counters set up in Home Assistant:
- `counter.song_quiz_player_1`
- `counter.song_quiz_player_2`
- `counter.song_quiz_player_3`
- `counter.song_quiz_player_4`
domain: automation
input:
game_rounds:
name: Game Rounds
description: How many rounds a game has.
default: 5
default_volume:
name: Default Volume
description: Default volume for playing the music.
default: 0.6
spotify_uris_90s:
name: Spotify URI for 90s
description: Playlist URI for the 90s era.
default: "spotify:playlist:37i9dQZF1DXbTxeAdrVG2l"
spotify_uris_2000s:
name: Spotify URI for 2000s
description: Playlist URI for the 2000s era.
default: "spotify:playlist:37i9dQZF1DX4o1oenSJRJd"
spotify_uris_2010s:
name: Spotify URI for 2010s
description: Playlist URI for the 2010s era.
default: "spotify:playlist:37i9dQZF1DX4UtSsGT1Sbe"
spotify_uris_now:
name: Spotify URI for Now
description: Playlist URI for current hits.
default: "spotify:playlist:37i9dQZEVXbMDoHDwVN2tF"
spotify_uris_default:
name: Spotify URI for fallback
description: Playlist URI as a fallback if no era is set.
default: "spotify:playlist:37i9dQZF1DX4UtSsGT1Sbe"
trigger:
- command:
- song quiz
- start song quiz
trigger: conversation
- trigger: conversation
command: stop song quiz
id: stop
actions:
- variables:
assist_area: "{{ trigger.device_id | device_attr('area_id') }}"
media_player_temp: >-
{% set players = area_entities(assist_area) | select('match', 'media_player\\..*') | list %}
{% set valid_players = players | select('in', integration_entities('music_assistant')) | list %}
{{ valid_players | first if valid_players else none }}
media_player: "{{ media_player_temp if media_player_temp else 'media_player.' ~ trigger.device_id.split('.')[1] }}"
- choose:
- conditions:
- condition: trigger
id:
- stop
sequence:
- action: media_player.media_pause
metadata: {}
data: {}
target:
entity_id: "{{ media_player }}"
- set_conversation_response: Song quiz stopped. Thanks for playing!
default:
- if:
- condition: template
value_template: "{{ is_state(media_player,'playing') }}"
then:
- action: media_player.media_pause
data: {}
target:
entity_id: "{{ media_player }}"
- action: counter.reset
target:
entity_id:
- counter.song_quiz_player_1
- counter.song_quiz_player_2
- counter.song_quiz_player_3
- counter.song_quiz_player_4
data: {}
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
Welcome to Song Quiz! How many players are playing? Names are
optional.
action: assist_satellite.announce
- wait_for_trigger:
- trigger: conversation
command:
- "{number} players"
- "{number} player"
- "{number} players {names}"
continue_on_timeout: false
timeout:
hours: 0
minutes: 0
seconds: 30
milliseconds: 0
- variables:
num_players: >-
{% set mapping = {'One': 1, 'Two': 2, 'Three': 3, 'Four': 4} %} {{
mapping.get(wait.trigger.slots.number, 1) }}
names_specified: "{{ 'names' in wait.trigger.slots }}"
player_names: |-
{% if names_specified %}
{{ wait.trigger.slots.names.replace(' and ', ' ').split(' ')[:num_players] }}
{% else %}
{{ ['Player 1', 'Player 2', 'Player 3', 'Player 4'][:num_players] }}
{% endif %}
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
{% if names_specified %}Welcome, {{ player_names | join(', ') }}!{%
else %}Great, {{ num_players }} Players! {% endif %} Choose a music
era: 90s, 2000s, 2010s or now.
action: assist_satellite.announce
- wait_for_trigger:
- trigger: conversation
command:
- "{time}"
continue_on_timeout: false
timeout:
hours: 0
minutes: 0
seconds: 30
milliseconds: 0
- variables:
era: "{{ wait.trigger.slots.time }}"
playlist_uri: >-
{% if era == '90s' %} !input spotify_uris_90s
{% elif era == '2000s' %} !input spotify_uris_2000s
{% elif era == '2010s' %} !input spotify_uris_2010s
{% elif era == 'now' %} !input spotify_uris_now
{% else %} !input spotify_uris_default {% endif %}
- variables:
round: 0
- repeat:
count: !input game_rounds
sequence:
- variables:
round: "{{ repeat.index }}"
- target:
device_id: "{{ trigger.device_id }}"
data:
message: Round {{ round }} begins!
action: assist_satellite.announce
- repeat:
count: "{{ num_players }}"
sequence:
- variables:
player: "{{ repeat.index }}"
default_volume: !input default_volume
current_volume: >-
{{ current_volume if current_volume else default_volume
}}
- if:
- condition: template
value_template: "{{ not is_state(media_player,\"playing\") }}"
then:
- action: music_assistant.play_media
data:
media_type: playlist
media_id: "{{ playlist_uri | string }}"
target:
entity_id: "{{ media_player }}"
- delay:
hours: 0
minutes: 0
seconds: 5
milliseconds: 0
- action: media_player.shuffle_set
data:
shuffle: true
target:
entity_id: "{{ media_player }}"
- delay:
hours: 0
minutes: 0
seconds: 2
milliseconds: 0
- variables:
current_volume: "{{ state_attr(media_player, 'volume_level') }}"
- data:
entity_id: "{{ media_player }}"
volume_level: 0.2
action: media_player.volume_set
- target:
device_id: "{{ trigger.device_id }}"
data:
message: "{{ player_names[player - 1] }}, listen to your song!"
action: assist_satellite.announce
- action: media_player.media_next_track
data: {}
target:
entity_id: "{{ media_player }}"
- data:
entity_id: "{{ media_player }}"
volume_level: "{{ current_volume | float }}"
action: media_player.volume_set
- delay: "00:00:30"
- variables:
current_volume: "{{ state_attr(media_player, 'volume_level') }}"
- data:
entity_id: "{{ media_player }}"
volume_level: 0.2
action: media_player.volume_set
- target:
device_id: "{{ trigger.device_id }}"
data:
message: "{{ player_names[player - 1] }}, what is your guess?"
action: assist_satellite.announce
- wait_for_trigger:
- trigger: conversation
command:
- "{guess}"
continue_on_timeout: true
timeout:
hours: 0
minutes: 0
seconds: 30
milliseconds: 0
- variables:
answer: "{{ wait.trigger.slots.guess | lower }}"
correct_song: >-
{% set song = state_attr(media_player, 'media_title') |
lower %} {% if song is string %}
{% set song = song | regex_replace(find='\[.*?\]|\(.*?\)', replace='') %} {# Remove anything in brackets #}
{% set song = song | regex_replace(find='[0-9!?.,]', replace='') %} {# Remove numbers, exclamation marks, and punctuation #}
{{ song | trim }}
{% else %}
""
{% endif %}
correct_artist: >-
{% set artist = state_attr(media_player, 'media_artist')
| lower %} {% if artist is string %}
{% set artist = artist.split('/')[0] %} {# Take only the first part if there's a '/' #}
{% set artist = artist | regex_replace(find='[0-9&.-]', replace='', ignorecase=True) %} {# Remove numbers and '&' #}
{{ artist | trim }}
{% else %}
{% if artist is iterable %}
{{ artist | join(' / ') | regex_replace(find='[0-9&.-]', replace='', ignorecase=True) | trim }}
{% else %}
""
{% endif %}
{% endif %}
counter_entity: |-
{% if player == 1 %}
counter.song_quiz_player_1
{% elif player == 2 %}
counter.song_quiz_player_2
{% elif player == 3 %}
counter.song_quiz_player_3
{% else %}
counter.song_quiz_player_4
{% endif %}
- choose:
- conditions:
- condition: template
value_template: >-
{{ correct_song in answer | lower and
correct_artist in answer | lower }}
sequence:
- action: counter.set_value
data:
value: >-
{{ states(counter_entity) |
int + 25 }}
target:
entity_id: "{{ counter_entity }}"
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
Bazinga! {{ player_names[player - 1] }} now has {{
states(counter_entity) }}
points.
action: assist_satellite.announce
- conditions:
- condition: template
value_template: >-
{{ correct_song in answer | lower or
correct_artist in answer | lower }}
sequence:
- action: counter.set_value
data:
value: >-
{{ states(counter_entity) |
int + 10 }}
target:
entity_id: "{{ counter_entity }}"
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
Great Job! {{ player_names[player - 1] }} now has {{
states(counter_entity) }}
points.
action: assist_satellite.announce
default:
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
Sorry thats incorrect! {{ player_names[player - 1] }} now
has {{ states(counter_entity) }}
points.
action: assist_satellite.announce
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
End of round {{ round }}! Here are the standings: {{
player_names[0] }}: {{ states("counter.song_quiz_player_1") }}
points, {% if num_players > 1 %} {{ player_names[1] }}: {{
states("counter.song_quiz_player_2") }} points, {% endif %} {%
if num_players > 2 %} {{ player_names[2] }}: {{
states("counter.song_quiz_player_3") }} points, {% endif %} {%
if num_players > 3 %} {{ player_names[4] }}: {{
states("counter.song_quiz_player_4") }} points. {% endif %}
action: assist_satellite.announce
- target:
device_id: "{{ trigger.device_id }}"
data:
message: "Game over! Let's see the final scores:"
action: assist_satellite.announce
- repeat:
count: "{{ num_players }}"
sequence:
- variables:
player: "{{ repeat.index }}"
- target:
device_id: "{{ trigger.device_id }}"
data:
message: >-
{{ player_names[player - 1] }} scored {{
states("counter.song_quiz_player_1") if player == 1 else
states("counter.song_quiz_player_2") if player == 2 else
states("counter.song_quiz_player_3") if player == 3 else
states("counter.song_quiz_player_4") }} points.
action: assist_satellite.announce
- variables:
scores: |-
{% set score_list = [
states("counter.song_quiz_player_1") | int,
states("counter.song_quiz_player_2") | int,
states("counter.song_quiz_player_3") | int,
states("counter.song_quiz_player_4") | int
] %} {{ score_list[:num_players] }}
highest_score: "{{ scores | max }}"
winners: |-
{% if highest_score %}
{% set player_ids = [1, 2, 3, 4][:num_players] %}
{% set ns = namespace(winners=[]) %}
{% for i in range(num_players) %}
{% if scores[i] == highest_score %}
{% set ns.winners = ns.winners + [player_ids[i]] %}
{% endif %}
{% endfor %}
{{ ns.winners }}
{% else %}
[]
{% endif %}
- target:
device_id: "{{ trigger.device_id }}"
data:
message: |-
{% if not highest_score %}
No winner this time! Better luck next time!
{% elif winners | length == 1 %}
The winner is Player {{ winners[0] }}! Congratulations!
{% else %}
It's a tie between Players {{ winners | join(' and ') }}! Well played!
{% endif %}
action: assist_satellite.announce
- action: media_player.media_pause
metadata: {}
data: {}
target:
entity_id: "{{ media_player }}"
mode: restart