If any cover reaches a position, trigger automation for that cover

I am helping a friend set up an automation at their company where they want to automate covers. They have loads of covers so it should be as dynamic as possible.

Basically what I want is this:

alias: Stop covers when they reach 15%
description: ""
triggers:
  - trigger: numeric_state
    entity_id:
      - cover.s_cover_1_1_cover
      - cover.motor_0002d228_0
    below: 15
conditions: []
actions:
  - action: cover.stop_cover
    target:
      entity_id: "{{trigger.entity_id}}"
mode: parallel
max: 50

But: the entity_id: part of the trigger should not be a list of entities, but instead some kind of wildcard.

Is that at all possible?

My ideas so far:

  • template trigger to generate a list of covers that are closing and <15. → But this breaks if multiple covers reach <15 around the same time and only the first one will get stopped
  • wildcards such as cover.* - doesn’t seem to exist? (maybe vote for this and this while youre here.

Would very much welcome inputs on how to achieve this (without yaml mode) :slight_smile:

No. State triggers (including the numeric kind) unfortunately only work with static entity ids, no templates or other conveniences such as label ids. Your option is as you’ve already concluded some clever use of a template trigger.

Well that’s too bad! Templates would be an option, but in all honesty, that’s not gonna be waterproof. I’ve tested this with just two covers and even then they either had to close at exactly the same time or far enough apart to not interfere with each other. I’m sure some more templating logic could resolve that, but then this will just unveil the next issue. There’s always that one eventuality that will break it and that’s not good enough in this specific case :frowning:

Thanks for validating my suspiscion!

If you set it up as a blueprint they will have a very fast and easy to use interface for adding and removing which covers are included in the automation.

I’m not familiar with how covers work. Is the number a percentage, and you for some reason want to stop them before they close all the way (at 15%)? Looking at the Cover integration documentation, it looks like the state should not be a percentage but rather a word that describes whether it is moving and in which direction. Are your triggers setup against the correct entity ids / attribute?

If all your automation is doing is triggering a stop, and the updated state makes its way back to HASS pretty much immediately, the time window for missed triggers (when using a template trigger) should be very very small. Non-existent even, if using mode restart and sending the stop command to all covers currently moving in the danger zone regardless of which actually triggered first/last.

You can get this done, but it takes a bit to set up.

Make a template entity.

template:
- sensor:
  - name: Last Cover Updated
    state: >
      {{ states.cover | map(attribute='last_updated') | sort(reverse=True) | first }}
    device_class: timestamp
    attributes:
      last_cover:
        {% set last = states.cover | sort(attribute='last_updated', reverse=True) | first | default({}) %}
        {% set key_attributes = last.attributes.items() | rejectattr('0', 'eq', 'supported_features') | list %}
        {{ dict(state=last.state, entity_id=last.entity_id, attributes=dict.from_keys(key_attributes)) }} }}

Then use that as a state trigger.

alias: Stop covers when they reach 15%
description: ""
triggers:
  - trigger: state
    entity_id: sensor.last_cover_updated
conditions:
  - condition: template
    value_template: "{{ trigger.to_state.last_cover.state == 'closing' and trigger.to_state.last_cover.attributes.current_position <= 15 }}"
actions:
  - action: cover.stop_cover
    target:
      entity_id: "{{ trigger.to_state.attributes.last_cover.entity_id }}"
mode: parallel
max: 50

You’ll likely need to alter the condtion template to catch opening or closing.


Also, keep in mind, the entity (templates) will be throttled to at-most one update per second because we are traversing a single domain in the state machine.

exactly, 15 would be the attributes.current_position

I tried this yesterday with a template listing all covers that meet the condition and then also stopping all covers that meet the condition. But even with just two covers, this failed :confused: (one stopped, the other one didn’t)

Not sure exactly what you’ve tested already, but here is my attempt:

alias: Stop covers when they reach 15%
mode: restart
triggers:
  - trigger: template
    value_template: >-
      {{ state.cover | selectattr('state', 'closing')
      | selectattr('attributes.current_position', '<', 15)
      | list | count > 0 }}
actions:
  - action: cover.stop_cover
    target:
      entity_id: >-
        {{ state.cover | selectattr('state', 'closing')
        | selectattr('attributes.current_position', '<', 15)
        | map('attr', 'entity_id') | list }}

Completely untested of course so probably has typos or some missing filter :stuck_out_tongue:

That won’t trigger if 2 covers alternate state within a single second.

That’s more or less what I’ve tried unsuccessfully :confused: the first cover to close actually stopped and the automation ended, but because it seemingly takes just a bit until the closing changes to open, by the time the second cover reached the limit, the first one was still showing closing and <15 and so the template never resolved false and because of that it didn’t trigger a second time.

With 2 covers this was of course a bit forced, but with around 100 covers this is going to happen all the time (I assume)

that’s an interesting approach and would give me what I’m looking for: just a single entity triggering the automation. I’m going to try that!

Either you’re speaking gibberish or this is beyond my understanding - I’m guessing the latter :smiley:

so sensor.last_cover_updated will only be updated once a second to show the most recently moved cover, did I understand this correctly? I’d need to check if that’s enough - assuming that 50 covers close at the same time it could take a minute for all of them to get considered once (if at all, because the same one could be last_updated multiple times, right?) and by that time the covers might already be fully closed ^^

Templates are throttled so they do not overwhelm HA.

If a template uses states without using as a function, i.e states | ... the template is throttled. It will only update at most once a minute. This is because the template traverses all entities in the system.

If a template uses states.<domain> without using as a function, i.e. states.cover | ... the template is throttled. It will only update at most once a second. This is because the template traverses all covers on the system. In reality, this restriction was put in place because of the sensor domain. However it’s imposed on all domains unfortunately.

Oh, I forgot this one too: If a template outputs more than a specific number of bytes, the template produces an error. IIRC it’s in the 15000 range.

got it thanks! And can you tell me what is meant by “without using as a function”? Is there a different way for me to set this up without falling into that restriction?

I assume that with just 1 update per second and around 100 covers I’ll run into issues, what’s your opinion on that?

in regards to states it means (not throttled):

states('sensor.abc') #Using states as a function
states['sensor.abc'] #Getting an item from states.
states.sensor.abc #Getting an item from states

using states object without using as a function (throttled, at most 1 update per minute)

states | ...

In regards to state.<domain>, it really can’t be called as a function but you can get an item from it (not throttled).

states.cover['abc'] #Getting an item from states.
states.cover.abc #Getting an item from states

using states.domain as an object (throttled, at most 1 update per second):

states.cover | ...

I don’t think you will. If you want to get around that, there are ways to do that. If you want that, I can show you how.

I just use a scene to change all my covers. Works well at firing them off at the same time.

I would be very interested to see how this can be done! :heart: