1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 23:21:54 +00:00

Merge branch 'template_lambdas_m' into integration

This commit is contained in:
J. Nick Koston
2025-10-26 19:43:16 -05:00
23 changed files with 48 additions and 39 deletions

View File

@@ -38,8 +38,14 @@ async def to_code(config):
condition = await automation.build_condition( condition = await automation.build_condition(
condition, cg.TemplateArguments(), [] condition, cg.TemplateArguments(), []
) )
# Generate a stateless lambda that calls condition.check()
# capture="" is safe because condition is a global variable in generated C++ code
# and doesn't need to be captured. This allows implicit conversion to function pointer.
template_ = LambdaExpression( template_ = LambdaExpression(
f"return {condition.check()};", [], return_type=cg.optional.template(bool) f"return {condition.check()};",
[],
return_type=cg.optional.template(bool),
capture="",
) )
cg.add(var.set_template(template_)) cg.add(var.set_template(template_))

View File

@@ -9,10 +9,10 @@ static const char *const TAG = "template.binary_sensor";
void TemplateBinarySensor::setup() { this->loop(); } void TemplateBinarySensor::setup() { this->loop(); }
void TemplateBinarySensor::loop() { void TemplateBinarySensor::loop() {
if (this->f_ == nullptr) if (!this->f_.has_value())
return; return;
auto s = this->f_(); auto s = (*this->f_)();
if (s.has_value()) { if (s.has_value()) {
this->publish_state(*s); this->publish_state(*s);
} }

View File

@@ -8,7 +8,7 @@ namespace template_ {
class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor { class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor {
public: public:
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; } void set_template(optional<bool> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void loop() override; void loop() override;
@@ -17,7 +17,7 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso
float get_setup_priority() const override { return setup_priority::HARDWARE; } float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected: protected:
std::function<optional<bool>()> f_{nullptr}; optional<optional<bool> (*)()> f_;
}; };
} // namespace template_ } // namespace template_

View File

@@ -63,7 +63,7 @@ void TemplateCover::loop() {
} }
void TemplateCover::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } 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_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
void TemplateCover::set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; } void TemplateCover::set_state_lambda(optional<float> (*f)()) { this->state_f_ = f; }
float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; } float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; } Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; }
Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; } Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; }
@@ -124,7 +124,7 @@ CoverTraits TemplateCover::get_traits() {
} }
Trigger<float> *TemplateCover::get_position_trigger() const { return this->position_trigger_; } Trigger<float> *TemplateCover::get_position_trigger() const { return this->position_trigger_; }
Trigger<float> *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; } Trigger<float> *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; }
void TemplateCover::set_tilt_lambda(std::function<optional<float>()> &&tilt_f) { this->tilt_f_ = tilt_f; } 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_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_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; }
void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; } void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; }

View File

@@ -17,7 +17,7 @@ class TemplateCover : public cover::Cover, public Component {
public: public:
TemplateCover(); TemplateCover();
void set_state_lambda(std::function<optional<float>()> &&f); void set_state_lambda(optional<float> (*f)());
Trigger<> *get_open_trigger() const; Trigger<> *get_open_trigger() const;
Trigger<> *get_close_trigger() const; Trigger<> *get_close_trigger() const;
Trigger<> *get_stop_trigger() const; Trigger<> *get_stop_trigger() const;
@@ -26,7 +26,7 @@ class TemplateCover : public cover::Cover, public Component {
Trigger<float> *get_tilt_trigger() const; Trigger<float> *get_tilt_trigger() const;
void set_optimistic(bool optimistic); void set_optimistic(bool optimistic);
void set_assumed_state(bool assumed_state); void set_assumed_state(bool assumed_state);
void set_tilt_lambda(std::function<optional<float>()> &&tilt_f); void set_tilt_lambda(optional<float> (*tilt_f)());
void set_has_stop(bool has_stop); void set_has_stop(bool has_stop);
void set_has_position(bool has_position); void set_has_position(bool has_position);
void set_has_tilt(bool has_tilt); void set_has_tilt(bool has_tilt);
@@ -45,8 +45,8 @@ class TemplateCover : public cover::Cover, public Component {
void stop_prev_trigger_(); void stop_prev_trigger_();
TemplateCoverRestoreMode restore_mode_{COVER_RESTORE}; TemplateCoverRestoreMode restore_mode_{COVER_RESTORE};
optional<std::function<optional<float>()>> state_f_; optional<optional<float> (*)()> state_f_;
optional<std::function<optional<float>()>> tilt_f_; optional<optional<float> (*)()> tilt_f_;
bool assumed_state_{false}; bool assumed_state_{false};
bool optimistic_{false}; bool optimistic_{false};
Trigger<> *open_trigger_; Trigger<> *open_trigger_;

View File

@@ -15,7 +15,7 @@ namespace template_ {
class TemplateDate : public datetime::DateEntity, public PollingComponent { class TemplateDate : public datetime::DateEntity, public PollingComponent {
public: public:
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; } void set_template(optional<ESPTime> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void update() override; void update() override;
@@ -35,7 +35,7 @@ class TemplateDate : public datetime::DateEntity, public PollingComponent {
ESPTime initial_value_{}; ESPTime initial_value_{};
bool restore_value_{false}; bool restore_value_{false};
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>(); Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
optional<std::function<optional<ESPTime>()>> f_; optional<optional<ESPTime> (*)()> f_;
ESPPreferenceObject pref_; ESPPreferenceObject pref_;
}; };

View File

@@ -15,7 +15,7 @@ namespace template_ {
class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent { class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent {
public: public:
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; } void set_template(optional<ESPTime> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void update() override; void update() override;
@@ -35,7 +35,7 @@ class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponen
ESPTime initial_value_{}; ESPTime initial_value_{};
bool restore_value_{false}; bool restore_value_{false};
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>(); Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
optional<std::function<optional<ESPTime>()>> f_; optional<optional<ESPTime> (*)()> f_;
ESPPreferenceObject pref_; ESPPreferenceObject pref_;
}; };

View File

@@ -15,7 +15,7 @@ namespace template_ {
class TemplateTime : public datetime::TimeEntity, public PollingComponent { class TemplateTime : public datetime::TimeEntity, public PollingComponent {
public: public:
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; } void set_template(optional<ESPTime> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void update() override; void update() override;
@@ -35,7 +35,7 @@ class TemplateTime : public datetime::TimeEntity, public PollingComponent {
ESPTime initial_value_{}; ESPTime initial_value_{};
bool restore_value_{false}; bool restore_value_{false};
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>(); Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
optional<std::function<optional<ESPTime>()>> f_; optional<optional<ESPTime> (*)()> f_;
ESPPreferenceObject pref_; ESPPreferenceObject pref_;
}; };

View File

@@ -45,7 +45,7 @@ void TemplateLock::open_latch() {
this->open_trigger_->trigger(); this->open_trigger_->trigger();
} }
void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
void TemplateLock::set_state_lambda(std::function<optional<lock::LockState>()> &&f) { this->f_ = f; } void TemplateLock::set_state_lambda(optional<lock::LockState> (*f)()) { this->f_ = f; }
float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; } float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; } Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; }
Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; } Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; }

View File

@@ -13,7 +13,7 @@ class TemplateLock : public lock::Lock, public Component {
void dump_config() override; void dump_config() override;
void set_state_lambda(std::function<optional<lock::LockState>()> &&f); void set_state_lambda(optional<lock::LockState> (*f)());
Trigger<> *get_lock_trigger() const; Trigger<> *get_lock_trigger() const;
Trigger<> *get_unlock_trigger() const; Trigger<> *get_unlock_trigger() const;
Trigger<> *get_open_trigger() const; Trigger<> *get_open_trigger() const;
@@ -26,7 +26,7 @@ class TemplateLock : public lock::Lock, public Component {
void control(const lock::LockCall &call) override; void control(const lock::LockCall &call) override;
void open_latch() override; void open_latch() override;
optional<std::function<optional<lock::LockState>()>> f_; optional<optional<lock::LockState> (*)()> f_;
bool optimistic_{false}; bool optimistic_{false};
Trigger<> *lock_trigger_; Trigger<> *lock_trigger_;
Trigger<> *unlock_trigger_; Trigger<> *unlock_trigger_;

View File

@@ -10,7 +10,7 @@ namespace template_ {
class TemplateNumber : public number::Number, public PollingComponent { class TemplateNumber : public number::Number, public PollingComponent {
public: public:
void set_template(std::function<optional<float>()> &&f) { this->f_ = f; } void set_template(optional<float> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void update() override; void update() override;
@@ -28,7 +28,7 @@ class TemplateNumber : public number::Number, public PollingComponent {
float initial_value_{NAN}; float initial_value_{NAN};
bool restore_value_{false}; bool restore_value_{false};
Trigger<float> *set_trigger_ = new Trigger<float>(); Trigger<float> *set_trigger_ = new Trigger<float>();
optional<std::function<optional<float>()>> f_; optional<optional<float> (*)()> f_;
ESPPreferenceObject pref_; ESPPreferenceObject pref_;
}; };

View File

@@ -10,7 +10,7 @@ namespace template_ {
class TemplateSelect : public select::Select, public PollingComponent { class TemplateSelect : public select::Select, public PollingComponent {
public: public:
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; } void set_template(optional<std::string> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void update() override; void update() override;
@@ -28,7 +28,7 @@ class TemplateSelect : public select::Select, public PollingComponent {
size_t initial_option_index_{0}; size_t initial_option_index_{0};
bool restore_value_ = false; bool restore_value_ = false;
Trigger<std::string> *set_trigger_ = new Trigger<std::string>(); Trigger<std::string> *set_trigger_ = new Trigger<std::string>();
optional<std::function<optional<std::string>()>> f_; optional<optional<std::string> (*)()> f_;
ESPPreferenceObject pref_; ESPPreferenceObject pref_;
}; };

View File

@@ -17,7 +17,7 @@ void TemplateSensor::update() {
} }
} }
float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; } float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
void TemplateSensor::set_template(std::function<optional<float>()> &&f) { this->f_ = f; } void TemplateSensor::set_template(optional<float> (*f)()) { this->f_ = f; }
void TemplateSensor::dump_config() { void TemplateSensor::dump_config() {
LOG_SENSOR("", "Template Sensor", this); LOG_SENSOR("", "Template Sensor", this);
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);

View File

@@ -8,7 +8,7 @@ namespace template_ {
class TemplateSensor : public sensor::Sensor, public PollingComponent { class TemplateSensor : public sensor::Sensor, public PollingComponent {
public: public:
void set_template(std::function<optional<float>()> &&f); void set_template(optional<float> (*f)());
void update() override; void update() override;
@@ -17,7 +17,7 @@ class TemplateSensor : public sensor::Sensor, public PollingComponent {
float get_setup_priority() const override; float get_setup_priority() const override;
protected: protected:
optional<std::function<optional<float>()>> f_; optional<optional<float> (*)()> f_;
}; };
} // namespace template_ } // namespace template_

View File

@@ -35,7 +35,7 @@ void TemplateSwitch::write_state(bool state) {
} }
void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
bool TemplateSwitch::assumed_state() { return this->assumed_state_; } bool TemplateSwitch::assumed_state() { return this->assumed_state_; }
void TemplateSwitch::set_state_lambda(std::function<optional<bool>()> &&f) { this->f_ = f; } void TemplateSwitch::set_state_lambda(optional<bool> (*f)()) { this->f_ = f; }
float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWARE - 2.0f; } 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_on_trigger() const { return this->turn_on_trigger_; }
Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; } Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; }

View File

@@ -14,7 +14,7 @@ class TemplateSwitch : public switch_::Switch, public Component {
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
void set_state_lambda(std::function<optional<bool>()> &&f); void set_state_lambda(optional<bool> (*f)());
Trigger<> *get_turn_on_trigger() const; Trigger<> *get_turn_on_trigger() const;
Trigger<> *get_turn_off_trigger() const; Trigger<> *get_turn_off_trigger() const;
void set_optimistic(bool optimistic); void set_optimistic(bool optimistic);
@@ -28,7 +28,7 @@ class TemplateSwitch : public switch_::Switch, public Component {
void write_state(bool state) override; void write_state(bool state) override;
optional<std::function<optional<bool>()>> f_; optional<optional<bool> (*)()> f_;
bool optimistic_{false}; bool optimistic_{false};
bool assumed_state_{false}; bool assumed_state_{false};
Trigger<> *turn_on_trigger_; Trigger<> *turn_on_trigger_;

View File

@@ -61,7 +61,7 @@ template<uint8_t SZ> class TextSaver : public TemplateTextSaverBase {
class TemplateText : public text::Text, public PollingComponent { class TemplateText : public text::Text, public PollingComponent {
public: public:
void set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; } void set_template(optional<std::string> (*f)()) { this->f_ = f; }
void setup() override; void setup() override;
void update() override; void update() override;
@@ -78,7 +78,7 @@ class TemplateText : public text::Text, public PollingComponent {
bool optimistic_ = false; bool optimistic_ = false;
std::string initial_value_; std::string initial_value_;
Trigger<std::string> *set_trigger_ = new Trigger<std::string>(); Trigger<std::string> *set_trigger_ = new Trigger<std::string>();
optional<std::function<optional<std::string>()>> f_{nullptr}; optional<optional<std::string> (*)()> f_{nullptr};
TemplateTextSaverBase *pref_ = nullptr; TemplateTextSaverBase *pref_ = nullptr;
}; };

View File

@@ -16,7 +16,7 @@ void TemplateTextSensor::update() {
} }
} }
float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; } float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
void TemplateTextSensor::set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; } void TemplateTextSensor::set_template(optional<std::string> (*f)()) { this->f_ = f; }
void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); } void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); }
} // namespace template_ } // namespace template_

View File

@@ -9,7 +9,7 @@ namespace template_ {
class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent { class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent {
public: public:
void set_template(std::function<optional<std::string>()> &&f); void set_template(optional<std::string> (*f)());
void update() override; void update() override;
@@ -18,7 +18,7 @@ class TemplateTextSensor : public text_sensor::TextSensor, public PollingCompone
void dump_config() override; void dump_config() override;
protected: protected:
optional<std::function<optional<std::string>()>> f_{}; optional<optional<std::string> (*)()> f_{};
}; };
} // namespace template_ } // namespace template_

View File

@@ -55,7 +55,7 @@ void TemplateValve::loop() {
void TemplateValve::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } 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_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
void TemplateValve::set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; } void TemplateValve::set_state_lambda(optional<float> (*f)()) { this->state_f_ = f; }
float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; } float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; } Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; }

View File

@@ -17,7 +17,7 @@ class TemplateValve : public valve::Valve, public Component {
public: public:
TemplateValve(); TemplateValve();
void set_state_lambda(std::function<optional<float>()> &&f); void set_state_lambda(optional<float> (*f)());
Trigger<> *get_open_trigger() const; Trigger<> *get_open_trigger() const;
Trigger<> *get_close_trigger() const; Trigger<> *get_close_trigger() const;
Trigger<> *get_stop_trigger() const; Trigger<> *get_stop_trigger() const;
@@ -42,7 +42,7 @@ class TemplateValve : public valve::Valve, public Component {
void stop_prev_trigger_(); void stop_prev_trigger_();
TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE}; TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE};
optional<std::function<optional<float>()>> state_f_; optional<optional<float> (*)()> state_f_;
bool assumed_state_{false}; bool assumed_state_{false};
bool optimistic_{false}; bool optimistic_{false};
Trigger<> *open_trigger_; Trigger<> *open_trigger_;

View File

@@ -19,7 +19,9 @@ classifiers = [
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Topic :: Home Automation", "Topic :: Home Automation",
] ]
requires-python = ">=3.11.0"
# Python 3.14 is currently not supported by IDF <= 5.5.1, see https://github.com/esphome/esphome/issues/11502
requires-python = ">=3.11.0,<3.14"
dynamic = ["dependencies", "optional-dependencies", "version"] dynamic = ["dependencies", "optional-dependencies", "version"]

View File

@@ -66,5 +66,6 @@ def test_text_config_lamda_is_set(generate_main):
main_cpp = generate_main("tests/component_tests/text/test_text.yaml") main_cpp = generate_main("tests/component_tests/text/test_text.yaml")
# Then # Then
# Stateless lambda optimization: empty capture list allows function pointer conversion
assert "it_4->set_template([]() -> esphome::optional<std::string> {" in main_cpp assert "it_4->set_template([]() -> esphome::optional<std::string> {" in main_cpp
assert 'return std::string{"Hello"};' in main_cpp assert 'return std::string{"Hello"};' in main_cpp