mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add min/max filters (#1569)
This commit is contained in:
		| @@ -72,6 +72,8 @@ SensorPublishAction = sensor_ns.class_('SensorPublishAction', automation.Action) | ||||
| # Filters | ||||
| Filter = sensor_ns.class_('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) | ||||
| ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter) | ||||
| LambdaFilter = sensor_ns.class_('LambdaFilter', Filter) | ||||
| @@ -165,6 +167,32 @@ def median_filter_to_code(config, filter_id): | ||||
|                            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({ | ||||
|     cv.Optional(CONF_WINDOW_SIZE, 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_; } | ||||
|  | ||||
| // 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(size_t window_size, size_t send_every, | ||||
|                                                                    size_t send_first_at) | ||||
|   | ||||
| @@ -76,6 +76,66 @@ class MedianFilter : public Filter { | ||||
|   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. | ||||
|  * | ||||
|  * Essentially just takes takes the average of the last window_size values and pushes them out | ||||
|   | ||||
		Reference in New Issue
	
	Block a user