mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Autorepeat filter for the binary sensors (#1681)
* add the autorepeat filter * add a test for the autorepeat filter * make no timing equivalent to a single default one
This commit is contained in:
parent
54660300e9
commit
00c144daeb
4 changed files with 132 additions and 0 deletions
|
@ -4,6 +4,7 @@ from esphome import automation, core
|
||||||
from esphome.automation import Condition, maybe_simple_id
|
from esphome.automation import Condition, maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_DELAY,
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_FILTERS,
|
CONF_FILTERS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
@ -120,6 +121,7 @@ DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Co
|
||||||
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
|
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
|
||||||
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
|
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
|
||||||
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
|
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
|
||||||
|
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
|
||||||
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
|
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
|
||||||
|
|
||||||
FILTER_REGISTRY = Registry()
|
FILTER_REGISTRY = Registry()
|
||||||
|
@ -158,6 +160,51 @@ def delayed_off_filter_to_code(config, filter_id):
|
||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
|
CONF_TIME_OFF = "time_off"
|
||||||
|
CONF_TIME_ON = "time_on"
|
||||||
|
|
||||||
|
DEFAULT_DELAY = "1s"
|
||||||
|
DEFAULT_TIME_OFF = "100ms"
|
||||||
|
DEFAULT_TIME_ON = "900ms"
|
||||||
|
|
||||||
|
|
||||||
|
@FILTER_REGISTRY.register(
|
||||||
|
"autorepeat",
|
||||||
|
AutorepeatFilter,
|
||||||
|
cv.All(
|
||||||
|
cv.ensure_list(
|
||||||
|
{
|
||||||
|
cv.Optional(
|
||||||
|
CONF_DELAY, default=DEFAULT_DELAY
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_TIME_OFF, default=DEFAULT_TIME_OFF
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_TIME_ON, default=DEFAULT_TIME_ON
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def autorepeat_filter_to_code(config, filter_id):
|
||||||
|
timings = []
|
||||||
|
if len(config) > 0:
|
||||||
|
for conf in config:
|
||||||
|
timings.append((conf[CONF_DELAY], conf[CONF_TIME_OFF], conf[CONF_TIME_ON]))
|
||||||
|
else:
|
||||||
|
timings.append(
|
||||||
|
(
|
||||||
|
cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds,
|
||||||
|
cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds,
|
||||||
|
cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var = cg.new_Pvariable(filter_id, timings)
|
||||||
|
yield cg.register_component(var, {})
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
|
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
|
||||||
def lambda_filter_to_code(config, filter_id):
|
def lambda_filter_to_code(config, filter_id):
|
||||||
lambda_ = yield cg.process_lambda(
|
lambda_ = yield cg.process_lambda(
|
||||||
|
|
|
@ -64,6 +64,50 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
|
||||||
|
|
||||||
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
||||||
|
|
||||||
|
AutorepeatFilter::AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings) : timings_(timings) {}
|
||||||
|
|
||||||
|
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
|
||||||
|
if (value) {
|
||||||
|
// Ignore if already running
|
||||||
|
if (this->active_timing_ != 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
this->next_timing_();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this->cancel_timeout("TIMING");
|
||||||
|
this->cancel_timeout("ON_OFF");
|
||||||
|
this->active_timing_ = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
this->set_timeout("TIMING", this->timings_[this->active_timing_].delay, [this]() { this->next_timing_(); });
|
||||||
|
|
||||||
|
if (this->active_timing_ <= this->timings_.size()) {
|
||||||
|
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) {
|
||||||
|
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
|
||||||
|
this->output(val, false); // This is at least the second one so not initial
|
||||||
|
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
|
||||||
|
}
|
||||||
|
|
||||||
|
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
|
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
|
||||||
|
|
||||||
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
||||||
|
|
|
@ -66,6 +66,33 @@ class InvertFilter : public Filter {
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AutorepeatFilterTiming {
|
||||||
|
AutorepeatFilterTiming(uint32_t delay, uint32_t off, uint32_t on) {
|
||||||
|
this->delay = delay;
|
||||||
|
this->time_off = off;
|
||||||
|
this->time_on = on;
|
||||||
|
}
|
||||||
|
uint32_t delay;
|
||||||
|
uint32_t time_off;
|
||||||
|
uint32_t time_on;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutorepeatFilter : public Filter, public Component {
|
||||||
|
public:
|
||||||
|
explicit AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings);
|
||||||
|
|
||||||
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void next_timing_();
|
||||||
|
void next_value_(bool val);
|
||||||
|
|
||||||
|
std::vector<AutorepeatFilterTiming> timings_;
|
||||||
|
uint8_t active_timing_{0};
|
||||||
|
};
|
||||||
|
|
||||||
class LambdaFilter : public Filter {
|
class LambdaFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);
|
explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);
|
||||||
|
|
|
@ -86,6 +86,20 @@ binary_sensor:
|
||||||
- platform: tuya
|
- platform: tuya
|
||||||
id: tuya_binary_sensor
|
id: tuya_binary_sensor
|
||||||
sensor_datapoint: 1
|
sensor_datapoint: 1
|
||||||
|
- platform: template
|
||||||
|
id: ar1
|
||||||
|
lambda: 'return {};'
|
||||||
|
filters:
|
||||||
|
- autorepeat:
|
||||||
|
- delay: 2s
|
||||||
|
time_off: 100ms
|
||||||
|
time_on: 900ms
|
||||||
|
- delay: 4s
|
||||||
|
time_off: 100ms
|
||||||
|
time_on: 400ms
|
||||||
|
on_state:
|
||||||
|
then:
|
||||||
|
- lambda: 'ESP_LOGI("ar1:", "%d", x);'
|
||||||
|
|
||||||
climate:
|
climate:
|
||||||
- platform: tuya
|
- platform: tuya
|
||||||
|
|
Loading…
Reference in a new issue