1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 07:31:51 +00:00

[sensor] Optimize filter memory usage with ValueListFilter base class (#11407)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
J. Nick Koston
2025-10-20 10:38:49 -10:00
committed by GitHub
parent e23d66a8cf
commit 1706a69fad
3 changed files with 59 additions and 49 deletions

View File

@@ -261,9 +261,12 @@ ThrottleAverageFilter = sensor_ns.class_("ThrottleAverageFilter", Filter, cg.Com
LambdaFilter = sensor_ns.class_("LambdaFilter", Filter) LambdaFilter = sensor_ns.class_("LambdaFilter", Filter)
OffsetFilter = sensor_ns.class_("OffsetFilter", Filter) OffsetFilter = sensor_ns.class_("OffsetFilter", Filter)
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter) MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter) ValueListFilter = sensor_ns.class_("ValueListFilter", Filter)
FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", ValueListFilter)
ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter) ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter)
ThrottleWithPriorityFilter = sensor_ns.class_("ThrottleWithPriorityFilter", Filter) ThrottleWithPriorityFilter = sensor_ns.class_(
"ThrottleWithPriorityFilter", ValueListFilter
)
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component) TimeoutFilter = sensor_ns.class_("TimeoutFilter", 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)

View File

@@ -228,27 +228,40 @@ MultiplyFilter::MultiplyFilter(TemplatableValue<float> multiplier) : multiplier_
optional<float> MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); } optional<float> MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); }
// FilterOutValueFilter // ValueListFilter (base class)
FilterOutValueFilter::FilterOutValueFilter(std::vector<TemplatableValue<float>> values_to_filter_out) ValueListFilter::ValueListFilter(std::initializer_list<TemplatableValue<float>> values) : values_(values) {}
: values_to_filter_out_(std::move(values_to_filter_out)) {}
optional<float> FilterOutValueFilter::new_value(float value) { bool ValueListFilter::value_matches_any_(float sensor_value) {
int8_t accuracy = this->parent_->get_accuracy_decimals(); int8_t accuracy = this->parent_->get_accuracy_decimals();
float accuracy_mult = powf(10.0f, accuracy); float accuracy_mult = powf(10.0f, accuracy);
for (auto filter_value : this->values_to_filter_out_) { float rounded_sensor = roundf(accuracy_mult * sensor_value);
if (std::isnan(filter_value.value())) {
if (std::isnan(value)) { for (auto &filter_value : this->values_) {
return {}; float fv = filter_value.value();
}
// Handle NaN comparison
if (std::isnan(fv)) {
if (std::isnan(sensor_value))
return true;
continue; continue;
} }
float rounded_filter_out = roundf(accuracy_mult * filter_value.value());
float rounded_value = roundf(accuracy_mult * value); // Compare rounded values
if (rounded_filter_out == rounded_value) { if (roundf(accuracy_mult * fv) == rounded_sensor)
return {}; return true;
}
} }
return value;
return false;
}
// FilterOutValueFilter
FilterOutValueFilter::FilterOutValueFilter(std::initializer_list<TemplatableValue<float>> values_to_filter_out)
: ValueListFilter(values_to_filter_out) {}
optional<float> FilterOutValueFilter::new_value(float value) {
if (this->value_matches_any_(value))
return {}; // Filter out
return value; // Pass through
} }
// ThrottleFilter // ThrottleFilter
@@ -263,33 +276,15 @@ optional<float> ThrottleFilter::new_value(float value) {
} }
// ThrottleWithPriorityFilter // ThrottleWithPriorityFilter
ThrottleWithPriorityFilter::ThrottleWithPriorityFilter(uint32_t min_time_between_inputs, ThrottleWithPriorityFilter::ThrottleWithPriorityFilter(
std::vector<TemplatableValue<float>> prioritized_values) uint32_t min_time_between_inputs, std::initializer_list<TemplatableValue<float>> prioritized_values)
: min_time_between_inputs_(min_time_between_inputs), prioritized_values_(std::move(prioritized_values)) {} : ValueListFilter(prioritized_values), min_time_between_inputs_(min_time_between_inputs) {}
optional<float> ThrottleWithPriorityFilter::new_value(float value) { optional<float> ThrottleWithPriorityFilter::new_value(float value) {
bool is_prioritized_value = false;
int8_t accuracy = this->parent_->get_accuracy_decimals();
float accuracy_mult = powf(10.0f, accuracy);
const uint32_t now = App.get_loop_component_start_time(); const uint32_t now = App.get_loop_component_start_time();
// First, determine if the new value is one of the prioritized values // Allow value through if: no previous input, time expired, or is prioritized
for (auto prioritized_value : this->prioritized_values_) { if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ ||
if (std::isnan(prioritized_value.value())) { this->value_matches_any_(value)) {
if (std::isnan(value)) {
is_prioritized_value = true;
break;
}
continue;
}
float rounded_prioritized_value = roundf(accuracy_mult * prioritized_value.value());
float rounded_value = roundf(accuracy_mult * value);
if (rounded_prioritized_value == rounded_value) {
is_prioritized_value = true;
break;
}
}
// Finally, determine if the new value should be throttled and pass it through if not
if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ || is_prioritized_value) {
this->last_input_ = now; this->last_input_ = now;
return value; return value;
} }

View File

@@ -317,15 +317,28 @@ class MultiplyFilter : public Filter {
TemplatableValue<float> multiplier_; TemplatableValue<float> multiplier_;
}; };
/** Base class for filters that compare sensor values against a list of configured values.
*
* This base class provides common functionality for filters that need to check if a sensor
* value matches any value in a configured list, with proper handling of NaN values and
* accuracy-based rounding for comparisons.
*/
class ValueListFilter : public Filter {
protected:
explicit ValueListFilter(std::initializer_list<TemplatableValue<float>> values);
/// Check if sensor value matches any configured value (with accuracy rounding)
bool value_matches_any_(float sensor_value);
FixedVector<TemplatableValue<float>> values_;
};
/// A simple filter that only forwards the filter chain if it doesn't receive `value_to_filter_out`. /// A simple filter that only forwards the filter chain if it doesn't receive `value_to_filter_out`.
class FilterOutValueFilter : public Filter { class FilterOutValueFilter : public ValueListFilter {
public: public:
explicit FilterOutValueFilter(std::vector<TemplatableValue<float>> values_to_filter_out); explicit FilterOutValueFilter(std::initializer_list<TemplatableValue<float>> values_to_filter_out);
optional<float> new_value(float value) override; optional<float> new_value(float value) override;
protected:
std::vector<TemplatableValue<float>> values_to_filter_out_;
}; };
class ThrottleFilter : public Filter { class ThrottleFilter : public Filter {
@@ -340,17 +353,16 @@ class ThrottleFilter : public Filter {
}; };
/// Same as 'throttle' but will immediately publish values contained in `value_to_prioritize`. /// Same as 'throttle' but will immediately publish values contained in `value_to_prioritize`.
class ThrottleWithPriorityFilter : public Filter { class ThrottleWithPriorityFilter : public ValueListFilter {
public: public:
explicit ThrottleWithPriorityFilter(uint32_t min_time_between_inputs, explicit ThrottleWithPriorityFilter(uint32_t min_time_between_inputs,
std::vector<TemplatableValue<float>> prioritized_values); std::initializer_list<TemplatableValue<float>> prioritized_values);
optional<float> new_value(float value) override; optional<float> new_value(float value) override;
protected: protected:
uint32_t last_input_{0}; uint32_t last_input_{0};
uint32_t min_time_between_inputs_; uint32_t min_time_between_inputs_;
std::vector<TemplatableValue<float>> prioritized_values_;
}; };
class TimeoutFilter : public Filter, public Component { class TimeoutFilter : public Filter, public Component {