Refactored using templates

This commit is contained in:
clydebarrow
2026-01-23 16:42:32 +11:00
parent bccfe9eead
commit 8abb783b64
2 changed files with 77 additions and 132 deletions

View File

@@ -1,3 +1,5 @@
import logging
from esphome import automation
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
@@ -9,6 +11,7 @@ from esphome.const import (
CONF_ICON,
CONF_ID,
CONF_MQTT_ID,
CONF_ON_IDLE,
CONF_ON_OPEN,
CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC,
@@ -53,6 +56,8 @@ DEVICE_CLASSES = [
DEVICE_CLASS_WINDOW,
]
_LOGGER = logging.getLogger(__name__)
cover_ns = cg.esphome_ns.namespace("cover")
Cover = cover_ns.class_("Cover", cg.EntityBase)
@@ -81,25 +86,28 @@ StopAction = cover_ns.class_("StopAction", automation.Action)
ToggleAction = cover_ns.class_("ToggleAction", automation.Action)
ControlAction = cover_ns.class_("ControlAction", automation.Action)
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
# Triggers
CoverOpenTrigger = cover_ns.class_("CoverOpenTrigger", automation.Trigger.template())
CoverOpenedTrigger = cover_ns.class_("CoverOpenedTrigger", automation.Trigger.template())
CoverClosedTrigger = cover_ns.class_(
"CoverClosedTrigger", automation.Trigger.template()
CoverIsCondition = cover_ns.class_("CoverIsCondition", Condition)
CoverPositionTrigger = cover_ns.class_(
"CoverPositionTrigger", automation.Trigger.template()
)
CoverOpeningTrigger = cover_ns.class_("CoverOpeningTrigger", automation.Trigger.template())
CoverClosingTrigger = cover_ns.class_("CoverClosingTrigger", automation.Trigger.template())
CoverIdleTrigger = cover_ns.class_("CoverIdleTrigger", automation.Trigger.template())
CoverTrigger = cover_ns.class_("CoverTrigger", automation.Trigger.template())
# Cover-specific constants
CONF_ON_CLOSED = "on_closed"
CONF_ON_OPENED = "on_opened"
CONF_ON_OPENING = "on_opening"
CONF_ON_CLOSING = "on_closing"
CONF_ON_IDLE = "on_idle"
OPERATIONS = (
CONF_ON_CLOSING,
CONF_ON_OPENING,
CONF_ON_IDLE,
)
def get_operation_from_conf_(conf: str) -> CoverOperation:
return getattr(CoverOperation, "COVER_OPERATION_" + conf.split("_")[1].upper())
_COVER_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
@@ -120,36 +128,38 @@ _COVER_SCHEMA = (
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
# Deprecated trigger
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
CoverPositionTrigger.template(1.0)
),
}
),
cv.Optional(CONF_ON_OPENED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenedTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
CoverPositionTrigger.template(1.0)
),
}
),
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
}
),
cv.Optional(CONF_ON_OPENING): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpeningTrigger),
}
),
cv.Optional(CONF_ON_CLOSING): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosingTrigger),
}
),
cv.Optional(CONF_ON_IDLE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverIdleTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
CoverPositionTrigger.template(0.0)
),
}
),
**{
cv.Optional(conf): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
CoverTrigger.template(get_operation_from_conf_(conf))
),
}
)
for conf in OPERATIONS
},
}
)
)
@@ -186,24 +196,23 @@ async def setup_cover_core_(var, config):
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class))
for conf in config.get(CONF_ON_OPEN, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_OPENED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLOSED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_OPENING, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLOSING, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_IDLE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if on_opens := config.get(CONF_ON_OPEN):
_LOGGER.warning(
"The 'on_open' trigger for covers is deprecated and will be removed in a future release. Please use 'on_opened' instead."
)
for conf in on_opens:
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for on_op in OPERATIONS:
if triggers := config.get(on_op):
for conf in triggers:
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for on_state in [CONF_ON_OPENED, CONF_ON_CLOSED]:
if triggers := config.get(on_state):
for conf in triggers:
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
mqtt_ = cg.new_Pvariable(mqtt_id, var)

View File

@@ -5,7 +5,6 @@
#include "cover.h"
namespace esphome::cover {
template<typename... Ts> class OpenAction : public Action<Ts...> {
public:
explicit OpenAction(Cover *cover) : cover_(cover) {}
@@ -72,6 +71,7 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
public:
CoverPublishAction(Cover *cover) : cover_(cover) {}
TEMPLATABLE_VALUE(float, position)
TEMPLATABLE_VALUE(float, tilt)
TEMPLATABLE_VALUE(CoverOperation, current_operation)
@@ -90,66 +90,39 @@ template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
Cover *cover_;
};
template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
template<float POS, typename... Ts> class CoverIsCondition : public Condition<Ts...> {
public:
CoverIsOpenCondition(Cover *cover) : cover_(cover) {}
bool check(const Ts &...x) override { return this->cover_->is_fully_open(); }
CoverIsCondition(Cover *cover) : cover_(cover) {}
bool check(const Ts &...x) override { return this->cover_->position == POS; }
protected:
Cover *cover_;
};
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
template<float POS> class CoverPositionTrigger : public Trigger<> {
public:
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
bool check(const Ts &...x) override { return this->cover_->is_fully_closed(); }
CoverPositionTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->position != this->last_position_) {
this->last_position_ = a_cover->position;
if (a_cover->position == POS)
this->trigger();
}
});
}
protected:
Cover *cover_;
float last_position_{NAN};
};
class CoverOpenTrigger : public Trigger<> {
template<CoverOperation OP> class CoverTrigger : public Trigger<> {
public:
CoverOpenTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_open()) {
this->trigger();
}
});
}
};
// Separate CoverOpenedTrigger class for improved naming clarity.
// Both on_open and on_opened are supported for backward compatibility.
class CoverOpenedTrigger : public Trigger<> {
public:
CoverOpenedTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_open()) {
this->trigger();
}
});
}
};
class CoverClosedTrigger : public Trigger<> {
public:
CoverClosedTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_closed()) {
this->trigger();
}
});
}
};
class CoverOpeningTrigger : public Trigger<> {
public:
CoverOpeningTrigger(Cover *a_cover) {
CoverTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
auto current_op = a_cover->current_operation;
if (current_op == COVER_OPERATION_OPENING) {
if (!this->last_operation_.has_value() || this->last_operation_.value() != COVER_OPERATION_OPENING) {
if (current_op == OP) {
if (!this->last_operation_.has_value() || this->last_operation_.value() != OP) {
this->trigger();
}
}
@@ -160,41 +133,4 @@ class CoverOpeningTrigger : public Trigger<> {
protected:
optional<CoverOperation> last_operation_{};
};
class CoverClosingTrigger : public Trigger<> {
public:
CoverClosingTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
auto current_op = a_cover->current_operation;
if (current_op == COVER_OPERATION_CLOSING) {
if (!this->last_operation_.has_value() || this->last_operation_.value() != COVER_OPERATION_CLOSING) {
this->trigger();
}
}
this->last_operation_ = current_op;
});
}
protected:
optional<CoverOperation> last_operation_{};
};
class CoverIdleTrigger : public Trigger<> {
public:
CoverIdleTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
auto current_op = a_cover->current_operation;
if (current_op == COVER_OPERATION_IDLE) {
if (this->last_operation_.has_value() && this->last_operation_.value() != COVER_OPERATION_IDLE) {
this->trigger();
}
}
this->last_operation_ = current_op;
});
}
protected:
optional<CoverOperation> last_operation_{};
};
} // namespace esphome::cover