diff --git a/esphome/automation.py b/esphome/automation.py index 0c4bda09d1..d90a9cb99a 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME, + CONF_UPDATE_INTERVAL, ) from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.util import Registry @@ -69,6 +70,8 @@ WhileAction = cg.esphome_ns.class_("WhileAction", Action) RepeatAction = cg.esphome_ns.class_("RepeatAction", Action) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action) +SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action) +ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action) Automation = cg.esphome_ns.class_("Automation") LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition) @@ -303,6 +306,41 @@ async def component_update_action_to_code(config, action_id, template_arg, args) return cg.new_Pvariable(action_id, template_arg, comp) +@register_action( + "component.suspend", + SuspendComponentAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), + } + ), +) +async def component_suspend_action_to_code(config, action_id, template_arg, args): + comp = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, comp) + + +@register_action( + "component.resume", + ResumeComponentAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), + cv.Optional(CONF_UPDATE_INTERVAL): cv.templatable( + cv.positive_time_period_milliseconds + ), + } + ), +) +async def component_resume_action_to_code(config, action_id, template_arg, args): + comp = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, comp) + if CONF_UPDATE_INTERVAL in config: + template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int) + cg.add(var.set_update_interval(template_)) + return var + + async def build_action(full_config, template_arg, args): registry_entry, config = cg.extract_registry_entry_config( ACTION_REGISTRY, full_config diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index a17b6a6f85..af618af99a 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -330,4 +330,38 @@ template class UpdateComponentAction : public Action { PollingComponent *component_; }; +template class SuspendComponentAction : public Action { + public: + SuspendComponentAction(PollingComponent *component) : component_(component) {} + + void play(Ts... x) override { + if (!this->component_->is_ready()) + return; + this->component_->stop_poller(); + } + + protected: + PollingComponent *component_; +}; + +template class ResumeComponentAction : public Action { + public: + ResumeComponentAction(PollingComponent *component) : component_(component) {} + TEMPLATABLE_VALUE(uint32_t, update_interval) + + void play(Ts... x) override { + if (!this->component_->is_ready()) { + return; + } + optional update_interval = this->update_interval_.optional_value(x...); + if (update_interval.has_value()) { + this->component_->set_update_interval(update_interval.value()); + } + this->component_->start_poller(); + } + + protected: + PollingComponent *component_; +}; + } // namespace esphome diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index ae85d55498..e2f27f9828 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -188,10 +188,20 @@ void PollingComponent::call_setup() { // Let the polling component subclass setup their HW. this->setup(); + // init the poller + this->start_poller(); +} + +void PollingComponent::start_poller() { // Register interval. this->set_interval("update", this->get_update_interval(), [this]() { this->update(); }); } +void PollingComponent::stop_poller() { + // Clear the interval to suspend component + this->cancel_interval("update"); +} + uint32_t PollingComponent::get_update_interval() const { return this->update_interval_; } void PollingComponent::set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } diff --git a/esphome/core/component.h b/esphome/core/component.h index 7382f1c617..51a6296811 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -308,6 +308,12 @@ class PollingComponent : public Component { /// Get the update interval in ms of this sensor virtual uint32_t get_update_interval() const; + // Start the poller, used for component.suspend + void start_poller(); + + // Stop the poller, used for component.suspend + void stop_poller(); + protected: uint32_t update_interval_; }; diff --git a/tests/test1.yaml b/tests/test1.yaml index 96dda707b6..b84aa21439 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -3566,6 +3566,22 @@ button: name: Midea Power Inverse on_press: midea_ac.power_toggle: + - platform: template + name: Poller component suspend test + on_press: + - component.suspend: myteleinfo + - delay: 20s + - component.update: myteleinfo + - delay: 20s + - component.resume: myteleinfo + - delay: 20s + - component.resume: + id: myteleinfo + update_interval: 2s + - delay: 20s + - component.resume: + id: myteleinfo + update_interval: !lambda return 2500; - platform: ld2410 factory_reset: name: "factory reset"