How to make this string more readable in yaml?

Hi yaml wizards!

I have a working automation that sends entity state changes to a rest api.
See below for the full automation.

The question is about this part:

parameter: >-
        HA {{ trigger.entity_id  }}/{% set state = trigger.to_state.state %}{%
        if state == "on" %}{% set state = "1" %}{% elif state == "off" %}{% set
        state = "0" %}{% endif %}{{state}}

What is does is building up the parameter of the REST url.
It converts the string in trigger.to_state.state to 1 if it is “on” and to 0 if it is “on”. Else it passes the string as is.
This works as intended, but it is quite unreadable by a human. Or at least by me :wink:
My question is: How can I rewrite this in a more cleaner/readable way?

For context, here is the whole automation:

alias: Home Assistant to Loxone
description: ''
trigger:
  - platform: state
    entity_id:
      - sensor.exhaust_fan_duty
      - media_player.radio
      - binary_sensor.aqara_washing_machine_sensor_contact
      - sensor.esphome_web_600e0b_boiler_return_temperature
      - sensor.esphome_web_600e0b_boiler_outlet_temperature_2
      - sensor.esphome_web_a6982c_co2_value
      - sensor.hue_remote_bathroom_action
      - input_select.ha_shading_controller
      - sensor.sk1_hall_motion
condition: []
action:
  - service: rest_command.update_loxone
    data:
      parameter: >-
        HA {{ trigger.entity_id  }}/{% set state = trigger.to_state.state %}{%
        if state == "on" %}{% set state = "1" %}{% elif state == "off" %}{% set
        state = "0" %}{% endif %}{{state}}
mode: parallel
id: '1703699168468'

A link to some docs covering this are also welcome!
Thanks!
Gert

All of these entities report a value of either on or off?

      - sensor.exhaust_fan_duty
      - media_player.radio
      - binary_sensor.aqara_washing_machine_sensor_contact
      - sensor.esphome_web_600e0b_boiler_return_temperature
      - sensor.esphome_web_600e0b_boiler_outlet_temperature_2
      - sensor.esphome_web_a6982c_co2_value
      - sensor.hue_remote_bathroom_action
      - input_select.ha_shading_controller
      - sensor.sk1_hall_motion

Even the two temperature sensors?


What you are requesting is to simplify the Jinja2 template, not YAML (they’re two separate and very different things).

Here’s a simplified version of the template:

        HA {{ trigger.entity_id  }}/{{ trigger.to_state.state | bool | abs }} 

Your automation’s State Trigger should be enhanced to ensure it only triggers for changes to the entity’s state and not its attributes and it should not trigger if the value of the entity’s state becomes unavailable or unknown.

alias: Home Assistant to Loxone
description: ''
trigger:
  - platform: state
    entity_id:
      - sensor.exhaust_fan_duty
      - media_player.radio
      - binary_sensor.aqara_washing_machine_sensor_contact
      - sensor.esphome_web_600e0b_boiler_return_temperature
      - sensor.esphome_web_600e0b_boiler_outlet_temperature_2
      - sensor.esphome_web_a6982c_co2_value
      - sensor.hue_remote_bathroom_action
      - input_select.ha_shading_controller
      - sensor.sk1_hall_motion
    not_to:
      - unavailable
      - unknown 
condition: []
action:
  - service: rest_command.update_loxone
    data:
      parameter: >-
        HA {{ trigger.entity_id  }}/{{ trigger.to_state.state | bool | abs }} 
mode: parallel
id: '1703699168468'

NOTE

If any of the entities reports a value other than on or off then the template I suggested will produce an incorrect value or fail with an error (it depends on the supplied value). Faced with the same situation, your template will report nothing at all.

Since you’re dealing with values that could be anything, it’s pretty hard to reduce your code… you could start by removing the set though.

parameter: >-
  HA {{ trigger.entity_id  }}/
  {%- if trigger.to_state.state == "on" -%}
    1
  {%- elif trigger.to_state.state == "off" -%}
    0
  {%- else -%}
    {{ trigger.to_state.state }}
  {%- endif -%}

Though HA has a bad habit of poorly auto-formatting templates, so don’t expect indentations to hold between saves…

Equivalent one-liner:

parameter: >-
  HA {{ trigger.entity_id  }}/{{ "1" if trigger.to_state.state == "on" else "0" if trigger.to_state.state == "off" else trigger.to_state.state }}

The best you probably could do is:

parameter: >-
  HA {{ trigger.entity_id  }}/{{ {"on": "1", "off": "0" }[trigger.to_state.state] | default(trigger.to_state.state) }}

Jinja templates docs

Thanks for pointing out that yaml and Jinja2 are not the same. I did not know that yet and will investigate further.

It might be unclear, but the entities can indeed report other states than “on” or “off”. In that case, I just send the state as is: like temperatures, media player states (playing/idle/…) should pass the string as it is.
Only if the state has the string “on” or “off”, it should be replaced by 1 of 0. In fact that was already working. I was only looking for a nicer way to do so :slight_smile:
Is there still a more readable way?

The tighter filtering on state (not attributes) is a nice addition indeed. The states unknown and unavailable however must go through, because I also want my REST API endpoint to know about this.

Thanks, this is already looking a lot more readable!
I did not know I could use multiple lines here.
As long as I only change directly in yaml (not the UI editor), it keeps the formatting, right?

Thanks for the link, also.

The - glued to the % serves to remove spacing.
If it’s edited directly in the file, it should retain formatting yes.

The last one, the shortest, basically maps the value of state to the dictionary, and if the key (on/off) isn’t found, then default to the value of state. The one benefit of this, is that you could easily add mapped values without adding more elif.
For example

{{ {"on": "1", "off": "0", "unavailable": "-1", "unknown": "-1" }[trigger.to_state.state] | default(trigger.to_state.state) }}

EDIT: You could also use a variable to make it cleaner:

action:
  - variables:
      mapped_states:
        "on": "1"
        "off": "0"
  - service: rest_command.update_loxone
    data:
      parameter: >-
        HA {{ trigger.entity_id  }}/{{ mapped_states[trigger.to_state.state] | default(trigger.to_state.state) }}

Thanks, I overlooked that important detail.

“Readable” is in the eye of the beholder. The suggested use of a chain of if-elif-else may appear to be more ‘readable’ to a novice, but needlessly verbose for a more experienced user.

For example, the following “inline if” does the same thing as the verbose version but in a single line. It is literally read like this:

The value is converted to an integer (1 or 0) if it’s either on or off otherwise it’s reported as is.

{{ trigger.to_state.state | bool | abs if trigger.to_state.state in ['on', 'off'] else trigger.to_state.state }}

It can also be spread over multiple lines if one feels that’s more ‘readable’.

{{ trigger.to_state.state | bool | abs
   if trigger.to_state.state in ['on', 'off'] else
   trigger.to_state.state }}

It can also be done with an Immediate If like this:

If the value is either on or off it’s converted to an integer otherwise it’s reported as is.

{{ iif(trigger.to_state.state in ['on', 'off'], trigger.to_state.state | bool | abs,  trigger.to_state.state) }}

It can also be spread over multiple lines:

{{ iif(trigger.to_state.state in ['on', 'off'],
   trigger.to_state.state | bool | abs,
   trigger.to_state.state) }}

Both are equivalent to this more verbose version:

{% if trigger.to_state.state in ['on', 'off'] %}
{{ trigger.to_state.state | bool | abs }}
{% else %}
{{ trigger.to_state.state }}
{% endif %}

Thanks @123 and @Nerivec: Both very helpful info. I’m learning a lot here! :partying_face: