ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
exponential_moving_average_sensor.cpp
Go to the documentation of this file.
3#include "esphome/core/log.h"
4
5#include <cmath>
6
8
9ESPHOME_LOG_TAG(TAG, "exponential_moving_average");
10
11const LogString *time_weighting_to_string(TimeWeighting weighting) {
12 switch (weighting) {
14 return LOG_STR("previous");
16 return LOG_STR("linear");
17 default:
18 return LOG_STR("new");
19 }
20}
21
23 if (ms < 1000)
24 return {static_cast<float>(ms), LOG_STR("ms"), 0};
25 if (ms < 60 * 1000)
26 return {ms / 1000.0f, LOG_STR("s"), 1};
27 if (ms < 60 * 60 * 1000)
28 return {ms / (60 * 1000.0f), LOG_STR("min"), 1};
29 return {ms / (60 * 60 * 1000.0f), LOG_STR("h"), 1};
30}
31
33 if (this->restore_) {
35 float restored;
36 if (this->pref_.load(&restored) && std::isfinite(restored)) {
37 this->accumulator_ = restored;
38 this->publish_state(restored);
39 }
40 }
42 this->last_update_ = now;
44 [this](float value) { this->process_(value, App.get_loop_component_start_time()); });
45 // The source may have published during its own setup(), before the callback was added.
46 if (this->source_->has_state())
47 this->process_(this->source_->state, now);
48}
49
51 LOG_SENSOR("", "Exponential Moving Average Sensor", this);
52 if (this->time_constant_ms_ != 0) {
53 const ScaledDuration time_constant = scale_duration(this->time_constant_ms_);
54 ESP_LOGCONFIG(TAG,
55 " Time Constant: %.*f %s\n"
56 " Time Weighting: %s",
57 time_constant.decimals, time_constant.value, LOG_STR_ARG(time_constant.unit),
58 LOG_STR_ARG(time_weighting_to_string(this->time_weighting_)));
59 } else {
60 ESP_LOGCONFIG(TAG, " Alpha: %.3f", this->alpha_);
61 }
62 ESP_LOGCONFIG(TAG, " Restore: %s", YESNO(this->restore_));
63}
64
66
68 if (std::isnan(value))
69 return;
70 // After a reboot the downtime is unknown, so the first interval is measured from setup().
71 const uint32_t dt = now - this->last_update_;
72 this->last_update_ = now;
73 const float previous = this->previous_value_;
74 this->previous_value_ = value;
75 if (std::isnan(this->accumulator_)) {
76 this->publish_and_save_(value);
77 return;
78 }
79 if (this->time_constant_ms_ == 0) {
80 this->publish_and_save_(this->alpha_ * value + (1.0f - this->alpha_) * this->accumulator_);
81 return;
82 }
83 // Computed in double with expm1(): when the interval is short compared to the time constant, the weights are
84 // tiny and float rounding of exp() would swamp them.
85 const double x = static_cast<double>(dt) / this->time_constant_ms_;
86 // The share of the old average replaced during this interval.
87 const double gain = -std::expm1(-x);
88 const double average = this->accumulator_;
89 // After a reboot there is no previous reading, so only the new value can be used.
90 const TimeWeighting weighting = std::isnan(previous) ? TIME_WEIGHTING_NEW : this->time_weighting_;
91 double result;
92 switch (weighting) {
94 result = average + gain * (previous - average);
95 break;
97 // Exact result for a value moving in a straight line from the previous reading to the new one.
98 const double weight_new = x > 0.0 ? (x + std::expm1(-x)) / x : 0.0;
99 result = average + (gain - weight_new) * (previous - average) + weight_new * (value - average);
100 break;
101 }
102 default:
103 result = average + gain * (value - average);
104 break;
105 }
106 this->publish_and_save_(static_cast<float>(result));
107}
108
110 this->accumulator_ = value;
111 this->publish_state(value);
112 if (this->restore_)
113 this->pref_.save(&value);
114}
115
116} // namespace esphome::exponential_moving_average
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.
ESPPreferenceObject make_entity_preference(uint32_t version=0)
Create a preference object for storing this entity's state/settings.
bool has_state() const
void add_on_state_callback(F &&callback)
Add a callback that will be called every time a filtered value arrives.
Definition sensor.h:121
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
float state
This member variable stores the last state that has passed through all filters.
Definition sensor.h:140
AlsGain501 gain
ESPHOME_LOG_TAG(TAG, "exponential_moving_average")
const LogString * time_weighting_to_string(TimeWeighting weighting)
TimeWeighting
Which value is assumed to apply during the time between two readings, when a time constant is used.
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
A duration in the largest of ms, s, min or h that keeps the value at 1 or more.
uint16_t x
Definition tt21100.cpp:5