From 48658d5a552a3615eece481f01ad52ffb670ff8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Sun, 19 Mar 2023 20:08:51 +0100 Subject: [PATCH] Add a simple 'skip_initial' filter (#4582) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a simple 'skip' filter This filter simply skips the first `send_first_at` values, then passes everything as-is. This is quite useful when you know the first few sensor readings should be ignored. Example YAML: ```yaml sensor: - platform: sgp30 id: mysensor_sgp30 eco2: id: mysensor_sgp30_co2 name: "eCO₂" accuracy_decimals: 0 filters: - skip: send_first_at: 41 ``` * Rename the filter to `skip_initial` and simplify the schema New usage: ```yaml filters: - skip_initial: 41 ``` * Apply clang-format --- esphome/components/sensor/__init__.py | 6 ++++++ esphome/components/sensor/filter.cpp | 13 +++++++++++++ esphome/components/sensor/filter.h | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 821e0afac0..b63e157efb 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -194,6 +194,7 @@ SensorPublishAction = sensor_ns.class_("SensorPublishAction", automation.Action) Filter = sensor_ns.class_("Filter") QuantileFilter = sensor_ns.class_("QuantileFilter", Filter) MedianFilter = sensor_ns.class_("MedianFilter", Filter) +SkipInitialFilter = sensor_ns.class_("SkipInitialFilter", Filter) MinFilter = sensor_ns.class_("MinFilter", Filter) MaxFilter = sensor_ns.class_("MaxFilter", Filter) SlidingWindowMovingAverageFilter = sensor_ns.class_( @@ -365,6 +366,11 @@ MIN_SCHEMA = cv.All( ) +@FILTER_REGISTRY.register("skip_initial", SkipInitialFilter, cv.positive_not_null_int) +async def skip_initial_filter_to_code(config, filter_id): + return cg.new_Pvariable(filter_id, config) + + @FILTER_REGISTRY.register("min", MinFilter, MIN_SCHEMA) async def min_filter_to_code(config, filter_id): return cg.new_Pvariable( diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index bf65ac590f..472649ebdc 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -74,6 +74,19 @@ optional MedianFilter::new_value(float value) { return {}; } +// SkipInitialFilter +SkipInitialFilter::SkipInitialFilter(size_t num_to_ignore) : num_to_ignore_(num_to_ignore) {} +optional SkipInitialFilter::new_value(float value) { + if (num_to_ignore_ > 0) { + num_to_ignore_--; + ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SKIPPING, %u left", this, value, num_to_ignore_); + return {}; + } + + ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SENDING", this, value); + return value; +} + // QuantileFilter QuantileFilter::QuantileFilter(size_t window_size, size_t send_every, size_t send_first_at, float quantile) : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size), quantile_(quantile) {} diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index b560545b76..05934a26e8 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -102,6 +102,24 @@ class MedianFilter : public Filter { size_t window_size_; }; +/** Simple skip filter. + * + * Skips the first N values, then passes everything else. + */ +class SkipInitialFilter : public Filter { + public: + /** Construct a SkipInitialFilter. + * + * @param num_to_ignore How many values to ignore before the filter becomes a no-op. + */ + explicit SkipInitialFilter(size_t num_to_ignore); + + optional new_value(float value) override; + + protected: + size_t num_to_ignore_; +}; + /** Simple min filter. * * Takes the min of the last values and pushes it out every .