ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
filter.cpp
Go to the documentation of this file.
2#ifdef USE_SENSOR_FILTER
3
4#include "filter.h"
5#include <cmath>
7#include "esphome/core/hal.h"
9#include "esphome/core/log.h"
10#include "sensor.h"
11
12namespace esphome::sensor {
13
14static const char *const TAG = "sensor.filter";
15
16// Filter scheduler IDs.
17// Each filter is its own Component instance, so the scheduler scopes
18// IDs by component pointer — no risk of collisions between instances.
19constexpr uint32_t FILTER_ID = 0;
20
21// Filter
22void Filter::input(float value) {
23 ESP_LOGVV(TAG, "Filter(%p)::input(%f)", this, value);
24 optional<float> out = this->new_value(value);
25 if (out.has_value())
26 this->output(*out);
27}
28void Filter::output(float value) {
29 if (this->next_ == nullptr) {
30 ESP_LOGVV(TAG, "Filter(%p)::output(%f) -> SENSOR", this, value);
32 } else {
33 ESP_LOGVV(TAG, "Filter(%p)::output(%f) -> %p", this, value, this->next_);
34 this->next_->input(value);
35 }
36}
37void Filter::initialize(Sensor *parent, Filter *next) {
38 ESP_LOGVV(TAG, "Filter(%p)::initialize(parent=%p next=%p)", this, parent, next);
39 this->parent_ = parent;
40 this->next_ = next;
41}
42
43// SlidingWindowFilter
44SlidingWindowFilter::SlidingWindowFilter(size_t window_size, size_t send_every, size_t send_first_at)
45 : window_size_(window_size), send_every_(send_every), send_at_(send_every - send_first_at) {
46 // Allocate ring buffer once at initialization
47 this->window_.init(window_size);
48}
49
51 // Add value to ring buffer
53 // Buffer not yet full - just append
54 this->window_.push_back(value);
56 } else {
57 // Buffer full - overwrite oldest value (ring buffer)
58 this->window_[this->window_head_] = value;
59 this->window_head_++;
60 if (this->window_head_ >= this->window_size_) {
61 this->window_head_ = 0;
62 }
63 }
64
65 // Check if we should send a result
66 if (++this->send_at_ >= this->send_every_) {
67 this->send_at_ = 0;
68 float result = this->compute_result();
69 ESP_LOGVV(TAG, "SlidingWindowFilter(%p)::new_value(%f) SENDING %f", this, value, result);
70 return result;
71 }
72 return {};
73}
74
75// SortedWindowFilter
77 // Copy window without NaN values using FixedVector (no heap allocation)
78 // Returns unsorted values - caller will use std::nth_element for partial sorting as needed
79 FixedVector<float> values;
80 values.init(this->window_count_);
81 for (size_t i = 0; i < this->window_count_; i++) {
82 float v = this->window_[i];
83 if (!std::isnan(v)) {
84 values.push_back(v);
85 }
86 }
87 return values;
88}
89
90// MedianFilter
93 if (values.empty())
94 return NAN;
95
96 size_t size = values.size();
97 size_t mid = size / 2;
98
99 if (size % 2) {
100 // Odd number of elements - use nth_element to find middle element
101 std::nth_element(values.begin(), values.begin() + mid, values.end());
102 return values[mid];
103 }
104 // Even number of elements - need both middle elements
105 // Use nth_element to find upper middle element
106 std::nth_element(values.begin(), values.begin() + mid, values.end());
107 float upper = values[mid];
108 // Find the maximum of the lower half (which is now everything before mid)
109 float lower = *std::max_element(values.begin(), values.begin() + mid);
110 return (lower + upper) / 2.0f;
111}
112
113// SkipInitialFilter
114SkipInitialFilter::SkipInitialFilter(size_t num_to_ignore) : num_to_ignore_(num_to_ignore) {}
116 if (num_to_ignore_ > 0) {
118 ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SKIPPING, %zu left", this, value, num_to_ignore_);
119 return {};
120 }
121
122 ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SENDING", this, value);
123 return value;
124}
125
126// QuantileFilter
127QuantileFilter::QuantileFilter(size_t window_size, size_t send_every, size_t send_first_at, float quantile)
128 : SortedWindowFilter(window_size, send_every, send_first_at), quantile_(quantile) {}
129
131 FixedVector<float> values = this->get_window_values_();
132 if (values.empty())
133 return NAN;
134
135 size_t position = ceilf(values.size() * this->quantile_) - 1;
136 ESP_LOGVV(TAG, "QuantileFilter(%p)::position: %zu/%zu", this, position + 1, values.size());
137
138 // Use nth_element to find the quantile element (O(n) instead of O(n log n))
139 std::nth_element(values.begin(), values.begin() + position, values.end());
140 return values[position];
141}
142
143// MinFilter
145
146// MaxFilter
148
149// SlidingWindowMovingAverageFilter
151 float sum = 0;
152 size_t valid_count = 0;
153 for (size_t i = 0; i < this->window_count_; i++) {
154 float v = this->window_[i];
155 if (!std::isnan(v)) {
156 sum += v;
157 valid_count++;
158 }
159 }
160 return valid_count ? sum / valid_count : NAN;
161}
162
163// ExponentialMovingAverageFilter
164ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every, size_t send_first_at)
165 : alpha_(alpha), send_every_(send_every), send_at_(send_every - send_first_at) {}
167 if (!std::isnan(value)) {
168 if (this->first_value_) {
169 this->accumulator_ = value;
170 this->first_value_ = false;
171 } else {
172 this->accumulator_ = (this->alpha_ * value) + (1.0f - this->alpha_) * this->accumulator_;
173 }
174 }
175
176 const float average = std::isnan(value) ? value : this->accumulator_;
177 ESP_LOGVV(TAG, "ExponentialMovingAverageFilter(%p)::new_value(%f) -> %f", this, value, average);
178
179 if (++this->send_at_ >= this->send_every_) {
180 ESP_LOGVV(TAG, "ExponentialMovingAverageFilter(%p)::new_value(%f) SENDING %f", this, value, average);
181 this->send_at_ = 0;
182 return average;
183 }
184 return {};
185}
186void ExponentialMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; }
187void ExponentialMovingAverageFilter::set_alpha(float alpha) { this->alpha_ = alpha; }
188
189// ThrottleAverageFilter
190ThrottleAverageFilter::ThrottleAverageFilter(uint32_t time_period) : time_period_(time_period) {}
191
193 ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::new_value(value=%f)", this, value);
194 if (std::isnan(value)) {
195 this->have_nan_ = true;
196 } else {
197 this->sum_ += value;
198 this->n_++;
199 }
200 return {};
201}
203 this->set_interval(FILTER_ID, this->time_period_, [this]() {
204 ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::interval(sum=%f, n=%i)", this, this->sum_, this->n_);
205 if (this->n_ == 0) {
206 if (this->have_nan_)
207 this->output(NAN);
208 } else {
209 this->output(this->sum_ / this->n_);
210 this->sum_ = 0.0f;
211 this->n_ = 0;
212 }
213 this->have_nan_ = false;
214 });
215}
217
218// LambdaFilter
219LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {}
221void LambdaFilter::set_lambda_filter(const lambda_filter_t &lambda_filter) { this->lambda_filter_ = lambda_filter; }
222
224 auto it = this->lambda_filter_(value);
225 ESP_LOGVV(TAG, "LambdaFilter(%p)::new_value(%f) -> %f", this, value, it.value_or(INFINITY));
226 return it;
227}
228
229// OffsetFilter
230OffsetFilter::OffsetFilter(TemplatableValue<float> offset) : offset_(std::move(offset)) {}
231
232optional<float> OffsetFilter::new_value(float value) { return value + this->offset_.value(); }
233
234// MultiplyFilter
235MultiplyFilter::MultiplyFilter(TemplatableValue<float> multiplier) : multiplier_(std::move(multiplier)) {}
236
237optional<float> MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); }
238
239// ValueListFilter (base class)
240ValueListFilter::ValueListFilter(std::initializer_list<TemplatableValue<float>> values) : values_(values) {}
241
242bool ValueListFilter::value_matches_any_(float sensor_value) {
243 int8_t accuracy = this->parent_->get_accuracy_decimals();
244 float accuracy_mult = pow10_int(accuracy);
245 float rounded_sensor = roundf(accuracy_mult * sensor_value);
246
247 for (auto &filter_value : this->values_) {
248 float fv = filter_value.value();
249
250 // Handle NaN comparison
251 if (std::isnan(fv)) {
252 if (std::isnan(sensor_value))
253 return true;
254 continue;
255 }
256
257 // Compare rounded values
258 if (roundf(accuracy_mult * fv) == rounded_sensor)
259 return true;
260 }
261
262 return false;
263}
264
265// FilterOutValueFilter
266FilterOutValueFilter::FilterOutValueFilter(std::initializer_list<TemplatableValue<float>> values_to_filter_out)
267 : ValueListFilter(values_to_filter_out) {}
268
270 if (this->value_matches_any_(value))
271 return {}; // Filter out
272 return value; // Pass through
273}
274
275// ThrottleFilter
276ThrottleFilter::ThrottleFilter(uint32_t min_time_between_inputs) : min_time_between_inputs_(min_time_between_inputs) {}
278 const uint32_t now = App.get_loop_component_start_time();
279 if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_) {
280 this->last_input_ = now;
281 return value;
282 }
283 return {};
284}
285
286// ThrottleWithPriorityFilter
288 uint32_t min_time_between_inputs, std::initializer_list<TemplatableValue<float>> prioritized_values)
289 : ValueListFilter(prioritized_values), min_time_between_inputs_(min_time_between_inputs) {}
290
292 const uint32_t now = App.get_loop_component_start_time();
293 // Allow value through if: no previous input, time expired, or is prioritized
294 if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ ||
295 this->value_matches_any_(value)) {
296 this->last_input_ = now;
297 return value;
298 }
299 return {};
300}
301
302// DeltaFilter
303DeltaFilter::DeltaFilter(float min_a0, float min_a1, float max_a0, float max_a1)
304 : min_a0_(min_a0), min_a1_(min_a1), max_a0_(max_a0), max_a1_(max_a1) {}
305
306void DeltaFilter::set_baseline(float (*fn)(float)) { this->baseline_ = fn; }
307
309 // Always yield the first value.
310 if (std::isnan(this->last_value_)) {
311 this->last_value_ = value;
312 return value;
313 }
314 // calculate min and max using the linear equation
315 float ref = this->baseline_(this->last_value_);
316 float min = fabsf(this->min_a0_ + ref * this->min_a1_);
317 float max = fabsf(this->max_a0_ + ref * this->max_a1_);
318 float delta = fabsf(value - ref);
319 // if there is no reference, e.g. for the first value, just accept this one,
320 // otherwise accept only if within range.
321 if (delta > min && delta <= max) {
322 this->last_value_ = value;
323 return value;
324 }
325 return {};
326}
327
328// OrFilter
329OrFilter::OrFilter(std::initializer_list<Filter *> filters) : filters_(filters), phi_(this) {}
330OrFilter::PhiNode::PhiNode(OrFilter *or_parent) : or_parent_(or_parent) {}
331
333 if (!this->or_parent_->has_value_) {
334 this->or_parent_->output(value);
335 this->or_parent_->has_value_ = true;
336 }
337
338 return {};
339}
341 this->has_value_ = false;
342 for (auto *filter : this->filters_)
343 filter->input(value);
344
345 return {};
346}
347void OrFilter::initialize(Sensor *parent, Filter *next) {
348 Filter::initialize(parent, next);
349 for (auto *filter : this->filters_) {
350 filter->initialize(parent, &this->phi_);
351 }
352 this->phi_.initialize(parent, nullptr);
353}
354
355// TimeoutFilterBase - shared loop logic
357 // Check if timeout period has elapsed
358 // Use cached loop start time to avoid repeated millis() calls
359 const uint32_t now = App.get_loop_component_start_time();
360 if (now - this->timeout_start_time_ >= this->time_period_) {
361 // Timeout fired - get output value from derived class and output it
362 this->output(this->get_output_value());
363
364 // Disable loop until next value arrives
365 this->disable_loop();
366 }
367}
368
370
371// TimeoutFilterLast - "last" mode implementation
373 // Store the value to output when timeout fires
374 this->pending_value_ = value;
375
376 // Record when timeout started and enable loop
377 this->timeout_start_time_ = millis();
378 this->enable_loop();
379
380 return value;
381}
382
383// TimeoutFilterConfigured - configured value mode implementation
385 // Record when timeout started and enable loop
386 // Note: we don't store the incoming value since we have a configured value
387 this->timeout_start_time_ = millis();
388 this->enable_loop();
389
390 return value;
391}
392
393// DebounceFilter
395 this->set_timeout(FILTER_ID, this->time_period_, [this, value]() { this->output(value); });
396
397 return {};
398}
399
400DebounceFilter::DebounceFilter(uint32_t time_period) : time_period_(time_period) {}
402
403// HeartbeatFilter
404HeartbeatFilter::HeartbeatFilter(uint32_t time_period) : time_period_(time_period), last_input_(NAN) {}
405
407 ESP_LOGVV(TAG, "HeartbeatFilter(%p)::new_value(value=%f)", this, value);
408 this->last_input_ = value;
409 this->has_value_ = true;
410
411 if (this->optimistic_) {
412 return value;
413 }
414 return {};
415}
416
418 this->set_interval(FILTER_ID, this->time_period_, [this]() {
419 ESP_LOGVV(TAG, "HeartbeatFilter(%p)::interval(has_value=%s, last_input=%f)", this, YESNO(this->has_value_),
420 this->last_input_);
421 if (!this->has_value_)
422 return;
423
424 this->output(this->last_input_);
425 });
426}
427
429
430CalibrateLinearFilter::CalibrateLinearFilter(std::initializer_list<std::array<float, 3>> linear_functions)
431 : linear_functions_(linear_functions) {}
432
434 for (const auto &f : this->linear_functions_) {
435 if (!std::isfinite(f[2]) || value < f[2])
436 return (value * f[0]) + f[1];
437 }
438 return NAN;
439}
440
441CalibratePolynomialFilter::CalibratePolynomialFilter(std::initializer_list<float> coefficients)
442 : coefficients_(coefficients) {}
443
445 float res = 0.0f;
446 float x = 1.0f;
447 for (const auto &coefficient : this->coefficients_) {
448 res += x * coefficient;
449 x *= value;
450 }
451 return res;
452}
453
454ClampFilter::ClampFilter(float min, float max, bool ignore_out_of_range)
455 : min_(min), max_(max), ignore_out_of_range_(ignore_out_of_range) {}
457 if (std::isfinite(this->min_) && !(value >= this->min_)) {
458 if (this->ignore_out_of_range_) {
459 return {};
460 }
461 return this->min_;
462 }
463
464 if (std::isfinite(this->max_) && !(value <= this->max_)) {
465 if (this->ignore_out_of_range_) {
466 return {};
467 }
468 return this->max_;
469 }
470 return value;
471}
472
473RoundFilter::RoundFilter(uint8_t precision) : precision_(precision) {}
475 if (std::isfinite(value)) {
476 float accuracy_mult = pow10_int(this->precision_);
477 return roundf(accuracy_mult * value) / accuracy_mult;
478 }
479 return value;
480}
481
482RoundMultipleFilter::RoundMultipleFilter(float multiple) : multiple_(multiple) {}
484 if (std::isfinite(value)) {
485 return value - remainderf(value, this->multiple_);
486 }
487 return value;
488}
489
491 if (!std::isfinite(value)) {
492 return NAN;
493 }
494 double k = 273.15;
495 // https://de.wikipedia.org/wiki/Steinhart-Hart-Gleichung#cite_note-stein2_s4-3
496 double t = value + k;
497 double y = (this->a_ - 1 / (t)) / (2 * this->c_);
498 double x = sqrt(pow(this->b_ / (3 * this->c_), 3) + y * y);
499 double resistance = exp(pow(x - y, 1 / 3.0) - pow(x + y, 1 / 3.0));
500 return resistance;
501}
502
504 if (!std::isfinite(value)) {
505 return NAN;
506 }
507 double lr = log(double(value));
508 double v = this->a_ + this->b_ * lr + this->c_ * lr * lr * lr;
509 double temp = float(1.0 / v - 273.15);
510 return temp;
511}
512
513// StreamingFilter (base class)
514StreamingFilter::StreamingFilter(size_t window_size, size_t send_first_at)
515 : window_size_(window_size), send_first_at_(send_first_at) {}
516
518 // Process the value (child class tracks min/max/sum/etc)
519 this->process_value(value);
520
521 this->count_++;
522
523 // Check if we should send (handle send_first_at for first value)
524 bool should_send = false;
525 if (this->first_send_ && this->count_ >= this->send_first_at_) {
526 should_send = true;
527 this->first_send_ = false;
528 } else if (!this->first_send_ && this->count_ >= this->window_size_) {
529 should_send = true;
530 }
531
532 if (should_send) {
533 float result = this->compute_batch_result();
534 // Reset for next batch
535 this->count_ = 0;
536 this->reset_batch();
537 ESP_LOGVV(TAG, "StreamingFilter(%p)::new_value(%f) SENDING %f", this, value, result);
538 return result;
539 }
540
541 return {};
542}
543
544// StreamingMinFilter
546 // Update running minimum (ignore NaN values)
547 if (!std::isnan(value)) {
548 this->current_min_ = std::isnan(this->current_min_) ? value : std::min(this->current_min_, value);
549 }
550}
551
553
555
556// StreamingMaxFilter
558 // Update running maximum (ignore NaN values)
559 if (!std::isnan(value)) {
560 this->current_max_ = std::isnan(this->current_max_) ? value : std::max(this->current_max_, value);
561 }
562}
563
565
567
568// StreamingMovingAverageFilter
570 // Accumulate sum (ignore NaN values)
571 if (!std::isnan(value)) {
572 this->sum_ += value;
574 }
575}
576
578 return this->valid_count_ > 0 ? this->sum_ / this->valid_count_ : NAN;
579}
580
582 this->sum_ = 0.0f;
583 this->valid_count_ = 0;
584}
585
586} // namespace esphome::sensor
587
588#endif // USE_SENSOR_FILTER
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_interval(const std voi set_interval)(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
Definition component.h:350
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:299
bool empty() const
Definition helpers.h:457
size_t size() const
Definition helpers.h:456
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:416
void init(size_t n)
Definition helpers.h:389
T value(X... x) const
Definition automation.h:160
bool has_value() const
Definition optional.h:92
FixedVector< std::array< float, 3 > > linear_functions_
Definition filter.h:503
optional< float > new_value(float value) override
Definition filter.cpp:433
CalibrateLinearFilter(std::initializer_list< std::array< float, 3 > > linear_functions)
Definition filter.cpp:430
CalibratePolynomialFilter(std::initializer_list< float > coefficients)
Definition filter.cpp:441
optional< float > new_value(float value) override
Definition filter.cpp:444
ClampFilter(float min, float max, bool ignore_out_of_range)
Definition filter.cpp:454
optional< float > new_value(float value) override
Definition filter.cpp:456
optional< float > new_value(float value) override
Definition filter.cpp:394
float get_setup_priority() const override
Definition filter.cpp:401
DebounceFilter(uint32_t time_period)
Definition filter.cpp:400
void set_baseline(float(*fn)(float))
Definition filter.cpp:306
DeltaFilter(float min_a0, float min_a1, float max_a0, float max_a1)
Definition filter.cpp:303
optional< float > new_value(float value) override
Definition filter.cpp:308
float(* baseline_)(float)
Definition filter.h:469
optional< float > new_value(float value) override
Definition filter.cpp:166
ExponentialMovingAverageFilter(float alpha, size_t send_every, size_t send_first_at)
Definition filter.cpp:164
Apply a filter to sensor values such as moving average.
Definition filter.h:21
virtual optional< float > new_value(float value)=0
This will be called every time the filter receives a new value.
void output(float value)
Definition filter.cpp:28
virtual void initialize(Sensor *parent, Filter *next)
Initialize this filter, please note this can be called more than once.
Definition filter.cpp:37
void input(float value)
Definition filter.cpp:22
optional< float > new_value(float value) override
Definition filter.cpp:269
FilterOutValueFilter(std::initializer_list< TemplatableValue< float > > values_to_filter_out)
Definition filter.cpp:266
HeartbeatFilter(uint32_t time_period)
Definition filter.cpp:404
optional< float > new_value(float value) override
Definition filter.cpp:406
float get_setup_priority() const override
Definition filter.cpp:428
const lambda_filter_t & get_lambda_filter() const
Definition filter.cpp:220
LambdaFilter(lambda_filter_t lambda_filter)
Definition filter.cpp:219
lambda_filter_t lambda_filter_
Definition filter.h:297
void set_lambda_filter(const lambda_filter_t &lambda_filter)
Definition filter.cpp:221
optional< float > new_value(float value) override
Definition filter.cpp:223
float compute_result() override
Definition filter.cpp:147
float compute_result() override
Definition filter.cpp:91
float compute_result() override
Definition filter.cpp:144
float find_extremum_()
Helper to find min or max value in window, skipping NaN values Usage: find_extremum_<std::less<float>...
Definition filter.h:84
optional< float > new_value(float value) override
Definition filter.cpp:237
MultiplyFilter(TemplatableValue< float > multiplier)
Definition filter.cpp:235
TemplatableValue< float > multiplier_
Definition filter.h:333
optional< float > new_value(float value) override
Definition filter.cpp:232
OffsetFilter(TemplatableValue< float > offset)
Definition filter.cpp:230
TemplatableValue< float > offset_
Definition filter.h:323
optional< float > new_value(float value) override
Definition filter.cpp:332
PhiNode(OrFilter *or_parent)
Definition filter.cpp:330
OrFilter(std::initializer_list< Filter * > filters)
Definition filter.cpp:329
optional< float > new_value(float value) override
Definition filter.cpp:340
void initialize(Sensor *parent, Filter *next) override
Definition filter.cpp:347
FixedVector< Filter * > filters_
Definition filter.h:492
float compute_result() override
Definition filter.cpp:130
QuantileFilter(size_t window_size, size_t send_every, size_t send_first_at, float quantile)
Construct a QuantileFilter.
Definition filter.cpp:127
RoundFilter(uint8_t precision)
Definition filter.cpp:473
optional< float > new_value(float value) override
Definition filter.cpp:474
optional< float > new_value(float value) override
Definition filter.cpp:483
Base-class for all sensors.
Definition sensor.h:47
void internal_send_state_to_frontend(float state)
Definition sensor.cpp:121
int8_t get_accuracy_decimals()
Get the accuracy in decimals, using the manual override if set.
Definition sensor.cpp:45
SkipInitialFilter(size_t num_to_ignore)
Construct a SkipInitialFilter.
Definition filter.cpp:114
optional< float > new_value(float value) override
Definition filter.cpp:115
size_t window_head_
Index where next value will be written.
Definition filter.h:66
size_t window_size_
Maximum window size.
Definition filter.h:68
optional< float > new_value(float value) final
Definition filter.cpp:50
size_t window_count_
Number of valid values in window (0 to window_size_)
Definition filter.h:67
FixedVector< float > window_
Access the sliding window values (ring buffer implementation) Use: for (size_t i = 0; i < window_coun...
Definition filter.h:65
size_t send_at_
Counter for send_every.
Definition filter.h:70
size_t send_every_
Send result every N values.
Definition filter.h:69
virtual float compute_result()=0
Called by new_value() to compute the filtered result from the current window.
SlidingWindowFilter(size_t window_size, size_t send_every, size_t send_first_at)
Definition filter.cpp:44
Base class for filters that need a sorted window (Median, Quantile).
Definition filter.h:102
FixedVector< float > get_window_values_()
Helper to get non-NaN values from the window (not sorted - caller will use nth_element) Returns empty...
Definition filter.cpp:76
virtual void process_value(float value)=0
Called by new_value() to process each value in the batch.
virtual float compute_batch_result()=0
Called by new_value() to compute the result after collecting window_size values.
StreamingFilter(size_t window_size, size_t send_first_at)
Definition filter.cpp:514
virtual void reset_batch()=0
Called by new_value() to reset internal state after sending a result.
optional< float > new_value(float value) final
Definition filter.cpp:517
void process_value(float value) override
Definition filter.cpp:557
float compute_batch_result() override
Definition filter.cpp:564
float compute_batch_result() override
Definition filter.cpp:552
void process_value(float value) override
Definition filter.cpp:545
void process_value(float value) override
Definition filter.cpp:569
optional< float > new_value(float value) override
Definition filter.cpp:192
ThrottleAverageFilter(uint32_t time_period)
Definition filter.cpp:190
float get_setup_priority() const override
Definition filter.cpp:216
ThrottleFilter(uint32_t min_time_between_inputs)
Definition filter.cpp:276
optional< float > new_value(float value) override
Definition filter.cpp:277
optional< float > new_value(float value) override
Definition filter.cpp:291
ThrottleWithPriorityFilter(uint32_t min_time_between_inputs, std::initializer_list< TemplatableValue< float > > prioritized_values)
Definition filter.cpp:287
float get_setup_priority() const override
Definition filter.cpp:369
optional< float > new_value(float value) override
Definition filter.cpp:384
optional< float > new_value(float value) override
Definition filter.cpp:372
optional< float > new_value(float value) override
Definition filter.cpp:490
optional< float > new_value(float value) override
Definition filter.cpp:503
Base class for filters that compare sensor values against a list of configured values.
Definition filter.h:342
ValueListFilter(std::initializer_list< TemplatableValue< float > > values)
Definition filter.cpp:240
FixedVector< TemplatableValue< float > > values_
Definition filter.h:349
bool value_matches_any_(float sensor_value)
Check if sensor value matches any configured value (with accuracy rounding)
Definition filter.cpp:242
float position
Definition cover.h:0
constexpr uint32_t FILTER_ID
Definition filter.cpp:19
std::function< optional< float >(float)> lambda_filter_t
Definition filter.h:278
constexpr float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition component.h:29
size_t size
Definition helpers.h:854
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
float pow10_int(int8_t exp)
Compute 10^exp using iterative multiplication/division.
Definition helpers.h:517
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6