From eea9f8438d7ae2cf809221bb1f47e0e950bec4e2 Mon Sep 17 00:00:00 2001 From: Artem Sheremet Date: Fri, 22 May 2026 22:11:42 +0000 Subject: [PATCH] Fix garage door not moving in opposite direction --- components/ratgdo/ratgdo.cpp | 74 +++++++++++++++++++++++++++++++----- components/ratgdo/ratgdo.h | 1 + garage-gate.yaml | 7 ---- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index 7dde5b7..f79f234 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -302,18 +302,65 @@ void RATGDOComponent::cancel_door_state_expiry() this->cancel_timeout(TIMEOUT_DOOR_STATE_EXPIRY); } +void RATGDOComponent::smart_door_action(DoorAction target_direction) +{ + // target_direction must be OPEN or CLOSE + if (target_direction != DoorAction::OPEN && target_direction != DoorAction::CLOSE) { + return; + } + + DoorState expected_state = (target_direction == DoorAction::OPEN) ? DoorState::OPENING : DoorState::CLOSING; + DoorState opposite_state = (target_direction == DoorAction::OPEN) ? DoorState::CLOSING : DoorState::OPENING; + + // 1. Try the discrete command first + this->door_action(target_direction); + + // 2. Set a timeout to check if it worked + this->set_timeout(2000, [this, target_direction, expected_state, opposite_state]() { + if (*this->door_state == expected_state || + (*this->door_state == DoorState::OPEN && target_direction == DoorAction::OPEN) || + (*this->door_state == DoorState::CLOSED && target_direction == DoorAction::CLOSE)) { + // It worked (or is already at destination) + return; + } + + ESP_LOGW(TAG, "Discrete %s command ignored. Falling back to TOGGLE...", + target_direction == DoorAction::OPEN ? "OPEN" : "CLOSE"); + + // 3. Fallback: Send a TOGGLE + this->door_action(DoorAction::TOGGLE); + + // 4. Check what the toggle did + this->on_door_state([this, target_direction, expected_state, opposite_state](DoorState s) { + if (s == opposite_state) { + // Wrong direction! Stop it. + ESP_LOGW(TAG, "Toggle went the wrong way. Stopping..."); + this->door_action(DoorAction::STOP); + + // Once stopped, toggle again to go the right way + this->on_door_state([this](DoorState s2) { + if (s2 == DoorState::STOPPED) { + ESP_LOGD(TAG, "Stopped. Toggling again for correct direction."); + this->door_action(DoorAction::TOGGLE); + } + }); + } + }); + }); +} + void RATGDOComponent::door_open() { if (*this->door_state == DoorState::OPENING) { return; // gets ignored by opener } - this->door_action(DoorAction::OPEN); + this->smart_door_action(DoorAction::OPEN); if (*this->opening_duration > 0) { // query state in case we don't get a status message this->set_timeout( - TIMEOUT_DOOR_QUERY_STATE, (*this->opening_duration + 2) * 1000, + TIMEOUT_DOOR_QUERY_STATE, (*this->opening_duration + 5) * 1000, [this]() { if (*this->door_state != DoorState::OPEN && *this->door_state != DoorState::STOPPED) { this->received(DoorState::OPEN); // probably missed a status mesage, @@ -335,7 +382,7 @@ void RATGDOComponent::door_close() this->door_action(DoorAction::STOP); this->on_door_state([this](DoorState s) { if (s == DoorState::STOPPED) { - this->door_action(DoorAction::CLOSE); + this->smart_door_action(DoorAction::CLOSE); } else { ESP_LOGW(TAG, "Door did not stop, ignoring close command"); } @@ -343,12 +390,12 @@ void RATGDOComponent::door_close() return; } - this->door_action(DoorAction::CLOSE); + this->smart_door_action(DoorAction::CLOSE); if (*this->closing_duration > 0) { // query state in case we don't get a status message this->set_timeout( - TIMEOUT_DOOR_QUERY_STATE, (*this->closing_duration + 2) * 1000, + TIMEOUT_DOOR_QUERY_STATE, (*this->closing_duration + 5) * 1000, [this]() { if (*this->door_state != DoorState::CLOSED && *this->door_state != DoorState::STOPPED) { this->received(DoorState::CLOSED); // probably missed a status @@ -362,7 +409,6 @@ void RATGDOComponent::door_close() void RATGDOComponent::door_stop() { if (*this->door_state != DoorState::OPENING && *this->door_state != DoorState::CLOSING) { - ESP_LOGW(TAG, "The door is not moving."); return; } this->door_action(DoorAction::STOP); @@ -401,12 +447,20 @@ void RATGDOComponent::door_move_to_position(float position) auto operation_time = 1000 * duration * delta; this->door_move_delta = delta; - ESP_LOGD(TAG, "Moving to position %.2f in %.1fs", position, + + ESP_LOGD(TAG, "Moving to position %.2f (target duration %.1fs)", position, operation_time / 1000.0); - this->door_action(delta > 0 ? DoorAction::OPEN : DoorAction::CLOSE); - this->set_timeout(TIMEOUT_MOVE_TO_POSITION, operation_time, - [this] { this->door_action(DoorAction::STOP); }); + // Wait for the door to actually start moving before starting the stop timer. + // This makes the timing independent of any smart fallback delays. + this->on_door_state([this, operation_time](DoorState s) { + if (s == DoorState::OPENING || s == DoorState::CLOSING) { + this->set_timeout(TIMEOUT_MOVE_TO_POSITION, operation_time, + [this] { this->door_action(DoorAction::STOP); }); + } + }); + + this->smart_door_action(delta > 0 ? DoorAction::OPEN : DoorAction::CLOSE); } void RATGDOComponent::cancel_position_sync_callbacks() diff --git a/components/ratgdo/ratgdo.h b/components/ratgdo/ratgdo.h index 2ad9e47..44c691c 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -98,6 +98,7 @@ public: void door_stop(); void door_action(DoorAction action); + void smart_door_action(DoorAction target_direction); void door_move_to_position(float position); void set_door_position(float door_position) { this->door_position = door_position; } void set_opening_duration(float duration); diff --git a/garage-gate.yaml b/garage-gate.yaml index d089ff5..af19057 100644 --- a/garage-gate.yaml +++ b/garage-gate.yaml @@ -168,13 +168,6 @@ button: then: lambda: !lambda |- id(garage_gate).sync(); - - platform: template - id: garage_gate_toggle_door - name: "Toggle door" - on_press: - then: - lambda: !lambda |- - id(garage_gate).door_toggle(); - platform: template id: garage_gate_play_alert name: "Play Alert"