mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 05:24:53 +01:00
Add state callback to ota component (#1816)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net> Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
parent
cdbc146e5d
commit
623570a117
5 changed files with 184 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
||||||
from esphome.cpp_generator import RawExpression
|
from esphome.cpp_generator import RawExpression
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_NUM_ATTEMPTS,
|
CONF_NUM_ATTEMPTS,
|
||||||
|
@ -8,14 +9,29 @@ from esphome.const import (
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_REBOOT_TIMEOUT,
|
CONF_REBOOT_TIMEOUT,
|
||||||
CONF_SAFE_MODE,
|
CONF_SAFE_MODE,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
DEPENDENCIES = ["network"]
|
DEPENDENCIES = ["network"]
|
||||||
|
|
||||||
|
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||||
|
CONF_ON_BEGIN = "on_begin"
|
||||||
|
CONF_ON_PROGRESS = "on_progress"
|
||||||
|
CONF_ON_END = "on_end"
|
||||||
|
CONF_ON_ERROR = "on_error"
|
||||||
|
|
||||||
ota_ns = cg.esphome_ns.namespace("ota")
|
ota_ns = cg.esphome_ns.namespace("ota")
|
||||||
|
OTAState = ota_ns.enum("OTAState")
|
||||||
OTAComponent = ota_ns.class_("OTAComponent", cg.Component)
|
OTAComponent = ota_ns.class_("OTAComponent", cg.Component)
|
||||||
|
OTAStateChangeTrigger = ota_ns.class_(
|
||||||
|
"OTAStateChangeTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
OTAStartTrigger = ota_ns.class_("OTAStartTrigger", automation.Trigger.template())
|
||||||
|
OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.template())
|
||||||
|
OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template())
|
||||||
|
OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template())
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
|
@ -27,6 +43,31 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
CONF_REBOOT_TIMEOUT, default="5min"
|
CONF_REBOOT_TIMEOUT, default="5min"
|
||||||
): cv.positive_time_period_milliseconds,
|
): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int,
|
cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int,
|
||||||
|
cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStateChangeTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_BEGIN): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStartTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_ERROR): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAErrorTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_PROGRESS): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAProgressTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_END): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAEndTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
@ -49,3 +90,27 @@ async def to_code(config):
|
||||||
cg.add_library("Update", None)
|
cg.add_library("Update", None)
|
||||||
elif CORE.is_esp32:
|
elif CORE.is_esp32:
|
||||||
cg.add_library("Hash", None)
|
cg.add_library("Hash", None)
|
||||||
|
|
||||||
|
use_state_callback = False
|
||||||
|
for conf in config.get(CONF_ON_STATE_CHANGE, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [(OTAState, "state")], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_BEGIN, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_PROGRESS, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_END, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_ERROR, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [(int, "x")], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
if use_state_callback:
|
||||||
|
cg.add_define("USE_OTA_STATE_CALLBACK")
|
||||||
|
|
71
esphome/components/ota/automation.h
Normal file
71
esphome/components/ota/automation.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/components/ota/ota_component.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ota {
|
||||||
|
|
||||||
|
class OTAStateChangeTrigger : public Trigger<OTAState> {
|
||||||
|
public:
|
||||||
|
explicit OTAStateChangeTrigger(OTAComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||||
|
if (!parent->is_failed()) {
|
||||||
|
return trigger(state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OTAStartTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit OTAStartTrigger(OTAComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||||
|
if (state == OTA_STARTED && !parent->is_failed()) {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OTAProgressTrigger : public Trigger<float> {
|
||||||
|
public:
|
||||||
|
explicit OTAProgressTrigger(OTAComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||||
|
if (state == OTA_IN_PROGRESS && !parent->is_failed()) {
|
||||||
|
trigger(progress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OTAEndTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit OTAEndTrigger(OTAComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||||
|
if (state == OTA_COMPLETED && !parent->is_failed()) {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OTAErrorTrigger : public Trigger<int> {
|
||||||
|
public:
|
||||||
|
explicit OTAErrorTrigger(OTAComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
|
||||||
|
if (state == OTA_ERROR && !parent->is_failed()) {
|
||||||
|
trigger(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ota
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_OTA_STATE_CALLBACK
|
|
@ -1,7 +1,6 @@
|
||||||
#include "ota_component.h"
|
#include "ota_component.h"
|
||||||
|
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/core/util.h"
|
||||||
|
|
||||||
|
@ -25,6 +24,7 @@ void OTAComponent::setup() {
|
||||||
|
|
||||||
this->dump_config();
|
this->dump_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OTAComponent::dump_config() {
|
void OTAComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Over-The-Air Updates:");
|
ESP_LOGCONFIG(TAG, "Over-The-Air Updates:");
|
||||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
|
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
|
||||||
|
@ -71,6 +71,9 @@ void OTAComponent::handle_() {
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_.remoteIP().toString().c_str());
|
ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_.remoteIP().toString().c_str());
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
this->state_callback_.call(OTA_STARTED, 0.0f, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!this->wait_receive_(buf, 5)) {
|
if (!this->wait_receive_(buf, 5)) {
|
||||||
ESP_LOGW(TAG, "Reading magic bytes failed!");
|
ESP_LOGW(TAG, "Reading magic bytes failed!");
|
||||||
|
@ -241,6 +244,9 @@ void OTAComponent::handle_() {
|
||||||
last_progress = now;
|
last_progress = now;
|
||||||
float percentage = (total * 100.0f) / ota_size;
|
float percentage = (total * 100.0f) / ota_size;
|
||||||
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
|
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
this->state_callback_.call(OTA_IN_PROGRESS, percentage, 0);
|
||||||
|
#endif
|
||||||
// slow down OTA update to avoid getting killed by task watchdog (task_wdt)
|
// slow down OTA update to avoid getting killed by task watchdog (task_wdt)
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
@ -268,6 +274,9 @@ void OTAComponent::handle_() {
|
||||||
delay(10);
|
delay(10);
|
||||||
ESP_LOGI(TAG, "OTA update finished!");
|
ESP_LOGI(TAG, "OTA update finished!");
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
this->state_callback_.call(OTA_COMPLETED, 100.0f, 0);
|
||||||
|
#endif
|
||||||
delay(100); // NOLINT
|
delay(100); // NOLINT
|
||||||
App.safe_reboot();
|
App.safe_reboot();
|
||||||
|
|
||||||
|
@ -296,6 +305,9 @@ error:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->status_momentary_error("onerror", 5000);
|
this->status_momentary_error("onerror", 5000);
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
this->state_callback_.call(OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
global_preferences.prevent_write(false);
|
global_preferences.prevent_write(false);
|
||||||
|
@ -400,5 +412,11 @@ void OTAComponent::on_safe_shutdown() {
|
||||||
this->clean_rtc();
|
this->clean_rtc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
void OTAComponent::add_on_state_callback(std::function<void(OTAState, float, uint8_t)> &&callback) {
|
||||||
|
this->state_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace ota
|
} // namespace ota
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/preferences.h"
|
#include "esphome/core/preferences.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
#include <WiFiServer.h>
|
#include <WiFiServer.h>
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
|
|
||||||
|
@ -32,6 +33,8 @@ enum OTAResponseTypes {
|
||||||
OTA_RESPONSE_ERROR_UNKNOWN = 255,
|
OTA_RESPONSE_ERROR_UNKNOWN = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR };
|
||||||
|
|
||||||
/// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA.
|
/// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA.
|
||||||
class OTAComponent : public Component {
|
class OTAComponent : public Component {
|
||||||
public:
|
public:
|
||||||
|
@ -49,6 +52,10 @@ class OTAComponent : public Component {
|
||||||
|
|
||||||
bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time);
|
bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time);
|
||||||
|
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
void add_on_state_callback(std::function<void(OTAState, float, uint8_t)> &&callback);
|
||||||
|
#endif
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
// ========== INTERNAL METHODS ==========
|
||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
@ -82,6 +89,10 @@ class OTAComponent : public Component {
|
||||||
uint32_t safe_mode_rtc_value_;
|
uint32_t safe_mode_rtc_value_;
|
||||||
uint8_t safe_mode_num_attempts_;
|
uint8_t safe_mode_num_attempts_;
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
|
|
||||||
|
#ifdef USE_OTA_STATE_CALLBACK
|
||||||
|
CallbackManager<void(OTAState, float, uint8_t)> state_callback_{};
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ota
|
} // namespace ota
|
||||||
|
|
|
@ -197,6 +197,24 @@ ota:
|
||||||
port: 3286
|
port: 3286
|
||||||
reboot_timeout: 2min
|
reboot_timeout: 2min
|
||||||
num_attempts: 5
|
num_attempts: 5
|
||||||
|
on_state_change:
|
||||||
|
then:
|
||||||
|
lambda: >-
|
||||||
|
ESP_LOGD("ota", "State %d", state);
|
||||||
|
on_begin:
|
||||||
|
then:
|
||||||
|
logger.log: "OTA begin"
|
||||||
|
on_progress:
|
||||||
|
then:
|
||||||
|
lambda: >-
|
||||||
|
ESP_LOGD("ota", "Got progress %f", x);
|
||||||
|
on_end:
|
||||||
|
then:
|
||||||
|
logger.log: "OTA end"
|
||||||
|
on_error:
|
||||||
|
then:
|
||||||
|
lambda: >-
|
||||||
|
ESP_LOGD("ota", "Got error code %d", x);
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
baud_rate: 0
|
baud_rate: 0
|
||||||
|
|
Loading…
Reference in a new issue