From d1276dc6df07f4af76cba228c063c3faea6c132b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 2 Sep 2025 16:41:50 -0500 Subject: [PATCH] [core] Replace magic coroutine priority numbers with self-documenting CoroPriority enum (#10518) --- .../alarm_control_panel/__init__.py | 4 +- esphome/components/api/__init__.py | 4 +- esphome/components/async_tcp/__init__.py | 4 +- esphome/components/audio_adc/__init__.py | 4 +- esphome/components/audio_dac/__init__.py | 4 +- esphome/components/binary_sensor/__init__.py | 4 +- esphome/components/button/__init__.py | 4 +- esphome/components/captive_portal/__init__.py | 4 +- esphome/components/climate/__init__.py | 4 +- esphome/components/cover/__init__.py | 4 +- esphome/components/datetime/__init__.py | 4 +- esphome/components/display/__init__.py | 4 +- .../components/esp32_ble_tracker/__init__.py | 4 +- esphome/components/esp8266/__init__.py | 4 +- esphome/components/esp8266/gpio.py | 4 +- esphome/components/esphome/ota/__init__.py | 4 +- esphome/components/ethernet/__init__.py | 9 +- esphome/components/event/__init__.py | 4 +- esphome/components/fan/__init__.py | 4 +- esphome/components/globals/__init__.py | 4 +- .../components/http_request/ota/__init__.py | 4 +- esphome/components/i2c/__init__.py | 4 +- esphome/components/json/__init__.py | 4 +- esphome/components/light/__init__.py | 4 +- esphome/components/lock/__init__.py | 4 +- esphome/components/logger/__init__.py | 4 +- esphome/components/mdns/__init__.py | 4 +- esphome/components/media_player/__init__.py | 4 +- esphome/components/microphone/__init__.py | 4 +- esphome/components/mqtt/__init__.py | 4 +- esphome/components/network/__init__.py | 4 +- esphome/components/nrf52/__init__.py | 6 +- esphome/components/number/__init__.py | 4 +- esphome/components/ota/__init__.py | 4 +- esphome/components/rp2040/__init__.py | 4 +- esphome/components/safe_mode/__init__.py | 4 +- esphome/components/select/__init__.py | 4 +- esphome/components/sensor/__init__.py | 4 +- esphome/components/speaker/__init__.py | 4 +- esphome/components/spi/__init__.py | 4 +- esphome/components/status_led/__init__.py | 4 +- esphome/components/stepper/__init__.py | 4 +- esphome/components/switch/__init__.py | 4 +- esphome/components/text/__init__.py | 4 +- esphome/components/text_sensor/__init__.py | 4 +- esphome/components/time/__init__.py | 4 +- esphome/components/touchscreen/__init__.py | 4 +- esphome/components/update/__init__.py | 4 +- esphome/components/valve/__init__.py | 4 +- esphome/components/web_server/__init__.py | 4 +- esphome/components/web_server/ota/__init__.py | 4 +- .../components/web_server_base/__init__.py | 4 +- esphome/components/wifi/__init__.py | 4 +- esphome/core/__init__.py | 1 + esphome/core/config.py | 14 +- esphome/coroutine.py | 83 ++++++- tests/unit_tests/test_coroutine.py | 204 ++++++++++++++++++ 57 files changed, 404 insertions(+), 117 deletions(-) create mode 100644 tests/unit_tests/test_coroutine.py diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 058e061d1e..174a9d9e0a 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -13,7 +13,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_WEB_SERVER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -345,6 +345,6 @@ async def alarm_control_panel_is_armed_to_code( return cg.new_Pvariable(condition_id, template_arg, paren) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(alarm_control_panel_ns.using) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 2672ea1edb..5fb84d3c21 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -24,7 +24,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VARIABLES, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority DOMAIN = "api" DEPENDENCIES = ["network"] @@ -134,7 +134,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(40.0) +@coroutine_with_priority(CoroPriority.WEB) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 942d5bc8e5..f2d8895b39 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -8,7 +8,7 @@ from esphome.const import ( PLATFORM_LN882X, PLATFORM_RTL87XX, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] @@ -27,7 +27,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(200.0) +@coroutine_with_priority(CoroPriority.NETWORK_TRANSPORT) async def to_code(config): if CORE.is_esp32 or CORE.is_libretiny: # https://github.com/ESP32Async/AsyncTCP diff --git a/esphome/components/audio_adc/__init__.py b/esphome/components/audio_adc/__init__.py index dd3c958821..2f95a039f5 100644 --- a/esphome/components/audio_adc/__init__.py +++ b/esphome/components/audio_adc/__init__.py @@ -2,7 +2,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_MIC_GAIN -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority CODEOWNERS = ["@kbx81"] IS_PLATFORM_COMPONENT = True @@ -35,7 +35,7 @@ async def audio_adc_set_mic_gain_to_code(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_define("USE_AUDIO_ADC") cg.add_global(audio_adc_ns.using) diff --git a/esphome/components/audio_dac/__init__.py b/esphome/components/audio_dac/__init__.py index 978ed195bd..92e6cb18fa 100644 --- a/esphome/components/audio_dac/__init__.py +++ b/esphome/components/audio_dac/__init__.py @@ -3,7 +3,7 @@ from esphome.automation import maybe_simple_id import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_VOLUME -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority CODEOWNERS = ["@kbx81"] IS_PLATFORM_COMPONENT = True @@ -51,7 +51,7 @@ async def audio_dac_set_volume_to_code(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_define("USE_AUDIO_DAC") cg.add_global(audio_dac_ns.using) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index b56fde1ffd..6aa97d6e05 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -59,7 +59,7 @@ from esphome.const import ( DEVICE_CLASS_VIBRATION, DEVICE_CLASS_WINDOW, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass from esphome.util import Registry @@ -652,7 +652,7 @@ async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args) return cg.new_Pvariable(condition_id, template_arg, paren, False) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(binary_sensor_ns.using) diff --git a/esphome/components/button/__init__.py b/esphome/components/button/__init__.py index a23958989e..e1ac875cb0 100644 --- a/esphome/components/button/__init__.py +++ b/esphome/components/button/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( DEVICE_CLASS_RESTART, DEVICE_CLASS_UPDATE, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -134,6 +134,6 @@ async def button_press_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg, paren) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(button_ns.using) diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index cd69b67c78..39cafc7cb4 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -10,7 +10,7 @@ from esphome.const import ( PLATFORM_LN882X, PLATFORM_RTL87XX, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority AUTO_LOAD = ["web_server_base", "ota.web_server"] DEPENDENCIES = ["wifi"] @@ -40,7 +40,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(64.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 4af3a619b5..c0c33d7242 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -47,7 +47,7 @@ from esphome.const import ( CONF_VISUAL, CONF_WEB_SERVER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -517,6 +517,6 @@ async def climate_control_to_code(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(climate_ns.using) diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 383cfaf8fb..bec6dcbdac 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -32,7 +32,7 @@ from esphome.const import ( DEVICE_CLASS_SHUTTER, DEVICE_CLASS_WINDOW, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -263,6 +263,6 @@ async def cover_control_to_code(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(cover_ns.using) diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 1d84b75f26..602db3827a 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -21,7 +21,7 @@ from esphome.const import ( CONF_WEB_SERVER, CONF_YEAR, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -172,7 +172,7 @@ async def new_datetime(config, *args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(datetime_ns.using) diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index e55afcebbf..ccbeedcd2f 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -15,7 +15,7 @@ from esphome.const import ( CONF_UPDATE_INTERVAL, SCHEDULER_DONT_RUN, ) -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority IS_PLATFORM_COMPONENT = True @@ -218,7 +218,7 @@ async def display_is_displaying_page_to_code(config, condition_id, template_arg, return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(display_ns.using) cg.add_define("USE_DISPLAY") diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 9ad2f3b25f..558143b007 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -30,7 +30,7 @@ from esphome.const import ( CONF_SERVICE_UUID, CONF_TRIGGER_ID, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.enum import StrEnum from esphome.types import ConfigType @@ -368,7 +368,7 @@ async def to_code(config): # This needs to be run as a job with very low priority so that all components have # chance to call register_ble_tracker and register_client before the list is checked # and added to the global defines list. -@coroutine_with_priority(-1000) +@coroutine_with_priority(CoroPriority.FINAL) async def _add_ble_features(): # Add feature-specific defines based on what's needed if BLEFeatures.ESP_BT_DEVICE in _required_features: diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 33a4149571..b85314214e 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( PLATFORM_ESP8266, ThreadModel, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.helpers import copy_file_if_changed from .boards import BOARDS, ESP8266_LD_SCRIPTS @@ -176,7 +176,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(1000) +@coroutine_with_priority(CoroPriority.PLATFORM) async def to_code(config): cg.add(esp8266_ns.setup_preferences()) diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py index 050efaacae..2bc2291117 100644 --- a/esphome/components/esp8266/gpio.py +++ b/esphome/components/esp8266/gpio.py @@ -17,7 +17,7 @@ from esphome.const import ( CONF_PULLUP, PLATFORM_ESP8266, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from . import boards from .const import KEY_BOARD, KEY_ESP8266, KEY_PIN_INITIAL_STATES, esp8266_ns @@ -188,7 +188,7 @@ async def esp8266_pin_to_code(config): return var -@coroutine_with_priority(-999.0) +@coroutine_with_priority(CoroPriority.WORKAROUNDS) async def add_pin_initial_states_array(): # Add includes at the very end, so that they override everything initial_states: list[PinInitialState] = CORE.data[KEY_ESP8266][ diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 9facdc3bc6..7b579501ed 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -16,7 +16,7 @@ from esphome.const import ( CONF_SAFE_MODE, CONF_VERSION, ) -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority import esphome.final_validate as fv _LOGGER = logging.getLogger(__name__) @@ -121,7 +121,7 @@ CONFIG_SCHEMA = ( FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate -@coroutine_with_priority(52.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_port(config[CONF_PORT])) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 7a412a643d..a26238553c 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -38,7 +38,12 @@ from esphome.const import ( KEY_CORE, KEY_FRAMEWORK_VERSION, ) -from esphome.core import CORE, TimePeriodMilliseconds, coroutine_with_priority +from esphome.core import ( + CORE, + CoroPriority, + TimePeriodMilliseconds, + coroutine_with_priority, +) import esphome.final_validate as fv CONFLICTS_WITH = ["wifi"] @@ -289,7 +294,7 @@ def phy_register(address: int, value: int, page: int): ) -@coroutine_with_priority(60.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 1948570ecd..449cc48625 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( DEVICE_CLASS_EMPTY, DEVICE_CLASS_MOTION, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -143,6 +143,6 @@ async def event_fire_to_code(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(event_ns.using) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 3fb217a24e..da8bf850c7 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -31,7 +31,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_WEB_SERVER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity IS_PLATFORM_COMPONENT = True @@ -398,6 +398,6 @@ async def fan_is_on_off_to_code(config, condition_id, template_arg, args): return cg.new_Pvariable(condition_id, template_arg, paren) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(fan_ns.using) diff --git a/esphome/components/globals/__init__.py b/esphome/components/globals/__init__.py index e4bce99b0b..633ccea66b 100644 --- a/esphome/components/globals/__init__.py +++ b/esphome/components/globals/__init__.py @@ -8,7 +8,7 @@ from esphome.const import ( CONF_TYPE, CONF_VALUE, ) -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] globals_ns = cg.esphome_ns.namespace("globals") @@ -35,7 +35,7 @@ CONFIG_SCHEMA = cv.Schema( # Run with low priority so that namespaces are registered first -@coroutine_with_priority(-100.0) +@coroutine_with_priority(CoroPriority.LATE) async def to_code(config): type_ = cg.RawExpression(config[CONF_TYPE]) restore = config[CONF_RESTORE_VALUE] diff --git a/esphome/components/http_request/ota/__init__.py b/esphome/components/http_request/ota/__init__.py index a3f6d5840c..fd542e594a 100644 --- a/esphome/components/http_request/ota/__init__.py +++ b/esphome/components/http_request/ota/__init__.py @@ -3,7 +3,7 @@ import esphome.codegen as cg from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority from .. import CONF_HTTP_REQUEST_ID, HttpRequestComponent, http_request_ns @@ -40,7 +40,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(52.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ota_to_code(var, config) diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 35b9fab9e4..3cfec1e94d 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -18,7 +18,7 @@ from esphome.const import ( PLATFORM_RP2040, PlatformFramework, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority import esphome.final_validate as fv LOGGER = logging.getLogger(__name__) @@ -74,7 +74,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(1.0) +@coroutine_with_priority(CoroPriority.BUS) async def to_code(config): cg.add_global(i2c_ns.using) cg.add_define("USE_I2C") diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 87aa823c0d..4cd737c60d 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] json_ns = cg.esphome_ns.namespace("json") @@ -10,7 +10,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(1.0) +@coroutine_with_priority(CoroPriority.BUS) async def to_code(config): cg.add_library("bblanchon/ArduinoJson", "7.4.2") cg.add_define("USE_JSON") diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index fa39721ee2..f1089ad64f 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -37,7 +37,7 @@ from esphome.const import ( CONF_WEB_SERVER, CONF_WHITE, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -283,6 +283,6 @@ async def new_light(config, *args): return output_var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(light_ns.using) diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index 7977efd264..04c1586ddd 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -13,7 +13,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_WEB_SERVER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -155,6 +155,6 @@ async def lock_is_off_to_code(config, condition_id, template_arg, args): return cg.new_Pvariable(condition_id, template_arg, paren, False) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(lock_ns.using) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index d8c95d75f2..2865355278 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -51,7 +51,7 @@ from esphome.const import ( PLATFORM_RTL87XX, PlatformFramework, ) -from esphome.core import CORE, Lambda, coroutine_with_priority +from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") @@ -275,7 +275,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(90.0) +@coroutine_with_priority(CoroPriority.DIAGNOSTICS) async def to_code(config): baud_rate = config[CONF_BAUD_RATE] level = config[CONF_LEVEL] diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 469fe8ada6..a21ef9d97b 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -11,7 +11,7 @@ from esphome.const import ( CONF_SERVICES, PlatformFramework, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] @@ -72,7 +72,7 @@ def mdns_service( ) -@coroutine_with_priority(55.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): if config[CONF_DISABLED] is True: return diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index d288e70cba..70c7cf7a56 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( ) from esphome.core import CORE from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity -from esphome.coroutine import coroutine_with_priority +from esphome.coroutine import CoroPriority, coroutine_with_priority from esphome.cpp_generator import MockObjClass CODEOWNERS = ["@jesserockz"] @@ -303,7 +303,7 @@ async def media_player_volume_set_action(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(media_player_ns.using) cg.add_define("USE_MEDIA_PLAYER") diff --git a/esphome/components/microphone/__init__.py b/esphome/components/microphone/__init__.py index 29bdcfa3f3..1fc0df88a3 100644 --- a/esphome/components/microphone/__init__.py +++ b/esphome/components/microphone/__init__.py @@ -12,7 +12,7 @@ from esphome.const import ( CONF_TRIGGER_ID, ) from esphome.core import CORE -from esphome.coroutine import coroutine_with_priority +from esphome.coroutine import CoroPriority, coroutine_with_priority AUTO_LOAD = ["audio"] CODEOWNERS = ["@jesserockz", "@kahrendt"] @@ -213,7 +213,7 @@ automation.register_condition( )(microphone_action) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(microphone_ns.using) cg.add_define("USE_MICROPHONE") diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 52d3181780..814fb566d4 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -57,7 +57,7 @@ from esphome.const import ( PLATFORM_ESP8266, PlatformFramework, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority DEPENDENCIES = ["network"] @@ -321,7 +321,7 @@ def exp_mqtt_message(config): ) -@coroutine_with_priority(40.0) +@coroutine_with_priority(CoroPriority.WEB) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index b04fca7a1c..9679961b15 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option import esphome.config_validation as cv from esphome.const import CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["mdns"] @@ -36,7 +36,7 @@ CONFIG_SCHEMA = cv.Schema( ) -@coroutine_with_priority(201.0) +@coroutine_with_priority(CoroPriority.NETWORK) async def to_code(config): cg.add_define("USE_NETWORK") if CORE.using_arduino and CORE.is_esp32: diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index a4e387b77a..84e505a90a 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -30,7 +30,7 @@ from esphome.const import ( PLATFORM_NRF52, ThreadModel, ) -from esphome.core import CORE, EsphomeError, coroutine_with_priority +from esphome.core import CORE, CoroPriority, EsphomeError, coroutine_with_priority from esphome.storage_json import StorageJSON from esphome.types import ConfigType @@ -132,7 +132,7 @@ def _final_validate(config): FINAL_VALIDATE_SCHEMA = _final_validate -@coroutine_with_priority(1000) +@coroutine_with_priority(CoroPriority.PLATFORM) async def to_code(config: ConfigType) -> None: """Convert the configuration to code.""" cg.add_platformio_option("board", config[CONF_BOARD]) @@ -170,7 +170,7 @@ async def to_code(config: ConfigType) -> None: CORE.add_job(_dfu_to_code, dfu_config) -@coroutine_with_priority(90) +@coroutine_with_priority(CoroPriority.DIAGNOSTICS) async def _dfu_to_code(dfu_config): cg.add_define("USE_NRF52_DFU") var = cg.new_Pvariable(dfu_config[CONF_ID]) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 4a83d5fc5f..c2cad2f7f1 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -76,7 +76,7 @@ from esphome.const import ( DEVICE_CLASS_WIND_DIRECTION, DEVICE_CLASS_WIND_SPEED, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -321,7 +321,7 @@ async def number_in_range_to_code(config, condition_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(number_ns.using) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 4d5b8a61e2..cf814fb1ee 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -10,7 +10,7 @@ from esphome.const import ( CONF_TRIGGER_ID, PlatformFramework, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5", "safe_mode"] @@ -82,7 +82,7 @@ BASE_OTA_SCHEMA = cv.Schema( ) -@coroutine_with_priority(54.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): cg.add_define("USE_OTA") diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 46eabb5325..1ec38e0159 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -18,7 +18,7 @@ from esphome.const import ( PLATFORM_RP2040, ThreadModel, ) -from esphome.core import CORE, EsphomeError, coroutine_with_priority +from esphome.core import CORE, CoroPriority, EsphomeError, coroutine_with_priority from esphome.helpers import copy_file_if_changed, mkdir_p, read_file, write_file from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns @@ -159,7 +159,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(1000) +@coroutine_with_priority(CoroPriority.PLATFORM) async def to_code(config): cg.add(rp2040_ns.setup_preferences()) diff --git a/esphome/components/safe_mode/__init__.py b/esphome/components/safe_mode/__init__.py index 991747b089..9944d71722 100644 --- a/esphome/components/safe_mode/__init__.py +++ b/esphome/components/safe_mode/__init__.py @@ -10,7 +10,7 @@ from esphome.const import ( CONF_TRIGGER_ID, KEY_PAST_SAFE_MODE, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.cpp_generator import RawExpression CODEOWNERS = ["@paulmonigatti", "@jsuanet", "@kbx81"] @@ -53,7 +53,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(50.0) +@coroutine_with_priority(CoroPriority.APPLICATION) async def to_code(config): if not config[CONF_DISABLED]: var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index 756e98c906..c7146df9fb 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -16,7 +16,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_WEB_SERVER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -124,7 +124,7 @@ async def new_select(config, *args, options: list[str]): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(select_ns.using) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 277718e46c..fe9822b3ca 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -101,7 +101,7 @@ from esphome.const import ( DEVICE_CLASS_WIND_SPEED, ENTITY_CATEGORY_CONFIG, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass from esphome.util import Registry @@ -1142,6 +1142,6 @@ def _lstsq(a, b): return _mat_dot(_mat_dot(x, a_t), b) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(sensor_ns.using) diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 2ac1ca0cb9..5f1ba94ee6 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -4,7 +4,7 @@ from esphome.components import audio, audio_dac import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME from esphome.core import CORE -from esphome.coroutine import coroutine_with_priority +from esphome.coroutine import CoroPriority, coroutine_with_priority AUTO_LOAD = ["audio"] CODEOWNERS = ["@jesserockz", "@kahrendt"] @@ -138,7 +138,7 @@ async def speaker_mute_action_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg, paren) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(speaker_ns.using) cg.add_define("USE_SPEAKER") diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index a436bc6dab..894c6d1878 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -35,7 +35,7 @@ from esphome.const import ( PLATFORM_RP2040, PlatformFramework, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority import esphome.final_validate as fv CODEOWNERS = ["@esphome/core", "@clydebarrow"] @@ -351,7 +351,7 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(1.0) +@coroutine_with_priority(CoroPriority.BUS) async def to_code(configs): cg.add_define("USE_SPI") cg.add_global(spi_ns.using) diff --git a/esphome/components/status_led/__init__.py b/esphome/components/status_led/__init__.py index b299ae7ff7..b0fce37126 100644 --- a/esphome/components/status_led/__init__.py +++ b/esphome/components/status_led/__init__.py @@ -2,7 +2,7 @@ from esphome import pins import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PIN -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority status_led_ns = cg.esphome_ns.namespace("status_led") StatusLED = status_led_ns.class_("StatusLED", cg.Component) @@ -15,7 +15,7 @@ CONFIG_SCHEMA = cv.Schema( ).extend(cv.COMPONENT_SCHEMA) -@coroutine_with_priority(80.0) +@coroutine_with_priority(CoroPriority.STATUS) async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) rhs = StatusLED.new(pin) diff --git a/esphome/components/stepper/__init__.py b/esphome/components/stepper/__init__.py index c234388e7e..62bc71f2d1 100644 --- a/esphome/components/stepper/__init__.py +++ b/esphome/components/stepper/__init__.py @@ -10,7 +10,7 @@ from esphome.const import ( CONF_SPEED, CONF_TARGET, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority IS_PLATFORM_COMPONENT = True @@ -178,6 +178,6 @@ async def stepper_set_deceleration_to_code(config, action_id, template_arg, args return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(stepper_ns.using) diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index f495dbc0b4..0e7b35b373 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -21,7 +21,7 @@ from esphome.const import ( DEVICE_CLASS_OUTLET, DEVICE_CLASS_SWITCH, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -230,6 +230,6 @@ async def switch_is_off_to_code(config, condition_id, template_arg, args): return cg.new_Pvariable(condition_id, template_arg, paren, False) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(switch_ns.using) diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index aa831d1f06..1baacc239f 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -13,7 +13,7 @@ from esphome.const import ( CONF_VALUE, CONF_WEB_SERVER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -149,7 +149,7 @@ async def new_text( return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(text_ns.using) diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index e4aa701a7b..f7b3b5c55e 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -20,7 +20,7 @@ from esphome.const import ( DEVICE_CLASS_EMPTY, DEVICE_CLASS_TIMESTAMP, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass from esphome.util import Registry @@ -230,7 +230,7 @@ async def new_text_sensor(config, *args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(text_sensor_ns.using) diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index a38ad4eae3..a20d79b857 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -26,7 +26,7 @@ from esphome.const import ( CONF_TIMEZONE, CONF_TRIGGER_ID, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority _LOGGER = logging.getLogger(__name__) @@ -340,7 +340,7 @@ async def register_time(time_var, config): await setup_time_core_(time_var, config) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): if CORE.using_zephyr: zephyr_add_prj_conf("POSIX_CLOCK", True) diff --git a/esphome/components/touchscreen/__init__.py b/esphome/components/touchscreen/__init__.py index 01a271a34e..4a5c03ace4 100644 --- a/esphome/components/touchscreen/__init__.py +++ b/esphome/components/touchscreen/__init__.py @@ -13,7 +13,7 @@ from esphome.const import ( CONF_SWAP_XY, CONF_TRANSFORM, ) -from esphome.core import coroutine_with_priority +from esphome.core import CoroPriority, coroutine_with_priority CODEOWNERS = ["@jesserockz", "@nielsnl68"] DEPENDENCIES = ["display"] @@ -152,7 +152,7 @@ async def register_touchscreen(var, config): ) -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(touchscreen_ns.using) cg.add_define("USE_TOUCHSCREEN") diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index 50d8aaf139..35fc4eaf1d 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( DEVICE_CLASS_FIRMWARE, ENTITY_CATEGORY_CONFIG, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -124,7 +124,7 @@ async def new_update(config): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(update_ns.using) diff --git a/esphome/components/valve/__init__.py b/esphome/components/valve/__init__.py index 8185bd6ea2..6f31fc3a20 100644 --- a/esphome/components/valve/__init__.py +++ b/esphome/components/valve/__init__.py @@ -21,7 +21,7 @@ from esphome.const import ( DEVICE_CLASS_GAS, DEVICE_CLASS_WATER, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity from esphome.cpp_generator import MockObjClass @@ -233,6 +233,6 @@ async def valve_control_to_code(config, action_id, template_arg, args): return var -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config): cg.add_global(valve_ns.using) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index be193bbab8..288d928e80 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -31,7 +31,7 @@ from esphome.const import ( PLATFORM_LN882X, PLATFORM_RTL87XX, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority import esphome.final_validate as fv from esphome.types import ConfigType @@ -269,7 +269,7 @@ def add_resource_as_progmem( cg.add_global(cg.RawExpression(size_t)) -@coroutine_with_priority(40.0) +@coroutine_with_priority(CoroPriority.WEB) async def to_code(config): paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) diff --git a/esphome/components/web_server/ota/__init__.py b/esphome/components/web_server/ota/__init__.py index 3af14fd453..3ec7e65e1d 100644 --- a/esphome/components/web_server/ota/__init__.py +++ b/esphome/components/web_server/ota/__init__.py @@ -3,7 +3,7 @@ from esphome.components.esp32 import add_idf_component from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code import esphome.config_validation as cv from esphome.const import CONF_ID -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network", "web_server_base"] @@ -22,7 +22,7 @@ CONFIG_SCHEMA = ( ) -@coroutine_with_priority(52.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await ota_to_code(var, config) diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 50ae6b92fa..8add2f051f 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] @@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.Schema( ) -@coroutine_with_priority(65.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 4013e8f400..7943911021 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -44,7 +44,7 @@ from esphome.const import ( CONF_USERNAME, PlatformFramework, ) -from esphome.core import CORE, HexInt, coroutine_with_priority +from esphome.core import CORE, CoroPriority, HexInt, coroutine_with_priority import esphome.final_validate as fv from . import wpa2_eap @@ -370,7 +370,7 @@ def wifi_network(config, ap, static_ip): return ap -@coroutine_with_priority(60.0) +@coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 8a9630735e..89e3eff7d8 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -29,6 +29,7 @@ from esphome.const import ( # pylint: disable=unused-import from esphome.coroutine import ( # noqa: F401 + CoroPriority, FakeAwaitable as _FakeAwaitable, FakeEventLoop as _FakeEventLoop, coroutine, diff --git a/esphome/core/config.py b/esphome/core/config.py index b6ff1d8afd..96b9e23861 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -39,7 +39,7 @@ from esphome.const import ( PlatformFramework, __version__ as ESPHOME_VERSION, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.helpers import ( copy_file_if_changed, fnv1a_32bit_hash, @@ -359,7 +359,7 @@ ARDUINO_GLUE_CODE = """\ """ -@coroutine_with_priority(-999.0) +@coroutine_with_priority(CoroPriority.WORKAROUNDS) async def add_arduino_global_workaround(): # The Arduino framework defined these itself in the global # namespace. For the esphome codebase that is not a problem, @@ -376,7 +376,7 @@ async def add_arduino_global_workaround(): cg.add_global(cg.RawStatement(line)) -@coroutine_with_priority(-1000.0) +@coroutine_with_priority(CoroPriority.FINAL) async def add_includes(includes): # Add includes at the very end, so that the included files can access global variables for include in includes: @@ -392,7 +392,7 @@ async def add_includes(includes): include_file(path, basename) -@coroutine_with_priority(-1000.0) +@coroutine_with_priority(CoroPriority.FINAL) async def _add_platformio_options(pio_options): # Add includes at the very end, so that they override everything for key, val in pio_options.items(): @@ -401,7 +401,7 @@ async def _add_platformio_options(pio_options): cg.add_platformio_option(key, val) -@coroutine_with_priority(30.0) +@coroutine_with_priority(CoroPriority.AUTOMATION) async def _add_automations(config): for conf in config.get(CONF_ON_BOOT, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf.get(CONF_PRIORITY)) @@ -423,7 +423,7 @@ async def _add_automations(config): DATETIME_SUBTYPES = {"date", "time", "datetime"} -@coroutine_with_priority(-1000.0) +@coroutine_with_priority(CoroPriority.FINAL) async def _add_platform_defines() -> None: # Generate compile-time defines for platforms that have actual entities # Only add USE_* and count defines when there are entities @@ -442,7 +442,7 @@ async def _add_platform_defines() -> None: cg.add_define(f"USE_{platform_name.upper()}") -@coroutine_with_priority(100.0) +@coroutine_with_priority(CoroPriority.CORE) async def to_code(config: ConfigType) -> None: cg.add_global(cg.global_ns.namespace("esphome").using) # These can be used by user lambdas, put them to default scope diff --git a/esphome/coroutine.py b/esphome/coroutine.py index 8d952246f3..741a0c7c0c 100644 --- a/esphome/coroutine.py +++ b/esphome/coroutine.py @@ -42,7 +42,10 @@ Here everything is combined in `yield` expressions. You await other coroutines u the last `yield` expression defines what is returned. """ +from __future__ import annotations + from collections.abc import Awaitable, Callable, Generator, Iterator +import enum import functools import heapq import inspect @@ -53,6 +56,79 @@ from typing import Any _LOGGER = logging.getLogger(__name__) +class CoroPriority(enum.IntEnum): + """Execution priority stages for ESPHome code generation. + + Higher values run first. These stages ensure proper dependency + resolution during code generation. + """ + + # Platform initialization - must run first + # Examples: esp32, esp8266, rp2040 + PLATFORM = 1000 + + # Network infrastructure setup + # Examples: network (201) + NETWORK = 201 + + # Network transport layer + # Examples: async_tcp (200) + NETWORK_TRANSPORT = 200 + + # Core system components + # Examples: esphome core, most entity base components (cover, update, datetime, + # valve, alarm_control_panel, lock, event, binary_sensor, button, climate, fan, + # light, media_player, number, select, sensor, switch, text_sensor, text), + # microphone, speaker, audio_dac, touchscreen, stepper + CORE = 100 + + # Diagnostic and debugging systems + # Examples: logger (90) + DIAGNOSTICS = 90 + + # Status and monitoring systems + # Examples: status_led (80) + STATUS = 80 + + # Communication protocols and services + # Examples: web_server_base (65), captive_portal (64), wifi (60), ethernet (60), + # mdns (55), ota_updates (54), web_server_ota (52) + COMMUNICATION = 60 + + # Application-level services + # Examples: safe_mode (50) + APPLICATION = 50 + + # Web and UI services + # Examples: web_server (40) + WEB = 40 + + # Automations and user logic + # Examples: esphome core automations (30) + AUTOMATION = 30 + + # Bus and peripheral setup + # Examples: i2c (1) + BUS = 1 + + # Standard component priority (default) + # Components without explicit priority run at 0 + COMPONENT = 0 + + # Components that need others to be registered first + # Examples: globals (-100) + LATE = -100 + + # Platform-specific workarounds and fixes + # Examples: add_arduino_global_workaround (-999), esp8266 pin states (-999) + WORKAROUNDS = -999 + + # Final setup that requires all components to be registered + # Examples: add_includes, _add_platformio_options, _add_platform_defines (all -1000), + # esp32_ble_tracker feature defines (-1000) + FINAL = -1000 + + def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]: """Decorator to apply to methods to convert them to ESPHome coroutines.""" if getattr(func, "_esphome_coroutine", False): @@ -95,15 +171,16 @@ def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]: return coro -def coroutine_with_priority(priority: float): +def coroutine_with_priority(priority: float | CoroPriority): """Decorator to apply to functions to convert them to ESPHome coroutines. :param priority: priority with which to schedule the coroutine, higher priorities run first. + Can be a float or a CoroPriority enum value. """ def decorator(func): coro = coroutine(func) - coro.priority = priority + coro.priority = float(priority) return coro return decorator @@ -173,7 +250,7 @@ class _Task: self.iterator = iterator self.original_function = original_function - def with_priority(self, priority: float) -> "_Task": + def with_priority(self, priority: float) -> _Task: return _Task(priority, self.id_number, self.iterator, self.original_function) @property diff --git a/tests/unit_tests/test_coroutine.py b/tests/unit_tests/test_coroutine.py new file mode 100644 index 0000000000..138b08edb5 --- /dev/null +++ b/tests/unit_tests/test_coroutine.py @@ -0,0 +1,204 @@ +"""Tests for the coroutine module.""" + +import pytest + +from esphome.coroutine import CoroPriority, FakeEventLoop, coroutine_with_priority + + +def test_coro_priority_enum_values() -> None: + """Test that CoroPriority enum values match expected priorities.""" + assert CoroPriority.PLATFORM == 1000 + assert CoroPriority.NETWORK == 201 + assert CoroPriority.NETWORK_TRANSPORT == 200 + assert CoroPriority.CORE == 100 + assert CoroPriority.DIAGNOSTICS == 90 + assert CoroPriority.STATUS == 80 + assert CoroPriority.COMMUNICATION == 60 + assert CoroPriority.APPLICATION == 50 + assert CoroPriority.WEB == 40 + assert CoroPriority.AUTOMATION == 30 + assert CoroPriority.BUS == 1 + assert CoroPriority.COMPONENT == 0 + assert CoroPriority.LATE == -100 + assert CoroPriority.WORKAROUNDS == -999 + assert CoroPriority.FINAL == -1000 + + +def test_coroutine_with_priority_accepts_float() -> None: + """Test that coroutine_with_priority accepts float values.""" + + @coroutine_with_priority(100.0) + def test_func() -> None: + pass + + assert hasattr(test_func, "priority") + assert test_func.priority == 100.0 + + +def test_coroutine_with_priority_accepts_enum() -> None: + """Test that coroutine_with_priority accepts CoroPriority enum values.""" + + @coroutine_with_priority(CoroPriority.CORE) + def test_func() -> None: + pass + + assert hasattr(test_func, "priority") + assert test_func.priority == 100.0 + + +def test_float_and_enum_are_interchangeable() -> None: + """Test that float and CoroPriority enum values produce the same priority.""" + + @coroutine_with_priority(100.0) + def func_with_float() -> None: + pass + + @coroutine_with_priority(CoroPriority.CORE) + def func_with_enum() -> None: + pass + + assert func_with_float.priority == func_with_enum.priority + assert func_with_float.priority == 100.0 + + +@pytest.mark.parametrize( + ("enum_value", "float_value"), + [ + (CoroPriority.PLATFORM, 1000.0), + (CoroPriority.NETWORK, 201.0), + (CoroPriority.NETWORK_TRANSPORT, 200.0), + (CoroPriority.CORE, 100.0), + (CoroPriority.DIAGNOSTICS, 90.0), + (CoroPriority.STATUS, 80.0), + (CoroPriority.COMMUNICATION, 60.0), + (CoroPriority.APPLICATION, 50.0), + (CoroPriority.WEB, 40.0), + (CoroPriority.AUTOMATION, 30.0), + (CoroPriority.BUS, 1.0), + (CoroPriority.COMPONENT, 0.0), + (CoroPriority.LATE, -100.0), + (CoroPriority.WORKAROUNDS, -999.0), + (CoroPriority.FINAL, -1000.0), + ], +) +def test_all_priority_values_are_interchangeable( + enum_value: CoroPriority, float_value: float +) -> None: + """Test that all CoroPriority values work correctly with coroutine_with_priority.""" + + @coroutine_with_priority(enum_value) + def func_with_enum() -> None: + pass + + @coroutine_with_priority(float_value) + def func_with_float() -> None: + pass + + assert func_with_enum.priority == float_value + assert func_with_float.priority == float_value + assert func_with_enum.priority == func_with_float.priority + + +def test_execution_order_with_enum_priorities() -> None: + """Test that execution order is correct when using enum priorities.""" + execution_order: list[str] = [] + + @coroutine_with_priority(CoroPriority.PLATFORM) + async def platform_func() -> None: + execution_order.append("platform") + + @coroutine_with_priority(CoroPriority.CORE) + async def core_func() -> None: + execution_order.append("core") + + @coroutine_with_priority(CoroPriority.FINAL) + async def final_func() -> None: + execution_order.append("final") + + # Create event loop and add jobs + loop = FakeEventLoop() + loop.add_job(platform_func) + loop.add_job(core_func) + loop.add_job(final_func) + + # Run all tasks + loop.flush_tasks() + + # Check execution order (higher priority runs first) + assert execution_order == ["platform", "core", "final"] + + +def test_mixed_float_and_enum_priorities() -> None: + """Test that mixing float and enum priorities works correctly.""" + execution_order: list[str] = [] + + @coroutine_with_priority(1000.0) # Same as PLATFORM + async def func1() -> None: + execution_order.append("func1") + + @coroutine_with_priority(CoroPriority.CORE) + async def func2() -> None: + execution_order.append("func2") + + @coroutine_with_priority(-1000.0) # Same as FINAL + async def func3() -> None: + execution_order.append("func3") + + # Create event loop and add jobs + loop = FakeEventLoop() + loop.add_job(func2) + loop.add_job(func3) + loop.add_job(func1) + + # Run all tasks + loop.flush_tasks() + + # Check execution order + assert execution_order == ["func1", "func2", "func3"] + + +def test_enum_priority_comparison() -> None: + """Test that enum priorities can be compared directly.""" + assert CoroPriority.PLATFORM > CoroPriority.NETWORK + assert CoroPriority.NETWORK > CoroPriority.NETWORK_TRANSPORT + assert CoroPriority.NETWORK_TRANSPORT > CoroPriority.CORE + assert CoroPriority.CORE > CoroPriority.DIAGNOSTICS + assert CoroPriority.DIAGNOSTICS > CoroPriority.STATUS + assert CoroPriority.STATUS > CoroPriority.COMMUNICATION + assert CoroPriority.COMMUNICATION > CoroPriority.APPLICATION + assert CoroPriority.APPLICATION > CoroPriority.WEB + assert CoroPriority.WEB > CoroPriority.AUTOMATION + assert CoroPriority.AUTOMATION > CoroPriority.BUS + assert CoroPriority.BUS > CoroPriority.COMPONENT + assert CoroPriority.COMPONENT > CoroPriority.LATE + assert CoroPriority.LATE > CoroPriority.WORKAROUNDS + assert CoroPriority.WORKAROUNDS > CoroPriority.FINAL + + +def test_custom_priority_between_enum_values() -> None: + """Test that custom float priorities between enum values work correctly.""" + execution_order: list[str] = [] + + @coroutine_with_priority(CoroPriority.CORE) # 100 + async def core_func() -> None: + execution_order.append("core") + + @coroutine_with_priority(95.0) # Between CORE and DIAGNOSTICS + async def custom_func() -> None: + execution_order.append("custom") + + @coroutine_with_priority(CoroPriority.DIAGNOSTICS) # 90 + async def diag_func() -> None: + execution_order.append("diagnostics") + + # Create event loop and add jobs + loop = FakeEventLoop() + loop.add_job(diag_func) + loop.add_job(core_func) + loop.add_job(custom_func) + + # Run all tasks + loop.flush_tasks() + + # Check execution order + assert execution_order == ["core", "custom", "diagnostics"]