mirror of
https://github.com/esphome/esphome.git
synced 2025-11-16 23:05:46 +00:00
[sensor] Replace timeout filter scheduler with loop-based implementation
This commit is contained in:
@@ -271,6 +271,9 @@ ThrottleWithPriorityFilter = sensor_ns.class_(
|
|||||||
"ThrottleWithPriorityFilter", ValueListFilter
|
"ThrottleWithPriorityFilter", ValueListFilter
|
||||||
)
|
)
|
||||||
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
|
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
|
||||||
|
TimeoutFilterConfigured = sensor_ns.class_(
|
||||||
|
"TimeoutFilterConfigured", Filter, cg.Component
|
||||||
|
)
|
||||||
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component)
|
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component)
|
||||||
HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
|
HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
|
||||||
DeltaFilter = sensor_ns.class_("DeltaFilter", Filter)
|
DeltaFilter = sensor_ns.class_("DeltaFilter", Filter)
|
||||||
@@ -684,8 +687,13 @@ TIMEOUT_SCHEMA = cv.maybe_simple_value(
|
|||||||
@FILTER_REGISTRY.register("timeout", TimeoutFilter, TIMEOUT_SCHEMA)
|
@FILTER_REGISTRY.register("timeout", TimeoutFilter, TIMEOUT_SCHEMA)
|
||||||
async def timeout_filter_to_code(config, filter_id):
|
async def timeout_filter_to_code(config, filter_id):
|
||||||
if config[CONF_VALUE] == "last":
|
if config[CONF_VALUE] == "last":
|
||||||
|
# Use TimeoutFilter for "last" mode (smaller, more common - LD2450, LD2412, etc.)
|
||||||
var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT])
|
var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT])
|
||||||
else:
|
else:
|
||||||
|
# Use TimeoutFilterConfigured for configured value mode
|
||||||
|
# Change the type to TimeoutFilterConfigured (similar to stateless lambda pattern)
|
||||||
|
filter_id = filter_id.copy()
|
||||||
|
filter_id.type = TimeoutFilterConfigured
|
||||||
template_ = await cg.templatable(config[CONF_VALUE], [], float)
|
template_ = await cg.templatable(config[CONF_VALUE], [], float)
|
||||||
var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_)
|
var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_)
|
||||||
await cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
|
|||||||
@@ -339,20 +339,43 @@ void OrFilter::initialize(Sensor *parent, Filter *next) {
|
|||||||
this->phi_.initialize(parent, nullptr);
|
this->phi_.initialize(parent, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimeoutFilter
|
// TimeoutFilterBase - shared loop logic
|
||||||
optional<float> TimeoutFilter::new_value(float value) {
|
void TimeoutFilterBase::loop() {
|
||||||
if (this->value_.has_value()) {
|
// Check if timeout period has elapsed
|
||||||
this->set_timeout("timeout", this->time_period_, [this]() { this->output(this->value_.value().value()); });
|
// Use cached loop start time to avoid repeated millis() calls
|
||||||
} else {
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
this->set_timeout("timeout", this->time_period_, [this, value]() { this->output(value); });
|
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; }
|
||||||
|
|
||||||
|
// TimeoutFilter - "last" mode implementation
|
||||||
|
optional<float> TimeoutFilter::new_value(float value) {
|
||||||
|
// Store the value to output when timeout fires
|
||||||
|
this->pending_value_ = value;
|
||||||
|
|
||||||
|
// Record when timeout started and enable loop
|
||||||
|
this->timeout_start_time_ = millis();
|
||||||
|
this->enable_loop();
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeoutFilter::TimeoutFilter(uint32_t time_period) : time_period_(time_period) {}
|
// TimeoutFilterConfigured - configured value mode implementation
|
||||||
TimeoutFilter::TimeoutFilter(uint32_t time_period, const TemplatableValue<float> &new_value)
|
optional<float> TimeoutFilterConfigured::new_value(float value) {
|
||||||
: time_period_(time_period), value_(new_value) {}
|
// Record when timeout started and enable loop
|
||||||
float TimeoutFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
// Note: we don't store the incoming value since we have a configured value
|
||||||
|
this->timeout_start_time_ = millis();
|
||||||
|
this->enable_loop();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// DebounceFilter
|
// DebounceFilter
|
||||||
optional<float> DebounceFilter::new_value(float value) {
|
optional<float> DebounceFilter::new_value(float value) {
|
||||||
|
|||||||
@@ -380,18 +380,46 @@ class ThrottleWithPriorityFilter : public ValueListFilter {
|
|||||||
uint32_t min_time_between_inputs_;
|
uint32_t min_time_between_inputs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TimeoutFilter : public Filter, public Component {
|
// Base class for timeout filters - contains common loop logic
|
||||||
|
class TimeoutFilterBase : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit TimeoutFilter(uint32_t time_period);
|
void loop() override;
|
||||||
explicit TimeoutFilter(uint32_t time_period, const TemplatableValue<float> &new_value);
|
|
||||||
|
|
||||||
optional<float> new_value(float value) override;
|
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t time_period_;
|
explicit TimeoutFilterBase(uint32_t time_period) : time_period_(time_period) { this->disable_loop(); }
|
||||||
optional<TemplatableValue<float>> value_;
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
// Timeout filter for "last" mode - outputs the last received value after timeout
|
||||||
|
class TimeoutFilter : public TimeoutFilterBase {
|
||||||
|
public:
|
||||||
|
explicit TimeoutFilter(uint32_t time_period) : TimeoutFilterBase(time_period) {}
|
||||||
|
|
||||||
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
// Timeout filter with configured value - evaluates TemplatableValue after timeout
|
||||||
|
class TimeoutFilterConfigured : public TimeoutFilterBase {
|
||||||
|
public:
|
||||||
|
explicit TimeoutFilterConfigured(uint32_t time_period, const TemplatableValue<float> &new_value)
|
||||||
|
: TimeoutFilterBase(time_period), value_(new_value) {}
|
||||||
|
|
||||||
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float get_output_value() override { return this->value_.value(); }
|
||||||
|
TemplatableValue<float> value_; // 16 bytes (configured output value, can be lambda)
|
||||||
|
// Total: 8 (base) + 16 = 24 bytes + vtable ptr + Component overhead
|
||||||
};
|
};
|
||||||
|
|
||||||
class DebounceFilter : public Filter, public Component {
|
class DebounceFilter : public Filter, public Component {
|
||||||
|
|||||||
Reference in New Issue
Block a user