[sensor] Specialize throttle_with_priority NaN-only case (#15823)

This commit is contained in:
J. Nick Koston
2026-04-21 04:41:13 +02:00
committed by GitHub
parent 78875abee4
commit f05fa45747
3 changed files with 40 additions and 3 deletions
+15 -3
View File
@@ -275,6 +275,9 @@ ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter)
ThrottleWithPriorityFilter = sensor_ns.class_(
"ThrottleWithPriorityFilter", ValueListFilter
)
ThrottleWithPriorityNanFilter = sensor_ns.class_(
"ThrottleWithPriorityNanFilter", Filter
)
TimeoutFilterBase = sensor_ns.class_("TimeoutFilterBase", Filter, cg.Component)
TimeoutFilterLast = sensor_ns.class_("TimeoutFilterLast", TimeoutFilterBase)
TimeoutFilterConfigured = sensor_ns.class_("TimeoutFilterConfigured", TimeoutFilterBase)
@@ -656,9 +659,18 @@ THROTTLE_WITH_PRIORITY_SCHEMA = cv.maybe_simple_value(
THROTTLE_WITH_PRIORITY_SCHEMA,
)
async def throttle_with_priority_filter_to_code(config, filter_id):
if not isinstance(config[CONF_VALUE], list):
config[CONF_VALUE] = [config[CONF_VALUE]]
template_ = [await cg.templatable(x, [], cg.float_) for x in config[CONF_VALUE]]
values = config[CONF_VALUE]
if not isinstance(values, list):
values = [values]
# Specialize the common "NaN-only" case (the schema default when the user
# omits `value:`) to avoid the TemplatableFn<float> array + NaN lambda the
# generic ValueListFilter path requires. Behavior is identical: NaN sensor
# readings always bypass the throttle.
if values and all(isinstance(v, float) and math.isnan(v) for v in values):
filter_id = filter_id.copy()
filter_id.type = ThrottleWithPriorityNanFilter
return cg.new_Pvariable(filter_id, config[CONF_TIMEOUT])
template_ = [await cg.templatable(x, [], cg.float_) for x in values]
return cg.new_Pvariable(
filter_id, cg.TemplateArguments(len(template_)), config[CONF_TIMEOUT], template_
)
+12
View File
@@ -269,6 +269,18 @@ optional<float> throttle_with_priority_new_value(Sensor *parent, float value, co
return {};
}
// ThrottleWithPriorityNanFilter
ThrottleWithPriorityNanFilter::ThrottleWithPriorityNanFilter(uint32_t min_time_between_inputs)
: min_time_between_inputs_(min_time_between_inputs) {}
optional<float> ThrottleWithPriorityNanFilter::new_value(float value) {
const uint32_t now = App.get_loop_component_start_time();
if (this->last_input_ == 0 || now - this->last_input_ >= this->min_time_between_inputs_ || std::isnan(value)) {
this->last_input_ = now;
return value;
}
return {};
}
// DeltaFilter
DeltaFilter::DeltaFilter(float min_a0, float min_a1, float max_a0, float max_a1)
: min_a0_(min_a0), min_a1_(min_a1), max_a0_(max_a0), max_a1_(max_a1) {}
+13
View File
@@ -399,6 +399,19 @@ template<size_t N> class ThrottleWithPriorityFilter : public ValueListFilter<N>
uint32_t min_time_between_inputs_;
};
/// Specialization of ThrottleWithPriorityFilter for the common "prioritize NaN"
/// case: skips the TemplatableFn<float> array + lambda and inlines the check.
class ThrottleWithPriorityNanFilter : public Filter {
public:
explicit ThrottleWithPriorityNanFilter(uint32_t min_time_between_inputs);
optional<float> new_value(float value) override;
protected:
uint32_t last_input_{0};
uint32_t min_time_between_inputs_;
};
// Base class for timeout filters - contains common loop logic
class TimeoutFilterBase : public Filter, public Component {
public: