Create a dictionary with dynamic structure with templating

Hello to all.

I want to use two diffrent actions in one automation depending on the input. Problem is that the actions require diffrent data Dictionaries which I try to acomplish by using templating.

More Background Info:
I am trying to control my lights (Philips Hue) with a Dropdown Helper. Each Room has its own Dropdown Helper with some light moods (actually Philips Hue Scenes) and one option called “Aus” to turn the light off.

Per helper I have one automation where I check for new selected Inputs.
If the new input is a light mood (for example “Arbeitslicht” or “Fernsehen”) it should call the hue.hue_activate_scene action with the selected input as scene name.
If the new input is “Aus”, it should call the light.turn_off action with the group_name of the room. (for example “light.wohnzimmer”)

Here is my working example:

alias: Wohnzimmer Lichter Steuern
description: Input Selector -> Philips Hue Room
trigger:
  - platform: state
    entity_id: input_select.wohnzimmer_licht
condition: []
action:
  - service: >-
      {% if is_state(trigger.entity_id, 'Aus') -%} light.turn_off {%- else -%}
      hue.hue_activate_scene {%- endif %}
    data:
      '{%- if is_state(trigger.entity_id, ''Aus'') -%}entity_id{%- else -%}group_name{%- endif -%}': >-
        {%- if is_state(trigger.entity_id, 'Aus') -%}light.wohnzimmer{%- else
        -%}Wohnzimmer{%- endif -%}
      '{%- if is_state(trigger.entity_id, ''Aus'') -%}transition{%- else -%}scene_name{%- endif -%}': >-
        {%- if is_state(trigger.entity_id, 'Aus') -%}-1{%- else
        -%}{{states(trigger.entity_id)}}{%- endif -%}
mode: single

Problem is with this example, that I have a fixed structure of two keys and two values for every action.

I want to create a version where for the action hue.hue_activate_scene both required values are set and for the light.turn_off only the only one required value. My workaround uses an second optional value transition to keep the same dictonary structure, but the transition setting creates problems with my lights.

I read that it is possible to output a native type, like a dictionary, with templating, but I dind’t got it to work.
It should look like this:
If trigger.entity_id = “Aus” then

data: 
  entity_id: light.wohnzimmer

else

data: 
  group_name: Wohnzimmer
  scene_name: trigger.entity_id

Thanks a lot for your help.

How about using choose in the action and define two different service calls based on the trigger?

1 Like

You can’t template keys so you will need to use Burningstone’s suggestion (use choose).

This looks like the missing piece I did not find. Thank you. The first try gave me Syntax errors but I will try to fix this the next days. Is the YAML Editor of the Webinterface the right tool or does it have problems with more complex automatations? I am asking because it did take long time to get the rule posted above working.

I thoug as of Version 0.118 it is indeed possible to generate other types than strings with templates. Source

The YAML editor should be fine, however I prefer to use Visual Studio Code on my Desktop and edit the files via Samba.

Yes, you can template different types now, but you still can’t template a key.

Correct. A template can produce a result whose type is not limited to string. However the template’s result represents the value of an option (key). That value cannot contain other options (keys).

That’s not necessarily true. The limiting factor is that the data field itself does not allow templates, it expects a dictionary. You can return a dictionary to a field inside the data field and it’s value will stick and it can contain keys (I.e. it can be a dictionary).

service: ...
data: #<--- does not allow templates
service: ...
data:
  fielda: #<--- does allow templates
  fieldb: #<--- does allow templates

in example 2, you can return a dictionary to fielda or fieldb and it will retain its type and it would be accessible in the service.

1 Like

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.