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:
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user