Selecting between multiple sources with a button

I have a simple problem, though can’t seem to figure it out on my own. I have a physical button, connected to esp, configured through esphome showing in lovelace. I also have a SONOS speaker connected and configured and I can MANUALLY select between sources in lovelace. Everything good so far.
But here comes the catch: I cannot configure an automation to select between MULTIPLE sources based on button press, I can only do one source per one button (with media_player.select_source).

From programmers point of view i would love to have a variable being my index and that way selecting between array of sources like sources[number] and the number would increase or decrease based on button press. I can’t add another button since the remote is one made by yeelight, connected through ESP32’s BLE to esphome.

Anyway, I hope I made clear what my problem is. Just can’t think in HA way about the issue.

The list of sources in your sonos entity is an array.
So in your automation you can put the template code in it that basically says, select next array. All ingredients can be found:

Current source:
{{ state_attr("media_player.your_sonos","media_channel") }}

All sources:
{{ state_attr("media_player.your_sonos", "source_list") }}

Length of source array (remember, arrays start at 0):
{{ state_attr("media_player.your_sonos", "source_list")|count }}

Position of source:
{{ state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","media_channel")) }}

Next source
{{ state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","media_channel")) + 1 if state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","media_channel")) < state_attr("media_player.your_sonos", "source_list")|count - 1 else 0 }}

Idealy (because of readability) you put it in a variable that you use when selecting, so that makes it:
{% set next_source = state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","media_channel")) + 1 if state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","media_channel"))  != state_attr("media_player.your_sonos", "source_list")|count - 1 else 0 %}
{{ state_attr("media_player.your_sonos", "source_list")[next_source] }}

Edit 1: made one mistake in selecting the first element if the array. It said 1 and should have been 0
Edit 2: source has been moved to media_channel, updated the solution state_attr("media_player.your_sonos","source") to state_attr("media_player.your_sonos","media_channel").
Edit 3: Added a “solution” for a situation after you upgraded to 2022.4
Edit 4: Update on how to use the favorites sensor that was (re)introduced in v 2022.5


ohh and in adition, you might want to add a condition that checks if the current source is even a radio source that is listed. One can do so by creating a template condition like this:

{{ state_attr("media_player.your_sonos","source") in state_attr("media_player.your_sonos", "source_list") }}

The previous source is not too complicated either by the way:

{% set prev_source = state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","source")) - 1 if state_attr("media_player.your_sonos", "source_list").index(state_attr("media_player.your_sonos","source"))  != 0 else state_attr("media_player.your_sonos", "source_list")|count - 1 %}
{{ state_attr("media_player.your_sonos", "source_list")[prev_source] }}

That’s exactly what I’ve been looking for.
Thank you so much @Recte
Have a nice day :slight_smile:

You’re welcome!
To help others, you might want to mark it as solution.

Right, thanks for reminding me, this is literally my first post.

Since 2022.4 the favorites are not listed as attribute anymore. Luckily the media_player.select_source is still there. One could replace state_attr("media_player.your_sonos", "source_list") with an input_select helper that lists the favorites while the favorites will become hopefully accessible again in the (near) future.

Say you name the helper input_select.sonos_favorites the replacement looks like:

Old: state_attr("media_player.your_sonos", "source_list")
New: state_attr("input_select.sonos_favorites","options")

So for next source

service: media_player.select_source
  source: >-
    {% set next_source = state_attr("input_select.sonos_favorites",
    "options").index(state_attr("media_player.<your player>","media_channel"))
    + 1 if state_attr("input_select.sonos_favorites",
    "options").index(state_attr("media_player.<your player>","media_channel"))
    != state_attr("input_select.sonos_favorites", "options")|count - 1 else 0 %}
    {{ state_attr("input_select.sonos_favorites", "options")[next_source] }}
  entity_id: media_player.<your player>

For previous source:

service: media_player.select_source
  source: >-
    {% set prev_source = state_attr("input_select.sonos_favorites",
    "options").index(state_attr("media_player.<your player>", "media_channel"))
    - 1 if state_attr("input_select.sonos_favorites",
    "options").index(state_attr("media_player.<your player>","media_channel")) 
    != 0 else state_attr("input_select.sonos_favorites","options")|count - 1 %}
    {{ state_attr("input_select.sonos_favorites","options")[prev_source] }}
  entity_id: media_player.<your player>

The favorites returned in v2022.5. Regarding the code above, one should replace:
state_attr("input_select.sonos_favorites", "options")
(state_attr("sensor.sonos_favorites", "items").values() | list) and you are ready to go.