Create a dictionary with dynamic structure with templating

I agree the rendered value can be a dictionary but that dictionary’s keys cannot be (YAML) options.

That’s exactly what Mario_held is attempting to do, use a template to generate options. In one case he wants the template to produce a single option (entity_id) and in another he wants it to produce two options (group_name and scene_name). That’s not supported.

They can be if the data field was templateable, that’s what I’m saying. It would actually be a small PR to make that happen.

To clarify, there are other areas in home assistant where this is possible. Just not for data. Yaml accepts dictionaries as JSON.

I’m confused. Are you describing what exists or what could exist?

Because a template that generates options has never worked, even after templates were enhanced to support types. :thinking: Or does it?

Do you mean something like this? Where the template generates options for data in the form of a dictionary?

{% set is_aus = True %}
{ {{"\"entity_id\"" if is_aus else "\"group_name\""}}: {{ "\"light.wohnzimmer\"" if is_aus else "\"Wohnzimmer\""}},
  {{"\"transition\"" if is_aus else "\"scene_name\""}}: {{ "-1" if is_aus else states(trigger.entity_id)}} }

action:
  - variables:
      is_aus: "{{ is_state(trigger.entity_id, 'Aus') }}"
  - service: "{{ 'light.turn_off' if is_aus else 'hue.hue_activate_scene' }}"
    data: >
      { {{"\"entity_id\"" if is_aus else "\"group_name\""}}: {{ "\"light.wohnzimmer\"" if is_aus else "\"Wohnzimmer\""}},
        {{"\"transition\"" if is_aus else "\"scene_name\""}}: {{ "-1" if is_aus else states(trigger.entity_id)}} }

I was clarify the reason why it’s not available. It’s not available because data is not templateable, not because they are options. Specifically, it currently just fails validation. The data field might actually be templateable.

Yah, thats exactly what I meant.

Sorry for the confusion, I was just trying to provide more info. I think it would be a good PR.

OK, I think I understand what you’re driving at:

  • In theory, it should be possible (ever since templates were enhanced to support types) to make a template generate options (as per my example shown above) for data.
  • In practice, it cannot currently be done due to a minor technicality (fails validation).

If I got that right then I would definitely be in favor of a PR to “make this happen” because it adds another tool to the templating toolbox. Having said that, in this particular case, it would be easier for the OP to use choose. That template’s syntax can befuddle a casual user.

{ {{"\"entity_id\"" if is_aus else "\"group_name\""}}: {{ "\"light.wohnzimmer\"" if is_aus else "\"Wohnzimmer\""}},
  {{"\"transition\"" if is_aus else "\"scene_name\""}}: {{ "-1" if is_aus else states(trigger.entity_id)}} }
3 Likes

somewhat related to what we can do in js in the button-card templates. maybe another idea for op:

button_switch:
  variables:
    id: >
      [[[ entity.entity_id.split('.')[1]; ]]]
    z_wave: >
      [[[ return states['group.z_wave_switches'].attributes.entity_id.includes(entity.entity_id); ]]]



  hold_action:
    action: >
      [[[ return variables.z_wave ? 'call-service' : 'call-service'; ]]]
    service: >
      [[[ return variables.z_wave ? 'zwave_js.refresh_value' : 'script.turn_on'; ]]]
    service_data:
      entity_id: >
        [[[ return variables.z_wave ? 'sensor.' + id + '_actueel' : 'script.' + id + '_meterget_power'; ]]]
#      refresh_all_values: >
#        [[[ return variables.z_wave ? true : null; ]]]

or using some other actions:

hold_action:
  action: >
    [[[ return (window.location.pathname.split('/')[2] == 'time_settings')
        ? 'more-info' : 'navigate'; ]]]
  navigation_path: >
    [[[ return (window.location.pathname.split('/')[2] == 'time_settings')
        ? null : '/ui-data/time_settings'; ]]]
  entity: >
    [[[ return (window.location.pathname.split('/')[2] == 'time_settings')
        ? entity.entity_id : null; ]]]

Thank you for giving more details of the underlying problem. Maybe it should be mentioned in the docs that it is not possible to use templates for data, eventhough it is tempting.
I also agree that it would be a good idea to make a PR for this. Maybe you could do this task, as you have a lot more knowledge about this topic?

Thats true for this use case it is clearly better and easier to use choose: instead of templating, but there is sure enough more use cases where templating data: can be usefull.

Give me two examples where it would be superior to choose.

Personally, I don’t think it is. Choose is a much better choice and it’s easier to read.

I agree.

action:
  - choose:
      - conditions: "{{ is_state(trigger.entity_id, 'Aus') }}"
        sequence:
          - service: light.turn_off
            data:
              entity_id: light.wohnzimmer
              transition: -1
    default:
      - service: hue.hue_activate_scene
        data:
          group_name: Wohnzimmer
          scene_name: '{{ states(trigger.entity_id) }}'

How about loops? You could use templates to generate a list for options.

use repeat for loops. Make a list and store it in a variable, then use repeat.index to grab each item as you move through the repeat. This can also be combined with choose. The current automation engine is excellent and can pretty much do anything in yaml without templates. Templates only compliment them. And if you’re good enough, you can take long yaml automations and tighten them up with variable templates.

Show me an example of a template that does that. Remember, we’re talking about a template that generates options (like the one I posted above).

I’m so excited. I actually get to answer a question for petro! I stumbled across your comment here looking for something totally different (info on zwave.refresh_values).

The data: parameter of a service call DOES accept a template! I didn’t realize it myself until I tried it out helping out a fella on this thread. Maybe this has been added since your comment (this is an old thread after all) but I really don’t want to go back through all the release notes to check.

That guy never did report back if it worked for him but I tried it out locally with a test script to call another script in my config and it worked a treat.

Test Script
test_alarm_options:
  alias: 'Test Alarm Options'
  fields:
    zone:
      description: 'Alarm zone.'
    mode:
      description: 'Alarm arm mode.'
    code:
      description: 'Alarm arm code.'
    override:
      description: 'Bypass open sensors.'
  variables:
    zone: "{{ zone|default('') }}"
    mode: "{{ mode|default('') }}"
    code: "{{ code|default('') }}"
    override: "{{ override|default('') }}"
    options: >
      {% set option_dict = namespace(value='') %}
      {% set options = ['zone','mode','code','override'] %}
      {% set values = [zone,mode,code,override] %}
      {% for item in options %}
        {% if values[loop.index0] != '' %}
          {% set option_dict.value = option_dict.value
              ~ '"' ~ item ~ '":"'
              ~ values[loop.index0] ~ '"' ~ ',' %}
        {% endif %}
      {% endfor %}
      {{ '{' ~ option_dict.value[:-1] ~ '}' }}
  sequence:
    - service: script.arm_alarm
      data: '{{ options }}'

image

I’m sure there has to be a more efficient way to do it, but this does work.

I do have a question. Is there an easier way to build the dictonary? I did something very similar here but it feels like there should be an easier way to work with the dictonary than building it as a string. Maybe not since that was the approach @123 used in this thread.

4 Likes

It was added by koying and incorporated into the November 2021 release.

I haven’t used it yet but it’s an interesting addition to one’s toolkit.

1 Like

Hah, yes. @koying added it a bit ago, been using it for some time. Thanks for the heads up though!

1 Like

I use it to get around validation on yaml, like when you need to put a string representation of an object in a notification message. Normally that would fail with “Message needs to be a string”, but if you template the whole data section, it doesn’t validate the data for message.