[sensor] Add new filter: `throttle_with_priority` (#9937)

This commit is contained in:
Keith Burzinski 2025-07-29 19:53:14 -05:00 committed by GitHub
parent 14dd48f9c3
commit 374858efeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 69 additions and 0 deletions

View File

@ -256,6 +256,7 @@ OffsetFilter = sensor_ns.class_("OffsetFilter", Filter)
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter)
ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter)
ThrottleWithPriorityFilter = sensor_ns.class_("ThrottleWithPriorityFilter", Filter)
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component)
HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
@ -595,6 +596,25 @@ async def throttle_filter_to_code(config, filter_id):
return cg.new_Pvariable(filter_id, config)
TIMEOUT_WITH_PRIORITY_SCHEMA = cv.maybe_simple_value(
{
cv.Required(CONF_TIMEOUT): cv.positive_time_period_milliseconds,
cv.Optional(CONF_VALUE, default="nan"): cv.ensure_list(cv.float_),
},
key=CONF_TIMEOUT,
)
@FILTER_REGISTRY.register(
"throttle_with_priority",
ThrottleWithPriorityFilter,
TIMEOUT_WITH_PRIORITY_SCHEMA,
)
async def throttle_with_priority_filter_to_code(config, filter_id):
template_ = [await cg.templatable(x, [], float) for x in config[CONF_VALUE]]
return cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_)
@FILTER_REGISTRY.register(
"heartbeat", HeartbeatFilter, cv.positive_time_period_milliseconds
)

View File

@ -1,5 +1,6 @@
#include "filter.h"
#include <cmath>
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "sensor.h"
@ -332,6 +333,40 @@ optional<float> ThrottleFilter::new_value(float value) {
return {};
}
// ThrottleWithPriorityFilter
ThrottleWithPriorityFilter::ThrottleWithPriorityFilter(uint32_t min_time_between_inputs,
std::vector<TemplatableValue<float>> prioritized_values)
: min_time_between_inputs_(min_time_between_inputs), prioritized_values_(std::move(prioritized_values)) {}
optional<float> ThrottleWithPriorityFilter::new_value(float value) {
bool is_prioritized_value = false;
int8_t accuracy = this->parent_->get_accuracy_decimals();
float accuracy_mult = powf(10.0f, accuracy);
const uint32_t now = App.get_loop_component_start_time();
// First, determine if the new value is one of the prioritized values
for (auto prioritized_value : this->prioritized_values_) {
if (std::isnan(prioritized_value.value())) {
if (std::isnan(value)) {
is_prioritized_value = true;
break;
}
continue;
}
float rounded_prioritized_value = roundf(accuracy_mult * prioritized_value.value());
float rounded_value = roundf(accuracy_mult * value);
if (rounded_prioritized_value == rounded_value) {
is_prioritized_value = true;
break;
}
}
// Finally, determine if the new value should be throttled and pass it through if not
if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ || is_prioritized_value) {
this->last_input_ = now;
return value;
}
return {};
}
// DeltaFilter
DeltaFilter::DeltaFilter(float delta, bool percentage_mode)
: delta_(delta), current_delta_(delta), percentage_mode_(percentage_mode), last_value_(NAN) {}

View File

@ -314,6 +314,20 @@ class ThrottleFilter : public Filter {
uint32_t min_time_between_inputs_;
};
/// Same as 'throttle' but will immediately publish values contained in `value_to_prioritize`.
class ThrottleWithPriorityFilter : public Filter {
public:
explicit ThrottleWithPriorityFilter(uint32_t min_time_between_inputs,
std::vector<TemplatableValue<float>> prioritized_values);
optional<float> new_value(float value) override;
protected:
uint32_t last_input_{0};
uint32_t min_time_between_inputs_;
std::vector<TemplatableValue<float>> prioritized_values_;
};
class TimeoutFilter : public Filter, public Component {
public:
explicit TimeoutFilter(uint32_t time_period, TemplatableValue<float> new_value);