From 130a01fd0afeb0f2568599d1691761103a2c3cb4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Mar 2026 21:35:30 -1000 Subject: [PATCH 1/2] [core] Use compile-time HasElse parameter in IfAction to save 8 bytes per instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IfAction now takes a bool HasElse template parameter. When false (the common case — most if actions have no else branch), the else_ ActionList is replaced by an empty struct with [[no_unique_address]], saving 8 bytes per instance. The codegen already knows at generation time whether an else branch exists and passes the appropriate template argument. --- esphome/automation.py | 9 +++++++-- esphome/core/base_automation.h | 27 ++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index 36ab30b654..669e902bc9 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -413,13 +413,18 @@ async def if_action_to_code( template_arg: cg.TemplateArguments, args: TemplateArgsType, ) -> MockObj: + has_else = CONF_ELSE in config + # Prepend HasElse bool to template arguments: IfAction + if_template_arg = cg.TemplateArguments( + cg.RawExpression("true" if has_else else "false"), *template_arg + ) cond_conf = next(el for el in config if el in (CONF_ANY, CONF_ALL, CONF_CONDITION)) condition = await build_condition(config[cond_conf], template_arg, args) - var = cg.new_Pvariable(action_id, template_arg, condition) + var = cg.new_Pvariable(action_id, if_template_arg, condition) if CONF_THEN in config: actions = await build_action_list(config[CONF_THEN], template_arg, args) cg.add(var.add_then(actions)) - if CONF_ELSE in config: + if has_else: actions = await build_action_list(config[CONF_ELSE], template_arg, args) cg.add(var.add_else(actions)) return var diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 985f26e711..efcffa8824 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -264,7 +264,7 @@ template class WhileLoopContinuation : public Action { WhileAction *parent_; }; -template class IfAction : public Action { +template class IfAction : public Action { public: explicit IfAction(Condition *condition) : condition_(condition) {} @@ -273,27 +273,25 @@ template class IfAction : public Action { this->then_.add_action(new ContinuationAction(this)); } - void add_else(const std::initializer_list *> &actions) { + void add_else(const std::initializer_list *> &actions) requires(HasElse) { this->else_.add_actions(actions); this->else_.add_action(new ContinuationAction(this)); } void play_complex(const Ts &...x) override { this->num_running_++; - bool res = this->condition_->check(x...); - if (res) { - if (this->then_.empty()) { - this->play_next_(x...); - } else if (this->num_running_ > 0) { + if (this->condition_->check(x...)) { + if (!this->then_.empty() && this->num_running_ > 0) { this->then_.play(x...); + return; } - } else { - if (this->else_.empty()) { - this->play_next_(x...); - } else if (this->num_running_ > 0) { + } else if constexpr (HasElse) { + if (!this->else_.empty() && this->num_running_ > 0) { this->else_.play(x...); + return; } } + this->play_next_(x...); } void play(const Ts &...x) override { /* ignore - see play_complex */ @@ -301,13 +299,16 @@ template class IfAction : public Action { void stop() override { this->then_.stop(); - this->else_.stop(); + if constexpr (HasElse) { + this->else_.stop(); + } } protected: Condition *condition_; ActionList then_; - ActionList else_; + struct NoElse {}; + [[no_unique_address]] std::conditional_t, NoElse> else_; }; template class WhileAction : public Action { From 4cab4c348af5a67d7f68d611598d5262b69dc13b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Mar 2026 21:38:33 -1000 Subject: [PATCH 2/2] Use plain bool instead of RawExpression for IfAction template arg --- esphome/automation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index 669e902bc9..17966dc782 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -415,9 +415,7 @@ async def if_action_to_code( ) -> MockObj: has_else = CONF_ELSE in config # Prepend HasElse bool to template arguments: IfAction - if_template_arg = cg.TemplateArguments( - cg.RawExpression("true" if has_else else "false"), *template_arg - ) + if_template_arg = cg.TemplateArguments(has_else, *template_arg) cond_conf = next(el for el in config if el in (CONF_ANY, CONF_ALL, CONF_CONDITION)) condition = await build_condition(config[cond_conf], template_arg, args) var = cg.new_Pvariable(action_id, if_template_arg, condition)