diff --git a/CODEOWNERS b/CODEOWNERS index 68c8684024..bcf2b7aca5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 9ffb6cf856..28dfd2262c 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -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 }; diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 32323b7504..b4c7a4e05b 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -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." diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index 562bcba3c2..f3bdfea2a0 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -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 diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 2242d709a4..547cf84ed1 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -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") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index d950ccb5f1..8e785da4be 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -18,7 +18,7 @@ #include #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(); diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 901657ec82..bf5c438f9b 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -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 ( diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 4cc82b9094..5f8d1baf49 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -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(buf); size_t ota_size; uint8_t ota_features; - std::unique_ptr backend; + std::unique_ptr 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(error_code)); + this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, static_cast(error_code)); #endif } diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index e0d09ff37e..08266122d6 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -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; } diff --git a/esphome/components/ethernet/esp_eth_phy_jl1101.c b/esphome/components/ethernet/esp_eth_phy_jl1101.c index de2a6f4f35..4f31e0a9fd 100644 --- a/esphome/components/ethernet/esp_eth_phy_jl1101.c +++ b/esphome/components/ethernet/esp_eth_phy_jl1101.c @@ -19,11 +19,7 @@ #include #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; diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 19a11c6945..f8c2f3a72e 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -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 diff --git a/esphome/components/http_request/ota/__init__.py b/esphome/components/http_request/ota/__init__.py index a3f6d5840c..d3a54c699b 100644 --- a/esphome/components/http_request/ota/__init__.py +++ b/esphome/components/http_request/ota/__init__.py @@ -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 ) diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index 4d9e868c74..23caa6fbd3 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -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 backend, +void OtaHttpRequestComponent::cleanup_(std::unique_ptr backend, const std::shared_ptr &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) { diff --git a/esphome/components/http_request/ota/ota_http_request.h b/esphome/components/http_request/ota/ota_http_request.h index 6a86b4ab43..138731fc5c 100644 --- a/esphome/components/http_request/ota/ota_http_request.h +++ b/esphome/components/http_request/ota/ota_http_request.h @@ -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 { +class OtaHttpRequestComponent : public ota_base::OTAComponent, public Parented { public: void setup() override; void dump_config() override; @@ -40,7 +40,7 @@ class OtaHttpRequestComponent : public ota::OTAComponent, public Parented backend, const std::shared_ptr &container); + void cleanup_(std::unique_ptr backend, const std::shared_ptr &container); uint8_t do_ota_(); std::string get_url_with_auth_(const std::string &url); bool http_get_md5_(); diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 6bc88ae49a..9f14d53eb9 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -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(); diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index ef95fd0b41..9a2aa0362f 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -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 diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp index 7ff21bba57..7f233516e6 100644 --- a/esphome/components/i2s_audio/i2s_audio.cpp +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -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; diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index c3a0f2eacc..ae4927828b 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -59,11 +59,7 @@ optional 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; } diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index f503927d8e..85844647f2 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -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 } diff --git a/esphome/components/internal_temperature/sensor.py b/esphome/components/internal_temperature/sensor.py index 9bfa3739c8..93b98a30f4 100644 --- a/esphome/components/internal_temperature/sensor.py +++ b/esphome/components/internal_temperature/sensor.py @@ -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 ." - ) - 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 ." - ) - 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, ) diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index b5ac84a665..41445fa3b4 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -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 diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 2f81068e8a..ed230d43aa 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -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") diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 201d956a37..583a4b2fe2 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -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_(); } }); diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 4648e66e1d..a096408aa5 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -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); diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 49482316ee..b2751470b2 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -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; diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 627c55e910..2ac09607be 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -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() diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h index 7e1a60f3ce..5c71859d43 100644 --- a/esphome/components/ota/automation.h +++ b/esphome/components/ota/automation.h @@ -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 { 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 { 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 { 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); } }); diff --git a/esphome/components/ota_base/__init__.py b/esphome/components/ota_base/__init__.py new file mode 100644 index 0000000000..2203785953 --- /dev/null +++ b/esphome/components/ota_base/__init__.py @@ -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) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota_base/ota_backend.cpp similarity index 80% rename from esphome/components/ota/ota_backend.cpp rename to esphome/components/ota_base/ota_backend.cpp index 30de4ec4b3..7cbc795866 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota_base/ota_backend.cpp @@ -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 diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota_base/ota_backend.h similarity index 66% rename from esphome/components/ota/ota_backend.h rename to esphome/components/ota_base/ota_backend.h index bc8ab46643..27637a9af2 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -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 make_ota_backend(); + class OTAComponent : public Component { #ifdef USE_OTA_STATE_CALLBACK public: - void add_on_state_callback(std::function &&callback) { + void add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } protected: - CallbackManager 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 { + 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 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 diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp similarity index 65% rename from esphome/components/ota/ota_backend_arduino_esp32.cpp rename to esphome/components/ota_base/ota_backend_arduino_esp32.cpp index 15dfc98a6c..f239544cfe 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp @@ -8,13 +8,18 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_esp32"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } 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 diff --git a/esphome/components/ota/ota_backend_arduino_esp32.h b/esphome/components/ota_base/ota_backend_arduino_esp32.h similarity index 87% rename from esphome/components/ota/ota_backend_arduino_esp32.h rename to esphome/components/ota_base/ota_backend_arduino_esp32.h index ac7fe9f14f..e3966eb2f6 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.h +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.h @@ -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 diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp similarity index 68% rename from esphome/components/ota/ota_backend_arduino_esp8266.cpp rename to esphome/components/ota_base/ota_backend_arduino_esp8266.cpp index 42edbf5d2b..a9d48b59df 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp @@ -10,13 +10,18 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_esp8266"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } 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 diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota_base/ota_backend_arduino_esp8266.h similarity index 89% rename from esphome/components/ota/ota_backend_arduino_esp8266.h rename to esphome/components/ota_base/ota_backend_arduino_esp8266.h index 7f44d7c965..f399013121 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.h @@ -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 diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.cpp b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp similarity index 68% rename from esphome/components/ota/ota_backend_arduino_libretiny.cpp rename to esphome/components/ota_base/ota_backend_arduino_libretiny.cpp index 6b2cf80684..2596e3c2a3 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp @@ -8,13 +8,18 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_libretiny"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } 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 diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.h b/esphome/components/ota_base/ota_backend_arduino_libretiny.h similarity index 86% rename from esphome/components/ota/ota_backend_arduino_libretiny.h rename to esphome/components/ota_base/ota_backend_arduino_libretiny.h index 11deb6e2f2..33eebeb95a 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.h +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.h @@ -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 diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.cpp b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp similarity index 63% rename from esphome/components/ota/ota_backend_arduino_rp2040.cpp rename to esphome/components/ota_base/ota_backend_arduino_rp2040.cpp index ffeab2e93f..160c529231 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp @@ -10,13 +10,24 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_rp2040"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } 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 diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.h b/esphome/components/ota_base/ota_backend_arduino_rp2040.h similarity index 87% rename from esphome/components/ota/ota_backend_arduino_rp2040.h rename to esphome/components/ota_base/ota_backend_arduino_rp2040.h index b189964ab3..6d622d4a5a 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.h +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.h @@ -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 diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota_base/ota_backend_esp_idf.cpp similarity index 81% rename from esphome/components/ota/ota_backend_esp_idf.cpp rename to esphome/components/ota_base/ota_backend_esp_idf.cpp index 6f45fb75e4..e5cc1efaf6 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota_base/ota_backend_esp_idf.cpp @@ -6,15 +6,12 @@ #include #include - -#if ESP_IDF_VERSION_MAJOR >= 5 #include -#endif namespace esphome { -namespace ota { +namespace ota_base { -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } 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 diff --git a/esphome/components/ota/ota_backend_esp_idf.h b/esphome/components/ota_base/ota_backend_esp_idf.h similarity index 90% rename from esphome/components/ota/ota_backend_esp_idf.h rename to esphome/components/ota_base/ota_backend_esp_idf.h index ed66d9b970..3c760df1c8 100644 --- a/esphome/components/ota/ota_backend_esp_idf.h +++ b/esphome/components/ota_base/ota_backend_esp_idf.h @@ -8,7 +8,7 @@ #include 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 diff --git a/esphome/components/psram/psram.cpp b/esphome/components/psram/psram.cpp index 162543545e..6c110a577d 100644 --- a/esphome/components/psram/psram.cpp +++ b/esphome/components/psram/psram.cpp @@ -2,9 +2,7 @@ #ifdef USE_ESP32 #include "psram.h" #include -#if defined(USE_ESP_IDF) && ESP_IDF_VERSION_MAJOR >= 5 #include -#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 diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp index 74f63a9640..4b6c11b332 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp @@ -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; diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index 666bac354d..91eb947a3e 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -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, diff --git a/esphome/components/speaker/media_player/speaker_media_player.cpp b/esphome/components/speaker/media_player/speaker_media_player.cpp index 2c30f17c78..c6f6c91760 100644 --- a/esphome/components/speaker/media_player/speaker_media_player.cpp +++ b/esphome/components/speaker/media_player/speaker_media_player.cpp @@ -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(); } diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 6261f33b77..46509a7f9f 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -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, diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 8fae63a603..63b2579c3f 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -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; diff --git a/esphome/components/watchdog/watchdog.cpp b/esphome/components/watchdog/watchdog.cpp index f6f2992a11..2ce46756e4 100644 --- a/esphome/components/watchdog/watchdog.cpp +++ b/esphome/components/watchdog/watchdog.cpp @@ -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 diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index ca145c732b..8c77866540 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -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]: diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 9ad88e09f4..a683ee85eb 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -4,19 +4,16 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#ifdef USE_ARDUINO -#include -#if defined(USE_ESP32) || defined(USE_LIBRETINY) -#include -#endif -#ifdef USE_ESP8266 -#include -#endif +#ifdef USE_WEBSERVER_OTA +#include "esphome/components/ota_base/ota_backend.h" #endif -#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA) -#include -#include +#ifdef USE_ARDUINO +#ifdef USE_ESP8266 +#include +#elif defined(USE_ESP32) || defined(USE_LIBRETINY) +#include +#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(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(error_code)); +#endif return; } - this->ota_backend_ = backend; } - auto *backend = static_cast(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(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(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 diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 09a41956c9..99087ddfa8 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -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_backend_{nullptr}; }; #endif // USE_WEBSERVER_OTA diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index fc0e4e0538..8eff8e7b2a 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -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 diff --git a/tests/components/ota_base/common.yaml b/tests/components/ota_base/common.yaml new file mode 100644 index 0000000000..9b680b7c18 --- /dev/null +++ b/tests/components/ota_base/common.yaml @@ -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" diff --git a/tests/components/ota_base/test.esp32-idf.yaml b/tests/components/ota_base/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/ota_base/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml