mirror of
https://github.com/esphome/esphome.git
synced 2026-09-13 00:07:32 +00:00
[sensor] Use std::array in ValueList/FilterOut/ThrottleWithPriority filters
Template ValueListFilter, FilterOutValueFilter, and ThrottleWithPriorityFilter on N (set by code generation) to use std::array instead of FixedVector. Non-template helpers value_list_matches_any() and get_loop_component_start_time() keep shared logic in the .cpp to avoid per-instantiation flash duplication.
This commit is contained in:
@@ -397,7 +397,7 @@ async def filter_out_filter_to_code(config, filter_id):
|
||||
if not isinstance(config, list):
|
||||
config = [config]
|
||||
template_ = [await cg.templatable(x, [], float) for x in config]
|
||||
return cg.new_Pvariable(filter_id, template_)
|
||||
return cg.new_Pvariable(filter_id, cg.TemplateArguments(len(template_)), template_)
|
||||
|
||||
|
||||
QUANTILE_SCHEMA = cv.All(
|
||||
@@ -666,7 +666,9 @@ 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, [], float) for x in config[CONF_VALUE]]
|
||||
return cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_)
|
||||
return cg.new_Pvariable(
|
||||
filter_id, cg.TemplateArguments(len(template_)), config[CONF_TIMEOUT], template_
|
||||
)
|
||||
|
||||
|
||||
HEARTBEAT_SCHEMA = cv.Schema(
|
||||
|
||||
@@ -222,16 +222,16 @@ MultiplyFilter::MultiplyFilter(TemplatableValue<float> multiplier) : multiplier_
|
||||
|
||||
optional<float> MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); }
|
||||
|
||||
// ValueListFilter (base class)
|
||||
ValueListFilter::ValueListFilter(std::initializer_list<TemplatableValue<float>> values) : values_(values) {}
|
||||
uint32_t get_loop_component_start_time() { return App.get_loop_component_start_time(); }
|
||||
|
||||
bool ValueListFilter::value_matches_any_(float sensor_value) {
|
||||
int8_t accuracy = this->parent_->get_accuracy_decimals();
|
||||
// ValueListFilter helper (non-template, shared by all ValueListFilter<N> instantiations)
|
||||
bool value_list_matches_any(Sensor *parent, float sensor_value, const TemplatableValue<float> *values, size_t count) {
|
||||
int8_t accuracy = parent->get_accuracy_decimals();
|
||||
float accuracy_mult = pow10_int(accuracy);
|
||||
float rounded_sensor = roundf(accuracy_mult * sensor_value);
|
||||
|
||||
for (auto &filter_value : this->values_) {
|
||||
float fv = filter_value.value();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
float fv = values[i].value();
|
||||
|
||||
// Handle NaN comparison
|
||||
if (std::isnan(fv)) {
|
||||
@@ -248,16 +248,6 @@ bool ValueListFilter::value_matches_any_(float sensor_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::ThrottleFilter(uint32_t min_time_between_inputs) : min_time_between_inputs_(min_time_between_inputs) {}
|
||||
optional<float> ThrottleFilter::new_value(float value) {
|
||||
@@ -269,22 +259,6 @@ optional<float> ThrottleFilter::new_value(float value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// ThrottleWithPriorityFilter
|
||||
ThrottleWithPriorityFilter::ThrottleWithPriorityFilter(
|
||||
uint32_t min_time_between_inputs, std::initializer_list<TemplatableValue<float>> prioritized_values)
|
||||
: ValueListFilter(prioritized_values), min_time_between_inputs_(min_time_between_inputs) {}
|
||||
|
||||
optional<float> ThrottleWithPriorityFilter::new_value(float value) {
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
// Allow value through if: no previous input, time expired, or is prioritized
|
||||
if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ ||
|
||||
this->value_matches_any_(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) {}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_SENSOR_FILTER
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "esphome/core/automation.h"
|
||||
@@ -328,28 +329,50 @@ class MultiplyFilter : public Filter {
|
||||
TemplatableValue<float> multiplier_;
|
||||
};
|
||||
|
||||
/** Base class for filters that compare sensor values against a list of configured values.
|
||||
/// Non-template helper for value matching (implementation in filter.cpp)
|
||||
bool value_list_matches_any(Sensor *parent, float sensor_value, const TemplatableValue<float> *values, size_t count);
|
||||
|
||||
/// Non-template helper to get cached loop start time (avoids circular include of application.h)
|
||||
uint32_t get_loop_component_start_time();
|
||||
|
||||
/** Base class for filters that compare sensor values against a fixed 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.
|
||||
* Templated on N (the number of values) so the list is stored inline in a std::array,
|
||||
* avoiding heap allocation and the overhead of FixedVector.
|
||||
*
|
||||
* @tparam N Number of values in the filter list, set by code generation to match
|
||||
* the exact number of values configured in YAML.
|
||||
*/
|
||||
class ValueListFilter : public Filter {
|
||||
template<size_t N> class ValueListFilter : public Filter {
|
||||
protected:
|
||||
explicit ValueListFilter(std::initializer_list<TemplatableValue<float>> values);
|
||||
explicit ValueListFilter(std::initializer_list<TemplatableValue<float>> values) {
|
||||
size_t i = 0;
|
||||
for (const auto &v : values) {
|
||||
if (i >= N)
|
||||
break;
|
||||
this->values_[i++] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if sensor value matches any configured value (with accuracy rounding)
|
||||
bool value_matches_any_(float sensor_value);
|
||||
bool value_matches_any_(float sensor_value) {
|
||||
return value_list_matches_any(this->parent_, sensor_value, this->values_.data(), N);
|
||||
}
|
||||
|
||||
FixedVector<TemplatableValue<float>> values_;
|
||||
std::array<TemplatableValue<float>, N> values_{};
|
||||
};
|
||||
|
||||
/// A simple filter that only forwards the filter chain if it doesn't receive `value_to_filter_out`.
|
||||
class FilterOutValueFilter : public ValueListFilter {
|
||||
template<size_t N> class FilterOutValueFilter : public ValueListFilter<N> {
|
||||
public:
|
||||
explicit FilterOutValueFilter(std::initializer_list<TemplatableValue<float>> values_to_filter_out);
|
||||
explicit FilterOutValueFilter(std::initializer_list<TemplatableValue<float>> values_to_filter_out)
|
||||
: ValueListFilter<N>(values_to_filter_out) {}
|
||||
|
||||
optional<float> new_value(float value) override;
|
||||
optional<float> new_value(float value) override {
|
||||
if (this->value_matches_any_(value))
|
||||
return {}; // Filter out
|
||||
return value; // Pass through
|
||||
}
|
||||
};
|
||||
|
||||
class ThrottleFilter : public Filter {
|
||||
@@ -364,12 +387,21 @@ class ThrottleFilter : public Filter {
|
||||
};
|
||||
|
||||
/// Same as 'throttle' but will immediately publish values contained in `value_to_prioritize`.
|
||||
class ThrottleWithPriorityFilter : public ValueListFilter {
|
||||
template<size_t N> class ThrottleWithPriorityFilter : public ValueListFilter<N> {
|
||||
public:
|
||||
explicit ThrottleWithPriorityFilter(uint32_t min_time_between_inputs,
|
||||
std::initializer_list<TemplatableValue<float>> prioritized_values);
|
||||
std::initializer_list<TemplatableValue<float>> prioritized_values)
|
||||
: ValueListFilter<N>(prioritized_values), min_time_between_inputs_(min_time_between_inputs) {}
|
||||
|
||||
optional<float> new_value(float value) override;
|
||||
optional<float> new_value(float value) override {
|
||||
const uint32_t now = get_loop_component_start_time();
|
||||
if (this->last_input_ == 0 || now - this->last_input_ >= this->min_time_between_inputs_ ||
|
||||
this->value_matches_any_(value)) {
|
||||
this->last_input_ = now;
|
||||
return value;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t last_input_{0};
|
||||
|
||||
Reference in New Issue
Block a user