mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 21:44:55 +01:00
Add cover toggle support (#1809)
* Add cover toggle support Step through open/stop/close/stop sequence with every toggle * Move the cover toggle logic to perform() * Add clang-tidy CI suggestion * Implement cover toggle action as cover trait * Handle toggle correctly if cover fully closed on POR * Fix CI finding * Add deprecated warning * Don't add already deprecated interface Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> * Don't add already deprecated interface Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> * Don't add already deprecated interface Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> Co-authored-by: Mueller, Daniel <daniel.mueller@karlstorz.com> Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
parent
e30f17f64f
commit
2eb5f89d82
8 changed files with 70 additions and 1 deletions
|
@ -59,6 +59,7 @@ validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True)
|
||||||
OpenAction = cover_ns.class_("OpenAction", automation.Action)
|
OpenAction = cover_ns.class_("OpenAction", automation.Action)
|
||||||
CloseAction = cover_ns.class_("CloseAction", automation.Action)
|
CloseAction = cover_ns.class_("CloseAction", automation.Action)
|
||||||
StopAction = cover_ns.class_("StopAction", automation.Action)
|
StopAction = cover_ns.class_("StopAction", automation.Action)
|
||||||
|
ToggleAction = cover_ns.class_("ToggleAction", automation.Action)
|
||||||
ControlAction = cover_ns.class_("ControlAction", automation.Action)
|
ControlAction = cover_ns.class_("ControlAction", automation.Action)
|
||||||
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
|
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
|
||||||
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
|
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
|
||||||
|
@ -119,6 +120,12 @@ async def cover_stop_to_code(config, action_id, template_arg, args):
|
||||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA)
|
||||||
|
def cover_toggle_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = yield cg.get_variable(config[CONF_ID])
|
||||||
|
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
|
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.use_id(Cover),
|
cv.Required(CONF_ID): cv.use_id(Cover),
|
||||||
|
|
|
@ -37,6 +37,16 @@ template<typename... Ts> class StopAction : public Action<Ts...> {
|
||||||
Cover *cover_;
|
Cover *cover_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class ToggleAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit ToggleAction(Cover *cover) : cover_(cover) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->cover_->make_call().set_command_toggle().perform(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Cover *cover_;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename... Ts> class ControlAction : public Action<Ts...> {
|
template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit ControlAction(Cover *cover) : cover_(cover) {}
|
explicit ControlAction(Cover *cover) : cover_(cover) {}
|
||||||
|
|
|
@ -43,6 +43,8 @@ CoverCall &CoverCall::set_command(const char *command) {
|
||||||
this->set_command_close();
|
this->set_command_close();
|
||||||
} else if (strcasecmp(command, "STOP") == 0) {
|
} else if (strcasecmp(command, "STOP") == 0) {
|
||||||
this->set_command_stop();
|
this->set_command_stop();
|
||||||
|
} else if (strcasecmp(command, "TOGGLE") == 0) {
|
||||||
|
this->set_command_toggle();
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
||||||
}
|
}
|
||||||
|
@ -60,6 +62,10 @@ CoverCall &CoverCall::set_command_stop() {
|
||||||
this->stop_ = true;
|
this->stop_ = true;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
CoverCall &CoverCall::set_command_toggle() {
|
||||||
|
this->toggle_ = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
CoverCall &CoverCall::set_position(float position) {
|
CoverCall &CoverCall::set_position(float position) {
|
||||||
this->position_ = position;
|
this->position_ = position;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -85,10 +91,14 @@ void CoverCall::perform() {
|
||||||
if (this->tilt_.has_value()) {
|
if (this->tilt_.has_value()) {
|
||||||
ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f);
|
ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f);
|
||||||
}
|
}
|
||||||
|
if (this->toggle_.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Command: TOGGLE");
|
||||||
|
}
|
||||||
this->parent_->control(*this);
|
this->parent_->control(*this);
|
||||||
}
|
}
|
||||||
const optional<float> &CoverCall::get_position() const { return this->position_; }
|
const optional<float> &CoverCall::get_position() const { return this->position_; }
|
||||||
const optional<float> &CoverCall::get_tilt() const { return this->tilt_; }
|
const optional<float> &CoverCall::get_tilt() const { return this->tilt_; }
|
||||||
|
const optional<bool> &CoverCall::get_toggle() const { return this->toggle_; }
|
||||||
void CoverCall::validate_() {
|
void CoverCall::validate_() {
|
||||||
auto traits = this->parent_->get_traits();
|
auto traits = this->parent_->get_traits();
|
||||||
if (this->position_.has_value()) {
|
if (this->position_.has_value()) {
|
||||||
|
@ -111,6 +121,12 @@ void CoverCall::validate_() {
|
||||||
this->tilt_ = clamp(tilt, 0.0f, 1.0f);
|
this->tilt_ = clamp(tilt, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this->toggle_.has_value()) {
|
||||||
|
if (!traits.get_supports_toggle()) {
|
||||||
|
ESP_LOGW(TAG, "'%s' - This cover device does not support toggle!", this->parent_->get_name().c_str());
|
||||||
|
this->toggle_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this->stop_) {
|
if (this->stop_) {
|
||||||
if (this->position_.has_value()) {
|
if (this->position_.has_value()) {
|
||||||
ESP_LOGW(TAG, "Cannot set position when stopping a cover!");
|
ESP_LOGW(TAG, "Cannot set position when stopping a cover!");
|
||||||
|
@ -120,6 +136,10 @@ void CoverCall::validate_() {
|
||||||
ESP_LOGW(TAG, "Cannot set tilt when stopping a cover!");
|
ESP_LOGW(TAG, "Cannot set tilt when stopping a cover!");
|
||||||
this->tilt_.reset();
|
this->tilt_.reset();
|
||||||
}
|
}
|
||||||
|
if (this->toggle_.has_value()) {
|
||||||
|
ESP_LOGW(TAG, "Cannot set toggle when stopping a cover!");
|
||||||
|
this->toggle_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CoverCall &CoverCall::set_stop(bool stop) {
|
CoverCall &CoverCall::set_stop(bool stop) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ class CoverCall {
|
||||||
public:
|
public:
|
||||||
CoverCall(Cover *parent);
|
CoverCall(Cover *parent);
|
||||||
|
|
||||||
/// Set the command as a string, "STOP", "OPEN", "CLOSE".
|
/// Set the command as a string, "STOP", "OPEN", "CLOSE", "TOGGLE".
|
||||||
CoverCall &set_command(const char *command);
|
CoverCall &set_command(const char *command);
|
||||||
/// Set the command to open the cover.
|
/// Set the command to open the cover.
|
||||||
CoverCall &set_command_open();
|
CoverCall &set_command_open();
|
||||||
|
@ -37,6 +37,8 @@ class CoverCall {
|
||||||
CoverCall &set_command_close();
|
CoverCall &set_command_close();
|
||||||
/// Set the command to stop the cover.
|
/// Set the command to stop the cover.
|
||||||
CoverCall &set_command_stop();
|
CoverCall &set_command_stop();
|
||||||
|
/// Set the command to toggle the cover.
|
||||||
|
CoverCall &set_command_toggle();
|
||||||
/// Set the call to a certain target position.
|
/// Set the call to a certain target position.
|
||||||
CoverCall &set_position(float position);
|
CoverCall &set_position(float position);
|
||||||
/// Set the call to a certain target tilt.
|
/// Set the call to a certain target tilt.
|
||||||
|
@ -50,6 +52,7 @@ class CoverCall {
|
||||||
const optional<float> &get_position() const;
|
const optional<float> &get_position() const;
|
||||||
bool get_stop() const;
|
bool get_stop() const;
|
||||||
const optional<float> &get_tilt() const;
|
const optional<float> &get_tilt() const;
|
||||||
|
const optional<bool> &get_toggle() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void validate_();
|
void validate_();
|
||||||
|
@ -58,6 +61,7 @@ class CoverCall {
|
||||||
bool stop_{false};
|
bool stop_{false};
|
||||||
optional<float> position_{};
|
optional<float> position_{};
|
||||||
optional<float> tilt_{};
|
optional<float> tilt_{};
|
||||||
|
optional<bool> toggle_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Struct used to store the restored state of a cover
|
/// Struct used to store the restored state of a cover
|
||||||
|
|
|
@ -13,11 +13,14 @@ class CoverTraits {
|
||||||
void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; }
|
void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; }
|
||||||
bool get_supports_tilt() const { return this->supports_tilt_; }
|
bool get_supports_tilt() const { return this->supports_tilt_; }
|
||||||
void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
|
void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
|
||||||
|
bool get_supports_toggle() const { return this->supports_toggle_; }
|
||||||
|
void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool is_assumed_state_{false};
|
bool is_assumed_state_{false};
|
||||||
bool supports_position_{false};
|
bool supports_position_{false};
|
||||||
bool supports_tilt_{false};
|
bool supports_tilt_{false};
|
||||||
|
bool supports_toggle_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cover
|
} // namespace cover
|
||||||
|
|
|
@ -52,6 +52,7 @@ float TimeBasedCover::get_setup_priority() const { return setup_priority::DATA;
|
||||||
CoverTraits TimeBasedCover::get_traits() {
|
CoverTraits TimeBasedCover::get_traits() {
|
||||||
auto traits = CoverTraits();
|
auto traits = CoverTraits();
|
||||||
traits.set_supports_position(true);
|
traits.set_supports_position(true);
|
||||||
|
traits.set_supports_toggle(true);
|
||||||
traits.set_is_assumed_state(this->assumed_state_);
|
traits.set_is_assumed_state(this->assumed_state_);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,20 @@ void TimeBasedCover::control(const CoverCall &call) {
|
||||||
this->start_direction_(COVER_OPERATION_IDLE);
|
this->start_direction_(COVER_OPERATION_IDLE);
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
}
|
}
|
||||||
|
if (call.get_toggle().has_value()) {
|
||||||
|
if (this->current_operation != COVER_OPERATION_IDLE) {
|
||||||
|
this->start_direction_(COVER_OPERATION_IDLE);
|
||||||
|
this->publish_state();
|
||||||
|
} else {
|
||||||
|
if (this->position == COVER_CLOSED || this->last_operation_ == COVER_OPERATION_CLOSING) {
|
||||||
|
this->target_position_ = COVER_OPEN;
|
||||||
|
this->start_direction_(COVER_OPERATION_OPENING);
|
||||||
|
} else {
|
||||||
|
this->target_position_ = COVER_CLOSED;
|
||||||
|
this->start_direction_(COVER_OPERATION_CLOSING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (call.get_position().has_value()) {
|
if (call.get_position().has_value()) {
|
||||||
auto pos = *call.get_position();
|
auto pos = *call.get_position();
|
||||||
if (pos == this->position) {
|
if (pos == this->position) {
|
||||||
|
@ -105,9 +120,11 @@ void TimeBasedCover::start_direction_(CoverOperation dir) {
|
||||||
trig = this->stop_trigger_;
|
trig = this->stop_trigger_;
|
||||||
break;
|
break;
|
||||||
case COVER_OPERATION_OPENING:
|
case COVER_OPERATION_OPENING:
|
||||||
|
this->last_operation_ = dir;
|
||||||
trig = this->open_trigger_;
|
trig = this->open_trigger_;
|
||||||
break;
|
break;
|
||||||
case COVER_OPERATION_CLOSING:
|
case COVER_OPERATION_CLOSING:
|
||||||
|
this->last_operation_ = dir;
|
||||||
trig = this->close_trigger_;
|
trig = this->close_trigger_;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -45,6 +45,7 @@ class TimeBasedCover : public cover::Cover, public Component {
|
||||||
float target_position_{0};
|
float target_position_{0};
|
||||||
bool has_built_in_endstop_{false};
|
bool has_built_in_endstop_{false};
|
||||||
bool assumed_state_{false};
|
bool assumed_state_{false};
|
||||||
|
cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace time_based
|
} // namespace time_based
|
||||||
|
|
|
@ -729,6 +729,12 @@ binary_sensor:
|
||||||
id: r0_sensor
|
id: r0_sensor
|
||||||
name: 'R0 Sensor'
|
name: 'R0 Sensor'
|
||||||
component_name: page0.r0
|
component_name: page0.r0
|
||||||
|
- platform: template
|
||||||
|
id: 'cover_toggle'
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- cover.toggle: time_based_cover
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
- id: my_global_string
|
- id: my_global_string
|
||||||
type: std::string
|
type: std::string
|
||||||
|
@ -1018,6 +1024,7 @@ cover:
|
||||||
max_duration: 10min
|
max_duration: 10min
|
||||||
- platform: time_based
|
- platform: time_based
|
||||||
name: Time Based Cover
|
name: Time Based Cover
|
||||||
|
id: time_based_cover
|
||||||
stop_action:
|
stop_action:
|
||||||
- switch.turn_on: gpio_switch1
|
- switch.turn_on: gpio_switch1
|
||||||
open_action:
|
open_action:
|
||||||
|
|
Loading…
Reference in a new issue