Dynamic Automation for multiple components?

After a little testing on my end it seems that {{trigger.entity_id.split(’.’)[1]}} does work, but it doesn’t seem to work inside another template, like your looking for in your example above:
{{states.cover.{{trigger.entity_id.split('.'[1]}}.attributes.current_position | float }}

I was able to make it dynamically select the entity that triggered the automation, but when I tried nesting it I was never able to make it work…Maybe somebody has an idea to try, it seems like in theory it would work but I haven’t found a way yet.

EDIT: the code below does not throw any errors, when using the UI cofiguration validation. But HA will fail to start with that!!! See two posts below.

So basically, this code does NOT work! Don’t try this at home… assistant

- id: '834611532639'
  alias: "Open blinds when window gets opened"
  trigger:
    - platform: state
      entity_id: binary_sensor.dinning_room
      from: 'off'
      to: 'on'
    - platform: state
      entity_id: binary_sensor.living_room
      from: 'off'
      to: 'on'
    - platform: state
      entity_id: binary_sensor.kitchen
      from: 'off'
      to: 'on'
  condition:
    - condition: template
      value_template: '{{ states.cover.{{trigger.entity_id.split('.')[1]}}.attributes.current_position < (states.input_number.fresh_air.state | int) }}'
  action:
    - service: input_number.set_value
      entity_id: input_number.{{trigger.entity_id.split('.')[1]}}
      data_template:
        value: "{{states.cover.{{trigger.entity_id.split('.')[1]}}.attributes.current_position | float }}"
    - service: cover.set_cover_position
      entity_id: cover.{{trigger.entity_id.split('.')[1]}}
      data_template:
        position: '{{ states.input_number.fresh_air.state | int }}'
- id: '495011554639'
  alias: "Close blinds when window gets closed"
  trigger:
    - platform: state
      entity_id: binary_sensor.dinning_room
      from: 'on'
      to: 'off'
    - platform: state
      entity_id: binary_sensor.living_room
      from: 'on'
      to: 'off'
    - platform: state
      entity_id: binary_sensor.kitchen
      from: 'on'
      to: 'off'
  condition:
    - condition: template
      value_template: '{{ states.cover.{{trigger.entity_id.split('.')[1]}}.attributes.current_position > (states.input_number.{{trigger.entity_id.split('.')[1]}}.state | int) }}'
  action:
    - service: cover.set_cover_position
      entity_id: cover.{{trigger.entity_id.split('.')[1]}}
      data_template:
        position: '{{ states.input_number.{{trigger.entity_id.split('.')[1]}}.state | int }}'

I tested that by firing the state change events of the different binary sensors through the HA UI. Result: the corresponding automations got triggered every time, but only when the event of the window “dinning_room” arrived (which is the one that has a physical motor) it moved as it should :slight_smile: .
Come over here and grab a beer :beers: .

@brg468
this is strange. For me, it worked. Maybe you can compare your config to mine?
EDIT: you are right, it does not work… :frowning:

Greetings!

2 Likes

@Jojo-A AWESOME! Good job!!

Oh sorry guys, bad news:
it does still not work. I only thought it would work, because when I checked the code with the validating feature in the UI, it told me to be “Valid!”, but when I restart HA it fails :rage: . Dammit…
I’ll edit the post above accordingly.

So when I restart HA, it fails with the message:

ERROR (Thread-2) [homeassistant.util.yaml] while parsing a block mapping in "/home/homeassistant/.homeassistant/automations.yaml", line 89, column 7
expected <block end>, but found '<scalar>' in "/home/homeassistant/.homeassistant/automations.yaml", line 90, column 67

When I now replace all nested {{trigger.entity_id.split(’.’)[1]}} with just dinning_room, HA starts, but it throws a lot of errors regarding automation:

2017-12-14 14:04:01 ERROR (MainThread) [homeassistant.config] Invalid config for [automation]: Entity ID input_number.{{trigger.entity_id.split('.')[1]}} is an invalid entity id for dictionary value @ data['action'][0]['entity_id']. Got None
invalid template (TemplateSyntaxError: expected name or number) for dictionary value @ data['action'][0]['data_template']['value']. Got None. (See /home/homeassistant/.homeassistant/configuration.yaml, line 156). Please check the docs at https://home-assistant.io/components/automation/
2017-12-14 14:04:01 ERROR (MainThread) [homeassistant.config] Invalid config for [automation]: Entity ID cover.{{trigger.entity_id.split('.')[1]}} is an invalid entity id for dictionary value @ data['action'][0]['entity_id']. Got None. (See /home/homeassistant/.homeassistant/configuration.yaml, line 156). Please check the docs at https://home-assistant.io/components/automation/

This is actually bad, because it seems to me as if this nice idea does just not work in general :frowning: .

I also tried to use another component (input_text and template_sensor) as some kind of “buffer” for the trigger entity, that I don’t need to use nested templates. But this fails, too, with similar errors (“no valid entity_id” or so). If someone likes to investigate that with me, this would be welcome, of course.

I don’t know, if it is useful to let the idea of @brg468 marked as the solution. Sure, it extracts the name of the entity that triggerd the automation. But in the end, I was not able to use this for my problem. What do you think?

Until now I have tried A LOT of things, nothing worked as I hoped it would. So for now, I’ll give up and figure out another solution.

Thank you guys anyway. The beers are still here for you :wink: .

Greetings

Sorry it didn’t end up working. I hadn’t used anything quite like that before so it was kinda a shot in the dark that seemed to make sense. I tried to find another way to set it up that didn’t directly involve nesting the two templates, like using a variable to represent that part, but it always ended up with the same issue. I’m sure there’s a solution but it’s probably a bit more involved unfortunately.

@Jojo-A Don’t give up. There’s always other ways around this. Maybe move the entity_id underneath the data_template. Which line exactly is HA balking at? It says line 89, 90, can you paste which ones those are?

Worst case scenario you’ll have to make an automation per window. In my experience, everything eventually works, some just take a lot more effort and time than others.

No reason to be sorry, dude! I highly appreciate, that you shared your idea. And indeed, on the first glace it looked really charming and promising :slight_smile: . It’s not your fault that I couldn’t get it working. But I think I should remove the “Solution” mark, as it didn’t work in the end. Others should not take the same pain as me :wink: .

@Jer78
No, we never give up. Of course, there are other solutions, we just don’t know them, yet. I just give up this particular approach.
I think as a first step, I’ll have to create individual automations for each window. Worst case (which will be if I don’t learn/understand enough about templating) would be, that I have to create three automations per window, so 27 automations in total… Best case would be to have only one automation per window, if I am able to set up a proper template.

In pseudocode I like to do:

if(window got opened)
{
    set the heating to "lower"
    if(cover is below "threshold")
    {
        save actual cover position
        move cover up to "threshold"
    }
}
else    // if window got closed
{
    set the heating to "auto"
    if(cover is higher than previously saved position)
    {
        move cover back down to previously saved position
    }
}

So my main challenge will be to write a template with nested conditions. I don’t ask you to present me a solution for that, as I have not tried it by myself, yet. So I gonne try this by myself until I fail epically and then I came back to ask. But this will be topic for another thread :slight_smile: .

Greetings!

@Dolores
are you familar with python programming? I’ve thought much about your suggestion and I think I will give it a shot. I’d appreciate, if you coud take a look at my very first python script to see if it has a chance to work. Syntax errors are expected :wink:

The automation is now just:

- id: '303427509327'
  alias: "Call window automation script"
  trigger:
    - platform: state
      entity_id: binary_sensor.dinning_room
    - platform: state
      entity_id: binary_sensor.living_room
    - platform: state
      entity_id: binary_sensor.kitchen
    (... six more triggers here... )
  action:
    - service: python_script.window_automation
      data:
        entity_id: '{{ trigger.entity_id.split('.')[1] }}'
        sensor_state: '{{ trigger.to_state.state }}'

My window_automation.py contains:

sensor_state = data.get('sensor_state')
entity_id = data.get('entity_id')
current_position = hass.states.get(('cover.' + entity_id).attributes.current_position)
saved_position = (hass.states.get(('input_number.' + entity_id).state) | int)
fresh_air_position = (hass.states.get('input_number.fresh_air').state) | int)

# Window got opened, do the following
# - set heating to "lower"
# - save current cover position
# - open cover until "fresh air"-position is reached
if sensor_state == 'on':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'lowering'})
	if current_position < fresh_air_position:
		hass.services.call('input_number', 'set_value', { 'entity_id': ('input_number.' + entity_id), 'value': (current_position | float) })
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': fresh_air_position })

# Window got closed, do the following
# - set heating to "auto"
# - move cover back to previous position
elif sensor_state == 'off':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'auto'})
	if current_position > saved_position:
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': saved_position })

Thanks and greetings!

I must confess… to being a horrible person. :grin: I have very little python experience, having only created two apps, and never having created a python script!

You can shorten your trigger by just having one triggering, with a list of entity_id’s. Not that this will make much of a difference!

@Dolores
Oh, thats sad :frowning: . Anyway, I have posted this question into the “Scripts” section. Hopefully someone there knows better.
I already made first tests. But when the automation gets triggered, nothing happens. So I expect there are bugs in the script, but I don’t know how to debug that, because I don’t even see error messages.

From what I do know, instead of doing your fancy templating for service calls, I would define a python dictionary that converts from sensors to heaters.

https://www.python-course.eu/dictionaries.php

Ok guys,

I think I got it solved now. I followed the suggestion of @Dolores and wrote a short python script.

With this little piece of code, I only need one single automation, that will be triggered if any of my windows is opened OR closed. The rest is handled by the script.

This is the automation:

- id: '303427509327'
  alias: "Window contact covers/heating"
  trigger:
    - platform: state
      entity_id: binary_sensor.dinning_room
    - platform: state
      entity_id: binary_sensor.living_room
    - platform: state
      entity_id: binary_sensor.kitchen
    # ...many more triggers here...
  action:
    - service: python_script.window_automation
      data_template:
        entity_id: '{{ trigger.entity_id }}'
        sensor_state: '{{ trigger.to_state.state }}'

The script code:

sensor_state = data.get('sensor_state')
entity_id = data.get('entity_id')
entity_id = entity_id.split('.')[1]
current_position = hass.states.get('cover.' + entity_id).attributes['current_position']
saved_position = hass.states.get('input_number.' + entity_id).state
fresh_air_position = hass.states.get('input_number.fresh_air').state

# Window got opened, do the following
# - set heating to "lower"
# - save current cover position
# - open cover until "fresh air"-position is reached
if sensor_state == 'on':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'lowering'})
	if float(current_position) < float(fresh_air_position):
		hass.services.call('input_number', 'set_value', { 'entity_id': ('input_number.' + entity_id), 'value': float(current_position) })
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': int(float(fresh_air_position)) })

# Window got closed, do the following
# - set heating to "auto"
# - move cover back to previous position
elif sensor_state == 'off':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'auto'})
	if float(current_position) > float(saved_position):
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': int(float(saved_position)) })

This is my very first python script, so there is surely room for improvements :wink: .

During the debugging process of that script, I faced very similar errors as before when I tried to do that as templates in my automation directly. Most of these errors were just type conversion errors (especially this one “int(float(xyz))” drove my crazy…). So I THINK it should be possible to solve this inside a template with correct conversions as well. Maybe someone likes to give it another shot…

Thanks for your help!

Jojo

2 Likes