[core] Add type annotations to component Python (10/11) (#18347)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
Jesse Hills
2026-08-22 10:31:47 -05:00
committed by GitHub
co-authored by Jonathan Swoboda
parent ef1d77885d
commit ea10f94376
30 changed files with 230 additions and 114 deletions
+4 -1
View File
@@ -1,3 +1,5 @@
from typing import Any
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components.esp32 import ( from esphome.components.esp32 import (
@@ -16,6 +18,7 @@ from esphome.components.esp32 import (
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266 from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
from esphome.core import CORE from esphome.core import CORE
from esphome.types import ConfigType
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@@ -225,7 +228,7 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
} }
def validate_adc_pin(value): def validate_adc_pin(value: Any) -> ConfigType | str:
if str(value).upper() == "VCC": if str(value).upper() == "VCC":
if CORE.is_rp2: if CORE.is_rp2:
return pins.internal_gpio_input_pin_schema(29) return pins.internal_gpio_input_pin_schema(29)
+3 -3
View File
@@ -52,7 +52,7 @@ _attenuation = cv.enum(ATTENUATION_MODES, lower=True)
_sampling_mode = cv.enum(SAMPLING_MODES, lower=True) _sampling_mode = cv.enum(SAMPLING_MODES, lower=True)
def validate_config(config): def validate_config(config: ConfigType) -> ConfigType:
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto": if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set") raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
@@ -120,7 +120,7 @@ CONFIG_SCHEMA = cv.All(
CONF_ADC_CHANNEL_ID = "adc_channel_id" CONF_ADC_CHANNEL_ID = "adc_channel_id"
def _overlay_io_channels(): def _overlay_io_channels() -> str:
channel_count = CORE.data[CONF_ADC_CHANNEL_ID] channel_count = CORE.data[CONF_ADC_CHANNEL_ID]
entries = ", ".join(f"<&adc {channel_id}>" for channel_id in range(channel_count)) entries = ", ".join(f"<&adc {channel_id}>" for channel_id in range(channel_count))
return f""" return f"""
@@ -132,7 +132,7 @@ def _overlay_io_channels():
""" """
async def to_code(config): async def to_code(config: ConfigType) -> None:
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)
await sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
+26 -10
View File
@@ -1,5 +1,6 @@
import base64 import base64
import logging import logging
from typing import Any
from esphome import automation from esphome import automation
from esphome.automation import Condition from esphome.automation import Condition
@@ -129,7 +130,7 @@ def _register_provisioning_source(config: ConfigType) -> ConfigType:
return config return config
def validate_encryption_key(value): def validate_encryption_key(value: Any) -> str:
value = cv.string_strict(value) value = cv.string_strict(value)
try: try:
decoded = base64.b64decode(value, validate=True) decoded = base64.b64decode(value, validate=True)
@@ -217,7 +218,7 @@ def _auto_detect_supports_response(config: ConfigType) -> ConfigType:
return config return config
def _validate_supports_response(value): def _validate_supports_response(value: Any) -> str:
"""Validate supports_response after auto-detection has set the value.""" """Validate supports_response after auto-detection has set the value."""
return cv.enum(SUPPORTS_RESPONSE_OPTIONS, lower=True)(value) return cv.enum(SUPPORTS_RESPONSE_OPTIONS, lower=True)(value)
@@ -256,7 +257,7 @@ ENCRYPTION_SCHEMA = cv.Schema(
) )
def _encryption_schema(config): def _encryption_schema(config: ConfigType | None) -> ConfigType:
if config is None: if config is None:
config = {} config = {}
return ENCRYPTION_SCHEMA(config) return ENCRYPTION_SCHEMA(config)
@@ -393,7 +394,7 @@ async def to_code(config: ConfigType) -> None:
if actions := config.get(CONF_ACTIONS, []): if actions := config.get(CONF_ACTIONS, []):
# Collect all triggers first, then register all at once with initializer_list # Collect all triggers first, then register all at once with initializer_list
triggers: list[cg.Pvariable] = [] triggers: list[cg.MockObj] = []
for conf in actions: for conf in actions:
func_args: list[tuple[MockObj, str]] = [] func_args: list[tuple[MockObj, str]] = []
service_template_args: list[MockObj] = [] # User service argument types service_template_args: list[MockObj] = [] # User service argument types
@@ -581,7 +582,7 @@ async def homeassistant_service_to_code(
action_id: ID, action_id: ID,
template_arg: cg.TemplateArguments, template_arg: cg.TemplateArguments,
args: TemplateArgsType, args: TemplateArgsType,
): ) -> MockObj:
cg.add_define("USE_API_HOMEASSISTANT_SERVICES") cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
serv = await cg.get_variable(config[CONF_ID]) serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, False) var = cg.new_Pvariable(action_id, template_arg, serv, False)
@@ -647,7 +648,7 @@ async def homeassistant_service_to_code(
return var return var
def validate_homeassistant_event(value): def validate_homeassistant_event(value: Any) -> str:
value = cv.string(value) value = cv.string(value)
if not value.startswith("esphome."): if not value.startswith("esphome."):
raise cv.Invalid( raise cv.Invalid(
@@ -676,7 +677,12 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
HOMEASSISTANT_EVENT_ACTION_SCHEMA, HOMEASSISTANT_EVENT_ACTION_SCHEMA,
synchronous=True, synchronous=True,
) )
async def homeassistant_event_to_code(config, action_id, template_arg, args): async def homeassistant_event_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
cg.add_define("USE_API_HOMEASSISTANT_SERVICES") cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
serv = await cg.get_variable(config[CONF_ID]) serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True) var = cg.new_Pvariable(action_id, template_arg, serv, True)
@@ -724,7 +730,12 @@ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA, HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
synchronous=True, synchronous=True,
) )
async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args): async def homeassistant_tag_scanned_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
cg.add_define("USE_API_HOMEASSISTANT_SERVICES") cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
serv = await cg.get_variable(config[CONF_ID]) serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True) var = cg.new_Pvariable(action_id, template_arg, serv, True)
@@ -740,7 +751,7 @@ CONF_SUCCESS = "success"
CONF_ERROR_MESSAGE = "error_message" CONF_ERROR_MESSAGE = "error_message"
def _validate_api_respond_data(config): def _validate_api_respond_data(config: ConfigType) -> ConfigType:
"""Set flag during validation so AUTO_LOAD can include json component.""" """Set flag during validation so AUTO_LOAD can include json component."""
if CONF_DATA in config: if CONF_DATA in config:
CORE.data.setdefault(DOMAIN, {})[CONF_CAPTURE_RESPONSE] = True CORE.data.setdefault(DOMAIN, {})[CONF_CAPTURE_RESPONSE] = True
@@ -824,7 +835,12 @@ API_CONNECTED_CONDITION_SCHEMA = cv.Schema(
@automation.register_condition( @automation.register_condition(
"api.connected", APIConnectedCondition, API_CONNECTED_CONDITION_SCHEMA "api.connected", APIConnectedCondition, API_CONNECTED_CONDITION_SCHEMA
) )
async def api_connected_to_code(config, condition_id, template_arg, args): async def api_connected_to_code(
config: ConfigType,
condition_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(condition_id, template_arg) var = cg.new_Pvariable(condition_id, template_arg)
templ = await cg.templatable(config[CONF_STATE_SUBSCRIPTION_ONLY], args, cg.bool_) templ = await cg.templatable(config[CONF_STATE_SUBSCRIPTION_ONLY], args, cg.bool_)
cg.add(var.set_state_subscription_only(templ)) cg.add(var.set_state_subscription_only(templ))
+13 -7
View File
@@ -16,14 +16,15 @@ from esphome.const import (
DEVICE_CLASS_RESTART, DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE, DEVICE_CLASS_UPDATE,
) )
from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import ( from esphome.core.entity_helpers import (
entity_duplicate_validator, entity_duplicate_validator,
queue_entity_register, queue_entity_register,
setup_device_class, setup_device_class,
setup_entity, setup_entity,
) )
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.types import ConfigType, SafeExpType
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -88,7 +89,7 @@ _CALLBACK_AUTOMATIONS = (
@setup_entity("button") @setup_entity("button")
async def setup_button_core_(var, config): async def setup_button_core_(var: MockObj, config: ConfigType) -> None:
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS) await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
setup_device_class(config) setup_device_class(config)
@@ -101,7 +102,7 @@ async def setup_button_core_(var, config):
await web_server.add_entity_config(var, web_server_config) await web_server.add_entity_config(var, web_server_config)
async def register_button(var, config): async def register_button(var: MockObj, config: ConfigType) -> None:
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
queue_entity_register("button", config) queue_entity_register("button", config)
@@ -109,7 +110,7 @@ async def register_button(var, config):
await setup_button_core_(var, config) await setup_button_core_(var, config)
async def new_button(config, *args): async def new_button(config: ConfigType, *args: SafeExpType) -> MockObj:
var = cg.new_Pvariable(config[CONF_ID], *args) var = cg.new_Pvariable(config[CONF_ID], *args)
await register_button(var, config) await register_button(var, config)
return var return var
@@ -125,11 +126,16 @@ BUTTON_PRESS_SCHEMA = maybe_simple_id(
@automation.register_action( @automation.register_action(
"button.press", PressAction, BUTTON_PRESS_SCHEMA, synchronous=True "button.press", PressAction, BUTTON_PRESS_SCHEMA, synchronous=True
) )
async def button_press_to_code(config, action_id, template_arg, args): async def button_press_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@coroutine_with_priority(CoroPriority.CORE) @coroutine_with_priority(CoroPriority.CORE)
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_global(button_ns.using) cg.add_global(button_ns.using)
+21 -8
View File
@@ -1,3 +1,5 @@
from typing import Any
from esphome import automation from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
@@ -48,13 +50,19 @@ from esphome.const import (
CONF_VISUAL, CONF_VISUAL,
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority from esphome.core import CORE, ID, CoroPriority, Lambda, coroutine_with_priority
from esphome.core.entity_helpers import ( from esphome.core.entity_helpers import (
entity_duplicate_validator, entity_duplicate_validator,
queue_entity_register, queue_entity_register,
setup_entity, setup_entity,
) )
from esphome.cpp_generator import LambdaExpression, MockObjClass from esphome.cpp_generator import (
LambdaExpression,
MockObj,
MockObjClass,
TemplateArgsType,
)
from esphome.types import ConfigType, SafeExpType
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -132,7 +140,7 @@ VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Schema(
) )
def visual_temperature_step(value): def visual_temperature_step(value: Any) -> ConfigType:
# Allow defining target/current temperature steps separately # Allow defining target/current temperature steps separately
if isinstance(value, dict): if isinstance(value, dict):
return VISUAL_TEMPERATURE_STEP_SCHEMA(value) return VISUAL_TEMPERATURE_STEP_SCHEMA(value)
@@ -273,7 +281,7 @@ def climate_schema(
@setup_entity("climate") @setup_entity("climate")
async def setup_climate_core_(var, config): async def setup_climate_core_(var: MockObj, config: ConfigType) -> None:
visual = config.get(CONF_VISUAL, {}) visual = config.get(CONF_VISUAL, {})
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:
cg.add_define("USE_CLIMATE_VISUAL_OVERRIDES") cg.add_define("USE_CLIMATE_VISUAL_OVERRIDES")
@@ -443,7 +451,7 @@ async def setup_climate_core_(var, config):
await web_server.add_entity_config(var, web_server_config) await web_server.add_entity_config(var, web_server_config)
async def register_climate(var, config): async def register_climate(var: MockObj, config: ConfigType) -> None:
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
queue_entity_register("climate", config) queue_entity_register("climate", config)
@@ -451,7 +459,7 @@ async def register_climate(var, config):
await setup_climate_core_(var, config) await setup_climate_core_(var, config)
async def new_climate(config, *args): async def new_climate(config: ConfigType, *args: SafeExpType) -> MockObj:
var = cg.new_Pvariable(config[CONF_ID], *args) var = cg.new_Pvariable(config[CONF_ID], *args)
await register_climate(var, config) await register_climate(var, config)
return var return var
@@ -485,7 +493,12 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
CLIMATE_CONTROL_ACTION_SCHEMA, CLIMATE_CONTROL_ACTION_SCHEMA,
synchronous=True, synchronous=True,
) )
async def climate_control_to_code(config, action_id, template_arg, args): async def climate_control_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
# All configured fields are folded into a single stateless lambda whose # All configured fields are folded into a single stateless lambda whose
@@ -549,5 +562,5 @@ async def climate_control_to_code(config, action_id, template_arg, args):
@coroutine_with_priority(CoroPriority.CORE) @coroutine_with_priority(CoroPriority.CORE)
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_global(climate_ns.using) cg.add_global(climate_ns.using)
+30 -10
View File
@@ -46,7 +46,7 @@ from esphome.core.entity_helpers import (
setup_entity, setup_entity,
) )
from esphome.cpp_generator import LambdaExpression, MockObj, MockObjClass from esphome.cpp_generator import LambdaExpression, MockObj, MockObjClass
from esphome.types import ConfigType, TemplateArgsType from esphome.types import ConfigType, SafeExpType, TemplateArgsType
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -162,7 +162,7 @@ _COVER_SCHEMA = (
_COVER_SCHEMA.add_extra(entity_duplicate_validator("cover")) _COVER_SCHEMA.add_extra(entity_duplicate_validator("cover"))
def _validate_mqtt_state_topics(config): def _validate_mqtt_state_topics(config: ConfigType) -> ConfigType:
if config.get(CONF_MQTT_JSON_STATE_PAYLOAD): if config.get(CONF_MQTT_JSON_STATE_PAYLOAD):
if CONF_POSITION_STATE_TOPIC in config: if CONF_POSITION_STATE_TOPIC in config:
raise cv.Invalid( raise cv.Invalid(
@@ -201,7 +201,7 @@ def cover_schema(
@setup_entity("cover") @setup_entity("cover")
async def setup_cover_core_(var, config): async def setup_cover_core_(var: MockObj, config: ConfigType) -> None:
setup_device_class(config) setup_device_class(config)
if CONF_ON_OPEN in config: if CONF_ON_OPEN in config:
@@ -235,7 +235,7 @@ async def setup_cover_core_(var, config):
await web_server.add_entity_config(var, web_server_config) await web_server.add_entity_config(var, web_server_config)
async def register_cover(var, config): async def register_cover(var: MockObj, config: ConfigType) -> None:
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
queue_entity_register("cover", config) queue_entity_register("cover", config)
@@ -243,7 +243,7 @@ async def register_cover(var, config):
await setup_cover_core_(var, config) await setup_cover_core_(var, config)
async def new_cover(config, *args): async def new_cover(config: ConfigType, *args: SafeExpType) -> MockObj:
var = cg.new_Pvariable(config[CONF_ID], *args) var = cg.new_Pvariable(config[CONF_ID], *args)
await register_cover(var, config) await register_cover(var, config)
return var return var
@@ -259,7 +259,12 @@ COVER_ACTION_SCHEMA = maybe_simple_id(
@automation.register_action( @automation.register_action(
"cover.open", OpenAction, COVER_ACTION_SCHEMA, synchronous=True "cover.open", OpenAction, COVER_ACTION_SCHEMA, synchronous=True
) )
async def cover_open_to_code(config, action_id, template_arg, args): async def cover_open_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@@ -267,7 +272,12 @@ async def cover_open_to_code(config, action_id, template_arg, args):
@automation.register_action( @automation.register_action(
"cover.close", CloseAction, COVER_ACTION_SCHEMA, synchronous=True "cover.close", CloseAction, COVER_ACTION_SCHEMA, synchronous=True
) )
async def cover_close_to_code(config, action_id, template_arg, args): async def cover_close_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@@ -275,7 +285,12 @@ async def cover_close_to_code(config, action_id, template_arg, args):
@automation.register_action( @automation.register_action(
"cover.stop", StopAction, COVER_ACTION_SCHEMA, synchronous=True "cover.stop", StopAction, COVER_ACTION_SCHEMA, synchronous=True
) )
async def cover_stop_to_code(config, action_id, template_arg, args): async def cover_stop_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@@ -283,7 +298,12 @@ async def cover_stop_to_code(config, action_id, template_arg, args):
@automation.register_action( @automation.register_action(
"cover.toggle", ToggleAction, COVER_ACTION_SCHEMA, synchronous=True "cover.toggle", ToggleAction, COVER_ACTION_SCHEMA, synchronous=True
) )
async def cover_toggle_to_code(config, action_id, template_arg, args): async def cover_toggle_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@@ -421,5 +441,5 @@ automation.register_condition(
@coroutine_with_priority(CoroPriority.CORE) @coroutine_with_priority(CoroPriority.CORE)
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_global(cover_ns.using) cg.add_global(cover_ns.using)
@@ -2,6 +2,7 @@ import base64
from pathlib import Path from pathlib import Path
import re import re
import secrets import secrets
from typing import Any
import requests import requests
from ruamel.yaml import YAML from ruamel.yaml import YAML
@@ -13,6 +14,7 @@ import esphome.config_validation as cv
from esphome.const import CONF_ESPHOME, CONF_PROJECT, CONF_REF, CONF_WIFI from esphome.const import CONF_ESPHOME, CONF_PROJECT, CONF_REF, CONF_WIFI
import esphome.final_validate as fv import esphome.final_validate as fv
from esphome.happy_eyeballs import ensure_happy_eyeballs from esphome.happy_eyeballs import ensure_happy_eyeballs
from esphome.types import ConfigType
from esphome.yaml_util import dump from esphome.yaml_util import dump
dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import") dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import")
@@ -23,14 +25,14 @@ DEPENDENCIES = ["api"]
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
def validate_import_url(value): def validate_import_url(value: Any) -> str:
value = cv.string_strict(value) value = cv.string_strict(value)
value = cv.Length(max=255)(value) value = cv.Length(max=255)(value)
validate_source_shorthand(value) validate_source_shorthand(value)
return value return value
def validate_full_url(config): def validate_full_url(config: ConfigType) -> ConfigType:
if not config[CONF_IMPORT_FULL_CONFIG]: if not config[CONF_IMPORT_FULL_CONFIG]:
return config return config
source = validate_source_shorthand(config[CONF_PACKAGE_IMPORT_URL]) source = validate_source_shorthand(config[CONF_PACKAGE_IMPORT_URL])
@@ -55,7 +57,7 @@ CONFIG_SCHEMA = cv.All(
) )
def _final_validate(config): def _final_validate(config: ConfigType) -> None:
full_config = fv.full_config.get()[CONF_ESPHOME] full_config = fv.full_config.get()[CONF_ESPHOME]
if CONF_PROJECT not in full_config: if CONF_PROJECT not in full_config:
raise cv.Invalid( raise cv.Invalid(
@@ -73,7 +75,7 @@ wifi:
""" """
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_define("USE_DASHBOARD_IMPORT") cg.add_define("USE_DASHBOARD_IMPORT")
url = config[CONF_PACKAGE_IMPORT_URL] url = config[CONF_PACKAGE_IMPORT_URL]
if config[CONF_IMPORT_FULL_CONFIG]: if config[CONF_IMPORT_FULL_CONFIG]:
+2 -1
View File
@@ -12,6 +12,7 @@ from esphome.const import (
PlatformFramework, PlatformFramework,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.types import ConfigType
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
DEPENDENCIES = ["logger"] DEPENDENCIES = ["logger"]
@@ -45,7 +46,7 @@ CONFIG_SCHEMA = cv.All(
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
if CORE.using_zephyr: if CORE.using_zephyr:
zephyr_add_prj_conf("HWINFO", True) zephyr_add_prj_conf("HWINFO", True)
# gdb thread support # gdb thread support
+2 -1
View File
@@ -21,6 +21,7 @@ from esphome.const import (
UNIT_MILLISECOND, UNIT_MILLISECOND,
UNIT_PERCENT, UNIT_PERCENT,
) )
from esphome.types import ConfigType
from . import ( # noqa: F401 pylint: disable=unused-import from . import ( # noqa: F401 pylint: disable=unused-import
CONF_DEBUG_ID, CONF_DEBUG_ID,
@@ -111,7 +112,7 @@ CONFIG_SCHEMA = {
} }
async def to_code(config): async def to_code(config: ConfigType) -> None:
debug_component = await cg.get_variable(config[CONF_DEBUG_ID]) debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if free_conf := config.get(CONF_FREE): if free_conf := config.get(CONF_FREE):
+2 -1
View File
@@ -7,6 +7,7 @@ from esphome.const import (
ICON_CHIP, ICON_CHIP,
ICON_RESTART, ICON_RESTART,
) )
from esphome.types import ConfigType
from . import ( # noqa: F401 pylint: disable=unused-import from . import ( # noqa: F401 pylint: disable=unused-import
CONF_DEBUG_ID, CONF_DEBUG_ID,
@@ -33,7 +34,7 @@ CONFIG_SCHEMA = cv.Schema(
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
debug_component = await cg.get_variable(config[CONF_DEBUG_ID]) debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if CONF_DEVICE in config: if CONF_DEVICE in config:
+10 -8
View File
@@ -3,6 +3,7 @@ from pathlib import Path
import platform import platform
import re import re
import subprocess import subprocess
from typing import Any
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
@@ -31,6 +32,7 @@ from esphome.core import (
from esphome.core.config import BOARD_MAX_LENGTH from esphome.core.config import BOARD_MAX_LENGTH
from esphome.helpers import IS_MACOS, copy_file_if_changed from esphome.helpers import IS_MACOS, copy_file_if_changed
from esphome.platformio.toolchain import copy_ccache_script from esphome.platformio.toolchain import copy_ccache_script
from esphome.storage_json import StorageJSON
from esphome.types import ConfigType from esphome.types import ConfigType
from .boards import BOARDS, ESP8266_LD_SCRIPTS from .boards import BOARDS, ESP8266_LD_SCRIPTS
@@ -88,7 +90,7 @@ def lambdas_use_scanf_float(config: ConfigType) -> bool:
return False return False
def set_core_data(config): def set_core_data(config: ConfigType) -> ConfigType:
CORE.data[KEY_ESP8266] = {} CORE.data[KEY_ESP8266] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP8266 CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP8266
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
@@ -102,7 +104,7 @@ def set_core_data(config):
return config return config
def get_download_types(storage_json): def get_download_types(storage_json: StorageJSON) -> list[dict[str, str]]:
"""Binary-download entries for a built ESP8266 firmware. """Binary-download entries for a built ESP8266 firmware.
Used by device-builder (esphome/device-builder), via Used by device-builder (esphome/device-builder), via
@@ -157,7 +159,7 @@ ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 2, 0)
ARDUINO_4_PLATFORM_VERSION = cv.Version(4, 2, 1) ARDUINO_4_PLATFORM_VERSION = cv.Version(4, 2, 1)
def _arduino_check_versions(value): def _arduino_check_versions(value: ConfigType) -> ConfigType:
value = value.copy() value = value.copy()
lookups = { lookups = {
"dev": (cv.Version(3, 1, 2), "https://github.com/esp8266/Arduino.git"), "dev": (cv.Version(3, 1, 2), "https://github.com/esp8266/Arduino.git"),
@@ -200,7 +202,7 @@ def _arduino_check_versions(value):
return value return value
def _parse_platform_version(value): def _parse_platform_version(value: Any) -> str:
try: try:
# if platform version is a valid version constraint, prefix the default package # if platform version is a valid version constraint, prefix the default package
cv.platformio_version_constraint(value) cv.platformio_version_constraint(value)
@@ -275,7 +277,7 @@ def check_rosetta() -> None:
@coroutine_with_priority(CoroPriority.PLATFORM) @coroutine_with_priority(CoroPriority.PLATFORM)
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add(esp8266_ns.setup_preferences()) cg.add(esp8266_ns.setup_preferences())
cg.add_platformio_option("lib_ldf_mode", "off") cg.add_platformio_option("lib_ldf_mode", "off")
@@ -504,7 +506,7 @@ ESP8266_EXCEPTION_CODES = {
} }
def _decode_pc(config, addr): def _decode_pc(config: ConfigType, addr: str) -> None:
from esphome.platformio import toolchain from esphome.platformio import toolchain
idedata = toolchain.get_idedata(config) idedata = toolchain.get_idedata(config)
@@ -525,7 +527,7 @@ def _decode_pc(config, addr):
_LOGGER.warning("Decoded %s", translation) _LOGGER.warning("Decoded %s", translation)
def _parse_register(config, regex, line): def _parse_register(config: ConfigType, regex: re.Pattern[str], line: str) -> None:
match = regex.match(line) match = regex.match(line)
if match is not None: if match is not None:
_decode_pc(config, match.group(1)) _decode_pc(config, match.group(1))
@@ -549,7 +551,7 @@ STACKTRACE_BAD_ALLOC_RE = re.compile(
STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}")
def process_stacktrace(config, line, backtrace_state): def process_stacktrace(config: ConfigType, line: str, backtrace_state: bool) -> bool:
line = line.strip() line = line.strip()
# ESP8266 Exception type # ESP8266 Exception type
match = re.match(STACKTRACE_ESP8266_EXCEPTION_TYPE_RE, line) match = re.match(STACKTRACE_ESP8266_EXCEPTION_TYPE_RE, line)
+9 -6
View File
@@ -1,5 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import Any
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
@@ -18,6 +19,8 @@ from esphome.const import (
PLATFORM_ESP8266, PLATFORM_ESP8266,
) )
from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.cpp_generator import MockObj
from esphome.types import ConfigType
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
@@ -27,7 +30,7 @@ _LOGGER = logging.getLogger(__name__)
ESP8266GPIOPin = esp8266_ns.class_("ESP8266GPIOPin", cg.InternalGPIOPin) ESP8266GPIOPin = esp8266_ns.class_("ESP8266GPIOPin", cg.InternalGPIOPin)
def _lookup_pin(value): def _lookup_pin(value: str) -> int:
board = CORE.data[KEY_ESP8266][KEY_BOARD] board = CORE.data[KEY_ESP8266][KEY_BOARD]
board_pins = boards.ESP8266_BOARD_PINS.get(board, {}) board_pins = boards.ESP8266_BOARD_PINS.get(board, {})
@@ -42,7 +45,7 @@ def _lookup_pin(value):
raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.") raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.")
def _translate_pin(value): def _translate_pin(value: Any) -> int:
if isinstance(value, dict) or value is None: if isinstance(value, dict) or value is None:
raise cv.Invalid( raise cv.Invalid(
"This variable only supports pin numbers, not full pin schemas " "This variable only supports pin numbers, not full pin schemas "
@@ -69,7 +72,7 @@ _ESP_SDIO_PINS = {
} }
def validate_gpio_pin(value): def validate_gpio_pin(value: Any) -> int:
value = _translate_pin(value) value = _translate_pin(value)
if value < 0 or value > 17: if value < 0 or value > 17:
raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") raise cv.Invalid(f"ESP8266: Invalid pin number: {value}")
@@ -86,7 +89,7 @@ def validate_gpio_pin(value):
return value return value
def validate_supports(value): def validate_supports(value: ConfigType) -> ConfigType:
num = value[CONF_NUMBER] num = value[CONF_NUMBER]
mode = value[CONF_MODE] mode = value[CONF_MODE]
is_input = mode[CONF_INPUT] is_input = mode[CONF_INPUT]
@@ -160,7 +163,7 @@ class PinInitialState:
@pins.PIN_SCHEMA_REGISTRY.register(PLATFORM_ESP8266, ESP8266_PIN_SCHEMA) @pins.PIN_SCHEMA_REGISTRY.register(PLATFORM_ESP8266, ESP8266_PIN_SCHEMA)
async def esp8266_pin_to_code(config): async def esp8266_pin_to_code(config: ConfigType) -> MockObj:
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER] num = config[CONF_NUMBER]
mode = config[CONF_MODE] mode = config[CONF_MODE]
@@ -192,7 +195,7 @@ async def esp8266_pin_to_code(config):
@coroutine_with_priority(CoroPriority.WORKAROUNDS) @coroutine_with_priority(CoroPriority.WORKAROUNDS)
async def add_pin_initial_states_array(): async def add_pin_initial_states_array() -> None:
# 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][
KEY_PIN_INITIAL_STATES KEY_PIN_INITIAL_STATES
+10 -7
View File
@@ -5,6 +5,7 @@ import io
import logging import logging
from pathlib import Path from pathlib import Path
import re import re
from typing import Any
from PIL import Image, UnidentifiedImageError from PIL import Image, UnidentifiedImageError
@@ -75,12 +76,12 @@ def compute_local_image_path(value: str | ConfigType) -> Path:
return external_files.compute_local_file_path(DOMAIN, url) return external_files.compute_local_file_path(DOMAIN, url)
def local_path(value): def local_path(value: str | ConfigType) -> str:
value = value[CONF_PATH] if isinstance(value, dict) else value value = value[CONF_PATH] if isinstance(value, dict) else value
return str(CORE.relative_config_path(value)) return str(CORE.relative_config_path(value))
def download_file(url, path): def download_file(url: str, path: Path) -> str:
# The shared NETWORK_TIMEOUT applies; a per-caller timeout would be # The shared NETWORK_TIMEOUT applies; a per-caller timeout would be
# silently ignored on a per-run memo hit anyway (memos key by path). # silently ignored on a per-run memo hit anyway (memos key by path).
external_files.download_content(url, path) external_files.download_content(url, path)
@@ -98,7 +99,7 @@ def download_gh_svg(value: str | ConfigType, source: str) -> str:
return download_file(url, path) return download_file(url, path)
def download_image(value): def download_image(value: str | ConfigType) -> str:
value = value[CONF_URL] if isinstance(value, dict) else value value = value[CONF_URL] if isinstance(value, dict) else value
return download_file(value, compute_local_image_path(value)) return download_file(value, compute_local_image_path(value))
@@ -146,7 +147,7 @@ def _extract_entry_ref(entry: ConfigType) -> RemoteFile | None:
PREFETCH_FILES = external_files.single_stage_prefetch(_extract_entry_ref) PREFETCH_FILES = external_files.single_stage_prefetch(_extract_entry_ref)
def validate_file_shorthand(value): def validate_file_shorthand(value: Any) -> str:
value = cv.string_strict(value) value = cv.string_strict(value)
if (remote := _parse_remote_shorthand(value)) is not None: if (remote := _parse_remote_shorthand(value)) is not None:
return download_file(remote.url, remote.path) return download_file(remote.url, remote.path)
@@ -163,8 +164,8 @@ LOCAL_SCHEMA = cv.All(
) )
def mdi_schema(source): def mdi_schema(source: str) -> cv.All:
def validate_mdi(value): def validate_mdi(value: ConfigType) -> str:
return download_gh_svg(value, source) return download_gh_svg(value, source)
return cv.All( return cv.All(
@@ -259,7 +260,9 @@ async def new_image(config: ConfigType) -> MockObj:
return var return var
async def write_image(config, all_frames=False): async def write_image(
config: ConfigType, all_frames: bool = False
) -> tuple[MockObj, int, int, MockObj, MockObj, int]:
path = Path(config[CONF_FILE]) path = Path(config[CONF_FILE])
if not path.is_file(): if not path.is_file():
raise core.EsphomeError(f"Could not load image file {path}") raise core.EsphomeError(f"Could not load image file {path}")
+9 -3
View File
@@ -8,7 +8,8 @@ from esphome.const import (
CONF_TYPE, CONF_TYPE,
CONF_VALUE, CONF_VALUE,
) )
from esphome.core import CoroPriority, coroutine_with_priority from esphome.core import ID, CoroPriority, coroutine_with_priority
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType from esphome.types import ConfigType
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@@ -62,7 +63,7 @@ CONFIG_SCHEMA = _globals_schema
# Run with low priority so that namespaces are registered first # Run with low priority so that namespaces are registered first
@coroutine_with_priority(CoroPriority.LATE) @coroutine_with_priority(CoroPriority.LATE)
async def to_code(config): async def to_code(config: ConfigType) -> None:
type_ = cg.RawExpression(config[CONF_TYPE]) type_ = cg.RawExpression(config[CONF_TYPE])
restore = config[CONF_RESTORE_VALUE] restore = config[CONF_RESTORE_VALUE]
@@ -104,7 +105,12 @@ async def to_code(config):
), ),
synchronous=True, synchronous=True,
) )
async def globals_set_to_code(config, action_id, template_arg, args): async def globals_set_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
full_id, paren = await cg.get_variable_with_full_id(config[CONF_ID]) full_id, paren = await cg.get_variable_with_full_id(config[CONF_ID])
template_arg = cg.TemplateArguments(full_id.type, *template_arg) template_arg = cg.TemplateArguments(full_id.type, *template_arg)
var = cg.new_Pvariable(action_id, template_arg, paren) var = cg.new_Pvariable(action_id, template_arg, paren)
@@ -12,6 +12,7 @@ from esphome.const import (
CONF_PIN, CONF_PIN,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.types import ConfigType
from .. import gpio_ns from .. import gpio_ns
@@ -68,7 +69,7 @@ def _pin_shared_only_with_deep_sleep(pin_num: int) -> bool:
return any(path and path[0] == "deep_sleep" for path, _, _ in pin_users) return any(path and path[0] == "deep_sleep" for path, _, _ in pin_users)
def _final_validate(config) -> None: def _final_validate(config: ConfigType) -> None:
use_interrupt = config[CONF_USE_INTERRUPT] use_interrupt = config[CONF_USE_INTERRUPT]
if not use_interrupt: if not use_interrupt:
return return
@@ -124,7 +125,7 @@ def _final_validate(config) -> None:
FINAL_VALIDATE_SCHEMA = _final_validate FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = await binary_sensor.new_binary_sensor(config) var = await binary_sensor.new_binary_sensor(config)
await cg.register_component(var, config) await cg.register_component(var, config)
+2 -1
View File
@@ -3,6 +3,7 @@ import esphome.codegen as cg
from esphome.components.one_wire import OneWireBus from esphome.components.one_wire import OneWireBus
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.types import ConfigType
from .. import gpio_ns from .. import gpio_ns
@@ -18,7 +19,7 @@ CONFIG_SCHEMA = cv.Schema(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config: ConfigType) -> None:
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 -1
View File
@@ -3,6 +3,7 @@ import esphome.codegen as cg
from esphome.components import output from esphome.components import output
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.types import ConfigType
from .. import gpio_ns from .. import gpio_ns
@@ -16,7 +17,7 @@ CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await output.register_output(var, config) await output.register_output(var, config)
await cg.register_component(var, config) await cg.register_component(var, config)
+2 -1
View File
@@ -3,6 +3,7 @@ import esphome.codegen as cg
from esphome.components import switch from esphome.components import switch
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_INTERLOCK, CONF_PIN from esphome.const import CONF_INTERLOCK, CONF_PIN
from esphome.types import ConfigType
from .. import gpio_ns from .. import gpio_ns
@@ -24,7 +25,7 @@ CONFIG_SCHEMA = (
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = await switch.new_switch(config) var = await switch.new_switch(config)
await cg.register_component(var, config) await cg.register_component(var, config)
+9 -3
View File
@@ -1,13 +1,19 @@
from collections.abc import Callable, Iterable
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_ATTRIBUTE, CONF_ENTITY_ID, CONF_INTERNAL from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_INTERNAL
from esphome.cpp_generator import MockObj
from esphome.types import ConfigType
CODEOWNERS = ["@OttoWinter", "@esphome/core"] CODEOWNERS = ["@OttoWinter", "@esphome/core"]
homeassistant_ns = cg.esphome_ns.namespace("homeassistant") homeassistant_ns = cg.esphome_ns.namespace("homeassistant")
def validate_entity_domain(platform, supported_domains): def validate_entity_domain(
def validator(config): platform: str, supported_domains: Iterable[str]
) -> Callable[[ConfigType], ConfigType]:
def validator(config: ConfigType) -> ConfigType:
domain = config[CONF_ENTITY_ID].split(".", 1)[0] domain = config[CONF_ENTITY_ID].split(".", 1)[0]
if domain not in supported_domains: if domain not in supported_domains:
raise cv.Invalid( raise cv.Invalid(
@@ -34,7 +40,7 @@ HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA = cv.Schema(
) )
def setup_home_assistant_entity(var, config): def setup_home_assistant_entity(var: MockObj, config: ConfigType) -> None:
cg.add(var.set_entity_id(config[CONF_ENTITY_ID])) cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
if CONF_ATTRIBUTE in config: if CONF_ATTRIBUTE in config:
cg.add(var.set_attribute(config[CONF_ATTRIBUTE])) cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
@@ -1,5 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import binary_sensor from esphome.components import binary_sensor
from esphome.types import ConfigType
from .. import ( from .. import (
HOME_ASSISTANT_IMPORT_SCHEMA, HOME_ASSISTANT_IMPORT_SCHEMA,
@@ -18,7 +19,7 @@ CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(HomeassistantBinarySensor).ex
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = await binary_sensor.new_binary_sensor(config) var = await binary_sensor.new_binary_sensor(config)
await cg.register_component(var, config) await cg.register_component(var, config)
setup_home_assistant_entity(var, config) setup_home_assistant_entity(var, config)
@@ -1,6 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import number from esphome.components import number
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.types import ConfigType
from .. import ( from .. import (
HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA, HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA,
@@ -22,7 +23,7 @@ CONFIG_SCHEMA = (
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_define("USE_API_HOMEASSISTANT_SERVICES") cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
var = await number.new_number( var = await number.new_number(
config, config,
@@ -1,5 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import sensor from esphome.components import sensor
from esphome.types import ConfigType
from .. import ( from .. import (
HOME_ASSISTANT_IMPORT_SCHEMA, HOME_ASSISTANT_IMPORT_SCHEMA,
@@ -18,7 +19,7 @@ CONFIG_SCHEMA = sensor.sensor_schema(HomeassistantSensor, accuracy_decimals=1).e
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config) var = await sensor.new_sensor(config)
await cg.register_component(var, config) await cg.register_component(var, config)
setup_home_assistant_entity(var, config) setup_home_assistant_entity(var, config)
@@ -2,6 +2,7 @@ import esphome.codegen as cg
from esphome.components import switch from esphome.components import switch
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.types import ConfigType
from .. import ( from .. import (
HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA, HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA,
@@ -36,7 +37,7 @@ CONFIG_SCHEMA = cv.All(
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_define("USE_API_HOMEASSISTANT_SERVICES") cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
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)
@@ -1,5 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import text_sensor from esphome.components import text_sensor
from esphome.types import ConfigType
from .. import ( from .. import (
HOME_ASSISTANT_IMPORT_SCHEMA, HOME_ASSISTANT_IMPORT_SCHEMA,
@@ -18,7 +19,7 @@ CONFIG_SCHEMA = text_sensor.text_sensor_schema(HomeassistantTextSensor).extend(
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = await text_sensor.new_text_sensor(config) var = await text_sensor.new_text_sensor(config)
await cg.register_component(var, config) await cg.register_component(var, config)
setup_home_assistant_entity(var, config) setup_home_assistant_entity(var, config)
@@ -2,6 +2,7 @@ import esphome.codegen as cg
from esphome.components import time as time_ from esphome.components import time as time_
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_TIMEZONE from esphome.const import CONF_ID, CONF_TIMEZONE
from esphome.types import ConfigType
from .. import homeassistant_ns from .. import homeassistant_ns
@@ -16,7 +17,7 @@ CONFIG_SCHEMA = time_.TIME_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await time_.register_time(var, config) await time_.register_time(var, config)
await cg.register_component(var, config) await cg.register_component(var, config)
+3 -2
View File
@@ -11,6 +11,7 @@ from esphome.const import (
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.platformio.toolchain import copy_ccache_script from esphome.platformio.toolchain import copy_ccache_script
from esphome.types import ConfigType
from .const import KEY_HOST from .const import KEY_HOST
@@ -22,7 +23,7 @@ AUTO_LOAD = ["network", "preferences"]
IS_TARGET_PLATFORM = True IS_TARGET_PLATFORM = True
def set_core_data(config): def set_core_data(config: ConfigType) -> ConfigType:
CORE.data[KEY_HOST] = {} CORE.data[KEY_HOST] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_HOST CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_HOST
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host"
@@ -40,7 +41,7 @@ CONFIG_SCHEMA = cv.All(
) )
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_build_flag("-DUSE_HOST") cg.add_build_flag("-DUSE_HOST")
cg.add_define("USE_NATIVE_64BIT_TIME") cg.add_define("USE_NATIVE_64BIT_TIME")
# The prefs file finds stored preferences by key, so key migration is possible # The prefs file finds stored preferences by key, so key migration is possible
+6 -3
View File
@@ -1,4 +1,5 @@
import logging import logging
from typing import Any
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
@@ -14,6 +15,8 @@ from esphome.const import (
CONF_PULLDOWN, CONF_PULLDOWN,
CONF_PULLUP, CONF_PULLUP,
) )
from esphome.cpp_generator import MockObj
from esphome.types import ConfigType
from .const import host_ns from .const import host_ns
@@ -22,7 +25,7 @@ _LOGGER = logging.getLogger(__name__)
HostGPIOPin = host_ns.class_("HostGPIOPin", cg.InternalGPIOPin) HostGPIOPin = host_ns.class_("HostGPIOPin", cg.InternalGPIOPin)
def _translate_pin(value): def _translate_pin(value: Any) -> int | str:
if isinstance(value, dict) or value is None: if isinstance(value, dict) or value is None:
raise cv.Invalid( raise cv.Invalid(
"This variable only supports pin numbers, not full pin schemas " "This variable only supports pin numbers, not full pin schemas "
@@ -41,7 +44,7 @@ def _translate_pin(value):
return value return value
def validate_gpio_pin(value): def validate_gpio_pin(value: Any) -> int | str:
return _translate_pin(value) return _translate_pin(value)
@@ -53,7 +56,7 @@ HOST_PIN_SCHEMA = pins.gpio_base_schema(
@pins.PIN_SCHEMA_REGISTRY.register("host", HOST_PIN_SCHEMA) @pins.PIN_SCHEMA_REGISTRY.register("host", HOST_PIN_SCHEMA)
async def host_pin_to_code(config): async def host_pin_to_code(config: ConfigType) -> MockObj:
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER] num = config[CONF_NUMBER]
cg.add(var.set_pin(num)) cg.add(var.set_pin(num))
+2 -1
View File
@@ -2,6 +2,7 @@ import esphome.codegen as cg
from esphome.components import time as time_ from esphome.components import time as time_
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.types import ConfigType
CODEOWNERS = ["@clydebarrow"] CODEOWNERS = ["@clydebarrow"]
@@ -14,7 +15,7 @@ CONFIG_SCHEMA = time_.TIME_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config: ConfigType) -> None:
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)
await time_.register_time(var, config) await time_.register_time(var, config)
+17 -15
View File
@@ -1,6 +1,7 @@
import logging import logging
import re import re
import sys import sys
from typing import Any
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
@@ -52,9 +53,10 @@ from esphome.const import (
PLATFORM_RP2, PLATFORM_RP2,
PlatformFramework, PlatformFramework,
) )
from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.cpp_generator import MockObj from esphome.cpp_generator import MockObj
import esphome.final_validate as fv import esphome.final_validate as fv
from esphome.types import ConfigType
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@@ -96,13 +98,13 @@ CONF_SCL_PULLUP_ENABLED = "scl_pullup_enabled"
MULTI_CONF = True MULTI_CONF = True
def validate_device(value): def validate_device(value: str) -> str:
if not re.match(r"^/(?:[^/]+/)*[^/]+$", value): if not re.match(r"^/(?:[^/]+/)*[^/]+$", value):
raise cv.Invalid("Device must be an absolute device path (e.g., /dev/i2c-0)") raise cv.Invalid("Device must be an absolute device path (e.g., /dev/i2c-0)")
return value return value
def _bus_declare_type(value): def _bus_declare_type(value: Any) -> ID:
if CORE.is_esp32: if CORE.is_esp32:
return cv.declare_id(IDFI2CBus)(value) return cv.declare_id(IDFI2CBus)(value)
if CORE.using_arduino: if CORE.using_arduino:
@@ -114,7 +116,7 @@ def _bus_declare_type(value):
raise NotImplementedError raise NotImplementedError
def _rp2040_i2c_controller(pin): def _rp2040_i2c_controller(pin: int) -> int:
"""Return the I2C controller number (0 or 1) for a given RP2040/RP2350 GPIO pin. """Return the I2C controller number (0 or 1) for a given RP2040/RP2350 GPIO pin.
See RP2040 datasheet Table 2 (section 1.4.3, "GPIO Functions"): See RP2040 datasheet Table 2 (section 1.4.3, "GPIO Functions"):
@@ -125,7 +127,7 @@ def _rp2040_i2c_controller(pin):
return (pin // 2) % 2 return (pin // 2) % 2
def validate_config(config): def validate_config(config: ConfigType) -> ConfigType:
if CORE.is_esp32: if CORE.is_esp32:
return cv.require_framework_version( return cv.require_framework_version(
esp_idf=cv.Version(5, 4, 2), esp32_arduino=cv.Version(3, 2, 1) esp_idf=cv.Version(5, 4, 2), esp32_arduino=cv.Version(3, 2, 1)
@@ -142,7 +144,7 @@ def validate_config(config):
return config return config
def validate_host_config(config): def validate_host_config(config: ConfigType) -> ConfigType:
if CORE.is_host: if CORE.is_host:
# Host I2C is currently only supported on Linux # Host I2C is currently only supported on Linux
if not sys.platform.lower().startswith("linux"): if not sys.platform.lower().startswith("linux"):
@@ -229,7 +231,7 @@ CONFIG_SCHEMA = cv.All(
) )
def _final_validate(config): def _final_validate(config: ConfigType) -> None:
full_config = fv.full_config.get()[CONF_I2C] full_config = fv.full_config.get()[CONF_I2C]
if CORE.using_zephyr and len(full_config) > 1: if CORE.using_zephyr and len(full_config) > 1:
raise cv.Invalid("Second i2c is not implemented on Zephyr yet") raise cv.Invalid("Second i2c is not implemented on Zephyr yet")
@@ -281,7 +283,7 @@ FINAL_VALIDATE_SCHEMA = _final_validate
@coroutine_with_priority(CoroPriority.BUS) @coroutine_with_priority(CoroPriority.BUS)
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_global(i2c_ns.using) cg.add_global(i2c_ns.using)
cg.add_define("USE_I2C") cg.add_define("USE_I2C")
if CORE.is_esp32: if CORE.is_esp32:
@@ -358,7 +360,7 @@ async def to_code(config):
cg.add(var.set_lp_mode(bool(config[CONF_LOW_POWER_MODE]))) cg.add(var.set_lp_mode(bool(config[CONF_LOW_POWER_MODE])))
def i2c_device_schema(default_address): def i2c_device_schema(default_address: int | None) -> cv.Schema:
"""Create a schema for a i2c device. """Create a schema for a i2c device.
:param default_address: The default address of the i2c device, can be None to represent :param default_address: The default address of the i2c device, can be None to represent
@@ -375,7 +377,7 @@ def i2c_device_schema(default_address):
return cv.Schema(schema) return cv.Schema(schema)
async def register_i2c_device(var, config): async def register_i2c_device(var: MockObj, config: ConfigType) -> None:
"""Register an i2c device with the given config. """Register an i2c device with the given config.
Sets the i2c bus to use and the i2c address. Sets the i2c bus to use and the i2c address.
@@ -390,11 +392,11 @@ async def register_i2c_device(var, config):
def final_validate_device_schema( def final_validate_device_schema(
name: str, name: str,
*, *,
min_frequency: cv.frequency = None, min_frequency: Any = None,
max_frequency: cv.frequency = None, max_frequency: Any = None,
min_timeout: cv.time_period = None, min_timeout: Any = None,
max_timeout: cv.time_period = None, max_timeout: Any = None,
): ) -> cv.Schema:
hub_schema = {} hub_schema = {}
if (min_frequency is not None) and (max_frequency is not None): if (min_frequency is not None) and (max_frequency is not None):
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range( hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
+25 -9
View File
@@ -12,13 +12,14 @@ from esphome.const import (
CONF_ON_UNLOCK, CONF_ON_UNLOCK,
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import ( from esphome.core.entity_helpers import (
entity_duplicate_validator, entity_duplicate_validator,
queue_entity_register, queue_entity_register,
setup_entity, setup_entity,
) )
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.types import ConfigType, SafeExpType
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -102,7 +103,7 @@ _CALLBACK_AUTOMATIONS = (
@setup_entity("lock") @setup_entity("lock")
async def _setup_lock_core(var, config): async def _setup_lock_core(var: MockObj, config: ConfigType) -> None:
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS) await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
if mqtt_id := config.get(CONF_MQTT_ID): if mqtt_id := config.get(CONF_MQTT_ID):
@@ -113,7 +114,7 @@ async def _setup_lock_core(var, config):
await web_server.add_entity_config(var, web_server_config) await web_server.add_entity_config(var, web_server_config)
async def register_lock(var, config): async def register_lock(var: MockObj, config: ConfigType) -> None:
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
queue_entity_register("lock", config) queue_entity_register("lock", config)
@@ -121,7 +122,7 @@ async def register_lock(var, config):
await _setup_lock_core(var, config) await _setup_lock_core(var, config)
async def new_lock(config, *args): async def new_lock(config: ConfigType, *args: SafeExpType) -> MockObj:
var = cg.new_Pvariable(config[CONF_ID], *args) var = cg.new_Pvariable(config[CONF_ID], *args)
await register_lock(var, config) await register_lock(var, config)
return var return var
@@ -143,23 +144,38 @@ LOCK_ACTION_SCHEMA = maybe_simple_id(
@automation.register_action( @automation.register_action(
"lock.open", OpenAction, LOCK_ACTION_SCHEMA, synchronous=True "lock.open", OpenAction, LOCK_ACTION_SCHEMA, synchronous=True
) )
async def lock_action_to_code(config, action_id, template_arg, args): async def lock_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_condition("lock.is_locked", LockCondition, LOCK_ACTION_SCHEMA) @automation.register_condition("lock.is_locked", LockCondition, LOCK_ACTION_SCHEMA)
async def lock_is_on_to_code(config, condition_id, template_arg, args): async def lock_is_on_to_code(
config: ConfigType,
condition_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren, True) return cg.new_Pvariable(condition_id, template_arg, paren, True)
@automation.register_condition("lock.is_unlocked", LockCondition, LOCK_ACTION_SCHEMA) @automation.register_condition("lock.is_unlocked", LockCondition, LOCK_ACTION_SCHEMA)
async def lock_is_off_to_code(config, condition_id, template_arg, args): async def lock_is_off_to_code(
config: ConfigType,
condition_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren, False) return cg.new_Pvariable(condition_id, template_arg, paren, False)
@coroutine_with_priority(CoroPriority.CORE) @coroutine_with_priority(CoroPriority.CORE)
async def to_code(config): async def to_code(config: ConfigType) -> None:
cg.add_global(lock_ns.using) cg.add_global(lock_ns.using)