mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-05-19 04:33:27 +02:00
🏗 Merge C++ into python codebase (#504)
## Description: Move esphome-core codebase into esphome (and a bunch of other refactors). See https://github.com/esphome/feature-requests/issues/97 Yes this is a shit ton of work and no there's no way to automate it :( But it will be worth it 👍 Progress: - Core support (file copy etc): 80% - Base Abstractions (light, switch): ~50% - Integrations: ~10% - Working? Yes, (but only with ported components). Other refactors: - Moves all codegen related stuff into a single class: `esphome.codegen` (imported as `cg`) - Rework coroutine syntax - Move from `component/platform.py` to `domain/component.py` structure as with HA - Move all defaults out of C++ and into config validation. - Remove `make_...` helpers from Application class. Reason: Merge conflicts with every single new integration. - Pointer Variables are stored globally instead of locally in setup(). Reason: stack size limit. Future work: - Rework const.py - Move all `CONF_...` into a conf class (usage `conf.UPDATE_INTERVAL` vs `CONF_UPDATE_INTERVAL`). Reason: Less convoluted import block - Enable loading from `custom_components` folder. **Related issue (if applicable):** https://github.com/esphome/feature-requests/issues/97 **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here> ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_STATUS_LED
|
||||
#include "esphome/components/status_led/status_led.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *TAG = "app";
|
||||
|
||||
void Application::register_component_(Component *comp) {
|
||||
if (comp == nullptr) {
|
||||
ESP_LOGW(TAG, "Tried to register null component!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *c : this->components_) {
|
||||
if (comp == c) {
|
||||
ESP_LOGW(TAG, "Component already registered! (%p)", c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->components_.push_back(comp);
|
||||
}
|
||||
void Application::setup() {
|
||||
ESP_LOGI(TAG, "Running through setup()...");
|
||||
ESP_LOGV(TAG, "Sorting components by setup priority...");
|
||||
std::stable_sort(this->components_.begin(), this->components_.end(), [](const Component *a, const Component *b) {
|
||||
return a->get_actual_setup_priority() > b->get_actual_setup_priority();
|
||||
});
|
||||
|
||||
for (uint32_t i = 0; i < this->components_.size(); i++) {
|
||||
Component *component = this->components_[i];
|
||||
if (component->is_failed())
|
||||
continue;
|
||||
|
||||
component->call_setup();
|
||||
if (component->can_proceed())
|
||||
continue;
|
||||
|
||||
std::stable_sort(this->components_.begin(), this->components_.begin() + i + 1,
|
||||
[](Component *a, Component *b) { return a->get_loop_priority() > b->get_loop_priority(); });
|
||||
|
||||
do {
|
||||
uint32_t new_app_state = STATUS_LED_WARNING;
|
||||
for (uint32_t j = 0; j <= i; j++) {
|
||||
if (!this->components_[j]->is_failed()) {
|
||||
this->components_[j]->call_loop();
|
||||
}
|
||||
new_app_state |= this->components_[j]->get_component_state();
|
||||
this->app_state_ |= new_app_state;
|
||||
}
|
||||
this->app_state_ = new_app_state;
|
||||
yield();
|
||||
} while (!component->can_proceed());
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "setup() finished successfully!");
|
||||
this->dump_config();
|
||||
}
|
||||
void Application::dump_config() {
|
||||
ESP_LOGI(TAG, "esphome-core version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str());
|
||||
|
||||
for (auto component : this->components_) {
|
||||
component->dump_config();
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR HOT Application::feed_wdt() {
|
||||
static uint32_t LAST_FEED = 0;
|
||||
uint32_t now = millis();
|
||||
if (now - LAST_FEED > 3) {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
yield();
|
||||
#endif
|
||||
LAST_FEED = now;
|
||||
#ifdef USE_STATUS_LED
|
||||
if (status_led::global_status_led != nullptr) {
|
||||
status_led::global_status_led->call_loop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void Application::reboot() {
|
||||
ESP_LOGI(TAG, "Forcing a reboot...");
|
||||
for (auto *comp : this->components_)
|
||||
comp->on_shutdown();
|
||||
ESP.restart();
|
||||
// restart() doesn't always end execution
|
||||
while (true) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
void Application::safe_reboot() {
|
||||
ESP_LOGI(TAG, "Rebooting safely...");
|
||||
for (auto *comp : this->components_)
|
||||
comp->on_safe_shutdown();
|
||||
for (auto *comp : this->components_)
|
||||
comp->on_shutdown();
|
||||
ESP.restart();
|
||||
// restart() doesn't always end execution
|
||||
while (true) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
Application App;
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,271 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
#include "esphome/components/fan/fan_state.h"
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
#include "esphome/components/light/light_state.h"
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
#include "esphome/components/cover/cover.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
class Application {
|
||||
public:
|
||||
void pre_setup(const std::string &name, const char *compilation_time) {
|
||||
this->name_ = name;
|
||||
this->compilation_time_ = compilation_time;
|
||||
global_preferences.begin(this->name_);
|
||||
}
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void register_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||
this->binary_sensors_.push_back(binary_sensor);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void register_sensor(sensor::Sensor *sensor) { this->sensors_.push_back(sensor); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
void register_switch(switch_::Switch *a_switch) { this->switches_.push_back(a_switch); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void register_text_sensor(text_sensor::TextSensor *sensor) { this->text_sensors_.push_back(sensor); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_FAN
|
||||
void register_fan(fan::FanState *state) { this->fans_.push_back(state); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_COVER
|
||||
void register_cover(cover::Cover *cover) { this->covers_.push_back(cover); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
void register_climate(climate::Climate *climate) { this->climates_.push_back(climate); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
void register_light(light::LightState *light) { this->lights_.push_back(light); }
|
||||
#endif
|
||||
|
||||
/// Register the component in this Application instance.
|
||||
template<class C> C *register_component(C *c) {
|
||||
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
||||
this->register_component_((Component *) c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/// Set up all the registered components. Call this at the end of your setup() function.
|
||||
void setup();
|
||||
|
||||
/// Make a loop iteration. Call this in your loop() function.
|
||||
void loop() {
|
||||
uint32_t new_app_state = 0;
|
||||
for (Component *component : this->components_) {
|
||||
if (!component->is_failed()) {
|
||||
component->call_loop();
|
||||
}
|
||||
new_app_state |= component->get_component_state();
|
||||
this->app_state_ |= new_app_state;
|
||||
this->feed_wdt();
|
||||
}
|
||||
this->app_state_ = new_app_state;
|
||||
|
||||
const uint32_t now = millis();
|
||||
if (HighFrequencyLoopRequester::is_high_frequency()) {
|
||||
yield();
|
||||
} else {
|
||||
uint32_t delay_time = this->loop_interval_;
|
||||
if (now - this->last_loop_ < this->loop_interval_)
|
||||
delay_time = this->loop_interval_ - (now - this->last_loop_);
|
||||
delay(delay_time);
|
||||
}
|
||||
this->last_loop_ = now;
|
||||
|
||||
if (this->dump_config_scheduled_) {
|
||||
this->dump_config();
|
||||
this->dump_config_scheduled_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of this Application set by set_name().
|
||||
const std::string &get_name() const { return this->name_; }
|
||||
|
||||
const std::string &get_compilation_time() const { return this->compilation_time_; }
|
||||
|
||||
/** Set the target interval with which to run the loop() calls.
|
||||
* If the loop() method takes longer than the target interval, ESPHome won't
|
||||
* sleep in loop(), but if the time spent in loop() is small than the target, ESPHome
|
||||
* will delay at the end of the App.loop() method.
|
||||
*
|
||||
* This is done to conserve power: In most use-cases, high-speed loop() calls are not required
|
||||
* and degrade power consumption.
|
||||
*
|
||||
* Each component can request a high frequency loop execution by using the HighFrequencyLoopRequester
|
||||
* helper in helpers.h
|
||||
*
|
||||
* @param loop_interval The interval in milliseconds to run the core loop at. Defaults to 16 milliseconds.
|
||||
*/
|
||||
void set_loop_interval(uint32_t loop_interval) { this->loop_interval_ = loop_interval; }
|
||||
|
||||
void dump_config();
|
||||
void schedule_dump_config() { this->dump_config_scheduled_ = true; }
|
||||
|
||||
void feed_wdt();
|
||||
|
||||
void reboot();
|
||||
|
||||
void safe_reboot();
|
||||
|
||||
void run_safe_shutdown_hooks() {
|
||||
for (auto *comp : this->components_)
|
||||
comp->on_safe_shutdown();
|
||||
}
|
||||
|
||||
uint32_t get_app_state() const { return this->app_state_; }
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
const std::vector<binary_sensor::BinarySensor *> &get_binary_sensors() { return this->binary_sensors_; }
|
||||
binary_sensor::BinarySensor *get_binary_sensor_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->binary_sensors_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
const std::vector<switch_::Switch *> &get_switches() { return this->switches_; }
|
||||
switch_::Switch *get_switch_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->switches_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
const std::vector<sensor::Sensor *> &get_sensors() { return this->sensors_; }
|
||||
sensor::Sensor *get_sensor_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->sensors_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
const std::vector<text_sensor::TextSensor *> &get_text_sensors() { return this->text_sensors_; }
|
||||
text_sensor::TextSensor *get_text_sensor_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->text_sensors_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
const std::vector<fan::FanState *> &get_fans() { return this->fans_; }
|
||||
fan::FanState *get_fan_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->fans_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
const std::vector<cover::Cover *> &get_covers() { return this->covers_; }
|
||||
cover::Cover *get_cover_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->covers_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
const std::vector<light::LightState *> &get_lights() { return this->lights_; }
|
||||
light::LightState *get_light_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->lights_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
const std::vector<climate::Climate *> &get_climates() { return this->climates_; }
|
||||
climate::Climate *get_climate_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->climates_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
friend Component;
|
||||
|
||||
void register_component_(Component *comp);
|
||||
|
||||
std::vector<Component *> components_{};
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
std::vector<binary_sensor::BinarySensor *> binary_sensors_{};
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
std::vector<switch_::Switch *> switches_{};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::vector<sensor::Sensor *> sensors_{};
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
std::vector<text_sensor::TextSensor *> text_sensors_{};
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
std::vector<fan::FanState *> fans_{};
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
std::vector<cover::Cover *> covers_{};
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
std::vector<climate::Climate *> climates_{};
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
std::vector<light::LightState *> lights_{};
|
||||
#endif
|
||||
|
||||
std::string name_;
|
||||
std::string compilation_time_;
|
||||
uint32_t last_loop_{0};
|
||||
uint32_t loop_interval_{16};
|
||||
bool dump_config_scheduled_{false};
|
||||
uint32_t app_state_{0};
|
||||
};
|
||||
|
||||
/// Global storage of Application pointer - only one Application can exist.
|
||||
extern Application App;
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *TAG = "automation";
|
||||
|
||||
void StartupTrigger::setup() { this->trigger(); }
|
||||
float StartupTrigger::get_setup_priority() const {
|
||||
// Run after everything is set up
|
||||
return this->setup_priority_;
|
||||
}
|
||||
StartupTrigger::StartupTrigger(float setup_priority) : setup_priority_(setup_priority) {}
|
||||
|
||||
void ShutdownTrigger::on_shutdown() { this->trigger(); }
|
||||
|
||||
void LoopTrigger::loop() { this->trigger(); }
|
||||
float LoopTrigger::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
RangeCondition::RangeCondition() = default;
|
||||
|
||||
bool RangeCondition::check(float x) {
|
||||
float min = this->min_.value(x);
|
||||
float max = this->max_.value(x);
|
||||
if (isnan(min)) {
|
||||
return x >= max;
|
||||
} else if (isnan(max)) {
|
||||
return x >= min;
|
||||
} else {
|
||||
return min <= x && x <= max;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,241 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
#define TEMPLATABLE_VALUE_(type, name) \
|
||||
protected: \
|
||||
TemplatableValue<type, Ts...> name##_{}; \
|
||||
\
|
||||
public: \
|
||||
template<typename V> void set_##name(V value_) { this->name##_ = value_; }
|
||||
|
||||
#define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name)
|
||||
|
||||
template<typename... Ts> class Condition {
|
||||
public:
|
||||
virtual bool check(Ts... x) = 0;
|
||||
|
||||
bool check_tuple(const std::tuple<Ts...> &tuple);
|
||||
|
||||
protected:
|
||||
template<int... S> bool check_tuple_(const std::tuple<Ts...> &tuple, seq<S...>);
|
||||
};
|
||||
|
||||
template<typename... Ts> class AndCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit AndCondition(const std::vector<Condition<Ts...> *> &conditions);
|
||||
bool check(Ts... x) override;
|
||||
|
||||
protected:
|
||||
std::vector<Condition<Ts...> *> conditions_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class OrCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit OrCondition(const std::vector<Condition<Ts...> *> &conditions);
|
||||
bool check(Ts... x) override;
|
||||
|
||||
protected:
|
||||
std::vector<Condition<Ts...> *> conditions_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit LambdaCondition(std::function<bool(Ts...)> &&f);
|
||||
bool check(Ts... x) override;
|
||||
|
||||
protected:
|
||||
std::function<bool(Ts...)> f_;
|
||||
};
|
||||
|
||||
class RangeCondition : public Condition<float> {
|
||||
public:
|
||||
explicit RangeCondition();
|
||||
bool check(float x) override;
|
||||
|
||||
template<typename V> void set_min(V value) { this->min_ = value; }
|
||||
template<typename V> void set_max(V value) { this->max_ = value; }
|
||||
|
||||
protected:
|
||||
TemplatableValue<float, float> min_{NAN};
|
||||
TemplatableValue<float, float> max_{NAN};
|
||||
};
|
||||
|
||||
template<typename... Ts> class Automation;
|
||||
|
||||
template<typename... Ts> class Trigger {
|
||||
public:
|
||||
void trigger(Ts... x);
|
||||
void set_parent(Automation<Ts...> *parent);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
Automation<Ts...> *parent_{nullptr};
|
||||
};
|
||||
|
||||
class StartupTrigger : public Trigger<>, public Component {
|
||||
public:
|
||||
explicit StartupTrigger(float setup_priority = setup_priority::LATE);
|
||||
void setup() override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
float setup_priority_;
|
||||
};
|
||||
|
||||
class ShutdownTrigger : public Trigger<>, public Component {
|
||||
public:
|
||||
void on_shutdown() override;
|
||||
};
|
||||
|
||||
class LoopTrigger : public Trigger<>, public Component {
|
||||
public:
|
||||
void loop() override;
|
||||
float get_setup_priority() const override;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ActionList;
|
||||
|
||||
template<typename... Ts> class Action {
|
||||
public:
|
||||
virtual void play(Ts... x) = 0;
|
||||
void play_next(Ts... x);
|
||||
virtual void stop();
|
||||
void stop_next();
|
||||
|
||||
void play_next_tuple(const std::tuple<Ts...> &tuple);
|
||||
|
||||
protected:
|
||||
friend ActionList<Ts...>;
|
||||
|
||||
template<int... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, seq<S...>);
|
||||
|
||||
Action<Ts...> *next_ = nullptr;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DelayAction : public Action<Ts...>, public Component {
|
||||
public:
|
||||
explicit DelayAction();
|
||||
|
||||
template<typename V> void set_delay(V value) { this->delay_ = value; }
|
||||
void stop() override;
|
||||
|
||||
void play(Ts... x) override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
TemplatableValue<uint32_t, Ts...> delay_{0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class LambdaAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit LambdaAction(std::function<void(Ts...)> &&f);
|
||||
void play(Ts... x) override;
|
||||
|
||||
protected:
|
||||
std::function<void(Ts...)> f_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class IfAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit IfAction(std::vector<Condition<Ts...> *> conditions);
|
||||
|
||||
void add_then(const std::vector<Action<Ts...> *> &actions);
|
||||
|
||||
void add_else(const std::vector<Action<Ts...> *> &actions);
|
||||
|
||||
void play(Ts... x) override;
|
||||
|
||||
void stop() override;
|
||||
|
||||
protected:
|
||||
std::vector<Condition<Ts...> *> conditions_;
|
||||
ActionList<Ts...> then_;
|
||||
ActionList<Ts...> else_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class WhileAction : public Action<Ts...> {
|
||||
public:
|
||||
WhileAction(const std::vector<Condition<Ts...> *> &conditions);
|
||||
|
||||
void add_then(const std::vector<Action<Ts...> *> &actions);
|
||||
|
||||
void play(Ts... x) override;
|
||||
|
||||
void stop() override;
|
||||
|
||||
protected:
|
||||
std::vector<Condition<Ts...> *> conditions_;
|
||||
ActionList<Ts...> then_;
|
||||
bool is_running_{false};
|
||||
};
|
||||
|
||||
template<typename... Ts> class WaitUntilAction : public Action<Ts...>, public Component {
|
||||
public:
|
||||
WaitUntilAction(const std::vector<Condition<Ts...> *> &conditions);
|
||||
|
||||
void play(Ts... x) override;
|
||||
|
||||
void stop() override;
|
||||
|
||||
void loop() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
std::vector<Condition<Ts...> *> conditions_;
|
||||
bool triggered_{false};
|
||||
std::tuple<Ts...> var_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class UpdateComponentAction : public Action<Ts...> {
|
||||
public:
|
||||
UpdateComponentAction(PollingComponent *component);
|
||||
void play(Ts... x) override;
|
||||
|
||||
protected:
|
||||
PollingComponent *component_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ActionList {
|
||||
public:
|
||||
Action<Ts...> *add_action(Action<Ts...> *action);
|
||||
void add_actions(const std::vector<Action<Ts...> *> &actions);
|
||||
void play(Ts... x);
|
||||
void stop();
|
||||
bool empty() const;
|
||||
|
||||
protected:
|
||||
Action<Ts...> *actions_begin_{nullptr};
|
||||
Action<Ts...> *actions_end_{nullptr};
|
||||
};
|
||||
|
||||
template<typename... Ts> class Automation {
|
||||
public:
|
||||
explicit Automation(Trigger<Ts...> *trigger);
|
||||
|
||||
Condition<Ts...> *add_condition(Condition<Ts...> *condition);
|
||||
void add_conditions(const std::vector<Condition<Ts...> *> &conditions);
|
||||
|
||||
Action<Ts...> *add_action(Action<Ts...> *action);
|
||||
void add_actions(const std::vector<Action<Ts...> *> &actions);
|
||||
|
||||
void stop();
|
||||
|
||||
void trigger(Ts... x);
|
||||
|
||||
protected:
|
||||
Trigger<Ts...> *trigger_;
|
||||
std::vector<Condition<Ts...> *> conditions_;
|
||||
ActionList<Ts...> actions_;
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#include "esphome/core/automation.tcc"
|
||||
@@ -0,0 +1,243 @@
|
||||
#pragma once
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
template<typename... Ts> bool Condition<Ts...>::check_tuple(const std::tuple<Ts...> &tuple) {
|
||||
return this->check_tuple_(tuple, typename gens<sizeof...(Ts)>::type());
|
||||
}
|
||||
template<typename... Ts>
|
||||
template<int... S>
|
||||
bool Condition<Ts...>::check_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) {
|
||||
return this->check(std::get<S>(tuple)...);
|
||||
}
|
||||
|
||||
template<typename... Ts> bool AndCondition<Ts...>::check(Ts... x) {
|
||||
for (auto *condition : this->conditions_) {
|
||||
if (!condition->check(x...))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
AndCondition<Ts...>::AndCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
|
||||
|
||||
template<typename... Ts> bool OrCondition<Ts...>::check(Ts... x) {
|
||||
for (auto *condition : this->conditions_) {
|
||||
if (condition->check(x...))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
OrCondition<Ts...>::OrCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
|
||||
|
||||
template<typename... Ts> void Trigger<Ts...>::set_parent(Automation<Ts...> *parent) { this->parent_ = parent; }
|
||||
|
||||
template<typename... Ts> void Trigger<Ts...>::trigger(Ts... x) {
|
||||
if (this->parent_ == nullptr)
|
||||
return;
|
||||
this->parent_->trigger(x...);
|
||||
}
|
||||
template<typename... Ts> void Trigger<Ts...>::stop() {
|
||||
if (this->parent_ == nullptr)
|
||||
return;
|
||||
this->parent_->stop();
|
||||
}
|
||||
|
||||
template<typename... Ts> void Action<Ts...>::play_next(Ts... x) {
|
||||
if (this->next_ != nullptr) {
|
||||
this->next_->play(x...);
|
||||
}
|
||||
}
|
||||
template<typename... Ts> void Action<Ts...>::stop() { this->stop_next(); }
|
||||
template<typename... Ts> void Action<Ts...>::stop_next() {
|
||||
if (this->next_ != nullptr) {
|
||||
this->next_->stop();
|
||||
}
|
||||
}
|
||||
template<typename... Ts> void Action<Ts...>::play_next_tuple(const std::tuple<Ts...> &tuple) {
|
||||
this->play_next_tuple_(tuple, typename gens<sizeof...(Ts)>::type());
|
||||
}
|
||||
template<typename... Ts>
|
||||
template<int... S>
|
||||
void Action<Ts...>::play_next_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) {
|
||||
this->play_next(std::get<S>(tuple)...);
|
||||
}
|
||||
|
||||
template<typename... Ts> DelayAction<Ts...>::DelayAction() = default;
|
||||
|
||||
template<typename... Ts> void DelayAction<Ts...>::play(Ts... x) {
|
||||
auto f = std::bind(&DelayAction<Ts...>::play_next, this, x...);
|
||||
this->set_timeout(this->delay_.value(x...), f);
|
||||
}
|
||||
template<typename... Ts> float DelayAction<Ts...>::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
template<typename... Ts> void DelayAction<Ts...>::stop() {
|
||||
this->cancel_timeout("");
|
||||
this->stop_next();
|
||||
}
|
||||
|
||||
template<typename... Ts> Condition<Ts...> *Automation<Ts...>::add_condition(Condition<Ts...> *condition) {
|
||||
this->conditions_.push_back(condition);
|
||||
return condition;
|
||||
}
|
||||
template<typename... Ts> void Automation<Ts...>::add_conditions(const std::vector<Condition<Ts...> *> &conditions) {
|
||||
for (auto *condition : conditions) {
|
||||
this->add_condition(condition);
|
||||
}
|
||||
}
|
||||
template<typename... Ts> Automation<Ts...>::Automation(Trigger<Ts...> *trigger) : trigger_(trigger) {
|
||||
this->trigger_->set_parent(this);
|
||||
}
|
||||
template<typename... Ts> Action<Ts...> *Automation<Ts...>::add_action(Action<Ts...> *action) {
|
||||
this->actions_.add_action(action);
|
||||
}
|
||||
template<typename... Ts> void Automation<Ts...>::add_actions(const std::vector<Action<Ts...> *> &actions) {
|
||||
this->actions_.add_actions(actions);
|
||||
}
|
||||
template<typename... Ts> void Automation<Ts...>::trigger(Ts... x) {
|
||||
for (auto *condition : this->conditions_) {
|
||||
if (!condition->check(x...))
|
||||
return;
|
||||
}
|
||||
|
||||
this->actions_.play(x...);
|
||||
}
|
||||
template<typename... Ts> void Automation<Ts...>::stop() { this->actions_.stop(); }
|
||||
template<typename... Ts> LambdaCondition<Ts...>::LambdaCondition(std::function<bool(Ts...)> &&f) : f_(std::move(f)) {}
|
||||
template<typename... Ts> bool LambdaCondition<Ts...>::check(Ts... x) { return this->f_(x...); }
|
||||
|
||||
template<typename... Ts> LambdaAction<Ts...>::LambdaAction(std::function<void(Ts...)> &&f) : f_(std::move(f)) {}
|
||||
template<typename... Ts> void LambdaAction<Ts...>::play(Ts... x) {
|
||||
this->f_(x...);
|
||||
this->play_next(x...);
|
||||
}
|
||||
|
||||
template<typename... Ts> Action<Ts...> *ActionList<Ts...>::add_action(Action<Ts...> *action) {
|
||||
if (this->actions_end_ == nullptr) {
|
||||
this->actions_begin_ = action;
|
||||
} else {
|
||||
this->actions_end_->next_ = action;
|
||||
}
|
||||
return this->actions_end_ = action;
|
||||
}
|
||||
template<typename... Ts> void ActionList<Ts...>::add_actions(const std::vector<Action<Ts...> *> &actions) {
|
||||
for (auto *action : actions) {
|
||||
this->add_action(action);
|
||||
}
|
||||
}
|
||||
template<typename... Ts> void ActionList<Ts...>::play(Ts... x) {
|
||||
if (this->actions_begin_ != nullptr)
|
||||
this->actions_begin_->play(x...);
|
||||
}
|
||||
template<typename... Ts> void ActionList<Ts...>::stop() {
|
||||
if (this->actions_begin_ != nullptr)
|
||||
this->actions_begin_->stop();
|
||||
}
|
||||
template<typename... Ts> bool ActionList<Ts...>::empty() const { return this->actions_begin_ == nullptr; }
|
||||
template<typename... Ts>
|
||||
IfAction<Ts...>::IfAction(const std::vector<Condition<Ts...> *> conditions) : conditions_(conditions) {}
|
||||
template<typename... Ts> void IfAction<Ts...>::play(Ts... x) {
|
||||
bool res = true;
|
||||
for (auto *condition : this->conditions_) {
|
||||
if (!condition->check(x...)) {
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (res) {
|
||||
if (this->then_.empty()) {
|
||||
this->play_next(x...);
|
||||
} else {
|
||||
this->then_.play(x...);
|
||||
}
|
||||
} else {
|
||||
if (this->else_.empty()) {
|
||||
this->play_next(x...);
|
||||
} else {
|
||||
this->else_.play(x...);
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename... Ts> void IfAction<Ts...>::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...); }));
|
||||
}
|
||||
template<typename... Ts> void IfAction<Ts...>::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...); }));
|
||||
}
|
||||
template<typename... Ts> void IfAction<Ts...>::stop() {
|
||||
this->then_.stop();
|
||||
this->else_.stop();
|
||||
this->stop_next();
|
||||
}
|
||||
|
||||
template<typename... Ts> void UpdateComponentAction<Ts...>::play(Ts... x) {
|
||||
this->component_->update();
|
||||
this->play_next(x...);
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
UpdateComponentAction<Ts...>::UpdateComponentAction(PollingComponent *component) : component_(component) {}
|
||||
|
||||
template<typename... Ts>
|
||||
WhileAction<Ts...>::WhileAction(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
|
||||
|
||||
template<typename... Ts> void WhileAction<Ts...>::add_then(const std::vector<Action<Ts...> *> &actions) {
|
||||
this->then_.add_actions(actions);
|
||||
this->then_.add_action(new LambdaAction<Ts...>([this](Ts... x) {
|
||||
this->is_running_ = false;
|
||||
this->play(x...);
|
||||
}));
|
||||
}
|
||||
template<typename... Ts> void WhileAction<Ts...>::play(Ts... x) {
|
||||
if (this->is_running_)
|
||||
return;
|
||||
|
||||
for (auto *condition : this->conditions_) {
|
||||
if (!condition->check(x...)) {
|
||||
this->play_next(x...);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->is_running_ = true;
|
||||
this->then_.play(x...);
|
||||
}
|
||||
template<typename... Ts> void WhileAction<Ts...>::stop() {
|
||||
this->then_.stop();
|
||||
this->is_running_ = false;
|
||||
this->stop_next();
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
WaitUntilAction<Ts...>::WaitUntilAction(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
|
||||
template<typename... Ts> void WaitUntilAction<Ts...>::play(Ts... x) {
|
||||
this->var_ = std::make_tuple(x...);
|
||||
this->triggered_ = true;
|
||||
this->loop();
|
||||
}
|
||||
template<typename... Ts> void WaitUntilAction<Ts...>::stop() {
|
||||
this->triggered_ = false;
|
||||
this->stop_next();
|
||||
}
|
||||
template<typename... Ts> void WaitUntilAction<Ts...>::loop() {
|
||||
if (!this->triggered_)
|
||||
return;
|
||||
|
||||
for (auto *condition : this->conditions_) {
|
||||
if (!condition->check_tuple(this->var_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->triggered_ = false;
|
||||
this->play_next_tuple(this->var_);
|
||||
}
|
||||
template<typename... Ts> float WaitUntilAction<Ts...>::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,251 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *TAG = "component";
|
||||
|
||||
namespace setup_priority {
|
||||
|
||||
const float BUS = 1000.0f;
|
||||
const float IO = 900.0f;
|
||||
const float HARDWARE = 800.0f;
|
||||
const float DATA = 600.0f;
|
||||
const float PROCESSOR = 400.0;
|
||||
const float WIFI = 250.0f;
|
||||
const float AFTER_WIFI = 200.0f;
|
||||
const float AFTER_CONNECTION = 100.0f;
|
||||
const float LATE = -100.0f;
|
||||
|
||||
} // namespace setup_priority
|
||||
|
||||
const uint32_t COMPONENT_STATE_MASK = 0xFF;
|
||||
const uint32_t COMPONENT_STATE_CONSTRUCTION = 0x00;
|
||||
const uint32_t COMPONENT_STATE_SETUP = 0x01;
|
||||
const uint32_t COMPONENT_STATE_LOOP = 0x02;
|
||||
const uint32_t COMPONENT_STATE_FAILED = 0x03;
|
||||
const uint32_t STATUS_LED_MASK = 0xFF00;
|
||||
const uint32_t STATUS_LED_OK = 0x0000;
|
||||
const uint32_t STATUS_LED_WARNING = 0x0100;
|
||||
const uint32_t STATUS_LED_ERROR = 0x0200;
|
||||
|
||||
uint32_t global_state = 0;
|
||||
|
||||
float Component::get_loop_priority() const { return 0.0f; }
|
||||
|
||||
float Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void Component::setup() {}
|
||||
|
||||
void Component::loop() {}
|
||||
|
||||
void Component::set_interval(const std::string &name, uint32_t interval, std::function<void()> &&f) { // NOLINT
|
||||
const uint32_t now = millis();
|
||||
// only put offset in lower half
|
||||
uint32_t offset = 0;
|
||||
if (interval != 0)
|
||||
offset = (random_uint32() % interval) / 2;
|
||||
ESP_LOGVV(TAG, "set_interval(name='%s', interval=%u, offset=%u)", name.c_str(), interval, offset);
|
||||
|
||||
if (!name.empty()) {
|
||||
this->cancel_interval(name);
|
||||
}
|
||||
struct TimeFunction function = {
|
||||
.name = name,
|
||||
.type = TimeFunction::INTERVAL,
|
||||
.interval = interval,
|
||||
.last_execution = now - interval - offset,
|
||||
.f = std::move(f),
|
||||
.remove = false,
|
||||
};
|
||||
this->time_functions_.push_back(function);
|
||||
}
|
||||
|
||||
bool Component::cancel_interval(const std::string &name) { // NOLINT
|
||||
return this->cancel_time_function_(name, TimeFunction::INTERVAL);
|
||||
}
|
||||
|
||||
void Component::set_timeout(const std::string &name, uint32_t timeout, std::function<void()> &&f) { // NOLINT
|
||||
const uint32_t now = millis();
|
||||
ESP_LOGVV(TAG, "set_timeout(name='%s', timeout=%u)", name.c_str(), timeout);
|
||||
|
||||
if (!name.empty()) {
|
||||
this->cancel_timeout(name);
|
||||
}
|
||||
struct TimeFunction function = {
|
||||
.name = name,
|
||||
.type = TimeFunction::TIMEOUT,
|
||||
.interval = timeout,
|
||||
.last_execution = now,
|
||||
.f = std::move(f),
|
||||
.remove = false,
|
||||
};
|
||||
this->time_functions_.push_back(function);
|
||||
}
|
||||
|
||||
bool Component::cancel_timeout(const std::string &name) { // NOLINT
|
||||
return this->cancel_time_function_(name, TimeFunction::TIMEOUT);
|
||||
}
|
||||
|
||||
void Component::call_loop() {
|
||||
this->loop_internal_();
|
||||
this->loop();
|
||||
}
|
||||
|
||||
bool Component::cancel_time_function_(const std::string &name, TimeFunction::Type type) {
|
||||
// NOLINTNEXTLINE
|
||||
for (auto iter = this->time_functions_.begin(); iter != this->time_functions_.end(); iter++) {
|
||||
if (!iter->remove && iter->name == name && iter->type == type) {
|
||||
ESP_LOGVV(TAG, "Removing old time function %s.", iter->name.c_str());
|
||||
iter->remove = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Component::call_setup() {
|
||||
this->setup_internal_();
|
||||
this->setup();
|
||||
}
|
||||
uint32_t Component::get_component_state() const { return this->component_state_; }
|
||||
void Component::loop_internal_() {
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= COMPONENT_STATE_LOOP;
|
||||
|
||||
for (unsigned int i = 0; i < this->time_functions_.size(); i++) { // NOLINT
|
||||
const uint32_t now = millis();
|
||||
TimeFunction *tf = &this->time_functions_[i];
|
||||
if (tf->should_run(now)) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
const char *type =
|
||||
tf->type == TimeFunction::INTERVAL ? "interval" : (tf->type == TimeFunction::TIMEOUT ? "timeout" : "defer");
|
||||
ESP_LOGVV(TAG, "Running %s '%s':%u with interval=%u last_execution=%u (now=%u)", type, tf->name.c_str(), i,
|
||||
tf->interval, tf->last_execution, now);
|
||||
#endif
|
||||
|
||||
tf->f();
|
||||
// The vector might have reallocated due to new items
|
||||
tf = &this->time_functions_[i];
|
||||
|
||||
if (tf->type == TimeFunction::INTERVAL && tf->interval != 0) {
|
||||
const uint32_t amount = (now - tf->last_execution) / tf->interval;
|
||||
tf->last_execution += (amount * tf->interval);
|
||||
} else if (tf->type == TimeFunction::DEFER || tf->type == TimeFunction::TIMEOUT) {
|
||||
tf->remove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->time_functions_.erase(std::remove_if(this->time_functions_.begin(), this->time_functions_.end(),
|
||||
[](const TimeFunction &tf) -> bool { return tf.remove; }),
|
||||
this->time_functions_.end());
|
||||
}
|
||||
void Component::setup_internal_() {
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= COMPONENT_STATE_SETUP;
|
||||
}
|
||||
void Component::mark_failed() {
|
||||
ESP_LOGE(TAG, "Component was marked as failed.");
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= COMPONENT_STATE_FAILED;
|
||||
this->status_set_error();
|
||||
}
|
||||
void Component::defer(std::function<void()> &&f) { this->defer("", std::move(f)); } // NOLINT
|
||||
bool Component::cancel_defer(const std::string &name) { // NOLINT
|
||||
return this->cancel_time_function_(name, TimeFunction::DEFER);
|
||||
}
|
||||
void Component::defer(const std::string &name, std::function<void()> &&f) { // NOLINT
|
||||
if (!name.empty()) {
|
||||
this->cancel_defer(name);
|
||||
}
|
||||
struct TimeFunction function = {
|
||||
.name = name,
|
||||
.type = TimeFunction::DEFER,
|
||||
.interval = 0,
|
||||
.last_execution = 0,
|
||||
.f = std::move(f),
|
||||
.remove = false,
|
||||
};
|
||||
this->time_functions_.push_back(function);
|
||||
}
|
||||
void Component::set_timeout(uint32_t timeout, std::function<void()> &&f) { // NOLINT
|
||||
this->set_timeout("", timeout, std::move(f));
|
||||
}
|
||||
void Component::set_interval(uint32_t interval, std::function<void()> &&f) { // NOLINT
|
||||
this->set_interval("", interval, std::move(f));
|
||||
}
|
||||
bool Component::is_failed() { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; }
|
||||
bool Component::can_proceed() { return true; }
|
||||
bool Component::status_has_warning() { return this->component_state_ & STATUS_LED_WARNING; }
|
||||
bool Component::status_has_error() { return this->component_state_ & STATUS_LED_ERROR; }
|
||||
void Component::status_set_warning() {
|
||||
this->component_state_ |= STATUS_LED_WARNING;
|
||||
App.app_state_ |= STATUS_LED_WARNING;
|
||||
}
|
||||
void Component::status_set_error() {
|
||||
this->component_state_ |= STATUS_LED_ERROR;
|
||||
App.app_state_ |= STATUS_LED_ERROR;
|
||||
}
|
||||
void Component::status_clear_warning() { this->component_state_ &= ~STATUS_LED_WARNING; }
|
||||
void Component::status_clear_error() { this->component_state_ &= ~STATUS_LED_ERROR; }
|
||||
void Component::status_momentary_warning(const std::string &name, uint32_t length) {
|
||||
this->status_set_warning();
|
||||
this->set_timeout(name, length, [this]() { this->status_clear_warning(); });
|
||||
}
|
||||
void Component::status_momentary_error(const std::string &name, uint32_t length) {
|
||||
this->status_set_error();
|
||||
this->set_timeout(name, length, [this]() { this->status_clear_error(); });
|
||||
}
|
||||
void Component::dump_config() {}
|
||||
float Component::get_actual_setup_priority() const {
|
||||
return this->setup_priority_override_.value_or(this->get_setup_priority());
|
||||
}
|
||||
void Component::set_setup_priority(float priority) { this->setup_priority_override_ = priority; }
|
||||
|
||||
PollingComponent::PollingComponent(uint32_t update_interval) : Component(), update_interval_(update_interval) {}
|
||||
|
||||
void PollingComponent::call_setup() {
|
||||
// Call component internal setup.
|
||||
this->setup_internal_();
|
||||
|
||||
// Let the polling component subclass setup their HW.
|
||||
this->setup();
|
||||
|
||||
// Register interval.
|
||||
this->set_interval("update", this->get_update_interval(), [this]() { this->update(); });
|
||||
}
|
||||
|
||||
uint32_t PollingComponent::get_update_interval() const { return this->update_interval_; }
|
||||
void PollingComponent::set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
|
||||
|
||||
const std::string &Nameable::get_name() const { return this->name_; }
|
||||
void Nameable::set_name(const std::string &name) {
|
||||
this->name_ = name;
|
||||
this->calc_object_id_();
|
||||
}
|
||||
Nameable::Nameable(const std::string &name) : name_(name) { this->calc_object_id_(); }
|
||||
|
||||
const std::string &Nameable::get_object_id() { return this->object_id_; }
|
||||
bool Nameable::is_internal() const { return this->internal_; }
|
||||
void Nameable::set_internal(bool internal) { this->internal_ = internal; }
|
||||
void Nameable::calc_object_id_() {
|
||||
this->object_id_ = sanitize_string_whitelist(to_lowercase_underscore(this->name_), HOSTNAME_CHARACTER_WHITELIST);
|
||||
// FNV-1 hash
|
||||
this->object_id_hash_ = fnv1_hash(this->object_id_);
|
||||
}
|
||||
uint32_t Nameable::get_object_id_hash() { return this->object_id_hash_; }
|
||||
|
||||
bool Component::TimeFunction::should_run(uint32_t now) const {
|
||||
if (this->remove)
|
||||
return false;
|
||||
if (this->type == DEFER)
|
||||
return true;
|
||||
return this->interval != 4294967295UL && now - this->last_execution > this->interval;
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/optional.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/** Default setup priorities for components of different types.
|
||||
*
|
||||
* Components should return one of these setup priorities in get_setup_priority.
|
||||
*/
|
||||
namespace setup_priority {
|
||||
|
||||
/// For communication buses like i2c/spi
|
||||
extern const float BUS;
|
||||
/// For components that represent GPIO pins like PCF8573
|
||||
extern const float IO;
|
||||
/// For components that deal with hardware and are very important like GPIO switch
|
||||
extern const float HARDWARE;
|
||||
/// For components that import data from directly connected sensors like DHT.
|
||||
extern const float DATA;
|
||||
/// Alias for DATA (here for compatability reasons)
|
||||
extern const float HARDWARE_LATE;
|
||||
/// For components that use data from sensors like displays
|
||||
extern const float PROCESSOR;
|
||||
extern const float WIFI;
|
||||
/// For components that should be initialized after WiFi is connected.
|
||||
extern const float AFTER_WIFI;
|
||||
/// For components that should be initialized after a data connection (API/MQTT) is connected.
|
||||
extern const float AFTER_CONNECTION;
|
||||
/// For components that should be initialized at the very end of the setup process.
|
||||
extern const float LATE;
|
||||
|
||||
} // namespace setup_priority
|
||||
|
||||
#define LOG_UPDATE_INTERVAL(this) \
|
||||
if (this->get_update_interval() < 100) { \
|
||||
ESP_LOGCONFIG(TAG, " Update Interval: %.3fs", this->get_update_interval() / 1000.0f); \
|
||||
} else { \
|
||||
ESP_LOGCONFIG(TAG, " Update Interval: %.1fs", this->get_update_interval() / 1000.0f); \
|
||||
}
|
||||
|
||||
extern const uint32_t COMPONENT_STATE_MASK;
|
||||
extern const uint32_t COMPONENT_STATE_CONSTRUCTION;
|
||||
extern const uint32_t COMPONENT_STATE_SETUP;
|
||||
extern const uint32_t COMPONENT_STATE_LOOP;
|
||||
extern const uint32_t COMPONENT_STATE_FAILED;
|
||||
extern const uint32_t STATUS_LED_MASK;
|
||||
extern const uint32_t STATUS_LED_OK;
|
||||
extern const uint32_t STATUS_LED_WARNING;
|
||||
extern const uint32_t STATUS_LED_ERROR;
|
||||
|
||||
class Component {
|
||||
public:
|
||||
/** Where the component's initialization should happen.
|
||||
*
|
||||
* Analogous to Arduino's setup(). This method is guaranteed to only be called once.
|
||||
* Defaults to doing nothing.
|
||||
*/
|
||||
virtual void setup();
|
||||
|
||||
/** This method will be called repeatedly.
|
||||
*
|
||||
* Analogous to Arduino's loop(). setup() is guaranteed to be called before this.
|
||||
* Defaults to doing nothing.
|
||||
*/
|
||||
virtual void loop();
|
||||
|
||||
virtual void dump_config();
|
||||
|
||||
/** priority of setup(). higher -> executed earlier
|
||||
*
|
||||
* Defaults to 0.
|
||||
*
|
||||
* @return The setup priority of this component
|
||||
*/
|
||||
virtual float get_setup_priority() const;
|
||||
|
||||
float get_actual_setup_priority() const;
|
||||
|
||||
void set_setup_priority(float priority);
|
||||
|
||||
/** priority of loop(). higher -> executed earlier
|
||||
*
|
||||
* Defaults to 0.
|
||||
*
|
||||
* @return The loop priority of this component
|
||||
*/
|
||||
virtual float get_loop_priority() const;
|
||||
|
||||
/** Public loop() functions. These will be called by the Application instance.
|
||||
*
|
||||
* Note: This should normally not be overriden, unless you know what you're doing.
|
||||
* They're basically to make creating custom components easier. For example the
|
||||
* SensorComponent can override these methods to not have the user call some super
|
||||
* methods within their custom sensors. These methods should ALWAYS call the loop_internal()
|
||||
* and setup_internal() methods.
|
||||
*
|
||||
* Basically, it handles stuff like interval/timeout functions and eventually calls loop().
|
||||
*/
|
||||
virtual void call_loop();
|
||||
virtual void call_setup();
|
||||
|
||||
virtual void on_shutdown() {}
|
||||
virtual void on_safe_shutdown() {}
|
||||
|
||||
uint32_t get_component_state() const;
|
||||
|
||||
/** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called.
|
||||
*
|
||||
* This might be useful if a component wants to indicate that a connection to its peripheral failed.
|
||||
* For example, i2c based components can check if the remote device is responding and otherwise
|
||||
* mark the component as failed. Eventually this will also enable smart status LEDs.
|
||||
*/
|
||||
virtual void mark_failed();
|
||||
|
||||
bool is_failed();
|
||||
|
||||
virtual bool can_proceed();
|
||||
|
||||
bool status_has_warning();
|
||||
|
||||
bool status_has_error();
|
||||
|
||||
void status_set_warning();
|
||||
|
||||
void status_set_error();
|
||||
|
||||
void status_clear_warning();
|
||||
|
||||
void status_clear_error();
|
||||
|
||||
void status_momentary_warning(const std::string &name, uint32_t length = 5000);
|
||||
|
||||
void status_momentary_error(const std::string &name, uint32_t length = 5000);
|
||||
|
||||
protected:
|
||||
/** Set an interval function with a unique name. Empty name means no cancelling possible.
|
||||
*
|
||||
* This will call f every interval ms. Can be cancelled via CancelInterval().
|
||||
* Similar to javascript's setInterval().
|
||||
*
|
||||
* IMPORTANT: Do not rely on this having correct timing. This is only called from
|
||||
* loop() and therefore can be significantly delay. If you need exact timing please
|
||||
* use hardware timers.
|
||||
*
|
||||
* @param name The identifier for this interval function.
|
||||
* @param interval The interval in ms.
|
||||
* @param f The function (or lambda) that should be called
|
||||
*
|
||||
* @see cancel_interval()
|
||||
*/
|
||||
void set_interval(const std::string &name, uint32_t interval, std::function<void()> &&f); // NOLINT
|
||||
|
||||
void set_interval(uint32_t interval, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Cancel an interval function.
|
||||
*
|
||||
* @param name The identifier for this interval function.
|
||||
* @return Whether an interval functions was deleted.
|
||||
*/
|
||||
bool cancel_interval(const std::string &name); // NOLINT
|
||||
|
||||
void set_timeout(uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Set a timeout function with a unique name.
|
||||
*
|
||||
* Similar to javascript's setTimeout(). Empty name means no cancelling possible.
|
||||
*
|
||||
* IMPORTANT: Do not rely on this having correct timing. This is only called from
|
||||
* loop() and therefore can be significantly delay. If you need exact timing please
|
||||
* use hardware timers.
|
||||
*
|
||||
* @param name The identifier for this timeout function.
|
||||
* @param timeout The timeout in ms.
|
||||
* @param f The function (or lambda) that should be called
|
||||
*
|
||||
* @see cancel_timeout()
|
||||
*/
|
||||
void set_timeout(const std::string &name, uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Cancel a timeout function.
|
||||
*
|
||||
* @param name The identifier for this timeout function.
|
||||
* @return Whether a timeout functions was deleted.
|
||||
*/
|
||||
bool cancel_timeout(const std::string &name); // NOLINT
|
||||
|
||||
/** Defer a callback to the next loop() call.
|
||||
*
|
||||
* If name is specified and a defer() object with the same name exists, the old one is first removed.
|
||||
*
|
||||
* @param name The name of the defer function.
|
||||
* @param f The callback.
|
||||
*/
|
||||
void defer(const std::string &name, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/// Defer a callback to the next loop() call.
|
||||
void defer(std::function<void()> &&f); // NOLINT
|
||||
|
||||
/// Cancel a defer callback using the specified name, name must not be empty.
|
||||
bool cancel_defer(const std::string &name); // NOLINT
|
||||
|
||||
void loop_internal_();
|
||||
void setup_internal_();
|
||||
|
||||
/// Internal struct for storing timeout/interval functions.
|
||||
struct TimeFunction {
|
||||
std::string name; ///< The name/id of this TimeFunction.
|
||||
enum Type { TIMEOUT, INTERVAL, DEFER } type; ///< The type of this TimeFunction. Either TIMEOUT, INTERVAL or DEFER.
|
||||
uint32_t interval; ///< The interval/timeout of this function.
|
||||
/// The last execution for interval functions and the time, SetInterval was called, for timeout functions.
|
||||
uint32_t last_execution;
|
||||
std::function<void()> f; ///< The function (or callback) itself.
|
||||
bool remove;
|
||||
|
||||
bool should_run(uint32_t now) const;
|
||||
};
|
||||
|
||||
/// Cancel an only time function. If name is empty, won't do anything.
|
||||
bool cancel_time_function_(const std::string &name, TimeFunction::Type type);
|
||||
|
||||
/** Storage for interval/timeout functions.
|
||||
*
|
||||
* Intentionally a vector despite its map-like nature, because of the
|
||||
* memory overhead.
|
||||
*/
|
||||
std::vector<TimeFunction> time_functions_;
|
||||
|
||||
uint32_t component_state_{0x0000}; ///< State of this component.
|
||||
optional<float> setup_priority_override_;
|
||||
};
|
||||
|
||||
/** This class simplifies creating components that periodically check a state.
|
||||
*
|
||||
* You basically just need to implement the update() function, it will be called every update_interval ms
|
||||
* after startup. Note that this class cannot guarantee a correct timing, as it's not using timers, just
|
||||
* a software polling feature with set_interval() from Component.
|
||||
*/
|
||||
class PollingComponent : public Component {
|
||||
public:
|
||||
/** Initialize this polling component with the given update interval in ms.
|
||||
*
|
||||
* @param update_interval The update interval in ms.
|
||||
*/
|
||||
explicit PollingComponent(uint32_t update_interval);
|
||||
|
||||
/** Manually set the update interval in ms for this polling object.
|
||||
*
|
||||
* Override this if you want to do some validation for the update interval.
|
||||
*
|
||||
* @param update_interval The update interval in ms.
|
||||
*/
|
||||
virtual void set_update_interval(uint32_t update_interval);
|
||||
|
||||
// ========== OVERRIDE METHODS ==========
|
||||
// (You'll only need this when creating your own custom sensor)
|
||||
virtual void update() = 0;
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
void call_setup() override;
|
||||
|
||||
/// Get the update interval in ms of this sensor
|
||||
virtual uint32_t get_update_interval() const;
|
||||
|
||||
protected:
|
||||
uint32_t update_interval_;
|
||||
};
|
||||
|
||||
/// Helper class that enables naming of objects so that it doesn't have to be re-implement every time.
|
||||
class Nameable {
|
||||
public:
|
||||
explicit Nameable(const std::string &name);
|
||||
const std::string &get_name() const;
|
||||
void set_name(const std::string &name);
|
||||
/// Get the sanitized name of this nameable as an ID. Caching it internally.
|
||||
const std::string &get_object_id();
|
||||
uint32_t get_object_id_hash();
|
||||
|
||||
bool is_internal() const;
|
||||
void set_internal(bool internal);
|
||||
|
||||
protected:
|
||||
virtual uint32_t hash_base() = 0;
|
||||
|
||||
void calc_object_id_();
|
||||
|
||||
std::string name_;
|
||||
std::string object_id_;
|
||||
uint32_t object_id_hash_;
|
||||
bool internal_{false};
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "controller.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
void Controller::setup_controller() {
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
for (auto *obj : App.get_binary_sensors()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](bool state) { this->on_binary_sensor_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
for (auto *obj : App.get_fans()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_fan_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
for (auto *obj : App.get_lights()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_new_remote_values_callback([this, obj]() { this->on_light_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
for (auto *obj : App.get_sensors()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](float state) { this->on_sensor_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
for (auto *obj : App.get_switches()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](bool state) { this->on_switch_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
for (auto *obj : App.get_covers()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_cover_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
for (auto *obj : App.get_text_sensors()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](std::string state) { this->on_text_sensor_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
for (auto *obj : App.get_climates()) {
|
||||
if (!obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_climate_update(obj); });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
#include "esphome/components/fan/fan_state.h"
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
#include "esphome/components/light/light_state.h"
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
#include "esphome/components/cover/cover.h"
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
void setup_controller();
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state){};
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
virtual void on_fan_update(fan::FanState *obj){};
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
virtual void on_light_update(light::LightState *obj){};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
virtual void on_sensor_update(sensor::Sensor *obj, float state){};
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
virtual void on_switch_update(switch_::Switch *obj, bool state){};
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
virtual void on_cover_update(cover::Cover *obj){};
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
virtual void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state){};
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void on_climate_update(climate::Climate *obj){};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
// This file is auto-generated! Do not edit!
|
||||
|
||||
#define ESPHOME_VERSION "dev"
|
||||
|
||||
#define ESPHOME_LOG_LEVEL 6
|
||||
#define USE_API
|
||||
#define USE_LOGGER
|
||||
#define USE_BINARY_SENSOR
|
||||
#define USE_SENSOR
|
||||
#define USE_SWITCH
|
||||
#define USE_WIFI
|
||||
#define USE_STATUS_LED
|
||||
#define USE_TEXT_SENSOR
|
||||
#define USE_FAN
|
||||
#define USE_COVER
|
||||
#define USE_LIGHT
|
||||
#define USE_CLIMATE
|
||||
#define USE_MQTT
|
||||
#define USE_POWER_SUPPLY
|
||||
#define USE_HOMEASSISTANT_TIME
|
||||
#define USE_JSON
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define USE_ESP32_CAMERA
|
||||
#endif
|
||||
#define USE_TIME
|
||||
#define USE_DEEP_SLEEP
|
||||
@@ -0,0 +1,280 @@
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
extern "C" {
|
||||
typedef struct { // NOLINT
|
||||
void *interruptInfo; // NOLINT
|
||||
void *functionInfo; // NOLINT
|
||||
} ArgStructure;
|
||||
|
||||
void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void (*)(void *), void *fp, // NOLINT
|
||||
int mode);
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *TAG = "esphal";
|
||||
|
||||
GPIOPin::GPIOPin(uint8_t pin, uint8_t mode, bool inverted)
|
||||
: pin_(pin),
|
||||
mode_(mode),
|
||||
inverted_(inverted),
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
gpio_read_(pin < 16 ? &GPI : &GP16I),
|
||||
gpio_mask_(pin < 16 ? (1UL << pin) : 1)
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
gpio_set_(pin < 32 ? &GPIO.out_w1ts : &GPIO.out1_w1ts.val),
|
||||
gpio_clear_(pin < 32 ? &GPIO.out_w1tc : &GPIO.out1_w1tc.val),
|
||||
gpio_read_(pin < 32 ? &GPIO.in : &GPIO.in1.val),
|
||||
gpio_mask_(pin < 32 ? (1UL << pin) : (1UL << (pin - 32)))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
const char *GPIOPin::get_pin_mode_name() const {
|
||||
const char *mode_s;
|
||||
switch (this->mode_) {
|
||||
case INPUT:
|
||||
mode_s = "INPUT";
|
||||
break;
|
||||
case OUTPUT:
|
||||
mode_s = "OUTPUT";
|
||||
break;
|
||||
case INPUT_PULLUP:
|
||||
mode_s = "INPUT_PULLUP";
|
||||
break;
|
||||
case OUTPUT_OPEN_DRAIN:
|
||||
mode_s = "OUTPUT_OPEN_DRAIN";
|
||||
break;
|
||||
case SPECIAL:
|
||||
mode_s = "SPECIAL";
|
||||
break;
|
||||
case FUNCTION_1:
|
||||
mode_s = "FUNCTION_1";
|
||||
break;
|
||||
case FUNCTION_2:
|
||||
mode_s = "FUNCTION_2";
|
||||
break;
|
||||
case FUNCTION_3:
|
||||
mode_s = "FUNCTION_3";
|
||||
break;
|
||||
case FUNCTION_4:
|
||||
mode_s = "FUNCTION_4";
|
||||
break;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case PULLUP:
|
||||
mode_s = "PULLUP";
|
||||
break;
|
||||
case PULLDOWN:
|
||||
mode_s = "PULLDOWN";
|
||||
break;
|
||||
case INPUT_PULLDOWN:
|
||||
mode_s = "INPUT_PULLDOWN";
|
||||
break;
|
||||
case OPEN_DRAIN:
|
||||
mode_s = "OPEN_DRAIN";
|
||||
break;
|
||||
case FUNCTION_5:
|
||||
mode_s = "FUNCTION_5";
|
||||
break;
|
||||
case FUNCTION_6:
|
||||
mode_s = "FUNCTION_6";
|
||||
break;
|
||||
case ANALOG:
|
||||
mode_s = "ANALOG";
|
||||
break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
case FUNCTION_0:
|
||||
mode_s = "FUNCTION_0";
|
||||
break;
|
||||
case WAKEUP_PULLUP:
|
||||
mode_s = "WAKEUP_PULLUP";
|
||||
break;
|
||||
case WAKEUP_PULLDOWN:
|
||||
mode_s = "WAKEUP_PULLDOWN";
|
||||
break;
|
||||
case INPUT_PULLDOWN_16:
|
||||
mode_s = "INPUT_PULLDOWN_16";
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
mode_s = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
return mode_s;
|
||||
}
|
||||
|
||||
unsigned char GPIOPin::get_pin() const { return this->pin_; }
|
||||
unsigned char GPIOPin::get_mode() const { return this->mode_; }
|
||||
|
||||
bool GPIOPin::is_inverted() const { return this->inverted_; }
|
||||
void GPIOPin::setup() { this->pin_mode(this->mode_); }
|
||||
bool ICACHE_RAM_ATTR HOT GPIOPin::digital_read() {
|
||||
return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_;
|
||||
}
|
||||
bool ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_read() {
|
||||
return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_;
|
||||
}
|
||||
void ICACHE_RAM_ATTR HOT GPIOPin::digital_write(bool value) {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (this->pin_ != 16) {
|
||||
if (value != this->inverted_) {
|
||||
GPOS = this->gpio_mask_;
|
||||
} else {
|
||||
GPOC = this->gpio_mask_;
|
||||
}
|
||||
} else {
|
||||
if (value != this->inverted_) {
|
||||
GP16O |= 1;
|
||||
} else {
|
||||
GP16O &= ~1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (value != this->inverted_) {
|
||||
(*this->gpio_set_) = this->gpio_mask_;
|
||||
} else {
|
||||
(*this->gpio_clear_) = this->gpio_mask_;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void ISRInternalGPIOPin::digital_write(bool value) {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (this->pin_ != 16) {
|
||||
if (value != this->inverted_) {
|
||||
GPOS = this->gpio_mask_;
|
||||
} else {
|
||||
GPOC = this->gpio_mask_;
|
||||
}
|
||||
} else {
|
||||
if (value != this->inverted_) {
|
||||
GP16O |= 1;
|
||||
} else {
|
||||
GP16O &= ~1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (value != this->inverted_) {
|
||||
(*this->gpio_set_) = this->gpio_mask_;
|
||||
} else {
|
||||
(*this->gpio_clear_) = this->gpio_mask_;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ISRInternalGPIOPin::ISRInternalGPIOPin(uint8_t pin,
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set,
|
||||
#endif
|
||||
volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted)
|
||||
: pin_(pin),
|
||||
inverted_(inverted),
|
||||
gpio_read_(gpio_read),
|
||||
gpio_mask_(gpio_mask)
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
,
|
||||
gpio_clear_(gpio_clear),
|
||||
gpio_set_(gpio_set)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
void ICACHE_RAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, this->gpio_mask_);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (this->pin_ < 32) {
|
||||
GPIO.status_w1tc = this->gpio_mask_;
|
||||
} else {
|
||||
GPIO.status1_w1tc.intr_st = this->gpio_mask_;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR HOT GPIOPin::pin_mode(uint8_t mode) {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (this->pin_ == 16 && mode == INPUT_PULLUP) {
|
||||
// pullups are not available on GPIO16, manually override with
|
||||
// input mode.
|
||||
pinMode(16, INPUT);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
pinMode(this->pin_, mode);
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
struct ESPHomeInterruptFuncInfo {
|
||||
void (*func)(void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
void ICACHE_RAM_ATTR interrupt_handler(void *arg) {
|
||||
ArgStructure *as = static_cast<ArgStructure *>(arg);
|
||||
auto *info = static_cast<ESPHomeInterruptFuncInfo *>(as->functionInfo);
|
||||
info->func(info->arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const {
|
||||
if (this->inverted_) {
|
||||
if (mode == RISING) {
|
||||
mode = FALLING;
|
||||
} else if (mode == FALLING) {
|
||||
mode = RISING;
|
||||
}
|
||||
}
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
ArgStructure *as = new ArgStructure;
|
||||
as->interruptInfo = nullptr;
|
||||
|
||||
as->functionInfo = new ESPHomeInterruptFuncInfo{
|
||||
.func = func,
|
||||
.arg = arg,
|
||||
};
|
||||
|
||||
__attachInterruptArg(this->pin_, interrupt_handler, as, mode);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// work around issue https://github.com/espressif/arduino-esp32/pull/1776 in arduino core
|
||||
// yet again proves how horrible code is there :( - how could that have been accepted...
|
||||
auto *attach = reinterpret_cast<void (*)(uint8_t, void (*)(void *), void *, int)>(attachInterruptArg);
|
||||
attach(this->pin_, func, arg, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
ISRInternalGPIOPin *GPIOPin::to_isr() const {
|
||||
return new ISRInternalGPIOPin(this->pin_,
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
this->gpio_clear_, this->gpio_set_,
|
||||
#endif
|
||||
this->gpio_read_, this->gpio_mask_, this->inverted_);
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#ifdef ARDUINO_ESP8266_RELEASE_2_3_0
|
||||
// Fix 2.3.0 std missing memchr
|
||||
extern "C" {
|
||||
void *memchr(const void *s, int c, size_t n) {
|
||||
if (n == 0)
|
||||
return nullptr;
|
||||
const uint8_t *p = reinterpret_cast<const uint8_t *>(s);
|
||||
do {
|
||||
if (*p++ == c)
|
||||
return const_cast<void *>(reinterpret_cast<const void *>(p - 1));
|
||||
} while (--n != 0);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <esp32-hal.h>
|
||||
#endif
|
||||
// Fix some arduino defs
|
||||
#ifdef round
|
||||
#undef round
|
||||
#endif
|
||||
#ifdef bool
|
||||
#undef bool
|
||||
#endif
|
||||
#ifdef true
|
||||
#undef true
|
||||
#endif
|
||||
#ifdef false
|
||||
#undef false
|
||||
#endif
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#ifdef abs
|
||||
#undef abs
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
#define LOG_PIN(prefix, pin) \
|
||||
if ((pin) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, prefix LOG_PIN_PATTERN, LOG_PIN_ARGS(pin)); \
|
||||
}
|
||||
#define LOG_PIN_PATTERN "GPIO%u (Mode: %s%s)"
|
||||
#define LOG_PIN_ARGS(pin) (pin)->get_pin(), (pin)->get_pin_mode_name(), ((pin)->is_inverted() ? ", INVERTED" : "")
|
||||
|
||||
/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
|
||||
class ISRInternalGPIOPin {
|
||||
public:
|
||||
ISRInternalGPIOPin(uint8_t pin,
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set,
|
||||
#endif
|
||||
volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted);
|
||||
bool digital_read();
|
||||
void digital_write(bool value);
|
||||
void clear_interrupt();
|
||||
|
||||
protected:
|
||||
const uint8_t pin_;
|
||||
const bool inverted_;
|
||||
volatile uint32_t *const gpio_read_;
|
||||
const uint32_t gpio_mask_;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
volatile uint32_t *const gpio_clear_;
|
||||
volatile uint32_t *const gpio_set_;
|
||||
#endif
|
||||
};
|
||||
|
||||
/** A high-level abstraction class that can expose a pin together with useful options like pinMode.
|
||||
*
|
||||
* Set the parameters for this at construction time and use setup() to apply them. The inverted parameter will
|
||||
* automatically invert the input/output for you.
|
||||
*
|
||||
* Use read_value() and write_value() to use digitalRead() and digitalWrite(), respectively.
|
||||
*/
|
||||
class GPIOPin {
|
||||
public:
|
||||
/** Construct the GPIOPin instance.
|
||||
*
|
||||
* @param pin The GPIO pin number of this instance.
|
||||
* @param mode The Arduino pinMode that this pin should be put into at setup().
|
||||
* @param inverted Whether all digitalRead/digitalWrite calls should be inverted.
|
||||
*/
|
||||
GPIOPin(uint8_t pin, uint8_t mode, bool inverted = false);
|
||||
|
||||
/// Setup the pin mode.
|
||||
virtual void setup();
|
||||
/// Read the binary value from this pin using digitalRead (and inverts automatically).
|
||||
virtual bool digital_read();
|
||||
/// Write the binary value to this pin using digitalWrite (and inverts automatically).
|
||||
virtual void digital_write(bool value);
|
||||
/// Set the pin mode
|
||||
virtual void pin_mode(uint8_t mode);
|
||||
|
||||
/// Get the GPIO pin number.
|
||||
uint8_t get_pin() const;
|
||||
const char *get_pin_mode_name() const;
|
||||
/// Get the pinMode of this pin.
|
||||
uint8_t get_mode() const;
|
||||
/// Return whether this pin shall be treated as inverted. (for example active-low)
|
||||
bool is_inverted() const;
|
||||
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, int mode) const;
|
||||
|
||||
ISRInternalGPIOPin *to_isr() const;
|
||||
|
||||
protected:
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, int mode) const;
|
||||
|
||||
const uint8_t pin_;
|
||||
const uint8_t mode_;
|
||||
const bool inverted_;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
volatile uint32_t *const gpio_set_;
|
||||
volatile uint32_t *const gpio_clear_;
|
||||
#endif
|
||||
volatile uint32_t *const gpio_read_;
|
||||
const uint32_t gpio_mask_;
|
||||
};
|
||||
|
||||
template<typename T> void GPIOPin::attach_interrupt(void (*func)(T *), T *arg, int mode) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, mode);
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,307 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#include <Esp.h>
|
||||
#endif
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *TAG = "helpers";
|
||||
|
||||
std::string get_mac_address() {
|
||||
char tmp[20];
|
||||
uint8_t mac[6];
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_efuse_mac_get_default(mac);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
WiFi.macAddress(mac);
|
||||
#endif
|
||||
sprintf(tmp, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
std::string get_mac_address_pretty() {
|
||||
char tmp[20];
|
||||
uint8_t mac[6];
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_efuse_mac_get_default(mac);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
WiFi.macAddress(mac);
|
||||
#endif
|
||||
sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
std::string generate_hostname(const std::string &base) { return base + std::string("-") + get_mac_address(); }
|
||||
|
||||
uint32_t random_uint32() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
return esp_random();
|
||||
#else
|
||||
return os_random();
|
||||
#endif
|
||||
}
|
||||
|
||||
double random_double() { return random_uint32() / double(UINT32_MAX); }
|
||||
|
||||
float random_float() { return float(random_double()); }
|
||||
|
||||
static uint32_t fast_random_seed = 0;
|
||||
|
||||
void fast_random_set_seed(uint32_t seed) { fast_random_seed = seed; }
|
||||
uint32_t fast_random_32() {
|
||||
fast_random_seed = (fast_random_seed * 2654435769ULL) + 40503ULL;
|
||||
return fast_random_seed;
|
||||
}
|
||||
uint16_t fast_random_16() {
|
||||
uint32_t rand32 = fast_random_32();
|
||||
return (rand32 & 0xFFFF) + (rand32 >> 16);
|
||||
}
|
||||
uint8_t fast_random_8() {
|
||||
uint8_t rand32 = fast_random_32();
|
||||
return (rand32 & 0xFF) + ((rand32 >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
float gamma_correct(float value, float gamma) {
|
||||
if (value <= 0.0f)
|
||||
return 0.0f;
|
||||
if (gamma <= 0.0f)
|
||||
return value;
|
||||
|
||||
return powf(value, gamma);
|
||||
}
|
||||
std::string to_lowercase_underscore(std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
std::replace(s.begin(), s.end(), ' ', '_');
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string sanitize_string_whitelist(const std::string &s, const std::string &whitelist) {
|
||||
std::string out(s);
|
||||
out.erase(std::remove_if(out.begin(), out.end(),
|
||||
[&out, &whitelist](const char &c) { return whitelist.find(c) == std::string::npos; }),
|
||||
out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string sanitize_hostname(const std::string &hostname) {
|
||||
std::string s = sanitize_string_whitelist(hostname, HOSTNAME_CHARACTER_WHITELIST);
|
||||
return truncate_string(s, 63);
|
||||
}
|
||||
|
||||
std::string truncate_string(const std::string &s, size_t length) {
|
||||
if (s.length() > length)
|
||||
return s.substr(0, length);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
|
||||
auto multiplier = float(pow10(accuracy_decimals));
|
||||
float value_rounded = roundf(value * multiplier) / multiplier;
|
||||
char tmp[32]; // should be enough, but we should maybe improve this at some point.
|
||||
dtostrf(value_rounded, 0, uint8_t(std::max(0, int(accuracy_decimals))), tmp);
|
||||
return std::string(tmp);
|
||||
}
|
||||
std::string uint64_to_string(uint64_t num) {
|
||||
char buffer[17];
|
||||
auto *address16 = reinterpret_cast<uint16_t *>(&num);
|
||||
snprintf(buffer, sizeof(buffer), "%04X%04X%04X%04X", address16[3], address16[2], address16[1], address16[0]);
|
||||
return std::string(buffer);
|
||||
}
|
||||
std::string uint32_to_string(uint32_t num) {
|
||||
char buffer[9];
|
||||
auto *address16 = reinterpret_cast<uint16_t *>(&num);
|
||||
snprintf(buffer, sizeof(buffer), "%04X%04X", address16[1], address16[0]);
|
||||
return std::string(buffer);
|
||||
}
|
||||
static char *global_json_build_buffer = nullptr;
|
||||
static size_t global_json_build_buffer_size = 0;
|
||||
|
||||
void reserve_global_json_build_buffer(size_t required_size) {
|
||||
if (global_json_build_buffer_size == 0 || global_json_build_buffer_size < required_size) {
|
||||
delete[] global_json_build_buffer;
|
||||
global_json_build_buffer_size = std::max(required_size, global_json_build_buffer_size * 2);
|
||||
|
||||
size_t remainder = global_json_build_buffer_size % 16U;
|
||||
if (remainder != 0)
|
||||
global_json_build_buffer_size += 16 - remainder;
|
||||
|
||||
global_json_build_buffer = new char[global_json_build_buffer_size];
|
||||
}
|
||||
}
|
||||
|
||||
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
|
||||
if (on == nullptr && strcasecmp(str, "on") == 0)
|
||||
return PARSE_ON;
|
||||
if (on != nullptr && strcasecmp(str, on) == 0)
|
||||
return PARSE_ON;
|
||||
if (off == nullptr && strcasecmp(str, "off") == 0)
|
||||
return PARSE_OFF;
|
||||
if (off != nullptr && strcasecmp(str, off) == 0)
|
||||
return PARSE_OFF;
|
||||
if (strcasecmp(str, "toggle") == 0)
|
||||
return PARSE_TOGGLE;
|
||||
|
||||
return PARSE_NONE;
|
||||
}
|
||||
|
||||
const char *HOSTNAME_CHARACTER_WHITELIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
||||
|
||||
void disable_interrupts() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
portDISABLE_INTERRUPTS();
|
||||
#else
|
||||
noInterrupts();
|
||||
#endif
|
||||
}
|
||||
void enable_interrupts() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
portENABLE_INTERRUPTS();
|
||||
#else
|
||||
interrupts();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t crc8(uint8_t *data, uint8_t len) {
|
||||
uint8_t crc = 0;
|
||||
|
||||
while ((len--) != 0u) {
|
||||
uint8_t inbyte = *data++;
|
||||
for (uint8_t i = 8; i != 0u; i--) {
|
||||
bool mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix)
|
||||
crc ^= 0x8C;
|
||||
inbyte >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
void delay_microseconds_accurate(uint32_t usec) {
|
||||
if (usec == 0)
|
||||
return;
|
||||
|
||||
if (usec <= 16383UL) {
|
||||
delayMicroseconds(usec);
|
||||
} else {
|
||||
delay(usec / 1000UL);
|
||||
delayMicroseconds(usec % 1000UL);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t reverse_bits_8(uint8_t x) {
|
||||
x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
|
||||
x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
|
||||
x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
uint16_t reverse_bits_16(uint16_t x) {
|
||||
return uint16_t(reverse_bits_8(x & 0xFF) << 8) | uint16_t(reverse_bits_8(x >> 8));
|
||||
}
|
||||
std::string to_string(const std::string &val) { return val; }
|
||||
std::string to_string(int val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%d", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(long val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%ld", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(long long val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%lld", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(unsigned val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%u", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(unsigned long val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%lu", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(unsigned long long val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%llu", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(float val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%f", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(double val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%f", val);
|
||||
return buf;
|
||||
}
|
||||
std::string to_string(long double val) {
|
||||
char buf[64];
|
||||
sprintf(buf, "%Lf", val);
|
||||
return buf;
|
||||
}
|
||||
optional<float> parse_float(const std::string &str) {
|
||||
char *end;
|
||||
float value = ::strtof(str.c_str(), &end);
|
||||
if (end == nullptr)
|
||||
return {};
|
||||
return value;
|
||||
}
|
||||
uint32_t fnv1_hash(const std::string &str) {
|
||||
uint32_t hash = 2166136261UL;
|
||||
for (char c : str) {
|
||||
hash *= 16777619UL;
|
||||
hash ^= c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
bool str_equals_case_insensitive(const std::string &a, const std::string &b) {
|
||||
return strcasecmp(a.c_str(), b.c_str()) == 0;
|
||||
}
|
||||
|
||||
template<uint32_t> uint32_t reverse_bits(uint32_t x) {
|
||||
return uint32_t(reverse_bits_16(x & 0xFFFF) << 16) | uint32_t(reverse_bits_16(x >> 16));
|
||||
}
|
||||
|
||||
static int high_freq_num_requests = 0;
|
||||
|
||||
void HighFrequencyLoopRequester::start() {
|
||||
if (this->started_)
|
||||
return;
|
||||
high_freq_num_requests++;
|
||||
this->started_ = true;
|
||||
}
|
||||
void HighFrequencyLoopRequester::stop() {
|
||||
if (!this->started_)
|
||||
return;
|
||||
high_freq_num_requests--;
|
||||
this->started_ = false;
|
||||
}
|
||||
bool HighFrequencyLoopRequester::is_high_frequency() { return high_freq_num_requests > 0; }
|
||||
|
||||
float clamp(float val, float min, float max) {
|
||||
if (min > max)
|
||||
std::swap(min, max);
|
||||
if (val < min)
|
||||
return min;
|
||||
if (val > max)
|
||||
return max;
|
||||
return val;
|
||||
}
|
||||
float lerp(float completion, float start, float end) { return start + (end - start) * completion; }
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,256 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "esphome/core/optional.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
|
||||
#ifdef CLANG_TIDY
|
||||
#undef ICACHE_RAM_ATTR
|
||||
#define ICACHE_RAM_ATTR
|
||||
#undef ICACHE_RODATA_ATTR
|
||||
#define ICACHE_RODATA_ATTR
|
||||
#endif
|
||||
|
||||
#define HOT __attribute__((hot))
|
||||
#define ESPDEPRECATED(msg) __attribute__((deprecated(msg)))
|
||||
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// The characters that are allowed in a hostname.
|
||||
extern const char *HOSTNAME_CHARACTER_WHITELIST;
|
||||
|
||||
/// Gets the MAC address as a string, this can be used as way to identify this ESP.
|
||||
std::string get_mac_address();
|
||||
|
||||
std::string get_mac_address_pretty();
|
||||
|
||||
std::string to_string(const std::string &val);
|
||||
std::string to_string(int val);
|
||||
std::string to_string(long val);
|
||||
std::string to_string(long long val);
|
||||
std::string to_string(unsigned val);
|
||||
std::string to_string(unsigned long val);
|
||||
std::string to_string(unsigned long long val);
|
||||
std::string to_string(float val);
|
||||
std::string to_string(double val);
|
||||
std::string to_string(long double val);
|
||||
optional<float> parse_float(const std::string &str);
|
||||
|
||||
/// Sanitize the hostname by removing characters that are not in the whitelist and truncating it to 63 chars.
|
||||
std::string sanitize_hostname(const std::string &hostname);
|
||||
|
||||
/// Truncate a string to a specific length
|
||||
std::string truncate_string(const std::string &s, size_t length);
|
||||
|
||||
/// Convert the string to lowercase_underscore.
|
||||
std::string to_lowercase_underscore(std::string s);
|
||||
|
||||
/// Compare string a to string b (ignoring case) and return whether they are equal.
|
||||
bool str_equals_case_insensitive(const std::string &a, const std::string &b);
|
||||
|
||||
class HighFrequencyLoopRequester {
|
||||
public:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
static bool is_high_frequency();
|
||||
|
||||
protected:
|
||||
bool started_{false};
|
||||
};
|
||||
|
||||
/** Clamp the value between min and max.
|
||||
*
|
||||
* @param val The value.
|
||||
* @param min The minimum value.
|
||||
* @param max The maximum value.
|
||||
* @return val clamped in between min and max.
|
||||
*/
|
||||
float clamp(float val, float min, float max);
|
||||
|
||||
/** Linearly interpolate between end start and end by completion.
|
||||
*
|
||||
* @tparam T The input/output typename.
|
||||
* @param start The start value.
|
||||
* @param end The end value.
|
||||
* @param completion The completion. 0 is start value, 1 is end value.
|
||||
* @return The linearly interpolated value.
|
||||
*/
|
||||
float lerp(float completion, float start, float end);
|
||||
|
||||
/// std::make_unique
|
||||
template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args &&... args) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/// Return a random 32 bit unsigned integer.
|
||||
uint32_t random_uint32();
|
||||
|
||||
/** Returns a random double between 0 and 1.
|
||||
*
|
||||
* Note: This function probably doesn't provide a truly uniform distribution.
|
||||
*/
|
||||
double random_double();
|
||||
|
||||
/// Returns a random float between 0 and 1. Essentially just casts random_double() to a float.
|
||||
float random_float();
|
||||
|
||||
void fast_random_set_seed(uint32_t seed);
|
||||
uint32_t fast_random_32();
|
||||
uint16_t fast_random_16();
|
||||
uint8_t fast_random_8();
|
||||
|
||||
/// Applies gamma correction with the provided gamma to value.
|
||||
float gamma_correct(float value, float gamma);
|
||||
|
||||
/// Create a string from a value and an accuracy in decimals.
|
||||
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
|
||||
|
||||
/// Convert a uint64_t to a hex string
|
||||
std::string uint64_to_string(uint64_t num);
|
||||
|
||||
/// Convert a uint32_t to a hex string
|
||||
std::string uint32_to_string(uint32_t num);
|
||||
|
||||
/// Sanitizes the input string with the whitelist.
|
||||
std::string sanitize_string_whitelist(const std::string &s, const std::string &whitelist);
|
||||
|
||||
uint8_t reverse_bits_8(uint8_t x);
|
||||
uint16_t reverse_bits_16(uint16_t x);
|
||||
uint32_t reverse_bits_32(uint32_t x);
|
||||
|
||||
/** Cross-platform method to disable interrupts.
|
||||
*
|
||||
* Useful when you need to do some timing-dependent communication.
|
||||
*
|
||||
* @see Do not forget to call `enable_interrupts()` again or otherwise things will go very wrong.
|
||||
*/
|
||||
void disable_interrupts();
|
||||
|
||||
/// Cross-platform method to enable interrupts after they have been disabled.
|
||||
void enable_interrupts();
|
||||
|
||||
/// Calculate a crc8 of data with the provided data length.
|
||||
uint8_t crc8(uint8_t *data, uint8_t len);
|
||||
|
||||
enum ParseOnOffState {
|
||||
PARSE_NONE = 0,
|
||||
PARSE_ON,
|
||||
PARSE_OFF,
|
||||
PARSE_TOGGLE,
|
||||
};
|
||||
|
||||
ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
|
||||
|
||||
// https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971
|
||||
template<int...> struct seq {}; // NOLINT
|
||||
template<int N, int... S> struct gens : gens<N - 1, N - 1, S...> {}; // NOLINT
|
||||
template<int... S> struct gens<0, S...> { using type = seq<S...>; }; // NOLINT
|
||||
|
||||
template<typename... X> class CallbackManager;
|
||||
|
||||
/** Simple helper class to allow having multiple subscribers to a signal.
|
||||
*
|
||||
* @tparam Ts The arguments for the callback, wrapped in void().
|
||||
*/
|
||||
template<typename... Ts> class CallbackManager<void(Ts...)> {
|
||||
public:
|
||||
/// Add a callback to the internal callback list.
|
||||
void add(std::function<void(Ts...)> &&callback) { this->callbacks_.push_back(std::move(callback)); }
|
||||
|
||||
/// Call all callbacks in this manager.
|
||||
void call(Ts... args) {
|
||||
for (auto &cb : this->callbacks_)
|
||||
cb(args...);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::function<void(Ts...)>> callbacks_;
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/a/37161919/8924614
|
||||
template<class T, class... Args>
|
||||
struct is_callable // NOLINT
|
||||
{
|
||||
template<class U> static auto test(U *p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
|
||||
|
||||
template<class U> static auto test(...) -> decltype(std::false_type());
|
||||
|
||||
static constexpr auto value = decltype(test<T>(nullptr))::value; // NOLINT
|
||||
};
|
||||
|
||||
template<bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
template<typename T, typename... X> class TemplatableValue {
|
||||
public:
|
||||
TemplatableValue() : type_(EMPTY) {}
|
||||
|
||||
template<typename F, enable_if_t<!is_callable<F, X...>::value, int> = 0>
|
||||
TemplatableValue(F value) : type_(VALUE), value_(value) {}
|
||||
|
||||
template<typename F, enable_if_t<is_callable<F, X...>::value, int> = 0>
|
||||
TemplatableValue(F f) : type_(LAMBDA), f_(f) {}
|
||||
|
||||
bool has_value() { return this->type_ != EMPTY; }
|
||||
|
||||
T value(X... x) {
|
||||
if (this->type_ == LAMBDA) {
|
||||
return this->f_(x...);
|
||||
}
|
||||
// return value also when empty
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
optional<T> optional_value(X... x) {
|
||||
if (!this->has_value()) {
|
||||
return {};
|
||||
}
|
||||
return this->value(x...);
|
||||
}
|
||||
|
||||
T value_or(X... x, T default_value) {
|
||||
if (!this->has_value()) {
|
||||
return default_value;
|
||||
}
|
||||
return this->value(x...);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
EMPTY,
|
||||
VALUE,
|
||||
LAMBDA,
|
||||
} type_;
|
||||
|
||||
T value_;
|
||||
std::function<T(X...)> f_;
|
||||
};
|
||||
|
||||
void delay_microseconds_accurate(uint32_t usec);
|
||||
|
||||
template<typename T> class Deduplicator {
|
||||
public:
|
||||
bool next(T value) {
|
||||
if (this->has_value_) {
|
||||
if (this->last_value_ == value)
|
||||
return false;
|
||||
}
|
||||
this->has_value_ = true;
|
||||
this->last_value_ = value;
|
||||
return true;
|
||||
}
|
||||
bool has_value() const { return this->has_value_; }
|
||||
|
||||
protected:
|
||||
bool has_value_{false};
|
||||
T last_value_{};
|
||||
};
|
||||
|
||||
uint32_t fnv1_hash(const std::string &str);
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
int HOT esp_log_printf_(int level, const char *tag, const char *format, ...) { // NOLINT
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int ret = esp_log_vprintf_(level, tag, format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
}
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
int HOT esp_log_printf_(int level, const char *tag, const __FlashStringHelper *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int ret = esp_log_vprintf_(level, tag, format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int HOT esp_log_vprintf_(int level, const char *tag, const char *format, va_list args) { // NOLINT
|
||||
#ifdef USE_LOGGER
|
||||
auto *log = logger::global_logger;
|
||||
if (log == nullptr)
|
||||
return 0;
|
||||
|
||||
return log->log_vprintf_(level, tag, format, args);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
int HOT esp_log_vprintf_(int level, const char *tag, const __FlashStringHelper *format, va_list args) { // NOLINT
|
||||
#ifdef USE_LOGGER
|
||||
auto *log = logger::global_logger;
|
||||
if (log == nullptr)
|
||||
return 0;
|
||||
|
||||
return log->log_vprintf_(level, tag, format, args);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int HOT esp_idf_log_vprintf_(const char *format, va_list args) { // NOLINT
|
||||
#ifdef USE_LOGGER
|
||||
auto *log = logger::global_logger;
|
||||
if (log == nullptr)
|
||||
return 0;
|
||||
|
||||
return log->log_vprintf_(log->get_global_log_level(), "", format, args);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
#include "WString.h"
|
||||
#endif
|
||||
|
||||
// avoid esp-idf redefining our macros
|
||||
#include "esphome/core/esphal.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp_err.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
#define ESPHOME_LOG_LEVEL_NONE 0
|
||||
#define ESPHOME_LOG_LEVEL_ERROR 1
|
||||
#define ESPHOME_LOG_LEVEL_WARN 2
|
||||
#define ESPHOME_LOG_LEVEL_INFO 3
|
||||
#define ESPHOME_LOG_LEVEL_DEBUG 4
|
||||
#define ESPHOME_LOG_LEVEL_VERBOSE 5
|
||||
#define ESPHOME_LOG_LEVEL_VERY_VERBOSE 6
|
||||
|
||||
#ifndef ESPHOME_LOG_LEVEL
|
||||
#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_DEBUG
|
||||
#endif
|
||||
|
||||
#define ESPHOME_LOG_COLOR_BLACK "30"
|
||||
#define ESPHOME_LOG_COLOR_RED "31" // ERROR
|
||||
#define ESPHOME_LOG_COLOR_GREEN "32" // INFO
|
||||
#define ESPHOME_LOG_COLOR_YELLOW "33" // WARNING
|
||||
#define ESPHOME_LOG_COLOR_BLUE "34"
|
||||
#define ESPHOME_LOG_COLOR_MAGENTA "35" // CONFIG
|
||||
#define ESPHOME_LOG_COLOR_CYAN "36" // DEBUG
|
||||
#define ESPHOME_LOG_COLOR_GRAY "37" // VERBOSE
|
||||
#define ESPHOME_LOG_COLOR_WHITE "38"
|
||||
#define ESPHOME_LOG_SECRET_BEGIN "\033[5m"
|
||||
#define ESPHOME_LOG_SECRET_END "\033[6m"
|
||||
#define LOG_SECRET(x) ESPHOME_LOG_SECRET_BEGIN x ESPHOME_LOG_SECRET_END
|
||||
|
||||
#define ESPHOME_LOG_COLOR(COLOR) "\033[0;" COLOR "m"
|
||||
#define ESPHOME_LOG_BOLD(COLOR) "\033[1;" COLOR "m"
|
||||
|
||||
#define ESPHOME_LOG_COLOR_E ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED)
|
||||
#define ESPHOME_LOG_COLOR_W ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_YELLOW)
|
||||
#define ESPHOME_LOG_COLOR_I ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GREEN)
|
||||
#define ESPHOME_LOG_COLOR_C ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_MAGENTA)
|
||||
#define ESPHOME_LOG_COLOR_D ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_CYAN)
|
||||
#define ESPHOME_LOG_COLOR_V ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GRAY)
|
||||
#define ESPHOME_LOG_COLOR_VV ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_WHITE)
|
||||
#define ESPHOME_LOG_RESET_COLOR "\033[0m"
|
||||
|
||||
int esp_log_printf_(int level, const char *tag, const char *format, ...) // NOLINT
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
int esp_log_printf_(int level, const char *tag, const __FlashStringHelper *format, ...);
|
||||
#endif
|
||||
int esp_log_vprintf_(int level, const char *tag, const char *format, va_list args); // NOLINT
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
int esp_log_vprintf_(int level, const char *tag, const __FlashStringHelper *format, va_list args);
|
||||
#endif
|
||||
int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT
|
||||
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
#define ESPHOME_LOG_FORMAT(tag, letter, format) \
|
||||
F(ESPHOME_LOG_COLOR_##letter "[" #letter "][%s:%03u]: " format ESPHOME_LOG_RESET_COLOR), tag, __LINE__
|
||||
#else
|
||||
#define ESPHOME_LOG_FORMAT(tag, letter, format) \
|
||||
ESPHOME_LOG_COLOR_##letter "[" #letter "][%s:%03u]: " format ESPHOME_LOG_RESET_COLOR, tag, __LINE__
|
||||
#endif
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
#define esph_log_vv(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_VERY_VERBOSE, tag, ESPHOME_LOG_FORMAT(tag, VV, format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
#else
|
||||
#define esph_log_vv(tag, format, ...)
|
||||
#endif
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
#define esph_log_v(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_VERBOSE, tag, ESPHOME_LOG_FORMAT(tag, V, format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_VERBOSE
|
||||
#else
|
||||
#define esph_log_v(tag, format, ...)
|
||||
#endif
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
#define esph_log_d(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_DEBUG, tag, ESPHOME_LOG_FORMAT(tag, D, format), ##__VA_ARGS__)
|
||||
|
||||
#define esph_log_config(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_DEBUG, tag, ESPHOME_LOG_FORMAT(tag, C, format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_DEBUG
|
||||
#define ESPHOME_LOG_HAS_CONFIG
|
||||
#else
|
||||
#define esph_log_d(tag, format, ...)
|
||||
|
||||
#define esph_log_config(tag, format, ...)
|
||||
#endif
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_INFO
|
||||
#define esph_log_i(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, tag, ESPHOME_LOG_FORMAT(tag, I, format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_INFO
|
||||
#else
|
||||
#define esph_log_i(tag, format, ...)
|
||||
#endif
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
|
||||
#define esph_log_w(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_WARN, tag, ESPHOME_LOG_FORMAT(tag, W, format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_WARN
|
||||
#else
|
||||
#define esph_log_w(tag, format, ...)
|
||||
#endif
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
#define esph_log_e(tag, format, ...) \
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_ERROR, tag, ESPHOME_LOG_FORMAT(tag, E, format), ##__VA_ARGS__)
|
||||
|
||||
#define ESPHOME_LOG_HAS_ERROR
|
||||
#else
|
||||
#define esph_log_e(tag, format, ...)
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LOGE
|
||||
#undef ESP_LOGE
|
||||
#endif
|
||||
#ifdef ESP_LOGW
|
||||
#undef ESP_LOGW
|
||||
#endif
|
||||
#ifdef ESP_LOGI
|
||||
#undef ESP_LOGI
|
||||
#endif
|
||||
#ifdef ESP_LOGD
|
||||
#undef ESP_LOGD
|
||||
#endif
|
||||
#ifdef ESP_LOGV
|
||||
#undef ESP_LOGV
|
||||
#endif
|
||||
|
||||
#define ESP_LOGE(tag, ...) esph_log_e(tag, __VA_ARGS__)
|
||||
#define LOG_E(tag, ...) ESP_LOGE(tag, __VA__ARGS__)
|
||||
#define ESP_LOGW(tag, ...) esph_log_w(tag, __VA_ARGS__)
|
||||
#define LOG_W(tag, ...) ESP_LOGW(tag, __VA__ARGS__)
|
||||
#define ESP_LOGI(tag, ...) esph_log_i(tag, __VA_ARGS__)
|
||||
#define LOG_I(tag, ...) ESP_LOGI(tag, __VA__ARGS__)
|
||||
#define ESP_LOGD(tag, ...) esph_log_d(tag, __VA_ARGS__)
|
||||
#define LOG_D(tag, ...) ESP_LOGD(tag, __VA__ARGS__)
|
||||
#define ESP_LOGCONFIG(tag, ...) esph_log_config(tag, __VA_ARGS__)
|
||||
#define LOG_CONFIG(tag, ...) ESP_LOGCONFIG(tag, __VA__ARGS__)
|
||||
#define ESP_LOGV(tag, ...) esph_log_v(tag, __VA_ARGS__)
|
||||
#define LOG_V(tag, ...) ESP_LOGV(tag, __VA__ARGS__)
|
||||
#define ESP_LOGVV(tag, ...) esph_log_vv(tag, __VA_ARGS__)
|
||||
#define LOG_VV(tag, ...) ESP_LOGVV(tag, __VA__ARGS__)
|
||||
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
((byte) &0x80 ? '1' : '0'), ((byte) &0x40 ? '1' : '0'), ((byte) &0x20 ? '1' : '0'), ((byte) &0x10 ? '1' : '0'), \
|
||||
((byte) &0x08 ? '1' : '0'), ((byte) &0x04 ? '1' : '0'), ((byte) &0x02 ? '1' : '0'), ((byte) &0x01 ? '1' : '0')
|
||||
#define YESNO(b) ((b) ? "YES" : "NO")
|
||||
#define ONOFF(b) ((b) ? "ON" : "OFF")
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,214 @@
|
||||
#pragma once
|
||||
//
|
||||
// Copyright (c) 2017 Martin Moene
|
||||
//
|
||||
// https://github.com/martinmoene/optional-bare
|
||||
//
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// Modified by Otto Winter on 18.05.18
|
||||
|
||||
namespace esphome {
|
||||
|
||||
// type for nullopt
|
||||
|
||||
struct nullopt_t { // NOLINT
|
||||
struct init {}; // NOLINT
|
||||
nullopt_t(init) {}
|
||||
};
|
||||
|
||||
// extra parenthesis to prevent the most vexing parse:
|
||||
|
||||
const nullopt_t nullopt((nullopt_t::init())); // NOLINT
|
||||
|
||||
// Simplistic optional: requires T to be default constructible, copyable.
|
||||
|
||||
template<typename T> class optional { // NOLINT
|
||||
private:
|
||||
using safe_bool = void (optional::*)() const;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
optional() {}
|
||||
|
||||
optional(nullopt_t) {}
|
||||
|
||||
optional(T const &arg) : has_value_(true), value_(arg) {}
|
||||
|
||||
template<class U> optional(optional<U> const &other) : has_value_(other.has_value()), value_(other.value()) {}
|
||||
|
||||
optional &operator=(nullopt_t) {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U> optional &operator=(optional<U> const &other) {
|
||||
has_value_ = other.has_value();
|
||||
value_ = other.value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(optional &rhs) {
|
||||
using std::swap;
|
||||
if (has_value() && rhs.has_value()) {
|
||||
swap(**this, *rhs);
|
||||
} else if (!has_value() && rhs.has_value()) {
|
||||
initialize(*rhs);
|
||||
rhs.reset();
|
||||
} else if (has_value() && !rhs.has_value()) {
|
||||
rhs.initialize(**this);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
// observers
|
||||
|
||||
value_type const *operator->() const { return &value_; }
|
||||
|
||||
value_type *operator->() { return &value_; }
|
||||
|
||||
value_type const &operator*() const { return value_; }
|
||||
|
||||
value_type &operator*() { return value_; }
|
||||
|
||||
operator safe_bool() const { return has_value() ? &optional::this_type_does_not_support_comparisons : nullptr; }
|
||||
|
||||
bool has_value() const { return has_value_; }
|
||||
|
||||
value_type const &value() const { return value_; }
|
||||
|
||||
value_type &value() { return value_; }
|
||||
|
||||
template<class U> value_type value_or(U const &v) const { return has_value() ? value() : static_cast<value_type>(v); }
|
||||
|
||||
// modifiers
|
||||
|
||||
void reset() { has_value_ = false; }
|
||||
|
||||
private:
|
||||
void this_type_does_not_support_comparisons() const {} // NOLINT
|
||||
|
||||
template<typename V> void initialize(V const &value) { // NOLINT
|
||||
value_ = value;
|
||||
has_value_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool has_value_{false}; // NOLINT
|
||||
value_type value_; // NOLINT
|
||||
};
|
||||
|
||||
// Relational operators
|
||||
|
||||
template<typename T, typename U> inline bool operator==(optional<T> const &x, optional<U> const &y) {
|
||||
return bool(x) != bool(y) ? false : !bool(x) ? true : *x == *y;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator!=(optional<T> const &x, optional<U> const &y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator<(optional<T> const &x, optional<U> const &y) {
|
||||
return (!y) ? false : (!x) ? true : *x < *y;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator>(optional<T> const &x, optional<U> const &y) { return (y < x); }
|
||||
|
||||
template<typename T, typename U> inline bool operator<=(optional<T> const &x, optional<U> const &y) { return !(y < x); }
|
||||
|
||||
template<typename T, typename U> inline bool operator>=(optional<T> const &x, optional<U> const &y) { return !(x < y); }
|
||||
|
||||
// Comparison with nullopt
|
||||
|
||||
template<typename T> inline bool operator==(optional<T> const &x, nullopt_t) { return (!x); }
|
||||
|
||||
template<typename T> inline bool operator==(nullopt_t, optional<T> const &x) { return (!x); }
|
||||
|
||||
template<typename T> inline bool operator!=(optional<T> const &x, nullopt_t) { return bool(x); }
|
||||
|
||||
template<typename T> inline bool operator!=(nullopt_t, optional<T> const &x) { return bool(x); }
|
||||
|
||||
template<typename T> inline bool operator<(optional<T> const &, nullopt_t) { return false; }
|
||||
|
||||
template<typename T> inline bool operator<(nullopt_t, optional<T> const &x) { return bool(x); }
|
||||
|
||||
template<typename T> inline bool operator<=(optional<T> const &x, nullopt_t) { return (!x); }
|
||||
|
||||
template<typename T> inline bool operator<=(nullopt_t, optional<T> const &) { return true; }
|
||||
|
||||
template<typename T> inline bool operator>(optional<T> const &x, nullopt_t) { return bool(x); }
|
||||
|
||||
template<typename T> inline bool operator>(nullopt_t, optional<T> const &) { return false; }
|
||||
|
||||
template<typename T> inline bool operator>=(optional<T> const &, nullopt_t) { return true; }
|
||||
|
||||
template<typename T> inline bool operator>=(nullopt_t, optional<T> const &x) { return (!x); }
|
||||
|
||||
// Comparison with T
|
||||
|
||||
template<typename T, typename U> inline bool operator==(optional<T> const &x, U const &v) {
|
||||
return bool(x) ? *x == v : false;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator==(U const &v, optional<T> const &x) {
|
||||
return bool(x) ? v == *x : false;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator!=(optional<T> const &x, U const &v) {
|
||||
return bool(x) ? *x != v : true;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator!=(U const &v, optional<T> const &x) {
|
||||
return bool(x) ? v != *x : true;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator<(optional<T> const &x, U const &v) {
|
||||
return bool(x) ? *x < v : true;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator<(U const &v, optional<T> const &x) {
|
||||
return bool(x) ? v < *x : false;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator<=(optional<T> const &x, U const &v) {
|
||||
return bool(x) ? *x <= v : true;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator<=(U const &v, optional<T> const &x) {
|
||||
return bool(x) ? v <= *x : false;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator>(optional<T> const &x, U const &v) {
|
||||
return bool(x) ? *x > v : false;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator>(U const &v, optional<T> const &x) {
|
||||
return bool(x) ? v > *x : true;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator>=(optional<T> const &x, U const &v) {
|
||||
return bool(x) ? *x >= v : false;
|
||||
}
|
||||
|
||||
template<typename T, typename U> inline bool operator>=(U const &v, optional<T> const &x) {
|
||||
return bool(x) ? v >= *x : true;
|
||||
}
|
||||
|
||||
// Specialized algorithms
|
||||
|
||||
template<typename T> void swap(optional<T> &x, optional<T> &y) { x.swap(y); }
|
||||
|
||||
// Convenience function to create an optional.
|
||||
|
||||
template<typename T> inline optional<T> make_optional(T const &v) { return optional<T>(v); }
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,233 @@
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
extern "C" {
|
||||
#include "spi_flash.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *TAG = "preferences";
|
||||
|
||||
ESPPreferenceObject::ESPPreferenceObject() : rtc_offset_(0), length_words_(0), type_(0), data_(nullptr) {}
|
||||
ESPPreferenceObject::ESPPreferenceObject(size_t rtc_offset, size_t length, uint32_t type)
|
||||
: rtc_offset_(rtc_offset), length_words_(length), type_(type) {
|
||||
this->data_ = new uint32_t[this->length_words_ + 1];
|
||||
for (uint32_t i = 0; i < this->length_words_ + 1; i++)
|
||||
this->data_[i] = 0;
|
||||
}
|
||||
bool ESPPreferenceObject::load_() {
|
||||
if (!this->is_initialized()) {
|
||||
ESP_LOGV(TAG, "Load Pref Not initialized!");
|
||||
return false;
|
||||
}
|
||||
if (!this->load_internal_())
|
||||
return false;
|
||||
|
||||
bool valid = this->data_[this->length_words_] == this->calculate_crc_();
|
||||
|
||||
ESP_LOGVV(TAG, "LOAD %zu: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, // NOLINT
|
||||
YESNO(valid), this->data_[0], this->data_[1], this->type_, this->calculate_crc_());
|
||||
return valid;
|
||||
}
|
||||
bool ESPPreferenceObject::save_() {
|
||||
if (!this->is_initialized()) {
|
||||
ESP_LOGV(TAG, "Save Pref Not initialized!");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->data_[this->length_words_] = this->calculate_crc_();
|
||||
if (!this->save_internal_())
|
||||
return false;
|
||||
ESP_LOGVV(TAG, "SAVE %zu: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, // NOLINT
|
||||
this->data_[0], this->data_[1], this->type_, this->calculate_crc_());
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
|
||||
#define ESP_RTC_USER_MEM_START 0x60001200
|
||||
#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START)
|
||||
#define ESP_RTC_USER_MEM_SIZE_WORDS 128
|
||||
#define ESP_RTC_USER_MEM_SIZE_BYTES ESP_RTC_USER_MEM_SIZE_WORDS * 4
|
||||
|
||||
static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) {
|
||||
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
|
||||
return false;
|
||||
}
|
||||
*dest = ESP_RTC_USER_MEM[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
static bool esp8266_preferences_modified = false;
|
||||
#endif
|
||||
|
||||
static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
|
||||
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
|
||||
return false;
|
||||
}
|
||||
if (index < 32 && global_preferences.is_prevent_write()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *ptr = &ESP_RTC_USER_MEM[index];
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
if (*ptr != value) {
|
||||
esp8266_preferences_modified = true;
|
||||
}
|
||||
#endif
|
||||
*ptr = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
extern "C" uint32_t _SPIFFS_end;
|
||||
|
||||
static const uint32_t get_esp8266_flash_sector() { return (uint32_t(&_SPIFFS_end) - 0x40200000) / SPI_FLASH_SEC_SIZE; }
|
||||
static const uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
|
||||
|
||||
static void load_esp8266_flash() {
|
||||
ESP_LOGVV(TAG, "Loading preferences from flash...");
|
||||
disable_interrupts();
|
||||
spi_flash_read(get_esp8266_flash_address(), ESP_RTC_USER_MEM, ESP_RTC_USER_MEM_SIZE_BYTES);
|
||||
enable_interrupts();
|
||||
}
|
||||
static void save_esp8266_flash() {
|
||||
if (!esp8266_preferences_modified)
|
||||
return;
|
||||
|
||||
ESP_LOGVV(TAG, "Saving preferences to flash...");
|
||||
disable_interrupts();
|
||||
auto erase_res = spi_flash_erase_sector(get_esp8266_flash_sector());
|
||||
if (erase_res != SPI_FLASH_RESULT_OK) {
|
||||
enable_interrupts();
|
||||
ESP_LOGV(TAG, "Erase ESP8266 flash failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto write_res = spi_flash_write(get_esp8266_flash_address(), ESP_RTC_USER_MEM, ESP_RTC_USER_MEM_SIZE_BYTES);
|
||||
enable_interrupts();
|
||||
if (write_res != SPI_FLASH_RESULT_OK) {
|
||||
ESP_LOGV(TAG, "Write ESP8266 flash failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
esp8266_preferences_modified = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ESPPreferenceObject::save_internal_() {
|
||||
for (uint32_t i = 0; i <= this->length_words_; i++) {
|
||||
if (!esp_rtc_user_mem_write(this->rtc_offset_ + i, this->data_[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
save_esp8266_flash();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
bool ESPPreferenceObject::load_internal_() {
|
||||
for (uint32_t i = 0; i <= this->length_words_; i++) {
|
||||
if (!esp_rtc_user_mem_read(this->rtc_offset_ + i, &this->data_[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ESPPreferences::ESPPreferences()
|
||||
// offset starts from start of user RTC mem (64 words before that are reserved for system),
|
||||
// an additional 32 words at the start of user RTC are for eboot (OTA, see eboot_command.h),
|
||||
// which will be reset each time OTA occurs
|
||||
: current_offset_(0) {}
|
||||
|
||||
void ESPPreferences::begin(const std::string &name) {
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
load_esp8266_flash();
|
||||
#endif
|
||||
}
|
||||
|
||||
ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type) {
|
||||
uint32_t start = this->current_offset_;
|
||||
uint32_t end = start + length + 1;
|
||||
bool in_normal = start < 96;
|
||||
// Normal: offset 0-95 maps to RTC offset 32 - 127,
|
||||
// Eboot: offset 96-127 maps to RTC offset 0 - 31 words
|
||||
if (in_normal && end > 96) {
|
||||
// start is in normal but end is not -> switch to Eboot
|
||||
this->current_offset_ = start = 96;
|
||||
end = start + length + 1;
|
||||
in_normal = false;
|
||||
}
|
||||
|
||||
if (end > 128) {
|
||||
// Doesn't fit in data, return uninitialized preference obj.
|
||||
return ESPPreferenceObject();
|
||||
}
|
||||
|
||||
uint32_t rtc_offset;
|
||||
if (in_normal) {
|
||||
rtc_offset = start + 32;
|
||||
} else {
|
||||
rtc_offset = start - 96;
|
||||
}
|
||||
|
||||
auto pref = ESPPreferenceObject(rtc_offset, length, type);
|
||||
this->current_offset_ += length + 1;
|
||||
return pref;
|
||||
}
|
||||
void ESPPreferences::prevent_write(bool prevent) { this->prevent_write_ = prevent; }
|
||||
bool ESPPreferences::is_prevent_write() { return this->prevent_write_; }
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
bool ESPPreferenceObject::save_internal_() {
|
||||
char key[32];
|
||||
sprintf(key, "%u", this->rtc_offset_);
|
||||
uint32_t len = (this->length_words_ + 1) * 4;
|
||||
size_t ret = global_preferences.preferences_.putBytes(key, this->data_, len);
|
||||
if (ret != len) {
|
||||
ESP_LOGV(TAG, "putBytes failed!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ESPPreferenceObject::load_internal_() {
|
||||
char key[32];
|
||||
sprintf(key, "%u", this->rtc_offset_);
|
||||
uint32_t len = (this->length_words_ + 1) * 4;
|
||||
size_t ret = global_preferences.preferences_.getBytes(key, this->data_, len);
|
||||
if (ret != len) {
|
||||
ESP_LOGV(TAG, "getBytes failed!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ESPPreferences::ESPPreferences() : current_offset_(0) {}
|
||||
void ESPPreferences::begin(const std::string &name) {
|
||||
const std::string key = truncate_string(name, 15);
|
||||
ESP_LOGV(TAG, "Opening preferences with key '%s'", key.c_str());
|
||||
this->preferences_.begin(key.c_str());
|
||||
}
|
||||
|
||||
ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type) {
|
||||
auto pref = ESPPreferenceObject(this->current_offset_, length, type);
|
||||
this->current_offset_++;
|
||||
return pref;
|
||||
}
|
||||
#endif
|
||||
uint32_t ESPPreferenceObject::calculate_crc_() const {
|
||||
uint32_t crc = this->type_;
|
||||
for (size_t i = 0; i < this->length_words_; i++) {
|
||||
crc ^= (this->data_[i] * 2654435769UL) >> 1;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
bool ESPPreferenceObject::is_initialized() const { return this->data_ != nullptr; }
|
||||
|
||||
ESPPreferences global_preferences;
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <Preferences.h>
|
||||
#endif
|
||||
|
||||
#include "esphome/core/esphal.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
class ESPPreferenceObject {
|
||||
public:
|
||||
ESPPreferenceObject();
|
||||
ESPPreferenceObject(size_t rtc_offset, size_t length, uint32_t type);
|
||||
|
||||
template<typename T> bool save(T *src);
|
||||
|
||||
template<typename T> bool load(T *dest);
|
||||
|
||||
bool is_initialized() const;
|
||||
|
||||
protected:
|
||||
bool save_();
|
||||
bool load_();
|
||||
bool save_internal_();
|
||||
bool load_internal_();
|
||||
|
||||
uint32_t calculate_crc_() const;
|
||||
|
||||
size_t rtc_offset_;
|
||||
size_t length_words_;
|
||||
uint32_t type_;
|
||||
uint32_t *data_;
|
||||
};
|
||||
|
||||
class ESPPreferences {
|
||||
public:
|
||||
ESPPreferences();
|
||||
void begin(const std::string &name);
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type);
|
||||
template<typename T> ESPPreferenceObject make_preference(uint32_t type);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
/** On the ESP8266, we can't override the first 128 bytes during OTA uploads
|
||||
* as the eboot parameters are stored there. Writing there during an OTA upload
|
||||
* would invalidate applying the new firmware. During normal operation, we use
|
||||
* this part of the RTC user memory, but stop writing to it during OTA uploads.
|
||||
*
|
||||
* @param prevent Whether to prevent writing to the first 32 words of RTC user memory.
|
||||
*/
|
||||
void prevent_write(bool prevent);
|
||||
bool is_prevent_write();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
friend ESPPreferenceObject;
|
||||
|
||||
uint32_t current_offset_;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
Preferences preferences_;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
bool prevent_write_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
extern ESPPreferences global_preferences;
|
||||
|
||||
template<typename T> ESPPreferenceObject ESPPreferences::make_preference(uint32_t type) {
|
||||
return this->make_preference((sizeof(T) + 3) / 4, type);
|
||||
}
|
||||
|
||||
template<typename T> bool ESPPreferenceObject::save(T *src) {
|
||||
if (!this->is_initialized())
|
||||
return false;
|
||||
memset(this->data_, 0, this->length_words_ * 4);
|
||||
memcpy(this->data_, src, sizeof(T));
|
||||
return this->save_();
|
||||
}
|
||||
|
||||
template<typename T> bool ESPPreferenceObject::load(T *dest) {
|
||||
memset(this->data_, 0, this->length_words_ * 4);
|
||||
if (!this->load_())
|
||||
return false;
|
||||
|
||||
memcpy(dest, this->data_, sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#ifdef USE_WIFI
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_API
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_ETHERNET
|
||||
#include "esphome/components/ethernet/ethernet_component.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <ESPmDNS.h>
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <ESP8266mDNS.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
bool network_is_connected() {
|
||||
#ifdef USE_ETHERNET
|
||||
if (ethernet::global_eth_component != nullptr && ethernet::global_eth_component->is_connected())
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr)
|
||||
return wifi::global_wifi_component->is_connected();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void network_setup() {
|
||||
bool ready = true;
|
||||
#ifdef USE_ETHERNET
|
||||
if (ethernet::global_eth_component != nullptr) {
|
||||
ethernet::global_eth_component->call_setup();
|
||||
ready = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr) {
|
||||
wifi::global_wifi_component->call_setup();
|
||||
ready = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!ready) {
|
||||
#ifdef USE_ETHERNET
|
||||
if (ethernet::global_eth_component != nullptr) {
|
||||
ethernet::global_eth_component->call_loop();
|
||||
ready = ready || ethernet::global_eth_component->can_proceed();
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr) {
|
||||
wifi::global_wifi_component->call_loop();
|
||||
ready = ready || wifi::global_wifi_component->can_proceed();
|
||||
}
|
||||
#endif
|
||||
|
||||
App.feed_wdt();
|
||||
}
|
||||
}
|
||||
void network_tick() {
|
||||
#ifdef USE_ETHERNET
|
||||
if (ethernet::global_eth_component != nullptr)
|
||||
ethernet::global_eth_component->call_loop();
|
||||
#endif
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr)
|
||||
wifi::global_wifi_component->call_loop();
|
||||
#endif
|
||||
}
|
||||
|
||||
void network_setup_mdns() {
|
||||
MDNS.begin(App.get_name().c_str());
|
||||
#ifdef USE_API
|
||||
if (api::global_api_server != nullptr) {
|
||||
MDNS.addService("esphomelib", "tcp", api::global_api_server->get_port());
|
||||
// DNS-SD (!=mDNS !) requires at least one TXT record for service discovery - let's add version
|
||||
MDNS.addServiceTxt("esphomelib", "tcp", "version", ESPHOME_VERSION);
|
||||
MDNS.addServiceTxt("esphomelib", "tcp", "address", network_get_address().c_str());
|
||||
} else {
|
||||
#endif
|
||||
// Publish "http" service if not using native API.
|
||||
// This is just to have *some* mDNS service so that .local resolution works
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addServiceTxt("http", "tcp", "version", ESPHOME_VERSION);
|
||||
#ifdef USE_API
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void network_tick_mdns() {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
MDNS.update();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string network_get_address() {
|
||||
#ifdef USE_ETHERNET
|
||||
if (ethernet::global_eth_component != nullptr)
|
||||
return ethernet::global_eth_component->get_use_address();
|
||||
#endif
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr)
|
||||
return wifi::global_wifi_component->get_use_address();
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Return whether the node is connected to the network (through wifi, eth, ...)
|
||||
bool network_is_connected();
|
||||
/// Get the active network hostname
|
||||
std::string network_get_address();
|
||||
|
||||
/// Manually set up the network stack (outside of the App.setup() loop, for example in OTA safe mode)
|
||||
void network_setup();
|
||||
void network_tick();
|
||||
void network_setup_mdns();
|
||||
void network_tick_mdns();
|
||||
|
||||
} // namespace esphome
|
||||
Reference in New Issue
Block a user