From 8849443bf6e9f56e5c0f682a25a78a6782007a1e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:08:11 +1200 Subject: [PATCH] [update] Implement ``update.perform`` action and ``update.is_available`` condition (#7165) * [update] Fix unimplemented yaml action/condition * Add/update tests --------- Co-authored-by: Keith Burzinski --- .../update/http_request_update.cpp | 4 +- .../http_request/update/http_request_update.h | 2 +- esphome/components/update/__init__.py | 48 +++++++++++++------ esphome/components/update/automation.h | 23 +++++++++ esphome/components/update/update_entity.h | 4 +- tests/components/update/common.yaml | 27 +++++++++++ tests/components/update/test.esp32-ard.yaml | 3 ++ tests/components/update/test.esp8266-ard.yaml | 3 ++ tests/components/update/test.rp2040-ard.yaml | 3 ++ 9 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 esphome/components/update/automation.h diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 0a14dfd933..059148e7e5 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -138,8 +138,8 @@ void HttpRequestUpdate::update() { this->publish_state(); } -void HttpRequestUpdate::perform() { - if (this->state_ != update::UPDATE_STATE_AVAILABLE) { +void HttpRequestUpdate::perform(bool force) { + if (this->state_ != update::UPDATE_STATE_AVAILABLE && !force) { return; } diff --git a/esphome/components/http_request/update/http_request_update.h b/esphome/components/http_request/update/http_request_update.h index a6bc97392b..943231a906 100644 --- a/esphome/components/http_request/update/http_request_update.h +++ b/esphome/components/http_request/update/http_request_update.h @@ -15,7 +15,7 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent { void setup() override; void update() override; - void perform() override; + void perform(bool force) override; void set_source_url(const std::string &source_url) { this->source_url_ = source_url; } diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index 45bf082fa4..ba3b2f20df 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -1,10 +1,11 @@ from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server import esphome.config_validation as cv -import esphome.codegen as cg from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, + CONF_FORCE_UPDATE, CONF_ID, CONF_MQTT_ID, CONF_WEB_SERVER_ID, @@ -23,8 +24,12 @@ UpdateEntity = update_ns.class_("UpdateEntity", cg.EntityBase) UpdateInfo = update_ns.struct("UpdateInfo") -PerformAction = update_ns.class_("PerformAction", automation.Action) -IsAvailableCondition = update_ns.class_("IsAvailableCondition", automation.Condition) +PerformAction = update_ns.class_( + "PerformAction", automation.Action, cg.Parented.template(UpdateEntity) +) +IsAvailableCondition = update_ns.class_( + "IsAvailableCondition", automation.Condition, cg.Parented.template(UpdateEntity) +) DEVICE_CLASSES = [ DEVICE_CLASS_EMPTY, @@ -92,24 +97,37 @@ async def to_code(config): cg.add_global(update_ns.using) -UPDATE_AUTOMATION_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.use_id(UpdateEntity), - } +@automation.register_action( + "update.perform", + PerformAction, + automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(UpdateEntity), + cv.Optional(CONF_FORCE_UPDATE, default=False): cv.templatable(cv.boolean), + } + ), ) - - -@automation.register_action("update.perform", PerformAction, UPDATE_AUTOMATION_SCHEMA) async def update_perform_action_to_code(config, action_id, template_arg, args): - paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(action_id, paren, paren) + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + + force = await cg.templatable(config[CONF_FORCE_UPDATE], args, cg.bool_) + cg.add(var.set_force(force)) + return var @automation.register_condition( - "update.is_available", IsAvailableCondition, UPDATE_AUTOMATION_SCHEMA + "update.is_available", + IsAvailableCondition, + automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(UpdateEntity), + } + ), ) async def update_is_available_condition_to_code( config, condition_id, template_arg, args ): - paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(condition_id, paren, paren) + var = cg.new_Pvariable(condition_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/update/automation.h b/esphome/components/update/automation.h new file mode 100644 index 0000000000..df50f86a0c --- /dev/null +++ b/esphome/components/update/automation.h @@ -0,0 +1,23 @@ +#pragma once + +#include "update_entity.h" + +#include "esphome/core/automation.h" + +namespace esphome { +namespace update { + +template class PerformAction : public Action, public Parented { + TEMPLATABLE_VALUE(bool, force) + + public: + void play(Ts... x) override { this->parent_->perform(this->force_.value(x...)); } +}; + +template class IsAvailableCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->state == UPDATE_STATE_AVAILABLE; } +}; + +} // namespace update +} // namespace esphome diff --git a/esphome/components/update/update_entity.h b/esphome/components/update/update_entity.h index 5984c8e35b..568fbe3bb0 100644 --- a/esphome/components/update/update_entity.h +++ b/esphome/components/update/update_entity.h @@ -32,7 +32,9 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { void publish_state(); - virtual void perform() = 0; + void perform() { this->perform(false); } + + virtual void perform(bool force) = 0; const UpdateInfo &update_info = update_info_; const UpdateState &state = state_; diff --git a/tests/components/update/common.yaml b/tests/components/update/common.yaml index 91b8669505..dcb4f42527 100644 --- a/tests/components/update/common.yaml +++ b/tests/components/update/common.yaml @@ -1 +1,28 @@ +substitutions: + verify_ssl: "true" + +esphome: + on_boot: + then: + - if: + condition: + update.is_available: + then: + - logger.log: "Update available" + - update.perform: + force_update: true + +wifi: + ssid: MySSID + password: password1 + +http_request: + verify_ssl: ${verify_ssl} + +ota: + - platform: http_request + update: + - platform: http_request + name: Firmware Update + source: http://example.com/manifest.json diff --git a/tests/components/update/test.esp32-ard.yaml b/tests/components/update/test.esp32-ard.yaml index dade44d145..c1937b5a10 100644 --- a/tests/components/update/test.esp32-ard.yaml +++ b/tests/components/update/test.esp32-ard.yaml @@ -1 +1,4 @@ +substitutions: + verify_ssl: "false" + <<: !include common.yaml diff --git a/tests/components/update/test.esp8266-ard.yaml b/tests/components/update/test.esp8266-ard.yaml index dade44d145..c1937b5a10 100644 --- a/tests/components/update/test.esp8266-ard.yaml +++ b/tests/components/update/test.esp8266-ard.yaml @@ -1 +1,4 @@ +substitutions: + verify_ssl: "false" + <<: !include common.yaml diff --git a/tests/components/update/test.rp2040-ard.yaml b/tests/components/update/test.rp2040-ard.yaml index dade44d145..c1937b5a10 100644 --- a/tests/components/update/test.rp2040-ard.yaml +++ b/tests/components/update/test.rp2040-ard.yaml @@ -1 +1,4 @@ +substitutions: + verify_ssl: "false" + <<: !include common.yaml