Revert "[sensor] Drop Component from timeout filters, use self-keyed scheduler"

This reverts commit 6ccc2b23b5.
This commit is contained in:
J. Nick Koston
2026-04-30 11:15:10 -05:00
parent 7d529bff12
commit e9bd84cb43
3 changed files with 51 additions and 17 deletions
+2 -1
View File
@@ -280,7 +280,7 @@ ThrottleWithPriorityFilter = sensor_ns.class_(
ThrottleWithPriorityNanFilter = sensor_ns.class_(
"ThrottleWithPriorityNanFilter", Filter
)
TimeoutFilterBase = sensor_ns.class_("TimeoutFilterBase", Filter)
TimeoutFilterBase = sensor_ns.class_("TimeoutFilterBase", Filter, cg.Component)
TimeoutFilterLast = sensor_ns.class_("TimeoutFilterLast", TimeoutFilterBase)
TimeoutFilterConfigured = sensor_ns.class_("TimeoutFilterConfigured", TimeoutFilterBase)
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter)
@@ -730,6 +730,7 @@ async def timeout_filter_to_code(config, filter_id):
filter_id.type = TimeoutFilterConfigured
template_ = await cg.templatable(config[CONF_VALUE], [], cg.float_)
var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_)
await cg.register_component(var, {})
return var
+29 -7
View File
@@ -317,19 +317,41 @@ optional<float> or_filter_new_value(Filter **filters, size_t count, float value,
return {};
}
// TimeoutFilterLast - "last" mode: re-arm on every input; output the latest value if no further
// input arrives within time_period_. Self-keyed scheduler.set_timeout(this, ...) cancels and
// replaces any pending arm in O(1).
// TimeoutFilterBase - shared loop logic
void TimeoutFilterBase::loop() {
// Check if timeout period has elapsed
// Use cached loop start time to avoid repeated millis() calls
const uint32_t now = App.get_loop_component_start_time();
if (now - this->timeout_start_time_ >= this->time_period_) {
// Timeout fired - get output value from derived class and output it
this->output(this->get_output_value());
// Disable loop until next value arrives
this->disable_loop();
}
}
float TimeoutFilterBase::get_setup_priority() const { return setup_priority::HARDWARE; }
// TimeoutFilterLast - "last" mode implementation
optional<float> TimeoutFilterLast::new_value(float value) {
// Store the value to output when timeout fires
this->pending_value_ = value;
App.scheduler.set_timeout(this, this->time_period_, [this]() { this->output(this->pending_value_); });
// Record when timeout started and enable loop
this->timeout_start_time_ = millis();
this->enable_loop();
return value;
}
// TimeoutFilterConfigured - configured-value mode: re-arm on every input; output the configured
// value (static or lambda) if no further input arrives within time_period_.
// TimeoutFilterConfigured - configured value mode implementation
optional<float> TimeoutFilterConfigured::new_value(float value) {
App.scheduler.set_timeout(this, this->time_period_, [this]() { this->output(this->value_.value()); });
// Record when timeout started and enable loop
// Note: we don't store the incoming value since we have a configured value
this->timeout_start_time_ = millis();
this->enable_loop();
return value;
}
+20 -9
View File
@@ -413,15 +413,22 @@ class ThrottleWithPriorityNanFilter : public Filter {
uint32_t min_time_between_inputs_;
};
// Base class for timeout filters. Self-keyed scheduler timeout (`this` as key) re-arms on each
// new_value(). Filter instances live for the program's lifetime, so the scheduler key never dangles.
class TimeoutFilterBase : public Filter {
// Base class for timeout filters - contains common loop logic
class TimeoutFilterBase : public Filter, public Component {
public:
void loop() override;
float get_setup_priority() const override;
protected:
explicit TimeoutFilterBase(uint32_t time_period) : time_period_(time_period) {}
uint32_t time_period_;
explicit TimeoutFilterBase(uint32_t time_period) : time_period_(time_period) { this->disable_loop(); }
virtual float get_output_value() = 0;
uint32_t time_period_; // 4 bytes (timeout duration in ms)
uint32_t timeout_start_time_{0}; // 4 bytes (when the timeout was started)
// Total base: 8 bytes
};
// "last" mode outputs the most recent input after time_period_ ms of silence.
// Timeout filter for "last" mode - outputs the last received value after timeout
class TimeoutFilterLast : public TimeoutFilterBase {
public:
explicit TimeoutFilterLast(uint32_t time_period) : TimeoutFilterBase(time_period) {}
@@ -429,10 +436,12 @@ class TimeoutFilterLast : public TimeoutFilterBase {
optional<float> new_value(float value) override;
protected:
float pending_value_{0};
float get_output_value() override { return this->pending_value_; }
float pending_value_{0}; // 4 bytes (value to output when timeout fires)
// Total: 8 (base) + 4 = 12 bytes + vtable ptr + Component overhead
};
// Configured-value mode — outputs a static or lambda value after time_period_ ms of silence.
// Timeout filter with configured value - evaluates TemplatableValue after timeout
class TimeoutFilterConfigured : public TimeoutFilterBase {
public:
explicit TimeoutFilterConfigured(uint32_t time_period, const TemplatableFn<float> &new_value)
@@ -441,7 +450,9 @@ class TimeoutFilterConfigured : public TimeoutFilterBase {
optional<float> new_value(float value) override;
protected:
TemplatableFn<float> value_;
float get_output_value() override { return this->value_.value(); }
TemplatableFn<float> value_; // 4 bytes (configured output value, can be lambda)
// Total: 8 (base) + 4 = 12 bytes + vtable ptr + Component overhead
};
class DebounceFilter : public Filter {