Merge branch 'ota_base_extract' into integration

This commit is contained in:
J. Nick Koston 2025-07-01 14:09:51 -05:00
commit 3908677fe2
No known key found for this signature in database
53 changed files with 402 additions and 596 deletions

View File

@ -327,6 +327,7 @@ esphome/components/opentherm/* @olegtarasov
esphome/components/openthread/* @mrene
esphome/components/opt3001/* @ccutrer
esphome/components/ota/* @esphome/core
esphome/components/ota_base/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/packet_transport/* @clydebarrow
esphome/components/pca6416a/* @Mat931

View File

@ -15,8 +15,7 @@ namespace adc {
#ifdef USE_ESP32
// clang-format off
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 7)) || \
(ESP_IDF_VERSION_MAJOR == 5 && \
#if (ESP_IDF_VERSION_MAJOR == 5 && \
((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \
(ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \
(ESP_IDF_VERSION_MINOR >= 2)) \
@ -100,11 +99,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
bool autorange_{false};
#if ESP_IDF_VERSION_MAJOR >= 5
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
#else
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
#endif // ESP_IDF_VERSION_MAJOR
#endif // USE_ESP32
};

View File

@ -411,8 +411,8 @@ def _esp_idf_check_versions(value):
version = cv.Version.parse(cv.version_number(value[CONF_VERSION]))
source = value.get(CONF_SOURCE, None)
if version < cv.Version(4, 0, 0):
raise cv.Invalid("Only ESP-IDF 4.0+ is supported.")
if version < cv.Version(5, 0, 0):
raise cv.Invalid("Only ESP-IDF 5.0+ is supported.")
# flag this for later *before* we set value[CONF_PLATFORM_VERSION] below
has_platform_ver = CONF_PLATFORM_VERSION in value
@ -422,20 +422,15 @@ def _esp_idf_check_versions(value):
)
if (
(is_platformio := _platform_is_platformio(value[CONF_PLATFORM_VERSION]))
and version.major >= 5
and version not in SUPPORTED_PLATFORMIO_ESP_IDF_5X
):
is_platformio := _platform_is_platformio(value[CONF_PLATFORM_VERSION])
) and version not in SUPPORTED_PLATFORMIO_ESP_IDF_5X:
raise cv.Invalid(
f"ESP-IDF {str(version)} not supported by platformio/espressif32"
)
if (
version.major < 5
or (
version in SUPPORTED_PLATFORMIO_ESP_IDF_5X
and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X
)
version in SUPPORTED_PLATFORMIO_ESP_IDF_5X
and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X
) and not has_platform_ver:
raise cv.Invalid(
f"ESP-IDF {value[CONF_VERSION]} may be supported by platformio/espressif32; please specify '{CONF_PLATFORM_VERSION}'"
@ -801,14 +796,9 @@ async def to_code(config):
if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC):
add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True)
if (framework_ver.major, framework_ver.minor) >= (4, 4):
add_idf_sdkconfig_option(
"CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False
)
else:
add_idf_sdkconfig_option(
"CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False
)
add_idf_sdkconfig_option(
"CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False
)
if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES):
_LOGGER.warning(
"Using experimental features in ESP-IDF may result in unexpected failures."

View File

@ -56,11 +56,7 @@ void arch_init() {
void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); }
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
#if ESP_IDF_VERSION_MAJOR >= 5
uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
#else
uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); }
#endif
uint32_t arch_get_cpu_freq_hz() {
uint32_t freq = 0;
#ifdef USE_ESP_IDF

View File

@ -29,8 +29,6 @@ from esphome.const import (
CONF_ON_BLE_SERVICE_DATA_ADVERTISE,
CONF_SERVICE_UUID,
CONF_TRIGGER_ID,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
)
from esphome.core import CORE
@ -323,10 +321,7 @@ async def to_code(config):
# https://github.com/espressif/esp-idf/issues/2503
# Match arduino CONFIG_BTU_TASK_STACK_SIZE
# https://github.com/espressif/arduino-esp32/blob/fd72cf46ad6fc1a6de99c1d83ba8eba17d80a4ee/tools/sdk/esp32/sdkconfig#L1866
if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(4, 4, 6):
add_idf_sdkconfig_option("CONFIG_BT_BTU_TASK_STACK_SIZE", 8192)
else:
add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192)
add_idf_sdkconfig_option("CONFIG_BT_BTU_TASK_STACK_SIZE", 8192)
add_idf_sdkconfig_option("CONFIG_BT_ACL_CONNECTIONS", 9)
add_idf_sdkconfig_option(
"CONFIG_BTDM_CTRL_BLE_MAX_CONN", config[CONF_MAX_CONNECTIONS]
@ -335,8 +330,7 @@ async def to_code(config):
# max notifications in 5.x, setting CONFIG_BT_ACL_CONNECTIONS
# is enough in 4.x
# https://github.com/esphome/issues/issues/6808
if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(5, 0, 0):
add_idf_sdkconfig_option("CONFIG_BT_GATTC_NOTIF_REG_MAX", 9)
add_idf_sdkconfig_option("CONFIG_BT_GATTC_NOTIF_REG_MAX", 9)
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
cg.add_define("USE_ESP32_BLE_CLIENT")

View File

@ -18,7 +18,7 @@
#include <cinttypes>
#ifdef USE_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota_base/ota_backend.h"
#endif
#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
@ -61,9 +61,9 @@ void ESP32BLETracker::setup() {
global_esp32_ble_tracker = this;
#ifdef USE_OTA
ota::get_global_ota_callback()->add_on_state_callback(
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) {
ota_base::get_global_ota_callback()->add_on_state_callback(
[this](ota_base::OTAState state, float progress, uint8_t error, ota_base::OTAComponent *comp) {
if (state == ota_base::OTA_STARTED) {
this->stop_scan();
for (auto *client : this->clients_) {
client->disconnect();

View File

@ -1,7 +1,8 @@
import logging
import esphome.codegen as cg
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code
from esphome.components.ota_base import OTAComponent
from esphome.config_helpers import merge_config
import esphome.config_validation as cv
from esphome.const import (

View File

@ -2,12 +2,12 @@
#ifdef USE_OTA
#include "esphome/components/md5/md5.h"
#include "esphome/components/network/util.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_arduino_esp32.h"
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_libretiny.h"
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota/ota_backend_esp_idf.h"
#include "esphome/components/ota_base/ota_backend.h" // For OTAComponent and callbacks
#include "esphome/components/ota_base/ota_backend_arduino_esp32.h"
#include "esphome/components/ota_base/ota_backend_arduino_esp8266.h"
#include "esphome/components/ota_base/ota_backend_arduino_libretiny.h"
#include "esphome/components/ota_base/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota_base/ota_backend_esp_idf.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
@ -23,7 +23,7 @@ static constexpr u_int16_t OTA_BLOCK_SIZE = 8192;
void ESPHomeOTAComponent::setup() {
#ifdef USE_OTA_STATE_CALLBACK
ota::register_ota_platform(this);
ota_base::register_ota_platform(this);
#endif
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
@ -94,7 +94,7 @@ void ESPHomeOTAComponent::loop() {
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
void ESPHomeOTAComponent::handle_() {
ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN;
ota_base::OTAResponseTypes error_code = ota_base::OTA_RESPONSE_ERROR_UNKNOWN;
bool update_started = false;
size_t total = 0;
uint32_t last_progress = 0;
@ -102,7 +102,7 @@ void ESPHomeOTAComponent::handle_() {
char *sbuf = reinterpret_cast<char *>(buf);
size_t ota_size;
uint8_t ota_features;
std::unique_ptr<ota::OTABackend> backend;
std::unique_ptr<ota_base::OTABackend> backend;
(void) ota_features;
#if USE_OTA_VERSION == 2
size_t size_acknowledged = 0;
@ -129,7 +129,7 @@ void ESPHomeOTAComponent::handle_() {
ESP_LOGD(TAG, "Starting update from %s", this->client_->getpeername().c_str());
this->status_set_warning();
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
this->state_callback_.call(ota_base::OTA_STARTED, 0.0f, 0);
#endif
if (!this->readall_(buf, 5)) {
@ -140,16 +140,16 @@ void ESPHomeOTAComponent::handle_() {
if (buf[0] != 0x6C || buf[1] != 0x26 || buf[2] != 0xF7 || buf[3] != 0x5C || buf[4] != 0x45) {
ESP_LOGW(TAG, "Magic bytes do not match! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3],
buf[4]);
error_code = ota::OTA_RESPONSE_ERROR_MAGIC;
error_code = ota_base::OTA_RESPONSE_ERROR_MAGIC;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Send OK and version - 2 bytes
buf[0] = ota::OTA_RESPONSE_OK;
buf[0] = ota_base::OTA_RESPONSE_OK;
buf[1] = USE_OTA_VERSION;
this->writeall_(buf, 2);
backend = ota::make_ota_backend();
backend = ota_base::make_ota_backend();
// Read features - 1 byte
if (!this->readall_(buf, 1)) {
@ -160,16 +160,16 @@ void ESPHomeOTAComponent::handle_() {
ESP_LOGV(TAG, "Features: 0x%02X", ota_features);
// Acknowledge header - 1 byte
buf[0] = ota::OTA_RESPONSE_HEADER_OK;
buf[0] = ota_base::OTA_RESPONSE_HEADER_OK;
if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) {
buf[0] = ota::OTA_RESPONSE_SUPPORTS_COMPRESSION;
buf[0] = ota_base::OTA_RESPONSE_SUPPORTS_COMPRESSION;
}
this->writeall_(buf, 1);
#ifdef USE_OTA_PASSWORD
if (!this->password_.empty()) {
buf[0] = ota::OTA_RESPONSE_REQUEST_AUTH;
buf[0] = ota_base::OTA_RESPONSE_REQUEST_AUTH;
this->writeall_(buf, 1);
md5::MD5Digest md5{};
md5.init();
@ -220,14 +220,14 @@ void ESPHomeOTAComponent::handle_() {
if (!matches) {
ESP_LOGW(TAG, "Auth failed! Passwords do not match");
error_code = ota::OTA_RESPONSE_ERROR_AUTH_INVALID;
error_code = ota_base::OTA_RESPONSE_ERROR_AUTH_INVALID;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
}
#endif // USE_OTA_PASSWORD
// Acknowledge auth OK - 1 byte
buf[0] = ota::OTA_RESPONSE_AUTH_OK;
buf[0] = ota_base::OTA_RESPONSE_AUTH_OK;
this->writeall_(buf, 1);
// Read size, 4 bytes MSB first
@ -243,12 +243,12 @@ void ESPHomeOTAComponent::handle_() {
ESP_LOGV(TAG, "Size is %u bytes", ota_size);
error_code = backend->begin(ota_size);
if (error_code != ota::OTA_RESPONSE_OK)
if (error_code != ota_base::OTA_RESPONSE_OK)
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
update_started = true;
// Acknowledge prepare OK - 1 byte
buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK;
buf[0] = ota_base::OTA_RESPONSE_UPDATE_PREPARE_OK;
this->writeall_(buf, 1);
// Read binary MD5, 32 bytes
@ -261,7 +261,7 @@ void ESPHomeOTAComponent::handle_() {
backend->set_update_md5(sbuf);
// Acknowledge MD5 OK - 1 byte
buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK;
buf[0] = ota_base::OTA_RESPONSE_BIN_MD5_OK;
this->writeall_(buf, 1);
while (total < ota_size) {
@ -285,14 +285,14 @@ void ESPHomeOTAComponent::handle_() {
}
error_code = backend->write(buf, read);
if (error_code != ota::OTA_RESPONSE_OK) {
if (error_code != ota_base::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error writing binary data to flash!, error_code: %d", error_code);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
total += read;
#if USE_OTA_VERSION == 2
while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
buf[0] = ota::OTA_RESPONSE_CHUNK_OK;
buf[0] = ota_base::OTA_RESPONSE_CHUNK_OK;
this->writeall_(buf, 1);
size_acknowledged += OTA_BLOCK_SIZE;
}
@ -304,7 +304,7 @@ void ESPHomeOTAComponent::handle_() {
float percentage = (total * 100.0f) / ota_size;
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
this->state_callback_.call(ota_base::OTA_IN_PROGRESS, percentage, 0);
#endif
// feed watchdog and give other tasks a chance to run
App.feed_wdt();
@ -313,21 +313,21 @@ void ESPHomeOTAComponent::handle_() {
}
// Acknowledge receive OK - 1 byte
buf[0] = ota::OTA_RESPONSE_RECEIVE_OK;
buf[0] = ota_base::OTA_RESPONSE_RECEIVE_OK;
this->writeall_(buf, 1);
error_code = backend->end();
if (error_code != ota::OTA_RESPONSE_OK) {
if (error_code != ota_base::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error ending update! error_code: %d", error_code);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Acknowledge Update end OK - 1 byte
buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK;
buf[0] = ota_base::OTA_RESPONSE_UPDATE_END_OK;
this->writeall_(buf, 1);
// Read ACK
if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) {
if (!this->readall_(buf, 1) || buf[0] != ota_base::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Reading back acknowledgement failed");
// do not go to error, this is not fatal
}
@ -338,7 +338,7 @@ void ESPHomeOTAComponent::handle_() {
ESP_LOGI(TAG, "Update complete");
this->status_clear_warning();
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, 0);
this->state_callback_.call(ota_base::OTA_COMPLETED, 100.0f, 0);
#endif
delay(100); // NOLINT
App.safe_reboot();
@ -355,7 +355,7 @@ error:
this->status_momentary_error("onerror", 5000);
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
#endif
}

View File

@ -4,13 +4,13 @@
#ifdef USE_OTA
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/socket/socket.h"
namespace esphome {
/// ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA.
class ESPHomeOTAComponent : public ota::OTAComponent {
class ESPHomeOTAComponent : public ota_base::OTAComponent {
public:
#ifdef USE_OTA_PASSWORD
void set_auth_password(const std::string &password) { password_ = password; }

View File

@ -19,11 +19,7 @@
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_eth.h"
#if ESP_IDF_VERSION_MAJOR >= 5
#include "esp_eth_phy_802_3.h"
#else
#include "eth_phy_regs_struct.h"
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
@ -174,11 +170,7 @@ static esp_err_t jl1101_reset_hw(esp_eth_phy_t *phy) {
return ESP_OK;
}
#if ESP_IDF_VERSION_MAJOR >= 5
static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *nego_state) {
#else
static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy) {
#endif
phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
esp_eth_mediator_t *eth = jl1101->eth;
/* in case any link status has changed, let's assume we're in link down status */
@ -293,11 +285,7 @@ static esp_err_t jl1101_init(esp_eth_phy_t *phy) {
esp_eth_mediator_t *eth = jl1101->eth;
// Detect PHY address
if (jl1101->addr == ESP_ETH_PHY_ADDR_AUTO) {
#if ESP_IDF_VERSION_MAJOR >= 5
PHY_CHECK(esp_eth_phy_802_3_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err);
#else
PHY_CHECK(esp_eth_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err);
#endif
}
/* Power on Ethernet PHY */
PHY_CHECK(jl1101_pwrctl(phy, true) == ESP_OK, "power control failed", err);
@ -336,11 +324,7 @@ esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config) {
jl1101->parent.init = jl1101_init;
jl1101->parent.deinit = jl1101_deinit;
jl1101->parent.set_mediator = jl1101_set_mediator;
#if ESP_IDF_VERSION_MAJOR >= 5
jl1101->parent.autonego_ctrl = jl1101_negotiate;
#else
jl1101->parent.negotiate = jl1101_negotiate;
#endif
jl1101->parent.get_link = jl1101_get_link;
jl1101->parent.pwrctl = jl1101_pwrctl;
jl1101->parent.get_addr = jl1101_get_addr;

View File

@ -122,25 +122,12 @@ void EthernetComponent::setup() {
.post_cb = nullptr,
};
#if ESP_IDF_VERSION_MAJOR >= 5
#if CONFIG_ETH_SPI_ETHERNET_W5500
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg);
#endif
#if CONFIG_ETH_SPI_ETHERNET_DM9051
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(host, &devcfg);
#endif
#else
spi_device_handle_t spi_handle = nullptr;
err = spi_bus_add_device(host, &devcfg, &spi_handle);
ESPHL_ERROR_CHECK(err, "SPI bus add device error");
#if CONFIG_ETH_SPI_ETHERNET_W5500
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
#endif
#if CONFIG_ETH_SPI_ETHERNET_DM9051
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
#endif
#endif // ESP_IDF_VERSION_MAJOR >= 5
#if CONFIG_ETH_SPI_ETHERNET_W5500
w5500_config.int_gpio_num = this->interrupt_pin_;
@ -211,11 +198,7 @@ void EthernetComponent::setup() {
}
case ETHERNET_TYPE_KSZ8081:
case ETHERNET_TYPE_KSZ8081RNA: {
#if ESP_IDF_VERSION_MAJOR >= 5
this->phy_ = esp_eth_phy_new_ksz80xx(&phy_config);
#else
this->phy_ = esp_eth_phy_new_ksz8081(&phy_config);
#endif
break;
}
#endif

View File

@ -1,6 +1,6 @@
from esphome import automation
import esphome.codegen as cg
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME
from esphome.core import coroutine_with_priority
@ -15,6 +15,9 @@ DEPENDENCIES = ["network", "http_request"]
CONF_MD5 = "md5"
CONF_MD5_URL = "md5_url"
ota_base_ns = cg.esphome_ns.namespace("ota_base")
OTAComponent = ota_base_ns.class_("OTAComponent", cg.Component)
OtaHttpRequestComponent = http_request_ns.class_(
"OtaHttpRequestComponent", OTAComponent
)

View File

@ -6,11 +6,11 @@
#include "esphome/components/md5/md5.h"
#include "esphome/components/watchdog/watchdog.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_arduino_esp32.h"
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota/ota_backend_esp_idf.h"
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/ota_base/ota_backend_arduino_esp32.h"
#include "esphome/components/ota_base/ota_backend_arduino_esp8266.h"
#include "esphome/components/ota_base/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota_base/ota_backend_esp_idf.h"
namespace esphome {
namespace http_request {
@ -19,7 +19,7 @@ static const char *const TAG = "http_request.ota";
void OtaHttpRequestComponent::setup() {
#ifdef USE_OTA_STATE_CALLBACK
ota::register_ota_platform(this);
ota_base::register_ota_platform(this);
#endif
}
@ -50,15 +50,15 @@ void OtaHttpRequestComponent::flash() {
ESP_LOGI(TAG, "Starting update");
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
this->state_callback_.call(ota_base::OTA_STARTED, 0.0f, 0);
#endif
auto ota_status = this->do_ota_();
switch (ota_status) {
case ota::OTA_RESPONSE_OK:
case ota_base::OTA_RESPONSE_OK:
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, ota_status);
this->state_callback_.call(ota_base::OTA_COMPLETED, 100.0f, ota_status);
#endif
delay(10);
App.safe_reboot();
@ -66,7 +66,7 @@ void OtaHttpRequestComponent::flash() {
default:
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_ERROR, 0.0f, ota_status);
this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, ota_status);
#endif
this->md5_computed_.clear(); // will be reset at next attempt
this->md5_expected_.clear(); // will be reset at next attempt
@ -74,7 +74,7 @@ void OtaHttpRequestComponent::flash() {
}
}
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota::OTABackend> backend,
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota_base::OTABackend> backend,
const std::shared_ptr<HttpContainer> &container) {
if (this->update_started_) {
ESP_LOGV(TAG, "Aborting OTA backend");
@ -115,9 +115,9 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
ESP_LOGV(TAG, "MD5Digest initialized");
ESP_LOGV(TAG, "OTA backend begin");
auto backend = ota::make_ota_backend();
auto backend = ota_base::make_ota_backend();
auto error_code = backend->begin(container->content_length);
if (error_code != ota::OTA_RESPONSE_OK) {
if (error_code != ota_base::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "backend->begin error: %d", error_code);
this->cleanup_(std::move(backend), container);
return error_code;
@ -144,7 +144,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
// write bytes to OTA backend
this->update_started_ = true;
error_code = backend->write(buf, bufsize);
if (error_code != ota::OTA_RESPONSE_OK) {
if (error_code != ota_base::OTA_RESPONSE_OK) {
// error code explanation available at
// https://github.com/esphome/esphome/blob/dev/esphome/components/ota/ota_backend.h
ESP_LOGE(TAG, "Error code (%02X) writing binary data to flash at offset %d and size %d", error_code,
@ -160,7 +160,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
float percentage = container->get_bytes_read() * 100.0f / container->content_length;
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
this->state_callback_.call(ota_base::OTA_IN_PROGRESS, percentage, 0);
#endif
}
} // while
@ -174,7 +174,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
if (strncmp(this->md5_computed_.c_str(), this->md5_expected_.c_str(), MD5_SIZE) != 0) {
ESP_LOGE(TAG, "MD5 computed: %s - Aborting due to MD5 mismatch", this->md5_computed_.c_str());
this->cleanup_(std::move(backend), container);
return ota::OTA_RESPONSE_ERROR_MD5_MISMATCH;
return ota_base::OTA_RESPONSE_ERROR_MD5_MISMATCH;
} else {
backend->set_update_md5(md5_receive_str.get());
}
@ -187,14 +187,14 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
delay(100); // NOLINT
error_code = backend->end();
if (error_code != ota::OTA_RESPONSE_OK) {
if (error_code != ota_base::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error ending update! error_code: %d", error_code);
this->cleanup_(std::move(backend), container);
return error_code;
}
ESP_LOGI(TAG, "Update complete");
return ota::OTA_RESPONSE_OK;
return ota_base::OTA_RESPONSE_OK;
}
std::string OtaHttpRequestComponent::get_url_with_auth_(const std::string &url) {

View File

@ -1,6 +1,6 @@
#pragma once
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
@ -22,7 +22,7 @@ enum OtaHttpRequestError : uint8_t {
OTA_CONNECTION_ERROR = 0x12,
};
class OtaHttpRequestComponent : public ota::OTAComponent, public Parented<HttpRequestComponent> {
class OtaHttpRequestComponent : public ota_base::OTAComponent, public Parented<HttpRequestComponent> {
public:
void setup() override;
void dump_config() override;
@ -40,7 +40,7 @@ class OtaHttpRequestComponent : public ota::OTAComponent, public Parented<HttpRe
void flash();
protected:
void cleanup_(std::unique_ptr<ota::OTABackend> backend, const std::shared_ptr<HttpContainer> &container);
void cleanup_(std::unique_ptr<ota_base::OTABackend> backend, const std::shared_ptr<HttpContainer> &container);
uint8_t do_ota_();
std::string get_url_with_auth_(const std::string &url);
bool http_get_md5_();

View File

@ -5,6 +5,7 @@
#include "esphome/components/json/json_util.h"
#include "esphome/components/network/util.h"
#include "esphome/components/ota_base/ota_backend.h"
namespace esphome {
namespace http_request {
@ -21,13 +22,13 @@ static const char *const TAG = "http_request.update";
static const size_t MAX_READ_SIZE = 256;
void HttpRequestUpdate::setup() {
this->ota_parent_->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t err) {
if (state == ota::OTAState::OTA_IN_PROGRESS) {
this->ota_parent_->add_on_state_callback([this](ota_base::OTAState state, float progress, uint8_t err) {
if (state == ota_base::OTAState::OTA_IN_PROGRESS) {
this->state_ = update::UPDATE_STATE_INSTALLING;
this->update_info_.has_progress = true;
this->update_info_.progress = progress;
this->publish_state();
} else if (state == ota::OTAState::OTA_ABORT || state == ota::OTAState::OTA_ERROR) {
} else if (state == ota_base::OTAState::OTA_ABORT || state == ota_base::OTAState::OTA_ERROR) {
this->state_ = update::UPDATE_STATE_AVAILABLE;
this->status_set_error("Failed to install firmware");
this->publish_state();

View File

@ -9,14 +9,7 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S3,
)
import esphome.config_validation as cv
from esphome.const import (
CONF_BITS_PER_SAMPLE,
CONF_CHANNEL,
CONF_ID,
CONF_SAMPLE_RATE,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
)
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE
from esphome.core import CORE
from esphome.cpp_generator import MockObjClass
import esphome.final_validate as fv
@ -250,8 +243,7 @@ def _final_validate(_):
def use_legacy():
framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0):
if CORE.using_esp_idf:
if not _use_legacy_driver:
return False
return True

View File

@ -9,15 +9,11 @@ namespace i2s_audio {
static const char *const TAG = "i2s_audio";
#if ESP_IDF_VERSION_MAJOR >= 5
static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :(
#endif
void I2SAudioComponent::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
static i2s_port_t next_port_num = I2S_NUM_0;
if (next_port_num >= I2S_NUM_MAX) {
if (next_port_num >= SOC_I2S_NUM) {
ESP_LOGE(TAG, "Too many components");
this->mark_failed();
return;

View File

@ -59,11 +59,7 @@ optional<uint8_t> ImprovSerialComponent::read_byte_() {
break;
#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC)
case logger::UART_SELECTION_USB_CDC:
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
if (esp_usb_console_available_for_read()) {
#else
if (esp_usb_console_read_available()) {
#endif
esp_usb_console_read_buf((char *) &data, 1);
byte = data;
}

View File

@ -10,11 +10,7 @@ uint8_t temprature_sens_read();
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \
defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \
defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32P4)
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#include "driver/temp_sensor.h"
#else
#include "driver/temperature_sensor.h"
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#endif // USE_ESP32_VARIANT
#endif // USE_ESP32
#ifdef USE_RP2040
@ -31,12 +27,11 @@ namespace internal_temperature {
static const char *const TAG = "internal_temperature";
#ifdef USE_ESP32
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \
(defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \
defined(USE_ESP32_VARIANT_ESP32P4))
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \
defined(USE_ESP32_VARIANT_ESP32P4)
static temperature_sensor_handle_t tsensNew = NULL;
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT
#endif // USE_ESP32_VARIANT
#endif // USE_ESP32
void InternalTemperatureSensor::update() {
@ -51,24 +46,11 @@ void InternalTemperatureSensor::update() {
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \
defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \
defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32P4)
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT();
temp_sensor_set_config(tsens);
temp_sensor_start();
#if defined(USE_ESP32_VARIANT_ESP32S3) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 3))
#error \
"ESP32-S3 internal temperature sensor requires ESP IDF V4.4.3 or higher. See https://github.com/esphome/issues/issues/4271"
#endif
esp_err_t result = temp_sensor_read_celsius(&temperature);
temp_sensor_stop();
success = (result == ESP_OK);
#else
esp_err_t result = temperature_sensor_get_celsius(tsensNew, &temperature);
success = (result == ESP_OK);
if (!success) {
ESP_LOGE(TAG, "Reading failed (%d)", result);
}
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#endif // USE_ESP32_VARIANT
#endif // USE_ESP32
#ifdef USE_RP2040
@ -99,10 +81,9 @@ void InternalTemperatureSensor::update() {
void InternalTemperatureSensor::setup() {
#ifdef USE_ESP32
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \
(defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \
defined(USE_ESP32_VARIANT_ESP32P4))
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \
defined(USE_ESP32_VARIANT_ESP32P4)
ESP_LOGCONFIG(TAG, "Running setup");
temperature_sensor_config_t tsens_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
@ -120,7 +101,7 @@ void InternalTemperatureSensor::setup() {
this->mark_failed();
return;
}
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT
#endif // USE_ESP32_VARIANT
#endif // USE_ESP32
}

View File

@ -1,46 +1,21 @@
import esphome.codegen as cg
from esphome.components import sensor
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import VARIANT_ESP32S3
import esphome.config_validation as cv
from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
ENTITY_CATEGORY_DIAGNOSTIC,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
PLATFORM_BK72XX,
PLATFORM_ESP32,
PLATFORM_RP2040,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
from esphome.core import CORE
internal_temperature_ns = cg.esphome_ns.namespace("internal_temperature")
InternalTemperatureSensor = internal_temperature_ns.class_(
"InternalTemperatureSensor", sensor.Sensor, cg.PollingComponent
)
def validate_config(config):
if CORE.is_esp32:
variant = get_esp32_variant()
if variant == VARIANT_ESP32S3:
if CORE.using_arduino and CORE.data[KEY_CORE][
KEY_FRAMEWORK_VERSION
] < cv.Version(2, 0, 6):
raise cv.Invalid(
"ESP32-S3 Internal Temperature Sensor requires framework version 2.0.6 or higher. See <https://github.com/esphome/issues/issues/4271>."
)
if CORE.using_esp_idf and CORE.data[KEY_CORE][
KEY_FRAMEWORK_VERSION
] < cv.Version(4, 4, 3):
raise cv.Invalid(
"ESP32-S3 Internal Temperature Sensor requires framework version 4.4.3 or higher. See <https://github.com/esphome/issues/issues/4271>."
)
return config
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
InternalTemperatureSensor,
@ -51,7 +26,6 @@ CONFIG_SCHEMA = cv.All(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
).extend(cv.polling_component_schema("60s")),
cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040, PLATFORM_BK72XX]),
validate_config,
)

View File

@ -83,9 +83,7 @@ void init_uart(uart_port_t uart_num, uint32_t baud_rate, int tx_buffer_size) {
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
uart_config.source_clk = UART_SCLK_DEFAULT;
#endif
uart_param_config(uart_num, &uart_config);
const int uart_buffer_size = tx_buffer_size;
// Install UART driver using an event queue here

View File

@ -8,8 +8,6 @@ from esphome.const import (
CONF_PROTOCOL,
CONF_SERVICE,
CONF_SERVICES,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
)
from esphome.core import CORE, coroutine_with_priority
@ -85,9 +83,7 @@ async def to_code(config):
elif CORE.is_rp2040:
cg.add_library("LEAmDNS", None)
if CORE.using_esp_idf and CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(
5, 0, 0
):
if CORE.using_esp_idf:
add_idf_component(name="espressif/mdns", ref="1.8.2")
cg.add_define("USE_MDNS")

View File

@ -9,7 +9,7 @@
#include "esphome/components/audio/audio_transfer_buffer.h"
#ifdef USE_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota_base/ota_backend.h"
#endif
namespace esphome {
@ -121,11 +121,11 @@ void MicroWakeWord::setup() {
});
#ifdef USE_OTA
ota::get_global_ota_callback()->add_on_state_callback(
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) {
ota_base::get_global_ota_callback()->add_on_state_callback(
[this](ota_base::OTAState state, float progress, uint8_t error, ota_base::OTAComponent *comp) {
if (state == ota_base::OTA_STARTED) {
this->suspend_task_();
} else if (state == ota::OTA_ERROR) {
} else if (state == ota_base::OTA_ERROR) {
this->resume_task_();
}
});

View File

@ -14,49 +14,6 @@ namespace mqtt {
static const char *const TAG = "mqtt.idf";
bool MQTTBackendESP32::initialize_() {
#if ESP_IDF_VERSION_MAJOR < 5
mqtt_cfg_.user_context = (void *) this;
mqtt_cfg_.buffer_size = MQTT_BUFFER_SIZE;
mqtt_cfg_.host = this->host_.c_str();
mqtt_cfg_.port = this->port_;
mqtt_cfg_.keepalive = this->keep_alive_;
mqtt_cfg_.disable_clean_session = !this->clean_session_;
if (!this->username_.empty()) {
mqtt_cfg_.username = this->username_.c_str();
if (!this->password_.empty()) {
mqtt_cfg_.password = this->password_.c_str();
}
}
if (!this->lwt_topic_.empty()) {
mqtt_cfg_.lwt_topic = this->lwt_topic_.c_str();
this->mqtt_cfg_.lwt_qos = this->lwt_qos_;
this->mqtt_cfg_.lwt_retain = this->lwt_retain_;
if (!this->lwt_message_.empty()) {
mqtt_cfg_.lwt_msg = this->lwt_message_.c_str();
mqtt_cfg_.lwt_msg_len = this->lwt_message_.size();
}
}
if (!this->client_id_.empty()) {
mqtt_cfg_.client_id = this->client_id_.c_str();
}
if (ca_certificate_.has_value()) {
mqtt_cfg_.cert_pem = ca_certificate_.value().c_str();
mqtt_cfg_.skip_cert_common_name_check = skip_cert_cn_check_;
mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_SSL;
if (this->cl_certificate_.has_value() && this->cl_key_.has_value()) {
mqtt_cfg_.client_cert_pem = this->cl_certificate_.value().c_str();
mqtt_cfg_.client_key_pem = this->cl_key_.value().c_str();
}
} else {
mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_TCP;
}
#else
mqtt_cfg_.broker.address.hostname = this->host_.c_str();
mqtt_cfg_.broker.address.port = this->port_;
mqtt_cfg_.session.keepalive = this->keep_alive_;
@ -95,7 +52,7 @@ bool MQTTBackendESP32::initialize_() {
} else {
mqtt_cfg_.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
}
#endif
auto *mqtt_client = esp_mqtt_client_init(&mqtt_cfg_);
if (mqtt_client) {
handler_.reset(mqtt_client);

View File

@ -272,18 +272,13 @@ bool OpenTherm::init_esp32_timer_() {
this->timer_idx_ = timer_idx;
timer_config_t const config = {
.alarm_en = TIMER_ALARM_EN,
.counter_en = TIMER_PAUSE,
.intr_type = TIMER_INTR_LEVEL,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = TIMER_AUTORELOAD_EN,
#if ESP_IDF_VERSION_MAJOR >= 5
.clk_src = TIMER_SRC_CLK_DEFAULT,
#endif
.divider = 80,
#if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5
.clk_src = TIMER_SRC_CLK_APB
#endif
.alarm_en = TIMER_ALARM_EN,
.counter_en = TIMER_PAUSE,
.intr_type = TIMER_INTR_LEVEL,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = TIMER_AUTORELOAD_EN,
.clk_src = TIMER_SRC_CLK_DEFAULT,
.divider = 80,
};
esp_err_t result;

View File

@ -8,10 +8,12 @@ from esphome.const import (
CONF_PLATFORM,
CONF_TRIGGER_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core import coroutine_with_priority
from ..ota_base import OTAState
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["md5", "safe_mode"]
AUTO_LOAD = ["safe_mode", "ota_base"]
IS_PLATFORM_COMPONENT = True
@ -23,8 +25,6 @@ CONF_ON_STATE_CHANGE = "on_state_change"
ota_ns = cg.esphome_ns.namespace("ota")
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())
@ -84,12 +84,6 @@ BASE_OTA_SCHEMA = cv.Schema(
async def to_code(config):
cg.add_define("USE_OTA")
if CORE.is_esp32 and CORE.using_arduino:
cg.add_library("Update", None)
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()

View File

@ -1,12 +1,16 @@
#pragma once
#ifdef USE_OTA_STATE_CALLBACK
#include "ota_backend.h"
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/core/automation.h"
namespace esphome {
namespace ota {
// Import types from ota_base for the automation triggers
using ota_base::OTAComponent;
using ota_base::OTAState;
class OTAStateChangeTrigger : public Trigger<OTAState> {
public:
explicit OTAStateChangeTrigger(OTAComponent *parent) {
@ -22,7 +26,7 @@ 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()) {
if (state == ota_base::OTA_STARTED && !parent->is_failed()) {
trigger();
}
});
@ -33,7 +37,7 @@ 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()) {
if (state == ota_base::OTA_IN_PROGRESS && !parent->is_failed()) {
trigger(progress);
}
});
@ -44,7 +48,7 @@ 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()) {
if (state == ota_base::OTA_COMPLETED && !parent->is_failed()) {
trigger();
}
});
@ -55,7 +59,7 @@ 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()) {
if (state == ota_base::OTA_ABORT && !parent->is_failed()) {
trigger();
}
});
@ -66,7 +70,7 @@ class OTAErrorTrigger : public Trigger<uint8_t> {
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()) {
if (state == ota_base::OTA_ERROR && !parent->is_failed()) {
trigger(error);
}
});

View File

@ -0,0 +1,23 @@
import esphome.codegen as cg
from esphome.core import CORE, coroutine_with_priority
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["md5"]
ota_base_ns = cg.esphome_ns.namespace("ota_base")
OTAComponent = ota_base_ns.class_("OTAComponent", cg.Component)
OTAState = ota_base_ns.enum("OTAState")
@coroutine_with_priority(52.0)
async def to_code(config):
# Note: USE_OTA_STATE_CALLBACK is not defined here
# Components that need OTA callbacks (like esp32_ble_tracker, speaker, etc.)
# define USE_OTA_STATE_CALLBACK themselves in their own __init__.py files
# This ensures the callback functionality is only compiled when actually needed
if CORE.is_esp32 and CORE.using_arduino:
cg.add_library("Update", None)
if CORE.is_rp2040 and CORE.using_arduino:
cg.add_library("Updater", None)

View File

@ -1,7 +1,9 @@
#include "ota_backend.h"
namespace esphome {
namespace ota {
namespace ota_base {
// The make_ota_backend() implementation is provided by each platform-specific backend
#ifdef USE_OTA_STATE_CALLBACK
OTAGlobalCallback *global_ota_callback{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@ -16,5 +18,5 @@ OTAGlobalCallback *get_global_ota_callback() {
void register_ota_platform(OTAComponent *ota_caller) { get_global_ota_callback()->register_ota(ota_caller); }
#endif
} // namespace ota
} // namespace ota_base
} // namespace esphome

View File

@ -9,7 +9,7 @@
#endif
namespace esphome {
namespace ota {
namespace ota_base {
enum OTAResponseTypes {
OTA_RESPONSE_OK = 0x00,
@ -59,15 +59,38 @@ class OTABackend {
virtual bool supports_compression() = 0;
};
std::unique_ptr<OTABackend> make_ota_backend();
class OTAComponent : public Component {
#ifdef USE_OTA_STATE_CALLBACK
public:
void add_on_state_callback(std::function<void(ota::OTAState, float, uint8_t)> &&callback) {
void add_on_state_callback(std::function<void(OTAState, float, uint8_t)> &&callback) {
this->state_callback_.add(std::move(callback));
}
protected:
CallbackManager<void(ota::OTAState, float, uint8_t)> state_callback_{};
/** Extended callback manager with deferred call support.
*
* This adds a call_deferred() method for thread-safe execution from other tasks.
*/
class StateCallbackManager : public CallbackManager<void(OTAState, float, uint8_t)> {
public:
StateCallbackManager(OTAComponent *component) : component_(component) {}
/** Call callbacks with deferral to main loop (for thread safety).
*
* This should be used by OTA implementations that run in separate tasks
* (like web_server OTA) to ensure callbacks execute in the main loop.
*/
void call_deferred(OTAState state, float progress, uint8_t error) {
component_->defer([this, state, progress, error]() { this->call(state, progress, error); });
}
private:
OTAComponent *component_;
};
StateCallbackManager state_callback_{this};
#endif
};
@ -89,8 +112,12 @@ class OTAGlobalCallback {
OTAGlobalCallback *get_global_ota_callback();
void register_ota_platform(OTAComponent *ota_caller);
#endif
std::unique_ptr<ota::OTABackend> make_ota_backend();
} // namespace ota
// OTA implementations should use:
// - state_callback_.call() when already in main loop (e.g., esphome OTA)
// - state_callback_.call_deferred() when in separate task (e.g., web_server OTA)
// This ensures proper callback execution in all contexts.
#endif
} // namespace ota_base
} // namespace esphome

View File

@ -8,13 +8,18 @@
#include <Update.h>
namespace esphome {
namespace ota {
namespace ota_base {
static const char *const TAG = "ota.arduino_esp32";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP32OTABackend>(); }
std::unique_ptr<OTABackend> make_ota_backend() { return make_unique<ArduinoESP32OTABackend>(); }
OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA
// where the exact firmware size is unknown due to multipart encoding
if (image_size == 0) {
image_size = UPDATE_SIZE_UNKNOWN;
}
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
return OTA_RESPONSE_OK;
@ -29,7 +34,10 @@ OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_UNKNOWN;
}
void ArduinoESP32OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); }
void ArduinoESP32OTABackend::set_update_md5(const char *md5) {
Update.setMD5(md5);
this->md5_set_ = true;
}
OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
@ -44,7 +52,9 @@ OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) {
}
OTAResponseTypes ArduinoESP32OTABackend::end() {
if (Update.end()) {
// Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5
// This matches the behavior of the old web_server OTA implementation
if (Update.end(!this->md5_set_)) {
return OTA_RESPONSE_OK;
}
@ -56,7 +66,7 @@ OTAResponseTypes ArduinoESP32OTABackend::end() {
void ArduinoESP32OTABackend::abort() { Update.abort(); }
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@ -6,7 +6,7 @@
#include "esphome/core/helpers.h"
namespace esphome {
namespace ota {
namespace ota_base {
class ArduinoESP32OTABackend : public OTABackend {
public:
@ -16,9 +16,12 @@ class ArduinoESP32OTABackend : public OTABackend {
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@ -10,13 +10,18 @@
#include <Updater.h>
namespace esphome {
namespace ota {
namespace ota_base {
static const char *const TAG = "ota.arduino_esp8266";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP8266OTABackend>(); }
std::unique_ptr<OTABackend> make_ota_backend() { return make_unique<ArduinoESP8266OTABackend>(); }
OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) by calculating available space
if (image_size == 0) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
image_size = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
}
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
esp8266::preferences_prevent_write(true);
@ -38,7 +43,10 @@ OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_UNKNOWN;
}
void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); }
void ArduinoESP8266OTABackend::set_update_md5(const char *md5) {
Update.setMD5(md5);
this->md5_set_ = true;
}
OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
@ -53,13 +61,19 @@ OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) {
}
OTAResponseTypes ArduinoESP8266OTABackend::end() {
if (Update.end()) {
// Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5
// This matches the behavior of the old web_server OTA implementation
bool success = Update.end(!this->md5_set_);
// On ESP8266, Update.end() might return false even with error code 0
// Check the actual error code to determine success
uint8_t error = Update.getError();
if (success || error == UPDATE_ERROR_OK) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
@ -68,7 +82,7 @@ void ArduinoESP8266OTABackend::abort() {
esp8266::preferences_prevent_write(false);
}
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif

View File

@ -7,7 +7,7 @@
#include "esphome/core/macros.h"
namespace esphome {
namespace ota {
namespace ota_base {
class ArduinoESP8266OTABackend : public OTABackend {
public:
@ -21,9 +21,12 @@ class ArduinoESP8266OTABackend : public OTABackend {
#else
bool supports_compression() override { return false; }
#endif
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif

View File

@ -8,13 +8,18 @@
#include <Update.h>
namespace esphome {
namespace ota {
namespace ota_base {
static const char *const TAG = "ota.arduino_libretiny";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoLibreTinyOTABackend>(); }
std::unique_ptr<OTABackend> make_ota_backend() { return make_unique<ArduinoLibreTinyOTABackend>(); }
OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA
// where the exact firmware size is unknown due to multipart encoding
if (image_size == 0) {
image_size = UPDATE_SIZE_UNKNOWN;
}
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
return OTA_RESPONSE_OK;
@ -29,7 +34,10 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_UNKNOWN;
}
void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); }
void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) {
Update.setMD5(md5);
this->md5_set_ = true;
}
OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
@ -44,7 +52,9 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) {
}
OTAResponseTypes ArduinoLibreTinyOTABackend::end() {
if (Update.end()) {
// Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5
// This matches the behavior of the old web_server OTA implementation
if (Update.end(!this->md5_set_)) {
return OTA_RESPONSE_OK;
}
@ -56,7 +66,7 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::end() {
void ArduinoLibreTinyOTABackend::abort() { Update.abort(); }
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif // USE_LIBRETINY

View File

@ -5,7 +5,7 @@
#include "esphome/core/defines.h"
namespace esphome {
namespace ota {
namespace ota_base {
class ArduinoLibreTinyOTABackend : public OTABackend {
public:
@ -15,9 +15,12 @@ class ArduinoLibreTinyOTABackend : public OTABackend {
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif // USE_LIBRETINY

View File

@ -10,13 +10,24 @@
#include <Updater.h>
namespace esphome {
namespace ota {
namespace ota_base {
static const char *const TAG = "ota.arduino_rp2040";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoRP2040OTABackend>(); }
std::unique_ptr<OTABackend> make_ota_backend() { return make_unique<ArduinoRP2040OTABackend>(); }
OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) by calculating available space
if (image_size == 0) {
// Similar to ESP8266, calculate available space from flash layout
extern uint8_t _FS_start;
extern uint8_t _FS_end;
// Calculate the size of the filesystem area which will be used for OTA
size_t fs_size = &_FS_end - &_FS_start;
// Reserve some space for filesystem overhead
image_size = (fs_size - 0x1000) & 0xFFFFF000;
ESP_LOGD(TAG, "OTA size unknown, using filesystem size: %u bytes", image_size);
}
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
rp2040::preferences_prevent_write(true);
@ -38,7 +49,10 @@ OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_UNKNOWN;
}
void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); }
void ArduinoRP2040OTABackend::set_update_md5(const char *md5) {
Update.setMD5(md5);
this->md5_set_ = true;
}
OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) {
size_t written = Update.write(data, len);
@ -53,7 +67,9 @@ OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) {
}
OTAResponseTypes ArduinoRP2040OTABackend::end() {
if (Update.end()) {
// Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5
// This matches the behavior of the old web_server OTA implementation
if (Update.end(!this->md5_set_)) {
return OTA_RESPONSE_OK;
}
@ -68,7 +84,7 @@ void ArduinoRP2040OTABackend::abort() {
rp2040::preferences_prevent_write(false);
}
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif // USE_RP2040

View File

@ -7,7 +7,7 @@
#include "esphome/core/macros.h"
namespace esphome {
namespace ota {
namespace ota_base {
class ArduinoRP2040OTABackend : public OTABackend {
public:
@ -17,9 +17,12 @@ class ArduinoRP2040OTABackend : public OTABackend {
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif // USE_RP2040

View File

@ -6,15 +6,12 @@
#include <esp_ota_ops.h>
#include <esp_task_wdt.h>
#if ESP_IDF_VERSION_MAJOR >= 5
#include <spi_flash_mmap.h>
#endif
namespace esphome {
namespace ota {
namespace ota_base {
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::IDFOTABackend>(); }
std::unique_ptr<OTABackend> make_ota_backend() { return make_unique<IDFOTABackend>(); }
OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
this->partition_ = esp_ota_get_next_update_partition(nullptr);
@ -24,7 +21,6 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// The following function takes longer than the 5 seconds timeout of WDT
#if ESP_IDF_VERSION_MAJOR >= 5
esp_task_wdt_config_t wdtc;
wdtc.idle_core_mask = 0;
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
@ -36,21 +32,14 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
wdtc.timeout_ms = 15000;
wdtc.trigger_panic = false;
esp_task_wdt_reconfigure(&wdtc);
#else
esp_task_wdt_init(15, false);
#endif
#endif
esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_);
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// Set the WDT back to the configured timeout
#if ESP_IDF_VERSION_MAJOR >= 5
wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000;
esp_task_wdt_reconfigure(&wdtc);
#else
esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false);
#endif
#endif
if (err != ESP_OK) {
@ -67,7 +56,10 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
return OTA_RESPONSE_OK;
}
void IDFOTABackend::set_update_md5(const char *expected_md5) { memcpy(this->expected_bin_md5_, expected_md5, 32); }
void IDFOTABackend::set_update_md5(const char *expected_md5) {
memcpy(this->expected_bin_md5_, expected_md5, 32);
this->md5_set_ = true;
}
OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) {
esp_err_t err = esp_ota_write(this->update_handle_, data, len);
@ -84,10 +76,12 @@ OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) {
}
OTAResponseTypes IDFOTABackend::end() {
this->md5_.calculate();
if (!this->md5_.equals_hex(this->expected_bin_md5_)) {
this->abort();
return OTA_RESPONSE_ERROR_MD5_MISMATCH;
if (this->md5_set_) {
this->md5_.calculate();
if (!this->md5_.equals_hex(this->expected_bin_md5_)) {
this->abort();
return OTA_RESPONSE_ERROR_MD5_MISMATCH;
}
}
esp_err_t err = esp_ota_end(this->update_handle_);
this->update_handle_ = 0;
@ -111,6 +105,6 @@ void IDFOTABackend::abort() {
this->update_handle_ = 0;
}
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif

View File

@ -8,7 +8,7 @@
#include <esp_ota_ops.h>
namespace esphome {
namespace ota {
namespace ota_base {
class IDFOTABackend : public OTABackend {
public:
@ -24,8 +24,9 @@ class IDFOTABackend : public OTABackend {
const esp_partition_t *partition_;
md5::MD5Digest md5_{};
char expected_bin_md5_[32];
bool md5_set_{false};
};
} // namespace ota
} // namespace ota_base
} // namespace esphome
#endif

View File

@ -2,9 +2,7 @@
#ifdef USE_ESP32
#include "psram.h"
#include <esp_idf_version.h>
#if defined(USE_ESP_IDF) && ESP_IDF_VERSION_MAJOR >= 5
#include <esp_psram.h>
#endif // USE_ESP_IDF
#include "esphome/core/log.h"
@ -16,7 +14,6 @@ static const char *const TAG = "psram";
void PsramComponent::dump_config() {
ESP_LOGCONFIG(TAG, "PSRAM:");
#if defined(USE_ESP_IDF) && ESP_IDF_VERSION_MAJOR >= 5
bool available = esp_psram_is_initialized();
ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available));
@ -26,23 +23,6 @@ void PsramComponent::dump_config() {
ESP_LOGCONFIG(TAG, " ECC enabled: YES");
#endif
}
#else
// Technically this can be false if the PSRAM is full, but heap_caps_get_total_size() isn't always available, and it's
// very unlikely for the PSRAM to be full.
bool available = heap_caps_get_free_size(MALLOC_CAP_SPIRAM) > 0;
ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available));
if (available) {
const size_t psram_total_size_bytes = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
const float psram_total_size_kb = psram_total_size_bytes / 1024.0f;
if (abs(std::round(psram_total_size_kb) - psram_total_size_kb) < 0.05f) {
ESP_LOGCONFIG(TAG, " Size: %.0f KB", psram_total_size_kb);
} else {
ESP_LOGCONFIG(TAG, " Size: %zu bytes", psram_total_size_bytes);
}
}
#endif // USE_ESP_IDF
}
} // namespace psram

View File

@ -146,11 +146,7 @@ void PVVXDisplay::sync_time_() {
}
time.recalc_timestamp_utc(true); // calculate timestamp of local time
uint8_t blk[5] = {};
#if ESP_IDF_VERSION_MAJOR >= 5
ESP_LOGD(TAG, "[%s] Sync time with timestamp %" PRIu64 ".", this->parent_->address_str().c_str(), time.timestamp);
#else
ESP_LOGD(TAG, "[%s] Sync time with timestamp %lu.", this->parent_->address_str().c_str(), time.timestamp);
#endif
blk[0] = 0x23;
blk[1] = time.timestamp & 0xff;
blk[2] = (time.timestamp >> 8) & 0xff;

View File

@ -10,10 +10,8 @@ void RpiDpiRgb::setup() {
this->reset_display_();
esp_lcd_rgb_panel_config_t config{};
config.flags.fb_in_psram = 1;
#if ESP_IDF_VERSION_MAJOR >= 5
config.bounce_buffer_size_px = this->width_ * 10;
config.num_fbs = 1;
#endif // ESP_IDF_VERSION_MAJOR
config.timings.h_res = this->width_;
config.timings.v_res = this->height_;
config.timings.hsync_pulse_width = this->hsync_pulse_width_;
@ -47,10 +45,8 @@ void RpiDpiRgb::setup() {
ESP_LOGCONFIG(TAG, "RPI_DPI_RGB setup complete");
}
void RpiDpiRgb::loop() {
#if ESP_IDF_VERSION_MAJOR >= 5
if (this->handle_ != nullptr)
esp_lcd_rgb_panel_restart(this->handle_);
#endif // ESP_IDF_VERSION_MAJOR
}
void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,

View File

@ -6,7 +6,7 @@
#include "esphome/components/audio/audio.h"
#ifdef USE_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota_base/ota_backend.h"
#endif
namespace esphome {
@ -67,16 +67,16 @@ void SpeakerMediaPlayer::setup() {
}
#ifdef USE_OTA
ota::get_global_ota_callback()->add_on_state_callback(
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) {
ota_base::get_global_ota_callback()->add_on_state_callback(
[this](ota_base::OTAState state, float progress, uint8_t error, ota_base::OTAComponent *comp) {
if (state == ota_base::OTA_STARTED) {
if (this->media_pipeline_ != nullptr) {
this->media_pipeline_->suspend_tasks();
}
if (this->announcement_pipeline_ != nullptr) {
this->announcement_pipeline_->suspend_tasks();
}
} else if (state == ota::OTA_ERROR) {
} else if (state == ota_base::OTA_ERROR) {
if (this->media_pipeline_ != nullptr) {
this->media_pipeline_->resume_tasks();
}

View File

@ -12,10 +12,8 @@ void ST7701S::setup() {
esp_lcd_rgb_panel_config_t config{};
config.flags.fb_in_psram = 1;
#if ESP_IDF_VERSION_MAJOR >= 5
config.bounce_buffer_size_px = this->width_ * 10;
config.num_fbs = 1;
#endif // ESP_IDF_VERSION_MAJOR
config.timings.h_res = this->width_;
config.timings.v_res = this->height_;
config.timings.hsync_pulse_width = this->hsync_pulse_width_;
@ -48,10 +46,8 @@ void ST7701S::setup() {
}
void ST7701S::loop() {
#if ESP_IDF_VERSION_MAJOR >= 5
if (this->handle_ != nullptr)
esp_lcd_rgb_panel_restart(this->handle_);
#endif // ESP_IDF_VERSION_MAJOR
}
void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,

View File

@ -48,11 +48,7 @@ uart_config_t IDFUARTComponent::get_config_() {
uart_config.parity = parity;
uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
uart_config.source_clk = UART_SCLK_DEFAULT;
#else
uart_config.source_clk = UART_SCLK_APB;
#endif
uart_config.rx_flow_ctrl_thresh = 122;
return uart_config;

View File

@ -38,16 +38,12 @@ WatchdogManager::~WatchdogManager() {
void WatchdogManager::set_timeout_(uint32_t timeout_ms) {
ESP_LOGV(TAG, "Adjusting WDT to %" PRIu32 "ms", timeout_ms);
#ifdef USE_ESP32
#if ESP_IDF_VERSION_MAJOR >= 5
esp_task_wdt_config_t wdt_config = {
.timeout_ms = timeout_ms,
.idle_core_mask = (1 << SOC_CPU_CORES_NUM) - 1,
.trigger_panic = true,
};
esp_task_wdt_reconfigure(&wdt_config);
#else
esp_task_wdt_init(timeout_ms / 1000, true);
#endif // ESP_IDF_VERSION_MAJOR
#endif // USE_ESP32
#ifdef USE_RP2040

View File

@ -34,7 +34,7 @@ from esphome.const import (
from esphome.core import CORE, coroutine_with_priority
import esphome.final_validate as fv
AUTO_LOAD = ["json", "web_server_base"]
AUTO_LOAD = ["json", "web_server_base", "ota_base"]
CONF_SORTING_GROUP_ID = "sorting_group_id"
CONF_SORTING_GROUPS = "sorting_groups"
@ -274,7 +274,7 @@ async def to_code(config):
cg.add(var.set_allow_ota(config[CONF_OTA]))
if config[CONF_OTA]:
# Define USE_WEBSERVER_OTA based only on web_server OTA config
# This allows web server OTA to work without loading the OTA component
# Web server OTA now uses ota_base backend for consistency
cg.add_define("USE_WEBSERVER_OTA")
cg.add(var.set_expose_log(config[CONF_LOG]))
if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]:

View File

@ -4,19 +4,16 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ARDUINO
#include <StreamString.h>
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#include <Update.h>
#endif
#ifdef USE_ESP8266
#include <Updater.h>
#endif
#ifdef USE_WEBSERVER_OTA
#include "esphome/components/ota_base/ota_backend.h"
#endif
#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
#include <esp_ota_ops.h>
#include <esp_task_wdt.h>
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
#include <Updater.h>
#elif defined(USE_ESP32) || defined(USE_LIBRETINY)
#include <Update.h>
#endif
#endif
namespace esphome {
@ -24,104 +21,6 @@ namespace web_server_base {
static const char *const TAG = "web_server_base";
#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
// Minimal OTA backend implementation for web server
// This allows OTA updates via web server without requiring the OTA component
// TODO: In the future, this should be refactored into a common ota_base component
// that both web_server and ota components can depend on, avoiding code duplication
// while keeping the components independent. This would allow both ESP-IDF and Arduino
// implementations to share the base OTA functionality without requiring the full OTA component.
// The IDFWebServerOTABackend class is intentionally designed with the same interface
// as OTABackend to make it easy to swap to using OTABackend when the ota component
// is split into ota and ota_base in the future.
class IDFWebServerOTABackend {
public:
bool begin() {
this->partition_ = esp_ota_get_next_update_partition(nullptr);
if (this->partition_ == nullptr) {
ESP_LOGE(TAG, "No OTA partition available");
return false;
}
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// The following function takes longer than the default timeout of WDT due to flash erase
#if ESP_IDF_VERSION_MAJOR >= 5
esp_task_wdt_config_t wdtc;
wdtc.idle_core_mask = 0;
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
wdtc.idle_core_mask |= (1 << 0);
#endif
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
wdtc.idle_core_mask |= (1 << 1);
#endif
wdtc.timeout_ms = 15000;
wdtc.trigger_panic = false;
esp_task_wdt_reconfigure(&wdtc);
#else
esp_task_wdt_init(15, false);
#endif
#endif
esp_err_t err = esp_ota_begin(this->partition_, 0, &this->update_handle_);
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// Set the WDT back to the configured timeout
#if ESP_IDF_VERSION_MAJOR >= 5
wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000;
esp_task_wdt_reconfigure(&wdtc);
#else
esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false);
#endif
#endif
if (err != ESP_OK) {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(err));
return false;
}
return true;
}
bool write(uint8_t *data, size_t len) {
esp_err_t err = esp_ota_write(this->update_handle_, data, len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_write failed: %s", esp_err_to_name(err));
return false;
}
return true;
}
bool end() {
esp_err_t err = esp_ota_end(this->update_handle_);
this->update_handle_ = 0;
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(err));
return false;
}
err = esp_ota_set_boot_partition(this->partition_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(err));
return false;
}
return true;
}
void abort() {
if (this->update_handle_ != 0) {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
}
}
private:
esp_ota_handle_t update_handle_{0};
const esp_partition_t *partition_{nullptr};
};
#endif
void WebServerBase::add_handler(AsyncWebHandler *handler) {
// remove all handlers
@ -138,12 +37,21 @@ void WebServerBase::add_handler(AsyncWebHandler *handler) {
void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {
const uint32_t now = millis();
if (now - this->last_ota_progress_ > 1000) {
float percentage = 0.0f;
if (request->contentLength() != 0) {
float percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
// Note: Using contentLength() for progress calculation is technically wrong as it includes
// multipart headers/boundaries, but it's only off by a small amount and we don't have
// access to the actual firmware size until the upload is complete. This is intentional
// as it still gives the user a reasonable progress indication.
percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
} else {
ESP_LOGD(TAG, "OTA in progress: %u bytes read", this->ota_read_length_);
}
#ifdef USE_OTA_STATE_CALLBACK
// Report progress - use call_deferred since we're in web server task
this->parent_->state_callback_.call_deferred(ota_base::OTA_IN_PROGRESS, percentage, 0);
#endif
this->last_ota_progress_ = now;
}
}
@ -159,87 +67,72 @@ void OTARequestHandler::schedule_ota_reboot_() {
void OTARequestHandler::ota_init_(const char *filename) {
ESP_LOGI(TAG, "OTA Update Start: %s", filename);
this->ota_read_length_ = 0;
}
void report_ota_error() {
#ifdef USE_ARDUINO
StreamString ss;
Update.printError(ss);
ESP_LOGW(TAG, "OTA Update failed! Error: %s", ss.c_str());
#endif
this->ota_success_ = false;
}
void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index,
uint8_t *data, size_t len, bool final) {
#ifdef USE_ARDUINO
bool success;
if (index == 0) {
ota_base::OTAResponseTypes error_code = ota_base::OTA_RESPONSE_OK;
if (index == 0 && !this->ota_backend_) {
// Initialize OTA on first call
this->ota_init_(filename.c_str());
#ifdef USE_OTA_STATE_CALLBACK
// Notify OTA started - use call_deferred since we're in web server task
this->parent_->state_callback_.call_deferred(ota_base::OTA_STARTED, 0.0f, 0);
#endif
// Platform-specific pre-initialization
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
Update.runAsync(true);
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
#endif
#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_LIBRETINY)
if (Update.isRunning()) {
Update.abort();
}
success = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH);
#endif
if (!success) {
report_ota_error();
return;
}
} else if (Update.hasError()) {
// don't spam logs with errors if something failed at start
return;
}
success = Update.write(data, len) == len;
if (!success) {
report_ota_error();
return;
}
this->ota_read_length_ += len;
this->report_ota_progress_(request);
if (final) {
if (Update.end(true)) {
this->schedule_ota_reboot_();
} else {
report_ota_error();
}
}
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
// ESP-IDF implementation
if (index == 0 && !this->ota_backend_) {
// Initialize OTA on first call
this->ota_init_(filename.c_str());
this->ota_success_ = false;
auto *backend = new IDFWebServerOTABackend();
if (!backend->begin()) {
ESP_LOGE(TAG, "OTA begin failed");
delete backend;
this->ota_backend_ = ota_base::make_ota_backend();
if (!this->ota_backend_) {
ESP_LOGE(TAG, "Failed to create OTA backend");
#ifdef USE_OTA_STATE_CALLBACK
this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f,
static_cast<uint8_t>(ota_base::OTA_RESPONSE_ERROR_UNKNOWN));
#endif
return;
}
// Web server OTA uses multipart uploads where the actual firmware size
// is unknown (contentLength includes multipart overhead)
// Pass 0 to indicate unknown size
error_code = this->ota_backend_->begin(0);
if (error_code != ota_base::OTA_RESPONSE_OK) {
ESP_LOGE(TAG, "OTA begin failed: %d", error_code);
this->ota_backend_.reset();
#ifdef USE_OTA_STATE_CALLBACK
this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
#endif
return;
}
this->ota_backend_ = backend;
}
auto *backend = static_cast<IDFWebServerOTABackend *>(this->ota_backend_);
if (!backend) {
if (!this->ota_backend_) {
return;
}
// Process data
if (len > 0) {
if (!backend->write(data, len)) {
ESP_LOGE(TAG, "OTA write failed");
backend->abort();
delete backend;
this->ota_backend_ = nullptr;
error_code = this->ota_backend_->write(data, len);
if (error_code != ota_base::OTA_RESPONSE_OK) {
ESP_LOGE(TAG, "OTA write failed: %d", error_code);
this->ota_backend_->abort();
this->ota_backend_.reset();
#ifdef USE_OTA_STATE_CALLBACK
this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
#endif
return;
}
this->ota_read_length_ += len;
@ -248,40 +141,45 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin
// Finalize
if (final) {
this->ota_success_ = backend->end();
if (this->ota_success_) {
ESP_LOGD(TAG, "OTA final chunk: index=%u, len=%u, total_read=%u, contentLength=%u", index, len,
this->ota_read_length_, request->contentLength());
// For Arduino framework, the Update library tracks expected size from firmware header
// If we haven't received enough data, calling end() will fail
// This can happen if the upload is interrupted or the client disconnects
error_code = this->ota_backend_->end();
if (error_code == ota_base::OTA_RESPONSE_OK) {
this->ota_success_ = true;
#ifdef USE_OTA_STATE_CALLBACK
// Report completion before reboot - use call_deferred since we're in web server task
this->parent_->state_callback_.call_deferred(ota_base::OTA_COMPLETED, 100.0f, 0);
#endif
this->schedule_ota_reboot_();
} else {
ESP_LOGE(TAG, "OTA end failed");
ESP_LOGE(TAG, "OTA end failed: %d", error_code);
#ifdef USE_OTA_STATE_CALLBACK
this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
#endif
}
delete backend;
this->ota_backend_ = nullptr;
this->ota_backend_.reset();
}
#endif // USE_ESP_IDF
}
void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response;
#ifdef USE_ARDUINO
if (!Update.hasError()) {
response = request->beginResponse(200, "text/plain", "Update Successful!");
} else {
StreamString ss;
ss.print("Update Failed: ");
Update.printError(ss);
response = request->beginResponse(200, "text/plain", ss);
}
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
// Send response based on the OTA result
response = request->beginResponse(200, "text/plain", this->ota_success_ ? "Update Successful!" : "Update Failed!");
#endif // USE_ESP_IDF
// Use the ota_success_ flag to determine the actual result
const char *msg = this->ota_success_ ? "Update Successful!" : "Update Failed!";
response = request->beginResponse(200, "text/plain", msg);
response->addHeader("Connection", "close");
request->send(response);
}
void WebServerBase::add_ota_handler() {
this->add_handler(new OTARequestHandler(this)); // NOLINT
#ifdef USE_OTA_STATE_CALLBACK
// Register with global OTA callback system
ota_base::register_ota_platform(this);
#endif
}
#endif

View File

@ -14,6 +14,10 @@
#include "esphome/components/web_server_idf/web_server_idf.h"
#endif
#ifdef USE_WEBSERVER_OTA
#include "esphome/components/ota_base/ota_backend.h"
#endif
namespace esphome {
namespace web_server_base {
@ -79,7 +83,11 @@ class AuthMiddlewareHandler : public MiddlewareHandler {
} // namespace internal
#ifdef USE_WEBSERVER_OTA
class WebServerBase : public ota_base::OTAComponent {
#else
class WebServerBase : public Component {
#endif
public:
void init() {
if (this->initialized_) {
@ -151,12 +159,10 @@ class OTARequestHandler : public AsyncWebHandler {
uint32_t last_ota_progress_{0};
uint32_t ota_read_length_{0};
WebServerBase *parent_;
bool ota_success_{false};
private:
#ifdef USE_ESP_IDF
void *ota_backend_{nullptr};
bool ota_success_{false};
#endif
std::unique_ptr<ota_base::OTABackend> ota_backend_{nullptr};
};
#endif // USE_WEBSERVER_OTA

View File

@ -6,14 +6,7 @@ import esphome.codegen as cg
from esphome.components import time
from esphome.components.esp32 import CORE, add_idf_sdkconfig_option
import esphome.config_validation as cv
from esphome.const import (
CONF_ADDRESS,
CONF_ID,
CONF_REBOOT_TIMEOUT,
CONF_TIME_ID,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
)
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_REBOOT_TIMEOUT, CONF_TIME_ID
from esphome.core import TimePeriod
CONF_NETMASK = "netmask"
@ -125,9 +118,7 @@ async def to_code(config):
# Workaround for crash on IDF 5+
# See https://github.com/trombik/esp_wireguard/issues/33#issuecomment-1568503651
if CORE.using_esp_idf and CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(
5, 0, 0
):
if CORE.using_esp_idf:
add_idf_sdkconfig_option("CONFIG_LWIP_PPP_SUPPORT", True)
# This flag is added here because the esp_wireguard library statically

View File

@ -0,0 +1,10 @@
# Test that ota_base compiles correctly as a dependency
# This component is typically auto-loaded by other components
wifi:
ssid: MySSID
password: password1
ota:
- platform: esphome
password: "test1234"

View File

@ -0,0 +1 @@
<<: !include common.yaml