mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Add Current based cover (#1439)
* Adding first version of current_base cover. No Interlock yet. * simplifying code * Implementing malfunction protection * Adding test * Fixing too long lines * Fixing test sensor names * Adding missing id's in ade7953 tests * Adding code owners as requested * Fixing issue setting position when stop reached * Fixing issue setting position when stop reached * Black formatting * Fixing format issues * Fix for concurrent changes Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							1ba560dc9e
						
					
				
				
					commit
					e30f17f64f
				
			| @@ -39,6 +39,7 @@ esphome/components/coolix/* @glmnet | ||||
| esphome/components/cover/* @esphome/core | ||||
| esphome/components/cs5460a/* @balrog-kun | ||||
| esphome/components/ct_clamp/* @jesserockz | ||||
| esphome/components/current_based/* @djwmarcx | ||||
| esphome/components/daly_bms/* @s1lvi0 | ||||
| esphome/components/dashboard_import/* @esphome/core | ||||
| esphome/components/debug/* @OttoWinter | ||||
|   | ||||
							
								
								
									
										1
									
								
								esphome/components/current_based/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/current_based/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@djwmarcx"] | ||||
							
								
								
									
										124
									
								
								esphome/components/current_based/cover.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								esphome/components/current_based/cover.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.components import sensor, cover | ||||
| from esphome.const import ( | ||||
|     CONF_CLOSE_ACTION, | ||||
|     CONF_CLOSE_DURATION, | ||||
|     CONF_ID, | ||||
|     CONF_OPEN_ACTION, | ||||
|     CONF_OPEN_DURATION, | ||||
|     CONF_STOP_ACTION, | ||||
|     CONF_MAX_DURATION, | ||||
| ) | ||||
|  | ||||
|  | ||||
| CONF_OPEN_SENSOR = "open_sensor" | ||||
| CONF_OPEN_MOVING_CURRENT_THRESHOLD = "open_moving_current_threshold" | ||||
| CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD = "open_obstacle_current_threshold" | ||||
|  | ||||
| CONF_CLOSE_SENSOR = "close_sensor" | ||||
| CONF_CLOSE_MOVING_CURRENT_THRESHOLD = "close_moving_current_threshold" | ||||
| CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD = "close_obstacle_current_threshold" | ||||
|  | ||||
| CONF_OBSTACLE_ROLLBACK = "obstacle_rollback" | ||||
| CONF_MALFUNCTION_DETECTION = "malfunction_detection" | ||||
| CONF_MALFUNCTION_ACTION = "malfunction_action" | ||||
| CONF_START_SENSING_DELAY = "start_sensing_delay" | ||||
|  | ||||
| current_based_ns = cg.esphome_ns.namespace("current_based") | ||||
| CurrentBasedCover = current_based_ns.class_( | ||||
|     "CurrentBasedCover", cover.Cover, cg.Component | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(CurrentBasedCover), | ||||
|         cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Required(CONF_OPEN_SENSOR): cv.use_id(sensor.Sensor), | ||||
|         cv.Required(CONF_OPEN_MOVING_CURRENT_THRESHOLD): cv.float_range( | ||||
|             min=0, min_included=False | ||||
|         ), | ||||
|         cv.Optional(CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD): cv.float_range( | ||||
|             min=0, min_included=False | ||||
|         ), | ||||
|         cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, | ||||
|         cv.Required(CONF_CLOSE_SENSOR): cv.use_id(sensor.Sensor), | ||||
|         cv.Required(CONF_CLOSE_MOVING_CURRENT_THRESHOLD): cv.float_range( | ||||
|             min=0, min_included=False | ||||
|         ), | ||||
|         cv.Optional(CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD): cv.float_range( | ||||
|             min=0, min_included=False | ||||
|         ), | ||||
|         cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, | ||||
|         cv.Optional(CONF_OBSTACLE_ROLLBACK, default="10%"): cv.percentage, | ||||
|         cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds, | ||||
|         cv.Optional(CONF_MALFUNCTION_DETECTION, default=True): cv.boolean, | ||||
|         cv.Optional(CONF_MALFUNCTION_ACTION): automation.validate_automation( | ||||
|             single=True | ||||
|         ), | ||||
|         cv.Optional( | ||||
|             CONF_START_SENSING_DELAY, default="500ms" | ||||
|         ): cv.positive_time_period_milliseconds, | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield cover.register_cover(var, config) | ||||
|  | ||||
|     yield automation.build_automation( | ||||
|         var.get_stop_trigger(), [], config[CONF_STOP_ACTION] | ||||
|     ) | ||||
|  | ||||
|     # OPEN | ||||
|     bin = yield cg.get_variable(config[CONF_OPEN_SENSOR]) | ||||
|     cg.add(var.set_open_sensor(bin)) | ||||
|     cg.add( | ||||
|         var.set_open_moving_current_threshold( | ||||
|             config[CONF_OPEN_MOVING_CURRENT_THRESHOLD] | ||||
|         ) | ||||
|     ) | ||||
|     if CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD in config: | ||||
|         cg.add( | ||||
|             var.set_open_obstacle_current_threshold( | ||||
|                 config[CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD] | ||||
|             ) | ||||
|         ) | ||||
|     cg.add(var.set_open_duration(config[CONF_OPEN_DURATION])) | ||||
|     yield automation.build_automation( | ||||
|         var.get_open_trigger(), [], config[CONF_OPEN_ACTION] | ||||
|     ) | ||||
|  | ||||
|     # CLOSE | ||||
|     bin = yield cg.get_variable(config[CONF_CLOSE_SENSOR]) | ||||
|     cg.add(var.set_close_sensor(bin)) | ||||
|     cg.add( | ||||
|         var.set_close_moving_current_threshold( | ||||
|             config[CONF_CLOSE_MOVING_CURRENT_THRESHOLD] | ||||
|         ) | ||||
|     ) | ||||
|     if CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD in config: | ||||
|         cg.add( | ||||
|             var.set_close_obstacle_current_threshold( | ||||
|                 config[CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD] | ||||
|             ) | ||||
|         ) | ||||
|     cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION])) | ||||
|     yield automation.build_automation( | ||||
|         var.get_close_trigger(), [], config[CONF_CLOSE_ACTION] | ||||
|     ) | ||||
|  | ||||
|     cg.add(var.set_obstacle_rollback(config[CONF_OBSTACLE_ROLLBACK])) | ||||
|     if CONF_MAX_DURATION in config: | ||||
|         cg.add(var.set_max_duration(config[CONF_MAX_DURATION])) | ||||
|     cg.add(var.set_malfunction_detection(config[CONF_MALFUNCTION_DETECTION])) | ||||
|     if CONF_MALFUNCTION_ACTION in config: | ||||
|         yield automation.build_automation( | ||||
|             var.get_malfunction_trigger(), [], config[CONF_MALFUNCTION_ACTION] | ||||
|         ) | ||||
|     cg.add(var.set_start_sensing_delay(config[CONF_START_SENSING_DELAY])) | ||||
							
								
								
									
										251
									
								
								esphome/components/current_based/current_based_cover.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								esphome/components/current_based/current_based_cover.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| #include "current_based_cover.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <cfloat> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace current_based { | ||||
|  | ||||
| static const char *const TAG = "current_based.cover"; | ||||
|  | ||||
| using namespace esphome::cover; | ||||
|  | ||||
| CoverTraits CurrentBasedCover::get_traits() { | ||||
|   auto traits = CoverTraits(); | ||||
|   traits.set_supports_position(true); | ||||
|   traits.set_is_assumed_state(false); | ||||
|   return traits; | ||||
| } | ||||
| void CurrentBasedCover::control(const CoverCall &call) { | ||||
|   if (call.get_stop()) { | ||||
|     this->direction_idle_(); | ||||
|   } | ||||
|   if (call.get_position().has_value()) { | ||||
|     auto pos = *call.get_position(); | ||||
|     if (pos == this->position) { | ||||
|       // already at target | ||||
|     } else { | ||||
|       auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; | ||||
|       this->target_position_ = pos; | ||||
|       this->start_direction_(op); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| void CurrentBasedCover::setup() { | ||||
|   auto restore = this->restore_state_(); | ||||
|   if (restore.has_value()) { | ||||
|     restore->apply(this); | ||||
|   } else { | ||||
|     this->position = 0.5f; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CurrentBasedCover::loop() { | ||||
|   if (this->current_operation == COVER_OPERATION_IDLE) | ||||
|     return; | ||||
|  | ||||
|   const uint32_t now = millis(); | ||||
|  | ||||
|   if (this->current_operation == COVER_OPERATION_OPENING) { | ||||
|     if (this->malfunction_detection_ && this->is_closing_()) {  // Malfunction | ||||
|       this->direction_idle_(); | ||||
|       this->malfunction_trigger_->trigger(); | ||||
|       ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit", | ||||
|                this->name_.c_str()); | ||||
|     } else if (this->is_opening_blocked_()) {  // Blocked | ||||
|       ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str()); | ||||
|       this->direction_idle_(); | ||||
|       if (this->obstacle_rollback_ != 0) { | ||||
|         this->set_timeout("rollback", 300, [this]() { | ||||
|           ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str()); | ||||
|           this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F); | ||||
|           this->start_direction_(COVER_OPERATION_CLOSING); | ||||
|         }); | ||||
|       } | ||||
|     } else if (this->is_initial_delay_finished_() && !this->is_opening_()) {  // End reached | ||||
|       auto dur = (now - this->start_dir_time_) / 1e3f; | ||||
|       ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur); | ||||
|       this->direction_idle_(COVER_OPEN); | ||||
|     } | ||||
|   } else if (this->current_operation == COVER_OPERATION_CLOSING) { | ||||
|     if (this->malfunction_detection_ && this->is_opening_()) {  // Malfunction | ||||
|       this->direction_idle_(); | ||||
|       this->malfunction_trigger_->trigger(); | ||||
|       ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit", | ||||
|                this->name_.c_str()); | ||||
|     } else if (this->is_closing_blocked_()) {  // Blocked | ||||
|       ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str()); | ||||
|       this->direction_idle_(); | ||||
|       if (this->obstacle_rollback_ != 0) { | ||||
|         this->set_timeout("rollback", 300, [this]() { | ||||
|           ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str()); | ||||
|           this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F); | ||||
|           this->start_direction_(COVER_OPERATION_OPENING); | ||||
|         }); | ||||
|       } | ||||
|     } else if (this->is_initial_delay_finished_() && !this->is_closing_()) {  // End reached | ||||
|       auto dur = (now - this->start_dir_time_) / 1e3f; | ||||
|       ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur); | ||||
|       this->direction_idle_(COVER_CLOSED); | ||||
|     } | ||||
|   } else if (now - this->start_dir_time_ > this->max_duration_) { | ||||
|     ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str()); | ||||
|     this->direction_idle_(); | ||||
|   } | ||||
|  | ||||
|   // Recompute position every loop cycle | ||||
|   this->recompute_position_(); | ||||
|  | ||||
|   if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) { | ||||
|     this->direction_idle_(); | ||||
|   } | ||||
|  | ||||
|   // Send current position every second | ||||
|   if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) { | ||||
|     this->publish_state(false); | ||||
|     this->last_publish_time_ = now; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CurrentBasedCover::direction_idle_(float new_position) { | ||||
|   this->start_direction_(COVER_OPERATION_IDLE); | ||||
|   if (new_position != FLT_MAX) { | ||||
|     this->position = new_position; | ||||
|   } | ||||
|   this->publish_state(); | ||||
| } | ||||
|  | ||||
| void CurrentBasedCover::dump_config() { | ||||
|   LOG_COVER("", "Endstop Cover", this); | ||||
|   LOG_SENSOR("  ", "Open Sensor", this->open_sensor_); | ||||
|   ESP_LOGCONFIG(TAG, "  Open moving current threshold: %.11fA", this->open_moving_current_threshold_); | ||||
|   if (this->open_obstacle_current_threshold_ != FLT_MAX) { | ||||
|     ESP_LOGCONFIG(TAG, "  Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_); | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "  Open Duration: %.1fs", this->open_duration_ / 1e3f); | ||||
|   LOG_SENSOR("  ", "Close Sensor", this->close_sensor_); | ||||
|   ESP_LOGCONFIG(TAG, "  Close moving current threshold: %.11fA", this->close_moving_current_threshold_); | ||||
|   if (this->close_obstacle_current_threshold_ != FLT_MAX) { | ||||
|     ESP_LOGCONFIG(TAG, "  Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_); | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "  Close Duration: %.1fs", this->close_duration_ / 1e3f); | ||||
|   ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100); | ||||
|   if (this->max_duration_ != UINT32_MAX) { | ||||
|     ESP_LOGCONFIG(TAG, "Maximun duration: %.1fs", this->max_duration_ / 1e3f); | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f); | ||||
|   ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_)); | ||||
| } | ||||
|  | ||||
| float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; } | ||||
| void CurrentBasedCover::stop_prev_trigger_() { | ||||
|   if (this->prev_command_trigger_ != nullptr) { | ||||
|     this->prev_command_trigger_->stop_action(); | ||||
|     this->prev_command_trigger_ = nullptr; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool CurrentBasedCover::is_opening_() const { | ||||
|   return this->open_sensor_->get_state() > this->open_moving_current_threshold_; | ||||
| } | ||||
|  | ||||
| bool CurrentBasedCover::is_opening_blocked_() const { | ||||
|   if (this->open_obstacle_current_threshold_ == FLT_MAX) { | ||||
|     return false; | ||||
|   } | ||||
|   return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_; | ||||
| } | ||||
|  | ||||
| bool CurrentBasedCover::is_closing_() const { | ||||
|   return this->close_sensor_->get_state() > this->close_moving_current_threshold_; | ||||
| } | ||||
|  | ||||
| bool CurrentBasedCover::is_closing_blocked_() const { | ||||
|   if (this->close_obstacle_current_threshold_ == FLT_MAX) { | ||||
|     return false; | ||||
|   } | ||||
|   return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_; | ||||
| } | ||||
| bool CurrentBasedCover::is_initial_delay_finished_() const { | ||||
|   return millis() - this->start_dir_time_ > this->start_sensing_delay_; | ||||
| } | ||||
|  | ||||
| bool CurrentBasedCover::is_at_target_() const { | ||||
|   switch (this->current_operation) { | ||||
|     case COVER_OPERATION_OPENING: | ||||
|       if (this->target_position_ == COVER_OPEN) { | ||||
|         if (!this->is_initial_delay_finished_())  // During initial delay, state is assumed | ||||
|           return false; | ||||
|         return !this->is_opening_(); | ||||
|       } | ||||
|       return this->position >= this->target_position_; | ||||
|     case COVER_OPERATION_CLOSING: | ||||
|       if (this->target_position_ == COVER_CLOSED) { | ||||
|         if (!this->is_initial_delay_finished_())  // During initial delay, state is assumed | ||||
|           return false; | ||||
|         return !this->is_closing_(); | ||||
|       } | ||||
|       return this->position <= this->target_position_; | ||||
|     case COVER_OPERATION_IDLE: | ||||
|     default: | ||||
|       return true; | ||||
|   } | ||||
| } | ||||
| void CurrentBasedCover::start_direction_(CoverOperation dir) { | ||||
|   if (dir == this->current_operation) | ||||
|     return; | ||||
|  | ||||
|   this->recompute_position_(); | ||||
|   Trigger<> *trig; | ||||
|   switch (dir) { | ||||
|     case COVER_OPERATION_IDLE: | ||||
|       trig = this->stop_trigger_; | ||||
|       break; | ||||
|     case COVER_OPERATION_OPENING: | ||||
|       trig = this->open_trigger_; | ||||
|       break; | ||||
|     case COVER_OPERATION_CLOSING: | ||||
|       trig = this->close_trigger_; | ||||
|       break; | ||||
|     default: | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   this->current_operation = dir; | ||||
|  | ||||
|   this->stop_prev_trigger_(); | ||||
|   trig->trigger(); | ||||
|   this->prev_command_trigger_ = trig; | ||||
|  | ||||
|   const auto now = millis(); | ||||
|   this->start_dir_time_ = now; | ||||
|   this->last_recompute_time_ = now; | ||||
| } | ||||
| void CurrentBasedCover::recompute_position_() { | ||||
|   if (this->current_operation == COVER_OPERATION_IDLE) | ||||
|     return; | ||||
|  | ||||
|   float dir; | ||||
|   float action_dur; | ||||
|   switch (this->current_operation) { | ||||
|     case COVER_OPERATION_OPENING: | ||||
|       dir = 1.0F; | ||||
|       action_dur = this->open_duration_; | ||||
|       break; | ||||
|     case COVER_OPERATION_CLOSING: | ||||
|       dir = -1.0F; | ||||
|       action_dur = this->close_duration_; | ||||
|       break; | ||||
|     default: | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   const auto now = millis(); | ||||
|   this->position += dir * (now - this->last_recompute_time_) / action_dur; | ||||
|   this->position = clamp(this->position, 0.0F, 1.0F); | ||||
|  | ||||
|   this->last_recompute_time_ = now; | ||||
| } | ||||
|  | ||||
| }  // namespace current_based | ||||
| }  // namespace esphome | ||||
							
								
								
									
										95
									
								
								esphome/components/current_based/current_based_cover.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								esphome/components/current_based/current_based_cover.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/cover/cover.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include <cfloat> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace current_based { | ||||
|  | ||||
| class CurrentBasedCover : public cover::Cover, public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   Trigger<> *get_stop_trigger() const { return this->stop_trigger_; } | ||||
|  | ||||
|   Trigger<> *get_open_trigger() const { return this->open_trigger_; } | ||||
|   void set_open_sensor(sensor::Sensor *open_sensor) { this->open_sensor_ = open_sensor; } | ||||
|   void set_open_moving_current_threshold(float open_moving_current_threshold) { | ||||
|     this->open_moving_current_threshold_ = open_moving_current_threshold; | ||||
|   } | ||||
|   void set_open_obstacle_current_threshold(float open_obstacle_current_threshold) { | ||||
|     this->open_obstacle_current_threshold_ = open_obstacle_current_threshold; | ||||
|   } | ||||
|   void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; } | ||||
|  | ||||
|   Trigger<> *get_close_trigger() const { return this->close_trigger_; } | ||||
|   void set_close_sensor(sensor::Sensor *close_sensor) { this->close_sensor_ = close_sensor; } | ||||
|   void set_close_moving_current_threshold(float close_moving_current_threshold) { | ||||
|     this->close_moving_current_threshold_ = close_moving_current_threshold; | ||||
|   } | ||||
|   void set_close_obstacle_current_threshold(float close_obstacle_current_threshold) { | ||||
|     this->close_obstacle_current_threshold_ = close_obstacle_current_threshold; | ||||
|   } | ||||
|   void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; } | ||||
|  | ||||
|   void set_max_duration(uint32_t max_duration) { this->max_duration_ = max_duration; } | ||||
|   void set_obstacle_rollback(float obstacle_rollback) { this->obstacle_rollback_ = obstacle_rollback; } | ||||
|  | ||||
|   void set_malfunction_detection(bool malfunction_detection) { this->malfunction_detection_ = malfunction_detection; } | ||||
|   void set_start_sensing_delay(uint32_t start_sensing_delay) { this->start_sensing_delay_ = start_sensing_delay; } | ||||
|  | ||||
|   Trigger<> *get_malfunction_trigger() const { return this->malfunction_trigger_; } | ||||
|  | ||||
|   cover::CoverTraits get_traits() override; | ||||
|  | ||||
|  protected: | ||||
|   void control(const cover::CoverCall &call) override; | ||||
|   void stop_prev_trigger_(); | ||||
|  | ||||
|   bool is_at_target_() const; | ||||
|   bool is_opening_() const; | ||||
|   bool is_opening_blocked_() const; | ||||
|   bool is_closing_() const; | ||||
|   bool is_closing_blocked_() const; | ||||
|   bool is_initial_delay_finished_() const; | ||||
|  | ||||
|   void direction_idle_(float new_position = FLT_MAX); | ||||
|   void start_direction_(cover::CoverOperation dir); | ||||
|  | ||||
|   void recompute_position_(); | ||||
|  | ||||
|   Trigger<> *stop_trigger_{new Trigger<>()}; | ||||
|  | ||||
|   sensor::Sensor *open_sensor_{nullptr}; | ||||
|   Trigger<> *open_trigger_{new Trigger<>()}; | ||||
|   float open_moving_current_threshold_; | ||||
|   float open_obstacle_current_threshold_{FLT_MAX}; | ||||
|   uint32_t open_duration_; | ||||
|  | ||||
|   sensor::Sensor *close_sensor_{nullptr}; | ||||
|   Trigger<> *close_trigger_{new Trigger<>()}; | ||||
|   float close_moving_current_threshold_; | ||||
|   float close_obstacle_current_threshold_{FLT_MAX}; | ||||
|   uint32_t close_duration_; | ||||
|  | ||||
|   uint32_t max_duration_{UINT32_MAX}; | ||||
|   bool malfunction_detection_{true}; | ||||
|   Trigger<> *malfunction_trigger_{new Trigger<>()}; | ||||
|   uint32_t start_sensing_delay_; | ||||
|   float obstacle_rollback_; | ||||
|  | ||||
|   Trigger<> *prev_command_trigger_{nullptr}; | ||||
|   uint32_t last_recompute_time_{0}; | ||||
|   uint32_t start_dir_time_{0}; | ||||
|   uint32_t last_publish_time_{0}; | ||||
|   float target_position_{0}; | ||||
| }; | ||||
|  | ||||
| }  // namespace current_based | ||||
| }  // namespace esphome | ||||
| @@ -426,14 +426,19 @@ sensor: | ||||
|     irq_pin: GPIO16 | ||||
|     voltage: | ||||
|       name: ADE7953 Voltage | ||||
|       id: ade7953_voltage | ||||
|     current_a: | ||||
|       name: ADE7953 Current A | ||||
|       id: ade7953_current_a | ||||
|     current_b: | ||||
|       name: ADE7953 Current B | ||||
|       id: ade7953_current_b | ||||
|     active_power_a: | ||||
|       name: ADE7953 Active Power A | ||||
|       id: ade7953_active_power_a | ||||
|     active_power_b: | ||||
|       name: ADE7953 Active Power B | ||||
|       id: ade7953_active_power_b | ||||
|   - platform: pzem004t | ||||
|     uart_id: uart3 | ||||
|     voltage: | ||||
| @@ -1021,6 +1026,29 @@ cover: | ||||
|     close_action: | ||||
|       - switch.turn_on: gpio_switch2 | ||||
|     close_duration: 4.5min | ||||
|   - platform: current_based | ||||
|     name: "Current Based Cover" | ||||
|     open_sensor: ade7953_current_a | ||||
|     open_moving_current_threshold: 0.5 | ||||
|     open_obstacle_current_threshold: 0.8 | ||||
|     open_duration: 12s | ||||
|     open_action: | ||||
|       - switch.turn_on: gpio_switch1 | ||||
|     close_sensor: ade7953_current_b | ||||
|     close_moving_current_threshold: 0.5 | ||||
|     close_obstacle_current_threshold: 0.8 | ||||
|     close_duration: 10s | ||||
|     close_action: | ||||
|       - switch.turn_on: gpio_switch2 | ||||
|     stop_action: | ||||
|       - switch.turn_off: gpio_switch1 | ||||
|       - switch.turn_off: gpio_switch2 | ||||
|     obstacle_rollback: 30% | ||||
|     start_sensing_delay: 0.8s | ||||
|     malfunction_detection: true | ||||
|     malfunction_action: | ||||
|       then: | ||||
|         - logger.log: "Malfunction Detected" | ||||
|   - platform: template | ||||
|     name: Template Cover with Tilt | ||||
|     tilt_lambda: 'return 0.5;' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user