#pragma once #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/preferences.h" #include namespace esphome { template class AndCondition : public Condition { public: explicit AndCondition(const std::vector *> &conditions) : conditions_(conditions) {} bool check(Ts... x) override { for (auto *condition : this->conditions_) { if (!condition->check(x...)) return false; } return true; } protected: std::vector *> conditions_; }; template class OrCondition : public Condition { public: explicit OrCondition(const std::vector *> &conditions) : conditions_(conditions) {} bool check(Ts... x) override { for (auto *condition : this->conditions_) { if (condition->check(x...)) return true; } return false; } protected: std::vector *> conditions_; }; template class NotCondition : public Condition { public: explicit NotCondition(Condition *condition) : condition_(condition) {} bool check(Ts... x) override { return !this->condition_->check(x...); } protected: Condition *condition_; }; template class XorCondition : public Condition { public: explicit XorCondition(const std::vector *> &conditions) : conditions_(conditions) {} bool check(Ts... x) override { size_t result = 0; for (auto *condition : this->conditions_) { result += condition->check(x...); } return result == 1; } protected: std::vector *> conditions_; }; template class LambdaCondition : public Condition { public: explicit LambdaCondition(std::function &&f) : f_(std::move(f)) {} bool check(Ts... x) override { return this->f_(x...); } protected: std::function f_; }; template class ForCondition : public Condition, public Component { public: explicit ForCondition(Condition<> *condition) : condition_(condition) {} TEMPLATABLE_VALUE(uint32_t, time); void loop() override { this->check_internal(); } float get_setup_priority() const override { return setup_priority::DATA; } bool check_internal() { bool cond = this->condition_->check(); if (!cond) this->last_inactive_ = millis(); return cond; } bool check(Ts... x) override { if (!this->check_internal()) return false; return millis() - this->last_inactive_ >= this->time_.value(x...); } protected: Condition<> *condition_; uint32_t last_inactive_{0}; }; class StartupTrigger : public Trigger<>, public Component { public: explicit StartupTrigger(float setup_priority) : setup_priority_(setup_priority) {} void setup() override { this->trigger(); } float get_setup_priority() const override { return this->setup_priority_; } protected: float setup_priority_; }; class ShutdownTrigger : public Trigger<>, public Component { public: explicit ShutdownTrigger(float setup_priority) : setup_priority_(setup_priority) {} void on_shutdown() override { this->trigger(); } float get_setup_priority() const override { return this->setup_priority_; } protected: float setup_priority_; }; class LoopTrigger : public Trigger<>, public Component { public: void loop() override { this->trigger(); } float get_setup_priority() const override { return setup_priority::DATA; } }; #ifdef ESPHOME_PROJECT_NAME class ProjectUpdateTrigger : public Trigger, public Component { public: void setup() override { uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME); ESPPreferenceObject pref = global_preferences->make_preference(hash, true); char previous_version[30]; char current_version[30] = ESPHOME_PROJECT_VERSION_30; if (pref.load(&previous_version)) { int cmp = strcmp(previous_version, current_version); if (cmp < 0) { this->trigger(previous_version); } } pref.save(¤t_version); global_preferences->sync(); } float get_setup_priority() const override { return setup_priority::PROCESSOR; } }; #endif template class DelayAction : public Action, public Component { public: explicit DelayAction() = default; TEMPLATABLE_VALUE(uint32_t, delay) void play_complex(Ts... x) override { auto f = std::bind(&DelayAction::play_next_, this, x...); this->num_running_++; this->set_timeout(this->delay_.value(x...), f); } float get_setup_priority() const override { return setup_priority::HARDWARE; } void play(Ts... x) override { /* ignore - see play_complex */ } void stop() override { this->cancel_timeout(""); } }; template class LambdaAction : public Action { public: explicit LambdaAction(std::function &&f) : f_(std::move(f)) {} void play(Ts... x) override { this->f_(x...); } protected: std::function f_; }; template class IfAction : public Action { public: explicit IfAction(Condition *condition) : condition_(condition) {} void add_then(const std::vector *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } void add_else(const std::vector *> &actions) { this->else_.add_actions(actions); this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } void play_complex(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) { this->then_.play(x...); } } else { if (this->else_.empty()) { this->play_next_(x...); } else if (this->num_running_ > 0) { this->else_.play(x...); } } } void play(Ts... x) override { /* ignore - see play_complex */ } void stop() override { this->then_.stop(); this->else_.stop(); } protected: Condition *condition_; ActionList then_; ActionList else_; }; template class WhileAction : public Action { public: WhileAction(Condition *condition) : condition_(condition) {} void add_then(const std::vector *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](Ts... x) { if (this->num_running_ > 0 && this->condition_->check_tuple(this->var_)) { // play again if (this->num_running_ > 0) { this->then_.play_tuple(this->var_); } } else { // condition false, play next this->play_next_tuple_(this->var_); } })); } void play_complex(Ts... x) override { this->num_running_++; // Store loop parameters this->var_ = std::make_tuple(x...); // Initial condition check if (!this->condition_->check_tuple(this->var_)) { // If new condition check failed, stop loop if running this->then_.stop(); this->play_next_tuple_(this->var_); return; } if (this->num_running_ > 0) { this->then_.play_tuple(this->var_); } } void play(Ts... x) override { /* ignore - see play_complex */ } void stop() override { this->then_.stop(); } protected: Condition *condition_; ActionList then_; std::tuple var_{}; }; template class RepeatAction : public Action { public: TEMPLATABLE_VALUE(uint32_t, count) void add_then(const std::vector *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](uint32_t iteration, Ts... x) { iteration++; if (iteration >= this->count_.value(x...)) this->play_next_tuple_(this->var_); else this->then_.play(iteration, x...); })); } void play_complex(Ts... x) override { this->num_running_++; this->var_ = std::make_tuple(x...); if (this->count_.value(x...) > 0) { this->then_.play(0, x...); } else { this->play_next_tuple_(this->var_); } } void play(Ts... x) override { /* ignore - see play_complex */ } void stop() override { this->then_.stop(); } protected: ActionList then_; std::tuple var_; }; template class WaitUntilAction : public Action, public Component { public: WaitUntilAction(Condition *condition) : condition_(condition) {} TEMPLATABLE_VALUE(uint32_t, timeout_value) void play_complex(Ts... x) override { this->num_running_++; // Check if we can continue immediately. if (this->condition_->check(x...)) { if (this->num_running_ > 0) { this->play_next_(x...); } return; } this->var_ = std::make_tuple(x...); if (this->timeout_value_.has_value()) { auto f = std::bind(&WaitUntilAction::play_next_, this, x...); this->set_timeout("timeout", this->timeout_value_.value(x...), f); } this->loop(); } void loop() override { if (this->num_running_ == 0) return; if (!this->condition_->check_tuple(this->var_)) { return; } this->cancel_timeout("timeout"); this->play_next_tuple_(this->var_); } float get_setup_priority() const override { return setup_priority::DATA; } void play(Ts... x) override { /* ignore - see play_complex */ } void stop() override { this->cancel_timeout("timeout"); } protected: Condition *condition_; std::tuple var_{}; }; template class UpdateComponentAction : public Action { public: UpdateComponentAction(PollingComponent *component) : component_(component) {} void play(Ts... x) override { if (!this->component_->is_ready()) return; this->component_->update(); } protected: PollingComponent *component_; }; template class SuspendComponentAction : public Action { public: SuspendComponentAction(PollingComponent *component) : component_(component) {} void play(Ts... x) override { if (!this->component_->is_ready()) return; this->component_->stop_poller(); } protected: PollingComponent *component_; }; template class ResumeComponentAction : public Action { public: ResumeComponentAction(PollingComponent *component) : component_(component) {} TEMPLATABLE_VALUE(uint32_t, update_interval) void play(Ts... x) override { if (!this->component_->is_ready()) { return; } optional update_interval = this->update_interval_.optional_value(x...); if (update_interval.has_value()) { this->component_->set_update_interval(update_interval.value()); } this->component_->start_poller(); } protected: PollingComponent *component_; }; } // namespace esphome