From 629c891dfc57157d04354cdd25c779ddcccbd500 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 12:12:16 -0500 Subject: [PATCH 01/15] Filter unused files --- esphome/components/adc/__init__.py | 23 ++++++- esphome/components/debug/__init__.py | 14 +++++ esphome/components/deep_sleep/__init__.py | 10 +++ esphome/components/http_request/__init__.py | 15 +++++ esphome/components/i2c/__init__.py | 14 +++++ esphome/components/libretiny/__init__.py | 10 +++ esphome/components/logger/__init__.py | 18 ++++++ esphome/components/mdns/__init__.py | 14 +++++ esphome/components/mqtt/__init__.py | 9 +++ esphome/components/nextion/__init__.py | 13 ++++ esphome/components/ota/__init__.py | 14 +++++ .../components/remote_receiver/__init__.py | 15 +++++ .../components/remote_transmitter/__init__.py | 15 +++++ esphome/components/spi/__init__.py | 14 +++++ esphome/components/uart/__init__.py | 15 +++++ esphome/components/wifi/__init__.py | 13 ++++ esphome/const.py | 61 ++++++++++++++++--- esphome/core/config.py | 9 +++ esphome/dashboard/entries.py | 2 +- esphome/{dashboard => }/enum.py | 0 esphome/loader.py | 52 +++++++++++++--- 21 files changed, 333 insertions(+), 17 deletions(-) rename esphome/{dashboard => }/enum.py (100%) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 5f94c61a08..c3ababbb84 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -11,7 +11,13 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S3, ) import esphome.config_validation as cv -from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266 +from esphome.const import ( + CONF_ANALOG, + CONF_INPUT, + CONF_NUMBER, + PLATFORM_ESP8266, + PlatformFramework, +) from esphome.core import CORE CODEOWNERS = ["@esphome/core"] @@ -229,3 +235,18 @@ def validate_adc_pin(value): )(value) raise NotImplementedError + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "adc_sensor_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "adc_sensor_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index 1955b5d22c..b5cdef4f0e 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -7,6 +7,7 @@ from esphome.const import ( CONF_FREE, CONF_ID, CONF_LOOP_TIME, + PlatformFramework, ) CODEOWNERS = ["@OttoWinter"] @@ -44,3 +45,16 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "debug_esp32.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, + "debug_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "debug_host.cpp": {PlatformFramework.HOST_NATIVE}, + "debug_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "debug_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 63b359bd5b..096d2eaa38 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -27,6 +27,7 @@ from esphome.const import ( CONF_WAKEUP_PIN, PLATFORM_ESP32, PLATFORM_ESP8266, + PlatformFramework, ) WAKEUP_PINS = { @@ -313,3 +314,12 @@ async def deep_sleep_action_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) return var + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "deep_sleep_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "deep_sleep_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, +} diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 18373edb77..0eaecaac45 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -13,6 +13,7 @@ from esphome.const import ( CONF_URL, CONF_WATCHDOG_TIMEOUT, PLATFORM_HOST, + PlatformFramework, __version__, ) from esphome.core import CORE, Lambda @@ -319,3 +320,17 @@ async def http_request_action_to_code(config, action_id, template_arg, args): await automation.build_automation(trigger, [], conf) return var + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "http_request_host.cpp": {PlatformFramework.HOST_NATIVE}, + "http_request_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "http_request_idf.cpp": {PlatformFramework.ESP32_IDF}, +} diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 6adb9b71aa..db52479783 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -18,6 +18,7 @@ from esphome.const import ( PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, + PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv @@ -205,3 +206,16 @@ def final_validate_device_schema( {cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)}, extra=cv.ALLOW_EXTRA, ) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "i2c_bus_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "i2c_bus_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, +} diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 149e5d1179..e4f6f15576 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -20,6 +20,7 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PlatformFramework, __version__, ) from esphome.core import CORE @@ -340,3 +341,12 @@ async def component_to_code(config): cg.add_platformio_option("custom_fw_version", __version__) await cg.register_component(var, config) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "gpio_arduino.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 3d4907aa6e..4f65907210 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -42,6 +42,7 @@ from esphome.const import ( PLATFORM_LN882X, PLATFORM_RP2040, PLATFORM_RTL87XX, + PlatformFramework, ) from esphome.core import CORE, Lambda, coroutine_with_priority @@ -444,3 +445,20 @@ async def logger_set_level_to_code(config, action_id, template_arg, args): lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void) return cg.new_Pvariable(action_id, template_arg, lambda_) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "logger_esp32.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, + "logger_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, + "logger_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "logger_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "task_log_buffer.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, +} diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index ed230d43aa..43d214d77b 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_PROTOCOL, CONF_SERVICE, CONF_SERVICES, + PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority @@ -108,3 +109,16 @@ async def to_code(config): ) cg.add(var.add_extra_service(exp)) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "mdns_esp32.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, + "mdns_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "mdns_host.cpp": {PlatformFramework.HOST_NATIVE}, + "mdns_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "mdns_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index f0d5a95d43..101761be9d 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -54,6 +54,7 @@ from esphome.const import ( PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, + PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority @@ -596,3 +597,11 @@ async def mqtt_enable_to_code(config, action_id, template_arg, args): async def mqtt_disable_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) return cg.new_Pvariable(action_id, template_arg, paren) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "mqtt_backend_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, +} diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index fb75daf4ba..332c27409c 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -1,5 +1,6 @@ import esphome.codegen as cg from esphome.components import uart +from esphome.const import PlatformFramework nextion_ns = cg.esphome_ns.namespace("nextion") Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) @@ -8,3 +9,15 @@ nextion_ref = Nextion.operator("ref") CONF_NEXTION_ID = "nextion_id" CONF_PUBLISH_STATE = "publish_state" CONF_SEND_TO_NEXTION = "send_to_nextion" + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "nextion_upload_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "nextion_upload_idf.cpp": {PlatformFramework.ESP32_IDF}, +} diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 627c55e910..bd7a97536c 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -7,6 +7,7 @@ from esphome.const import ( CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID, + PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority @@ -120,3 +121,16 @@ async def ota_to_code(var, config): use_state_callback = True if use_state_callback: cg.add_define("USE_OTA_STATE_CALLBACK") + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "ota_backend_arduino_esp32.cpp": {PlatformFramework.ESP32_ARDUINO}, + "ota_backend_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "ota_backend_arduino_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "ota_backend_arduino_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "ota_backend_arduino_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 5de7d8c9c4..f395aea3c8 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CONF_TYPE, CONF_USE_DMA, CONF_VALUE, + PlatformFramework, ) from esphome.core import CORE, TimePeriod @@ -170,3 +171,17 @@ async def to_code(config): cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE])) cg.add(var.set_filter_us(config[CONF_FILTER])) cg.add(var.set_idle_us(config[CONF_IDLE])) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "remote_receiver_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "remote_receiver_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "remote_receiver_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index 713cee0186..1e935354f9 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -12,6 +12,7 @@ from esphome.const import ( CONF_PIN, CONF_RMT_SYMBOLS, CONF_USE_DMA, + PlatformFramework, ) from esphome.core import CORE @@ -95,3 +96,17 @@ async def to_code(config): await automation.build_automation( var.get_complete_trigger(), [], on_complete_config ) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "remote_transmitter_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "remote_transmitter_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "remote_transmitter_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 55a4b9c8f6..43fd6920ac 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -31,6 +31,7 @@ from esphome.const import ( PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, + PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv @@ -423,3 +424,16 @@ def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: {cv.Required(CONF_SPI_ID): fv.id_declaration_match_schema(hub_schema)}, extra=cv.ALLOW_EXTRA, ) + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "spi_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "spi_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, +} diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index a0908a299c..03341b5ff3 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -27,6 +27,7 @@ from esphome.const import ( CONF_TX_PIN, CONF_UART_ID, PLATFORM_HOST, + PlatformFramework, ) from esphome.core import CORE import esphome.final_validate as fv @@ -438,3 +439,17 @@ async def uart_write_to_code(config, action_id, template_arg, args): else: cg.add(var.set_data_static(data)) return var + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "uart_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, + "uart_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "uart_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "uart_component_host.cpp": {PlatformFramework.HOST_NATIVE}, + "uart_component_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "uart_component_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index e8ae9b1b4e..47c59af241 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -39,6 +39,7 @@ from esphome.const import ( CONF_TTLS_PHASE_2, CONF_USE_ADDRESS, CONF_USERNAME, + PlatformFramework, ) from esphome.core import CORE, HexInt, coroutine_with_priority import esphome.final_validate as fv @@ -526,3 +527,15 @@ async def wifi_set_sta_to_code(config, action_id, template_arg, args): await automation.build_automation(var.get_error_trigger(), [], on_error_config) await cg.register_component(var, config) return var + + +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "wifi_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, + "wifi_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "wifi_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "wifi_component_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, +} diff --git a/esphome/const.py b/esphome/const.py index 4aeb5179e6..085b9b39b8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,5 +1,9 @@ """Constants used by esphome.""" +from enum import Enum + +from esphome.enum import StrEnum + __version__ = "2025.7.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" @@ -7,14 +11,55 @@ VALID_SUBSTITUTIONS_CHARACTERS = ( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" ) -PLATFORM_BK72XX = "bk72xx" -PLATFORM_ESP32 = "esp32" -PLATFORM_ESP8266 = "esp8266" -PLATFORM_HOST = "host" -PLATFORM_LIBRETINY_OLDSTYLE = "libretiny" -PLATFORM_LN882X = "ln882x" -PLATFORM_RP2040 = "rp2040" -PLATFORM_RTL87XX = "rtl87xx" + +class Platform(StrEnum): + """Platform identifiers for ESPHome.""" + + BK72XX = "bk72xx" + ESP32 = "esp32" + ESP8266 = "esp8266" + HOST = "host" + LIBRETINY_OLDSTYLE = "libretiny" + LN882X = "ln882x" + RP2040 = "rp2040" + RTL87XX = "rtl87xx" + + +class Framework(StrEnum): + """Framework identifiers for ESPHome.""" + + ARDUINO = "arduino" + ESP_IDF = "esp-idf" + NATIVE = "host" + + +class PlatformFramework(Enum): + """Combined platform-framework identifiers with tuple values.""" + + # ESP32 variants + ESP32_ARDUINO = (Platform.ESP32, Framework.ARDUINO) + ESP32_IDF = (Platform.ESP32, Framework.ESP_IDF) + + # Arduino framework platforms + ESP8266_ARDUINO = (Platform.ESP8266, Framework.ARDUINO) + RP2040_ARDUINO = (Platform.RP2040, Framework.ARDUINO) + BK72XX_ARDUINO = (Platform.BK72XX, Framework.ARDUINO) + RTL87XX_ARDUINO = (Platform.RTL87XX, Framework.ARDUINO) + LN882X_ARDUINO = (Platform.LN882X, Framework.ARDUINO) + + # Host platform (native) + HOST_NATIVE = (Platform.HOST, Framework.NATIVE) + + +# Maintain backward compatibility by reassigning after enum definition +PLATFORM_BK72XX = Platform.BK72XX +PLATFORM_ESP32 = Platform.ESP32 +PLATFORM_ESP8266 = Platform.ESP8266 +PLATFORM_HOST = Platform.HOST +PLATFORM_LIBRETINY_OLDSTYLE = Platform.LIBRETINY_OLDSTYLE +PLATFORM_LN882X = Platform.LN882X +PLATFORM_RP2040 = Platform.RP2040 +PLATFORM_RTL87XX = Platform.RTL87XX SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} diff --git a/esphome/core/config.py b/esphome/core/config.py index 641c73a292..cfff50a5c8 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -35,6 +35,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VERSION, KEY_CORE, + PlatformFramework, __version__ as ESPHOME_VERSION, ) from esphome.core import CORE, coroutine_with_priority @@ -551,3 +552,11 @@ async def to_code(config: ConfigType) -> None: cg.add(dev.set_area_id(area_id_hash)) cg.add(cg.App.register_device(dev)) + + +# Platform-specific source files for core +PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { + "ring_buffer.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, + # Note: lock_free_queue.h and event_pool.h are header files and don't need to be filtered + # as they are only included when needed by the preprocessor +} diff --git a/esphome/dashboard/entries.py b/esphome/dashboard/entries.py index e4825298f7..b138cfd272 100644 --- a/esphome/dashboard/entries.py +++ b/esphome/dashboard/entries.py @@ -9,6 +9,7 @@ import os from typing import TYPE_CHECKING, Any from esphome import const, util +from esphome.enum import StrEnum from esphome.storage_json import StorageJSON, ext_storage_path from .const import ( @@ -18,7 +19,6 @@ from .const import ( EVENT_ENTRY_STATE_CHANGED, EVENT_ENTRY_UPDATED, ) -from .enum import StrEnum from .util.subprocess import async_run_system_command if TYPE_CHECKING: diff --git a/esphome/dashboard/enum.py b/esphome/enum.py similarity index 100% rename from esphome/dashboard/enum.py rename to esphome/enum.py diff --git a/esphome/loader.py b/esphome/loader.py index 79a1d7f576..21e822da73 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -11,13 +11,26 @@ import sys from types import ModuleType from typing import Any -from esphome.const import SOURCE_FILE_EXTENSIONS +from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + SOURCE_FILE_EXTENSIONS, + Framework, + Platform, + PlatformFramework, +) from esphome.core import CORE import esphome.core.config from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) +# Build unified lookup table from PlatformFramework enum +_PLATFORM_FRAMEWORK_LOOKUP: dict[ + tuple[Platform, Framework | None], PlatformFramework +] = {pf.value: pf for pf in PlatformFramework} + @dataclass(frozen=True, order=True) class FileResource: @@ -107,13 +120,33 @@ class ComponentManifest: @property def resources(self) -> list[FileResource]: - """Return a list of all file resources defined in the package of this component. + """Return a list of all file resources defined in the package of this component.""" + ret: list[FileResource] = [] - This will return all cpp source files that are located in the same folder as the - loaded .py file (does not look through subdirectories) - """ - ret = [] + # Get current platform-framework combination + core_data: dict[str, Any] = CORE.data.get(KEY_CORE, {}) + target_platform: Platform | None = core_data.get(KEY_TARGET_PLATFORM) + target_framework: Framework | None = core_data.get(KEY_TARGET_FRAMEWORK) + # Get platform-specific files mapping + platform_source_files: dict[str, set[PlatformFramework]] = getattr( + self.module, "PLATFORM_SOURCE_FILES", {} + ) + + # Get current PlatformFramework + lookup_key = (target_platform, target_framework) + current_platform_framework: PlatformFramework | None = ( + _PLATFORM_FRAMEWORK_LOOKUP.get(lookup_key) + ) + + # Build set of allowed filenames for current platform + allowed_filenames: set[str] = set() + if current_platform_framework and platform_source_files: + for filename, platforms in platform_source_files.items(): + if current_platform_framework in platforms: + allowed_filenames.add(filename) + + # Process all resources for resource in ( r.name for r in importlib.resources.files(self.package).iterdir() @@ -122,8 +155,13 @@ class ComponentManifest: if Path(resource).suffix not in SOURCE_FILE_EXTENSIONS: continue if not importlib.resources.files(self.package).joinpath(resource).is_file(): - # Not a resource = this is a directory (yeah this is confusing) continue + + # Check platform restrictions only if file is platform-specific + # Common files (not in platform_source_files) are always included + if resource in platform_source_files and resource not in allowed_filenames: + continue + ret.append(FileResource(self.package, resource)) return ret From 8677918157bd3370ef32239d396dff034ca0cb94 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:16:49 -0500 Subject: [PATCH 02/15] tweaks --- esphome/components/adc/__init__.py | 29 +++++----- esphome/components/debug/__init__.py | 28 ++++++---- esphome/components/deep_sleep/__init__.py | 17 +++--- esphome/components/http_request/__init__.py | 28 +++++----- esphome/components/i2c/__init__.py | 25 +++++---- esphome/components/libretiny/__init__.py | 17 +++--- esphome/components/logger/__init__.py | 36 +++++++----- esphome/components/mdns/__init__.py | 28 ++++++---- esphome/components/mqtt/__init__.py | 15 +++-- esphome/components/nextion/__init__.py | 25 +++++---- esphome/components/ota/__init__.py | 25 +++++---- .../components/remote_receiver/__init__.py | 27 +++++---- .../components/remote_transmitter/__init__.py | 27 +++++---- esphome/components/socket/__init__.py | 19 +++++++ esphome/components/spi/__init__.py | 25 +++++---- esphome/components/uart/__init__.py | 27 +++++---- esphome/components/wifi/__init__.py | 23 ++++---- esphome/core/config.py | 16 ++++-- esphome/helpers.py | 56 +++++++++++++++++++ esphome/loader.py | 47 +++------------- 20 files changed, 324 insertions(+), 216 deletions(-) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index c3ababbb84..1bd5121998 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -19,6 +19,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE +from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] @@ -237,16 +238,18 @@ def validate_adc_pin(value): raise NotImplementedError -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "adc_sensor_esp32.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP32_IDF, - }, - "adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, - "adc_sensor_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "adc_sensor_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "adc_sensor_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index b5cdef4f0e..0a5756b8bd 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -9,6 +9,7 @@ from esphome.const import ( CONF_LOOP_TIME, PlatformFramework, ) +from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@OttoWinter"] DEPENDENCIES = ["logger"] @@ -47,14 +48,19 @@ async def to_code(config): await cg.register_component(var, config) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "debug_esp32.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, - "debug_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "debug_host.cpp": {PlatformFramework.HOST_NATIVE}, - "debug_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, - "debug_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "debug_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "debug_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "debug_host.cpp": {PlatformFramework.HOST_NATIVE}, + "debug_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "debug_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 096d2eaa38..5df9a23e47 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -29,6 +29,7 @@ from esphome.const import ( PLATFORM_ESP8266, PlatformFramework, ) +from esphome.helpers import filter_source_files_from_platform WAKEUP_PINS = { VARIANT_ESP32: [ @@ -316,10 +317,12 @@ async def deep_sleep_action_to_code(config, action_id, template_arg, args): return var -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "deep_sleep_esp32.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP32_IDF, - }, - "deep_sleep_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "deep_sleep_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "deep_sleep_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + } +) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 0eaecaac45..3c9f112e66 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( __version__, ) from esphome.core import CORE, Lambda -from esphome.helpers import IS_MACOS +from esphome.helpers import IS_MACOS, filter_source_files_from_platform DEPENDENCIES = ["network"] AUTO_LOAD = ["json", "watchdog"] @@ -322,15 +322,17 @@ async def http_request_action_to_code(config, action_id, template_arg, args): return var -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "http_request_host.cpp": {PlatformFramework.HOST_NATIVE}, - "http_request_arduino.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP8266_ARDUINO, - PlatformFramework.RP2040_ARDUINO, - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, - "http_request_idf.cpp": {PlatformFramework.ESP32_IDF}, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "http_request_host.cpp": {PlatformFramework.HOST_NATIVE}, + "http_request_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "http_request_idf.cpp": {PlatformFramework.ESP32_IDF}, + } +) diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index db52479783..8b47403d67 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -22,6 +22,7 @@ from esphome.const import ( ) from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv +from esphome.helpers import filter_source_files_from_platform LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] @@ -208,14 +209,16 @@ def final_validate_device_schema( ) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "i2c_bus_arduino.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP8266_ARDUINO, - PlatformFramework.RP2040_ARDUINO, - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, - "i2c_bus_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "i2c_bus_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "i2c_bus_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + } +) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index e4f6f15576..287f424467 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -24,6 +24,7 @@ from esphome.const import ( __version__, ) from esphome.core import CORE +from esphome.helpers import filter_source_files_from_platform from . import gpio # noqa from .const import ( @@ -343,10 +344,12 @@ async def component_to_code(config): await cg.register_component(var, config) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "gpio_arduino.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "gpio_arduino.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 4f65907210..001f99623c 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -45,6 +45,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, Lambda, coroutine_with_priority +from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") @@ -447,18 +448,23 @@ async def logger_set_level_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg, lambda_) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "logger_esp32.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, - "logger_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, - "logger_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, - "logger_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, - "task_log_buffer.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP32_IDF, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "logger_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "logger_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, + "logger_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "logger_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "task_log_buffer.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + } +) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 43d214d77b..22f416c94f 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -11,6 +11,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority +from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] @@ -111,14 +112,19 @@ async def to_code(config): cg.add(var.add_extra_service(exp)) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "mdns_esp32.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, - "mdns_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "mdns_host.cpp": {PlatformFramework.HOST_NATIVE}, - "mdns_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, - "mdns_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "mdns_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "mdns_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "mdns_host.cpp": {PlatformFramework.HOST_NATIVE}, + "mdns_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "mdns_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 101761be9d..989a1a650b 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -57,6 +57,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority +from esphome.helpers import filter_source_files_from_platform DEPENDENCIES = ["network"] @@ -599,9 +600,11 @@ async def mqtt_disable_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg, paren) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "mqtt_backend_esp32.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP32_IDF, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "mqtt_backend_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + } +) diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index 332c27409c..651e0ae5a4 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg from esphome.components import uart from esphome.const import PlatformFramework +from esphome.helpers import filter_source_files_from_platform nextion_ns = cg.esphome_ns.namespace("nextion") Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) @@ -10,14 +11,16 @@ CONF_NEXTION_ID = "nextion_id" CONF_PUBLISH_STATE = "publish_state" CONF_SEND_TO_NEXTION = "send_to_nextion" -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "nextion_upload_arduino.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP8266_ARDUINO, - PlatformFramework.RP2040_ARDUINO, - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, - "nextion_upload_idf.cpp": {PlatformFramework.ESP32_IDF}, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "nextion_upload_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "nextion_upload_idf.cpp": {PlatformFramework.ESP32_IDF}, + } +) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index bd7a97536c..83e637342b 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority +from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5", "safe_mode"] @@ -123,14 +124,16 @@ async def ota_to_code(var, config): cg.add_define("USE_OTA_STATE_CALLBACK") -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "ota_backend_arduino_esp32.cpp": {PlatformFramework.ESP32_ARDUINO}, - "ota_backend_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, - "ota_backend_arduino_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "ota_backend_arduino_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, - "ota_backend_arduino_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "ota_backend_arduino_esp32.cpp": {PlatformFramework.ESP32_ARDUINO}, + "ota_backend_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "ota_backend_arduino_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "ota_backend_arduino_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "ota_backend_arduino_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index f395aea3c8..82efe36879 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -18,6 +18,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, TimePeriod +from esphome.helpers import filter_source_files_from_platform CONF_FILTER_SYMBOLS = "filter_symbols" CONF_RECEIVE_SYMBOLS = "receive_symbols" @@ -173,15 +174,17 @@ async def to_code(config): cg.add(var.set_idle_us(config[CONF_IDLE])) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "remote_receiver_esp32.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP32_IDF, - }, - "remote_receiver_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "remote_receiver_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "remote_receiver_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "remote_receiver_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "remote_receiver_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index 1e935354f9..dbda8a7752 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE +from esphome.helpers import filter_source_files_from_platform AUTO_LOAD = ["remote_base"] @@ -98,15 +99,17 @@ async def to_code(config): ) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "remote_transmitter_esp32.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP32_IDF, - }, - "remote_transmitter_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "remote_transmitter_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "remote_transmitter_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "remote_transmitter_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "remote_transmitter_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index 26031a8da5..dcbab9d240 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -1,5 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.core import CORE CODEOWNERS = ["@esphome/core"] @@ -40,3 +41,21 @@ async def to_code(config): elif impl == IMPLEMENTATION_BSD_SOCKETS: cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS") cg.add_define("USE_SOCKET_SELECT_SUPPORT") + + +def FILTER_SOURCE_FILES() -> list[str]: + """Return list of socket implementation files that aren't selected by the user.""" + if not hasattr(CORE, "config") or "socket" not in CORE.config: + return [] + + impl = CORE.config["socket"][CONF_IMPLEMENTATION] + + # Build list of files to exclude based on selected implementation + excluded = [] + if impl != IMPLEMENTATION_LWIP_TCP: + excluded.append("lwip_raw_tcp_impl.cpp") + if impl != IMPLEMENTATION_BSD_SOCKETS: + excluded.append("bsd_sockets_impl.cpp") + if impl != IMPLEMENTATION_LWIP_SOCKETS: + excluded.append("lwip_sockets_impl.cpp") + return excluded diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 43fd6920ac..d949da0a60 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -35,6 +35,7 @@ from esphome.const import ( ) from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv +from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") @@ -426,14 +427,16 @@ def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: ) -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "spi_arduino.cpp": { - PlatformFramework.ESP32_ARDUINO, - PlatformFramework.ESP8266_ARDUINO, - PlatformFramework.RP2040_ARDUINO, - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, - "spi_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "spi_arduino.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.RP2040_ARDUINO, + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + "spi_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + } +) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 03341b5ff3..1bfcef8f61 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -31,6 +31,7 @@ from esphome.const import ( ) from esphome.core import CORE import esphome.final_validate as fv +from esphome.helpers import filter_source_files_from_platform from esphome.yaml_util import make_data_base CODEOWNERS = ["@esphome/core"] @@ -441,15 +442,17 @@ async def uart_write_to_code(config, action_id, template_arg, args): return var -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "uart_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, - "uart_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, - "uart_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "uart_component_host.cpp": {PlatformFramework.HOST_NATIVE}, - "uart_component_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, - "uart_component_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "uart_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, + "uart_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "uart_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "uart_component_host.cpp": {PlatformFramework.HOST_NATIVE}, + "uart_component_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO}, + "uart_component_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 47c59af241..a5a88862bf 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -43,6 +43,7 @@ from esphome.const import ( ) from esphome.core import CORE, HexInt, coroutine_with_priority import esphome.final_validate as fv +from esphome.helpers import filter_source_files_from_platform from . import wpa2_eap @@ -529,13 +530,15 @@ async def wifi_set_sta_to_code(config, action_id, template_arg, args): return var -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "wifi_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, - "wifi_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, - "wifi_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, - "wifi_component_libretiny.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "wifi_component_esp32_arduino.cpp": {PlatformFramework.ESP32_ARDUINO}, + "wifi_component_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "wifi_component_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "wifi_component_libretiny.cpp": { + PlatformFramework.BK72XX_ARDUINO, + PlatformFramework.RTL87XX_ARDUINO, + PlatformFramework.LN882X_ARDUINO, + }, + } +) diff --git a/esphome/core/config.py b/esphome/core/config.py index cfff50a5c8..4dc2e67e1d 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -41,6 +41,7 @@ from esphome.const import ( from esphome.core import CORE, coroutine_with_priority from esphome.helpers import ( copy_file_if_changed, + filter_source_files_from_platform, fnv1a_32bit_hash, get_str_env, walk_files, @@ -555,8 +556,13 @@ async def to_code(config: ConfigType) -> None: # Platform-specific source files for core -PLATFORM_SOURCE_FILES: dict[str, set[PlatformFramework]] = { - "ring_buffer.cpp": {PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF}, - # Note: lock_free_queue.h and event_pool.h are header files and don't need to be filtered - # as they are only included when needed by the preprocessor -} +FILTER_SOURCE_FILES = filter_source_files_from_platform( + { + "ring_buffer.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + # Note: lock_free_queue.h and event_pool.h are header files and don't need to be filtered + # as they are only included when needed by the preprocessor + } +) diff --git a/esphome/helpers.py b/esphome/helpers.py index bf0e3b5cf7..153dcf6a6a 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -1,4 +1,5 @@ import codecs +from collections.abc import Callable from contextlib import suppress import ipaddress import logging @@ -7,8 +8,12 @@ from pathlib import Path import platform import re import tempfile +from typing import TYPE_CHECKING from urllib.parse import urlparse +if TYPE_CHECKING: + from esphome.const import PlatformFramework + _LOGGER = logging.getLogger(__name__) IS_MACOS = platform.system() == "Darwin" @@ -505,3 +510,54 @@ _DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9-_]") def sanitize(value): """Same behaviour as `helpers.cpp` method `str_sanitize`.""" return _DISALLOWED_CHARS.sub("_", value) + + +def filter_source_files_from_platform( + files_map: dict[str, set["PlatformFramework"]], +) -> Callable[[], list[str]]: + """Helper to build a FILTER_SOURCE_FILES function from platform mapping. + + Args: + files_map: Dict mapping filename to set of PlatformFramework enums + that should compile this file + + Returns: + Function that returns list of files to exclude for current platform + """ + from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PlatformFramework, + ) + from esphome.core import CORE + + # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum + _PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} + + def filter_source_files() -> list[str]: + # Get current platform/framework + core_data = CORE.data.get(KEY_CORE, {}) + target_platform = core_data.get(KEY_TARGET_PLATFORM) + target_framework = core_data.get(KEY_TARGET_FRAMEWORK) + + if not target_platform or not target_framework: + return [] + + # Direct lookup of current PlatformFramework + current_platform_framework = _PLATFORM_FRAMEWORK_LOOKUP.get( + (target_platform, target_framework) + ) + + if not current_platform_framework: + return [] + + # Return files that should be excluded for current platform + excluded = [] + for filename, platforms in files_map.items(): + if current_platform_framework not in platforms: + excluded.append(filename) + + return excluded + + return filter_source_files diff --git a/esphome/loader.py b/esphome/loader.py index 21e822da73..4a6847bd89 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -11,26 +11,13 @@ import sys from types import ModuleType from typing import Any -from esphome.const import ( - KEY_CORE, - KEY_TARGET_FRAMEWORK, - KEY_TARGET_PLATFORM, - SOURCE_FILE_EXTENSIONS, - Framework, - Platform, - PlatformFramework, -) +from esphome.const import SOURCE_FILE_EXTENSIONS from esphome.core import CORE import esphome.core.config from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) -# Build unified lookup table from PlatformFramework enum -_PLATFORM_FRAMEWORK_LOOKUP: dict[ - tuple[Platform, Framework | None], PlatformFramework -] = {pf.value: pf for pf in PlatformFramework} - @dataclass(frozen=True, order=True) class FileResource: @@ -123,28 +110,13 @@ class ComponentManifest: """Return a list of all file resources defined in the package of this component.""" ret: list[FileResource] = [] - # Get current platform-framework combination - core_data: dict[str, Any] = CORE.data.get(KEY_CORE, {}) - target_platform: Platform | None = core_data.get(KEY_TARGET_PLATFORM) - target_framework: Framework | None = core_data.get(KEY_TARGET_FRAMEWORK) + # Get filter function for source files + filter_source_files_func = getattr(self.module, "FILTER_SOURCE_FILES", None) - # Get platform-specific files mapping - platform_source_files: dict[str, set[PlatformFramework]] = getattr( - self.module, "PLATFORM_SOURCE_FILES", {} - ) - - # Get current PlatformFramework - lookup_key = (target_platform, target_framework) - current_platform_framework: PlatformFramework | None = ( - _PLATFORM_FRAMEWORK_LOOKUP.get(lookup_key) - ) - - # Build set of allowed filenames for current platform - allowed_filenames: set[str] = set() - if current_platform_framework and platform_source_files: - for filename, platforms in platform_source_files.items(): - if current_platform_framework in platforms: - allowed_filenames.add(filename) + # Get list of files to exclude + excluded_files: set[str] = set() + if filter_source_files_func is not None: + excluded_files = set(filter_source_files_func()) # Process all resources for resource in ( @@ -157,9 +129,8 @@ class ComponentManifest: if not importlib.resources.files(self.package).joinpath(resource).is_file(): continue - # Check platform restrictions only if file is platform-specific - # Common files (not in platform_source_files) are always included - if resource in platform_source_files and resource not in allowed_filenames: + # Skip excluded files + if resource in excluded_files: continue ret.append(FileResource(self.package, resource)) From 737e1284afa10ed8f343e7764d0dfe90463906f8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:18:10 -0500 Subject: [PATCH 03/15] tweaks --- esphome/components/socket/__init__.py | 3 --- esphome/helpers.py | 11 +++++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index dcbab9d240..e085a09eac 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -45,9 +45,6 @@ async def to_code(config): def FILTER_SOURCE_FILES() -> list[str]: """Return list of socket implementation files that aren't selected by the user.""" - if not hasattr(CORE, "config") or "socket" not in CORE.config: - return [] - impl = CORE.config["socket"][CONF_IMPLEMENTATION] # Build list of files to exclude based on selected implementation diff --git a/esphome/helpers.py b/esphome/helpers.py index 153dcf6a6a..a99a317239 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -553,11 +553,10 @@ def filter_source_files_from_platform( return [] # Return files that should be excluded for current platform - excluded = [] - for filename, platforms in files_map.items(): - if current_platform_framework not in platforms: - excluded.append(filename) - - return excluded + return [ + filename + for filename, platforms in files_map.items() + if current_platform_framework not in platforms + ] return filter_source_files From ef98f42e7ee8c2cfaf573a97439ce0999ffedefb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:18:24 -0500 Subject: [PATCH 04/15] tweaks --- esphome/helpers.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/esphome/helpers.py b/esphome/helpers.py index a99a317239..a7f9a77228 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -11,8 +11,16 @@ import tempfile from typing import TYPE_CHECKING from urllib.parse import urlparse +from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PlatformFramework, +) +from esphome.core import CORE + if TYPE_CHECKING: - from esphome.const import PlatformFramework + pass # PlatformFramework is already imported above _LOGGER = logging.getLogger(__name__) @@ -524,13 +532,7 @@ def filter_source_files_from_platform( Returns: Function that returns list of files to exclude for current platform """ - from esphome.const import ( - KEY_CORE, - KEY_TARGET_FRAMEWORK, - KEY_TARGET_PLATFORM, - PlatformFramework, - ) - from esphome.core import CORE + from esphome.const import PlatformFramework # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum _PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} From a1f63c0dfc4540063c3917f5665e4718f93b2473 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:24:50 -0500 Subject: [PATCH 05/15] fixes --- esphome/helpers.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/esphome/helpers.py b/esphome/helpers.py index a7f9a77228..7df2fbdea2 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -11,16 +11,8 @@ import tempfile from typing import TYPE_CHECKING from urllib.parse import urlparse -from esphome.const import ( - KEY_CORE, - KEY_TARGET_FRAMEWORK, - KEY_TARGET_PLATFORM, - PlatformFramework, -) -from esphome.core import CORE - if TYPE_CHECKING: - pass # PlatformFramework is already imported above + from esphome.const import PlatformFramework _LOGGER = logging.getLogger(__name__) @@ -532,7 +524,14 @@ def filter_source_files_from_platform( Returns: Function that returns list of files to exclude for current platform """ - from esphome.const import PlatformFramework + # Import here to avoid circular imports + from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PlatformFramework, + ) + from esphome.core import CORE # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum _PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} From 023fa4d220f520326dc187a2d77b365a337ece38 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:37:41 -0500 Subject: [PATCH 06/15] fixes --- esphome/components/adc/__init__.py | 2 +- esphome/components/debug/__init__.py | 2 +- esphome/components/deep_sleep/__init__.py | 2 +- esphome/components/http_request/__init__.py | 3 +- esphome/components/i2c/__init__.py | 2 +- esphome/components/libretiny/__init__.py | 2 +- esphome/components/logger/__init__.py | 2 +- esphome/components/mdns/__init__.py | 2 +- esphome/components/mqtt/__init__.py | 2 +- esphome/components/nextion/__init__.py | 2 +- esphome/components/ota/__init__.py | 2 +- .../components/remote_receiver/__init__.py | 2 +- .../components/remote_transmitter/__init__.py | 2 +- esphome/components/spi/__init__.py | 2 +- esphome/components/uart/__init__.py | 2 +- esphome/components/wifi/__init__.py | 2 +- esphome/config_helpers.py | 53 +++++++++++++++++- esphome/core/config.py | 2 +- esphome/helpers.py | 56 ------------------- 19 files changed, 70 insertions(+), 74 deletions(-) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 1bd5121998..10b7df8638 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -10,6 +10,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, ) +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_ANALOG, @@ -19,7 +20,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE -from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index 0a5756b8bd..500dfac1fe 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -1,4 +1,5 @@ import esphome.codegen as cg +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_BLOCK, @@ -9,7 +10,6 @@ from esphome.const import ( CONF_LOOP_TIME, PlatformFramework, ) -from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@OttoWinter"] DEPENDENCIES = ["logger"] diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 5df9a23e47..55826f52bb 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -11,6 +11,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, ) +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_DEFAULT, @@ -29,7 +30,6 @@ from esphome.const import ( PLATFORM_ESP8266, PlatformFramework, ) -from esphome.helpers import filter_source_files_from_platform WAKEUP_PINS = { VARIANT_ESP32: [ diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 3c9f112e66..0d32bc97c2 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -2,6 +2,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components import esp32 from esphome.components.const import CONF_REQUEST_HEADERS +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_ESP8266_DISABLE_SSL_SUPPORT, @@ -17,7 +18,7 @@ from esphome.const import ( __version__, ) from esphome.core import CORE, Lambda -from esphome.helpers import IS_MACOS, filter_source_files_from_platform +from esphome.helpers import IS_MACOS DEPENDENCIES = ["network"] AUTO_LOAD = ["json", "watchdog"] diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 8b47403d67..4172b23845 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -3,6 +3,7 @@ import logging from esphome import pins import esphome.codegen as cg from esphome.components import esp32 +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_ADDRESS, @@ -22,7 +23,6 @@ from esphome.const import ( ) from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv -from esphome.helpers import filter_source_files_from_platform LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 287f424467..f641c1776d 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -3,6 +3,7 @@ import logging from os.path import dirname, isfile, join import esphome.codegen as cg +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_BOARD, @@ -24,7 +25,6 @@ from esphome.const import ( __version__, ) from esphome.core import CORE -from esphome.helpers import filter_source_files_from_platform from . import gpio # noqa from .const import ( diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 001f99623c..9ac2999696 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -21,6 +21,7 @@ from esphome.components.libretiny.const import ( COMPONENT_LN882X, COMPONENT_RTL87XX, ) +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_ARGS, @@ -45,7 +46,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, Lambda, coroutine_with_priority -from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 22f416c94f..e32d39cede 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -1,5 +1,6 @@ import esphome.codegen as cg from esphome.components.esp32 import add_idf_component +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_DISABLED, @@ -11,7 +12,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority -from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 989a1a650b..1a6fcabf42 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -5,6 +5,7 @@ from esphome.automation import Condition import esphome.codegen as cg from esphome.components import logger from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_AVAILABILITY, @@ -57,7 +58,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority -from esphome.helpers import filter_source_files_from_platform DEPENDENCIES = ["network"] diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index 651e0ae5a4..8adc49d68c 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg from esphome.components import uart +from esphome.config_helpers import filter_source_files_from_platform from esphome.const import PlatformFramework -from esphome.helpers import filter_source_files_from_platform nextion_ns = cg.esphome_ns.namespace("nextion") Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 83e637342b..4d5b8a61e2 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,5 +1,6 @@ from esphome import automation import esphome.codegen as cg +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, @@ -10,7 +11,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, coroutine_with_priority -from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5", "safe_mode"] diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 82efe36879..dffc088085 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,6 +1,7 @@ from esphome import pins import esphome.codegen as cg from esphome.components import esp32, esp32_rmt, remote_base +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, @@ -18,7 +19,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, TimePeriod -from esphome.helpers import filter_source_files_from_platform CONF_FILTER_SYMBOLS = "filter_symbols" CONF_RECEIVE_SYMBOLS = "receive_symbols" diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index dbda8a7752..47a46ff56b 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -1,6 +1,7 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import esp32, esp32_rmt, remote_base +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_CARRIER_DUTY_PERCENT, @@ -15,7 +16,6 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE -from esphome.helpers import filter_source_files_from_platform AUTO_LOAD = ["remote_base"] diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index d949da0a60..58bfc3f411 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -13,6 +13,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, ) +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_CLK_PIN, @@ -35,7 +36,6 @@ from esphome.const import ( ) from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv -from esphome.helpers import filter_source_files_from_platform CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 1bfcef8f61..7d4c6360fe 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -2,6 +2,7 @@ import re from esphome import automation, pins import esphome.codegen as cg +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_AFTER, @@ -31,7 +32,6 @@ from esphome.const import ( ) from esphome.core import CORE import esphome.final_validate as fv -from esphome.helpers import filter_source_files_from_platform from esphome.yaml_util import make_data_base CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index a5a88862bf..cb98325427 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -3,6 +3,7 @@ from esphome.automation import Condition import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant from esphome.components.network import IPAddress +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_AP, @@ -43,7 +44,6 @@ from esphome.const import ( ) from esphome.core import CORE, HexInt, coroutine_with_priority import esphome.final_validate as fv -from esphome.helpers import filter_source_files_from_platform from . import wpa2_eap diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index 54242bc259..5ecd665abe 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -1,4 +1,13 @@ -from esphome.const import CONF_ID +from collections.abc import Callable + +from esphome.const import ( + CONF_ID, + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PlatformFramework, +) +from esphome.core import CORE class Extend: @@ -103,3 +112,45 @@ def merge_config(full_old, full_new): return new return merge(full_old, full_new) + + +def filter_source_files_from_platform( + files_map: dict[str, set[PlatformFramework]], +) -> Callable[[], list[str]]: + """Helper to build a FILTER_SOURCE_FILES function from platform mapping. + + Args: + files_map: Dict mapping filename to set of PlatformFramework enums + that should compile this file + + Returns: + Function that returns list of files to exclude for current platform + """ + # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum + _PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} + + def filter_source_files() -> list[str]: + # Get current platform/framework + core_data = CORE.data.get(KEY_CORE, {}) + target_platform = core_data.get(KEY_TARGET_PLATFORM) + target_framework = core_data.get(KEY_TARGET_FRAMEWORK) + + if not target_platform or not target_framework: + return [] + + # Direct lookup of current PlatformFramework + current_platform_framework = _PLATFORM_FRAMEWORK_LOOKUP.get( + (target_platform, target_framework) + ) + + if not current_platform_framework: + return [] + + # Return files that should be excluded for current platform + return [ + filename + for filename, platforms in files_map.items() + if current_platform_framework not in platforms + ] + + return filter_source_files diff --git a/esphome/core/config.py b/esphome/core/config.py index 4dc2e67e1d..f73369f28f 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -6,6 +6,7 @@ from pathlib import Path from esphome import automation, core import esphome.codegen as cg +from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_AREA, @@ -41,7 +42,6 @@ from esphome.const import ( from esphome.core import CORE, coroutine_with_priority from esphome.helpers import ( copy_file_if_changed, - filter_source_files_from_platform, fnv1a_32bit_hash, get_str_env, walk_files, diff --git a/esphome/helpers.py b/esphome/helpers.py index 7df2fbdea2..bf0e3b5cf7 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -1,5 +1,4 @@ import codecs -from collections.abc import Callable from contextlib import suppress import ipaddress import logging @@ -8,12 +7,8 @@ from pathlib import Path import platform import re import tempfile -from typing import TYPE_CHECKING from urllib.parse import urlparse -if TYPE_CHECKING: - from esphome.const import PlatformFramework - _LOGGER = logging.getLogger(__name__) IS_MACOS = platform.system() == "Darwin" @@ -510,54 +505,3 @@ _DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9-_]") def sanitize(value): """Same behaviour as `helpers.cpp` method `str_sanitize`.""" return _DISALLOWED_CHARS.sub("_", value) - - -def filter_source_files_from_platform( - files_map: dict[str, set["PlatformFramework"]], -) -> Callable[[], list[str]]: - """Helper to build a FILTER_SOURCE_FILES function from platform mapping. - - Args: - files_map: Dict mapping filename to set of PlatformFramework enums - that should compile this file - - Returns: - Function that returns list of files to exclude for current platform - """ - # Import here to avoid circular imports - from esphome.const import ( - KEY_CORE, - KEY_TARGET_FRAMEWORK, - KEY_TARGET_PLATFORM, - PlatformFramework, - ) - from esphome.core import CORE - - # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum - _PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} - - def filter_source_files() -> list[str]: - # Get current platform/framework - core_data = CORE.data.get(KEY_CORE, {}) - target_platform = core_data.get(KEY_TARGET_PLATFORM) - target_framework = core_data.get(KEY_TARGET_FRAMEWORK) - - if not target_platform or not target_framework: - return [] - - # Direct lookup of current PlatformFramework - current_platform_framework = _PLATFORM_FRAMEWORK_LOOKUP.get( - (target_platform, target_framework) - ) - - if not current_platform_framework: - return [] - - # Return files that should be excluded for current platform - return [ - filename - for filename, platforms in files_map.items() - if current_platform_framework not in platforms - ] - - return filter_source_files From 96f0fda477ad8e9330fa30bae81feb44e59c1d45 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:42:18 -0500 Subject: [PATCH 07/15] fixes --- esphome/config_helpers.py | 5 +++-- esphome/loader.py | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index 5ecd665abe..3c866f6d31 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -9,6 +9,9 @@ from esphome.const import ( ) from esphome.core import CORE +# Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum +_PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} + class Extend: def __init__(self, value): @@ -126,8 +129,6 @@ def filter_source_files_from_platform( Returns: Function that returns list of files to exclude for current platform """ - # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum - _PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} def filter_source_files() -> list[str]: # Get current platform/framework diff --git a/esphome/loader.py b/esphome/loader.py index 4a6847bd89..06d1c7817b 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -107,7 +107,11 @@ class ComponentManifest: @property def resources(self) -> list[FileResource]: - """Return a list of all file resources defined in the package of this component.""" + """Return a list of all file resources defined in the package of this component. + + This will return all cpp source files that are located in the same folder as the + loaded .py file (does not look through subdirectories) + """ ret: list[FileResource] = [] # Get filter function for source files @@ -127,6 +131,7 @@ class ComponentManifest: if Path(resource).suffix not in SOURCE_FILE_EXTENSIONS: continue if not importlib.resources.files(self.package).joinpath(resource).is_file(): + # Not a resource = this is a directory (yeah this is confusing) continue # Skip excluded files From 05253991c2c967011dc33c98bc5b4d718508d054 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:42:44 -0500 Subject: [PATCH 08/15] fixes --- esphome/loader.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/loader.py b/esphome/loader.py index 06d1c7817b..7b2472521a 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -118,9 +118,9 @@ class ComponentManifest: filter_source_files_func = getattr(self.module, "FILTER_SOURCE_FILES", None) # Get list of files to exclude - excluded_files: set[str] = set() - if filter_source_files_func is not None: - excluded_files = set(filter_source_files_func()) + excluded_files = ( + set(filter_source_files_func()) if filter_source_files_func else set() + ) # Process all resources for resource in ( From 28886a896b0b2ff45ce9f8239a827a5a70873f72 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:48:11 -0500 Subject: [PATCH 09/15] some tests --- esphome/config_helpers.py | 4 +- tests/unit_tests/test_config_helpers.py | 75 +++++++++++++++++++++++++ tests/unit_tests/test_loader.py | 63 +++++++++++++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tests/unit_tests/test_config_helpers.py create mode 100644 tests/unit_tests/test_loader.py diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index 3c866f6d31..73ad4caff0 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -10,7 +10,9 @@ from esphome.const import ( from esphome.core import CORE # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum -_PLATFORM_FRAMEWORK_LOOKUP = {pf.value: pf for pf in PlatformFramework} +_PLATFORM_FRAMEWORK_LOOKUP = { + (pf.value[0].value, pf.value[1].value): pf for pf in PlatformFramework +} class Extend: diff --git a/tests/unit_tests/test_config_helpers.py b/tests/unit_tests/test_config_helpers.py new file mode 100644 index 0000000000..8b51a8adcd --- /dev/null +++ b/tests/unit_tests/test_config_helpers.py @@ -0,0 +1,75 @@ +"""Unit tests for esphome.config_helpers module.""" + +from unittest.mock import patch + +from esphome.config_helpers import filter_source_files_from_platform +from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PlatformFramework, +) + + +def test_filter_source_files_from_platform(): + """Test that filter_source_files_from_platform correctly filters files based on platform.""" + # Define test file mappings + files_map = { + "logger_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "logger_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, + "logger_common.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.HOST_NATIVE, + }, + } + + # Create the filter function + filter_func = filter_source_files_from_platform(files_map) + + # Test case 1: ESP32 with Arduino framework + mock_core_data = { + KEY_CORE: { + KEY_TARGET_PLATFORM: "esp32", + KEY_TARGET_FRAMEWORK: "arduino", + } + } + + with patch("esphome.config_helpers.CORE.data", mock_core_data): + excluded = filter_func() + # ESP32 Arduino should exclude ESP8266 and HOST files + assert "logger_esp8266.cpp" in excluded + assert "logger_host.cpp" in excluded + # But not ESP32 or common files + assert "logger_esp32.cpp" not in excluded + assert "logger_common.cpp" not in excluded + + # Test case 2: Host platform + mock_core_data = { + KEY_CORE: { + KEY_TARGET_PLATFORM: "host", + KEY_TARGET_FRAMEWORK: "host", # Framework.NATIVE is "host" + } + } + + with patch("esphome.config_helpers.CORE.data", mock_core_data): + excluded = filter_func() + # Host should exclude ESP32 and ESP8266 files + assert "logger_esp32.cpp" in excluded + assert "logger_esp8266.cpp" in excluded + # But not host or common files + assert "logger_host.cpp" not in excluded + assert "logger_common.cpp" not in excluded + + # Test case 3: Missing platform/framework data + mock_core_data = {KEY_CORE: {}} + + with patch("esphome.config_helpers.CORE.data", mock_core_data): + excluded = filter_func() + # Should return empty list when platform/framework not set + assert excluded == [] diff --git a/tests/unit_tests/test_loader.py b/tests/unit_tests/test_loader.py new file mode 100644 index 0000000000..24c8464aa4 --- /dev/null +++ b/tests/unit_tests/test_loader.py @@ -0,0 +1,63 @@ +"""Unit tests for esphome.loader module.""" + +from unittest.mock import MagicMock, patch + +from esphome.loader import ComponentManifest + + +def test_component_manifest_resources_with_filter_source_files(): + """Test that ComponentManifest.resources correctly filters out excluded files.""" + # Create a mock module with FILTER_SOURCE_FILES function + mock_module = MagicMock() + mock_module.FILTER_SOURCE_FILES = lambda: [ + "platform_esp32.cpp", + "platform_esp8266.cpp", + ] + mock_module.__package__ = "esphome.components.test_component" + + # Create ComponentManifest instance + manifest = ComponentManifest(mock_module) + + # Mock the files in the package + def create_mock_file(filename): + mock_file = MagicMock() + mock_file.name = filename + mock_file.is_file.return_value = True + return mock_file + + mock_files = [ + create_mock_file("test.cpp"), + create_mock_file("test.h"), + create_mock_file("platform_esp32.cpp"), + create_mock_file("platform_esp8266.cpp"), + create_mock_file("common.cpp"), + create_mock_file("README.md"), # Should be excluded by extension + ] + + # Mock importlib.resources + with patch("importlib.resources.files") as mock_files_func: + mock_package_files = MagicMock() + mock_package_files.iterdir.return_value = mock_files + mock_package_files.joinpath = lambda name: MagicMock(is_file=lambda: True) + mock_files_func.return_value = mock_package_files + + # Get resources + resources = manifest.resources + + # Convert to list of filenames for easier testing + resource_names = [r.resource for r in resources] + + # Check that platform files are excluded + assert "platform_esp32.cpp" not in resource_names + assert "platform_esp8266.cpp" not in resource_names + + # Check that other source files are included + assert "test.cpp" in resource_names + assert "test.h" in resource_names + assert "common.cpp" in resource_names + + # Check that non-source files are excluded + assert "README.md" not in resource_names + + # Verify the correct number of resources + assert len(resources) == 3 # test.cpp, test.h, common.cpp From 8d8db11dd91957ecc5ddd21c8e0065a75cc65668 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:51:17 -0500 Subject: [PATCH 10/15] some tests --- tests/unit_tests/test_config_helpers.py | 77 ++++++++++++++++++------- tests/unit_tests/test_loader.py | 4 +- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/tests/unit_tests/test_config_helpers.py b/tests/unit_tests/test_config_helpers.py index 8b51a8adcd..c1f8c2bef8 100644 --- a/tests/unit_tests/test_config_helpers.py +++ b/tests/unit_tests/test_config_helpers.py @@ -1,5 +1,6 @@ """Unit tests for esphome.config_helpers module.""" +from collections.abc import Callable from unittest.mock import patch from esphome.config_helpers import filter_source_files_from_platform @@ -11,8 +12,47 @@ from esphome.const import ( ) -def test_filter_source_files_from_platform(): - """Test that filter_source_files_from_platform correctly filters files based on platform.""" +def test_filter_source_files_from_platform_esp32() -> None: + """Test that filter_source_files_from_platform correctly filters files for ESP32 platform.""" + # Define test file mappings + files_map: dict[str, set[PlatformFramework]] = { + "logger_esp32.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + }, + "logger_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO}, + "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, + "logger_common.cpp": { + PlatformFramework.ESP32_ARDUINO, + PlatformFramework.ESP32_IDF, + PlatformFramework.ESP8266_ARDUINO, + PlatformFramework.HOST_NATIVE, + }, + } + + # Create the filter function + filter_func: Callable[[], list[str]] = filter_source_files_from_platform(files_map) + + # Test ESP32 with Arduino framework + mock_core_data: dict[str, dict[str, str]] = { + KEY_CORE: { + KEY_TARGET_PLATFORM: "esp32", + KEY_TARGET_FRAMEWORK: "arduino", + } + } + + with patch("esphome.config_helpers.CORE.data", mock_core_data): + excluded = filter_func() + # ESP32 Arduino should exclude ESP8266 and HOST files + assert "logger_esp8266.cpp" in excluded + assert "logger_host.cpp" in excluded + # But not ESP32 or common files + assert "logger_esp32.cpp" not in excluded + assert "logger_common.cpp" not in excluded + + +def test_filter_source_files_from_platform_host(): + """Test that filter_source_files_from_platform correctly filters files for HOST platform.""" # Define test file mappings files_map = { "logger_esp32.cpp": { @@ -32,24 +72,7 @@ def test_filter_source_files_from_platform(): # Create the filter function filter_func = filter_source_files_from_platform(files_map) - # Test case 1: ESP32 with Arduino framework - mock_core_data = { - KEY_CORE: { - KEY_TARGET_PLATFORM: "esp32", - KEY_TARGET_FRAMEWORK: "arduino", - } - } - - with patch("esphome.config_helpers.CORE.data", mock_core_data): - excluded = filter_func() - # ESP32 Arduino should exclude ESP8266 and HOST files - assert "logger_esp8266.cpp" in excluded - assert "logger_host.cpp" in excluded - # But not ESP32 or common files - assert "logger_esp32.cpp" not in excluded - assert "logger_common.cpp" not in excluded - - # Test case 2: Host platform + # Test Host platform mock_core_data = { KEY_CORE: { KEY_TARGET_PLATFORM: "host", @@ -66,7 +89,19 @@ def test_filter_source_files_from_platform(): assert "logger_host.cpp" not in excluded assert "logger_common.cpp" not in excluded - # Test case 3: Missing platform/framework data + +def test_filter_source_files_from_platform_handles_missing_data(): + """Test that filter_source_files_from_platform returns empty list when platform/framework data is missing.""" + # Define test file mappings + files_map = { + "logger_esp32.cpp": {PlatformFramework.ESP32_ARDUINO}, + "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, + } + + # Create the filter function + filter_func = filter_source_files_from_platform(files_map) + + # Test case: Missing platform/framework data mock_core_data = {KEY_CORE: {}} with patch("esphome.config_helpers.CORE.data", mock_core_data): diff --git a/tests/unit_tests/test_loader.py b/tests/unit_tests/test_loader.py index 24c8464aa4..c6d4c4aef0 100644 --- a/tests/unit_tests/test_loader.py +++ b/tests/unit_tests/test_loader.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch from esphome.loader import ComponentManifest -def test_component_manifest_resources_with_filter_source_files(): +def test_component_manifest_resources_with_filter_source_files() -> None: """Test that ComponentManifest.resources correctly filters out excluded files.""" # Create a mock module with FILTER_SOURCE_FILES function mock_module = MagicMock() @@ -19,7 +19,7 @@ def test_component_manifest_resources_with_filter_source_files(): manifest = ComponentManifest(mock_module) # Mock the files in the package - def create_mock_file(filename): + def create_mock_file(filename: str) -> MagicMock: mock_file = MagicMock() mock_file.name = filename mock_file.is_file.return_value = True From 03380a6ecd13700f87ef796f5d767b1ddc972a9e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 13:51:51 -0500 Subject: [PATCH 11/15] some tests --- tests/unit_tests/test_config_helpers.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/unit_tests/test_config_helpers.py b/tests/unit_tests/test_config_helpers.py index c1f8c2bef8..f4649a77f4 100644 --- a/tests/unit_tests/test_config_helpers.py +++ b/tests/unit_tests/test_config_helpers.py @@ -42,7 +42,7 @@ def test_filter_source_files_from_platform_esp32() -> None: } with patch("esphome.config_helpers.CORE.data", mock_core_data): - excluded = filter_func() + excluded: list[str] = filter_func() # ESP32 Arduino should exclude ESP8266 and HOST files assert "logger_esp8266.cpp" in excluded assert "logger_host.cpp" in excluded @@ -51,10 +51,10 @@ def test_filter_source_files_from_platform_esp32() -> None: assert "logger_common.cpp" not in excluded -def test_filter_source_files_from_platform_host(): +def test_filter_source_files_from_platform_host() -> None: """Test that filter_source_files_from_platform correctly filters files for HOST platform.""" # Define test file mappings - files_map = { + files_map: dict[str, set[PlatformFramework]] = { "logger_esp32.cpp": { PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF, @@ -70,10 +70,10 @@ def test_filter_source_files_from_platform_host(): } # Create the filter function - filter_func = filter_source_files_from_platform(files_map) + filter_func: Callable[[], list[str]] = filter_source_files_from_platform(files_map) # Test Host platform - mock_core_data = { + mock_core_data: dict[str, dict[str, str]] = { KEY_CORE: { KEY_TARGET_PLATFORM: "host", KEY_TARGET_FRAMEWORK: "host", # Framework.NATIVE is "host" @@ -81,7 +81,7 @@ def test_filter_source_files_from_platform_host(): } with patch("esphome.config_helpers.CORE.data", mock_core_data): - excluded = filter_func() + excluded: list[str] = filter_func() # Host should exclude ESP32 and ESP8266 files assert "logger_esp32.cpp" in excluded assert "logger_esp8266.cpp" in excluded @@ -90,21 +90,21 @@ def test_filter_source_files_from_platform_host(): assert "logger_common.cpp" not in excluded -def test_filter_source_files_from_platform_handles_missing_data(): +def test_filter_source_files_from_platform_handles_missing_data() -> None: """Test that filter_source_files_from_platform returns empty list when platform/framework data is missing.""" # Define test file mappings - files_map = { + files_map: dict[str, set[PlatformFramework]] = { "logger_esp32.cpp": {PlatformFramework.ESP32_ARDUINO}, "logger_host.cpp": {PlatformFramework.HOST_NATIVE}, } # Create the filter function - filter_func = filter_source_files_from_platform(files_map) + filter_func: Callable[[], list[str]] = filter_source_files_from_platform(files_map) # Test case: Missing platform/framework data - mock_core_data = {KEY_CORE: {}} + mock_core_data: dict[str, dict[str, str]] = {KEY_CORE: {}} with patch("esphome.config_helpers.CORE.data", mock_core_data): - excluded = filter_func() + excluded: list[str] = filter_func() # Should return empty list when platform/framework not set assert excluded == [] From 6af74302dc0dc4ca7e4f5f8735ee20ebc6ab37f8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 14:06:03 -0500 Subject: [PATCH 12/15] missed one --- esphome/components/wifi/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index cb98325427..61f37556ba 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -540,5 +540,6 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform( PlatformFramework.RTL87XX_ARDUINO, PlatformFramework.LN882X_ARDUINO, }, + "wifi_component_pico_w.cpp": {PlatformFramework.RP2040_ARDUINO}, } ) From 06dd731c78b6db5f2e05205e5f1286e1fc06769e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 14:10:20 -0500 Subject: [PATCH 13/15] preen --- esphome/components/libretiny/__init__.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index f641c1776d..149e5d1179 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -3,7 +3,6 @@ import logging from os.path import dirname, isfile, join import esphome.codegen as cg -from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_BOARD, @@ -21,7 +20,6 @@ from esphome.const import ( KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, - PlatformFramework, __version__, ) from esphome.core import CORE @@ -342,14 +340,3 @@ async def component_to_code(config): cg.add_platformio_option("custom_fw_version", __version__) await cg.register_component(var, config) - - -FILTER_SOURCE_FILES = filter_source_files_from_platform( - { - "gpio_arduino.cpp": { - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, - }, - } -) From 782d894801c5740ccac3377bb4dcbe5cebef083e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 14:18:05 -0500 Subject: [PATCH 14/15] preen --- esphome/components/api/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 2f1be28293..745a1bc58c 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -23,7 +23,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VARIABLES, ) -from esphome.core import coroutine_with_priority +from esphome.core import CORE, coroutine_with_priority DEPENDENCIES = ["network"] AUTO_LOAD = ["socket"] @@ -313,3 +313,13 @@ async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, arg @automation.register_condition("api.connected", APIConnectedCondition, {}) async def api_connected_to_code(config, condition_id, template_arg, args): return cg.new_Pvariable(condition_id, template_arg) + + +def FILTER_SOURCE_FILES() -> list[str]: + """Filter out api_pb2_dump.cpp when proto message dumping is not enabled.""" + # api_pb2_dump.cpp is only needed when HAS_PROTO_MESSAGE_DUMP is defined + # Check if HAS_PROTO_MESSAGE_DUMP is defined + if "HAS_PROTO_MESSAGE_DUMP" not in CORE.defines: + return ["api_pb2_dump.cpp"] + + return [] From 28a66d4bf08b6ba81b27d46e03b90d82939b2d1a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Jul 2025 14:18:17 -0500 Subject: [PATCH 15/15] preen --- esphome/components/api/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 745a1bc58c..e720ee349f 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -318,7 +318,8 @@ async def api_connected_to_code(config, condition_id, template_arg, args): def FILTER_SOURCE_FILES() -> list[str]: """Filter out api_pb2_dump.cpp when proto message dumping is not enabled.""" # api_pb2_dump.cpp is only needed when HAS_PROTO_MESSAGE_DUMP is defined - # Check if HAS_PROTO_MESSAGE_DUMP is defined + # This is a particularly large file that still needs to be opened and read + # all the way to the end even when ifdef'd out if "HAS_PROTO_MESSAGE_DUMP" not in CORE.defines: return ["api_pb2_dump.cpp"]