UPDATE: This project isn’t really supported going forward but it does work on my setup as of Jan 11, 2019. However, this is a better solution using the Mini Media Player component that I’d much prefer to recommend over here.
I finally got this working pretty well with some help from @petro so I thought I’d share in case anyone else is looking to have grouping capabilities of their Sonos speakers in HA.
1 - You’ll want to use a similar naming convention for your sonos speakers. Since you can set these up using the entity_registry I use media_player.sonos_room_name.
2 - Next, you’ll need to manually create input_booleans for every Sonos Speaker you want to control in groups. Just add _slave onto the end. Example media_player.sonos_living_room becomes input_boolean.sonos_living_room_slave:
input_boolean:
sonos_master_bedroom_slave:
name: Master Bedroom
sonos_guest_room_slave:
name: Guest Room
sonos_living_room_slave:
name: Living Room
sonos_basement_slave:
name: Basement
sonos_office_slave:
name: Office
3 - Make an input_select with your speakers, this will act as selecting the master.
input_select:
master_sonos:
name: Master Sonos Player
icon: mdi:itunes
options:
- Living Room
- Basement
- Master Bedroom
- Guest Room
- Office
4 - add the following sensors and under the entity_id’s add the input_booleans you created in the previous step. The rest of the sensor is not hardcoded so you shouldn’t have to modify anything else.
sensor:
platform: template
sensors:
sonos_join_speakers:
entity_id:
- input_boolean.sonos_living_room_slave
- input_boolean.sonos_guest_room_slave
- input_boolean.sonos_office_slave
- input_boolean.sonos_master_bedroom_slave
- input_boolean.sonos_basement_slave
value_template: >-
{% set master = states.sensor.sonos_group_master.state %}
{% for player in states.media_player -%}
{%- set domain, device = player.entity_id.split('.') -%}
{%- set new_domain = "input_boolean" -%}
{%- set new_device = [device, "slave"]|join("_") -%}
{%- set switch = [new_domain,new_device] | join(".") -%}
{%- if is_state(switch, 'on') and player.entity_id != master -%}
{{player.entity_id}},
{%- endif -%}
{%- endfor %}
sonos_unjoin_speakers:
entity_id:
- input_boolean.sonos_living_room_slave
- input_boolean.sonos_guest_room_slave
- input_boolean.sonos_office_slave
- input_boolean.sonos_master_bedroom_slave
- input_boolean.sonos_basement_slave
value_template: >-
{% set master = states.sensor.sonos_group_master.state %}
{% for player in states.media_player -%}
{%- set domain, device = player.entity_id.split('.') -%}
{%- set new_domain = "input_boolean" -%}
{%- set new_device = [device, "slave"]|join("_") -%}
{%- set switch = [new_domain,new_device] | join(".") -%}
{%- if is_state(switch, 'off') and player.entity_id != master -%}
{{player.entity_id}},
{%- endif -%}
{%- endfor %}
sonos_not_group_slaves:
friendly_name: Not Group Slaves
value_template: >-
{% for player in states.media_player if player.attributes.is_coordinator -%}
{%- if loop.last -%}{{player.entity_id}}--
{%- else -%}{{player.entity_id}};
{%- endif -%}
{%- endfor %}
sonos_group_slaves:
friendly_name: Group Slaves
value_template: >-
{% for player in states.media_player if player.state == 'playing' and not player.attributes.is_coordinator -%}
{%- if loop.last -%}{{player.entity_id}}--
{%- else -%}{{player.entity_id}};
{%- endif -%}
{%- endfor %}
sonos_group_song:
friendly_name: Group Song
value_template: >-
{% for player in states.media_player if player.state == 'playing' and not player.attributes.is_coordinator %}
{{player.attributes.media_title}}
{% endfor %}
sonos_group_master:
entity_id:
- sensor.sonos_group_song
value_template: >-
{% set group_song = states.sensor.sonos_group_song.state %}
{% for player in states.media_player if player.state == 'playing' and player.attributes.is_coordinator and player.attributes.media_title in group_song %}
{{player.entity_id}}
{% endfor %}
5 - Now we will create an automation that gets the state based on the sensors and turns the input_booleans on or off depending on the current state. You’ll just have to modify the master_sonos with your speakers and options from the input_select.
automation:
- alias: 'Set Sonos States'
initial_state: on
trigger:
- platform: state
entity_id: sensor.sonos_group_slaves
- platform: state
entity_id: sensor.sonos_not_group_slaves
- platform: state
entity_id: sensor.sonos_group_master
- platform: homeassistant
event: start
action:
- service: input_select.select_option
data_template:
entity_id: input_select.master_sonos
option: >-
{% set master = states.sensor.sonos_group_master.state %}
{% if master == "media_player.sonos_living_room"%}Living Room
{% elif master == "media_player.sonos_guest_room"%}Guest Room
{% elif master == "media_player.sonos_master_bedroom"%}Master Bedroom
{% elif master == "media_player.sonos_office"%}Office
{% elif master == "media_player.sonos_basement"%}Basement
{% endif %}
- service: input_boolean.turn_on
data_template:
entity_id: '{%- set slaves = states.sensor.sonos_group_slaves.state -%}{{slaves|replace("--", "_slave")|replace(";", "_slave, ")|replace("media_player", "input_boolean")}}'
- service: input_boolean.turn_off
data_template:
entity_id: '{%- set slaves = states.sensor.sonos_not_group_slaves.state -%}{{slaves|replace("--", "_slave")|replace(";", "_slave, ")|replace("media_player", "input_boolean")}}'
6 - Next step is to make a script that you’ll execute when you manually turn on or off speakers. Again, you’ll have to edit the master with your options in the input_select.
script:
sonos_group_speakers:
sequence:
- service: media_player.sonos_join
data_template:
master: >-
{% if is_state("input_select.master_sonos", "Living Room") %}media_player.sonos_living_room
{% elif is_state("input_select.master_sonos", "Basement") %}media_player.sonos_basement
{% elif is_state("input_select.master_sonos", "Guest Room") %}media_player.sonos_guest_room
{% elif is_state("input_select.master_sonos", "Master Bedroom") %}media_player.sonos_master_bedroom
{% elif is_state("input_select.master_sonos", "Office") %}media_player.sonos_office
{% endif %}
entity_id: '{%- set slaves = states.sensor.sonos_join_speakers.state -%}{{slaves[:-1]}}'
- service: media_player.sonos_unjoin
data_template:
entity_id: '{%- set slaves = states.sensor.sonos_unjoin_speakers.state -%}{{slaves[:-1]}}'
7 - Finally, add a group with everything.
group:
sonos_grouping_options:
view: no
name: Group Sonos Players
control: hidden
entities:
- input_select.master_sonos
- input_boolean.sonos_basement_slave
- input_boolean.sonos_living_room_slave
- input_boolean.sonos_master_bedroom_slave
- input_boolean.sonos_guest_room_slave
- input_boolean.sonos_office_slave
- script.sonos_group_speakers