Sonos Automation

"I created an automation for Sonos that plays a random music playlist in the morning and at night. It allows you to choose different media players based on the time of day using specific triggers.

The automation also ensures that the current queue restarts and speakers rejoin if the queue was paused or if a speaker becomes ungrouped. It compares the selected players (like [media_player.bedroom, media_player.livingroom]) with all Sonos players (all_sonos_players) to make sure everything is in sync.

To use this automation, you can customize it by listing the specific media players you want to use in the grouped_players,master_player and all_sonos_player (grouped_players+master_player) variable (e.g., [media_player.bedroom, media_player.livingroom]). The automation will then check this list against all_sonos_players to keep everything aligned.

Media Player Sonos Automation
alias: Media Player Sonos-Navidrome
description: ""
trigger:
  - platform: time
    at: "06:05:00"
    enabled: true
    id: "1"
  - platform: time
    at: "22:00:00"
    id: "2"
  - alias: >-
      trigger if any of the sonos media_players are NOT in a unknown state or
      playing (ie idle or paused)
    platform: template
    value_template: |
      {{expand(integration_entities('sonos') 
          | select("match","media_player"))
          | list
          | count > expand(integration_entities('sonos')
          | select("match","media_player"))
          | selectattr('state','in',['unavailable','unknown','playing'])
          | list
          | count}}
    for:
      hours: 0
      minutes: 0
      seconds: 15
    id: "3"
  - platform: homeassistant
    event: start
    id: "4"
condition: []
action:
  - variables:
      grouped_players: |
        {{ states.media_player 
             | map(attribute='entity_id') 
             | select('is_device_attr', 'manufacturer', 'Sonos')
             | reject('is_device_attr', 'model', 'Port') | list }}
      master_player: |
        {{ states.media_player 
             | map(attribute='entity_id') 
             | select('is_device_attr', 'manufacturer', 'Sonos')
             | select('is_device_attr', 'model', 'Port') | list }}
      night_players: |
        {{ expand(integration_entities('sonos') 
             | select("match","media_player"))
             | selectattr('entity_id', 'search','bedroom')
             | map(attribute='entity_id')
             | list}}
      allsonosplayers: |
        {{ integration_entities("sonos") 
             | select("match", "media_player")
             | list
        }}
      sonosplaylist: |
        {{ ["FV:2/9","FV:2/17","FV:2/18", "FV:2/11"] }}
      nightplaylist: |
        {{ ["FV:2/14"] }}
  - alias: Morning
    if:
      - condition: trigger
        id:
          - "1"
    then:
      - repeat:
          sequence:
            - alias: Restart
              if:
                - condition: and
                  conditions:
                    - alias: player_count > players_playing
                      condition: template
                      value_template: |
                        {{ allsonosplayers
                             | count > expand(integration_entities('sonos')
                             | select("match","media_player"))
                             | selectattr('entity_id','in', allsonosplayers)
                             | selectattr('state','eq', 'playing')
                             | list
                             | count}}
              then:
                - alias: group sonos media_players
                  if:
                    - condition: template
                      value_template: |
                        {{master_player|count > 0}}
                  then:
                    - continue_on_error: true
                      service: media_player.join
                      target:
                        entity_id: |
                          {{master_player[1:]}}
                      data:
                        group_members: |
                          {{master_player[:1]+grouped_players}}
                      alias: >-
                        assign newer sonos media_players as master and the
                        remaining  as group_members
                  else:
                    - continue_on_error: true
                      service: media_player.join
                      target:
                        entity_id: |
                          {{allsonosplayers[1:]}}
                      data:
                        group_members: |
                          {{allsonosplayers[:1]}}
                      alias: >-
                        join all sonos media_players in a random order as last
                        resort
                - continue_on_error: true
                  service: media_player.volume_set
                  metadata: {}
                  data:
                    volume_level: 0.33
                  target:
                    entity_id: |
                      {{allsonosplayers}}
                - continue_on_error: true
                  service: media_player.play_media
                  target:
                    entity_id: |
                      {{allsonosplayers[1:]}}
                  data:
                    media_content_type: favorite_item_id
                    media_content_id: |
                      {{ sonosplaylist | random }}
                  alias: Play Navidrome playlist
                - delay:
                    hours: 0
                    minutes: 0
                    seconds: 5
                    milliseconds: 0
            - delay:
                hours: 0
                minutes: 0
                seconds: 1
                milliseconds: 0
          while:
            - condition: and
              conditions:
                - alias: "Check total # sono media_players = the # currently playing"
                  condition: template
                  value_template: |
                    {{ allsonosplayers
                         | count > expand(integration_entities('sonos')
                         | select("match","media_player"))
                         | selectattr('entity_id','in', allsonosplayers)
                         | selectattr('state', 'eq','playing')
                         | list
                         | count}}
                - condition: time
                  after: "06:00:00"
                  before: "21:30:00"
  - alias: Night
    if:
      - condition: trigger
        id:
          - "2"
    then:
      - alias: >-
          check if night_players has media_players in group_members attribute.
          then unjoin. test if the group members == night_players
        repeat:
          sequence:
            - alias: Unjoin
              repeat:
                sequence:
                  - continue_on_error: true
                    service: media_player.unjoin
                    target:
                      entity_id: |
                        {{allsonosplayers}}
                    data: {}
                  - delay:
                      hours: 0
                      minutes: 0
                      seconds: 1
                      milliseconds: 0
                until:
                  - condition: template
                    value_template: |
                      {{ expand(integration_entities('sonos') 
                           | select("match","media_player"))
                           | selectattr('entity_id','in', allsonosplayers)
                           | map(attribute='attributes')
                           | map(attribute='group_members')
                           | first
                           | list
                           | count == 1}}
            - alias: loop until paused
              repeat:
                sequence:
                  - continue_on_error: true
                    service: media_player.media_pause
                    metadata: {}
                    data: {}
                    target:
                      entity_id: |
                        {{allsonosplayers}}
                  - delay:
                      hours: 0
                      minutes: 0
                      seconds: 1
                      milliseconds: 0
                until:
                  - condition: template
                    value_template: |
                      {{ allsonosplayers
                           | count == expand(integration_entities('sonos')
                           | select("match","media_player"))
                           | selectattr('entity_id','in', allsonosplayers)
                           | selectattr('state','in', ['paused','idle'])
                           | list
                           | count}}
            - continue_on_error: true
              service: media_player.play_media
              target:
                entity_id: |
                  {{night_players}}
              data:
                media_content_type: favorite_item_id
                media_content_id: |
                  {{ nightplaylist | random }}
              alias: Play Navidrome playlist
            - continue_on_error: true
              service: media_player.volume_set
              target:
                entity_id: |
                  {{night_players}}
              data:
                volume_level: 0.3
              alias: Set Volume
            - delay:
                hours: 0
                minutes: 0
                seconds: 20
                milliseconds: 0
          until:
            - condition: and
              conditions:
                - condition: template
                  value_template: |
                    {{ expand(integration_entities('sonos') 
                         | select("match","media_player"))
                         | selectattr('entity_id','in', night_players)
                         | selectattr('state', 'eq','playing')
                         | list
                         | count == night_players
                         | count}}
                - condition: time
                  after: "21:59:00"
                  before: "23:59:00"
      - continue_on_error: true
        service: media_player.shuffle_set
        metadata: {}
        data:
          shuffle: true
        target:
          entity_id: |
            {{night_players}}
  - alias: Restart
    if:
      - condition: trigger
        id:
          - "3"
          - "4"
    then:
      - repeat:
          sequence:
            - alias: Rejoin Speakers
              if:
                - condition: and
                  conditions:
                    - alias: >-
                        Check if sonos has in its group_members the total number
                        of sonos media_players. if not rejoin missing speakers
                      condition: template
                      value_template: |
                        {{ allsonosplayers
                             | count > expand(integration_entities('sonos')
                             | select("match","media_player"))
                             | selectattr('entity_id','in', allsonosplayers)
                             | selectattr('state','eq', 'playing')
                             | map(attribute='attributes')
                             | map(attribute='group_members')
                             | first
                             | list
                             | count}}
                      enabled: true
                    - alias: playing>0
                      condition: template
                      value_template: |
                        {{ expand(integration_entities('sonos')
                             | select("match","media_player"))
                             | selectattr('state','eq', 'playing')
                             | map(attribute='entity_id')
                             | list
                             | count > 0}}
                      enabled: true
              then:
                - variables:
                    group_membersandpaused: |
                      {{expand(integration_entities('sonos')
                        | select("match","media_player"))
                        | selectattr('entity_id','in', allsonosplayers)
                        | selectattr('state', 'eq','playing')
                        | map(attribute='attributes')
                        | map(attribute='group_members')
                        | first
                        | list + expand(integration_entities('sonos')
                        | select("match","media_player"))
                        | selectattr('entity_id','in', allsonosplayers)
                        | selectattr('state','in', ['paused','idle'])
                        | map(attribute='attributes')
                        | map(attribute='group_members')
                        | list
                        | first}}
                    master: |
                      {{expand(integration_entities('sonos')
                         | select("match","media_player"))
                         | selectattr('entity_id','in', allsonosplayers)
                         | selectattr('state', 'eq','playing')
                         | map(attribute='attributes')
                         | map(attribute='group_members')
                         | first
                         | list
                         | first}}
                - continue_on_error: true
                  service: media_player.join
                  metadata: {}
                  data:
                    group_members: |
                      {{group_membersandpaused[1:]}}    
                  target:
                    entity_id: |
                      {{master}}
                  alias: >-
                    check if the group_members attribute of the master
                    media_players contains anything
                - delay:
                    hours: 0
                    minutes: 0
                    seconds: 20
                    milliseconds: 0
                - variables:
                    group_membersandpaused: |
                      {{expand(integration_entities('sonos')
                        | select("match","media_player"))
                        | selectattr('state', 'eq','playing')
                        | map(attribute='attributes')
                        | map(attribute='group_members')
                        | first
                        | list+expand(integration_entities('sonos')
                        | select("match","media_player"))
                        | selectattr('state','in', ['paused','idle'])
                        | map(attribute='entity_id')
                        | list}}
                    master: |
                      {{expand(integration_entities('sonos')
                         | select("match","media_player"))
                         | selectattr('state', 'eq','playing')
                         | map(attribute='attributes')
                         | map(attribute='group_members')
                         | first
                         | list
                         | first}}
                  enabled: false
            - alias: Trigger Play
              if:
                - condition: and
                  conditions:
                    - alias: Players grouped
                      condition: template
                      value_template: |
                        {{ allsonosplayers
                             | count == expand(integration_entities('sonos')
                             | select("match","media_player"))
                             | selectattr('state','eq', 'paused')
                             | selectattr('entity_id','in', allsonosplayers)
                             | map(attribute='attributes')
                             | map(attribute='group_members')
                             | first
                             | list
                             | count}}
                      enabled: true
                    - alias: Does playlist exist
                      condition: template
                      value_template: |
                        {{expand(integration_entities('sonos')
                            | select("match","media_player"))
                            | selectattr('state','eq', 'paused')
                            | selectattr('entity_id','in', allsonosplayers)
                            | map(attribute='attributes')
                            | map(attribute='media_playlist')
                            | list
                            | count >= 1}}
                      enabled: true
              then:
                - continue_on_error: true
                  service: media_player.media_play
                  metadata: {}
                  data: {}
                  target:
                    entity_id: |
                      {{master_player[1:]}}
                - delay:
                    hours: 0
                    minutes: 0
                    seconds: 20
                    milliseconds: 0
            - alias: Restart
              if:
                - condition: and
                  conditions:
                    - alias: "!=unavil or !=playing"
                      condition: and
                      conditions:
                        - alias: unavailable=0
                          condition: template
                          value_template: |
                            {{ expand(integration_entities('sonos') 
                                 | select("match","media_player"))
                                 | selectattr('entity_id','in', allsonosplayers)
                                 | selectattr('state','in',['unavailable','unknown'])
                                 | map(attribute='entity_id')
                                 | list
                                 | count == 0}}
                          enabled: true
                        - alias: player_count>players_playing
                          condition: template
                          value_template: |
                            {{ allsonosplayers
                                 | count>expand(integration_entities('sonos')
                                 | select("match","media_player"))
                                 | selectattr('entity_id','in', allsonosplayers)
                                 | selectattr('state','eq', 'playing')
                                 | list
                                 | count}}
                                  
                        - alias: players_playing=0
                          condition: template
                          value_template: |
                            {{expand(integration_entities('sonos')
                                | select("match","media_player"))
                                | selectattr('entity_id','in', allsonosplayers)
                                | selectattr('state','eq', 'playing')
                                | list
                                | count == 0}}
              then:
                - alias: Assign newer devices=Master
                  if:
                    - alias: Check newer sonos devices to add as master
                      condition: template
                      value_template: |
                        {{master_player|count>0}}
                  then:
                    - continue_on_error: true
                      service: media_player.join
                      target:
                        entity_id: |
                          {{master_player[1:]}}
                      data:
                        group_members: |
                          {{master_player[:1]+grouped_players}}
                      alias: Join Sonos Speakers
                  else:
                    - continue_on_error: true
                      service: media_player.join
                      target:
                        entity_id: |
                          {{integration_entities("sonos") 
                              | select("match","media_player")
                              | list
                              | first}}
                      data:
                        group_members: |
                          {% set sonos= integration_entities("sonos") |
                          select("match", "media_player") | list%}

                          {{sonos[1:]}}
                      alias: Join Sonos Speakers
                - continue_on_error: true
                  service: media_player.play_media
                  target:
                    entity_id: |
                      {{allsonosplayers[1:]}}
                  data:
                    media_content_type: favorite_item_id
                    media_content_id: |
                      {{ sonosplaylist | random }}
                  alias: Play Navidrome playlist
                - delay:
                    hours: 0
                    minutes: 0
                    seconds: 20
                    milliseconds: 0
            - delay:
                hours: 0
                minutes: 0
                seconds: 1
                milliseconds: 0
          while:
            - condition: and
              conditions:
                - alias: "Check total # sono speaker=the # playing"
                  condition: template
                  value_template: |
                    {{ allsonosplayers
                         | count > expand(integration_entities('sonos')
                         | select("match","media_player"))
                         | selectattr('entity_id','in', allsonosplayers)
                         | selectattr('state', 'eq','playing')
                         | list
                         | count}}
                - condition: time
                  after: "06:00:00"
                  before: "21:50:00"
mode: single

1 Like

Hi sabot105mm,

That looks like a nice automation a a very nice thing to share with the community.
I have to admit it’s a tiny thing, but words have meaning and it confused me until I looked at the code. You refer to script several times in the description, when it is clearly an automation. You use automation in several other places. I realize now that you are likely using the script term generically, but since it has a specific meaning here in the HA world, would you consider changing those words in your description?
Thanks for your consideration.

In any case, thanks for contributing to the community.