From 464d9491813f4f7614570dac28a4cb0c09546bef Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 26 Oct 2025 14:55:10 -0700 Subject: [PATCH] wip --- esphome/components/template/cover/__init__.py | 18 ++++++-- .../template/cover/template_cover.cpp | 16 +++---- esphome/components/template/lock/__init__.py | 3 ++ .../template/lock/template_lock.cpp | 30 ++++--------- .../components/template/lock/template_lock.h | 44 ++++++++++++++----- 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/esphome/components/template/cover/__init__.py b/esphome/components/template/cover/__init__.py index a4fb0b7021..fff016bb9f 100644 --- a/esphome/components/template/cover/__init__.py +++ b/esphome/components/template/cover/__init__.py @@ -23,6 +23,9 @@ from esphome.const import ( from .. import template_ns TemplateCover = template_ns.class_("TemplateCover", cover.Cover, cg.Component) +StatelessTemplateCover = template_ns.class_( + "StatelessTemplateCover", cover.Cover, cg.Component +) TemplateCoverRestoreMode = template_ns.enum("TemplateCoverRestoreMode") RESTORE_MODES = { @@ -63,13 +66,22 @@ CONFIG_SCHEMA = ( async def to_code(config): - var = await cover.new_cover(config) - await cg.register_component(var, config) if CONF_LAMBDA in config: + # Use new_lambda_pvariable to create either TemplateCover or StatelessTemplateCover template_ = await cg.process_lambda( config[CONF_LAMBDA], [], return_type=cg.optional.template(float) ) - cg.add(var.set_state_lambda(template_)) + var = automation.new_lambda_pvariable( + config[CONF_ID], template_, StatelessTemplateCover + ) + # Manually register as cover since we didn't use new_cover + await cover.register_cover(var, config) + await cg.register_component(var, config) + else: + # No state lambda - just create the base template cover + var = await cover.new_cover(config) + await cg.register_component(var, config) + if CONF_OPEN_ACTION in config: await automation.build_automation( var.get_open_trigger(), [], config[CONF_OPEN_ACTION] diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index 09e57a2151..fd1c3cab31 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -76,7 +76,8 @@ template void TemplateCoverBase: this->publish_state(); } -CoverTraits TemplateCover::get_traits() { + +template CoverTraits TemplateCoverBase::get_traits() { auto traits = CoverTraits(); traits.set_is_assumed_state(this->assumed_state_); traits.set_supports_stop(this->has_stop_); @@ -85,19 +86,16 @@ CoverTraits TemplateCover::get_traits() { traits.set_supports_tilt(this->has_tilt_); return traits; } -Trigger *TemplateCover::get_position_trigger() const { return this->position_trigger_; } -Trigger *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; } -void TemplateCover::set_tilt_lambda(std::function()> &&tilt_f) { this->tilt_f_ = tilt_f; } -void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; } -void TemplateCover::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; } -void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; } -void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; } -void TemplateCover::stop_prev_trigger_() { + +template void TemplateCoverBase::stop_prev_trigger_() { if (this->prev_command_trigger_ != nullptr) { this->prev_command_trigger_->stop_action(); this->prev_command_trigger_ = nullptr; } } +template class TemplateCoverBase()>, std::function()>>; +template class TemplateCoverBase (*)(), optional (*)()>; + } // namespace template_ } // namespace esphome diff --git a/esphome/components/template/lock/__init__.py b/esphome/components/template/lock/__init__.py index 4c74a521fa..dc78f0241e 100644 --- a/esphome/components/template/lock/__init__.py +++ b/esphome/components/template/lock/__init__.py @@ -16,6 +16,9 @@ from esphome.const import ( from .. import template_ns TemplateLock = template_ns.class_("TemplateLock", lock.Lock, cg.Component) +StatelessTemplateLock = template_ns.class_( + "StatelessTemplateLock", lock.Lock, cg.Component +) TemplateLockPublishAction = template_ns.class_( "TemplateLockPublishAction", diff --git a/esphome/components/template/lock/template_lock.cpp b/esphome/components/template/lock/template_lock.cpp index 87ba1046eb..67bdee1c83 100644 --- a/esphome/components/template/lock/template_lock.cpp +++ b/esphome/components/template/lock/template_lock.cpp @@ -8,19 +8,8 @@ using namespace esphome::lock; static const char *const TAG = "template.lock"; -TemplateLock::TemplateLock() - : lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {} - -void TemplateLock::loop() { - if (!this->f_.has_value()) - return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - this->publish_state(*val); -} -void TemplateLock::control(const lock::LockCall &call) { +// Template instantiations +template void TemplateLockBase::control(const lock::LockCall &call) { if (this->prev_trigger_ != nullptr) { this->prev_trigger_->stop_action(); } @@ -37,23 +26,22 @@ void TemplateLock::control(const lock::LockCall &call) { if (this->optimistic_) this->publish_state(state); } -void TemplateLock::open_latch() { + +template void TemplateLockBase::open_latch() { if (this->prev_trigger_ != nullptr) { this->prev_trigger_->stop_action(); } this->prev_trigger_ = this->open_trigger_; this->open_trigger_->trigger(); } -void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } -void TemplateLock::set_state_lambda(std::function()> &&f) { this->f_ = f; } -float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; } -Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; } -Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; } -Trigger<> *TemplateLock::get_open_trigger() const { return this->open_trigger_; } -void TemplateLock::dump_config() { + +template void TemplateLockBase::dump_config() { LOG_LOCK("", "Template Lock", this); ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); } +template class TemplateLockBase()>>; +template class TemplateLockBase (*)()>; + } // namespace template_ } // namespace esphome diff --git a/esphome/components/template/lock/template_lock.h b/esphome/components/template/lock/template_lock.h index 4f798eca81..cd0dbd0885 100644 --- a/esphome/components/template/lock/template_lock.h +++ b/esphome/components/template/lock/template_lock.h @@ -7,26 +7,35 @@ namespace esphome { namespace template_ { -class TemplateLock : public lock::Lock, public Component { +template class TemplateLockBase : public lock::Lock, public Component { public: - TemplateLock(); + TemplateLockBase() + : lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {} + + void loop() override { + if (!this->f_.has_value()) + return; + auto val = (*this->f_)(); + if (!val.has_value()) + return; + + this->publish_state(*val); + } void dump_config() override; - void set_state_lambda(std::function()> &&f); - Trigger<> *get_lock_trigger() const; - Trigger<> *get_unlock_trigger() const; - Trigger<> *get_open_trigger() const; - void set_optimistic(bool optimistic); - void loop() override; + Trigger<> *get_lock_trigger() const { return this->lock_trigger_; } + Trigger<> *get_unlock_trigger() const { return this->unlock_trigger_; } + Trigger<> *get_open_trigger() const { return this->open_trigger_; } + void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } - float get_setup_priority() const override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } protected: void control(const lock::LockCall &call) override; void open_latch() override; - optional()>> f_; + optional f_; bool optimistic_{false}; Trigger<> *lock_trigger_; Trigger<> *unlock_trigger_; @@ -34,5 +43,20 @@ class TemplateLock : public lock::Lock, public Component { Trigger<> *prev_trigger_{nullptr}; }; +class TemplateLock : public TemplateLockBase()>> { + public: + void set_state_lambda(std::function()> &&f) { this->f_ = f; } +}; + +/** Optimized template lock for stateless lambdas (no capture). + * + * Uses function pointers instead of std::function to reduce memory overhead. + * Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function) per lambda. + */ +class StatelessTemplateLock : public TemplateLockBase (*)()> { + public: + explicit StatelessTemplateLock(optional (*f)()) { this->f_ = f; } +}; + } // namespace template_ } // namespace esphome