diff --git a/esphome/components/feedback/cover.py b/esphome/components/feedback/cover.py index 450eb967b1..b43d736fa8 100644 --- a/esphome/components/feedback/cover.py +++ b/esphome/components/feedback/cover.py @@ -1,18 +1,18 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import binary_sensor, cover +import esphome.config_validation as cv from esphome.const import ( CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, CONF_CLOSE_ENDSTOP, CONF_ID, + CONF_MAX_DURATION, CONF_OPEN_ACTION, CONF_OPEN_DURATION, CONF_OPEN_ENDSTOP, CONF_STOP_ACTION, - CONF_MAX_DURATION, CONF_UPDATE_INTERVAL, ) @@ -24,6 +24,7 @@ CONF_HAS_BUILT_IN_ENDSTOP = "has_built_in_endstop" CONF_INFER_ENDSTOP_FROM_MOVEMENT = "infer_endstop_from_movement" CONF_DIRECTION_CHANGE_WAIT_TIME = "direction_change_wait_time" CONF_ACCELERATION_WAIT_TIME = "acceleration_wait_time" +CONF_OVERSHOOT_DURATION = "overshoot_duration" CONF_OBSTACLE_ROLLBACK = "obstacle_rollback" endstop_ns = cg.esphome_ns.namespace("feedback") @@ -77,6 +78,9 @@ CONFIG_FEEDBACK_COVER_BASE_SCHEMA = cover.COVER_SCHEMA.extend( cv.Optional( CONF_ACCELERATION_WAIT_TIME, "0s" ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_OVERSHOOT_DURATION, "0s" + ): cv.positive_time_period_milliseconds, cv.Optional(CONF_OBSTACLE_ROLLBACK, default="10%"): cv.percentage, }, ).extend(cv.COMPONENT_SCHEMA) @@ -154,4 +158,5 @@ async def to_code(config): var.set_direction_change_waittime(config[CONF_DIRECTION_CHANGE_WAIT_TIME]) ) cg.add(var.set_acceleration_wait_time(config[CONF_ACCELERATION_WAIT_TIME])) + cg.add(var.set_overshoot_duration(config[CONF_OVERSHOOT_DURATION])) cg.add(var.set_obstacle_rollback(config[CONF_OBSTACLE_ROLLBACK])) diff --git a/esphome/components/feedback/feedback_cover.cpp b/esphome/components/feedback/feedback_cover.cpp index fa3166ba65..b69cdc693d 100644 --- a/esphome/components/feedback/feedback_cover.cpp +++ b/esphome/components/feedback/feedback_cover.cpp @@ -77,6 +77,9 @@ void FeedbackCover::dump_config() { if (this->acceleration_wait_time_) { ESP_LOGCONFIG(TAG, " Acceleration wait time: %.1fs", this->acceleration_wait_time_ / 1e3f); } + if (this->overshoot_duration_) { + ESP_LOGCONFIG(TAG, " Overshoot duration: %.1fs", this->overshoot_duration_ / 1e3f); + } #ifdef USE_BINARY_SENSOR if (this->obstacle_rollback_ && (this->open_obstacle_ != nullptr || this->close_obstacle_ != nullptr)) { ESP_LOGCONFIG(TAG, " Obstacle rollback: %.1f%%", this->obstacle_rollback_ * 100); @@ -229,11 +232,26 @@ void FeedbackCover::loop() { // (stoping from endstop sensor is handled in callback) if (this->current_trigger_operation_ != COVER_OPERATION_IDLE) { if (this->is_at_target_()) { - if (this->has_built_in_endstop_ && - (this->target_position_ == COVER_OPEN || this->target_position_ == COVER_CLOSED)) { - // Don't trigger stop, let the cover stop by itself. - this->set_current_operation_(COVER_OPERATION_IDLE, true); + if (this->target_position_ == COVER_OPEN || this->target_position_ == COVER_CLOSED) { + if (this->has_built_in_endstop_) { + // Don't trigger stop, let the cover stop by itself. + this->set_current_operation_(COVER_OPERATION_IDLE, true); + } else if (this->overshoot_duration_) { + // We want to overshoot a bit on cover being totaly open or closed, in order to make sure cover is synced + if (!this->start_overshoot_time_) { + // We just started overshooting, save time for comparison + this->start_overshoot_time_ = now; + } else if (now - this->start_overshoot_time_ > this->overshoot_duration_) { + // We overshot enough, stop the cover + ESP_LOGD(TAG, "'%s' - Overshoot duration reached. Stopping cover.", this->name_.c_str()); + this->start_direction_(COVER_OPERATION_IDLE); + } + } else { + // No overshoot_duration, stop immediately at open-close + this->start_direction_(COVER_OPERATION_IDLE); + } } else { + // Not on open-close position, stop immediately this->start_direction_(COVER_OPERATION_IDLE); } } else if (now - this->start_dir_time_ > this->max_duration_) { @@ -335,6 +353,7 @@ void FeedbackCover::start_direction_(CoverOperation dir) { switch (dir) { case COVER_OPERATION_IDLE: trig = this->stop_trigger_; + this->start_overshoot_time_ = 0; break; case COVER_OPERATION_OPENING: this->last_operation_ = dir; diff --git a/esphome/components/feedback/feedback_cover.h b/esphome/components/feedback/feedback_cover.h index 7e107aebcd..ace72d27e1 100644 --- a/esphome/components/feedback/feedback_cover.h +++ b/esphome/components/feedback/feedback_cover.h @@ -35,6 +35,7 @@ class FeedbackCover : public cover::Cover, public Component { void set_has_built_in_endstop(bool value) { this->has_built_in_endstop_ = value; } void set_assumed_state(bool value) { this->assumed_state_ = value; } void set_max_duration(uint32_t max_duration) { this->max_duration_ = max_duration; } + void set_overshoot_duration(uint32_t overshoot_duration) { this->overshoot_duration_ = overshoot_duration; } void set_obstacle_rollback(float obstacle_rollback) { this->obstacle_rollback_ = obstacle_rollback; } void set_update_interval(uint32_t interval) { this->update_interval_ = interval; } void set_infer_endstop(bool infer_endstop) { this->infer_endstop_ = infer_endstop; } @@ -69,6 +70,7 @@ class FeedbackCover : public cover::Cover, public Component { uint32_t open_duration_{0}; uint32_t close_duration_{0}; uint32_t max_duration_{UINT32_MAX}; + uint32_t overshoot_duration_{0}; optional direction_change_waittime_{}; uint32_t acceleration_wait_time_{0}; bool has_built_in_endstop_{false}; @@ -82,6 +84,7 @@ class FeedbackCover : public cover::Cover, public Component { uint32_t last_recompute_time_{0}; uint32_t start_dir_time_{0}; uint32_t last_publish_time_{0}; + uint32_t start_overshoot_time_{0}; float target_position_{0}; uint32_t update_interval_{1000}; };