[binary_sensor] Use std::array in AutorepeatFilter

This commit is contained in:
J. Nick Koston
2026-03-27 13:33:46 -10:00
parent a075f63b59
commit 83eb723c62
3 changed files with 37 additions and 25 deletions
+2 -1
View File
@@ -266,6 +266,7 @@ async def delayed_off_filter_to_code(config, filter_id):
): cv.positive_time_period_milliseconds,
}
),
cv.Length(max=254),
),
)
async def autorepeat_filter_to_code(config, filter_id):
@@ -294,7 +295,7 @@ async def autorepeat_filter_to_code(config, filter_id):
),
)
]
var = cg.new_Pvariable(filter_id, timings)
var = cg.new_Pvariable(filter_id, cg.TemplateArguments(len(timings)), timings)
await cg.register_component(var, {})
return var
+8 -19
View File
@@ -76,14 +76,11 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
optional<bool> InvertFilter::new_value(bool value) { return !value; }
AutorepeatFilter::AutorepeatFilter(std::initializer_list<AutorepeatFilterTiming> timings) : timings_(timings) {}
optional<bool> AutorepeatFilter::new_value(bool value) {
// AutorepeatFilterBase
optional<bool> AutorepeatFilterBase::new_value(bool value) {
if (value) {
// Ignore if already running
if (this->active_timing_ != 0)
return {};
this->next_timing_();
return true;
} else {
@@ -94,34 +91,26 @@ optional<bool> AutorepeatFilter::new_value(bool value) {
}
}
void AutorepeatFilter::next_timing_() {
// Entering this method
// 1st time: starts waiting the first delay
// 2nd time: starts waiting the second delay and starts toggling with the first time_off / _on
// last time: no delay to start but have to bump the index to reflect the last
if (this->active_timing_ < this->timings_.size()) {
void AutorepeatFilterBase::next_timing_() {
if (this->active_timing_ < this->timings_count_) {
this->set_timeout(AUTOREPEAT_TIMING_ID, this->timings_[this->active_timing_].delay,
[this]() { this->next_timing_(); });
}
if (this->active_timing_ <= this->timings_.size()) {
if (this->active_timing_ <= this->timings_count_) {
this->active_timing_++;
}
if (this->active_timing_ == 2)
this->next_value_(false);
// Leaving this method: if the toggling is started, it has to use [active_timing_ - 2] for the intervals
}
void AutorepeatFilter::next_value_(bool val) {
void AutorepeatFilterBase::next_value_(bool val) {
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
this->output(val); // This is at least the second one so not initial
this->output(val);
this->set_timeout(AUTOREPEAT_ON_OFF_ID, val ? timing.time_on : timing.time_off,
[this, val]() { this->next_value_(!val); });
}
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
float AutorepeatFilterBase::get_setup_priority() const { return setup_priority::HARDWARE; }
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
+27 -5
View File
@@ -3,6 +3,8 @@
#include "esphome/core/defines.h"
#ifdef USE_BINARY_SENSOR_FILTER
#include <array>
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
@@ -86,22 +88,42 @@ struct AutorepeatFilterTiming {
uint32_t time_on;
};
class AutorepeatFilter : public Filter, public Component {
/// Non-template base for AutorepeatFilter — all methods in filter.cpp.
/// Lambdas capture this base pointer, so set_timeout/cancel_timeout are instantiated once.
class AutorepeatFilterBase : public Filter, public Component {
public:
explicit AutorepeatFilter(std::initializer_list<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
protected:
AutorepeatFilterBase() = default;
void next_timing_();
void next_value_(bool val);
FixedVector<AutorepeatFilterTiming> timings_;
const AutorepeatFilterTiming *timings_{nullptr};
uint8_t timings_count_{0};
uint8_t active_timing_{0};
};
/// Template wrapper that provides inline std::array storage for timings.
/// N is set by code generation to match the exact number of timings configured in YAML.
template<size_t N> class AutorepeatFilter : public AutorepeatFilterBase {
public:
explicit AutorepeatFilter(std::initializer_list<AutorepeatFilterTiming> timings) {
size_t i = 0;
for (const auto &t : timings) {
if (i >= N)
break;
this->timings_storage_[i++] = t;
}
this->timings_ = this->timings_storage_.data();
this->timings_count_ = N;
}
protected:
std::array<AutorepeatFilterTiming, N> timings_storage_{};
};
class LambdaFilter : public Filter {
public:
explicit LambdaFilter(std::function<optional<bool>(bool)> f);