[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
Filter = binary_sensor_ns.class_("Filter")
TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component)
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter)
DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter)
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter)
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", 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__)
@@ -175,7 +175,6 @@ async def invert_filter_to_code(config, filter_id):
)
async def timeout_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_timeout_value(template_))
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):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
if isinstance(config, dict):
template_ = await cg.templatable(config[CONF_TIME_ON], [], cg.uint32)
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):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_delay(template_))
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):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_delay(template_))
return var
@@ -306,7 +302,6 @@ async def lambda_filter_to_code(config, filter_id):
)
async def settle_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_delay(template_))
return var
+12 -22
View File
@@ -4,16 +4,14 @@
#include "filter.h"
#include "binary_sensor.h"
#include "esphome/core/application.h"
namespace esphome::binary_sensor {
static const char *const TAG = "sensor.filter";
// Timeout IDs for filter classes.
// Each filter is its own Component instance, so the scheduler scopes
// 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)
// AutorepeatFilter still inherits Component (it schedules two distinct timer
// purposes), so it keeps the (Component *, id) scheduler API.
constexpr uint32_t AUTOREPEAT_TIMING_ID = 0;
constexpr uint32_t AUTOREPEAT_ON_OFF_ID = 1;
@@ -34,46 +32,40 @@ void Filter::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
this->output(value);
}
optional<bool> DelayedOnOffFilter::new_value(bool 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 {
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 {};
}
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOnFilter::new_value(bool 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 {};
} else {
this->cancel_timeout(FILTER_TIMEOUT_ID);
App.scheduler.cancel_timeout(this);
return false;
}
}
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOffFilter::new_value(bool 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 {};
} else {
this->cancel_timeout(FILTER_TIMEOUT_ID);
App.scheduler.cancel_timeout(this);
return true;
}
}
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> InvertFilter::new_value(bool value) { return !value; }
// AutorepeatFilterBase
@@ -118,20 +110,18 @@ optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); }
optional<bool> SettleFilter::new_value(bool value) {
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->output(value);
});
return {};
} else {
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;
}
}
float SettleFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
} // namespace esphome::binary_sensor
#endif // USE_BINARY_SENSOR_FILTER
+5 -13
View File
@@ -29,7 +29,7 @@ class Filter {
Deduplicator<bool> dedup_;
};
class TimeoutFilter : public Filter, public Component {
class TimeoutFilter : public Filter {
public:
optional<bool> new_value(bool value) override { return value; }
void input(bool value) override;
@@ -39,12 +39,10 @@ class TimeoutFilter : public Filter, public Component {
TemplatableFn<uint32_t> timeout_delay_{};
};
class DelayedOnOffFilter final : public Filter, public Component {
class DelayedOnOffFilter final : public Filter {
public:
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_off_delay(T delay) { this->off_delay_ = delay; }
@@ -53,24 +51,20 @@ class DelayedOnOffFilter final : public Filter, public Component {
TemplatableFn<uint32_t> off_delay_{};
};
class DelayedOnFilter : public Filter, public Component {
class DelayedOnFilter : public Filter {
public:
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
protected:
TemplatableFn<uint32_t> delay_{};
};
class DelayedOffFilter : public Filter, public Component {
class DelayedOffFilter : public Filter {
public:
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
protected:
@@ -146,12 +140,10 @@ class StatelessLambdaFilter : public Filter {
optional<bool> (*f_)(bool);
};
class SettleFilter : public Filter, public Component {
class SettleFilter : public Filter {
public:
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
protected: