Automation to fix cover.set_cover_position

Here is a proposal to fix an issue we can have with some Curtain/blind switches (TS130F). The set_cover_position does not work properly : the cover does a full up or down operation whatever the % we ask.
This automation works fine with Z2M integration (position and calibration time are exposed)
Just put the list of MQTT ids in the conditions/value_template/eid list.
Principle : the set_cover_position is intercepted, the motor running duration is calculated from the current positiion, the calibration time and the requested position. If a new command on the same cover arrives during the timeout, the STOP is not done.

automation:
  - alias: "Cover – Proportional Stop Auto (multi-cover, filtered wait)"
    description: >
      Automatically stops Loratap/Tuya covers after proportional travel time.
      Works around devices that always run full calibration time on set_cover_position.
      Filters events so simultaneous covers do not interfere.
    mode: parallel
    max: 20

    trigger:
      - platform: event
        event_type: call_service
        event_data:
          domain: cover
          service: set_cover_position

    # Only handle specific covers and intermediate positions (1–99%)
    condition:
      - condition: template
        value_template: >
          {% set eid = trigger.event.data.service_data.entity_id %}
          {% set pos = trigger.event.data.service_data.position | int(0) %}
          {% set allowed = [
            'cover.0xa4c1384b6ed93c6f',
            'cover.0xa4c138e49308dd17'
          ] %}
          {{ eid in allowed and 0 < pos < 100 }}

    variables:
      cover_entity: "{{ trigger.event.data.service_data.entity_id }}"
      target: "{{ trigger.event.data.service_data.position | int }}"
      current: "{{ state_attr(cover_entity, 'current_position') | int(0) }}"
      delta: "{{ (target - current) | abs }}"
      # Fetch calibration time from Zigbee2MQTT sensor (fallback to 17s)
      cover_obj: "{{ cover_entity.split('.')[1] }}"
      sensor_calib: "{{ 'sensor.' ~ cover_obj ~ '_calibration_time' }}"
      travel_time: >
        {% set s = states(sensor_calib) %}
        {% if s not in ['unknown','unavailable', None, ''] %}
          {{ s | float(17) }}
        {% else %}17{% endif %}
      # Clamp duration between 0.3s and travel_time
      duration: >
        {% set d = travel_time | float * delta | float / 100 %}
        {{ [ [ d, 0.3 ] | max, travel_time | float ] | min }}

    action:
      # Wait either for a new command (on same cover) or timeout
      - wait_for_trigger:
          - platform: event
            event_type: call_service
            event_data:
              domain: cover
              service: set_cover_position
              service_data:
                entity_id: "{{ cover_entity }}"
          - platform: event
            event_type: call_service
            event_data:
              domain: cover
              service: stop_cover
              service_data:
                entity_id: "{{ cover_entity }}"
          - platform: event
            event_type: call_service
            event_data:
              domain: cover
              service: open_cover
              service_data:
                entity_id: "{{ cover_entity }}"
          - platform: event
            event_type: call_service
            event_data:
              domain: cover
              service: close_cover
              service_data:
                entity_id: "{{ cover_entity }}"
        timeout:
          seconds: "{{ duration }}"
        continue_on_timeout: true

      # If we reached timeout → stop cover
      # If a new command was received for the same cover → do nothing
      - if: "{{ not wait.completed }}"
        then:
          - service: cover.stop_cover
            target:
              entity_id: "{{ cover_entity }}"