Select all Sonos media_players

Hi All,
I have a simple question where I just cant find or figure out the answer to. It must be a simple thing that I am just missing, so sorry in advance :upside_down_face:

I am searching for a selectattr or rejectattr that results in just my Sonos rooms/entities. The idea (among other things) is that I afterwards want to make a select on source, so once a speaker has the same source (say radio channel), it will be joined instead of playing on it’s own.

I was able to achieve this as a PoC using this Templating - Home Assistant, but it doesn’t suit the job.

{% for state in states.media_player -%}
  {%- if is_device_attr(state.entity_id, 'manufacturer', 'Sonos') %} {{ "\n"}}- {{ state.name }} {% endif -%}
{%- endfor %}

Anyone that can help me with the selection that returns just the devices/entities of a certain manufacturer?

Look at the attributes listed for your Sonos media_player entity in Developer Tools > States. Those are the only attributes accessible to a template.

The first thing you’ll notice is that manufacturer is not included in the list of available attributes.

If you wish, you can add a custom attribute that indicates the entity’s integration is sonos. Once that’s done, a template can easily report all media_players whose integration attribute is sonos.

{{ states.media_player | selectattr('attributes.integration', 'eq', 'sonos') | map(attribute='entity_id') | list }}

In this example I called the custom attribute integration but you can choose whatever name you want (provided it isn’t identical to an existing attribute’s name).

Or depending on how your Sonos devices entity_ids are named you could use this:

{{states.media_player | selectattr("entity_id", "search", "sonos") | map(attribute='entity_id') | list }}

All of mine contain the string “sonos” (‘media_player.sonos_diningroom’, ‘media_player.sonos_kitchen’, ‘media_player.sonos_livingroom’).

you won’t need the extra customization step that way.

Using the Sonos app, did you name it ‘Sonos Diningroom’? Or ‘Diningroom’ and then renamed its entity_id in Home Assistant?

I renamed the entity_id afterwards.

Hmmm, sounds like an “extra customization step”. :wink:

yeah, I knew where that was going. I could see it a mile away. :laughing:

I have several media players and some of them are in the same room so I need to differentiate those media players from each other so I don’t have two (or more) “media_player.livingroom” entities. If I’m already differentiating I may as well maintain a consistent naming method to help keep it all straight without needing to look in the entity attributes to see which entity is a Sonos speaker or which is an Echo device or my living room tv, etc.

and I don’t also now have to keep track of another bit of code that I had to create to do the customization. It’s all self contained in the entity_id.

I’m not against customizations if they are useful and not redundant. It’s a “two birds with one stone” thing.

I’ve actually used that exact method of creating customizations for all of my zwave devices with a “integration: zwavejs” attribute since the entity naming didn’t lend itself to differentiating the other way.

I assume you are using the Alexa Media Player integration? That would explain why your Echo devices appear as media_player entities.

If the room has both a Sonos and an Echo then you have more than one similarly named media_player and therefore need to differentiate them. I understand how, in this situation, adding sonos to the object_id becomes mandatory.

In my case, I am not using Alexa Media Player for my “Alexa-enabled” devices; only the Sonos speakers are represented as media_players. I’ve developed the habit of adding a custom integration attribute to most entities. It makes it easier for me to differentiate sensors for example (Homekit? MQTT? Tasmota?).

Hmmm, I first read it on my phone and mixed some posts.
But looking at the computer, I think I’ll look further since there is a relation between manufacturer and device/entity as shown in the example. Looking at the level HA is at the moment, it’s hard to imagine that it is not possible to just list all devices of a certain manufacturer.

Maybe the way to go is to create an array output of the PoC

{% for state in states.media_player -%}
  {%- if is_device_attr(state.entity_id, 'manufacturer', 'Sonos') %} {{ "\n"}}- {{ state.name }} {% endif -%}
{%- endfor %}

Renaming my entities isn’t my preferred choice either, then I guess I will just create a group with the entities in it. Probably the most CPU friendly way too.

Thanks all for your replies and I’ll report back once I have a nice solution.

Inspired by my own PoC, the code below returns an array with Sonos entiries. Of course this can be used for any manufacturer and with a little creativity with areas too.

{% set Sonos = namespace(player='') %}
{% for state in states.media_player -%}
  {%- if is_device_attr(state.entity_id, 'manufacturer', 'Sonos') %}
     {% set Sonos.player = Sonos.player + state.entity_id+',' %}
  {% endif -%}
{%- endfor %}

{{ Sonos.player.split(',')[0:-1] }}

I keep forgetting about the new device-oriented functions that were recently added to Home Assistant. :man_facepalming:

You’re correct, it’s now possible for a template to access more information than what is shown in Developer Tools> States (by referencing its associated device).

If you’re interested, here’s a shorter way to get a list of all Sonos media_player entities:

{% set ns = namespace(player=[]) %}
{% for state in states.media_player if is_device_attr(state.entity_id, 'manufacturer', 'Sonos') %}
  {% set ns.player = ns.player + [state.entity_id] %}
{%- endfor %}
{{ ns.player }}

Yes, a group would be the most efficient. To reference the group’s members in a template, just use the expand function.

If you wish, you can create a dynamic group; create an automation triggered on startup (and Reload Group) that uses your template to construct a group. Should you ever add another Sonos speaker, executing Reload Group, or restarting Home Assistant, will automatically add it to the group.

However, if you don’t foresee that you will be adding many new speakers, a statically defined group is simpler to implement.

That’s exactly what I did.
Referring to your improvement, thanks for that. Besides that it’s shorter, what I like, It’s better too cause mine has an empty element that I had to remove using the -1 in {{ Sonos.player.split(',')[0:-1] }}.

I’m following a similar method to group all Sono’s speakers in the house. What i can’t figure out is how to set one of the discovered entities (speakers) as Master. In your case, after joining the speakers, do you set one as Master? If so, what code is used to complete the task?

If you are referring to the sonos.join service call, the master must be defined at the moment the join is performed (not afterwards).

FWIW, I have several predefined groups (upstairs, downstairs, all, etc) where the first group member represents the master. Therefore the first item (zeroth index) in the group’s entity_id attribute is the master speaker (and supplied as the value for the master option of a sonos.join service call).

What’s important is that you have to reference it directly from the list in the group’s entity_id attribute and not by expanding the group. When you use expand, the list it produces is sorted alphabetically, so whatever you had as the first item may no longer be first (and you can no longer be certain which one represents the group’s master).

@123; yes, I am referring to the sonos.join service call (should have explained that!).

I have a couple of Sono’s units which are used on a patio. Sometimes they’re not turned on so predefining groups doesn’t seem to be an option. I’m looking for a way of determining determining which units are online then joining those units into a group. I understand the master must be defined at the time the join command is issued. I’m thinking of using the first item in the array list as the master. What I cannot figure out is how to use information collected in the array to setup the group. How do I know how many speakers are defined in the group. The array.length function seems like it will provide a count on the number of active speakers. How do I use this information to create a group with the first item in the array set as the master?

The template should simply reject media_players whose state is currently unavailable or unknown.

Maybe it’s best to create a new topic so other users with the same question are able to find it (in the future) as-well. If the question is clear, the solution isn’t likely to be very complicated. Now the solution might remain unseen because this topic already has a solution.

1 Like

I just ran into a problem where I needed a quick way to get all of the Sonos media players.

This worked for me and leverages the new integration_entities filter that was added in the last few months Templating - Home Assistant

{{ integration_entities("sonos") | select("match", "media_player") | list }}

The additional select is required because integration_entities returns additional entities like sensor.move_battery for my Sonos Move and binary_sensor.living_room_microphone for my Sonos Arc. If you don’t have a Move or Arc, it might not be necessary.

2 Likes

Nice work! :+1:

Yes, you still need to use select with other Sonos devices (they are all modeled using several kinds of entities).