Using templates for input_select.set_options (populating options within an input selector)

Hey all,

I am about 85% finished doing a project - getting HASS to control the Synology AudioStation - trying to keep a complicated task simple - but it has all come unstuck with regards to selecting the remote players available. I want this to be seamless and automatic, as this is the real archilles heel of the system.

My goal is to achieve this example below (from https://www.home-assistant.io/components/input_select/) - but using a template for the options - note the bit: options: [“Item A”, “Item B”, “Item C”]

# Example configuration.yaml entry
automation:
  - alias: example automation
    trigger:
      platform: event
      event_type: MY_CUSTOM_EVENT
    action:
      - service: input_select.set_options
        data:
          entity_id: input_select.who_cooks
          options: ["Item A", "Item B", "Item C"]   <-- this bit

So - my automation script goes like this - basically the same, but defining the options using a template instead:

update_synoplayers:
  alias: Update Synology Audio Players
  sequence:
    - service: shell_command.syno_audio_controls  ## Updates 'sensor.synoplayers'
      data:
        action: updatePlayers
        prot: any
        room: any
        id: all
    - delay: '00:00:05'
    - service: input_select.set_options
      data_template:
        entity_id: input_select.syno_players
        options: '[ "NotSet", "{% if states.sensor.synoplayers.attributes.data.players[0] %}{{ states.sensor.synoplayers.attributes.data.players[0].name }}"{% endif %}{% if states.sensor.synoplayers.attributes.data.players[1] %}, "{{ states.sensor.synoplayers.attributes.data.players[1].name }}"{% endif %}{% if states.sensor.synoplayers.attributes.data.players[2] %}, "{{ states.sensor.synoplayers.attributes.data.players[2].name }}"{% endif %}{% if states.sensor.synoplayers.attributes.data.players[3] %}, "{{ states.sensor.synoplayers.attributes.data.players[3].name }}"{% endif %}{% if states.sensor.synoplayers.attributes.data.players[4] %}, "{{ states.sensor.synoplayers.attributes.data.players[4].name }}"{% endif %}{% if states.sensor.synoplayers.attributes.data.players[5] %}, "{{ states.sensor.synoplayers.attributes.data.players[5].name }}" {% endif %} ]'

The template seems 100% fine, and I get no errors - this is how it looks in the templating section:
options: ‘[ “NotSet”, “TV_LCD60”, “Kodi_Lounge (DLNA)”, “Kodi_Lounge (AirPlay)”, “Multiple AirPlay Devices” ]’

My only issue - is that the input selector comes out looking like this (image below, this appears as the single option when I click it)… The template is clearly being parsed correctly but not accepted as an array for the Options

Can anyone tell me what I am doing wrong? Or perhaps inform me that ‘data_template’ doesn’t work here, saving me days of trial and error :smiley: ?

Many thanks if you have even read this far!

Regards
Matt

I’m curious if this will work too. Hoping to use a template to read the playlists I add in my Sonos players since right now I manually populate them. Any luck on this?

1 Like

the data_template construction should be alright, I use this and it works fine. much shorter, but the idea should be the same.

automation:
  - alias: Update Travel Destination options
    initial_state: 'on'
    trigger:
      - platform: state
        entity_id: input_text.travel_destination
      - platform: homeassistant
        event: start
    action:
      - service: input_select.set_options
        data_template:
          entity_id: input_select.travel_destination
          options:
            - Select
            - Brussels, Belgium
            - Berlin, Germany
            - Bayreuth, Germany
            - Paris, France
            - '{{ states("input_text.travel_destination") }}'
3 Likes

I got this to work based on @Mariusthvdb post. Finally, I don’t have to populate the playlists manually when adding or deleting them. I haven’t fully tested but looks like the limitations are you have to manually create a line. So in this example, if I wanted more than 21 in my list, I’d have to add a new line. But much better than the labor intensive I was doing it in the past.

  - alias: 'Set Music Options'
    initial_state: on
    trigger:
      - platform: time
        hours: '/6'
        minutes: '00'
      - platform: homeassistant
        event: start
    action:
      - service: input_select.set_options
        entity_id:
          - input_select.options_all_speakers_source
          - input_select.options_dinner_music_source
          - input_select.options_vacation_music
          - input_select.options_night_music
        data_template:
          options:
            - '{% if states.media_player.sonos_living_room.attributes.source_list[0] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[0]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[1] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[1]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[2] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[2]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[3] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[3]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[4] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[4]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[5] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[5]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[6] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[6]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[7] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[7]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[8] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[8]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[9] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[9]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[10] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[10]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[11] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[11]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[12] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[12]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[13] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[13]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[14] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[14]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[15] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[15]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[16] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[16]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[17] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[17]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[18] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[18]}}{%endif%}'
            - '{% if states.media_player.sonos_living_room.attributes.source_list[19] == "" %}""{%else%}{{states.media_player.sonos_living_room.attributes.source_list[19]}}{%endif%}'
1 Like

cool. You might want to consider a separate source_list_options template sensor to use in the data_template.

That way you keep things tidy, and wouldn’t need to change the logic if you’d decide to change the content…

glad you got it working.
Cheers,
Marius

@Mariusthvdb
How would you do the template sensor? My understanding was the list gets lost coming from a sensor. A simpler idea would be welcome! Thanks

If this true, keep it the way it is, I admit not to have a sensor like that. nor the sonos hardware so cant test it here.

But whenever an automation uses a template this big, I try to keep things separated.
Your data_template might be turned into a template_sensor’s value_template, and then you would be able to use the state of that sensor in the data_template of the automation…

of course, you’d have to try and see…
maybe give @petro a ring, he has template-shortening-capabilities beyond imagination :wink:

1 Like

So i’ve tried many times to get a list to work and the problem is that jinja just DOES NOT return lists. It’s super annoying. It can return items in a list, and I believe this works:

    data_template:
      options: >
        {% for item in state_attr('media_player.sonos_living_room', 'source_list') %}
        - {{ item }}
        {% endfor %}

The reason I got rid of the “” if item == “” is because your item will be populated or not. If it’s populated with “”, no reason to check for it if you are going to return “”. Just return the item. You can actually use that to simplify your original list (that you may have to use still if the above code doesn’t work).

 - "{{ state_attr('media_player.sonos_living_room','source_list')[0] }}"
3 Likes

Nice! Agreed it isn’t perfect, but I am very happy to have an option at all!

Now when casting playlists my missus and friends can just select an active remote player, instead of trying to figure out which is available or not. I will use this approach on quite a few things now…

Thanks @petro I’ll give this a try. This looks much cleaner.

The other issue I found is that when the list gets refreshed (as I had it every 6 hours or on restart) it deletes my previous selection, even though it may still be in the new list and returns just the top one. I think I’ll have to change it to a script instead of an automation and just manually run when I change my Sonos playlists and want the new list to appear.

@petro

Looks like it doesn’t properly format it for lists and seems to just put it all in one long string because I get the following in my log when trying to run: State max length is 255 characters.

Try a comma

data_template:
  options: >
    {% for item in state_attr('media_player.sonos_living_room', 'source_list') %}
    - {{ item }},
    {% endfor %}

Same issue. It doesn’t like to respect lists in templates

I came to the same conclusion. I couldn’t build a working JSON-type array either.

I ended up using your example, but with a small tweak so that I don’t get errors when referring to more items than existed.

- service: input_select.set_options
  data_template:
    entity_id: input_select.syno_players
    options:
      - 'Not Set'
      - '{% if states.sensor.synoplayers.attributes.data.players[0] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[0].name}}{%endif%}'
      - '{% if states.sensor.synoplayers.attributes.data.players[1] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[1].name}}{%endif%}'
      - '{% if states.sensor.synoplayers.attributes.data.players[2] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[2].name}}{%endif%}'
      - '{% if states.sensor.synoplayers.attributes.data.players[3] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[3].name}}{%endif%}'
      - '{% if states.sensor.synoplayers.attributes.data.players[4] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[4].name}}{%endif%}'
      - '{% if states.sensor.synoplayers.attributes.data.players[5] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[5].name}}{%endif%}'
      - '{% if states.sensor.synoplayers.attributes.data.players[6] is not defined %}NA{%else%}{{states.sensor.synoplayers.attributes.data.players[6].name}}{%endif%}'
1 Like

I just ran into this same issue myself. I was able to workaround it by calling the Home Assistant APi with JSON that’s created by a template. After you create the rest_command, you just need to call the service rest_command.workaround_set_options.

rest_command:
  workaround_set_options:
    url: "http://hassio/homeassistant/api/services/input_select/set_options"
    method: POST
    headers:
      content-type: application/json
    content_type: application/json
    payload: >-
      {
        "entity_id": "input_select.my_input_select",
        "options": [
      {%- for item in states.sensor %}
          "{{ item.entity_id }}"{% if not loop.last %}, {% endif %}
      {%- endfor %}
        ]
      }
4 Likes

Perfect! That’s what we were after! And also now that I can see how we can do ‘for each’ loops in templating, the sky is the limit - cheers!

EDIT - I got this to work too. I had to include Authentication in the headers. I have a bash script populating an HTTP Sensor with a JSON array of all the remote speakers available to my Synology Audio Station. so your approach was perfect for me; it captures any remote speakers in that HTTP sensor JSON and pops them through to my input select, I am very happy to get this nailed!

rest_command:
  update_syno_players:
    url: "http://localhost:8123/api/services/input_select/set_options"
    method: POST
    headers:
      content-type: application/json
      x-ha-access: MyHassHTTPpassword
    content_type: application/json
    payload: >-
      {
        "entity_id": "input_select.syno_players",
        "options": [
      {%- for item in states.sensor.synoplayers.attributes.data.players %}
          "{{ item.name }}"{% if not loop.last %}, {% endif %}
      {%- endfor %}
        ]
      }
1 Like

Mine took a little bit of re-working, for instance “item.name” resulted in errors so had to change to as below. But much cleaner code than I was using. Thanks.

rest_command:
  update_sonos_playlists:
    url: "http://localhost:8123/api/services/input_select/set_options"
    method: POST
    headers:
      content-type: application/json
      x-ha-access: !secret http_password
    content_type: application/json
    payload: >-
      {
        "entity_id": [
          "input_select.options_morning_music", "input_select.options_morning_music_sunday", "input_select.options_morning_music_monday", "input_select.options_morning_music_tuesday", "input_select.options_morning_music_wednesday", "input_select.options_morning_music_thursday", "input_select.options_morning_music_friday", "input_select.options_morning_music_saturday", "input_select.options_morning_music_guest", "input_select.options_all_speakers_source", "input_select.options_dinner_music_source", "input_select.options_vacation_music", "input_select.options_night_music"
        ],
        "options": [
      {%- for item in state_attr('media_player.sonos_living_room', 'source_list') %}
          "{{item}}"{% if not loop.last %}, {% endif %}
      {%- endfor %}
        ]
      }
2 Likes

Yeah when anyone calls each ‘item’ of an array (as in: ‘for item in [array]’) , the ‘item’ will have different sub-attributes in different scenarios.

Notorious needed ‘item.entity_id’. I needed ‘item.name’, whereas you just needed the ‘item’ object itself.

The ‘Templates’ area of the Home Assistant UI is perfect for getting all this stuff right.

Apologies if you knew all this already - I just wanted to explain in case it helps someone

2 Likes

how could i use this to list all attributes and their values of a single sensor? struggling a bit with these loops…

I’m not sure of what you are after, but I’ll do my best to answer this

So - given that the goal of this thread was to populate an input_select list, the ideal information source to achieve this would be a list of things, AKA an array.

In my case I created a sensor deliberately to provide this list/array - sensor.synoplayers - which is essentially a JSON payload containing a list of available wireless speakers within it.

“for item in states.sensor.synoplayers.attributes.data.players” basically means "for each player in the sensor list, process it and call it ‘item’ ". The rest of the logic ensures that a comma is added unless after the last item in the list.

The result is the JSON style payload we use to set the ‘input_select’ options. The logic takes the source JSON array from my sensor, and uses it to fill the “options” that the input_select.syno_players requires. By specifying {{ item.name }} I am only populating it with the remote speakers names.

payload: >-
  {
    "entity_id": "input_select.syno_players",
    "options": [
  {%- for item in states.sensor.synoplayers.attributes.data.players %}
      "{{ item.name }}" {% if not loop.last %}, {% endif %}
  {%- endfor %}
    ]
  }

…ends up as…

payload: >-
  {
    "entity_id": "input_select.syno_players",
    "options": [
  "Player1",
  "Player2",
  "Player3"
    ]
  }

…which dynamically sets the input_select with speakers that are actually available and working - the very goal I created the OP for :slight_smile:

I hope that helps some…

2 Likes