Dynamic attribute selection in a multiline data_template in automation?

I’m working on an automation to handle a four-button Hue remote (RWL020) triggering on/off and brightness control of some dimmable lights, similar to how it can be made to work with the Hue bridge. I’m using a Nortek Zigbee+Z-Wave dongle and direct ZHA integration, so there’s no Hue bridge involved. I’ve actually got it working with two separate automations, but it seems like I should be able to do it with just one.

Here’s what works with two automations, one to handle the ON+BRIGHTNESS update and another to handle the OFF update:

- id: '1551928271993'
  alias: Office Light Remote On
  trigger:
  - entity_id: binary_sensor.office_light_remote
    platform: state
  condition:
  - condition: state
    entity_id: binary_sensor.office_light_remote
    state: 'on'
  action:
  - data_template:
      brightness: '{{ state_attr("binary_sensor.office_light_remote", "level") }}'
    entity_id: light.office_desk_lamp
    service: light.turn_on
- id: '1551934815541'
  alias: Office Light Remote Off
  trigger:
  - entity_id: binary_sensor.office_light_remote
    platform: state
  condition:
  - condition: state
    entity_id: binary_sensor.office_light_remote
    state: 'off'
  action:
  - entity_id: light.office_desk_lamp
    service: light.turn_off

I tried combining them and putting the brightness update in a conditional block like this, so that brightness is only set if the switch is on:

- id: '1551928271572'
  alias: Office Light Remote Handler
  trigger:
  - entity_id: binary_sensor.office_light_remote
    platform: state
  condition:
  action:
  - entity_id: light.office_desk_lamp
    data_template: >
      {% if is_state("binary_sensor.office_light_remote", "on") %}
        brightness: '{{ state_attr("binary_sensor.office_light_remote", "level") }}'
      {% endif %}
    service_template: >
      {% if is_state("binary_sensor.office_light_remote", "on") %}
        light.turn_on
      {% else %}
        light.turn_off
      {% endif %}

The idea is that brightness should NOT be passed in the data if the light is turning off, since the light.turn_off service doesn’t accept it as an argument. (I found this out the hard way when my initial attempt at integrating brightness worked to turn the light on, but not off, because the off service was failing due to the invalid argument.)

However, with the above combined format, the config validation fails with this error:

Invalid config for [automation]: expected a dictionary for dictionary value @ data[‘action’][0][‘data_template’]. Got None. (See /home/homeassistant/.homeassistant/configuration.yaml, line 35). Please check the docs at Automation - Home Assistant

I’ve looked through the docs as well as other topics, but I haven’t been able to figure out just why this doesn’t work, or if there is a way to fix it without resorting to two separate automations (which just feels like a kludge to me). Is this an impossible goal?

Wait! I think I just discovered an interesting workaround, since I’m already using a service template. Although the light.turn_off service fails when brightness is included in the service data, the homeassistant.turn_off service does not fail! It appears to ignore the unexpected parameter and just turn the light off anyway.

EDIT: Maybe not. Although the service call appears to work, there’s still a log entry generated with this configuration:

voluptuous.error.MultipleInvalid: extra keys not allowed @ data['brightness']

So at best, this too is a kludge. Hmm.

EDIT #2: Yeah, the service call to homeassistant.turn_off returns success, but the actual execution fails, and the light stays on. Nuts.

Okay, here’s what I’ve come up with so far as a different workaround, but still within a single automation.

  1. Call either light.turn_on or light.turn_off based on remote binary sensor state, without passing brightness in either case
  2. Test whether the remote is on (stops further processing if not)
  3. Call light.turn_on with brightness set using a data_template entry

The only downside to this is that light.turn_on is called twice in a row if the light is meant to be on. However, since brightness is a retained value, there is no visible sign to the user that this has occurred. I think I’m happy enough with this solution for now, though I’d prefer to avoid the extra call. I can think of three possible solutions:

  • The option to ignore unsupported parameters in a service call (pros and cons to be sure, but in this case it allows the simplest automation construction by far)
  • A “no-operation” service that would take the place of my light.turn_on call, allowing the exact same logic without an actual Zigbee network transmission
  • Corrected syntax, if it exists, allowing the use of data_template in the way I originally tried, with brightness: ... inside the template instead of being its own template value

Regardless, here’s what I have now:

- id: '1551928271572'
  alias: Office Light Remote Handler
  trigger:
  - entity_id: binary_sensor.office_light_remote
    platform: state
  action:
  - entity_id: light.office_desk_lamp
    service_template: "{% if is_state('binary_sensor.office_light_remote', 'on') %} light.turn_on {% else %} light.turn_off {% endif %}"
  - condition: template
    value_template: "{{ is_state('binary_sensor.office_light_remote', 'on') }}"
  - entity_id: light.office_desk_lamp
    service: light.turn_on
    data_template:
      brightness: "{{ state_attr('binary_sensor.office_light_remote', 'level') }}"

This is a classic question/issue. If you read this forum much you’ll see that it has come up literally hundreds, if not thousands, of times. I’ve seen various solutions, and sometimes it depends on the capabilities of the light itself. (E.g., some lights can be turned off by turning them on to a brightness of zero. But that doesn’t work with all lights.)

In this case your solution seems very reasonable. The only suggestion I’d make is to use the trigger variable in the action steps. It’s probably not likely for binary_sensor.office_light_remote to change between the time the trigger was processed and the time the last action step completes, but why chance it?

- id: '1551928271572'
  alias: Office Light Remote Handler
  trigger:
  - entity_id: binary_sensor.office_light_remote
    platform: state
  action:
  - entity_id: light.office_desk_lamp
    service_template: "light.turn_{{ trigger.to_state.state }}"
  - condition: template
    value_template: "{{ trigger.to_state.state == 'on' }}"
  - entity_id: light.office_desk_lamp
    service: light.turn_on
    data_template:
      brightness: "{{ trigger.to_state.attributes.level|int }}"