Conditional "set position" service for covers

All the rolling shutters in our home are position-aware and we use intermediate positions a lot in automation to control the amount of sun and heat we let in during summer months.

I.E. if exterior luminance is above e.g. 45000 Lux, and the sun is positioned to shine on a window, set the cover to 30%.

The problem with that is that if the cover is already closed (i.e. 0%) I do not want it to open to 30%.
The work around is to put a conditional block in automation each and every time I want to set the position. (if position is above 30%, set to 30%).

A more elegant solution for this I believe would be to add to services to the base cover entity, one to open_cover_to_position and one to close_cover_to_position, that would be enabled only for context-aware covers and would perform this check internally.

I have already produced the code that could fulfill this request and tested it in my setup, if I make a formal PR on GitHub is there any chance for it to move along?

You could create template covers that do that.

Though I don’t really see the hassle in using conditions in your automations.

Number, that’s the hassle.
14 covers for which I need to add this control in every possible output (generally 2 or 3) in each automation (I think at this point I have 5 or 6 for various situations).
That makes the automations hard to read and harder to maintain.

I’m not a big fan of the generic solution of having to create a whole new template device to interact with in front of a real one, but I don’t even see how this would help in this case.
How would I use a template to achieve “If I set position with the intent of closing check that it’s currently above, and if I set position with the intent of opening check that it’s currently below”?

The biggest advantage of the solution I suggest is that you add both an open and close variant services, these functionalities are in my opinion necessary because they reflect different and opposite intentions while using “set position”

cover:
  - platform: template
    covers:
      garage_door:
        device_class: garage
        friendly_name: "Garage Door"
        value_template: "{{ states('sensor.garage_door')|float > 0 }}"
        open_cover:
          action: script.open_garage_door
        close_cover:
          action: script.close_garage_door
        stop_cover:
          action: script.stop_garage_door
        set_cover_position:
          action: script.set_position #  <- you do it in this script
# scripts.yaml
set_position:
  sequence:
    - action: cover.set_position
      target:
        entity_id: cover.original_cover_here
      data: 
        position: "{{ 30 if position > 30 else position }}"

Utilize templates & scripts in your automations to handle that.

set_cover_position:
  fields:
    cover:
      name: Cover
      description: Select any cover
      required: true
      selector:
        entity:
          multiple: true
          filter:
          - domain: cover

    position:
      name: Cover Position
      description: A position to set the cover to.
      required: true
      selector:
        number:
          min: 1
          max: 100
    variables:
      covers: "{{ [cover] if cover is string else cover }}"
    sequence:
    - repeat:
        for_each: "{{ covers }}"
        sequence:
        - condition: template
          value_template: "{{ state_attr(repeat.item, 'current_position') > position }}"
        - action: cover.set_cover_position
          target:
            entity_id: "{{ repeat.item }}"
          data: 
             position: "{{ position }}"

Then in your automations

- action: script.set_cover_position
  data:
    cover:
    - cover.a
    - cover.b
    position: 30

This is a generic script that sets the position for any number of covers if the cover is above the provided position.

EDIT: Added fields to it.

2 Likes

Thank you @petro, this looks like an elegant compromise if the core entity cannot be changed!

I’ll add this to my setup :slight_smile:

For anyone looking for a copy/paste solution (fixed a couple of minor typos) :

fields:
  cover:
    name: Cover
    description: Select any cover
    required: true
    selector:
      entity:
        multiple: true
        filter:
        - domain: cover

  position:
    name: Cover Position
    description: A position to set the cover to.
    required: true
    selector:
      number:
        min: 1
        max: 100
sequence:
  - variables:
      covers: "{{ [cover] if cover is string else cover }}"
  - repeat:
      for_each: "{{ covers }}"
      sequence:
      - condition: template
        value_template: "{{ state_attr(repeat.item, 'current_position') > position }}"
      - action: cover.set_cover_position
        target:
          entity_id: "{{ repeat.item }}"
        data: 
           position: "{{ position }}"

The version I wrote can be compied/pasted into a scripts.yaml file, not the UI. You’d just omit the set_cover_position: line to copy/paste it into the UI. Although I doubt the UI supports a variable section, it likely requires variables to be inside actions.

Indeed, the variable block need to be in the sequence in the UI.
Futhermore the attribute for the position is current_position, not position and the action to set the position is set_cover_position, not set_position.
Like I said, minor typos :slight_smile:

1 Like