Cover current_operation is not returning correctly

I am using one button to both start and stop a motor. If pressed when the motor is stopped, then I need it to start. But if pressed when the motor is started, then I need it to stop.

This motor controls a cover, so I’m using the Template Cover component as well.

To figure out if the motor is stopped or not, I’m using this lambda in an if statement in a binary_sensor:

lambda: 'return (id(ropener_cover).current_operation != COVER_OPERATION_IDLE);'

This should allow me to know if the motor is not idle. And if not idle, then stop. However, it does not work, it never evaluates to true and skips this step and I have no idea what I’m doing wrong. The reference I"m going off of for this is here and here.

The full YAML is below. The line above is in the binary_sensor section, about half way down. Thank you!!!

external_components:
  - source: github://slimcdk/esphome-custom-components
    components: [tmc2209_hub, tmc2209, stepper]

substitutions:
  steps_per_revolution: "1600"
  max_distance_revs: "float(231.9207)"
  max_distance_steps: "${max_distance_revs}*${steps_per_revolution}" 

api:

ota:
  - platform: esphome

logger:
  level: VERBOSE

esp32:
  board: esp32-c6-devkitc-1
  framework:
    type: esp-idf

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

esphome:
  name: glasscalibur
  on_boot:
    then:
    - tmc2209.configure:
        direction: cw
        microsteps: 8
        interpolation: true
    - tmc2209.stallguard:
        threshold: 50
    - tmc2209.currents:
        standstill_mode: freewheeling
        irun: 26
        ihold: 0
        tpowerdown: 0
        iholddelay: 0
    - if:
        condition:
          binary_sensor.is_off: limit_switch_1  # Check if switch is pressed
        then:
          - stepper.report_position:
              id: driver
              position: !lambda return ${max_distance_steps};
    - if:
        condition:
          binary_sensor.is_off: limit_switch_2  # Check if switch is pressed
        then:
          - stepper.report_position:
              id: driver
              position: 0

uart:
  tx_pin: 1
  rx_pin: 0
  baud_rate: 500000

stepper:
  - platform: tmc2209
    id: driver
    max_speed: 1500 steps/s
    acceleration: 1000 steps/s^2
    deceleration: 100000 steps/s^2
    config_dump_include_registers: true
    rsense: 220 mOhm
    vsense: False
    enn_pin: 8
    index_pin: 6
    diag_pin: 14
    on_stall:
      - stepper.stop: driver

# id(ropener_cover)->current_operation
# COVER_OPERATION_IDLE, COVER_OPERATION_OPENING, COVER_OPERATION_CLOSING

binary_sensor:
  - platform: gpio
    name: Button 1
    id: btn1
    pin:
      number: 23
      mode: INPUT
      inverted: true
    filters:
      - delayed_on: 10ms
    on_multi_click:

      # --- Single Click ---
      - timing:
          - ON for at most 1.0s
          - OFF for at least 0.5s
        then:
          - if:
              condition: ## If moving, then stop
                lambda: 'return (id(ropener_cover).current_operation != COVER_OPERATION_IDLE);' # Should evaluate to TRUE if moving
              then:
                - cover.stop: ropener_cover
              else: ## If not moving, then close
                - cover.close: ropener_cover

      # --- Double Click ---          
      - timing:
          - ON for at most 1.0s
          - OFF for at most 0.3s
          - ON for at most 1.0s
          - OFF for at least 0.5s
        then:
          - cover.open: ropener_cover


  - platform: gpio
    name: Open Limit Switch
    id: limit_switch_1
    pin:
      number: 15
      mode: INPUT
      inverted: true
    on_press:
      then:
      - stepper.report_position:
          id: driver
          position: 0
      - cover.stop: ropener_cover

  - platform: gpio
    name: Close Limit Switch
    id: limit_switch_2
    pin:
      number: 3
      mode: INPUT
      inverted: true
    on_press:
      then:
      - stepper.report_position:
          id: driver
          position: !lambda return ${max_distance_steps};
      - cover.stop: ropener_cover

cover:
  - platform: template
    id: ropener_cover
    name: Ropener
    has_position: true
    assumed_state: false
    lambda: "return 1.0 - (id(driver)->current_position / ${max_distance_steps});"
    stop_action:
      - stepper.stop: driver
    open_action:
      - stepper.set_target:
          id: driver
          target: 0
    close_action:
      - stepper.set_target:
          id: driver
          target: !lambda return ${max_distance_steps};
    position_action:
      - stepper.set_target:
          id: driver
          target: !lambda return (1.0 - pos) * ${max_distance_steps};```

Doesn’t look like the template cover ever sets the operation mode. You would need to do that yourself in the actions.

The problem is related to the cover position. When I was dividing two integers I would get a 0, and not a float like is required. So I just cast the position as a float and that’s fixed the tracking issue.

It’s still not working, even though I thought I had it. I’m even trying to set the operation manually in the cover and still no luck. It stays stuck in IDLE

Here are the logs while it’s moving:

14:05:26.494][D][main:246]: COVER open_action
[14:05:26.494][D][cover:147]: 'Glasscalibur' >>
[14:05:26.503][D][cover:150]:   Position: 0%
[14:05:26.503][D][cover:163]:   Current Operation: IDLE
[14:05:26.518][D][cover:147]: 'Glasscalibur' >>
[14:05:26.527][D][cover:150]:   Position: 0%
[14:05:26.528][D][cover:163]:   Current Operation: IDLE

Here’s the updated config:

external_components:
  - source: github://slimcdk/esphome-custom-components
    components: [tmc2209_hub, tmc2209, stepper]

substitutions:
  steps_per_revolution: "1600"
  max_distance_revs: "float(231.9207)"
  max_distance_steps: 371073 # converts cm to steps. Use this in remaining code

api:
  encryption:
    key: !secret api_key

ota:
  - platform: esphome

logger:
  level: VERBOSE
  hardware_uart: UART0

esp32:
  variant: esp32c6
  framework:
    type: esp-idf

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  on_connect:
    then:
      - logger.log: "Connected to WiFi! Running post-connect actions."
      - if:      # NEED TO CHECK IF EITHER LIMIT SWITCH IS PRESSED AND SET POSITION BASED ON THAT
          condition:
            binary_sensor.is_on: limit_switch_1  # Check if switch is pressed
          then:
            - logger.log: "SETTING LIMIT 1"
            - stepper.report_position:
                id: driver
                position: ${max_distance_steps}
            - stepper.set_target:
                id: driver
                target: ${max_distance_steps}

      - if:
          condition:
            binary_sensor.is_on: limit_switch_2  # Check if switch is pressed
          then:
            - logger.log: "SETTING LIMIT 2"
            - stepper.report_position:
                id: driver
                position: 0
            - stepper.set_target:
                id: driver
                target: 0

esphome:
  name: "glasscalibur-kids-bedroom"
  on_boot:
    # - priority: -100
    #   then:
      - logger.log: "CHECKING LIMIT SWITCHES"
      - if:      # NEED TO CHECK IF EITHER LIMIT SWITCH IS PRESSED AND SET POSITION BASED ON THAT
          condition:
            binary_sensor.is_on: limit_switch_1  # Check if switch is pressed
          then:
            - logger.log: "SETTING LIMIT 1"
            - stepper.report_position:
                id: driver
                position: ${max_distance_steps}
            - stepper.set_target:
                id: driver
                target: ${max_distance_steps}
      - if:
          condition:
            binary_sensor.is_on: limit_switch_2  # Check if switch is pressed
          then:
            - logger.log: "SETTING LIMIT 2"
            - stepper.report_position:
                id: driver
                position: 0
            - stepper.set_target:
                id: driver
                target: 0

      - tmc2209.configure:
          direction: ccw
          microsteps: 8
          interpolation: true
      - tmc2209.stallguard:
          threshold: 50
      - tmc2209.currents:
          standstill_mode: freewheeling
          irun: 25 # 26 = 0.6A current. DO NOT EXCEED. MOTOR GETS STUPID HOT.
          ihold: 0
          tpowerdown: 0
          iholddelay: 0

output:
  - platform: ledc
    pin: GPIO5
    id: buzzer

uart:
  tx_pin: 1
  rx_pin: 0
  baud_rate: 500000

stepper:
  - platform: tmc2209
    id: driver
    #max_speed: 1500 steps/s # Faster speed is louder #2000 good. #1500 great
    max_speed: 1501 steps/s # Faster speed is louder #2000 good. #1500 great
    acceleration: 1000 steps/s^2
    deceleration: 100000 steps/s^2 # Stop very fast in case limit swith reached
    config_dump_include_registers: true
    rsense: 220 mOhm
    vsense: False
    enn_pin: 8
    index_pin: 6
    diag_pin: 14
    #on_stall:
      #- stepper.stop: driver

binary_sensor:
  - platform: gpio
    name: Button 1
    id: btn1
    pin:
      number: 23
      mode: INPUT
      inverted: true
    filters:
      - delayed_on: 10ms
    on_multi_click:

      # --- Single Click ---
      - timing:
          - ON for at most 1.0s
          - OFF for at least 0.5s
        then:
          - cover.close: glasscalibur_cover
          - if:
              condition: ## If moving, then stop
                lambda: return (id(glasscalibur_cover).current_operation != COVER_OPERATION_IDLE); # Should evaluate to TRUE if moving
              then:
                - cover.stop: glasscalibur_cover
              else: ## If not moving, then close
                - cover.close: glasscalibur_cover

      # --- Double Click ---          
      - timing:
          - ON for at most 1.0s
          - OFF for at most 0.3s
          - ON for at most 1.0s
          - OFF for at least 0.5s
        then:
          - cover.open: glasscalibur_cover

  - platform: gpio
    name: Open Limit Switch
    id: limit_switch_1
    pin:
      number: 15
      mode: INPUT
      inverted: true
    interrupt_type: FALLING
    filters: 
      - delayed_on: 10ms   
    on_press:
      then:
      - logger.log: "LIMIT 1 PRESSED"
      - cover.stop: glasscalibur_cover

      - stepper.report_position:
          id: driver
          position: ${max_distance_steps}
      - stepper.set_target:
          id: driver
          target: ${max_distance_steps}

      - cover.template.publish:
          id: glasscalibur_cover
          position: 1.0
          

  - platform: gpio
    name: Close Limit Switch
    id: limit_switch_2
    pin:
      number: 3
      mode: INPUT
      inverted: true
    interrupt_type: FALLING
    # Optional: filters to prevent noise from triggering the sensor
    filters: 
      - delayed_on: 10ms   
    on_press:
      then:
      - cover.stop: glasscalibur_cover
      - logger.log: "LIMIT 2 PRESSED"
      - stepper.report_position:
          id: driver
          position: 0
      - stepper.set_target:
          id: driver
          target: 0
      - cover.template.publish:
          id: glasscalibur_cover
          position: 0

cover:
  - platform: template
    id: glasscalibur_cover
    name: Glasscalibur
    has_position: true
    lambda: "return (float(id(driver)->current_position) / float(${max_distance_steps}));"
    stop_action:
      - logger.log: "COVER stop_action"
      - stepper.stop: driver
    open_action:
      - logger.log: "COVER open_action"
      - cover.template.publish:
          id: glasscalibur_cover
          current_operation : OPENING
      - stepper.set_target:
          id: driver
          target: ${max_distance_steps}
      
    close_action:
      - logger.log: "COVER close_action"
      - stepper.set_target:
          id: driver
          target: 0
    position_action:
      - logger.log: "COVER position_action"
      - stepper.set_target:
          id: driver
          target: !lambda return pos * ${max_distance_steps};

          
    on_opening:
      - logger.log: "COVER Opening"
      - cover.template.publish:
          id: glasscalibur_cover
          current_operation : OPENING
    on_opened:
      - logger.log: "COVER Open"
      - cover.template.publish:
          id: glasscalibur_cover
          current_operation : IDLE
    on_closing:
      - logger.log: "COVER Closing"
      - cover.template.publish:
          id: glasscalibur_cover
          current_operation : CLOSING
    on_closed:
      - logger.log: "COVER Closed"
      - cover.template.publish:
          id: glasscalibur_cover
          current_operation : IDLE