mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	binary_sensor filters templatable delays (#5029)
This commit is contained in:
		| @@ -95,6 +95,14 @@ DEVICE_CLASSES = [ | |||||||
|  |  | ||||||
| IS_PLATFORM_COMPONENT = True | IS_PLATFORM_COMPONENT = True | ||||||
|  |  | ||||||
|  | CONF_TIME_OFF = "time_off" | ||||||
|  | CONF_TIME_ON = "time_on" | ||||||
|  |  | ||||||
|  | DEFAULT_DELAY = "1s" | ||||||
|  | DEFAULT_TIME_OFF = "100ms" | ||||||
|  | DEFAULT_TIME_ON = "900ms" | ||||||
|  |  | ||||||
|  |  | ||||||
| binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor") | binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor") | ||||||
| BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase) | BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase) | ||||||
| BinarySensorInitiallyOff = binary_sensor_ns.class_( | BinarySensorInitiallyOff = binary_sensor_ns.class_( | ||||||
| @@ -138,47 +146,75 @@ FILTER_REGISTRY = Registry() | |||||||
| validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) | validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) | ||||||
|  |  | ||||||
|  |  | ||||||
| @FILTER_REGISTRY.register("invert", InvertFilter, {}) | def register_filter(name, filter_type, schema): | ||||||
|  |     return FILTER_REGISTRY.register(name, filter_type, schema) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @register_filter("invert", InvertFilter, {}) | ||||||
| async def invert_filter_to_code(config, filter_id): | async def invert_filter_to_code(config, filter_id): | ||||||
|     return cg.new_Pvariable(filter_id) |     return cg.new_Pvariable(filter_id) | ||||||
|  |  | ||||||
|  |  | ||||||
| @FILTER_REGISTRY.register( | @register_filter( | ||||||
|     "delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds |     "delayed_on_off", | ||||||
|  |     DelayedOnOffFilter, | ||||||
|  |     cv.Any( | ||||||
|  |         cv.templatable(cv.positive_time_period_milliseconds), | ||||||
|  |         cv.Schema( | ||||||
|  |             { | ||||||
|  |                 cv.Required(CONF_TIME_ON): cv.templatable( | ||||||
|  |                     cv.positive_time_period_milliseconds | ||||||
|  |                 ), | ||||||
|  |                 cv.Required(CONF_TIME_OFF): cv.templatable( | ||||||
|  |                     cv.positive_time_period_milliseconds | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         ), | ||||||
|  |         msg="'delayed_on_off' filter requires either a delay time to be used for both " | ||||||
|  |         "turn-on and turn-off delays, or two parameters 'time_on' and 'time_off' if " | ||||||
|  |         "different delay times are required.", | ||||||
|  |     ), | ||||||
| ) | ) | ||||||
| async def delayed_on_off_filter_to_code(config, filter_id): | async def delayed_on_off_filter_to_code(config, filter_id): | ||||||
|     var = cg.new_Pvariable(filter_id, config) |     var = cg.new_Pvariable(filter_id) | ||||||
|     await cg.register_component(var, {}) |     await cg.register_component(var, {}) | ||||||
|  |     if isinstance(config, dict): | ||||||
|  |         template_ = await cg.templatable(config[CONF_TIME_ON], [], cg.uint32) | ||||||
|  |         cg.add(var.set_on_delay(template_)) | ||||||
|  |         template_ = await cg.templatable(config[CONF_TIME_OFF], [], cg.uint32) | ||||||
|  |         cg.add(var.set_off_delay(template_)) | ||||||
|  |     else: | ||||||
|  |         template_ = await cg.templatable(config, [], cg.uint32) | ||||||
|  |         cg.add(var.set_on_delay(template_)) | ||||||
|  |         cg.add(var.set_off_delay(template_)) | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| @FILTER_REGISTRY.register( | @register_filter( | ||||||
|     "delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds |     "delayed_on", DelayedOnFilter, cv.templatable(cv.positive_time_period_milliseconds) | ||||||
| ) | ) | ||||||
| async def delayed_on_filter_to_code(config, filter_id): | async def delayed_on_filter_to_code(config, filter_id): | ||||||
|     var = cg.new_Pvariable(filter_id, config) |     var = cg.new_Pvariable(filter_id) | ||||||
|     await cg.register_component(var, {}) |     await cg.register_component(var, {}) | ||||||
|  |     template_ = await cg.templatable(config, [], cg.uint32) | ||||||
|  |     cg.add(var.set_delay(template_)) | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| @FILTER_REGISTRY.register( | @register_filter( | ||||||
|     "delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds |     "delayed_off", | ||||||
|  |     DelayedOffFilter, | ||||||
|  |     cv.templatable(cv.positive_time_period_milliseconds), | ||||||
| ) | ) | ||||||
| async def delayed_off_filter_to_code(config, filter_id): | async def delayed_off_filter_to_code(config, filter_id): | ||||||
|     var = cg.new_Pvariable(filter_id, config) |     var = cg.new_Pvariable(filter_id) | ||||||
|     await cg.register_component(var, {}) |     await cg.register_component(var, {}) | ||||||
|  |     template_ = await cg.templatable(config, [], cg.uint32) | ||||||
|  |     cg.add(var.set_delay(template_)) | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| CONF_TIME_OFF = "time_off" | @register_filter( | ||||||
| CONF_TIME_ON = "time_on" |  | ||||||
|  |  | ||||||
| DEFAULT_DELAY = "1s" |  | ||||||
| DEFAULT_TIME_OFF = "100ms" |  | ||||||
| DEFAULT_TIME_ON = "900ms" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @FILTER_REGISTRY.register( |  | ||||||
|     "autorepeat", |     "autorepeat", | ||||||
|     AutorepeatFilter, |     AutorepeatFilter, | ||||||
|     cv.All( |     cv.All( | ||||||
| @@ -215,7 +251,7 @@ async def autorepeat_filter_to_code(config, filter_id): | |||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| @FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda) | @register_filter("lambda", LambdaFilter, cv.returning_lambda) | ||||||
| async def lambda_filter_to_code(config, filter_id): | async def lambda_filter_to_code(config, filter_id): | ||||||
|     lambda_ = await cg.process_lambda( |     lambda_ = await cg.process_lambda( | ||||||
|         config, [(bool, "x")], return_type=cg.optional.template(bool) |         config, [(bool, "x")], return_type=cg.optional.template(bool) | ||||||
|   | |||||||
| @@ -26,22 +26,20 @@ void Filter::input(bool value, bool is_initial) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| DelayedOnOffFilter::DelayedOnOffFilter(uint32_t delay) : delay_(delay) {} |  | ||||||
| optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) { | optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) { | ||||||
|   if (value) { |   if (value) { | ||||||
|     this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(true, is_initial); }); |     this->set_timeout("ON_OFF", this->on_delay_.value(), [this, is_initial]() { this->output(true, is_initial); }); | ||||||
|   } else { |   } else { | ||||||
|     this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); }); |     this->set_timeout("ON_OFF", this->off_delay_.value(), [this, is_initial]() { this->output(false, is_initial); }); | ||||||
|   } |   } | ||||||
|   return {}; |   return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||||
|  |  | ||||||
| DelayedOnFilter::DelayedOnFilter(uint32_t delay) : delay_(delay) {} |  | ||||||
| optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) { | optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) { | ||||||
|   if (value) { |   if (value) { | ||||||
|     this->set_timeout("ON", this->delay_, [this, is_initial]() { this->output(true, is_initial); }); |     this->set_timeout("ON", this->delay_.value(), [this, is_initial]() { this->output(true, is_initial); }); | ||||||
|     return {}; |     return {}; | ||||||
|   } else { |   } else { | ||||||
|     this->cancel_timeout("ON"); |     this->cancel_timeout("ON"); | ||||||
| @@ -51,10 +49,9 @@ optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) { | |||||||
|  |  | ||||||
| float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||||
|  |  | ||||||
| DelayedOffFilter::DelayedOffFilter(uint32_t delay) : delay_(delay) {} |  | ||||||
| optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) { | optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) { | ||||||
|   if (!value) { |   if (!value) { | ||||||
|     this->set_timeout("OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); }); |     this->set_timeout("OFF", this->delay_.value(), [this, is_initial]() { this->output(false, is_initial); }); | ||||||
|     return {}; |     return {}; | ||||||
|   } else { |   } else { | ||||||
|     this->cancel_timeout("OFF"); |     this->cancel_timeout("OFF"); | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| @@ -29,38 +30,40 @@ class Filter { | |||||||
|  |  | ||||||
| class DelayedOnOffFilter : public Filter, public Component { | class DelayedOnOffFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   explicit DelayedOnOffFilter(uint32_t delay); |  | ||||||
|  |  | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value, bool is_initial) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   template<typename T> void set_on_delay(T delay) { this->on_delay_ = delay; } | ||||||
|  |   template<typename T> void set_off_delay(T delay) { this->off_delay_ = delay; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   uint32_t delay_; |   TemplatableValue<uint32_t> on_delay_{}; | ||||||
|  |   TemplatableValue<uint32_t> off_delay_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class DelayedOnFilter : public Filter, public Component { | class DelayedOnFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   explicit DelayedOnFilter(uint32_t delay); |  | ||||||
|  |  | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value, bool is_initial) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   template<typename T> void set_delay(T delay) { this->delay_ = delay; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   uint32_t delay_; |   TemplatableValue<uint32_t> delay_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class DelayedOffFilter : public Filter, public Component { | class DelayedOffFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   explicit DelayedOffFilter(uint32_t delay); |  | ||||||
|  |  | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value, bool is_initial) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   template<typename T> void set_delay(T delay) { this->delay_ = delay; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   uint32_t delay_; |   TemplatableValue<uint32_t> delay_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class InvertFilter : public Filter { | class InvertFilter : public Filter { | ||||||
|   | |||||||
| @@ -1355,8 +1355,15 @@ binary_sensor: | |||||||
|     device_class: window |     device_class: window | ||||||
|     filters: |     filters: | ||||||
|       - invert: |       - invert: | ||||||
|  |       - delayed_on_off: 40ms | ||||||
|  |       - delayed_on_off: | ||||||
|  |           time_on: 10s | ||||||
|  |           time_off: !lambda "return 1000;" | ||||||
|       - delayed_on: 40ms |       - delayed_on: 40ms | ||||||
|       - delayed_off: 40ms |       - delayed_off: 40ms | ||||||
|  |       - delayed_on_off: !lambda "return 10;" | ||||||
|  |       - delayed_on: !lambda "return 1000;" | ||||||
|  |       - delayed_off: !lambda "return 0;" | ||||||
|     on_press: |     on_press: | ||||||
|       then: |       then: | ||||||
|         - lambda: >- |         - lambda: >- | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user