From 63464a13c31127885fe75b554f201a929dcbbaa2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 29 Dec 2025 16:57:22 -1000 Subject: [PATCH 1/3] [core] Fix incremental build failures when adding components on ESP32-Arduino (#12745) --- esphome/writer.py | 9 ++-- tests/unit_tests/test_writer.py | 88 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/esphome/writer.py b/esphome/writer.py index 9ae40e417a..cb9c921693 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -103,14 +103,11 @@ def storage_should_clean(old: StorageJSON | None, new: StorageJSON) -> bool: def storage_should_update_cmake_cache(old: StorageJSON, new: StorageJSON) -> bool: - if ( + # ESP32 uses CMake for both Arduino and ESP-IDF frameworks + return ( old.loaded_integrations != new.loaded_integrations or old.loaded_platforms != new.loaded_platforms - ) and new.core_platform == PLATFORM_ESP32: - from esphome.components.esp32 import FRAMEWORK_ESP_IDF - - return new.framework == FRAMEWORK_ESP_IDF - return False + ) and new.core_platform == PLATFORM_ESP32 def update_storage_json() -> None: diff --git a/tests/unit_tests/test_writer.py b/tests/unit_tests/test_writer.py index f354d71bb7..ac05e0d31b 100644 --- a/tests/unit_tests/test_writer.py +++ b/tests/unit_tests/test_writer.py @@ -13,6 +13,13 @@ from unittest.mock import MagicMock, patch import pytest +from esphome.const import ( + PLATFORM_BK72XX, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_RP2040, + PLATFORM_RTL87XX, +) from esphome.core import EsphomeError from esphome.storage_json import StorageJSON from esphome.writer import ( @@ -28,6 +35,7 @@ from esphome.writer import ( generate_build_info_data_h, get_build_info, storage_should_clean, + storage_should_update_cmake_cache, update_storage_json, write_cpp, write_gitignore, @@ -171,6 +179,86 @@ def test_storage_edge_case_from_empty_integrations( assert storage_should_clean(old, new) is False +# Tests for storage_should_update_cmake_cache + + +@pytest.mark.parametrize("framework", ["arduino", "esp-idf"]) +def test_storage_should_update_cmake_cache_when_integration_added_esp32( + create_storage: Callable[..., StorageJSON], + framework: str, +) -> None: + """Test cmake cache update triggered when integration added on ESP32.""" + old = create_storage( + loaded_integrations=["api", "wifi"], + core_platform=PLATFORM_ESP32, + framework=framework, + ) + new = create_storage( + loaded_integrations=["api", "wifi", "restart"], + core_platform=PLATFORM_ESP32, + framework=framework, + ) + assert storage_should_update_cmake_cache(old, new) is True + + +def test_storage_should_update_cmake_cache_when_platform_changed_esp32( + create_storage: Callable[..., StorageJSON], +) -> None: + """Test cmake cache update triggered when platforms change on ESP32.""" + old = create_storage( + loaded_integrations=["api", "wifi"], + loaded_platforms={"sensor"}, + core_platform=PLATFORM_ESP32, + framework="arduino", + ) + new = create_storage( + loaded_integrations=["api", "wifi"], + loaded_platforms={"sensor", "binary_sensor"}, + core_platform=PLATFORM_ESP32, + framework="arduino", + ) + assert storage_should_update_cmake_cache(old, new) is True + + +def test_storage_should_not_update_cmake_cache_when_nothing_changes( + create_storage: Callable[..., StorageJSON], +) -> None: + """Test cmake cache not updated when nothing changes.""" + old = create_storage( + loaded_integrations=["api", "wifi"], + core_platform=PLATFORM_ESP32, + framework="arduino", + ) + new = create_storage( + loaded_integrations=["api", "wifi"], + core_platform=PLATFORM_ESP32, + framework="arduino", + ) + assert storage_should_update_cmake_cache(old, new) is False + + +@pytest.mark.parametrize( + "core_platform", + [PLATFORM_ESP8266, PLATFORM_RP2040, PLATFORM_BK72XX, PLATFORM_RTL87XX], +) +def test_storage_should_not_update_cmake_cache_for_non_esp32( + create_storage: Callable[..., StorageJSON], + core_platform: str, +) -> None: + """Test cmake cache not updated for non-ESP32 platforms.""" + old = create_storage( + loaded_integrations=["api", "wifi"], + core_platform=core_platform, + framework="arduino", + ) + new = create_storage( + loaded_integrations=["api", "wifi", "restart"], + core_platform=core_platform, + framework="arduino", + ) + assert storage_should_update_cmake_cache(old, new) is False + + @patch("esphome.writer.clean_build") @patch("esphome.writer.StorageJSON") @patch("esphome.writer.storage_path") From d86c05bfe65b7d2746356df435dcc998c60f2907 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 29 Dec 2025 17:23:41 -1000 Subject: [PATCH 2/3] [esp32] Breaking Change: Change default framework to ESP-IDF (#12746) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/esp32/__init__.py | 52 +++++++++++----------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index d307ae75c8..929ced6e3b 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -729,12 +729,14 @@ FRAMEWORK_SCHEMA = cv.Schema( ) +# Remove this class in 2026.7.0 class _FrameworkMigrationWarning: shown = False def _show_framework_migration_message(name: str, variant: str) -> None: - """Show a friendly message about framework migration when defaulting to Arduino.""" + """Show a message about the framework default change and how to switch back to Arduino.""" + # Remove this function in 2026.7.0 if _FrameworkMigrationWarning.shown: return _FrameworkMigrationWarning.shown = True @@ -744,41 +746,27 @@ def _show_framework_migration_message(name: str, variant: str) -> None: message = ( color( AnsiFore.BOLD_CYAN, - f"💡 IMPORTANT: {name} doesn't have a framework specified!", + f"💡 NOTICE: {name} does not have a framework specified.", ) + "\n\n" - + f"Currently, {variant} defaults to the Arduino framework.\n" - + color(AnsiFore.YELLOW, "This will change to ESP-IDF in ESPHome 2026.1.0.\n") + + f"Starting with ESPHome 2026.1.0, the default framework for {variant} is ESP-IDF.\n" + + "(We've been warning about this change since ESPHome 2025.8.0)\n" + "\n" - + "Note: Newer ESP32 variants (C6, H2, P4, etc.) already use ESP-IDF by default.\n" - + "\n" - + "Why change? ESP-IDF offers:\n" - + color(AnsiFore.GREEN, " ✨ Up to 40% smaller binaries\n") - + color(AnsiFore.GREEN, " 🚀 Better performance and optimization\n") + + "Why we made this change:\n" + + color(AnsiFore.GREEN, " ✨ Up to 40% smaller firmware binaries\n") + color(AnsiFore.GREEN, " ⚡ 2-3x faster compile times\n") - + color(AnsiFore.GREEN, " 📦 Custom-built firmware for your exact needs\n") - + color( - AnsiFore.GREEN, - " 🔧 Active development and testing by ESPHome developers\n", - ) + + color(AnsiFore.GREEN, " 🚀 Better performance and newer features\n") + + color(AnsiFore.GREEN, " 🔧 More actively maintained by ESPHome\n") + "\n" - + "Trade-offs:\n" - + color(AnsiFore.YELLOW, " 🔄 Some components need migration\n") + + "To continue using Arduino, add this to your YAML under 'esp32:':\n" + + color(AnsiFore.WHITE, " framework:\n") + + color(AnsiFore.WHITE, " type: arduino\n") + "\n" - + "What should I do?\n" - + color(AnsiFore.CYAN, " Option 1") - + ": Migrate to ESP-IDF (recommended)\n" - + " Add this to your YAML under 'esp32:':\n" - + color(AnsiFore.WHITE, " framework:\n") - + color(AnsiFore.WHITE, " type: esp-idf\n") + + "To silence this message with ESP-IDF, explicitly set:\n" + + color(AnsiFore.WHITE, " framework:\n") + + color(AnsiFore.WHITE, " type: esp-idf\n") + "\n" - + color(AnsiFore.CYAN, " Option 2") - + ": Keep using Arduino (still supported)\n" - + " Add this to your YAML under 'esp32:':\n" - + color(AnsiFore.WHITE, " framework:\n") - + color(AnsiFore.WHITE, " type: arduino\n") - + "\n" - + "Need help? Check out the migration guide:\n" + + "Migration guide: " + color( AnsiFore.BLUE, "https://esphome.io/guides/esp32_arduino_to_idf/", @@ -793,13 +781,13 @@ def _set_default_framework(config): config[CONF_FRAMEWORK] = FRAMEWORK_SCHEMA({}) if CONF_TYPE not in config[CONF_FRAMEWORK]: variant = config[CONF_VARIANT] + config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF + # Show migration message for variants that previously defaulted to Arduino + # Remove this message in 2026.7.0 if variant in ARDUINO_ALLOWED_VARIANTS: - config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO _show_framework_migration_message( config.get(CONF_NAME, "This device"), variant ) - else: - config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF return config From 4c16afeacb059bbff720caae2e4524ad053ecc8a Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 29 Dec 2025 22:25:26 -0500 Subject: [PATCH 3/3] [esp32] Add IDF framework source for Arduino builds (#12731) Co-authored-by: Claude Opus 4.5 Co-authored-by: J. Nick Koston --- esphome/components/esp32/__init__.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 929ced6e3b..d8397a87cc 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -375,6 +375,23 @@ ARDUINO_PLATFORM_VERSION_LOOKUP = { cv.Version(3, 1, 1): cv.Version(53, 3, 11), cv.Version(3, 1, 0): cv.Version(53, 3, 10), } +# Maps Arduino framework versions to a compatible ESP-IDF version +# These versions correspond to pioarduino/esp-idf releases +# See: https://github.com/pioarduino/esp-idf/releases +ARDUINO_IDF_VERSION_LOOKUP = { + cv.Version(3, 3, 5): cv.Version(5, 5, 2), + cv.Version(3, 3, 4): cv.Version(5, 5, 1), + cv.Version(3, 3, 3): cv.Version(5, 5, 1), + cv.Version(3, 3, 2): cv.Version(5, 5, 1), + cv.Version(3, 3, 1): cv.Version(5, 5, 1), + cv.Version(3, 3, 0): cv.Version(5, 5, 0), + cv.Version(3, 2, 1): cv.Version(5, 4, 2), + cv.Version(3, 2, 0): cv.Version(5, 4, 2), + cv.Version(3, 1, 3): cv.Version(5, 3, 2), + cv.Version(3, 1, 2): cv.Version(5, 3, 2), + cv.Version(3, 1, 1): cv.Version(5, 3, 1), + cv.Version(3, 1, 0): cv.Version(5, 3, 0), +} # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases @@ -981,6 +998,13 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True) + # Add IDF framework source for Arduino builds to ensure it uses the same version as + # the ESP-IDF framework + if (idf_ver := ARDUINO_IDF_VERSION_LOOKUP.get(framework_ver)) is not None: + cg.add_platformio_option( + "platform_packages", [_format_framework_espidf_version(idf_ver, None)] + ) + # ESP32-S2 Arduino: Disable USB Serial on boot to avoid TinyUSB dependency if get_esp32_variant() == VARIANT_ESP32S2: cg.add_build_unflag("-DARDUINO_USB_CDC_ON_BOOT=1")