This commit is contained in:
klaudiusz223 2023-02-04 21:55:43 +01:00
parent edf6c9e5f2
commit 7c6c9344ce
3 changed files with 59 additions and 56 deletions

View file

@ -13,7 +13,9 @@ from esphome.const import (
) )
time_based_tilt_ns = cg.esphome_ns.namespace("time_based_tilt") time_based_tilt_ns = cg.esphome_ns.namespace("time_based_tilt")
TimeBasedTiltCover = time_based_tilt_ns.class_("TimeBasedTiltCover", cover.Cover, cg.Component) TimeBasedTiltCover = time_based_tilt_ns.class_(
"TimeBasedTiltCover", cover.Cover, cg.Component
)
CONF_TILT_OPEN_DURATION = "tilt_open_duration" CONF_TILT_OPEN_DURATION = "tilt_open_duration"
CONF_TILT_CLOSE_DURATION = "tilt_close_duration" CONF_TILT_CLOSE_DURATION = "tilt_close_duration"
@ -32,12 +34,24 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean, cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean,
cv.Optional(CONF_TILT_OPEN_DURATION, default="0ms"): cv.positive_time_period_milliseconds, cv.Optional(
cv.Optional(CONF_TILT_CLOSE_DURATION, default="0ms"): cv.positive_time_period_milliseconds, CONF_TILT_OPEN_DURATION, default="0ms"
cv.Optional(CONF_INTERLOCK_WAIT_TIME, default="0ms"): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional(CONF_RECALIBRATION_TIME, default="0ms"): cv.positive_time_period_milliseconds, cv.Optional(
cv.Optional(CONF_INERTIA_OPEN_TIME, default="0ms"): cv.positive_time_period_milliseconds, CONF_TILT_CLOSE_DURATION, default="0ms"
cv.Optional(CONF_INERTIA_CLOSE_TIME, default="0ms"): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_INTERLOCK_WAIT_TIME, default="0ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_RECALIBRATION_TIME, default="0ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_INERTIA_OPEN_TIME, default="0ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_INERTIA_CLOSE_TIME, default="0ms"
): cv.positive_time_period_milliseconds,
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)

View file

@ -21,8 +21,7 @@ void TimeBasedTiltCover::dump_config() {
ESP_LOGCONFIG(TAG, " Recalibration time: %.3fs", this->recalibration_time_ / 1e3f); ESP_LOGCONFIG(TAG, " Recalibration time: %.3fs", this->recalibration_time_ / 1e3f);
} }
void TimeBasedTiltCover::setup() { void TimeBasedTiltCover::setup() {
if ( this->tilt_close_duration_ == 0 || this->tilt_open_duration_ == 0 ) if ( this->tilt_close_duration_ == 0 || this->tilt_open_duration_ == 0 ) {
{
this->tilt_close_duration_ = 0; this->tilt_close_duration_ = 0;
this->tilt_open_duration_ = 0; this->tilt_open_duration_ = 0;
} }
@ -71,10 +70,9 @@ void TimeBasedTiltCover::loop() {
return; return;
} }
if ( this->fsm_state_ == STATE_STOPPING ) if ( this->fsm_state_ == STATE_STOPPING ) {
{
this->stop_trigger_->trigger(); this->stop_trigger_->trigger();
this->interlockedTime = millis(); this->interlocked_time = millis();
this->interlocked_direction = this->current_operation == COVER_OPERATION_CLOSING ? COVER_OPERATION_OPENING : COVER_OPERATION_CLOSING; this->interlocked_direction = this->current_operation == COVER_OPERATION_CLOSING ? COVER_OPERATION_OPENING : COVER_OPERATION_CLOSING;
this->fsm_state_ = STATE_IDLE; this->fsm_state_ = STATE_IDLE;
this->last_operation_ = this->current_operation; this->last_operation_ = this->current_operation;
@ -83,8 +81,7 @@ void TimeBasedTiltCover::loop() {
return; return;
} }
if ( this->fsm_state_ == STATE_IDLE && (this->target_position_ != TARGET_NONE || this->target_tilt_ != TARGET_NONE) ) if ( this->fsm_state_ == STATE_IDLE && (this->target_position_ != TARGET_NONE || this->target_tilt_ != TARGET_NONE) ) {
{
if (this->target_position_ != TARGET_NONE ) { if (this->target_position_ != TARGET_NONE ) {
this->current_operation = this->compute_direction(this->target_position_,this->position); this->current_operation = this->compute_direction(this->target_position_,this->position);
@ -93,7 +90,7 @@ void TimeBasedTiltCover::loop() {
} }
if ( this->current_operation == this->interlocked_direction if ( this->current_operation == this->interlocked_direction
&& now - this->interlockedTime < this->interlock_wait_time_ ) return; && now - this->interlocked_time < this->interlock_wait_time_ ) return;
Trigger<> *trig = this->current_operation == COVER_OPERATION_CLOSING ? this->close_trigger_ : this->open_trigger_; Trigger<> *trig = this->current_operation == COVER_OPERATION_CLOSING ? this->close_trigger_ : this->open_trigger_;
@ -104,8 +101,7 @@ void TimeBasedTiltCover::loop() {
return; return;
} }
if ( this->fsm_state_ == STATE_MOVING ) if ( this->fsm_state_ == STATE_MOVING ) {
{
auto travel_time = now - this->last_recompute_time_; auto travel_time = now - this->last_recompute_time_;
this->last_recompute_time_ = now; this->last_recompute_time_ = now;
@ -113,8 +109,7 @@ void TimeBasedTiltCover::loop() {
float dir_factor = this->current_operation == COVER_OPERATION_CLOSING ? -1.0 : 1.0 ; float dir_factor = this->current_operation == COVER_OPERATION_CLOSING ? -1.0 : 1.0 ;
auto inertia_time = this->current_operation == COVER_OPERATION_CLOSING ? this->inertia_close_time_ : this->inertia_open_time_ ; auto inertia_time = this->current_operation == COVER_OPERATION_CLOSING ? this->inertia_close_time_ : this->inertia_open_time_ ;
if ( inertia_time > 0 && this->inertia * dir_factor < 0.5f ) // inertia before movement if ( inertia_time > 0 && this->inertia * dir_factor < 0.5f ) { // inertia before movement
{
auto inertia_step = dir_factor * travel_time / inertia_time; auto inertia_step = dir_factor * travel_time / inertia_time;
this->inertia += inertia_step; this->inertia += inertia_step;
auto rest = this->inertia - clamp( this->inertia , -0.5f, 0.5f); auto rest = this->inertia - clamp( this->inertia , -0.5f, 0.5f);
@ -126,20 +121,19 @@ void TimeBasedTiltCover::loop() {
auto tilt_time = this->current_operation == COVER_OPERATION_CLOSING ? this->tilt_close_duration_ : this->tilt_open_duration_; auto tilt_time = this->current_operation == COVER_OPERATION_CLOSING ? this->tilt_close_duration_ : this->tilt_open_duration_;
if ( tilt_time > 0 && ( this->tilt - 0.5f ) * dir_factor < 0.5f ) // tilting before movement if ( tilt_time > 0 && ( this->tilt - 0.5f ) * dir_factor < 0.5f ) { // tilting before movement
{
auto tilt_step = dir_factor * travel_time / tilt_time; auto tilt_step = dir_factor * travel_time / tilt_time;
this->tilt += tilt_step; this->tilt += tilt_step;
auto rest = this->tilt - 0.5f - clamp( this->tilt - 0.5f , -0.5f, 0.5f); auto rest = this->tilt - 0.5f - clamp( this->tilt - 0.5f , -0.5f, 0.5f);
this->tilt = clamp( this->tilt , 0.0f, 1.0f); this->tilt = clamp( this->tilt , 0.0f, 1.0f);
if (this->target_position_ == TARGET_NONE && this->is_at_target_tilt_()){ // only tilting w/o position change if (this->target_position_ == TARGET_NONE && this->is_at_target_tilt_()) { // only tilting w/o position change
this->last_recompute_time_ = now; this->last_recompute_time_ = now;
this->target_tilt_ = TARGET_NONE; this->target_tilt_ = TARGET_NONE;
this->last_publish_time_= now; this->last_publish_time_= now;
if ( this->recalibration_time_ > 0 && if ( this->recalibration_time_ > 0 &&
(((this->position == COVER_CLOSED && (tilt_time == 0 || this->tilt == COVER_CLOSED)) || (((this->position == COVER_CLOSED && (tilt_time == 0 || this->tilt == COVER_CLOSED)) ||
(this->position == COVER_OPEN && (tilt_time == 0 || this->tilt == COVER_OPEN))))) (this->position == COVER_OPEN && (tilt_time == 0 || this->tilt == COVER_OPEN)))))
{ {
this->fsm_state_ = STATE_CALIBRATING; this->fsm_state_ = STATE_CALIBRATING;
@ -151,9 +145,9 @@ void TimeBasedTiltCover::loop() {
return; // only tilting w/o position change so no need to recompute position return; // only tilting w/o position change so no need to recompute position
} }
if ( now - this->last_publish_time_ > (( tilt_time / 5 ) > 1000 ? 1000 : ( tilt_time / 5 )) ){ if ( now - this->last_publish_time_ > (( tilt_time / 5 ) > 1000 ? 1000 : ( tilt_time / 5 )) ) {
this->publish_state(false); this->publish_state(false);
this->last_publish_time_= now; this->last_publish_time_= now;
} }
if ( ! rest ) return; // the movement has not yet actually started if ( ! rest ) return; // the movement has not yet actually started
@ -163,20 +157,19 @@ void TimeBasedTiltCover::loop() {
auto move_time = this->current_operation == COVER_OPERATION_CLOSING ? this->close_duration_ : this->open_duration_ ; auto move_time = this->current_operation == COVER_OPERATION_CLOSING ? this->close_duration_ : this->open_duration_ ;
if ( move_time > 0 && ( this->position - 0.5f ) * dir_factor < 0.5f ) if ( move_time > 0 && ( this->position - 0.5f ) * dir_factor < 0.5f ) {
{
auto move_step = dir_factor * travel_time / move_time; auto move_step = dir_factor * travel_time / move_time;
this->position += move_step; this->position += move_step;
this->position = clamp( this->position , 0.0f, 1.0f); this->position = clamp( this->position , 0.0f, 1.0f);
} }
if (this->is_at_target_position_()){ if (this->is_at_target_position_()) {
this->last_recompute_time_ = now; this->last_recompute_time_ = now;
this->target_position_ = TARGET_NONE; this->target_position_ = TARGET_NONE;
this->last_publish_time_= now; this->last_publish_time_= now;
if ( this->recalibration_time_ > 0 && if ( this->recalibration_time_ > 0 &&
(((this->position == COVER_CLOSED && (tilt_time == 0 || this->tilt == COVER_CLOSED)) || (((this->position == COVER_CLOSED && (tilt_time == 0 || this->tilt == COVER_CLOSED)) ||
(this->position == COVER_OPEN && (tilt_time == 0 || this->tilt == COVER_OPEN))))) (this->position == COVER_OPEN && (tilt_time == 0 || this->tilt == COVER_OPEN)))))
{ {
this->fsm_state_ = STATE_CALIBRATING; this->fsm_state_ = STATE_CALIBRATING;
@ -186,7 +179,7 @@ void TimeBasedTiltCover::loop() {
} }
} }
if ( now - this->last_publish_time_ > 1000 ){ if ( now - this->last_publish_time_ > 1000 ) {
this->publish_state(false); this->publish_state(false);
this->last_publish_time_= now; this->last_publish_time_= now;
} }
@ -207,7 +200,7 @@ void TimeBasedTiltCover::control(const CoverCall &call) {
if (call.get_stop()) { if (call.get_stop()) {
this->target_position_ = TARGET_NONE; this->target_position_ = TARGET_NONE;
this->target_tilt_= TARGET_NONE; this->target_tilt_= TARGET_NONE;
if (this->fsm_state_ == STATE_MOVING){ if (this->fsm_state_ == STATE_MOVING) {
this->fsm_state_ = STATE_STOPPING; this->fsm_state_ = STATE_STOPPING;
} }
return; return;
@ -224,11 +217,11 @@ void TimeBasedTiltCover::control(const CoverCall &call) {
this->target_position_ = pos; this->target_position_ = pos;
this->target_tilt_ = til; this->target_tilt_ = til;
if (this->fsm_state_ == STATE_MOVING){ //TODO: check direction also on tilting if (this->fsm_state_ == STATE_MOVING) { //TODO: check direction also on tilting
auto direction = COVER_OPERATION_IDLE; auto direction = COVER_OPERATION_IDLE;
if ( this->target_position_ != TARGET_NONE && this->target_position_ != this->position) { if ( this->target_position_ != TARGET_NONE && this->target_position_ != this->position) {
direction = this->compute_direction(this->target_position_ , this->position ); direction = this->compute_direction(this->target_position_ , this->position );
} else if ( this->target_tilt_ != TARGET_NONE && this->target_tilt_ != this->tilt){ } else if ( this->target_tilt_ != TARGET_NONE && this->target_tilt_ != this->tilt) {
direction = this->compute_direction( this->target_tilt_ , this->tilt ); direction = this->compute_direction( this->target_tilt_ , this->tilt );
} }
@ -240,50 +233,47 @@ void TimeBasedTiltCover::control(const CoverCall &call) {
} else if (call.get_position().has_value()) { } else if (call.get_position().has_value()) {
auto pos = *call.get_position(); auto pos = *call.get_position();
if ( pos == COVER_CLOSED && this->position == COVER_CLOSED && this->tilt != COVER_CLOSED ){ if ( pos == COVER_CLOSED && this->position == COVER_CLOSED && this->tilt != COVER_CLOSED ) {
pos = TARGET_NONE; pos = TARGET_NONE;
this->target_tilt_ = COVER_CLOSED; this->target_tilt_ = COVER_CLOSED;
} else if ( pos == COVER_OPEN && this->position == COVER_OPEN && this->tilt != COVER_OPEN ){ } else if ( pos == COVER_OPEN && this->position == COVER_OPEN && this->tilt != COVER_OPEN ) {
pos = TARGET_NONE; pos = TARGET_NONE;
this->target_tilt_ = COVER_OPEN; this->target_tilt_ = COVER_OPEN;
} else if ( this->round_position(pos) == this->round_position(this->position) ){ } else if ( this->round_position(pos) == this->round_position(this->position) ) {
pos = TARGET_NONE; pos = TARGET_NONE;
} }
this->target_position_ = pos; this->target_position_ = pos;
if (this->fsm_state_ == STATE_MOVING){ if (this->fsm_state_ == STATE_MOVING) {
auto direction = COVER_OPERATION_IDLE; auto direction = COVER_OPERATION_IDLE;
if ( this->target_position_ != TARGET_NONE && this->target_position_ != this->position) { if ( this->target_position_ != TARGET_NONE && this->target_position_ != this->position) {
direction = this->compute_direction(this->target_position_ , this->position ); direction = this->compute_direction(this->target_position_ , this->position );
this->target_tilt_ = TARGET_NONE; // unset previous target tilt this->target_tilt_ = TARGET_NONE; // unset previous target tilt
} else if ( this->target_tilt_ != TARGET_NONE && this->target_tilt_ != this->tilt){ } else if ( this->target_tilt_ != TARGET_NONE && this->target_tilt_ != this->tilt) {
direction = this->compute_direction( this->target_tilt_ , this->tilt ); direction = this->compute_direction( this->target_tilt_ , this->tilt );
} }
if (direction != this->current_operation ) if (direction != this->current_operation ) {
{
this->fsm_state_ = STATE_STOPPING; this->fsm_state_ = STATE_STOPPING;
} }
} }
} else if (call.get_tilt().has_value()) { } else if (call.get_tilt().has_value()) {
auto til = *call.get_tilt(); auto til = *call.get_tilt();
if ( this->round_position(til) == this->round_position(this->tilt) ) if ( this->round_position(til) == this->round_position(this->tilt) ) {
{
til = TARGET_NONE; til = TARGET_NONE;
} }
this->target_tilt_ = til; this->target_tilt_ = til;
if (this->fsm_state_ == STATE_MOVING){ if (this->fsm_state_ == STATE_MOVING) {
auto direction = COVER_OPERATION_IDLE; auto direction = COVER_OPERATION_IDLE;
if ( this->target_tilt_ != TARGET_NONE && this->target_tilt_ != this->tilt){ if ( this->target_tilt_ != TARGET_NONE && this->target_tilt_ != this->tilt) {
direction = this->compute_direction( this->target_tilt_ , this->tilt ); direction = this->compute_direction( this->target_tilt_ , this->tilt );
this->target_position_ = TARGET_NONE; this->target_position_ = TARGET_NONE;
} }
if (direction != this->current_operation ) if (direction != this->current_operation ) {
{
this->fsm_state_ = STATE_STOPPING; this->fsm_state_ = STATE_STOPPING;
} }
} }

View file

@ -25,7 +25,7 @@ class TimeBasedTiltCover : public cover::Cover, public Component {
void set_recalibration_time(uint32_t recalibration_time) { this->recalibration_time_ = recalibration_time; } void set_recalibration_time(uint32_t recalibration_time) { this->recalibration_time_ = recalibration_time; }
void set_inertia_open_time(uint32_t inertia_time) { this->inertia_open_time_ = inertia_time; } void set_inertia_open_time(uint32_t inertia_time) { this->inertia_open_time_ = inertia_time; }
void set_inertia_close_time(uint32_t inertia_time) { this->inertia_close_time_ = inertia_time; } void set_inertia_close_time(uint32_t inertia_time) { this->inertia_close_time_ = inertia_time; }
cover::CoverOperation compute_direction(float target, float current){ return target < current ? cover::COVER_OPERATION_CLOSING : cover::COVER_OPERATION_OPENING ; }; cover::CoverOperation compute_direction(float target, float current) { return target < current ? cover::COVER_OPERATION_CLOSING : cover::COVER_OPERATION_OPENING ; };
float round_position(float pos) { return round(100 * pos)/100; }; float round_position(float pos) { return round(100 * pos)/100; };
cover::CoverTraits get_traits() override; cover::CoverTraits get_traits() override;
void set_assumed_state(bool value) { this->assumed_state_ = value; } void set_assumed_state(bool value) { this->assumed_state_ = value; }
@ -34,7 +34,7 @@ class TimeBasedTiltCover : public cover::Cover, public Component {
void control(const cover::CoverCall &call) override; void control(const cover::CoverCall &call) override;
bool is_at_target_position_() const; bool is_at_target_position_() const;
bool is_at_target_tilt_() const; bool is_at_target_tilt_() const;
Trigger<> *open_trigger_{new Trigger<>()}; Trigger<> *open_trigger_{new Trigger<>()};
Trigger<> *close_trigger_{new Trigger<>()}; Trigger<> *close_trigger_{new Trigger<>()};
Trigger<> *stop_trigger_{new Trigger<>()}; Trigger<> *stop_trigger_{new Trigger<>()};
@ -52,13 +52,12 @@ class TimeBasedTiltCover : public cover::Cover, public Component {
const float TARGET_NONE{-1}; const float TARGET_NONE{-1};
enum State : uint8_t { enum State : uint8_t {
STATE_IDLE, STATE_IDLE,
STATE_MOVING, STATE_MOVING,
STATE_STOPPING, STATE_STOPPING,
STATE_CALIBRATING STATE_CALIBRATING
}; };
Trigger<> *prev_command_trigger_{nullptr};
uint32_t last_recompute_time_{0}; uint32_t last_recompute_time_{0};
uint32_t last_publish_time_{0}; uint32_t last_publish_time_{0};
float target_position_{TARGET_NONE}; float target_position_{TARGET_NONE};
@ -69,7 +68,7 @@ class TimeBasedTiltCover : public cover::Cover, public Component {
cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING}; cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING};
State fsm_state_{STATE_IDLE}; State fsm_state_{STATE_IDLE};
cover::CoverOperation interlocked_direction{cover::COVER_OPERATION_IDLE}; cover::CoverOperation interlocked_direction{cover::COVER_OPERATION_IDLE};
uint32_t interlockedTime{0}; uint32_t interlocked_time{0};
}; };
} // namespace time_based_tilt } // namespace time_based_tilt