1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 07:03:55 +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, 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(
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_))

View File

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

View File

@@ -8,7 +8,7 @@ namespace template_ {
class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor {
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 loop() override;
@@ -17,7 +17,7 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso
float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected:
std::function<optional<bool>()> f_{nullptr};
optional<optional<bool> (*)()> f_;
};
} // namespace template_

View File

@@ -63,7 +63,7 @@ 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(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; }
Trigger<> *TemplateCover::get_open_trigger() const { return this->open_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_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_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; }
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:
TemplateCover();
void set_state_lambda(std::function<optional<float>()> &&f);
void set_state_lambda(optional<float> (*f)());
Trigger<> *get_open_trigger() const;
Trigger<> *get_close_trigger() const;
Trigger<> *get_stop_trigger() const;
@@ -26,7 +26,7 @@ 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(std::function<optional<float>()> &&tilt_f);
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 +45,8 @@ class TemplateCover : public cover::Cover, public Component {
void stop_prev_trigger_();
TemplateCoverRestoreMode restore_mode_{COVER_RESTORE};
optional<std::function<optional<float>()>> state_f_;
optional<std::function<optional<float>()>> tilt_f_;
optional<optional<float> (*)()> state_f_;
optional<optional<float> (*)()> tilt_f_;
bool assumed_state_{false};
bool optimistic_{false};
Trigger<> *open_trigger_;

View File

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

View File

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

View File

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

View File

@@ -45,7 +45,7 @@ void TemplateLock::open_latch() {
this->open_trigger_->trigger();
}
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; }
Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_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 set_state_lambda(std::function<optional<lock::LockState>()> &&f);
void set_state_lambda(optional<lock::LockState> (*f)());
Trigger<> *get_lock_trigger() const;
Trigger<> *get_unlock_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 open_latch() override;
optional<std::function<optional<lock::LockState>()>> f_;
optional<optional<lock::LockState> (*)()> f_;
bool optimistic_{false};
Trigger<> *lock_trigger_;
Trigger<> *unlock_trigger_;

View File

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

View File

@@ -10,7 +10,7 @@ namespace template_ {
class TemplateSelect : public select::Select, public PollingComponent {
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 update() override;
@@ -28,7 +28,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<std::function<optional<std::string>()>> f_;
optional<optional<std::string> (*)()> f_;
ESPPreferenceObject pref_;
};

View File

@@ -17,7 +17,7 @@ void TemplateSensor::update() {
}
}
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() {
LOG_SENSOR("", "Template Sensor", this);
LOG_UPDATE_INTERVAL(this);

View File

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

View File

@@ -35,7 +35,7 @@ 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(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; }
Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_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 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_off_trigger() const;
void set_optimistic(bool optimistic);
@@ -28,7 +28,7 @@ class TemplateSwitch : public switch_::Switch, public Component {
void write_state(bool state) override;
optional<std::function<optional<bool>()>> f_;
optional<optional<bool> (*)()> f_;
bool optimistic_{false};
bool assumed_state_{false};
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 {
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 update() override;
@@ -78,7 +78,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<std::function<optional<std::string>()>> f_{nullptr};
optional<optional<std::string> (*)()> f_{nullptr};
TemplateTextSaverBase *pref_ = nullptr;
};

View File

@@ -16,7 +16,7 @@ void TemplateTextSensor::update() {
}
}
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); }
} // namespace template_

View File

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

View File

@@ -55,7 +55,7 @@ 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(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; }
Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; }

View File

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

View File

@@ -19,7 +19,9 @@ classifiers = [
"Programming Language :: Python :: 3",
"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"]

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")
# Then
# Stateless lambda optimization: empty capture list allows function pointer conversion
assert "it_4->set_template([]() -> esphome::optional<std::string> {" in main_cpp
assert 'return std::string{"Hello"};' in main_cpp