[binary_sensor] Drop Component from filter classes, use self-keyed scheduler (#16131)

This commit is contained in:
J. Nick Koston
2026-04-30 19:15:18 -05:00
committed by GitHub
parent ba7c06785a
commit 550444dc34
3 changed files with 22 additions and 45 deletions
+5 -10
View File
@@ -143,15 +143,15 @@ BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Conditi
# Filters # Filters
Filter = binary_sensor_ns.class_("Filter") Filter = binary_sensor_ns.class_("Filter")
TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter, cg.Component) TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter)
DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component) DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter)
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component) DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component) DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter)
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter) InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component) AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
StatelessLambdaFilter = binary_sensor_ns.class_("StatelessLambdaFilter", Filter) StatelessLambdaFilter = binary_sensor_ns.class_("StatelessLambdaFilter", Filter)
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component) SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter)
_LOGGER = getLogger(__name__) _LOGGER = getLogger(__name__)
@@ -175,7 +175,6 @@ async def invert_filter_to_code(config, filter_id):
) )
async def timeout_filter_to_code(config, filter_id): async def timeout_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id) var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32) template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_timeout_value(template_)) cg.add(var.set_timeout_value(template_))
return var return var
@@ -203,7 +202,6 @@ async def timeout_filter_to_code(config, filter_id):
) )
async def delayed_on_off_filter_to_code(config, filter_id): async def delayed_on_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id) var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
if isinstance(config, dict): if isinstance(config, dict):
template_ = await cg.templatable(config[CONF_TIME_ON], [], cg.uint32) template_ = await cg.templatable(config[CONF_TIME_ON], [], cg.uint32)
cg.add(var.set_on_delay(template_)) cg.add(var.set_on_delay(template_))
@@ -221,7 +219,6 @@ async def delayed_on_off_filter_to_code(config, filter_id):
) )
async def delayed_on_filter_to_code(config, filter_id): async def delayed_on_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id) var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32) template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_delay(template_)) cg.add(var.set_delay(template_))
return var return var
@@ -234,7 +231,6 @@ async def delayed_on_filter_to_code(config, filter_id):
) )
async def delayed_off_filter_to_code(config, filter_id): async def delayed_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id) var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32) template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_delay(template_)) cg.add(var.set_delay(template_))
return var return var
@@ -306,7 +302,6 @@ async def lambda_filter_to_code(config, filter_id):
) )
async def settle_filter_to_code(config, filter_id): async def settle_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id) var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32) template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_delay(template_)) cg.add(var.set_delay(template_))
return var return var
+12 -22
View File
@@ -4,16 +4,14 @@
#include "filter.h" #include "filter.h"
#include "binary_sensor.h" #include "binary_sensor.h"
#include "esphome/core/application.h"
namespace esphome::binary_sensor { namespace esphome::binary_sensor {
static const char *const TAG = "sensor.filter"; static const char *const TAG = "sensor.filter";
// Timeout IDs for filter classes. // AutorepeatFilter still inherits Component (it schedules two distinct timer
// Each filter is its own Component instance, so the scheduler scopes // purposes), so it keeps the (Component *, id) scheduler API.
// IDs by component pointer — no risk of collisions between instances.
constexpr uint32_t FILTER_TIMEOUT_ID = 0;
// AutorepeatFilter needs two distinct IDs (both timeouts on the same component)
constexpr uint32_t AUTOREPEAT_TIMING_ID = 0; constexpr uint32_t AUTOREPEAT_TIMING_ID = 0;
constexpr uint32_t AUTOREPEAT_ON_OFF_ID = 1; constexpr uint32_t AUTOREPEAT_ON_OFF_ID = 1;
@@ -34,46 +32,40 @@ void Filter::input(bool value) {
} }
void TimeoutFilter::input(bool value) { void TimeoutFilter::input(bool value) {
this->set_timeout(FILTER_TIMEOUT_ID, this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); }); App.scheduler.set_timeout(this, this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); });
// we do not de-dup here otherwise changes from invalid to valid state will not be output // we do not de-dup here otherwise changes from invalid to valid state will not be output
this->output(value); this->output(value);
} }
optional<bool> DelayedOnOffFilter::new_value(bool value) { optional<bool> DelayedOnOffFilter::new_value(bool value) {
if (value) { if (value) {
this->set_timeout(FILTER_TIMEOUT_ID, this->on_delay_.value(), [this]() { this->output(true); }); App.scheduler.set_timeout(this, this->on_delay_.value(), [this]() { this->output(true); });
} else { } else {
this->set_timeout(FILTER_TIMEOUT_ID, this->off_delay_.value(), [this]() { this->output(false); }); App.scheduler.set_timeout(this, this->off_delay_.value(), [this]() { this->output(false); });
} }
return {}; return {};
} }
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOnFilter::new_value(bool value) { optional<bool> DelayedOnFilter::new_value(bool value) {
if (value) { if (value) {
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this]() { this->output(true); }); App.scheduler.set_timeout(this, this->delay_.value(), [this]() { this->output(true); });
return {}; return {};
} else { } else {
this->cancel_timeout(FILTER_TIMEOUT_ID); App.scheduler.cancel_timeout(this);
return false; return false;
} }
} }
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOffFilter::new_value(bool value) { optional<bool> DelayedOffFilter::new_value(bool value) {
if (!value) { if (!value) {
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this]() { this->output(false); }); App.scheduler.set_timeout(this, this->delay_.value(), [this]() { this->output(false); });
return {}; return {};
} else { } else {
this->cancel_timeout(FILTER_TIMEOUT_ID); App.scheduler.cancel_timeout(this);
return true; return true;
} }
} }
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> InvertFilter::new_value(bool value) { return !value; } optional<bool> InvertFilter::new_value(bool value) { return !value; }
// AutorepeatFilterBase // AutorepeatFilterBase
@@ -118,20 +110,18 @@ optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); }
optional<bool> SettleFilter::new_value(bool value) { optional<bool> SettleFilter::new_value(bool value) {
if (!this->steady_) { if (!this->steady_) {
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this, value]() { App.scheduler.set_timeout(this, this->delay_.value(), [this, value]() {
this->steady_ = true; this->steady_ = true;
this->output(value); this->output(value);
}); });
return {}; return {};
} else { } else {
this->steady_ = false; this->steady_ = false;
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this]() { this->steady_ = true; }); App.scheduler.set_timeout(this, this->delay_.value(), [this]() { this->steady_ = true; });
return value; return value;
} }
} }
float SettleFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
} // namespace esphome::binary_sensor } // namespace esphome::binary_sensor
#endif // USE_BINARY_SENSOR_FILTER #endif // USE_BINARY_SENSOR_FILTER
+5 -13
View File
@@ -29,7 +29,7 @@ class Filter {
Deduplicator<bool> dedup_; Deduplicator<bool> dedup_;
}; };
class TimeoutFilter : public Filter, public Component { class TimeoutFilter : public Filter {
public: public:
optional<bool> new_value(bool value) override { return value; } optional<bool> new_value(bool value) override { return value; }
void input(bool value) override; void input(bool value) override;
@@ -39,12 +39,10 @@ class TimeoutFilter : public Filter, public Component {
TemplatableFn<uint32_t> timeout_delay_{}; TemplatableFn<uint32_t> timeout_delay_{};
}; };
class DelayedOnOffFilter final : public Filter, public Component { class DelayedOnOffFilter final : public Filter {
public: public:
optional<bool> new_value(bool value) override; optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_on_delay(T delay) { this->on_delay_ = delay; } template<typename T> void set_on_delay(T delay) { this->on_delay_ = delay; }
template<typename T> void set_off_delay(T delay) { this->off_delay_ = delay; } template<typename T> void set_off_delay(T delay) { this->off_delay_ = delay; }
@@ -53,24 +51,20 @@ class DelayedOnOffFilter final : public Filter, public Component {
TemplatableFn<uint32_t> off_delay_{}; TemplatableFn<uint32_t> off_delay_{};
}; };
class DelayedOnFilter : public Filter, public Component { class DelayedOnFilter : public Filter {
public: public:
optional<bool> new_value(bool value) override; optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_delay(T delay) { this->delay_ = delay; } template<typename T> void set_delay(T delay) { this->delay_ = delay; }
protected: protected:
TemplatableFn<uint32_t> delay_{}; TemplatableFn<uint32_t> delay_{};
}; };
class DelayedOffFilter : public Filter, public Component { class DelayedOffFilter : public Filter {
public: public:
optional<bool> new_value(bool value) override; optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_delay(T delay) { this->delay_ = delay; } template<typename T> void set_delay(T delay) { this->delay_ = delay; }
protected: protected:
@@ -146,12 +140,10 @@ class StatelessLambdaFilter : public Filter {
optional<bool> (*f_)(bool); optional<bool> (*f_)(bool);
}; };
class SettleFilter : public Filter, public Component { class SettleFilter : public Filter {
public: public:
optional<bool> new_value(bool value) override; optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_delay(T delay) { this->delay_ = delay; } template<typename T> void set_delay(T delay) { this->delay_ = delay; }
protected: protected: