diff --git a/esphome/components/time_based/cover.py b/esphome/components/time_based/cover.py index 9625781c96..a14a08ccad 100644 --- a/esphome/components/time_based/cover.py +++ b/esphome/components/time_based/cover.py @@ -16,6 +16,7 @@ time_based_ns = cg.esphome_ns.namespace("time_based") TimeBasedCover = time_based_ns.class_("TimeBasedCover", cover.Cover, cg.Component) CONF_HAS_BUILT_IN_ENDSTOP = "has_built_in_endstop" +CONF_MANUAL_CONTROL = "manual_control" CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( { @@ -26,6 +27,7 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean, + cv.Optional(CONF_MANUAL_CONTROL, default=False): cv.boolean, cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA) @@ -51,4 +53,5 @@ async def to_code(config): ) cg.add(var.set_has_built_in_endstop(config[CONF_HAS_BUILT_IN_ENDSTOP])) + cg.add(var.set_manual_control(config[CONF_MANUAL_CONTROL])) cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index 522252e907..a7ba6d0595 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -79,6 +79,14 @@ void TimeBasedCover::control(const CoverCall &call) { auto pos = *call.get_position(); if (pos == this->position) { // already at target + if (this->manual_control_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) { + // for covers with manual control switch, we can't rely on the computed position, so if + // the command triggered again, we'll assume it's in the opposite direction anyway. + auto op = pos == COVER_CLOSED ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; + this->position = pos == COVER_CLOSED ? COVER_OPEN : COVER_CLOSED; + this->target_position_ = pos; + this->start_direction_(op); + } // for covers with built in end stop, we should send the command again if (this->has_built_in_endstop_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) { auto op = pos == COVER_CLOSED ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; diff --git a/esphome/components/time_based/time_based_cover.h b/esphome/components/time_based/time_based_cover.h index 517ab77cb3..b7a826d237 100644 --- a/esphome/components/time_based/time_based_cover.h +++ b/esphome/components/time_based/time_based_cover.h @@ -21,6 +21,7 @@ class TimeBasedCover : public cover::Cover, public Component { void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; } cover::CoverTraits get_traits() override; void set_has_built_in_endstop(bool value) { this->has_built_in_endstop_ = value; } + void set_manual_control(bool value) { this->manual_control_ = value; } void set_assumed_state(bool value) { this->assumed_state_ = value; } protected: @@ -44,6 +45,7 @@ class TimeBasedCover : public cover::Cover, public Component { uint32_t last_publish_time_{0}; float target_position_{0}; bool has_built_in_endstop_{false}; + bool manual_control_{false}; bool assumed_state_{false}; cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING}; };