21#if defined(__GNUC__) || defined(__clang__)
22#pragma GCC diagnostic push
23#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
26template<
int...>
struct ESPDEPRECATED(
"Use std::index_sequence instead. Removed in 2026.6.0",
"2025.12.0")
seq {};
27template<
int N,
int... S>
28struct ESPDEPRECATED(
"Use std::make_index_sequence instead. Removed in 2026.6.0",
"2025.12.0") gens
29 : gens<N - 1, N - 1, S...> {};
30template<
int... S>
struct gens<0, S...> {
using type =
seq<S...>; };
32#if defined(__GNUC__) || defined(__clang__)
33#pragma GCC diagnostic pop
46 template<
typename F>
TemplatableFn(F f)
requires std::convertible_to<F, T (*)(X...)> :
f_(f) {}
53 [[deprecated(
"Lambda return type does not match TemplatableFn<T> — use the correct type in "
54 "codegen")]]
TemplatableFn(F)
requires(!std::convertible_to<F, T (*)(X...)>) &&
55 std::invocable<F, X...> &&std::convertible_to<std::invoke_result_t<F, X...>, T> &&std::is_empty_v<F>
56 &&std::default_initializable<F> :
f_([](X...
x) -> T { return static_cast<T>(F{}(
x...)); }) {}
61 (!std::convertible_to<F, T (*)(X...)>) &&(!std::is_empty_v<F> ||
62 !std::convertible_to<std::invoke_result_t<F, X...>, T> ||
63 !std::default_initializable<F>) =
delete;
69 template<
typename V>
TemplatableFn(V)
requires(!std::invocable<V, X...>) && (!std::convertible_to<V, T (*)(X...)>) {
70 static_assert(
sizeof(V) == 0,
"Missing cg.templatable(...) in Python codegen for this TEMPLATABLE_VALUE "
71 "field. The wrapper was always required; it worked by accident because the old "
72 "TemplatableValue implicitly converted raw constants. TemplatableFn cannot. See "
73 "https://developers.esphome.io/blog/2026/04/09/"
74 "templatablefn-4-byte-templatable-storage-for-trivially-copyable-types/");
77 bool has_value()
const {
return this->f_ !=
nullptr; }
79 T
value(X...
x)
const {
return this->f_ ? this->f_(
x...) : T{}; }
84 return this->f_(
x...);
87 T
value_or(X...
x, T default_value)
const {
return this->f_ ? this->f_(
x...) : default_value; }
90 T (*f_)(X...){
nullptr};
94template<
typename T,
typename... X>
class TemplatableValue;
99template<
typename T,
typename... X>
103#define TEMPLATABLE_VALUE_(type, name) \
105 TemplatableStorage<type, Ts...> name##_{}; \
108 template<typename V> void set_##name(V name) { this->name##_ = name; }
110#define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name)
121 template<
typename V>
TemplatableValue(V value)
requires(!std::invocable<V, X...>) : tag_(VALUE) {
122 new (&this->storage_.value_) T(
static_cast<T
>(std::move(value)));
126 template<
typename F>
TemplatableValue(F f)
requires std::convertible_to<F, T (*)(X...)> : tag_(FN) {
127 this->storage_.f_ = f;
132 [[deprecated(
"Lambda return type does not match TemplatableValue<T> — use the correct type in "
134 std::invocable<F, X...> &&std::convertible_to<std::invoke_result_t<F, X...>, T> &&std::is_empty_v<F>
135 &&std::default_initializable<F> : tag_(FN) {
136 this->storage_.f_ = [](X...
x) -> T {
return static_cast<T
>(F{}(
x...)); };
142 (!std::convertible_to<F, T (*)(X...)>) &&(!std::is_empty_v<F> ||
143 !std::convertible_to<std::invoke_result_t<F, X...>, T> ||
144 !std::default_initializable<F>) =
delete;
147 if (this->tag_ == VALUE) {
149 }
else if (this->tag_ == FN) {
155 if (this->tag_ == VALUE) {
156 new (&this->storage_.value_) T(std::move(other.storage_.value_));
158 }
else if (this->tag_ == FN) {
159 this->storage_.f_ = other.storage_.f_;
165 if (
this != &other) {
167 this->tag_ = other.tag_;
168 if (this->tag_ == VALUE) {
170 }
else if (this->tag_ == FN) {
178 if (
this != &other) {
180 this->tag_ = other.tag_;
181 if (this->tag_ == VALUE) {
182 new (&this->storage_.value_) T(std::move(other.storage_.value_));
184 }
else if (this->tag_ == FN) {
185 this->storage_.f_ = other.storage_.f_;
197 if (this->tag_ == FN)
198 return this->storage_.f_(
x...);
199 if (this->tag_ == VALUE)
200 return this->storage_.value_;
205 if (this->tag_ == NONE)
207 return this->value(
x...);
211 if (this->tag_ == NONE)
212 return default_value;
213 return this->value(
x...);
218 if constexpr (!std::is_trivially_destructible_v<T>) {
219 if (this->tag_ == VALUE)
220 this->storage_.value_.~T();
224 enum Tag : uint8_t { NONE, VALUE, FN } tag_{NONE};
251 this->static_str_ =
reinterpret_cast<const char *
>(str);
255 template<
typename F>
TemplatableValue(F value)
requires(!std::invocable<F, X...>) : type_(VALUE) {
256 this->value_ =
new std::string(std::move(value));
261 TemplatableValue(F f)
requires std::invocable<F, X...> && std::convertible_to<F, std::string (*)(X...)>
262 : type_(STATELESS_LAMBDA) {
263 this->stateless_f_ = f;
268 TemplatableValue(F f)
requires std::invocable<F, X...> &&(!std::convertible_to<F, std::string (*)(X...)>)
270 this->f_ =
new std::function<std::string(X...)>(std::move(f));
275 if (this->type_ == VALUE) {
276 this->value_ =
new std::string(*other.value_);
277 }
else if (this->type_ == LAMBDA) {
278 this->f_ =
new std::function<std::string(X...)>(*other.f_);
279 }
else if (this->type_ == STATELESS_LAMBDA) {
280 this->stateless_f_ = other.stateless_f_;
281 }
else if (this->type_ == STATIC_STRING || this->type_ == FLASH_STRING) {
282 this->static_str_ = other.static_str_;
288 if (this->type_ == VALUE) {
289 this->value_ = other.value_;
290 other.value_ =
nullptr;
291 }
else if (this->type_ == LAMBDA) {
294 }
else if (this->type_ == STATELESS_LAMBDA) {
295 this->stateless_f_ = other.stateless_f_;
296 }
else if (this->type_ == STATIC_STRING || this->type_ == FLASH_STRING) {
297 this->static_str_ = other.static_str_;
304 if (
this != &other) {
312 if (
this != &other) {
320 if (this->type_ == VALUE) {
322 }
else if (this->type_ == LAMBDA) {
331 switch (this->type_) {
332 case STATELESS_LAMBDA:
333 return this->stateless_f_(
x...);
335 return (*this->f_)(
x...);
337 return *this->value_;
339 return std::string(this->static_str_);
343 size_t len = strlen_P(this->static_str_);
344 std::string result(
len,
'\0');
345 memcpy_P(result.data(), this->static_str_,
len);
356 if (!this->has_value())
358 return this->value(
x...);
361 std::string
value_or(X...
x, std::string default_value)
const {
362 if (!this->has_value())
363 return default_value;
364 return this->value(
x...);
380 switch (this->type_) {
384 return this->static_str_ ==
nullptr || this->static_str_[0] ==
'\0';
388 return this->static_str_ ==
nullptr ||
392 return this->value_->empty();
394 return this->value().empty();
406 switch (this->type_) {
410 if (this->static_str_ ==
nullptr)
412 return StringRef(this->static_str_, strlen(this->static_str_));
415 if (this->static_str_ ==
nullptr)
419 size_t len = strlen_P(this->static_str_);
420 size_t copy_len = std::min(
len, lambda_buf_size - 1);
421 memcpy_P(lambda_buf, this->static_str_, copy_len);
422 lambda_buf[copy_len] =
'\0';
427 return StringRef(this->value_->data(), this->value_->size());
429 std::string result = this->value();
430 size_t copy_len = std::min(result.size(), lambda_buf_size - 1);
431 memcpy(lambda_buf, result.data(), copy_len);
432 lambda_buf[copy_len] =
'\0';
449 std::function<std::string(X...)> *
f_;
450 std::string (*stateless_f_)(X...);
466 return this->check_tuple_(tuple, std::make_index_sequence<
sizeof...(Ts)>{});
470 template<
size_t... S>
bool check_tuple_(
const std::tuple<Ts...> &tuple, std::index_sequence<S...> ) {
471 return this->check(std::get<S>(tuple)...);
475template<
typename... Ts>
class Automation;
482 inline void trigger(
const Ts &...
x) ESPHOME_ALWAYS_INLINE {
483 if (this->automation_parent_ ==
nullptr)
485 this->automation_parent_->trigger(
x...);
491 if (this->automation_parent_ ==
nullptr)
493 this->automation_parent_->stop();
497 if (this->automation_parent_ ==
nullptr)
499 return this->automation_parent_->is_running();
506template<
typename... Ts>
class ActionList;
511 this->num_running_++;
513 this->play_next_(
x...);
518 this->num_running_ = 0;
523 virtual bool is_running() {
return this->num_running_ > 0 || this->is_running_next_(); }
528 int total = this->num_running_;
529 if (this->next_ !=
nullptr)
530 total += this->next_->num_running_total();
538 virtual void play(
const Ts &...
x) = 0;
540 if (this->num_running_ > 0) {
541 this->num_running_--;
542 if (this->next_ !=
nullptr) {
543 this->next_->play_complex(
x...);
547 template<
size_t... S>
void play_next_tuple_(
const std::tuple<Ts...> &tuple, std::index_sequence<S...> ) {
548 this->play_next_(std::get<S>(tuple)...);
551 this->play_next_tuple_(tuple, std::make_index_sequence<
sizeof...(Ts)>{});
556 if (this->next_ !=
nullptr) {
557 this->next_->stop_complex();
562 if (this->next_ ==
nullptr)
564 return this->next_->is_running();
578 Action<Ts...> **tail = &this->actions_;
579 while (*tail !=
nullptr)
580 tail = &(*tail)->
next_;
585 Action<Ts...> **tail = &this->actions_;
586 while (*tail !=
nullptr)
587 tail = &(*tail)->
next_;
588 for (
auto *action : actions) {
590 tail = &action->next_;
595 inline void play(
const Ts &...
x) ESPHOME_ALWAYS_INLINE {
596 if (this->actions_ !=
nullptr)
597 this->actions_->play_complex(
x...);
600 this->play_tuple_(tuple, std::make_index_sequence<
sizeof...(Ts)>{});
603 if (this->actions_ !=
nullptr)
604 this->actions_->stop_complex();
606 bool empty()
const {
return this->actions_ ==
nullptr; }
610 if (this->actions_ ==
nullptr)
612 return this->actions_->is_running();
616 if (this->actions_ ==
nullptr)
618 return this->actions_->num_running_total();
622 template<
size_t... S>
void play_tuple_(
const std::tuple<Ts...> &tuple, std::index_sequence<S...> ) {
623 this->play(std::get<S>(tuple)...);
638 void stop() { this->actions_.stop(); }
642 inline void trigger(
const Ts &...
x) ESPHOME_ALWAYS_INLINE { this->actions_.play(
x...); }
683static_assert(
sizeof(TriggerForwarder<>) <=
sizeof(
void *));
684static_assert(
sizeof(TriggerOnTrueForwarder) <=
sizeof(
void *));
685static_assert(
sizeof(TriggerOnFalseForwarder) <=
sizeof(
void *));
686static_assert(std::is_trivially_copyable_v<TriggerForwarder<>>);
687static_assert(std::is_trivially_copyable_v<TriggerOnTrueForwarder>);
688static_assert(std::is_trivially_copyable_v<TriggerOnFalseForwarder>);
virtual bool is_running()
Check if this or any of the following actions are currently running.
void play_next_(const Ts &...x)
virtual void stop_complex()
virtual void play(const Ts &...x)=0
void play_next_tuple_(const std::tuple< Ts... > &tuple)
int num_running_total()
The total number of actions that are currently running in this plus any of the following actions in t...
void play_next_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
virtual void play_complex(const Ts &...x)
void add_action(Action< Ts... > *action)
void play_tuple(const std::tuple< Ts... > &tuple)
bool is_running()
Check if any action in this action list is currently running.
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
void play_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
void play(const Ts &...x) ESPHOME_ALWAYS_INLINE
int num_running()
Return the number of actions in this action list that are currently running.
void add_action(Action< Ts... > *action)
Automation()=default
Default constructor for use with TriggerForwarder (no Trigger object needed).
int num_running()
Return the number of actions in the action part of this automation that are currently running.
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Automation(Trigger< Ts... > *trigger)
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
ActionList< Ts... > actions_
Base class for all automation conditions.
bool check_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
bool check_tuple(const std::tuple< Ts... > &tuple)
Call check with a tuple of values as parameter.
virtual bool check(const Ts &...x)=0
Check whether this condition passes. This condition check must be instant, and not cause any delays.
Simple continuation action that calls play_next_ on a parent action.
StringRef is a reference to a string owned by something else.
Function-pointer-only templatable storage (4 bytes on 32-bit).
T value_or(X... x, T default_value) const
TemplatableFn(std::nullptr_t)=delete
optional< T > optional_value(X... x) const
TemplatableValue(const __FlashStringHelper *str)
TemplatableValue(TemplatableValue &&other) noexcept
TemplatableValue(const char *str)
TemplatableValue & operator=(const TemplatableValue &other)
bool is_empty() const
Check if the string value is empty without allocating.
bool is_static_string() const
Check if this holds a static string (const char* stored without allocation) The pointer is always dir...
TemplatableValue(const TemplatableValue &other)
TemplatableValue & operator=(TemplatableValue &&other) noexcept
TemplatableValue(F value)
StringRef ref_or_copy_to(char *lambda_buf, size_t lambda_buf_size) const
Get a StringRef to the string value without heap allocation when possible.
std::string value_or(X... x, std::string default_value) const
const char * get_static_string() const
Get the static string pointer (only valid if is_static_string() returns true) The pointer is always d...
std::string value(X... x) const
optional< std::string > optional_value(X... x) const
std::function< std::string(X...)> * f_
Primary TemplatableValue: stores either a constant value or a function pointer.
TemplatableValue(const TemplatableValue &other)
TemplatableValue & operator=(TemplatableValue &&other) noexcept
union esphome::TemplatableValue::Storage storage_
TemplatableValue(V value)
TemplatableValue(TemplatableValue &&other) noexcept
optional< T > optional_value(X... x) const
TemplatableValue()=default
TemplatableValue & operator=(const TemplatableValue &other)
T value_or(X... x, T default_value) const
TemplatableValue(F)=delete
TemplatableValue(std::nullptr_t)=delete
void stop_action()
Stop any action connected to this trigger.
bool is_action_running()
Returns true if any action connected to this trigger is running.
void set_automation_parent(Automation< Ts... > *automation_parent)
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Providing packet encoding functions for exchanging data with a remote host.
const char int const __FlashStringHelper va_list args
std::conditional_t< std::is_trivially_copyable_v< T >, TemplatableFn< T, X... >, TemplatableValue< T, X... > > TemplatableStorage
Selects TemplatableFn (4 bytes) for trivially copyable types, TemplatableValue (8 bytes) otherwise.
struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq
uint8_t progmem_read_byte(const uint8_t *addr)
Callback forwarder that triggers an Automation directly.
Automation< Ts... > * automation
void operator()(const Ts &...args) const
Callback forwarder that triggers an Automation<> only when the bool arg is false.
void operator()(bool state) const
Callback forwarder that triggers an Automation<> only when the bool arg is true.
void operator()(bool state) const