1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 08:41:59 +00:00

[automations] Reduce memory usage in if/while/repeat actions (32-36 bytes per instance) (#11650)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
J. Nick Koston
2025-11-04 15:48:20 -06:00
committed by GitHub
parent 191a88c2dc
commit aed7505f53
2 changed files with 73 additions and 19 deletions

View File

@@ -220,6 +220,7 @@ template<typename... Ts> class Action {
protected:
friend ActionList<Ts...>;
template<typename... Us> friend class ContinuationAction;
virtual void play(Ts... x) = 0;
void play_next_(Ts... x) {

View File

@@ -216,18 +216,46 @@ template<typename... Ts> class StatelessLambdaAction : public Action<Ts...> {
void (*f_)(Ts...);
};
/// Simple continuation action that calls play_next_ on a parent action.
/// Used internally by IfAction, WhileAction, RepeatAction, etc. to chain actions.
/// Memory: 4-8 bytes (parent pointer) vs 40 bytes (LambdaAction with std::function).
template<typename... Ts> class ContinuationAction : public Action<Ts...> {
public:
explicit ContinuationAction(Action<Ts...> *parent) : parent_(parent) {}
void play(Ts... x) override { this->parent_->play_next_(x...); }
protected:
Action<Ts...> *parent_;
};
// Forward declaration for WhileLoopContinuation
template<typename... Ts> class WhileAction;
/// Loop continuation for WhileAction that checks condition and repeats or continues.
/// Memory: 4-8 bytes (parent pointer) vs 40 bytes (LambdaAction with std::function).
template<typename... Ts> class WhileLoopContinuation : public Action<Ts...> {
public:
explicit WhileLoopContinuation(WhileAction<Ts...> *parent) : parent_(parent) {}
void play(Ts... x) override;
protected:
WhileAction<Ts...> *parent_;
};
template<typename... Ts> class IfAction : public Action<Ts...> {
public:
explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
void add_then(const std::initializer_list<Action<Ts...> *> &actions) {
this->then_.add_actions(actions);
this->then_.add_action(new LambdaAction<Ts...>([this](Ts... x) { this->play_next_(x...); }));
this->then_.add_action(new ContinuationAction<Ts...>(this));
}
void add_else(const std::initializer_list<Action<Ts...> *> &actions) {
this->else_.add_actions(actions);
this->else_.add_action(new LambdaAction<Ts...>([this](Ts... x) { this->play_next_(x...); }));
this->else_.add_action(new ContinuationAction<Ts...>(this));
}
void play_complex(Ts... x) override {
@@ -268,17 +296,11 @@ template<typename... Ts> class WhileAction : public Action<Ts...> {
void add_then(const std::initializer_list<Action<Ts...> *> &actions) {
this->then_.add_actions(actions);
this->then_.add_action(new LambdaAction<Ts...>([this](Ts... x) {
if (this->num_running_ > 0 && this->condition_->check(x...)) {
// play again
this->then_.play(x...);
} else {
// condition false, play next
this->play_next_(x...);
}
}));
this->then_.add_action(new WhileLoopContinuation<Ts...>(this));
}
friend class WhileLoopContinuation<Ts...>;
void play_complex(Ts... x) override {
this->num_running_++;
// Initial condition check
@@ -304,22 +326,43 @@ template<typename... Ts> class WhileAction : public Action<Ts...> {
ActionList<Ts...> then_;
};
// Implementation of WhileLoopContinuation::play
template<typename... Ts> void WhileLoopContinuation<Ts...>::play(Ts... x) {
if (this->parent_->num_running_ > 0 && this->parent_->condition_->check(x...)) {
// play again
this->parent_->then_.play(x...);
} else {
// condition false, play next
this->parent_->play_next_(x...);
}
}
// Forward declaration for RepeatLoopContinuation
template<typename... Ts> class RepeatAction;
/// Loop continuation for RepeatAction that increments iteration and repeats or continues.
/// Memory: 4-8 bytes (parent pointer) vs 40 bytes (LambdaAction with std::function).
template<typename... Ts> class RepeatLoopContinuation : public Action<uint32_t, Ts...> {
public:
explicit RepeatLoopContinuation(RepeatAction<Ts...> *parent) : parent_(parent) {}
void play(uint32_t iteration, Ts... x) override;
protected:
RepeatAction<Ts...> *parent_;
};
template<typename... Ts> class RepeatAction : public Action<Ts...> {
public:
TEMPLATABLE_VALUE(uint32_t, count)
void add_then(const std::initializer_list<Action<uint32_t, Ts...> *> &actions) {
this->then_.add_actions(actions);
this->then_.add_action(new LambdaAction<uint32_t, Ts...>([this](uint32_t iteration, Ts... x) {
iteration++;
if (iteration >= this->count_.value(x...)) {
this->play_next_(x...);
} else {
this->then_.play(iteration, x...);
}
}));
this->then_.add_action(new RepeatLoopContinuation<Ts...>(this));
}
friend class RepeatLoopContinuation<Ts...>;
void play_complex(Ts... x) override {
this->num_running_++;
if (this->count_.value(x...) > 0) {
@@ -338,6 +381,16 @@ template<typename... Ts> class RepeatAction : public Action<Ts...> {
ActionList<uint32_t, Ts...> then_;
};
// Implementation of RepeatLoopContinuation::play
template<typename... Ts> void RepeatLoopContinuation<Ts...>::play(uint32_t iteration, Ts... x) {
iteration++;
if (iteration >= this->parent_->count_.value(x...)) {
this->parent_->play_next_(x...);
} else {
this->parent_->then_.play(iteration, x...);
}
}
/** Wait until a condition is true to continue execution.
*
* Uses queue-based storage to safely handle concurrent executions.