mirror of
https://github.com/esphome/esphome.git
synced 2025-09-05 21:02:20 +01:00
Merge branch 'scheduler_pool_v2' into integration
This commit is contained in:
@@ -13,7 +13,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_WEB_SERVER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
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)
|
return cg.new_Pvariable(condition_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(alarm_control_panel_ns.using)
|
cg.add_global(alarm_control_panel_ns.using)
|
||||||
|
@@ -24,7 +24,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_VARIABLES,
|
CONF_VARIABLES,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
DOMAIN = "api"
|
DOMAIN = "api"
|
||||||
DEPENDENCIES = ["network"]
|
DEPENDENCIES = ["network"]
|
||||||
@@ -136,7 +136,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(40.0)
|
@coroutine_with_priority(CoroPriority.WEB)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
@@ -8,7 +8,7 @@ from esphome.const import (
|
|||||||
PLATFORM_LN882X,
|
PLATFORM_LN882X,
|
||||||
PLATFORM_RTL87XX,
|
PLATFORM_RTL87XX,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
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):
|
async def to_code(config):
|
||||||
if CORE.is_esp32 or CORE.is_libretiny:
|
if CORE.is_esp32 or CORE.is_libretiny:
|
||||||
# https://github.com/ESP32Async/AsyncTCP
|
# https://github.com/ESP32Async/AsyncTCP
|
||||||
|
@@ -2,7 +2,7 @@ from esphome import automation
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MIC_GAIN
|
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"]
|
CODEOWNERS = ["@kbx81"]
|
||||||
IS_PLATFORM_COMPONENT = True
|
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
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_define("USE_AUDIO_ADC")
|
cg.add_define("USE_AUDIO_ADC")
|
||||||
cg.add_global(audio_adc_ns.using)
|
cg.add_global(audio_adc_ns.using)
|
||||||
|
@@ -3,7 +3,7 @@ from esphome.automation import maybe_simple_id
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_VOLUME
|
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"]
|
CODEOWNERS = ["@kbx81"]
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
@@ -51,7 +51,7 @@ async def audio_dac_set_volume_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_define("USE_AUDIO_DAC")
|
cg.add_define("USE_AUDIO_DAC")
|
||||||
cg.add_global(audio_dac_ns.using)
|
cg.add_global(audio_dac_ns.using)
|
||||||
|
@@ -59,7 +59,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_VIBRATION,
|
DEVICE_CLASS_VIBRATION,
|
||||||
DEVICE_CLASS_WINDOW,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.util import Registry
|
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)
|
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):
|
async def to_code(config):
|
||||||
cg.add_global(binary_sensor_ns.using)
|
cg.add_global(binary_sensor_ns.using)
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_RESTART,
|
DEVICE_CLASS_RESTART,
|
||||||
DEVICE_CLASS_UPDATE,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
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)
|
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(button_ns.using)
|
cg.add_global(button_ns.using)
|
||||||
|
@@ -10,7 +10,7 @@ from esphome.const import (
|
|||||||
PLATFORM_LN882X,
|
PLATFORM_LN882X,
|
||||||
PLATFORM_RTL87XX,
|
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"]
|
AUTO_LOAD = ["web_server_base", "ota.web_server"]
|
||||||
DEPENDENCIES = ["wifi"]
|
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):
|
async def to_code(config):
|
||||||
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ from esphome.const import (
|
|||||||
CONF_VISUAL,
|
CONF_VISUAL,
|
||||||
CONF_WEB_SERVER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -517,6 +517,6 @@ async def climate_control_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(climate_ns.using)
|
cg.add_global(climate_ns.using)
|
||||||
|
@@ -32,7 +32,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_SHUTTER,
|
DEVICE_CLASS_SHUTTER,
|
||||||
DEVICE_CLASS_WINDOW,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -263,6 +263,6 @@ async def cover_control_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(cover_ns.using)
|
cg.add_global(cover_ns.using)
|
||||||
|
@@ -21,7 +21,7 @@ from esphome.const import (
|
|||||||
CONF_WEB_SERVER,
|
CONF_WEB_SERVER,
|
||||||
CONF_YEAR,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ async def new_datetime(config, *args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(datetime_ns.using)
|
cg.add_global(datetime_ns.using)
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ from esphome.const import (
|
|||||||
CONF_UPDATE_INTERVAL,
|
CONF_UPDATE_INTERVAL,
|
||||||
SCHEDULER_DONT_RUN,
|
SCHEDULER_DONT_RUN,
|
||||||
)
|
)
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ async def display_is_displaying_page_to_code(config, condition_id, template_arg,
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(display_ns.using)
|
cg.add_global(display_ns.using)
|
||||||
cg.add_define("USE_DISPLAY")
|
cg.add_define("USE_DISPLAY")
|
||||||
|
@@ -855,11 +855,6 @@ async def to_code(config):
|
|||||||
|
|
||||||
cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]])
|
cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]])
|
||||||
|
|
||||||
# platformio/toolchain-esp32ulp does not support linux_aarch64 yet and has not been updated for over 2 years
|
|
||||||
# This is espressif's own published version which is more up to date.
|
|
||||||
cg.add_platformio_option(
|
|
||||||
"platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"]
|
|
||||||
)
|
|
||||||
add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True)
|
add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True)
|
||||||
add_idf_sdkconfig_option(
|
add_idf_sdkconfig_option(
|
||||||
f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True
|
f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True
|
||||||
|
@@ -30,7 +30,7 @@ from esphome.const import (
|
|||||||
CONF_SERVICE_UUID,
|
CONF_SERVICE_UUID,
|
||||||
CONF_TRIGGER_ID,
|
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.enum import StrEnum
|
||||||
from esphome.types import ConfigType
|
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
|
# 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
|
# chance to call register_ble_tracker and register_client before the list is checked
|
||||||
# and added to the global defines list.
|
# and added to the global defines list.
|
||||||
@coroutine_with_priority(-1000)
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
async def _add_ble_features():
|
async def _add_ble_features():
|
||||||
# Add feature-specific defines based on what's needed
|
# Add feature-specific defines based on what's needed
|
||||||
if BLEFeatures.ESP_BT_DEVICE in _required_features:
|
if BLEFeatures.ESP_BT_DEVICE in _required_features:
|
||||||
|
@@ -17,7 +17,7 @@ from esphome.const import (
|
|||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
ThreadModel,
|
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 esphome.helpers import copy_file_if_changed
|
||||||
|
|
||||||
from .boards import BOARDS, ESP8266_LD_SCRIPTS
|
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):
|
async def to_code(config):
|
||||||
cg.add(esp8266_ns.setup_preferences())
|
cg.add(esp8266_ns.setup_preferences())
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ from esphome.const import (
|
|||||||
CONF_PULLUP,
|
CONF_PULLUP,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
from . import boards
|
from . import boards
|
||||||
from .const import KEY_BOARD, KEY_ESP8266, KEY_PIN_INITIAL_STATES, esp8266_ns
|
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
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(-999.0)
|
@coroutine_with_priority(CoroPriority.WORKAROUNDS)
|
||||||
async def add_pin_initial_states_array():
|
async def add_pin_initial_states_array():
|
||||||
# Add includes at the very end, so that they override everything
|
# Add includes at the very end, so that they override everything
|
||||||
initial_states: list[PinInitialState] = CORE.data[KEY_ESP8266][
|
initial_states: list[PinInitialState] = CORE.data[KEY_ESP8266][
|
||||||
|
@@ -16,7 +16,7 @@ from esphome.const import (
|
|||||||
CONF_SAFE_MODE,
|
CONF_SAFE_MODE,
|
||||||
CONF_VERSION,
|
CONF_VERSION,
|
||||||
)
|
)
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import CoroPriority, coroutine_with_priority
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -121,7 +121,7 @@ CONFIG_SCHEMA = (
|
|||||||
FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate
|
FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(52.0)
|
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
cg.add(var.set_port(config[CONF_PORT]))
|
cg.add(var.set_port(config[CONF_PORT]))
|
||||||
|
@@ -38,7 +38,12 @@ from esphome.const import (
|
|||||||
KEY_CORE,
|
KEY_CORE,
|
||||||
KEY_FRAMEWORK_VERSION,
|
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
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
CONFLICTS_WITH = ["wifi"]
|
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):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
@@ -17,7 +17,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_EMPTY,
|
DEVICE_CLASS_EMPTY,
|
||||||
DEVICE_CLASS_MOTION,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -143,6 +143,6 @@ async def event_fire_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(event_ns.using)
|
cg.add_global(event_ns.using)
|
||||||
|
@@ -31,7 +31,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_WEB_SERVER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
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)
|
return cg.new_Pvariable(condition_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(fan_ns.using)
|
cg.add_global(fan_ns.using)
|
||||||
|
@@ -8,7 +8,7 @@ from esphome.const import (
|
|||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
)
|
)
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
globals_ns = cg.esphome_ns.namespace("globals")
|
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
|
# 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):
|
async def to_code(config):
|
||||||
type_ = cg.RawExpression(config[CONF_TYPE])
|
type_ = cg.RawExpression(config[CONF_TYPE])
|
||||||
restore = config[CONF_RESTORE_VALUE]
|
restore = config[CONF_RESTORE_VALUE]
|
||||||
|
@@ -42,9 +42,10 @@ class HostPreferences : public ESPPreferences {
|
|||||||
if (len > 255)
|
if (len > 255)
|
||||||
return false;
|
return false;
|
||||||
this->setup_();
|
this->setup_();
|
||||||
if (this->data.count(key) == 0)
|
auto it = this->data.find(key);
|
||||||
|
if (it == this->data.end())
|
||||||
return false;
|
return false;
|
||||||
auto vec = this->data[key];
|
const auto &vec = it->second;
|
||||||
if (vec.size() != len)
|
if (vec.size() != len)
|
||||||
return false;
|
return false;
|
||||||
memcpy(data, vec.data(), len);
|
memcpy(data, vec.data(), len);
|
||||||
|
@@ -3,7 +3,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
|
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
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
|
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):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await ota_to_code(var, config)
|
await ota_to_code(var, config)
|
||||||
|
@@ -18,7 +18,7 @@ from esphome.const import (
|
|||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
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):
|
async def to_code(config):
|
||||||
cg.add_global(i2c_ns.using)
|
cg.add_global(i2c_ns.using)
|
||||||
cg.add_define("USE_I2C")
|
cg.add_define("USE_I2C")
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
json_ns = cg.esphome_ns.namespace("json")
|
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):
|
async def to_code(config):
|
||||||
cg.add_library("bblanchon/ArduinoJson", "7.4.2")
|
cg.add_library("bblanchon/ArduinoJson", "7.4.2")
|
||||||
cg.add_define("USE_JSON")
|
cg.add_define("USE_JSON")
|
||||||
|
@@ -37,7 +37,7 @@ from esphome.const import (
|
|||||||
CONF_WEB_SERVER,
|
CONF_WEB_SERVER,
|
||||||
CONF_WHITE,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -283,6 +283,6 @@ async def new_light(config, *args):
|
|||||||
return output_var
|
return output_var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(light_ns.using)
|
cg.add_global(light_ns.using)
|
||||||
|
@@ -13,7 +13,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_WEB_SERVER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
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)
|
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):
|
async def to_code(config):
|
||||||
cg.add_global(lock_ns.using)
|
cg.add_global(lock_ns.using)
|
||||||
|
@@ -51,7 +51,7 @@ from esphome.const import (
|
|||||||
PLATFORM_RTL87XX,
|
PLATFORM_RTL87XX,
|
||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, Lambda, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
logger_ns = cg.esphome_ns.namespace("logger")
|
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):
|
async def to_code(config):
|
||||||
baud_rate = config[CONF_BAUD_RATE]
|
baud_rate = config[CONF_BAUD_RATE]
|
||||||
level = config[CONF_LEVEL]
|
level = config[CONF_LEVEL]
|
||||||
|
@@ -11,7 +11,7 @@ from esphome.const import (
|
|||||||
CONF_SERVICES,
|
CONF_SERVICES,
|
||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
DEPENDENCIES = ["network"]
|
DEPENDENCIES = ["network"]
|
||||||
@@ -72,7 +72,7 @@ def mdns_service(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(55.0)
|
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if config[CONF_DISABLED] is True:
|
if config[CONF_DISABLED] is True:
|
||||||
return
|
return
|
||||||
|
@@ -14,7 +14,7 @@ from esphome.const import (
|
|||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
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
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
@@ -303,7 +303,7 @@ async def media_player_volume_set_action(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(media_player_ns.using)
|
cg.add_global(media_player_ns.using)
|
||||||
cg.add_define("USE_MEDIA_PLAYER")
|
cg.add_define("USE_MEDIA_PLAYER")
|
||||||
|
@@ -12,7 +12,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
from esphome.coroutine import coroutine_with_priority
|
from esphome.coroutine import CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
AUTO_LOAD = ["audio"]
|
AUTO_LOAD = ["audio"]
|
||||||
CODEOWNERS = ["@jesserockz", "@kahrendt"]
|
CODEOWNERS = ["@jesserockz", "@kahrendt"]
|
||||||
@@ -213,7 +213,7 @@ automation.register_condition(
|
|||||||
)(microphone_action)
|
)(microphone_action)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(microphone_ns.using)
|
cg.add_global(microphone_ns.using)
|
||||||
cg.add_define("USE_MICROPHONE")
|
cg.add_define("USE_MICROPHONE")
|
||||||
|
@@ -57,7 +57,7 @@ from esphome.const import (
|
|||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
DEPENDENCIES = ["network"]
|
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):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
@@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT
|
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"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
AUTO_LOAD = ["mdns"]
|
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):
|
async def to_code(config):
|
||||||
cg.add_define("USE_NETWORK")
|
cg.add_define("USE_NETWORK")
|
||||||
if CORE.using_arduino and CORE.is_esp32:
|
if CORE.using_arduino and CORE.is_esp32:
|
||||||
|
@@ -30,7 +30,7 @@ from esphome.const import (
|
|||||||
PLATFORM_NRF52,
|
PLATFORM_NRF52,
|
||||||
ThreadModel,
|
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.storage_json import StorageJSON
|
||||||
from esphome.types import ConfigType
|
from esphome.types import ConfigType
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ def _final_validate(config):
|
|||||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(1000)
|
@coroutine_with_priority(CoroPriority.PLATFORM)
|
||||||
async def to_code(config: ConfigType) -> None:
|
async def to_code(config: ConfigType) -> None:
|
||||||
"""Convert the configuration to code."""
|
"""Convert the configuration to code."""
|
||||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
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)
|
CORE.add_job(_dfu_to_code, dfu_config)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(90)
|
@coroutine_with_priority(CoroPriority.DIAGNOSTICS)
|
||||||
async def _dfu_to_code(dfu_config):
|
async def _dfu_to_code(dfu_config):
|
||||||
cg.add_define("USE_NRF52_DFU")
|
cg.add_define("USE_NRF52_DFU")
|
||||||
var = cg.new_Pvariable(dfu_config[CONF_ID])
|
var = cg.new_Pvariable(dfu_config[CONF_ID])
|
||||||
|
@@ -76,7 +76,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_WIND_DIRECTION,
|
DEVICE_CLASS_WIND_DIRECTION,
|
||||||
DEVICE_CLASS_WIND_SPEED,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
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
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(number_ns.using)
|
cg.add_global(number_ns.using)
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
AUTO_LOAD = ["md5", "safe_mode"]
|
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):
|
async def to_code(config):
|
||||||
cg.add_define("USE_OTA")
|
cg.add_define("USE_OTA")
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ from esphome.const import (
|
|||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
ThreadModel,
|
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 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
|
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):
|
async def to_code(config):
|
||||||
cg.add(rp2040_ns.setup_preferences())
|
cg.add(rp2040_ns.setup_preferences())
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
KEY_PAST_SAFE_MODE,
|
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
|
from esphome.cpp_generator import RawExpression
|
||||||
|
|
||||||
CODEOWNERS = ["@paulmonigatti", "@jsuanet", "@kbx81"]
|
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):
|
async def to_code(config):
|
||||||
if not config[CONF_DISABLED]:
|
if not config[CONF_DISABLED]:
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
@@ -16,7 +16,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_WEB_SERVER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ async def new_select(config, *args, options: list[str]):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(select_ns.using)
|
cg.add_global(select_ns.using)
|
||||||
|
|
||||||
|
@@ -101,7 +101,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_WIND_SPEED,
|
DEVICE_CLASS_WIND_SPEED,
|
||||||
ENTITY_CATEGORY_CONFIG,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
@@ -1142,6 +1142,6 @@ def _lstsq(a, b):
|
|||||||
return _mat_dot(_mat_dot(x, a_t), 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):
|
async def to_code(config):
|
||||||
cg.add_global(sensor_ns.using)
|
cg.add_global(sensor_ns.using)
|
||||||
|
@@ -4,7 +4,7 @@ from esphome.components import audio, audio_dac
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME
|
from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
from esphome.coroutine import coroutine_with_priority
|
from esphome.coroutine import CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
AUTO_LOAD = ["audio"]
|
AUTO_LOAD = ["audio"]
|
||||||
CODEOWNERS = ["@jesserockz", "@kahrendt"]
|
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)
|
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(speaker_ns.using)
|
cg.add_global(speaker_ns.using)
|
||||||
cg.add_define("USE_SPEAKER")
|
cg.add_define("USE_SPEAKER")
|
||||||
|
@@ -35,7 +35,7 @@ from esphome.const import (
|
|||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core", "@clydebarrow"]
|
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):
|
async def to_code(configs):
|
||||||
cg.add_define("USE_SPI")
|
cg.add_define("USE_SPI")
|
||||||
cg.add_global(spi_ns.using)
|
cg.add_global(spi_ns.using)
|
||||||
|
@@ -2,7 +2,7 @@ from esphome import pins
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_PIN
|
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")
|
status_led_ns = cg.esphome_ns.namespace("status_led")
|
||||||
StatusLED = status_led_ns.class_("StatusLED", cg.Component)
|
StatusLED = status_led_ns.class_("StatusLED", cg.Component)
|
||||||
@@ -15,7 +15,7 @@ CONFIG_SCHEMA = cv.Schema(
|
|||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(80.0)
|
@coroutine_with_priority(CoroPriority.STATUS)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
rhs = StatusLED.new(pin)
|
rhs = StatusLED.new(pin)
|
||||||
|
@@ -10,7 +10,7 @@ from esphome.const import (
|
|||||||
CONF_SPEED,
|
CONF_SPEED,
|
||||||
CONF_TARGET,
|
CONF_TARGET,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
@@ -178,6 +178,6 @@ async def stepper_set_deceleration_to_code(config, action_id, template_arg, args
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(stepper_ns.using)
|
cg.add_global(stepper_ns.using)
|
||||||
|
@@ -21,7 +21,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_OUTLET,
|
DEVICE_CLASS_OUTLET,
|
||||||
DEVICE_CLASS_SWITCH,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
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)
|
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):
|
async def to_code(config):
|
||||||
cg.add_global(switch_ns.using)
|
cg.add_global(switch_ns.using)
|
||||||
|
@@ -13,7 +13,7 @@ from esphome.const import (
|
|||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
CONF_WEB_SERVER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ async def new_text(
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(text_ns.using)
|
cg.add_global(text_ns.using)
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_EMPTY,
|
DEVICE_CLASS_EMPTY,
|
||||||
DEVICE_CLASS_TIMESTAMP,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
@@ -230,7 +230,7 @@ async def new_text_sensor(config, *args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(text_sensor_ns.using)
|
cg.add_global(text_sensor_ns.using)
|
||||||
|
|
||||||
|
@@ -26,7 +26,7 @@ from esphome.const import (
|
|||||||
CONF_TIMEZONE,
|
CONF_TIMEZONE,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -340,7 +340,7 @@ async def register_time(time_var, config):
|
|||||||
await setup_time_core_(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):
|
async def to_code(config):
|
||||||
if CORE.using_zephyr:
|
if CORE.using_zephyr:
|
||||||
zephyr_add_prj_conf("POSIX_CLOCK", True)
|
zephyr_add_prj_conf("POSIX_CLOCK", True)
|
||||||
|
@@ -13,7 +13,7 @@ from esphome.const import (
|
|||||||
CONF_SWAP_XY,
|
CONF_SWAP_XY,
|
||||||
CONF_TRANSFORM,
|
CONF_TRANSFORM,
|
||||||
)
|
)
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import CoroPriority, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz", "@nielsnl68"]
|
CODEOWNERS = ["@jesserockz", "@nielsnl68"]
|
||||||
DEPENDENCIES = ["display"]
|
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):
|
async def to_code(config):
|
||||||
cg.add_global(touchscreen_ns.using)
|
cg.add_global(touchscreen_ns.using)
|
||||||
cg.add_define("USE_TOUCHSCREEN")
|
cg.add_define("USE_TOUCHSCREEN")
|
||||||
|
@@ -14,7 +14,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_FIRMWARE,
|
DEVICE_CLASS_FIRMWARE,
|
||||||
ENTITY_CATEGORY_CONFIG,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ async def new_update(config):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(update_ns.using)
|
cg.add_global(update_ns.using)
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_GAS,
|
DEVICE_CLASS_GAS,
|
||||||
DEVICE_CLASS_WATER,
|
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.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
@@ -233,6 +233,6 @@ async def valve_control_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(valve_ns.using)
|
cg.add_global(valve_ns.using)
|
||||||
|
@@ -31,7 +31,7 @@ from esphome.const import (
|
|||||||
PLATFORM_LN882X,
|
PLATFORM_LN882X,
|
||||||
PLATFORM_RTL87XX,
|
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
|
import esphome.final_validate as fv
|
||||||
from esphome.types import ConfigType
|
from esphome.types import ConfigType
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ def add_resource_as_progmem(
|
|||||||
cg.add_global(cg.RawExpression(size_t))
|
cg.add_global(cg.RawExpression(size_t))
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(40.0)
|
@coroutine_with_priority(CoroPriority.WEB)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ from esphome.components.esp32 import add_idf_component
|
|||||||
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
|
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
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"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
DEPENDENCIES = ["network", "web_server_base"]
|
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):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await ota_to_code(var, config)
|
await ota_to_code(var, config)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
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"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
DEPENDENCIES = ["network"]
|
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):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
@@ -44,7 +44,7 @@ from esphome.const import (
|
|||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
PlatformFramework,
|
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
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
from . import wpa2_eap
|
from . import wpa2_eap
|
||||||
@@ -370,7 +370,7 @@ def wifi_network(config, ap, static_ip):
|
|||||||
return ap
|
return ap
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(60.0)
|
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
||||||
|
@@ -29,6 +29,7 @@ from esphome.const import (
|
|||||||
|
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from esphome.coroutine import ( # noqa: F401
|
from esphome.coroutine import ( # noqa: F401
|
||||||
|
CoroPriority,
|
||||||
FakeAwaitable as _FakeAwaitable,
|
FakeAwaitable as _FakeAwaitable,
|
||||||
FakeEventLoop as _FakeEventLoop,
|
FakeEventLoop as _FakeEventLoop,
|
||||||
coroutine,
|
coroutine,
|
||||||
|
@@ -39,7 +39,7 @@ from esphome.const import (
|
|||||||
PlatformFramework,
|
PlatformFramework,
|
||||||
__version__ as ESPHOME_VERSION,
|
__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 (
|
from esphome.helpers import (
|
||||||
copy_file_if_changed,
|
copy_file_if_changed,
|
||||||
fnv1a_32bit_hash,
|
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():
|
async def add_arduino_global_workaround():
|
||||||
# The Arduino framework defined these itself in the global
|
# The Arduino framework defined these itself in the global
|
||||||
# namespace. For the esphome codebase that is not a problem,
|
# 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))
|
cg.add_global(cg.RawStatement(line))
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(-1000.0)
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
async def add_includes(includes):
|
async def add_includes(includes):
|
||||||
# Add includes at the very end, so that the included files can access global variables
|
# Add includes at the very end, so that the included files can access global variables
|
||||||
for include in includes:
|
for include in includes:
|
||||||
@@ -392,7 +392,7 @@ async def add_includes(includes):
|
|||||||
include_file(path, basename)
|
include_file(path, basename)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(-1000.0)
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
async def _add_platformio_options(pio_options):
|
async def _add_platformio_options(pio_options):
|
||||||
# Add includes at the very end, so that they override everything
|
# Add includes at the very end, so that they override everything
|
||||||
for key, val in pio_options.items():
|
for key, val in pio_options.items():
|
||||||
@@ -401,7 +401,7 @@ async def _add_platformio_options(pio_options):
|
|||||||
cg.add_platformio_option(key, val)
|
cg.add_platformio_option(key, val)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(30.0)
|
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||||
async def _add_automations(config):
|
async def _add_automations(config):
|
||||||
for conf in config.get(CONF_ON_BOOT, []):
|
for conf in config.get(CONF_ON_BOOT, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf.get(CONF_PRIORITY))
|
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"}
|
DATETIME_SUBTYPES = {"date", "time", "datetime"}
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(-1000.0)
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
async def _add_platform_defines() -> None:
|
async def _add_platform_defines() -> None:
|
||||||
# Generate compile-time defines for platforms that have actual entities
|
# Generate compile-time defines for platforms that have actual entities
|
||||||
# Only add USE_* and count defines when there are 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()}")
|
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:
|
async def to_code(config: ConfigType) -> None:
|
||||||
cg.add_global(cg.global_ns.namespace("esphome").using)
|
cg.add_global(cg.global_ns.namespace("esphome").using)
|
||||||
# These can be used by user lambdas, put them to default scope
|
# These can be used by user lambdas, put them to default scope
|
||||||
|
@@ -15,21 +15,18 @@ namespace esphome {
|
|||||||
static const char *const TAG = "scheduler";
|
static const char *const TAG = "scheduler";
|
||||||
|
|
||||||
// Memory pool configuration constants
|
// Memory pool configuration constants
|
||||||
// Pool size of 10 is a balance between memory usage and performance:
|
// Pool size of 5 matches typical usage patterns (2-4 active timers)
|
||||||
// - Small enough to not waste memory on simple configs (1-2 timers)
|
// - Minimal memory overhead (~250 bytes on ESP32)
|
||||||
// - Large enough to handle complex setups with multiple sensors/components
|
// - Sufficient for most configs with a couple sensors/components
|
||||||
// - Prevents system-wide stalls from heap allocation/deallocation that can
|
// - Still prevents heap fragmentation and allocation stalls
|
||||||
// disrupt task synchronization and cause dropped events
|
// - Complex setups with many timers will just allocate beyond the pool
|
||||||
static constexpr size_t MAX_POOL_SIZE = 10;
|
// See https://github.com/esphome/backlog/issues/52
|
||||||
// Maximum number of cancelled items to keep in the heap before forcing a cleanup.
|
static constexpr size_t MAX_POOL_SIZE = 5;
|
||||||
// Set to 6 to trigger cleanup relatively frequently, ensuring cancelled items are
|
|
||||||
// recycled to the pool in a timely manner to maintain pool efficiency.
|
|
||||||
static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 6;
|
|
||||||
|
|
||||||
// Ensure MAX_LOGICALLY_DELETED_ITEMS is at least 4 smaller than MAX_POOL_SIZE
|
// Maximum number of logically deleted (cancelled) items before forcing cleanup.
|
||||||
// This guarantees we have room in the pool for recycled items when cleanup occurs
|
// Set to 5 to match the pool size - when we have as many cancelled items as our
|
||||||
static_assert(MAX_LOGICALLY_DELETED_ITEMS + 4 <= MAX_POOL_SIZE,
|
// pool can hold, it's time to clean up and recycle them.
|
||||||
"MAX_LOGICALLY_DELETED_ITEMS must be at least 4 smaller than MAX_POOL_SIZE");
|
static constexpr uint32_t MAX_LOGICALLY_DELETED_ITEMS = 5;
|
||||||
|
|
||||||
// Half the 32-bit range - used to detect rollovers vs normal time progression
|
// Half the 32-bit range - used to detect rollovers vs normal time progression
|
||||||
static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
|
static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
|
||||||
@@ -108,13 +105,13 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|||||||
item = std::move(this->scheduler_item_pool_.back());
|
item = std::move(this->scheduler_item_pool_.back());
|
||||||
this->scheduler_item_pool_.pop_back();
|
this->scheduler_item_pool_.pop_back();
|
||||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||||
ESP_LOGVV(TAG, "Reused item from pool (pool size now: %zu)", this->scheduler_item_pool_.size());
|
ESP_LOGD(TAG, "Reused item from pool (pool size now: %zu)", this->scheduler_item_pool_.size());
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
// Allocate new if pool is empty
|
// Allocate new if pool is empty
|
||||||
item = make_unique<SchedulerItem>();
|
item = make_unique<SchedulerItem>();
|
||||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||||
ESP_LOGVV(TAG, "Allocated new item (pool empty)");
|
ESP_LOGD(TAG, "Allocated new item (pool empty)");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
item->component = component;
|
item->component = component;
|
||||||
@@ -384,9 +381,10 @@ void HOT Scheduler::call(uint32_t now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *name = item->get_name();
|
const char *name = item->get_name();
|
||||||
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64,
|
bool is_cancelled = is_item_removed_(item.get());
|
||||||
|
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64 "%s",
|
||||||
item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
|
item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
|
||||||
item->next_execution_ - now_64, item->next_execution_);
|
item->next_execution_ - now_64, item->next_execution_, is_cancelled ? " [CANCELLED]" : "");
|
||||||
|
|
||||||
old_items.push_back(std::move(item));
|
old_items.push_back(std::move(item));
|
||||||
}
|
}
|
||||||
@@ -401,8 +399,13 @@ void HOT Scheduler::call(uint32_t now) {
|
|||||||
}
|
}
|
||||||
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
||||||
|
|
||||||
// If we have too many items to remove
|
// Cleanup removed items before processing
|
||||||
if (this->to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
|
// First try to clean items from the top of the heap (fast path)
|
||||||
|
this->cleanup_();
|
||||||
|
|
||||||
|
// If we still have too many cancelled items, do a full cleanup
|
||||||
|
// This only happens if cancelled items are stuck in the middle/bottom of the heap
|
||||||
|
if (this->to_remove_ >= MAX_LOGICALLY_DELETED_ITEMS) {
|
||||||
// We hold the lock for the entire cleanup operation because:
|
// We hold the lock for the entire cleanup operation because:
|
||||||
// 1. We're rebuilding the entire items_ list, so we need exclusive access throughout
|
// 1. We're rebuilding the entire items_ list, so we need exclusive access throughout
|
||||||
// 2. Other threads must see either the old state or the new state, not intermediate states
|
// 2. Other threads must see either the old state or the new state, not intermediate states
|
||||||
@@ -428,9 +431,6 @@ void HOT Scheduler::call(uint32_t now) {
|
|||||||
std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
||||||
this->to_remove_ = 0;
|
this->to_remove_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup removed items before processing
|
|
||||||
this->cleanup_();
|
|
||||||
while (!this->items_.empty()) {
|
while (!this->items_.empty()) {
|
||||||
// use scoping to indicate visibility of `item` variable
|
// use scoping to indicate visibility of `item` variable
|
||||||
{
|
{
|
||||||
@@ -516,7 +516,9 @@ void HOT Scheduler::call(uint32_t now) {
|
|||||||
void HOT Scheduler::process_to_add() {
|
void HOT Scheduler::process_to_add() {
|
||||||
LockGuard guard{this->lock_};
|
LockGuard guard{this->lock_};
|
||||||
for (auto &it : this->to_add_) {
|
for (auto &it : this->to_add_) {
|
||||||
if (it->remove) {
|
if (is_item_removed_(it.get())) {
|
||||||
|
// Recycle cancelled items
|
||||||
|
this->recycle_item_(std::move(it));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,14 +596,28 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
|
|
||||||
// Check all containers for matching items
|
// Check all containers for matching items
|
||||||
#ifndef ESPHOME_THREAD_SINGLE
|
#ifndef ESPHOME_THREAD_SINGLE
|
||||||
// Cancel and immediately recycle items in defer queue
|
// Mark items in defer queue as cancelled (they'll be skipped when processed)
|
||||||
if (type == SchedulerItem::TIMEOUT) {
|
if (type == SchedulerItem::TIMEOUT) {
|
||||||
total_cancelled +=
|
for (auto &item : this->defer_queue_) {
|
||||||
this->cancel_and_recycle_from_container_(this->defer_queue_, component, name_cstr, type, match_retry);
|
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
||||||
|
this->mark_item_removed_(item.get());
|
||||||
|
total_cancelled++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* not ESPHOME_THREAD_SINGLE */
|
#endif /* not ESPHOME_THREAD_SINGLE */
|
||||||
|
|
||||||
// Cancel items in the main heap (can't recycle immediately due to heap structure)
|
// Cancel items in the main heap
|
||||||
|
// Special case: if the last item in the heap matches, we can remove it immediately
|
||||||
|
// (removing the last element doesn't break heap structure)
|
||||||
|
if (!this->items_.empty()) {
|
||||||
|
auto &last_item = this->items_.back();
|
||||||
|
if (this->matches_item_(last_item, component, name_cstr, type, match_retry)) {
|
||||||
|
this->recycle_item_(std::move(this->items_.back()));
|
||||||
|
this->items_.pop_back();
|
||||||
|
total_cancelled++;
|
||||||
|
}
|
||||||
|
// For other items in heap, we can only mark for removal (can't remove from middle of heap)
|
||||||
for (auto &item : this->items_) {
|
for (auto &item : this->items_) {
|
||||||
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
||||||
this->mark_item_removed_(item.get());
|
this->mark_item_removed_(item.get());
|
||||||
@@ -609,9 +625,16 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
this->to_remove_++; // Track removals for heap items
|
this->to_remove_++; // Track removals for heap items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel and immediately recycle items in to_add_ since they're not in heap yet
|
// Cancel items in to_add_
|
||||||
total_cancelled += this->cancel_and_recycle_from_container_(this->to_add_, component, name_cstr, type, match_retry);
|
for (auto &item : this->to_add_) {
|
||||||
|
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
||||||
|
this->mark_item_removed_(item.get());
|
||||||
|
total_cancelled++;
|
||||||
|
// Don't track removals for to_add_ items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return total_cancelled > 0;
|
return total_cancelled > 0;
|
||||||
}
|
}
|
||||||
@@ -790,11 +813,11 @@ void Scheduler::recycle_item_(std::unique_ptr<SchedulerItem> item) {
|
|||||||
item->clear_dynamic_name();
|
item->clear_dynamic_name();
|
||||||
this->scheduler_item_pool_.push_back(std::move(item));
|
this->scheduler_item_pool_.push_back(std::move(item));
|
||||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||||
ESP_LOGVV(TAG, "Recycled item to pool (pool size now: %zu)", this->scheduler_item_pool_.size());
|
ESP_LOGD(TAG, "Recycled item to pool (pool size now: %zu)", this->scheduler_item_pool_.size());
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||||
ESP_LOGVV(TAG, "Pool full (size: %zu), deleting item", this->scheduler_item_pool_.size());
|
ESP_LOGD(TAG, "Pool full (size: %zu), deleting item", this->scheduler_item_pool_.size());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// else: unique_ptr will delete the item when it goes out of scope
|
// else: unique_ptr will delete the item when it goes out of scope
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <array>
|
|
||||||
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#endif
|
#endif
|
||||||
@@ -217,6 +216,15 @@ class Scheduler {
|
|||||||
// Common implementation for cancel operations
|
// Common implementation for cancel operations
|
||||||
bool cancel_item_(Component *component, bool is_static_string, const void *name_ptr, SchedulerItem::Type type);
|
bool cancel_item_(Component *component, bool is_static_string, const void *name_ptr, SchedulerItem::Type type);
|
||||||
|
|
||||||
|
// Helper to check if two scheduler item names match
|
||||||
|
inline bool HOT names_match_(const char *name1, const char *name2) const {
|
||||||
|
// Check pointer equality first (common for static strings), then string contents
|
||||||
|
// The core ESPHome codebase uses static strings (const char*) for component names,
|
||||||
|
// making pointer comparison effective. The std::string overloads exist only for
|
||||||
|
// compatibility with external components but are rarely used in practice.
|
||||||
|
return (name1 != nullptr && name2 != nullptr) && ((name1 == name2) || (strcmp(name1, name2) == 0));
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to check if item matches criteria for cancellation
|
// Helper function to check if item matches criteria for cancellation
|
||||||
inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr,
|
inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr,
|
||||||
SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const {
|
SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const {
|
||||||
@@ -224,19 +232,7 @@ class Scheduler {
|
|||||||
(match_retry && !item->is_retry)) {
|
(match_retry && !item->is_retry)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const char *item_name = item->get_name();
|
return this->names_match_(item->get_name(), name_cstr);
|
||||||
if (item_name == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Fast path: if pointers are equal
|
|
||||||
// This is effective because the core ESPHome codebase uses static strings (const char*)
|
|
||||||
// for component names. The std::string overloads exist only for compatibility with
|
|
||||||
// external components, but are rarely used in practice.
|
|
||||||
if (item_name == name_cstr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Slow path: compare string contents
|
|
||||||
return strcmp(name_cstr, item_name) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to execute a scheduler item
|
// Helper to execute a scheduler item
|
||||||
@@ -295,24 +291,6 @@ class Scheduler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template helper to cancel and recycle items from a container
|
|
||||||
template<typename Container>
|
|
||||||
size_t cancel_and_recycle_from_container_(Container &container, Component *component, const char *name_cstr,
|
|
||||||
SchedulerItem::Type type, bool match_retry) {
|
|
||||||
size_t cancelled = 0;
|
|
||||||
for (auto it = container.begin(); it != container.end();) {
|
|
||||||
if (this->matches_item_(*it, component, name_cstr, type, match_retry)) {
|
|
||||||
// Recycle the cancelled item immediately
|
|
||||||
this->recycle_item_(std::move(*it));
|
|
||||||
it = container.erase(it);
|
|
||||||
cancelled++;
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cancelled;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mutex lock_;
|
Mutex lock_;
|
||||||
std::vector<std::unique_ptr<SchedulerItem>> items_;
|
std::vector<std::unique_ptr<SchedulerItem>> items_;
|
||||||
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
|
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
|
||||||
@@ -325,8 +303,8 @@ class Scheduler {
|
|||||||
// Memory pool for recycling SchedulerItem objects to reduce heap churn.
|
// Memory pool for recycling SchedulerItem objects to reduce heap churn.
|
||||||
// Design decisions:
|
// Design decisions:
|
||||||
// - std::vector is used instead of a fixed array because many systems only need 1-2 scheduler items
|
// - std::vector is used instead of a fixed array because many systems only need 1-2 scheduler items
|
||||||
// - The vector grows dynamically up to MAX_POOL_SIZE (10) only when needed, saving memory on simple setups
|
// - The vector grows dynamically up to MAX_POOL_SIZE (5) only when needed, saving memory on simple setups
|
||||||
// - This approach balances memory efficiency for simple configs with performance for complex ones
|
// - Pool size of 5 matches typical usage (2-4 timers) while keeping memory overhead low (~250 bytes on ESP32)
|
||||||
// - The pool significantly reduces heap fragmentation which is critical because heap allocation/deallocation
|
// - The pool significantly reduces heap fragmentation which is critical because heap allocation/deallocation
|
||||||
// can stall the entire system, causing timing issues and dropped events for any components that need
|
// can stall the entire system, causing timing issues and dropped events for any components that need
|
||||||
// to synchronize between tasks (see https://github.com/esphome/backlog/issues/52)
|
// to synchronize between tasks (see https://github.com/esphome/backlog/issues/52)
|
||||||
|
@@ -42,7 +42,10 @@ Here everything is combined in `yield` expressions. You await other coroutines u
|
|||||||
the last `yield` expression defines what is returned.
|
the last `yield` expression defines what is returned.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Awaitable, Callable, Generator, Iterator
|
from collections.abc import Awaitable, Callable, Generator, Iterator
|
||||||
|
import enum
|
||||||
import functools
|
import functools
|
||||||
import heapq
|
import heapq
|
||||||
import inspect
|
import inspect
|
||||||
@@ -53,6 +56,79 @@ from typing import Any
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_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]]:
|
def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
|
||||||
"""Decorator to apply to methods to convert them to ESPHome coroutines."""
|
"""Decorator to apply to methods to convert them to ESPHome coroutines."""
|
||||||
if getattr(func, "_esphome_coroutine", False):
|
if getattr(func, "_esphome_coroutine", False):
|
||||||
@@ -95,15 +171,16 @@ def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
|
|||||||
return coro
|
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.
|
"""Decorator to apply to functions to convert them to ESPHome coroutines.
|
||||||
|
|
||||||
:param priority: priority with which to schedule the coroutine, higher priorities run first.
|
: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):
|
def decorator(func):
|
||||||
coro = coroutine(func)
|
coro = coroutine(func)
|
||||||
coro.priority = priority
|
coro.priority = float(priority)
|
||||||
return coro
|
return coro
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
@@ -173,7 +250,7 @@ class _Task:
|
|||||||
self.iterator = iterator
|
self.iterator = iterator
|
||||||
self.original_function = original_function
|
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)
|
return _Task(priority, self.id_number, self.iterator, self.original_function)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
110
tests/integration/fixtures/host_preferences_save_load.yaml
Normal file
110
tests/integration/fixtures/host_preferences_save_load.yaml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
esphome:
|
||||||
|
name: test_device
|
||||||
|
on_boot:
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGD("test", "Host preferences test starting");
|
||||||
|
|
||||||
|
host:
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: DEBUG
|
||||||
|
|
||||||
|
api:
|
||||||
|
|
||||||
|
preferences:
|
||||||
|
flash_write_interval: 0s # Disable automatic saving for test control
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: template
|
||||||
|
name: "Test Switch"
|
||||||
|
id: test_switch
|
||||||
|
optimistic: true
|
||||||
|
restore_mode: DISABLED # Don't auto-restore for test control
|
||||||
|
|
||||||
|
number:
|
||||||
|
- platform: template
|
||||||
|
name: "Test Number"
|
||||||
|
id: test_number
|
||||||
|
min_value: 0
|
||||||
|
max_value: 100
|
||||||
|
step: 0.1
|
||||||
|
optimistic: true
|
||||||
|
restore_value: false # Don't auto-restore for test control
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
name: "Save Preferences"
|
||||||
|
on_press:
|
||||||
|
- lambda: |-
|
||||||
|
// Save current values to preferences
|
||||||
|
ESPPreferenceObject switch_pref = global_preferences->make_preference<bool>(0x1234);
|
||||||
|
ESPPreferenceObject number_pref = global_preferences->make_preference<float>(0x5678);
|
||||||
|
|
||||||
|
bool switch_value = id(test_switch).state;
|
||||||
|
float number_value = id(test_number).state;
|
||||||
|
|
||||||
|
if (switch_pref.save(&switch_value)) {
|
||||||
|
ESP_LOGI("test", "Preference saved: key=switch, value=%.1f", switch_value ? 1.0 : 0.0);
|
||||||
|
}
|
||||||
|
if (number_pref.save(&number_value)) {
|
||||||
|
ESP_LOGI("test", "Preference saved: key=number, value=%.1f", number_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force sync to disk
|
||||||
|
global_preferences->sync();
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Load Preferences"
|
||||||
|
on_press:
|
||||||
|
- lambda: |-
|
||||||
|
// Load values from preferences
|
||||||
|
ESPPreferenceObject switch_pref = global_preferences->make_preference<bool>(0x1234);
|
||||||
|
ESPPreferenceObject number_pref = global_preferences->make_preference<float>(0x5678);
|
||||||
|
|
||||||
|
// Also try to load non-existent preferences (tests our fix)
|
||||||
|
ESPPreferenceObject fake_pref1 = global_preferences->make_preference<uint32_t>(0x9999);
|
||||||
|
ESPPreferenceObject fake_pref2 = global_preferences->make_preference<uint32_t>(0xAAAA);
|
||||||
|
|
||||||
|
bool switch_value = false;
|
||||||
|
float number_value = 0.0;
|
||||||
|
uint32_t fake_value = 0;
|
||||||
|
int loaded_count = 0;
|
||||||
|
|
||||||
|
// These should not exist and shouldn't create map entries
|
||||||
|
fake_pref1.load(&fake_value);
|
||||||
|
fake_pref2.load(&fake_value);
|
||||||
|
|
||||||
|
if (switch_pref.load(&switch_value)) {
|
||||||
|
id(test_switch).publish_state(switch_value);
|
||||||
|
ESP_LOGI("test", "Preference loaded: key=switch, value=%.1f", switch_value ? 1.0 : 0.0);
|
||||||
|
loaded_count++;
|
||||||
|
} else {
|
||||||
|
ESP_LOGW("test", "Failed to load switch preference");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number_pref.load(&number_value)) {
|
||||||
|
id(test_number).publish_state(number_value);
|
||||||
|
ESP_LOGI("test", "Preference loaded: key=number, value=%.1f", number_value);
|
||||||
|
loaded_count++;
|
||||||
|
} else {
|
||||||
|
ESP_LOGW("test", "Failed to load number preference");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log completion message for the test to detect
|
||||||
|
ESP_LOGI("test", "Final load test: loaded %d preferences successfully", loaded_count);
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Verify Preferences"
|
||||||
|
on_press:
|
||||||
|
- lambda: |-
|
||||||
|
// Verify current values match what we expect
|
||||||
|
bool switch_value = id(test_switch).state;
|
||||||
|
float number_value = id(test_number).state;
|
||||||
|
|
||||||
|
// After loading, switch should be true (1.0) and number should be 42.5
|
||||||
|
if (switch_value == true && number_value == 42.5) {
|
||||||
|
ESP_LOGI("test", "Preferences verified: values match!");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE("test", "Preferences mismatch: switch=%d, number=%.1f",
|
||||||
|
switch_value, number_value);
|
||||||
|
}
|
@@ -27,6 +27,9 @@ api:
|
|||||||
- service: run_phase_6
|
- service: run_phase_6
|
||||||
then:
|
then:
|
||||||
- script.execute: test_full_pool_reuse
|
- script.execute: test_full_pool_reuse
|
||||||
|
- service: run_phase_7
|
||||||
|
then:
|
||||||
|
- script.execute: test_same_defer_optimization
|
||||||
- service: run_complete
|
- service: run_complete
|
||||||
then:
|
then:
|
||||||
- script.execute: complete_test
|
- script.execute: complete_test
|
||||||
@@ -87,7 +90,8 @@ script:
|
|||||||
auto *component = id(test_sensor);
|
auto *component = id(test_sensor);
|
||||||
|
|
||||||
// Multiple sensors with different update intervals
|
// Multiple sensors with different update intervals
|
||||||
App.scheduler.set_interval(component, "temp_sensor", 100, []() {
|
// These should only allocate once and reuse the same item for each interval execution
|
||||||
|
App.scheduler.set_interval(component, "temp_sensor", 10, []() {
|
||||||
ESP_LOGD("test", "Temperature sensor update");
|
ESP_LOGD("test", "Temperature sensor update");
|
||||||
id(interval_counter)++;
|
id(interval_counter)++;
|
||||||
if (id(interval_counter) >= 3) {
|
if (id(interval_counter) >= 3) {
|
||||||
@@ -96,7 +100,7 @@ script:
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App.scheduler.set_interval(component, "humidity_sensor", 150, []() {
|
App.scheduler.set_interval(component, "humidity_sensor", 15, []() {
|
||||||
ESP_LOGD("test", "Humidity sensor update");
|
ESP_LOGD("test", "Humidity sensor update");
|
||||||
id(interval_counter)++;
|
id(interval_counter)++;
|
||||||
if (id(interval_counter) >= 5) {
|
if (id(interval_counter) >= 5) {
|
||||||
@@ -105,7 +109,9 @@ script:
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Only 2 allocations for the intervals, no matter how many times they execute
|
||||||
id(create_count) += 2;
|
id(create_count) += 2;
|
||||||
|
ESP_LOGD("test", "Created 2 intervals - they will reuse same items for each execution");
|
||||||
ESP_LOGI("test", "Phase 2 complete");
|
ESP_LOGI("test", "Phase 2 complete");
|
||||||
|
|
||||||
- id: test_communication_patterns
|
- id: test_communication_patterns
|
||||||
@@ -215,11 +221,14 @@ script:
|
|||||||
- id: test_full_pool_reuse
|
- id: test_full_pool_reuse
|
||||||
then:
|
then:
|
||||||
- lambda: |-
|
- lambda: |-
|
||||||
ESP_LOGI("test", "Phase 6: Testing full pool reuse after Phase 5 items complete");
|
ESP_LOGI("test", "Phase 6: Testing pool size limits after Phase 5 items complete");
|
||||||
|
|
||||||
// At this point, all Phase 5 timeouts should have completed and been recycled.
|
// At this point, all Phase 5 timeouts should have completed and been recycled.
|
||||||
// The pool should be at or near its maximum size (10).
|
// The pool should be at its maximum size (5).
|
||||||
// Creating 10 new items should reuse all from the pool.
|
// Creating 10 new items tests that:
|
||||||
|
// - First 5 items reuse from the pool
|
||||||
|
// - Remaining 5 items allocate new (pool empty)
|
||||||
|
// - Pool doesn't grow beyond MAX_POOL_SIZE of 5
|
||||||
|
|
||||||
auto *component = id(test_sensor);
|
auto *component = id(test_sensor);
|
||||||
int full_reuse_count = 10;
|
int full_reuse_count = 10;
|
||||||
@@ -235,6 +244,28 @@ script:
|
|||||||
id(create_count) += full_reuse_count;
|
id(create_count) += full_reuse_count;
|
||||||
ESP_LOGI("test", "Phase 6 complete");
|
ESP_LOGI("test", "Phase 6 complete");
|
||||||
|
|
||||||
|
- id: test_same_defer_optimization
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGI("test", "Phase 7: Testing same-named defer optimization");
|
||||||
|
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
|
||||||
|
// Create 10 defers with the same name - should optimize to update callback in-place
|
||||||
|
// This pattern is common in components like ratgdo that repeatedly defer state updates
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
App.scheduler.set_timeout(component, "repeated_defer", 0, [i]() {
|
||||||
|
ESP_LOGD("test", "Repeated defer executed with value: %d", i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the first should allocate, the rest should update in-place
|
||||||
|
// We expect only 1 allocation for all 10 operations
|
||||||
|
id(create_count) += 1; // Only count 1 since others should be optimized
|
||||||
|
|
||||||
|
ESP_LOGD("test", "Created 10 same-named defers (should only allocate once)");
|
||||||
|
ESP_LOGI("test", "Phase 7 complete");
|
||||||
|
|
||||||
- id: complete_test
|
- id: complete_test
|
||||||
then:
|
then:
|
||||||
- lambda: |-
|
- lambda: |-
|
||||||
|
167
tests/integration/test_host_preferences.py
Normal file
167
tests/integration/test_host_preferences.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
"""Test host preferences save and load functionality."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from aioesphomeapi import ButtonInfo, EntityInfo, NumberInfo, SwitchInfo
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
|
|
||||||
|
|
||||||
|
def find_entity_by_name(
|
||||||
|
entities: list[EntityInfo], entity_type: type, name: str
|
||||||
|
) -> Any:
|
||||||
|
"""Helper to find an entity by type and name."""
|
||||||
|
return next(
|
||||||
|
(e for e in entities if isinstance(e, entity_type) and e.name == name), None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_host_preferences_save_load(
|
||||||
|
yaml_config: str,
|
||||||
|
run_compiled: RunCompiledFunction,
|
||||||
|
api_client_connected: APIClientConnectedFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test that preferences are correctly saved and loaded after our optimization fix."""
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
log_lines: list[str] = []
|
||||||
|
preferences_saved = loop.create_future()
|
||||||
|
preferences_loaded = loop.create_future()
|
||||||
|
values_match = loop.create_future()
|
||||||
|
final_load_complete = loop.create_future()
|
||||||
|
|
||||||
|
# Patterns to match preference logs
|
||||||
|
save_pattern = re.compile(r"Preference saved: key=(\w+), value=([0-9.]+)")
|
||||||
|
load_pattern = re.compile(r"Preference loaded: key=(\w+), value=([0-9.]+)")
|
||||||
|
verify_pattern = re.compile(r"Preferences verified: values match!")
|
||||||
|
final_load_success_pattern = re.compile(
|
||||||
|
r"Final load test: loaded \d+ preferences successfully"
|
||||||
|
)
|
||||||
|
|
||||||
|
saved_values: dict[str, float] = {}
|
||||||
|
loaded_values: dict[str, float] = {}
|
||||||
|
|
||||||
|
def check_output(line: str) -> None:
|
||||||
|
"""Check log output for preference operations."""
|
||||||
|
log_lines.append(line)
|
||||||
|
|
||||||
|
# Look for save operations
|
||||||
|
match = save_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
key = match.group(1)
|
||||||
|
value = float(match.group(2))
|
||||||
|
saved_values[key] = value
|
||||||
|
if len(saved_values) >= 2 and not preferences_saved.done():
|
||||||
|
preferences_saved.set_result(True)
|
||||||
|
|
||||||
|
# Look for load operations
|
||||||
|
match = load_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
key = match.group(1)
|
||||||
|
value = float(match.group(2))
|
||||||
|
loaded_values[key] = value
|
||||||
|
if len(loaded_values) >= 2 and not preferences_loaded.done():
|
||||||
|
preferences_loaded.set_result(True)
|
||||||
|
|
||||||
|
# Look for verification
|
||||||
|
if verify_pattern.search(line) and not values_match.done():
|
||||||
|
values_match.set_result(True)
|
||||||
|
|
||||||
|
# Look for final load test completion
|
||||||
|
if final_load_success_pattern.search(line) and not final_load_complete.done():
|
||||||
|
final_load_complete.set_result(True)
|
||||||
|
|
||||||
|
async with (
|
||||||
|
run_compiled(yaml_config, line_callback=check_output),
|
||||||
|
api_client_connected() as client,
|
||||||
|
):
|
||||||
|
# Get entity list
|
||||||
|
entities, _ = await client.list_entities_services()
|
||||||
|
|
||||||
|
# Find our test entities using helper
|
||||||
|
test_switch = find_entity_by_name(entities, SwitchInfo, "Test Switch")
|
||||||
|
test_number = find_entity_by_name(entities, NumberInfo, "Test Number")
|
||||||
|
save_button = find_entity_by_name(entities, ButtonInfo, "Save Preferences")
|
||||||
|
load_button = find_entity_by_name(entities, ButtonInfo, "Load Preferences")
|
||||||
|
verify_button = find_entity_by_name(entities, ButtonInfo, "Verify Preferences")
|
||||||
|
|
||||||
|
assert test_switch is not None, "Test Switch not found"
|
||||||
|
assert test_number is not None, "Test Number not found"
|
||||||
|
assert save_button is not None, "Save Preferences button not found"
|
||||||
|
assert load_button is not None, "Load Preferences button not found"
|
||||||
|
assert verify_button is not None, "Verify Preferences button not found"
|
||||||
|
|
||||||
|
# Set initial values
|
||||||
|
client.switch_command(test_switch.key, True)
|
||||||
|
client.number_command(test_number.key, 42.5)
|
||||||
|
|
||||||
|
# Save preferences
|
||||||
|
client.button_command(save_button.key)
|
||||||
|
|
||||||
|
# Wait for save to complete
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(preferences_saved, timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Preferences not saved within timeout")
|
||||||
|
|
||||||
|
# Verify we saved the expected values
|
||||||
|
assert "switch" in saved_values, f"Switch preference not saved: {saved_values}"
|
||||||
|
assert "number" in saved_values, f"Number preference not saved: {saved_values}"
|
||||||
|
assert saved_values["switch"] == 1.0, (
|
||||||
|
f"Switch value incorrect: {saved_values['switch']}"
|
||||||
|
)
|
||||||
|
assert saved_values["number"] == 42.5, (
|
||||||
|
f"Number value incorrect: {saved_values['number']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Change the values to something else
|
||||||
|
client.switch_command(test_switch.key, False)
|
||||||
|
client.number_command(test_number.key, 13.7)
|
||||||
|
|
||||||
|
# Load preferences (should restore the saved values)
|
||||||
|
client.button_command(load_button.key)
|
||||||
|
|
||||||
|
# Wait for load to complete
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(preferences_loaded, timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Preferences not loaded within timeout")
|
||||||
|
|
||||||
|
# Verify loaded values match saved values
|
||||||
|
assert "switch" in loaded_values, (
|
||||||
|
f"Switch preference not loaded: {loaded_values}"
|
||||||
|
)
|
||||||
|
assert "number" in loaded_values, (
|
||||||
|
f"Number preference not loaded: {loaded_values}"
|
||||||
|
)
|
||||||
|
assert loaded_values["switch"] == saved_values["switch"], (
|
||||||
|
f"Loaded switch value {loaded_values['switch']} doesn't match saved {saved_values['switch']}"
|
||||||
|
)
|
||||||
|
assert loaded_values["number"] == saved_values["number"], (
|
||||||
|
f"Loaded number value {loaded_values['number']} doesn't match saved {saved_values['number']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the values were actually restored
|
||||||
|
client.button_command(verify_button.key)
|
||||||
|
|
||||||
|
# Wait for verification
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(values_match, timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Preference verification failed within timeout")
|
||||||
|
|
||||||
|
# Test that non-existent preferences don't crash (tests our fix)
|
||||||
|
# This will trigger load attempts for keys that don't exist
|
||||||
|
# Our fix should prevent map entries from being created
|
||||||
|
client.button_command(load_button.key)
|
||||||
|
|
||||||
|
# Wait for the final load test to complete
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(final_load_complete, timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Final load test did not complete within timeout")
|
@@ -48,6 +48,7 @@ async def test_scheduler_pool(
|
|||||||
4: loop.create_future(),
|
4: loop.create_future(),
|
||||||
5: loop.create_future(),
|
5: loop.create_future(),
|
||||||
6: loop.create_future(),
|
6: loop.create_future(),
|
||||||
|
7: loop.create_future(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_output(line: str) -> None:
|
def check_output(line: str) -> None:
|
||||||
@@ -69,9 +70,10 @@ async def test_scheduler_pool(
|
|||||||
new_alloc_count += 1
|
new_alloc_count += 1
|
||||||
|
|
||||||
# Track phase completion
|
# Track phase completion
|
||||||
for phase_num in range(1, 7):
|
for phase_num in range(1, 8):
|
||||||
if (
|
if (
|
||||||
f"Phase {phase_num} complete" in line
|
f"Phase {phase_num} complete" in line
|
||||||
|
and phase_num in phase_futures
|
||||||
and not phase_futures[phase_num].done()
|
and not phase_futures[phase_num].done()
|
||||||
):
|
):
|
||||||
phase_futures[phase_num].set_result(True)
|
phase_futures[phase_num].set_result(True)
|
||||||
@@ -102,6 +104,7 @@ async def test_scheduler_pool(
|
|||||||
"run_phase_4",
|
"run_phase_4",
|
||||||
"run_phase_5",
|
"run_phase_5",
|
||||||
"run_phase_6",
|
"run_phase_6",
|
||||||
|
"run_phase_7",
|
||||||
"run_complete",
|
"run_complete",
|
||||||
}
|
}
|
||||||
assert expected_services.issubset(service_names), (
|
assert expected_services.issubset(service_names), (
|
||||||
@@ -111,7 +114,7 @@ async def test_scheduler_pool(
|
|||||||
# Get service objects
|
# Get service objects
|
||||||
phase_services = {
|
phase_services = {
|
||||||
num: next(s for s in services if s.name == f"run_phase_{num}")
|
num: next(s for s in services if s.name == f"run_phase_{num}")
|
||||||
for num in range(1, 7)
|
for num in range(1, 8)
|
||||||
}
|
}
|
||||||
complete_service = next(s for s in services if s.name == "run_complete")
|
complete_service = next(s for s in services if s.name == "run_complete")
|
||||||
|
|
||||||
@@ -146,6 +149,11 @@ async def test_scheduler_pool(
|
|||||||
await asyncio.wait_for(phase_futures[6], timeout=1.0)
|
await asyncio.wait_for(phase_futures[6], timeout=1.0)
|
||||||
await asyncio.sleep(0.1) # Let Phase 6 timeouts complete
|
await asyncio.sleep(0.1) # Let Phase 6 timeouts complete
|
||||||
|
|
||||||
|
# Phase 7: Same-named defer optimization
|
||||||
|
client.execute_service(phase_services[7], {})
|
||||||
|
await asyncio.wait_for(phase_futures[7], timeout=1.0)
|
||||||
|
await asyncio.sleep(0.05) # Let the single defer execute
|
||||||
|
|
||||||
# Complete test
|
# Complete test
|
||||||
client.execute_service(complete_service, {})
|
client.execute_service(complete_service, {})
|
||||||
await asyncio.wait_for(test_complete_future, timeout=0.5)
|
await asyncio.wait_for(test_complete_future, timeout=0.5)
|
||||||
@@ -166,7 +174,7 @@ async def test_scheduler_pool(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Verify all test phases ran
|
# Verify all test phases ran
|
||||||
for phase_num in range(1, 7):
|
for phase_num in range(1, 8):
|
||||||
assert phase_futures[phase_num].done(), f"Phase {phase_num} did not complete"
|
assert phase_futures[phase_num].done(), f"Phase {phase_num} did not complete"
|
||||||
|
|
||||||
# Verify pool behavior
|
# Verify pool behavior
|
||||||
@@ -180,8 +188,8 @@ async def test_scheduler_pool(
|
|||||||
size = int(match.group(1))
|
size = int(match.group(1))
|
||||||
max_pool_size = max(max_pool_size, size)
|
max_pool_size = max(max_pool_size, size)
|
||||||
|
|
||||||
# Pool can grow up to its maximum of 10
|
# Pool can grow up to its maximum of 5
|
||||||
assert max_pool_size <= 10, f"Pool grew beyond maximum ({max_pool_size})"
|
assert max_pool_size <= 5, f"Pool grew beyond maximum ({max_pool_size})"
|
||||||
|
|
||||||
# Log summary for debugging
|
# Log summary for debugging
|
||||||
print("\nScheduler Pool Test Summary (Python Orchestrated):")
|
print("\nScheduler Pool Test Summary (Python Orchestrated):")
|
||||||
|
204
tests/unit_tests/test_coroutine.py
Normal file
204
tests/unit_tests/test_coroutine.py
Normal file
@@ -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"]
|
Reference in New Issue
Block a user