ESPhome Cover, how to force a known position

Hello,
When you use the ‘Time based cover’ (or the ‘Feedback cover’) in ESPhome it will ‘guess’ the current position according to the running time of the motor. You can also add end switches. In our setup we have switches at certain positions of the cover like 33% and 66%. So it is known when the cover is at 66%, how can you update the internal ‘guessed’ position to the known position of 66% (see it like a calibration of the internally calculated position)? This will greatly improve acuracy for e.g. 50%.
Thanks for any help!

I suspect you may need to use a template cover and develop your own lambda in the position: .

They didnt expose any way to modify the logic for estimated position or atleast none ive seen or heard of. You can ignore it and make your own logic in the feedback cover or a template cover. I did something similar with mine where it checks the open/closed state and then updates a text sensor to show Opening,Open,Closing,Closed,Stopped/Idle.

I know it already shows this within the cover entity but i wanted those states in another spot and you cant get them outside of the cover entity so i made my own logic.

Its probably very similar to what you need.

A long time ago i wanted a more precise position like you do and i was able to modify a rotary encoder to fit inside the shaft above the door that has the springs. So, that gives you movement, it gives you opening or closing and then you will know that (X) number of clicks is a full open to closed movement. Then all you do is take the number of clicks after each time it moves then add or subtract that from the total and that gives you an open/closed % very accurately.

So if the door is closed and you fully open it, lets say the rotary encoder gives you a value of 100. Push the button to start closing the door and then stop it. Now the rotary encoder shows a counterclockwise movement of 25 steps. 100 - 25 = 75. Now you know the door is 75% open or vise versa 25% closed.

Thats what i would try to do if i were you. Even though youve got switches at 33% and 66% as well as one at fully open and fully closed. Thats 4 switches youve got to wire, 4 failure points and your still going to be doing a level of guessing when you just want to crack the door. Is it closer to just cracked or is it closer to 33% where someone could slide underneath it? You cant really know and its gonna be a guess.

Would you mind to share your yaml-code?

The mount i had the rotary encoder attached to, it broke a while back and I dont use one anymore.

Nowadays, if you want to precisely track where your door is then just order a Shelly 2PM. Its only 1 device to install and its about the same cost if you were to buy all the individual sensors and components to DIY something similar and Shelly gives you motor controls, power metering, precise door location and state if its opening or closing and a bunch of other features that you’d be silly to not use… You get everything you would want for a door!

https://www.shelly.com/en-us/products/shop/shelly-plus-2-pm?srsltid=AfmBOoosffujTSKg1mISWQSYKKDDafnUe9oIDbQlSTyyXfoX4-v96Czv

Thanks the tip. I have everything of hardware in place, but cannot get it figured out with the yaml. I appreciate sharing of your yaml very much. :smiley:

Cant get which part figured out?

What hardware are you talking about?

If we’re using different hardware, what good would sharing my yaml do? It would be more confusing for you.

Where is your yaml? How am I supposed to know where your having problems with no yaml?

Post your config and hardware you chose.

I built it up on my own. I have a motor with power supply, endstops and a rotary encoder made out of two inductive sensors installed on the axle of the motor. The yaml runs on a esp32 devkit v1


esphome:
  name: hoftor

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  level: verbose  # debug

# Enable Home Assistant API
api:
  encryption:
    key: !secret api_pw

ota:
  platform: esphome
  password: !secret ota-password  

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    # To set a static IP for the ESP
    static_ip: 192.168.188.103
    gateway: 192.168.188.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Hoftor-FB"
    password: "redacted"

captive_portal:

substitutions:
  devicename: hoftor
  friendly_name: Hoftor
  
# Definitionen der Schalter für den 8-Kanal Relaisoptokoppler
switch:
  # Tor an, Betrieb      
  - platform: gpio
    name: "Motor Power-${friendly_name}"
    pin: 
      number: 22    # Schaltet das Hoftor ein:   Kanal 6 schaltet die Betriebsspannung für den Motor ein rt1sw
      inverted: false
      mode:
        output: true
    restore_mode: always off
    id: motor_power

  # Motor Direction Control (Relay to change motor polarity)
  - platform: gpio
    name: "Motor Direction-${friendly_name}"
    pin:
      number: 23  # Richtung Polwenderelais ein: Tor öffnet, aus: Tor schließt:   Kanal 7 rt2sw
      inverted: false 
      mode:
        output: true
    restore_mode: always off
    id: motor_direction
    interlock: [motor_power, motor_direction]  # Ensures motor power is off before changing direction

  # Umschaltung Torgeschwindigkeit durch Spannung am Motor:  
  # Kanal 8 Optokoppler Relais auf: 24V  / Relais ein: 12V  rt4sw
  - platform: gpio
    name: "Motor Speed-${friendly_name}"
    pin: 
      number: 18   
      inverted: false
      mode:
        output: true
    restore_mode: always off
    id: motor_spannung

  # Warnleuchte einschalten: Optokoppler Kanal 5 ein (Warnleuchte ein mit 12V) w4sw
  - platform: gpio
    pin: 
      number: 13
      inverted: false
    id: ${devicename}_warnleuchte
    name: "Warnleuchte-${friendly_name}"

# Lichtschranke (Detects objects in the gate's pathway)
binary_sensor:
  - platform: gpio
    name: "Lichtschranke-${friendly_name}"
    id: lichtschranke
    pin: 
      number: 14    # Lichtschranke unterbricht den Betrieb:   Kanal 6 schaltet auf Null w2sw
      inverted: false
      mode:
        input: true
        pullup: true
    device_class: safety
    on_press:
      - cover.stop: ${devicename}_control  # Stoppt das Cover, wenn ein Hindernis erkannt wird
      - delay: 1s  # Kurze Verzögerung

  # Endschalter Süd      
  - platform: gpio
    name: "Süd-Endschalter-${friendly_name}"
    id: ${devicename}_endschalter_s  # Endschalter Süd
    pin: 
      number: 26    # Schaltet das Hoftor aus:   Endschalter Süd 
      inverted: true
      mode:
        input: true
        pulldown: true
    on_press:
      then:
        - cover.stop: ${devicename}_control  # Stoppt das Tor durch Aufruf von cover.stop 
#        - sensor.rotary_encoder.set_value:
#            id: gate_position        # setzt den Zähler des Drehgeber auf die maximale Impulszahl gleich offen
#            value: 100  
    
  #  Endschalter Nord      
  - platform: gpio
    name: "Nord-Endschalter-${friendly_name}"
    id: ${devicename}_endschalter_n  
    pin: 
      number: 27    # Endschalter Nord 
      inverted: true
      mode:
        input: true
        pulldown: true
    on_press:
      then:
        - cover.stop: ${devicename}_control  # Stoppt das Tor 
#        - sensor.rotary_encoder.set_value:
#            id: gate_position
#            value: 0   # setzt den Zähler des Sensors Drehgeber auf Null gleich geschlossen  

# Zur Ansteuerung mit Taster Nr. 3 auf den Siedle Sprechanlagen
  - platform: gpio
    id: siedle_taster
    pin:
      number: GPIO4
      inverted: true
      mode:
        input: true
        pullup: true    
    on_press:
      then:
        - cover.toggle: ${devicename}_control
    
# Rotary Encoder (Tracks the gate's position)
sensor:
  - platform: rotary_encoder
    name: "${friendly_name}position"
    id: gate_position
    device_class: distance
    publish_initial_value: true
    pin_a:
      number: GPIO21 # Der selbstgebaute Drehgeber/ Encoder hat einen Spannungsteiler aus Widerständen und braucht deshalb kein pullup 
      mode:
        input: true
        pulldown: true
    pin_b:
      number: GPIO19
      mode:
        input: true
        pulldown: true
    resolution: 4
    unit_of_measurement: "%"
    filters:
      - multiply: 4.2   # 1 pulse = 1,05mm, 4 Pulse pro Rotation, 1 Rotation macht 4,2mm
      - round: 1
    on_clockwise:
      - logger.log: "Tor schliesst"
    on_anticlockwise:
      - logger.log: "Tor öffnet"
    on_value: 
      then:
        - cover.template.publish:
            id: ${devicename}_control
            state: !lambda 'return id(gate_position).state;'


  # Übergabewerte der Hoftorposition des Tores 25%, 50%, 100% offen in mm
  - platform: homeassistant
    entity_id: input_select.hoftoroffnung
    id: vorgabe_tor_position
    on_value:
      then:
        


# Covercontrol
cover:
  - platform: template
    id: ${devicename}_control
    name: Hoftor

    has_position: true
    optimistic: false
    assumed_state: false
    lambda: |-
      if (id(${devicename}_endschalter_s).state) {
        return COVER_OPEN;
      } else if (id(${devicename}_endschalter_n).state) {
        return COVER_CLOSED;
      } else {
        return {};
      }

    open_action: 
      - script.execute: toroeffnen

    close_action:       
      - script.execute: torschliessen
                
    stop_action: 
      - script.execute: torstop

script:
  - id: toroeffnen
    mode: single
    then:   
      - switch.turn_off: motor_power
      - switch.turn_on: ${devicename}_warnleuchte
      - delay: 3s
      - switch.turn_off: motor_spannung
      - switch.turn_off: motor_direction
      - delay: 0.2s
      - switch.turn_on: motor_power       

  - id: torschliessen
    mode: single
    then:   
      - switch.turn_off: motor_power
      - switch.turn_on: ${devicename}_warnleuchte      
      - delay: 3s
      - switch.turn_on: motor_direction  # 
      - switch.turn_off: motor_spannung   # immer 24V. falls die Spannungsversorgung geändert wird, kann man hier auch wieder auf kleinere Spannung gehen.
      - delay: 0.2s
      - switch.turn_on: motor_power
      - if:
          condition:
            lambda: 'return '(id(gate_position).state < 2.5);'
          then:
            - switch.turn_off: motor_power 
          else:
            - switch.turn_on: motor_power  # Bei Position >= 2,5% bleibt die Spannung auf 24V

  - id: torstop
    mode: single
    then:   
      - switch.turn_off: motor_power
      - delay: 2s  # Continue flashing for 1 second after motor stops
      - switch.turn_off: ${devicename}_warnleuchte
      - switch.turn_off: motor_direction  # damit das Relais am Optokoppler wieder spannungslos ist.  

# Optional: Set up a status LED
status_led:
  pin:
    number: GPIO2
    inverted: false

time:
  - platform: homeassistant
    id: homeassistant_time
    


#####     EOF    ##### 

I can now operate the gate, but I don’t know the positions while it is running. Furthermore I want to take over values from ha dashboard to bring the gate into certain positions.
I have not found an example with rotary encoder and a templated cover yet.

Dont force it. No means No, sir!

Thanks for your generous help anyway!

Did you try doing a Google search? Theres a whole bunch of results that deal with using a rotary encoder with a motor. It doesnt really matter if its not specifically a rotary encoder with a template cover because your looking for more of position control with the encoder so, just find something close and then modify it for a cover. 90% of the code is good to go as is.

https://www.google.com/search?q=esphome+cover+rotary+encoder&client=ms-android-samsung-rev2&sca_esv=b167fa96edc95c27&sca_upv=1&source=android-browser&sxsrf=ADLYWIKbHtREqco3qLZY1eVc7qvvcVwcBA%3A1725381490779&ei=cjvXZtWlL6CwptQPtIGbyQE&oq=esphome+cover+rotary+encoder&gs_lp=EhNtb2JpbGUtZ3dzLXdpei1zZXJwIhxlc3Bob21lIGNvdmVyIHJvdGFyeSBlbmNvZGVyMggQABiABBiiBDIIEAAYgAQYogQyCBAAGIAEGKIEMggQABiABBiiBDIIEAAYgAQYogRI2yBQmxVYjx5wAXgAkAEAmAGzAaABzweqAQMxLje4AQPIAQD4AQGYAgegAu4FwgIKEAAYsAMY1gQYR5gDAIgGAZAGCJIHAzEuNqAH3Rc&sclient=mobile-gws-wiz-serp