Shelly cover calibration automation using device API and position restoration

After a while, covers that use time or consumption-based positioning start to drift from their calibrated state. Therefore, I searched for a way to calibrate my covers without the need for manual intervention.

I came up with the below automation which takes the following actions:

  1. Stores the current state of all covers for later restoration to aux input text entities.
  2. Call Shelly API using REST according to Shelly’s API spec.
  3. Wait for 10 minutes for calibration to complete.
  4. Restore previous covers state.

This automation is set to trigger once a month but you can obviously change it to your liking.
You should keep in mind that for this to work you must take note of your Shelly 2PM module IP addresses and update it in the cover_ip_map.

Any questions or suggestions are welcome.

input_text:
  cover_dining_room_left_position:
    name: Dining Room Left Window Position
  cover_dining_room_right_position:
    name: Dining Room Right Window Position
  cover_dining_room_main_position:
    name: Dining Room Main Window Position
  cover_living_room_left_position:
    name: Living Room Left Window Position
  cover_living_room_right_position:
    name: Living Room Right Window Position
  cover_living_room_middle_position:
    name: Living Room Middle Window Position


rest_command:
  shelly_calibrate_cover:
    url: "http://{{ shelly_ip }}/rpc"
    method: POST
    headers:
      content-type: "application/json"
    payload: '{"id":1,"method":"Cover.Calibrate","params":{"id":0}}'


automation:
  - alias: "Monthly Cover Calibration"
    trigger:
      platform: time
      at: "12:00:00"
    condition:
      - condition: template
        value_template: "{{ now().day == 1 }}"  # Runs on the 1st of the month
    action:
      # Store current positions into input_text entities
      - action: input_text.set_value
        data_template:
          entity_id: input_text.cover_dining_room_left_position
          value: "{{ state_attr('cover.dining_room_left_window', 'current_position') }}"
      - action: input_text.set_value
        data_template:
          entity_id: input_text.cover_dining_room_right_position
          value: "{{ state_attr('cover.dining_room_right_window', 'current_position') }}"
      - action: input_text.set_value
        data_template:
          entity_id: input_text.cover_dining_room_main_position
          value: "{{ state_attr('cover.dining_room_main_window', 'current_position') }}"
      - action: input_text.set_value
        data_template:
          entity_id: input_text.cover_living_room_left_position
          value: "{{ state_attr('cover.living_room_left_window', 'current_position') }}"
      - action: input_text.set_value
        data_template:
          entity_id: input_text.cover_living_room_right_position
          value: "{{ state_attr('cover.living_room_right_window', 'current_position') }}"
      - action: input_text.set_value
        data_template:
          entity_id: input_text.cover_living_room_middle_position
          value: "{{ state_attr('cover.living_room_middle_window', 'current_position') }}"

      - variables:
          cover_ip_map:
            cover.dining_room_left_window: '172.16.1.237'
            cover.dining_room_right_window: '172.16.1.181'
            cover.dining_room_main_window: '172.16.1.189'
            cover.living_room_left_window: '172.16.1.68'
            cover.living_room_right_window: '172.16.1.28'
            cover.living_room_middle_window: '172.16.1.208'

      # Calibrate covers by iterating over entity IDs and fetching IPs
      - repeat:
          count: "{{ cover_ip_map | length }}"
          sequence:
            - action: rest_command.shelly_calibrate_cover
              data:
                shelly_ip: "{{ cover_ip_map[(cover_ip_map.keys() | list)[repeat.index -1]] }}"

      # Wait 10 minutes after calibration
      - delay: "00:10:00"

      # Restore positions from input_text entities
      - action: cover.set_cover_position
        target:
          entity_id: cover.dining_room_left_window
        data_template:
          position: "{{ states('input_text.cover_dining_room_left_position') | int }}"
      - action: cover.set_cover_position
        target:
          entity_id: cover.dining_room_right_window
        data_template:
          position: "{{ states('input_text.cover_dining_room_right_position') | int }}"
      - action: cover.set_cover_position
        target:
          entity_id: cover.dining_room_main_window
        data_template:
          position: "{{ states('input_text.cover_dining_room_main_position') | int }}"
      - action: cover.set_cover_position
        target:
          entity_id: cover.living_room_left_window
        data_template:
          position: "{{ states('input_text.cover_living_room_left_position') | int }}"
      - action: cover.set_cover_position
        target:
          entity_id: cover.living_room_right_window
        data_template:
          position: "{{ states('input_text.cover_living_room_right_position') | int }}"
      - action: cover.set_cover_position
        target:
          entity_id: cover.living_room_middle_window
        data_template:
          position: "{{ states('input_text.cover_living_room_middle_position') | int }}"
1 Like