esphome/esphome/core/base_automation.h

407 lines
11 KiB
C++

#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/preferences.h"
#include <vector>
namespace esphome {
template<typename... Ts> class AndCondition : public Condition<Ts...> {
public:
explicit AndCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
bool check(Ts... x) override {
for (auto *condition : this->conditions_) {
if (!condition->check(x...))
return false;
}
return true;
}
protected:
std::vector<Condition<Ts...> *> conditions_;
};
template<typename... Ts> class OrCondition : public Condition<Ts...> {
public:
explicit OrCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
bool check(Ts... x) override {
for (auto *condition : this->conditions_) {
if (condition->check(x...))
return true;
}
return false;
}
protected:
std::vector<Condition<Ts...> *> conditions_;
};
template<typename... Ts> class NotCondition : public Condition<Ts...> {
public:
explicit NotCondition(Condition<Ts...> *condition) : condition_(condition) {}
bool check(Ts... x) override { return !this->condition_->check(x...); }
protected:
Condition<Ts...> *condition_;
};
template<typename... Ts> class XorCondition : public Condition<Ts...> {
public:
explicit XorCondition(const std::vector<Condition<Ts...> *> &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<Condition<Ts...> *> conditions_;
};
template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
public:
explicit LambdaCondition(std::function<bool(Ts...)> &&f) : f_(std::move(f)) {}
bool check(Ts... x) override { return this->f_(x...); }
protected:
std::function<bool(Ts...)> f_;
};
template<typename... Ts> class ForCondition : public Condition<Ts...>, 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<std::string>, public Component {
public:
void setup() override {
uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME);
ESPPreferenceObject pref = global_preferences->make_preference<char[30]>(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(&current_version);
global_preferences->sync();
}
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
};
#endif
template<typename... Ts> class DelayAction : public Action<Ts...>, public Component {
public:
explicit DelayAction() = default;
TEMPLATABLE_VALUE(uint32_t, delay)
void play_complex(Ts... x) override {
auto f = std::bind(&DelayAction<Ts...>::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<typename... Ts> class LambdaAction : public Action<Ts...> {
public:
explicit LambdaAction(std::function<void(Ts...)> &&f) : f_(std::move(f)) {}
void play(Ts... x) override { this->f_(x...); }
protected:
std::function<void(Ts...)> f_;
};
template<typename... Ts> class IfAction : public Action<Ts...> {
public:
explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
void add_then(const std::vector<Action<Ts...> *> &actions) {
this->then_.add_actions(actions);
this->then_.add_action(new LambdaAction<Ts...>([this](Ts... x) { this->play_next_(x...); }));
}
void add_else(const std::vector<Action<Ts...> *> &actions) {
this->else_.add_actions(actions);
this->else_.add_action(new LambdaAction<Ts...>([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<Ts...> *condition_;
ActionList<Ts...> then_;
ActionList<Ts...> else_;
};
template<typename... Ts> class WhileAction : public Action<Ts...> {
public:
WhileAction(Condition<Ts...> *condition) : condition_(condition) {}
void add_then(const std::vector<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_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<Ts...> *condition_;
ActionList<Ts...> then_;
std::tuple<Ts...> var_{};
};
template<typename... Ts> class RepeatAction : public Action<Ts...> {
public:
TEMPLATABLE_VALUE(uint32_t, count)
void add_then(const std::vector<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_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<uint32_t, Ts...> then_;
std::tuple<Ts...> var_;
};
template<typename... Ts> class WaitUntilAction : public Action<Ts...>, public Component {
public:
WaitUntilAction(Condition<Ts...> *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<Ts...>::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<Ts...> *condition_;
std::tuple<Ts...> var_{};
};
template<typename... Ts> class UpdateComponentAction : public Action<Ts...> {
public:
UpdateComponentAction(PollingComponent *component) : component_(component) {}
void play(Ts... x) override {
if (!this->component_->is_ready())
return;
this->component_->update();
}
protected:
PollingComponent *component_;
};
template<typename... Ts> class SuspendComponentAction : public Action<Ts...> {
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<typename... Ts> class ResumeComponentAction : public Action<Ts...> {
public:
ResumeComponentAction(PollingComponent *component) : component_(component) {}
TEMPLATABLE_VALUE(uint32_t, update_interval)
void play(Ts... x) override {
if (!this->component_->is_ready()) {
return;
}
optional<uint32_t> 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