revert ota

This commit is contained in:
J. Nick Koston 2025-05-13 01:05:37 -05:00
parent 51d1da8460
commit 8fba8c2800
No known key found for this signature in database
13 changed files with 259 additions and 128 deletions

View File

@ -2,70 +2,70 @@ from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_NUM_ATTEMPTS,
CONF_ESPHOME,
CONF_ON_ERROR,
CONF_OTA,
CONF_PASSWORD,
CONF_PORT,
CONF_REBOOT_TIMEOUT,
CONF_SAFE_MODE,
CONF_PLATFORM,
CONF_TRIGGER_ID,
CONF_VERSION,
KEY_PAST_SAFE_MODE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import RawExpression
CODEOWNERS = ["@esphome/core"]
DEPENDENCIES = ["network"]
AUTO_LOAD = ["socket", "md5"]
AUTO_LOAD = ["md5", "safe_mode"]
CONF_ON_STATE_CHANGE = "on_state_change"
IS_PLATFORM_COMPONENT = True
CONF_ON_ABORT = "on_abort"
CONF_ON_BEGIN = "on_begin"
CONF_ON_PROGRESS = "on_progress"
CONF_ON_END = "on_end"
CONF_ON_ERROR = "on_error"
CONF_ON_PROGRESS = "on_progress"
CONF_ON_STATE_CHANGE = "on_state_change"
ota_ns = cg.esphome_ns.namespace("ota")
OTAState = ota_ns.enum("OTAState")
OTAComponent = ota_ns.class_("OTAComponent", cg.Component)
OTAState = ota_ns.enum("OTAState")
OTAAbortTrigger = ota_ns.class_("OTAAbortTrigger", automation.Trigger.template())
OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template())
OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template())
OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.template())
OTAStartTrigger = ota_ns.class_("OTAStartTrigger", automation.Trigger.template())
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(
def _ota_final_validate(config):
if len(config) < 1:
raise cv.Invalid(
f"At least one platform must be specified for '{CONF_OTA}'; add '{CONF_PLATFORM}: {CONF_ESPHOME}' for original OTA functionality"
)
FINAL_VALIDATE_SCHEMA = _ota_final_validate
BASE_OTA_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(OTAComponent),
cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2, int=True),
cv.SplitDefault(
CONF_PORT,
esp8266=8266,
esp32=3232,
rp2040=2040,
bk72xx=8892,
rtl87xx=8892,
): cv.port,
cv.Optional(CONF_PASSWORD): cv.string,
cv.Optional(
CONF_REBOOT_TIMEOUT, default="5min"
): cv.positive_time_period_milliseconds,
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_ABORT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAAbortTrigger),
}
),
cv.Optional(CONF_ON_BEGIN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStartTrigger),
}
),
cv.Optional(CONF_ON_END): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAEndTrigger),
}
),
cv.Optional(CONF_ON_ERROR): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAErrorTrigger),
@ -76,35 +76,13 @@ CONFIG_SCHEMA = cv.Schema(
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)
)
@coroutine_with_priority(50.0)
@coroutine_with_priority(54.0)
async def to_code(config):
CORE.data[CONF_OTA] = {}
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_port(config[CONF_PORT]))
cg.add_define("USE_OTA")
if CONF_PASSWORD in config:
cg.add(var.set_auth_password(config[CONF_PASSWORD]))
cg.add_define("USE_OTA_PASSWORD")
cg.add_define("USE_OTA_VERSION", config[CONF_VERSION])
await cg.register_component(var, config)
if config[CONF_SAFE_MODE]:
condition = var.should_enter_safe_mode(
config[CONF_NUM_ATTEMPTS], config[CONF_REBOOT_TIMEOUT]
)
cg.add(RawExpression(f"if ({condition}) return"))
CORE.data[CONF_OTA][KEY_PAST_SAFE_MODE] = True
if CORE.is_esp32 and CORE.using_arduino:
cg.add_library("Update", None)
@ -112,11 +90,18 @@ async def to_code(config):
if CORE.is_rp2040 and CORE.using_arduino:
cg.add_library("Updater", None)
async def ota_to_code(var, config):
await cg.past_safe_mode()
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_ABORT, []):
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_BEGIN, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)

View File

@ -1,11 +1,8 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_OTA_STATE_CALLBACK
#include "ota_backend.h"
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/ota/ota_component.h"
namespace esphome {
namespace ota {
@ -15,7 +12,7 @@ class OTAStateChangeTrigger : public Trigger<OTAState> {
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);
trigger(state);
}
});
}
@ -54,6 +51,17 @@ class OTAEndTrigger : public Trigger<> {
}
};
class OTAAbortTrigger : public Trigger<> {
public:
explicit OTAAbortTrigger(OTAComponent *parent) {
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
if (state == OTA_ABORT && !parent->is_failed()) {
trigger();
}
});
}
};
class OTAErrorTrigger : public Trigger<uint8_t> {
public:
explicit OTAErrorTrigger(OTAComponent *parent) {
@ -67,5 +75,4 @@ class OTAErrorTrigger : public Trigger<uint8_t> {
} // namespace ota
} // namespace esphome
#endif // USE_OTA_STATE_CALLBACK
#endif

View File

@ -1,9 +1,53 @@
#pragma once
#include "ota_component.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#ifdef USE_OTA_STATE_CALLBACK
#include "esphome/core/automation.h"
#endif
namespace esphome {
namespace ota {
enum OTAResponseTypes {
OTA_RESPONSE_OK = 0x00,
OTA_RESPONSE_REQUEST_AUTH = 0x01,
OTA_RESPONSE_HEADER_OK = 0x40,
OTA_RESPONSE_AUTH_OK = 0x41,
OTA_RESPONSE_UPDATE_PREPARE_OK = 0x42,
OTA_RESPONSE_BIN_MD5_OK = 0x43,
OTA_RESPONSE_RECEIVE_OK = 0x44,
OTA_RESPONSE_UPDATE_END_OK = 0x45,
OTA_RESPONSE_SUPPORTS_COMPRESSION = 0x46,
OTA_RESPONSE_CHUNK_OK = 0x47,
OTA_RESPONSE_ERROR_MAGIC = 0x80,
OTA_RESPONSE_ERROR_UPDATE_PREPARE = 0x81,
OTA_RESPONSE_ERROR_AUTH_INVALID = 0x82,
OTA_RESPONSE_ERROR_WRITING_FLASH = 0x83,
OTA_RESPONSE_ERROR_UPDATE_END = 0x84,
OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 0x85,
OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 0x86,
OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 0x87,
OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 0x88,
OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 0x89,
OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A,
OTA_RESPONSE_ERROR_MD5_MISMATCH = 0x8B,
OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C,
OTA_RESPONSE_ERROR_UNKNOWN = 0xFF,
};
enum OTAState {
OTA_COMPLETED = 0,
OTA_STARTED,
OTA_IN_PROGRESS,
OTA_ABORT,
OTA_ERROR,
};
class OTABackend {
public:
virtual ~OTABackend() = default;
@ -15,5 +59,38 @@ class OTABackend {
virtual bool supports_compression() = 0;
};
class OTAComponent : public Component {
#ifdef USE_OTA_STATE_CALLBACK
public:
void add_on_state_callback(std::function<void(ota::OTAState, float, uint8_t)> &&callback) {
this->state_callback_.add(std::move(callback));
}
protected:
CallbackManager<void(ota::OTAState, float, uint8_t)> state_callback_{};
#endif
};
#ifdef USE_OTA_STATE_CALLBACK
class OTAGlobalCallback {
public:
void register_ota(OTAComponent *ota_caller) {
ota_caller->add_on_state_callback([this, ota_caller](OTAState state, float progress, uint8_t error) {
this->state_callback_.call(state, progress, error, ota_caller);
});
}
void add_on_state_callback(std::function<void(OTAState, float, uint8_t, OTAComponent *)> &&callback) {
this->state_callback_.add(std::move(callback));
}
protected:
CallbackManager<void(OTAState, float, uint8_t, OTAComponent *)> state_callback_{};
};
OTAGlobalCallback *get_global_ota_callback();
void register_ota_platform(OTAComponent *ota_caller);
#endif
std::unique_ptr<ota::OTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome

View File

@ -1,15 +1,19 @@
#include "esphome/core/defines.h"
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include "ota_backend_arduino_esp32.h"
#include "ota_component.h"
#include "ota_backend.h"
#include "ota_backend_arduino_esp32.h"
#include <Update.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_esp32";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP32OTABackend>(); }
OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) {
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
@ -19,6 +23,9 @@ OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) {
uint8_t error = Update.getError();
if (error == UPDATE_ERROR_SIZE)
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
@ -26,16 +33,25 @@ void ArduinoESP32OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5
OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
if (written != len) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
if (written == len) {
return OTA_RESPONSE_OK;
}
return OTA_RESPONSE_OK;
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoESP32OTABackend::end() {
if (!Update.end())
return OTA_RESPONSE_ERROR_UPDATE_END;
return OTA_RESPONSE_OK;
if (Update.end()) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoESP32OTABackend::abort() { Update.abort(); }

View File

@ -1,10 +1,10 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "ota_component.h"
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace ota {

View File

@ -1,17 +1,21 @@
#include "esphome/core/defines.h"
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
#include "ota_backend_arduino_esp8266.h"
#include "ota_component.h"
#include "ota_backend.h"
#include "esphome/components/esp8266/preferences.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include <Updater.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_esp8266";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP8266OTABackend>(); }
OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) {
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
@ -28,6 +32,9 @@ OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG;
if (error == UPDATE_ERROR_SPACE)
return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
@ -35,16 +42,25 @@ void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { Update.setMD5(m
OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
if (written != len) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
if (written == len) {
return OTA_RESPONSE_OK;
}
return OTA_RESPONSE_OK;
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoESP8266OTABackend::end() {
if (!Update.end())
return OTA_RESPONSE_ERROR_UPDATE_END;
return OTA_RESPONSE_OK;
if (Update.end()) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoESP8266OTABackend::abort() {

View File

@ -1,10 +1,9 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
#include "ota_component.h"
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
namespace esphome {

View File

@ -1,15 +1,19 @@
#include "esphome/core/defines.h"
#ifdef USE_LIBRETINY
#include "ota_backend_arduino_libretiny.h"
#include "ota_component.h"
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include <Update.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_libretiny";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoLibreTinyOTABackend>(); }
OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) {
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
@ -19,6 +23,9 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) {
uint8_t error = Update.getError();
if (error == UPDATE_ERROR_SIZE)
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
@ -26,16 +33,25 @@ void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) { Update.setMD5
OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
if (written != len) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
if (written == len) {
return OTA_RESPONSE_OK;
}
return OTA_RESPONSE_OK;
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoLibreTinyOTABackend::end() {
if (!Update.end())
return OTA_RESPONSE_ERROR_UPDATE_END;
return OTA_RESPONSE_OK;
if (Update.end()) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoLibreTinyOTABackend::abort() { Update.abort(); }

View File

@ -1,10 +1,9 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_LIBRETINY
#include "ota_component.h"
#include "ota_backend.h"
#include "esphome/core/defines.h"
namespace esphome {
namespace ota {

View File

@ -1,17 +1,21 @@
#include "esphome/core/defines.h"
#ifdef USE_ARDUINO
#ifdef USE_RP2040
#include "ota_backend_arduino_rp2040.h"
#include "ota_backend.h"
#include "esphome/components/rp2040/preferences.h"
#include "ota_backend.h"
#include "ota_backend_arduino_rp2040.h"
#include "ota_component.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include <Updater.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_rp2040";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoRP2040OTABackend>(); }
OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
@ -28,6 +32,9 @@ OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG;
if (error == UPDATE_ERROR_SPACE)
return OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
@ -35,16 +42,25 @@ void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { Update.setMD5(md
OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
if (written != len) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
if (written == len) {
return OTA_RESPONSE_OK;
}
return OTA_RESPONSE_OK;
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoRP2040OTABackend::end() {
if (!Update.end())
return OTA_RESPONSE_ERROR_UPDATE_END;
return OTA_RESPONSE_OK;
if (Update.end()) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoRP2040OTABackend::abort() {

View File

@ -1,11 +1,10 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ARDUINO
#ifdef USE_RP2040
#include "esphome/core/macros.h"
#include "ota_backend.h"
#include "ota_component.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
namespace esphome {
namespace ota {

View File

@ -1,12 +1,11 @@
#include "esphome/core/defines.h"
#ifdef USE_ESP_IDF
#include <esp_task_wdt.h>
#include "ota_backend_esp_idf.h"
#include "ota_component.h"
#include <esp_ota_ops.h>
#include "esphome/components/md5/md5.h"
#include "esphome/core/defines.h"
#include <esp_ota_ops.h>
#include <esp_task_wdt.h>
#if ESP_IDF_VERSION_MAJOR >= 5
#include <spi_flash_mmap.h>
@ -15,6 +14,8 @@
namespace esphome {
namespace ota {
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::IDFOTABackend>(); }
OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
this->partition_ = esp_ota_get_next_update_partition(nullptr);
if (this->partition_ == nullptr) {

View File

@ -1,11 +1,11 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ESP_IDF
#include "ota_component.h"
#include "ota_backend.h"
#include <esp_ota_ops.h>
#include "esphome/components/md5/md5.h"
#include "esphome/core/defines.h"
#include <esp_ota_ops.h>
namespace esphome {
namespace ota {