mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 08:28:12 +01:00
Add min/max filters (#1569)
This commit is contained in:
parent
fa290fbce8
commit
88b46b7487
4 changed files with 158 additions and 0 deletions
|
@ -72,6 +72,8 @@ SensorPublishAction = sensor_ns.class_('SensorPublishAction', automation.Action)
|
||||||
# Filters
|
# Filters
|
||||||
Filter = sensor_ns.class_('Filter')
|
Filter = sensor_ns.class_('Filter')
|
||||||
MedianFilter = sensor_ns.class_('MedianFilter', Filter)
|
MedianFilter = sensor_ns.class_('MedianFilter', Filter)
|
||||||
|
MinFilter = sensor_ns.class_('MinFilter', Filter)
|
||||||
|
MaxFilter = sensor_ns.class_('MaxFilter', Filter)
|
||||||
SlidingWindowMovingAverageFilter = sensor_ns.class_('SlidingWindowMovingAverageFilter', Filter)
|
SlidingWindowMovingAverageFilter = sensor_ns.class_('SlidingWindowMovingAverageFilter', Filter)
|
||||||
ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter)
|
ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter)
|
||||||
LambdaFilter = sensor_ns.class_('LambdaFilter', Filter)
|
LambdaFilter = sensor_ns.class_('LambdaFilter', Filter)
|
||||||
|
@ -165,6 +167,32 @@ def median_filter_to_code(config, filter_id):
|
||||||
config[CONF_SEND_FIRST_AT])
|
config[CONF_SEND_FIRST_AT])
|
||||||
|
|
||||||
|
|
||||||
|
MIN_SCHEMA = cv.All(cv.Schema({
|
||||||
|
cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int,
|
||||||
|
cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int,
|
||||||
|
cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int,
|
||||||
|
}), validate_send_first_at)
|
||||||
|
|
||||||
|
|
||||||
|
@FILTER_REGISTRY.register('min', MinFilter, MIN_SCHEMA)
|
||||||
|
def min_filter_to_code(config, filter_id):
|
||||||
|
yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY],
|
||||||
|
config[CONF_SEND_FIRST_AT])
|
||||||
|
|
||||||
|
|
||||||
|
MAX_SCHEMA = cv.All(cv.Schema({
|
||||||
|
cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int,
|
||||||
|
cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int,
|
||||||
|
cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int,
|
||||||
|
}), validate_send_first_at)
|
||||||
|
|
||||||
|
|
||||||
|
@FILTER_REGISTRY.register('max', MaxFilter, MAX_SCHEMA)
|
||||||
|
def max_filter_to_code(config, filter_id):
|
||||||
|
yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY],
|
||||||
|
config[CONF_SEND_FIRST_AT])
|
||||||
|
|
||||||
|
|
||||||
SLIDING_AVERAGE_SCHEMA = cv.All(cv.Schema({
|
SLIDING_AVERAGE_SCHEMA = cv.All(cv.Schema({
|
||||||
cv.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int,
|
cv.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int,
|
||||||
cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int,
|
cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int,
|
||||||
|
|
|
@ -77,6 +77,68 @@ optional<float> MedianFilter::new_value(float value) {
|
||||||
|
|
||||||
uint32_t MedianFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
uint32_t MedianFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
||||||
|
|
||||||
|
// MinFilter
|
||||||
|
MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
||||||
|
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
|
||||||
|
void MinFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; }
|
||||||
|
void MinFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; }
|
||||||
|
optional<float> MinFilter::new_value(float value) {
|
||||||
|
if (!isnan(value)) {
|
||||||
|
while (this->queue_.size() >= this->window_size_) {
|
||||||
|
this->queue_.pop_front();
|
||||||
|
}
|
||||||
|
this->queue_.push_back(value);
|
||||||
|
ESP_LOGVV(TAG, "MinFilter(%p)::new_value(%f)", this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++this->send_at_ >= this->send_every_) {
|
||||||
|
this->send_at_ = 0;
|
||||||
|
|
||||||
|
float min = 0.0f;
|
||||||
|
if (!this->queue_.empty()) {
|
||||||
|
std::deque<float>::iterator it = std::min_element(queue_.begin(), queue_.end());
|
||||||
|
min = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGVV(TAG, "MinFilter(%p)::new_value(%f) SENDING", this, min);
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MinFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
||||||
|
|
||||||
|
// MaxFilter
|
||||||
|
MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
||||||
|
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
|
||||||
|
void MaxFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; }
|
||||||
|
void MaxFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; }
|
||||||
|
optional<float> MaxFilter::new_value(float value) {
|
||||||
|
if (!isnan(value)) {
|
||||||
|
while (this->queue_.size() >= this->window_size_) {
|
||||||
|
this->queue_.pop_front();
|
||||||
|
}
|
||||||
|
this->queue_.push_back(value);
|
||||||
|
ESP_LOGVV(TAG, "MaxFilter(%p)::new_value(%f)", this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++this->send_at_ >= this->send_every_) {
|
||||||
|
this->send_at_ = 0;
|
||||||
|
|
||||||
|
float max = 0.0f;
|
||||||
|
if (!this->queue_.empty()) {
|
||||||
|
std::deque<float>::iterator it = std::max_element(queue_.begin(), queue_.end());
|
||||||
|
max = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGVV(TAG, "MaxFilter(%p)::new_value(%f) SENDING", this, max);
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MaxFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
||||||
|
|
||||||
// SlidingWindowMovingAverageFilter
|
// SlidingWindowMovingAverageFilter
|
||||||
SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every,
|
SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every,
|
||||||
size_t send_first_at)
|
size_t send_first_at)
|
||||||
|
|
|
@ -76,6 +76,66 @@ class MedianFilter : public Filter {
|
||||||
size_t window_size_;
|
size_t window_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Simple min filter.
|
||||||
|
*
|
||||||
|
* Takes the min of the last <send_every> values and pushes it out every <send_every>.
|
||||||
|
*/
|
||||||
|
class MinFilter : public Filter {
|
||||||
|
public:
|
||||||
|
/** Construct a MinFilter.
|
||||||
|
*
|
||||||
|
* @param window_size The number of values that the min should be returned from.
|
||||||
|
* @param send_every After how many sensor values should a new one be pushed out.
|
||||||
|
* @param send_first_at After how many values to forward the very first value. Defaults to the first value
|
||||||
|
* on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to
|
||||||
|
* send_every.
|
||||||
|
*/
|
||||||
|
explicit MinFilter(size_t window_size, size_t send_every, size_t send_first_at);
|
||||||
|
|
||||||
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
|
void set_send_every(size_t send_every);
|
||||||
|
void set_window_size(size_t window_size);
|
||||||
|
|
||||||
|
uint32_t expected_interval(uint32_t input) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::deque<float> queue_;
|
||||||
|
size_t send_every_;
|
||||||
|
size_t send_at_;
|
||||||
|
size_t window_size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Simple max filter.
|
||||||
|
*
|
||||||
|
* Takes the max of the last <send_every> values and pushes it out every <send_every>.
|
||||||
|
*/
|
||||||
|
class MaxFilter : public Filter {
|
||||||
|
public:
|
||||||
|
/** Construct a MaxFilter.
|
||||||
|
*
|
||||||
|
* @param window_size The number of values that the max should be returned from.
|
||||||
|
* @param send_every After how many sensor values should a new one be pushed out.
|
||||||
|
* @param send_first_at After how many values to forward the very first value. Defaults to the first value
|
||||||
|
* on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to
|
||||||
|
* send_every.
|
||||||
|
*/
|
||||||
|
explicit MaxFilter(size_t window_size, size_t send_every, size_t send_first_at);
|
||||||
|
|
||||||
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
|
void set_send_every(size_t send_every);
|
||||||
|
void set_window_size(size_t window_size);
|
||||||
|
|
||||||
|
uint32_t expected_interval(uint32_t input) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::deque<float> queue_;
|
||||||
|
size_t send_every_;
|
||||||
|
size_t send_at_;
|
||||||
|
size_t window_size_;
|
||||||
|
};
|
||||||
|
|
||||||
/** Simple sliding window moving average filter.
|
/** Simple sliding window moving average filter.
|
||||||
*
|
*
|
||||||
* Essentially just takes takes the average of the last window_size values and pushes them out
|
* Essentially just takes takes the average of the last window_size values and pushes them out
|
||||||
|
|
|
@ -262,6 +262,14 @@ sensor:
|
||||||
window_size: 5
|
window_size: 5
|
||||||
send_every: 5
|
send_every: 5
|
||||||
send_first_at: 3
|
send_first_at: 3
|
||||||
|
- min:
|
||||||
|
window_size: 5
|
||||||
|
send_every: 5
|
||||||
|
send_first_at: 3
|
||||||
|
- max:
|
||||||
|
window_size: 5
|
||||||
|
send_every: 5
|
||||||
|
send_first_at: 3
|
||||||
- sliding_window_moving_average:
|
- sliding_window_moving_average:
|
||||||
window_size: 15
|
window_size: 15
|
||||||
send_every: 15
|
send_every: 15
|
||||||
|
|
Loading…
Reference in a new issue