2019-04-17 12:06:00 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <functional>
|
|
|
|
#include <vector>
|
|
|
|
#include <memory>
|
2019-06-07 14:26:17 +02:00
|
|
|
#include <type_traits>
|
2019-04-17 12:06:00 +02:00
|
|
|
|
2021-09-20 11:47:51 +02:00
|
|
|
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
2021-09-13 09:33:29 +02:00
|
|
|
#include "esp32-hal-psram.h"
|
|
|
|
#endif
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
#include "esphome/core/optional.h"
|
|
|
|
|
|
|
|
#define HOT __attribute__((hot))
|
2021-08-04 00:43:01 +02:00
|
|
|
#define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg)))
|
2019-04-17 12:06:00 +02:00
|
|
|
#define ALWAYS_INLINE __attribute__((always_inline))
|
2019-05-08 09:58:03 +02:00
|
|
|
#define PACKED __attribute__((packed))
|
2019-04-17 12:06:00 +02:00
|
|
|
|
2021-06-08 01:56:21 +02:00
|
|
|
#define xSemaphoreWait(semaphore, wait_time) \
|
|
|
|
xSemaphoreTake(semaphore, wait_time); \
|
|
|
|
xSemaphoreGive(semaphore);
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
namespace esphome {
|
|
|
|
|
|
|
|
/// The characters that are allowed in a hostname.
|
2021-06-10 22:19:44 +02:00
|
|
|
extern const char *const HOSTNAME_CHARACTER_ALLOWLIST;
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
/// 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);
|
2021-06-10 13:04:40 +02:00
|
|
|
std::string to_string(long val); // NOLINT
|
|
|
|
std::string to_string(long long val); // NOLINT
|
|
|
|
std::string to_string(unsigned val); // NOLINT
|
|
|
|
std::string to_string(unsigned long val); // NOLINT
|
|
|
|
std::string to_string(unsigned long long val); // NOLINT
|
2019-04-17 12:06:00 +02:00
|
|
|
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);
|
2021-03-17 14:40:02 +01:00
|
|
|
optional<int> parse_int(const std::string &str);
|
2021-09-26 10:34:06 +02:00
|
|
|
optional<int> parse_hex(const std::string &str, size_t start, size_t length);
|
|
|
|
optional<int> parse_hex(char chr);
|
2020-07-14 18:45:42 +02:00
|
|
|
/// Sanitize the hostname by removing characters that are not in the allowlist and truncating it to 63 chars.
|
2019-04-17 12:06:00 +02:00
|
|
|
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);
|
2019-04-24 23:49:02 +02:00
|
|
|
bool str_startswith(const std::string &full, const std::string &start);
|
|
|
|
bool str_endswith(const std::string &full, const std::string &ending);
|
2019-04-17 12:06:00 +02:00
|
|
|
|
2021-09-27 23:24:55 +02:00
|
|
|
/// sprintf-like function returning std::string instead of writing to char array.
|
|
|
|
std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...);
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
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.
|
|
|
|
*/
|
2021-07-14 07:08:18 +02:00
|
|
|
template<typename T> T clamp(T val, T min, T max);
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
/** 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);
|
|
|
|
|
2021-09-14 14:27:35 +02:00
|
|
|
// Not all platforms we support target C++14 yet, so we can't unconditionally use std::make_unique. Provide our own
|
|
|
|
// implementation if needed, and otherwise pull std::make_unique into scope so that we have a uniform API.
|
|
|
|
#if __cplusplus >= 201402L
|
|
|
|
using std::make_unique;
|
|
|
|
#else
|
2021-06-08 22:16:17 +02:00
|
|
|
template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args &&...args) {
|
2019-04-17 12:06:00 +02:00
|
|
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
|
|
}
|
2021-09-14 14:27:35 +02:00
|
|
|
#endif
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
/// 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();
|
|
|
|
|
2021-09-08 23:22:47 +02:00
|
|
|
void fill_random(uint8_t *data, size_t len);
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
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);
|
2021-07-29 19:11:56 +02:00
|
|
|
/// Reverts gamma correction with the provided gamma to value.
|
|
|
|
float gamma_uncorrect(float value, float gamma);
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
/// 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);
|
|
|
|
|
2020-07-14 18:45:42 +02:00
|
|
|
/// Sanitizes the input string with the allowlist.
|
|
|
|
std::string sanitize_string_allowlist(const std::string &s, const std::string &allowlist);
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
uint8_t reverse_bits_8(uint8_t x);
|
|
|
|
uint16_t reverse_bits_16(uint16_t x);
|
|
|
|
uint32_t reverse_bits_32(uint32_t x);
|
|
|
|
|
2019-05-13 13:06:14 +02:00
|
|
|
/// Encode a 16-bit unsigned integer given a most and least-significant byte.
|
|
|
|
uint16_t encode_uint16(uint8_t msb, uint8_t lsb);
|
|
|
|
/// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte.
|
|
|
|
std::array<uint8_t, 2> decode_uint16(uint16_t value);
|
2021-01-10 05:53:12 +01:00
|
|
|
/// Encode a 32-bit unsigned integer given four bytes in MSB -> LSB order
|
|
|
|
uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb);
|
2019-05-13 13:06:14 +02:00
|
|
|
|
2021-09-28 22:19:17 +02:00
|
|
|
/// Convert RGB floats (0-1) to hue (0-360) & saturation/value percentage (0-1)
|
|
|
|
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value);
|
|
|
|
/// Convert hue (0-360) & saturation/value percentage (0-1) to RGB floats (0-1)
|
|
|
|
void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue);
|
|
|
|
|
2019-12-04 19:30:10 +01:00
|
|
|
/***
|
|
|
|
* An interrupt helper class.
|
2019-04-17 12:06:00 +02:00
|
|
|
*
|
2019-12-04 19:30:10 +01:00
|
|
|
* This behaves like std::lock_guard. As long as the value is visible in the current stack, all interrupts
|
|
|
|
* (including flash reads) will be disabled.
|
2019-04-17 12:06:00 +02:00
|
|
|
*
|
2021-09-20 11:47:51 +02:00
|
|
|
* Please note all functions called when the interrupt lock must be marked IRAM_ATTR (loading code into
|
2019-12-04 19:30:10 +01:00
|
|
|
* instruction cache is done via interrupts; disabling interrupts prevents data not already in cache from being
|
|
|
|
* pulled from flash).
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* ```cpp
|
|
|
|
* // interrupts are enabled
|
|
|
|
* {
|
|
|
|
* InterruptLock lock;
|
|
|
|
* // do something
|
|
|
|
* // interrupts are disabled
|
|
|
|
* }
|
|
|
|
* // interrupts are enabled
|
|
|
|
* ```
|
2019-04-17 12:06:00 +02:00
|
|
|
*/
|
2019-12-04 19:30:10 +01:00
|
|
|
class InterruptLock {
|
|
|
|
public:
|
|
|
|
InterruptLock();
|
|
|
|
~InterruptLock();
|
2019-04-17 12:06:00 +02:00
|
|
|
|
2019-12-04 19:30:10 +01:00
|
|
|
protected:
|
2021-09-20 11:47:51 +02:00
|
|
|
#ifdef USE_ESP8266
|
2019-12-04 19:30:10 +01:00
|
|
|
uint32_t xt_state_;
|
|
|
|
#endif
|
|
|
|
};
|
2019-04-17 12:06:00 +02:00
|
|
|
|
|
|
|
/// 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);
|
|
|
|
|
2019-10-19 21:47:24 +02:00
|
|
|
// Encode raw data to a human-readable string (for debugging)
|
|
|
|
std::string hexencode(const uint8_t *data, uint32_t len);
|
2019-12-04 17:12:26 +01:00
|
|
|
template<typename T> std::string hexencode(const T &data) { return hexencode(data.data(), data.size()); }
|
2019-10-19 21:47:24 +02:00
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
// 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
|
|
|
|
|
2019-06-07 14:26:17 +02:00
|
|
|
template<bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
|
|
|
|
|
|
|
|
template<typename T, enable_if_t<!std::is_pointer<T>::value, int> = 0> T id(T value) { return value; }
|
|
|
|
template<typename T, enable_if_t<std::is_pointer<T *>::value, int> = 0> T &id(T *value) { return *value; }
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
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<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_;
|
|
|
|
|
2021-09-20 11:47:51 +02:00
|
|
|
T value_{};
|
|
|
|
std::function<T(X...)> f_{};
|
2019-04-17 12:06:00 +02:00
|
|
|
};
|
|
|
|
|
2019-06-18 19:31:22 +02:00
|
|
|
template<typename... X> class TemplatableStringValue : public TemplatableValue<std::string, X...> {
|
|
|
|
public:
|
|
|
|
TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
|
|
|
|
|
|
|
|
template<typename F, enable_if_t<!is_callable<F, X...>::value, int> = 0>
|
|
|
|
TemplatableStringValue(F value) : TemplatableValue<std::string, X...>(value) {}
|
|
|
|
|
|
|
|
template<typename F, enable_if_t<is_callable<F, X...>::value, int> = 0>
|
|
|
|
TemplatableStringValue(F f)
|
|
|
|
: TemplatableValue<std::string, X...>([f](X... x) -> std::string { return to_string(f(x...)); }) {}
|
|
|
|
};
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
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_{};
|
|
|
|
};
|
|
|
|
|
2019-05-11 12:31:00 +02:00
|
|
|
template<typename T> class Parented {
|
|
|
|
public:
|
|
|
|
Parented() {}
|
|
|
|
Parented(T *parent) : parent_(parent) {}
|
|
|
|
|
|
|
|
T *get_parent() const { return parent_; }
|
|
|
|
void set_parent(T *parent) { parent_ = parent; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
T *parent_{nullptr};
|
|
|
|
};
|
|
|
|
|
2019-04-17 12:06:00 +02:00
|
|
|
uint32_t fnv1_hash(const std::string &str);
|
|
|
|
|
2021-03-26 23:01:37 +01:00
|
|
|
template<typename T> T *new_buffer(size_t length) {
|
|
|
|
T *buffer;
|
2021-09-20 11:47:51 +02:00
|
|
|
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
2021-03-26 23:01:37 +01:00
|
|
|
if (psramFound()) {
|
|
|
|
buffer = (T *) ps_malloc(length);
|
|
|
|
} else {
|
2021-09-24 18:02:28 +02:00
|
|
|
buffer = new T[length]; // NOLINT(cppcoreguidelines-owning-memory)
|
2021-03-26 23:01:37 +01:00
|
|
|
}
|
|
|
|
#else
|
2021-09-24 18:02:28 +02:00
|
|
|
buffer = new T[length]; // NOLINT(cppcoreguidelines-owning-memory)
|
2021-03-26 23:01:37 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return buffer;
|
2021-06-08 01:56:21 +02:00
|
|
|
}
|
|
|
|
|
2021-03-26 23:01:37 +01:00
|
|
|
} // namespace esphome
|