Sonos Volumes with Input Numbers

Here’s my method of controlling Sonos volumes with HA syncing with the speakers. Coming soon is a method for controlling groups. I have the basic functionality working but have to work out a few bugs. Anyways, here’s some volume controls:

43 PM

Input Numbers (use 1 per speaker. Note you may wish to play with the max number for comfortable levels)

  sonos_guest_room_volume:
    name: Guest Room Volume
    min: 0
    max: 60
    step: 1
  sonos_living_room_volume:
    name: Living Room Volume
    min: 0
    max: 60
    step: 1
  sonos_master_bedroom_volume:
    name: Master Bedroom Volume
    min: 0
    max: 60
    step: 1
  sonos_office_volume:
    name: Office Volume
    min: 0
    max: 60
    step: 1
  sonos_basement_volume:
    name: Basement Volume
    min: 0
    max: 100
    step: 1

Sensors (you’ll need 2 per speaker. One is the volume from Sonos and the other compares if there’s a change between HA and Sonos, for use in the automations):

  sonos_guest_room_volume:
    value_template: >-
       {% set volume = states.media_player.sonos_guest_room.attributes.volume_level %}
       {% set new_volume = volume * 100 %}
       {{ new_volume}}
  sonos_guest_room_volume_changed:
    value_template: '{% if (states.input_number.sonos_guest_room_volume.state | int) != (states.sensor.sonos_guest_room_volume.state | int ) %}true{%else%}false{% endif %}'
  sonos_living_room_volume:
    value_template: >-
       {% set volume = states.media_player.sonos_living_room.attributes.volume_level %}
       {% set new_volume = volume * 100 %}
       {{ new_volume}}
  sonos_living_room_volume_changed:
    value_template: '{% if (states.input_number.sonos_living_room_volume.state | int) != (states.sensor.sonos_living_room_volume.state | int ) %}true{%else%}false{% endif %}'
  sonos_master_bedroom_volume:
    value_template: >-
       {% set volume = states.media_player.sonos_master_bedroom.attributes.volume_level %}
       {% set new_volume = volume * 100 %}
       {{ new_volume}}
  sonos_master_bedroom_volume_changed:
    value_template: '{% if (states.input_number.sonos_master_bedroom_volume.state | int) != (states.sensor.sonos_master_bedroom_volume.state | int ) %}true{%else%}false{% endif %}'
  sonos_office_volume:
    value_template: >-
       {% set volume = states.media_player.sonos_office.attributes.volume_level %}
       {% set new_volume = volume * 100 %}
       {{ new_volume}}
  sonos_office_volume_changed:
    value_template: '{% if (states.input_number.sonos_office_volume.state | int) != (states.sensor.sonos_office_volume.state | int ) %}true{%else%}false{% endif %}'
  sonos_basement_volume:
    value_template: >-
       {% set volume = states.media_player.sonos_basement.attributes.volume_level %}
       {% set new_volume = volume * 100 %}
       {{ new_volume}}
  sonos_basement_volume_changed:
    value_template: '{% if (states.input_number.sonos_basement_volume.state | int) != (states.sensor.sonos_basement_volume.state | int ) %}true{%else%}false{% endif %}'

Automations (the first is to set the volume from Sonos and the second is to send volume from HA):

  - alias: 'Sonos Set Input Number From Sonos Volume'
    initial_state: on
    trigger:
      - platform: state
        entity_id: sensor.sonos_living_room_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_guest_room_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_master_bedroom_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_office_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_basement_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: homeassistant
        event: start
    action:
      - service: input_number.set_value
        data_template:
          entity_id: >-
            {% set room = trigger.entity_id.split(".")[1] | replace("_changed", "") %}
            {% set entity = ["input_number",room]|join(".") %}
            {{entity}}
          value: >-
            {% set room = trigger.entity_id.split(".")[1] | replace("_volume_changed", "") %}
            {% if room == "sonos_basement" %}
              {{states.sensor.sonos_basement_volume.state }}
            {% elif room == "sonos_living_room" %}
              {{states.sensor.sonos_living_room_volume.state }}
            {% elif room == "sonos_guest_room" %}
              {{states.sensor.sonos_guest_room_volume.state }}
            {% elif room == "sonos_master_bedroom" %}
              {{states.sensor.sonos_master_bedroom_volume.state }}
            {% elif room == "sonos_office" %}
              {{states.sensor.sonos_office_volume.state }}
            {% endif %}




  - alias: 'Sonos Set Volume From Input Number'
    initial_state: on
    trigger:
      - platform: state
        entity_id: input_number.sonos_basement_volume
      - platform: state
        entity_id: input_number.sonos_living_room_volume
      - platform: state
        entity_id: input_number.sonos_master_bedroom_volume
      - platform: state
        entity_id: input_number.sonos_guest_room_volume
      - platform: state
        entity_id: input_number.sonos_office_volume
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: >-
            {% set room = trigger.entity_id.split(".")[1] %}
            {% set input_num = ["states.input_number",room,"state"]|join(".") %}
            {% set sonos_vol = ["states.sensor",room,"state"]|join(".") %}
            {% if input_num != sonos_vol %}true
            {%else%}false
            {% endif %}
    action:
      - service: media_player.volume_set
        data_template:
          entity_id: >-
            {% set room = trigger.entity_id.split(".")[1]|replace("_volume","")%}
            {% set entity = ["media_player",room]|join(".")%}
            {{entity}}
          volume_level: >-
            {% set room = trigger.entity_id.split(".")[1]|replace("_volume","")%}
            {% if room == "sonos_basement" %}
              {{states.input_number.sonos_basement_volume.state | int / 100 | float}}
            {% elif room == "sonos_living_room" %}
              {{states.input_number.sonos_living_room_volume.state | int / 100 | float}}
            {% elif room == "sonos_guest_room" %}
              {{states.input_number.sonos_guest_room_volume.state | int / 100 | float}}
            {% elif room == "sonos_master_bedroom" %}
              {{states.input_number.sonos_master_bedroom_volume.state | int / 100 | float}}
            {% elif room == "sonos_office" %}
              {{states.input_number.sonos_office_volume.state | int / 100 | float}}
            {% endif %}

Finally, the group for the frontend. Use entity_picture in the customize area at 192x192 pixels with transparent backgrounds. Google has lots, you just may need to resize:

  sonos_volumes:
    view: no
    name: Volumes
    entities:
      - input_number.sonos_living_room_volume
      - input_number.sonos_basement_volume
      - input_number.sonos_master_bedroom_volume
      - input_number.sonos_office_volume
      - input_number.sonos_guest_room_volume

That’s it!

6 Likes

Any update on controlling groups?

I can post what I have it doesn’t work as good as I’d like it to. The issue is the Sonos component has separate services for join and unjoin. There’s no way to just say I want these speakers together. You have to unjoin everything then join the ones you want.

@joshluster I’ve posted a new topic with how to group the speakers here…

Awesome, I was just working on this earlier today and banging my head against the wall! Ill take a look, thanks!

How do i change this automation to remove this error:
#---------
error
#---------
2018-04-01 15:25:06 ERROR (MainThread) [homeassistant.helpers.service] Error rendering data template: UndefinedError: 'dict object' has no attribute 'entity_id'
#---------
I only have one sonos (media_player.sonos_living_room) so maybe there is a way to simplify this automation so that i can get rid of the error.
#---------

automation:
  - alias: 'Sonos Set Input Number From Sonos Volume'
    initial_state: on
    trigger:
      - platform: state
        entity_id: sensor.sonos_living_room_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_guest_room_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_master_bedroom_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_office_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: state
        entity_id: sensor.sonos_basement_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: homeassistant
        event: start
    action:
      - service: input_number.set_value
        data_template:
          entity_id: >-
            {% set room = trigger.entity_id.split(".")[1] | replace("_changed", "") %}
            {% set entity = ["input_number",room]|join(".") %}
            {{entity}}
          value: >-
            {% set room = trigger.entity_id.split(".")[1] | replace("_volume_changed", "") %}
            {% if room == "sonos_basement" %}
              {{states.sensor.sonos_basement_volume.state }}
            {% elif room == "sonos_living_room" %}
              {{states.sensor.sonos_living_room_volume.state }}
            {% elif room == "sonos_guest_room" %}
              {{states.sensor.sonos_guest_room_volume.state }}
            {% elif room == "sonos_master_bedroom" %}
              {{states.sensor.sonos_master_bedroom_volume.state }}
            {% elif room == "sonos_office" %}
              {{states.sensor.sonos_office_volume.state }}
            {% endif %}

@mcfrojd

You should delete all of the triggers that you don’t need. Just leave the one sensor that you made for your Sonos player.

The triggers is not giving me the error, i have had them removed with same error, My guess is the action:.
I have trimmed it down to this:

  - alias: 'Sonos Set Volume From Input Number'
    initial_state: on
    trigger:
      - platform: state
        entity_id: input_number.sonos_living_room_volume
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: >-
            {% set room = trigger.entity_id.split(".")[1] %}
            {% set input_num = ["states.input_number",room,"state"]|join(".") %}
            {% set sonos_vol = ["states.sensor",room,"state"]|join(".") %}
            {% if input_num != sonos_vol %}true
            {%else%}false
            {% endif %}
    action:
      - service: media_player.volume_set
        data_template:
          entity_id: >-
            {% set room = trigger.entity_id.split(".")[1]|replace("_volume","")%}
            {% set entity = ["media_player",room]|join(".")%}
            {{entity}}
          volume_level: >-
            {% set room = trigger.entity_id.split(".")[1]|replace("_volume","")%}
            {% if room == "sonos_living_room" %}
              {{states.input_number.sonos_living_room_volume.state | int / 100 | float}}
            {% endif %}

But i still get that error:

2018-04-01 17:56:47 ERROR (MainThread) [homeassistant.helpers.service] Error rendering data template: UndefinedError: 'dict object' has no attribute 'entity_id'

What is the entity_id of your Sonos media_player called?

You mean from entity_registry.yaml ?

media_player.sonos_living_room:
  platform: sonos
  unique_id: RINCON_B8E93776677C01400

@mcfrojd I’ll check it out later and see if I can reproduce the error. Not sure why you’re getting this.

Great tutorial, well written and to the point. One question just by curiosity: How would you qualify the advantages of having the volume config done in HA rather than in the native SONOS app? Any particular reasons or just because you “can” do it? Cheers, Markus

@mastermarkush mostly because I hate switching apps and like everything in one place.

Have you found a solution on this error, @mcfrojd?
I have the same error message - and can’t figure out the origin of it…

1 Like

No solution, still same error.

It something in this automation!
If i comment it out the error is gone.

  - alias: 'Sonos Set Input Number From Sonos Volume'
    initial_state: on
    trigger:
      - platform: state
        entity_id: sensor.sonos_living_room_volume_changed
        to: 'true'
        for:
          seconds: 2
      - platform: homeassistant
        event: start
    action:
      - service: input_number.set_value
        data_template:
          entity_id: >-
            {% set room = trigger.entity_id.split(".")[1]|replace("_changed","")%}
            {% set entity = ["input_number",room]|join(".")%}
            {{entity}}
          value: >-
            {% set room = trigger.entity_id.split(".")[1]|replace("_volume_changed","")%}
            {% if room == "sonos_living_room" %}
              {{states.sensor.sonos_living_room_volume.state }}
            {% endif %}

Really interesting code you got there, congrats for that and thanks for the share.

I have a question nor really related to the main subject of this post, sorry for that, but I would like to ask you how to you get those gorgeous icons on a card. Those look really cool!

@jfpcarreira

It’s fairly easy. You just need to download a picture and put it into a directory and reference the location using entity_picture. Here’s the page describing it depending on if you use the UI or yaml in your config.

https://www.home-assistant.io/docs/configuration/customizing-devices/

1 Like

Thanks! :+1:

@Jer78 sorry to bring back up this old topic but I was wondering if you’ve kept up with this functionality? I came across this thread from a search in trying to sync volume across a couple Sonos speakers I have. Do you know if this is still the only way to do it? (i.e. create new sensors, automations like you have laid out)

I will try to play around with it but your automations confuse me quite a bit so I’m slightly intimidated. I have 2 sonos speakers in my basement which I want to keep their volume always in-sync. My thought was to capture a volume-change trigger and then set volume of unchanged speaker == volume of changed speaker… It looks like that’s what’s going on here and we just had to create the sensors for volume change because it’s not standard functionality? and then to be honest I’m not quite sure what the automations are doing. I assume it’s doing what I just outlined, but with a lot of variables depending on what speaker was changed that I wouldn’t need since it’s only 2 I’m syncing?

sorry again for my ignorance, just trying to make sure this is still the best way to go about it since it was originally posted over 3 years ago and hopefully get a better understanding of how the automation pieces are working