mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 23:21:54 +00:00 
			
		
		
		
	fix template regression
This commit is contained in:
		| @@ -6,17 +6,19 @@ namespace template_ { | ||||
|  | ||||
| static const char *const TAG = "template.binary_sensor"; | ||||
|  | ||||
| void TemplateBinarySensor::setup() { this->loop(); } | ||||
| void TemplateBinarySensor::setup() { | ||||
|   if (!this->f_.has_value()) | ||||
|     this->disable_loop(); | ||||
|   this->loop(); | ||||
| } | ||||
|  | ||||
| void TemplateBinarySensor::loop() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto s = (*this->f_)(); | ||||
|   auto s = this->f_(); | ||||
|   if (s.has_value()) { | ||||
|     this->publish_state(*s); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Template Binary Sensor", this); } | ||||
|  | ||||
| }  // namespace template_ | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/binary_sensor/binary_sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -8,7 +9,10 @@ namespace template_ { | ||||
|  | ||||
| class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor { | ||||
|  public: | ||||
|   void set_template(optional<bool> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { | ||||
|     this->f_.set(std::forward<F>(f)); | ||||
|     this->enable_loop(); | ||||
|   } | ||||
|  | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
| @@ -17,7 +21,7 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso | ||||
|   float get_setup_priority() const override { return setup_priority::HARDWARE; } | ||||
|  | ||||
|  protected: | ||||
|   optional<optional<bool> (*)()> f_; | ||||
|   TemplateLambda<bool> f_; | ||||
| }; | ||||
|  | ||||
| }  // namespace template_ | ||||
|   | ||||
| @@ -33,28 +33,27 @@ void TemplateCover::setup() { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if (!this->state_f_.has_value() && !this->tilt_f_.has_value()) | ||||
|     this->disable_loop(); | ||||
| } | ||||
| void TemplateCover::loop() { | ||||
|   bool changed = false; | ||||
|  | ||||
|   if (this->state_f_.has_value()) { | ||||
|     auto s = (*this->state_f_)(); | ||||
|     if (s.has_value()) { | ||||
|       auto pos = clamp(*s, 0.0f, 1.0f); | ||||
|       if (pos != this->position) { | ||||
|         this->position = pos; | ||||
|         changed = true; | ||||
|       } | ||||
|   auto s = this->state_f_(); | ||||
|   if (s.has_value()) { | ||||
|     auto pos = clamp(*s, 0.0f, 1.0f); | ||||
|     if (pos != this->position) { | ||||
|       this->position = pos; | ||||
|       changed = true; | ||||
|     } | ||||
|   } | ||||
|   if (this->tilt_f_.has_value()) { | ||||
|     auto s = (*this->tilt_f_)(); | ||||
|     if (s.has_value()) { | ||||
|       auto tilt = clamp(*s, 0.0f, 1.0f); | ||||
|       if (tilt != this->tilt) { | ||||
|         this->tilt = tilt; | ||||
|         changed = true; | ||||
|       } | ||||
|  | ||||
|   auto tilt = this->tilt_f_(); | ||||
|   if (tilt.has_value()) { | ||||
|     auto tilt_val = clamp(*tilt, 0.0f, 1.0f); | ||||
|     if (tilt_val != this->tilt) { | ||||
|       this->tilt = tilt_val; | ||||
|       changed = true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -63,7 +62,6 @@ void TemplateCover::loop() { | ||||
| } | ||||
| void TemplateCover::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||||
| void TemplateCover::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } | ||||
| void TemplateCover::set_state_lambda(optional<float> (*f)()) { this->state_f_ = f; } | ||||
| float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
| Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; } | ||||
| Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; } | ||||
| @@ -124,7 +122,6 @@ CoverTraits TemplateCover::get_traits() { | ||||
| } | ||||
| Trigger<float> *TemplateCover::get_position_trigger() const { return this->position_trigger_; } | ||||
| Trigger<float> *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; } | ||||
| void TemplateCover::set_tilt_lambda(optional<float> (*tilt_f)()) { this->tilt_f_ = tilt_f; } | ||||
| void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; } | ||||
| void TemplateCover::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; } | ||||
| void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/cover/cover.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -17,7 +18,14 @@ class TemplateCover : public cover::Cover, public Component { | ||||
|  public: | ||||
|   TemplateCover(); | ||||
|  | ||||
|   void set_state_lambda(optional<float> (*f)()); | ||||
|   template<typename F> void set_state_lambda(F &&f) { | ||||
|     this->state_f_.set(std::forward<F>(f)); | ||||
|     this->enable_loop(); | ||||
|   } | ||||
|   template<typename F> void set_tilt_lambda(F &&f) { | ||||
|     this->tilt_f_.set(std::forward<F>(f)); | ||||
|     this->enable_loop(); | ||||
|   } | ||||
|   Trigger<> *get_open_trigger() const; | ||||
|   Trigger<> *get_close_trigger() const; | ||||
|   Trigger<> *get_stop_trigger() const; | ||||
| @@ -26,7 +34,6 @@ class TemplateCover : public cover::Cover, public Component { | ||||
|   Trigger<float> *get_tilt_trigger() const; | ||||
|   void set_optimistic(bool optimistic); | ||||
|   void set_assumed_state(bool assumed_state); | ||||
|   void set_tilt_lambda(optional<float> (*tilt_f)()); | ||||
|   void set_has_stop(bool has_stop); | ||||
|   void set_has_position(bool has_position); | ||||
|   void set_has_tilt(bool has_tilt); | ||||
| @@ -45,8 +52,8 @@ class TemplateCover : public cover::Cover, public Component { | ||||
|   void stop_prev_trigger_(); | ||||
|  | ||||
|   TemplateCoverRestoreMode restore_mode_{COVER_RESTORE}; | ||||
|   optional<optional<float> (*)()> state_f_; | ||||
|   optional<optional<float> (*)()> tilt_f_; | ||||
|   TemplateLambda<float> state_f_; | ||||
|   TemplateLambda<float> tilt_f_; | ||||
|   bool assumed_state_{false}; | ||||
|   bool optimistic_{false}; | ||||
|   Trigger<> *open_trigger_; | ||||
|   | ||||
| @@ -37,17 +37,13 @@ void TemplateDate::setup() { | ||||
| } | ||||
|  | ||||
| void TemplateDate::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->year_ = val->year; | ||||
|   this->month_ = val->month; | ||||
|   this->day_ = val->day_of_month; | ||||
|   this->publish_state(); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->year_ = val->year; | ||||
|     this->month_ = val->month; | ||||
|     this->day_ = val->day_of_month; | ||||
|     this->publish_state(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateDate::control(const datetime::DateCall &call) { | ||||
|   | ||||
| @@ -9,13 +9,14 @@ | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "esphome/core/time.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| class TemplateDate : public datetime::DateEntity, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<ESPTime> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
| @@ -35,7 +36,7 @@ class TemplateDate : public datetime::DateEntity, public PollingComponent { | ||||
|   ESPTime initial_value_{}; | ||||
|   bool restore_value_{false}; | ||||
|   Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>(); | ||||
|   optional<optional<ESPTime> (*)()> f_; | ||||
|   TemplateLambda<ESPTime> f_; | ||||
|  | ||||
|   ESPPreferenceObject pref_; | ||||
| }; | ||||
|   | ||||
| @@ -40,20 +40,16 @@ void TemplateDateTime::setup() { | ||||
| } | ||||
|  | ||||
| void TemplateDateTime::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->year_ = val->year; | ||||
|   this->month_ = val->month; | ||||
|   this->day_ = val->day_of_month; | ||||
|   this->hour_ = val->hour; | ||||
|   this->minute_ = val->minute; | ||||
|   this->second_ = val->second; | ||||
|   this->publish_state(); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->year_ = val->year; | ||||
|     this->month_ = val->month; | ||||
|     this->day_ = val->day_of_month; | ||||
|     this->hour_ = val->hour; | ||||
|     this->minute_ = val->minute; | ||||
|     this->second_ = val->second; | ||||
|     this->publish_state(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateDateTime::control(const datetime::DateTimeCall &call) { | ||||
|   | ||||
| @@ -9,13 +9,14 @@ | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "esphome/core/time.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<ESPTime> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
| @@ -35,7 +36,7 @@ class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponen | ||||
|   ESPTime initial_value_{}; | ||||
|   bool restore_value_{false}; | ||||
|   Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>(); | ||||
|   optional<optional<ESPTime> (*)()> f_; | ||||
|   TemplateLambda<ESPTime> f_; | ||||
|  | ||||
|   ESPPreferenceObject pref_; | ||||
| }; | ||||
|   | ||||
| @@ -37,17 +37,13 @@ void TemplateTime::setup() { | ||||
| } | ||||
|  | ||||
| void TemplateTime::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->hour_ = val->hour; | ||||
|   this->minute_ = val->minute; | ||||
|   this->second_ = val->second; | ||||
|   this->publish_state(); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->hour_ = val->hour; | ||||
|     this->minute_ = val->minute; | ||||
|     this->second_ = val->second; | ||||
|     this->publish_state(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateTime::control(const datetime::TimeCall &call) { | ||||
|   | ||||
| @@ -9,13 +9,14 @@ | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "esphome/core/time.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| class TemplateTime : public datetime::TimeEntity, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<ESPTime> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
| @@ -35,7 +36,7 @@ class TemplateTime : public datetime::TimeEntity, public PollingComponent { | ||||
|   ESPTime initial_value_{}; | ||||
|   bool restore_value_{false}; | ||||
|   Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>(); | ||||
|   optional<optional<ESPTime> (*)()> f_; | ||||
|   TemplateLambda<ESPTime> f_; | ||||
|  | ||||
|   ESPPreferenceObject pref_; | ||||
| }; | ||||
|   | ||||
| @@ -12,13 +12,10 @@ TemplateLock::TemplateLock() | ||||
|     : lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {} | ||||
|  | ||||
| void TemplateLock::loop() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->publish_state(*val); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->publish_state(*val); | ||||
|   } | ||||
| } | ||||
| void TemplateLock::control(const lock::LockCall &call) { | ||||
|   if (this->prev_trigger_ != nullptr) { | ||||
| @@ -45,7 +42,6 @@ void TemplateLock::open_latch() { | ||||
|   this->open_trigger_->trigger(); | ||||
| } | ||||
| void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||||
| void TemplateLock::set_state_lambda(optional<lock::LockState> (*f)()) { this->f_ = f; } | ||||
| float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
| Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; } | ||||
| Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/lock/lock.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -13,7 +14,10 @@ class TemplateLock : public lock::Lock, public Component { | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|   void set_state_lambda(optional<lock::LockState> (*f)()); | ||||
|   template<typename F> void set_state_lambda(F &&f) { | ||||
|     this->f_.set(std::forward<F>(f)); | ||||
|     this->enable_loop(); | ||||
|   } | ||||
|   Trigger<> *get_lock_trigger() const; | ||||
|   Trigger<> *get_unlock_trigger() const; | ||||
|   Trigger<> *get_open_trigger() const; | ||||
| @@ -26,7 +30,7 @@ class TemplateLock : public lock::Lock, public Component { | ||||
|   void control(const lock::LockCall &call) override; | ||||
|   void open_latch() override; | ||||
|  | ||||
|   optional<optional<lock::LockState> (*)()> f_; | ||||
|   TemplateLambda<lock::LockState> f_; | ||||
|   bool optimistic_{false}; | ||||
|   Trigger<> *lock_trigger_; | ||||
|   Trigger<> *unlock_trigger_; | ||||
|   | ||||
| @@ -27,14 +27,10 @@ void TemplateNumber::setup() { | ||||
| } | ||||
|  | ||||
| void TemplateNumber::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->publish_state(*val); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->publish_state(*val); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateNumber::control(float value) { | ||||
|   | ||||
| @@ -4,13 +4,14 @@ | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| class TemplateNumber : public number::Number, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<float> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
| @@ -28,7 +29,7 @@ class TemplateNumber : public number::Number, public PollingComponent { | ||||
|   float initial_value_{NAN}; | ||||
|   bool restore_value_{false}; | ||||
|   Trigger<float> *set_trigger_ = new Trigger<float>(); | ||||
|   optional<optional<float> (*)()> f_; | ||||
|   TemplateLambda<float> f_; | ||||
|  | ||||
|   ESPPreferenceObject pref_; | ||||
| }; | ||||
|   | ||||
| @@ -28,19 +28,14 @@ void TemplateSelect::setup() { | ||||
| } | ||||
|  | ||||
| void TemplateSelect::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   if (!this->has_option(*val)) { | ||||
|     ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str()); | ||||
|     return; | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     if (!this->has_option(*val)) { | ||||
|       ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str()); | ||||
|       return; | ||||
|     } | ||||
|     this->publish_state(*val); | ||||
|   } | ||||
|  | ||||
|   this->publish_state(*val); | ||||
| } | ||||
|  | ||||
| void TemplateSelect::control(const std::string &value) { | ||||
|   | ||||
| @@ -4,13 +4,14 @@ | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| class TemplateSelect : public select::Select, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<std::string> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
| @@ -28,7 +29,7 @@ class TemplateSelect : public select::Select, public PollingComponent { | ||||
|   size_t initial_option_index_{0}; | ||||
|   bool restore_value_ = false; | ||||
|   Trigger<std::string> *set_trigger_ = new Trigger<std::string>(); | ||||
|   optional<optional<std::string> (*)()> f_; | ||||
|   TemplateLambda<std::string> f_; | ||||
|  | ||||
|   ESPPreferenceObject pref_; | ||||
| }; | ||||
|   | ||||
| @@ -8,16 +8,14 @@ namespace template_ { | ||||
| static const char *const TAG = "template.sensor"; | ||||
|  | ||||
| void TemplateSensor::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->publish_state(*val); | ||||
|   } | ||||
| } | ||||
|  | ||||
| float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
| void TemplateSensor::set_template(optional<float> (*f)()) { this->f_ = f; } | ||||
|  | ||||
| void TemplateSensor::dump_config() { | ||||
|   LOG_SENSOR("", "Template Sensor", this); | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -8,7 +9,7 @@ namespace template_ { | ||||
|  | ||||
| class TemplateSensor : public sensor::Sensor, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<float> (*f)()); | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void update() override; | ||||
|  | ||||
| @@ -17,7 +18,7 @@ class TemplateSensor : public sensor::Sensor, public PollingComponent { | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|  protected: | ||||
|   optional<optional<float> (*)()> f_; | ||||
|   TemplateLambda<float> f_; | ||||
| }; | ||||
|  | ||||
| }  // namespace template_ | ||||
|   | ||||
| @@ -9,13 +9,10 @@ static const char *const TAG = "template.switch"; | ||||
| TemplateSwitch::TemplateSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {} | ||||
|  | ||||
| void TemplateSwitch::loop() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|   auto s = (*this->f_)(); | ||||
|   if (!s.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->publish_state(*s); | ||||
|   auto s = this->f_(); | ||||
|   if (s.has_value()) { | ||||
|     this->publish_state(*s); | ||||
|   } | ||||
| } | ||||
| void TemplateSwitch::write_state(bool state) { | ||||
|   if (this->prev_trigger_ != nullptr) { | ||||
| @@ -35,11 +32,13 @@ void TemplateSwitch::write_state(bool state) { | ||||
| } | ||||
| void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||||
| bool TemplateSwitch::assumed_state() { return this->assumed_state_; } | ||||
| void TemplateSwitch::set_state_lambda(optional<bool> (*f)()) { this->f_ = f; } | ||||
| float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWARE - 2.0f; } | ||||
| Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; } | ||||
| Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; } | ||||
| void TemplateSwitch::setup() { | ||||
|   if (!this->f_.has_value()) | ||||
|     this->disable_loop(); | ||||
|  | ||||
|   optional<bool> initial_state = this->get_initial_state_with_restore_mode(); | ||||
|  | ||||
|   if (initial_state.has_value()) { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/switch/switch.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -14,7 +15,10 @@ class TemplateSwitch : public switch_::Switch, public Component { | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|  | ||||
|   void set_state_lambda(optional<bool> (*f)()); | ||||
|   template<typename F> void set_state_lambda(F &&f) { | ||||
|     this->f_.set(std::forward<F>(f)); | ||||
|     this->enable_loop(); | ||||
|   } | ||||
|   Trigger<> *get_turn_on_trigger() const; | ||||
|   Trigger<> *get_turn_off_trigger() const; | ||||
|   void set_optimistic(bool optimistic); | ||||
| @@ -28,7 +32,7 @@ class TemplateSwitch : public switch_::Switch, public Component { | ||||
|  | ||||
|   void write_state(bool state) override; | ||||
|  | ||||
|   optional<optional<bool> (*)()> f_; | ||||
|   TemplateLambda<bool> f_; | ||||
|   bool optimistic_{false}; | ||||
|   bool assumed_state_{false}; | ||||
|   Trigger<> *turn_on_trigger_; | ||||
|   | ||||
| @@ -7,10 +7,8 @@ namespace template_ { | ||||
| static const char *const TAG = "template.text"; | ||||
|  | ||||
| void TemplateText::setup() { | ||||
|   if (!(this->f_ == nullptr)) { | ||||
|     if (this->f_.has_value()) | ||||
|       return; | ||||
|   } | ||||
|   if (this->f_.has_value()) | ||||
|     return; | ||||
|   std::string value = this->initial_value_; | ||||
|   if (!this->pref_) { | ||||
|     ESP_LOGD(TAG, "State from initial: %s", value.c_str()); | ||||
| @@ -26,17 +24,10 @@ void TemplateText::setup() { | ||||
| } | ||||
|  | ||||
| void TemplateText::update() { | ||||
|   if (this->f_ == nullptr) | ||||
|     return; | ||||
|  | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   if (!val.has_value()) | ||||
|     return; | ||||
|  | ||||
|   this->publish_state(*val); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->publish_state(*val); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateText::control(const std::string &value) { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
| @@ -61,7 +62,7 @@ template<uint8_t SZ> class TextSaver : public TemplateTextSaverBase { | ||||
|  | ||||
| class TemplateText : public text::Text, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<std::string> (*f)()) { this->f_ = f; } | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
| @@ -78,7 +79,7 @@ class TemplateText : public text::Text, public PollingComponent { | ||||
|   bool optimistic_ = false; | ||||
|   std::string initial_value_; | ||||
|   Trigger<std::string> *set_trigger_ = new Trigger<std::string>(); | ||||
|   optional<optional<std::string> (*)()> f_{nullptr}; | ||||
|   TemplateLambda<std::string> f_{}; | ||||
|  | ||||
|   TemplateTextSaverBase *pref_ = nullptr; | ||||
| }; | ||||
|   | ||||
| @@ -7,16 +7,14 @@ namespace template_ { | ||||
| static const char *const TAG = "template.text_sensor"; | ||||
|  | ||||
| void TemplateTextSensor::update() { | ||||
|   if (!this->f_.has_value()) | ||||
|     return; | ||||
|  | ||||
|   auto val = (*this->f_)(); | ||||
|   auto val = this->f_(); | ||||
|   if (val.has_value()) { | ||||
|     this->publish_state(*val); | ||||
|   } | ||||
| } | ||||
|  | ||||
| float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
| void TemplateTextSensor::set_template(optional<std::string> (*f)()) { this->f_ = f; } | ||||
|  | ||||
| void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); } | ||||
|  | ||||
| }  // namespace template_ | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -9,7 +10,7 @@ namespace template_ { | ||||
|  | ||||
| class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent { | ||||
|  public: | ||||
|   void set_template(optional<std::string> (*f)()); | ||||
|   template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); } | ||||
|  | ||||
|   void update() override; | ||||
|  | ||||
| @@ -18,7 +19,7 @@ class TemplateTextSensor : public text_sensor::TextSensor, public PollingCompone | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   optional<optional<std::string> (*)()> f_{}; | ||||
|   TemplateLambda<std::string> f_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace template_ | ||||
|   | ||||
| @@ -33,19 +33,19 @@ void TemplateValve::setup() { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if (!this->state_f_.has_value()) | ||||
|     this->disable_loop(); | ||||
| } | ||||
|  | ||||
| void TemplateValve::loop() { | ||||
|   bool changed = false; | ||||
|  | ||||
|   if (this->state_f_.has_value()) { | ||||
|     auto s = (*this->state_f_)(); | ||||
|     if (s.has_value()) { | ||||
|       auto pos = clamp(*s, 0.0f, 1.0f); | ||||
|       if (pos != this->position) { | ||||
|         this->position = pos; | ||||
|         changed = true; | ||||
|       } | ||||
|   auto s = this->state_f_(); | ||||
|   if (s.has_value()) { | ||||
|     auto pos = clamp(*s, 0.0f, 1.0f); | ||||
|     if (pos != this->position) { | ||||
|       this->position = pos; | ||||
|       changed = true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -55,7 +55,6 @@ void TemplateValve::loop() { | ||||
|  | ||||
| void TemplateValve::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||||
| void TemplateValve::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } | ||||
| void TemplateValve::set_state_lambda(optional<float> (*f)()) { this->state_f_ = f; } | ||||
| float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
|  | ||||
| Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/template_lambda.h" | ||||
| #include "esphome/components/valve/valve.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -17,7 +18,10 @@ class TemplateValve : public valve::Valve, public Component { | ||||
|  public: | ||||
|   TemplateValve(); | ||||
|  | ||||
|   void set_state_lambda(optional<float> (*f)()); | ||||
|   template<typename F> void set_state_lambda(F &&f) { | ||||
|     this->state_f_.set(std::forward<F>(f)); | ||||
|     this->enable_loop(); | ||||
|   } | ||||
|   Trigger<> *get_open_trigger() const; | ||||
|   Trigger<> *get_close_trigger() const; | ||||
|   Trigger<> *get_stop_trigger() const; | ||||
| @@ -42,7 +46,7 @@ class TemplateValve : public valve::Valve, public Component { | ||||
|   void stop_prev_trigger_(); | ||||
|  | ||||
|   TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE}; | ||||
|   optional<optional<float> (*)()> state_f_; | ||||
|   TemplateLambda<float> state_f_; | ||||
|   bool assumed_state_{false}; | ||||
|   bool optimistic_{false}; | ||||
|   Trigger<> *open_trigger_; | ||||
|   | ||||
							
								
								
									
										120
									
								
								esphome/core/template_lambda.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								esphome/core/template_lambda.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <functional> | ||||
| #include <concepts> | ||||
| #include "esphome/core/optional.h" | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| /** Helper class for template platforms that stores either a stateless lambda (function pointer) | ||||
|  * or a stateful lambda (std::function pointer). | ||||
|  * | ||||
|  * This provides backward compatibility with PR #11555 while maintaining the optimization: | ||||
|  * - Stateless lambdas (no capture) → function pointer (4 bytes on ESP32) | ||||
|  * - Stateful lambdas (with capture) → pointer to std::function (4 bytes on ESP32) | ||||
|  * Total size: enum (1 byte) + union (4 bytes) + padding = 8 bytes (same as PR #11555) | ||||
|  * | ||||
|  * Both lambda types must return optional<T> (as YAML codegen does) to support the pattern: | ||||
|  *   return {};  // Don't publish a value | ||||
|  *   return 42.0;  // Publish this value | ||||
|  * | ||||
|  * operator() returns optional<T>, returning nullopt when no lambda is set (type == NONE). | ||||
|  * This eliminates redundant "is lambda set" checks by reusing optional's discriminator. | ||||
|  * | ||||
|  * @tparam T The return type (e.g., float for TemplateLambda<optional<float>>) | ||||
|  * @tparam Args Optional arguments for the lambda | ||||
|  */ | ||||
| template<typename T, typename... Args> class TemplateLambda { | ||||
|  public: | ||||
|   TemplateLambda() : type_(NONE) {} | ||||
|  | ||||
|   // For stateless lambdas: use function pointer | ||||
|   template<typename F> | ||||
|   requires std::invocable<F, Args...> && std::convertible_to < F, optional<T>(*) | ||||
|   (Args...) > void set(F f) { | ||||
|     this->reset(); | ||||
|     this->type_ = STATELESS_LAMBDA; | ||||
|     this->stateless_f_ = f;  // Implicit conversion to function pointer | ||||
|   } | ||||
|  | ||||
|   // For stateful lambdas: use std::function pointer | ||||
|   template<typename F> | ||||
|   requires std::invocable<F, Args...> && | ||||
|       (!std::convertible_to<F, optional<T> (*)(Args...)>) &&std::convertible_to<std::invoke_result_t<F, Args...>, | ||||
|                                                                                 optional<T>> void set(F &&f) { | ||||
|     this->reset(); | ||||
|     this->type_ = LAMBDA; | ||||
|     this->f_ = new std::function<optional<T>(Args...)>(std::forward<F>(f)); | ||||
|   } | ||||
|  | ||||
|   ~TemplateLambda() { this->reset(); } | ||||
|  | ||||
|   // Copy constructor | ||||
|   TemplateLambda(const TemplateLambda &) = delete; | ||||
|   TemplateLambda &operator=(const TemplateLambda &) = delete; | ||||
|  | ||||
|   // Move constructor | ||||
|   TemplateLambda(TemplateLambda &&other) noexcept : type_(other.type_) { | ||||
|     if (type_ == LAMBDA) { | ||||
|       this->f_ = other.f_; | ||||
|       other.f_ = nullptr; | ||||
|     } else if (type_ == STATELESS_LAMBDA) { | ||||
|       this->stateless_f_ = other.stateless_f_; | ||||
|     } | ||||
|     other.type_ = NONE; | ||||
|   } | ||||
|  | ||||
|   TemplateLambda &operator=(TemplateLambda &&other) noexcept { | ||||
|     if (this != &other) { | ||||
|       this->reset(); | ||||
|       this->type_ = other.type_; | ||||
|       if (type_ == LAMBDA) { | ||||
|         this->f_ = other.f_; | ||||
|         other.f_ = nullptr; | ||||
|       } else if (type_ == STATELESS_LAMBDA) { | ||||
|         this->stateless_f_ = other.stateless_f_; | ||||
|       } | ||||
|       other.type_ = NONE; | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   bool has_value() const { return this->type_ != NONE; } | ||||
|  | ||||
|   // Returns optional<T>, returning nullopt if no lambda is set | ||||
|   optional<T> operator()(Args... args) { | ||||
|     switch (this->type_) { | ||||
|       case STATELESS_LAMBDA: | ||||
|         return this->stateless_f_(args...);  // Direct function pointer call | ||||
|       case LAMBDA: | ||||
|         return (*this->f_)(args...);  // std::function call via pointer | ||||
|       case NONE: | ||||
|       default: | ||||
|         return nullopt;  // No lambda set | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   optional<T> call(Args... args) { return (*this)(args...); } | ||||
|  | ||||
|  protected: | ||||
|   void reset() { | ||||
|     if (this->type_ == LAMBDA) { | ||||
|       delete this->f_; | ||||
|       this->f_ = nullptr; | ||||
|     } | ||||
|     this->type_ = NONE; | ||||
|   } | ||||
|  | ||||
|   enum : uint8_t { | ||||
|     NONE, | ||||
|     STATELESS_LAMBDA, | ||||
|     LAMBDA, | ||||
|   } type_; | ||||
|  | ||||
|   union { | ||||
|     optional<T> (*stateless_f_)(Args...);     // Function pointer (4 bytes on ESP32) | ||||
|     std::function<optional<T>(Args...)> *f_;  // Pointer to std::function (4 bytes on ESP32) | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome | ||||
		Reference in New Issue
	
	Block a user