[core] add support for custom platform (#7616)

Co-authored-by: Tomasz Duda <tomaszduda23@gmai.com>
This commit is contained in:
tomaszduda23 2025-01-23 00:06:07 +01:00 committed by GitHub
parent 5a103543c4
commit 0c032bc431
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 57 additions and 22 deletions

View File

@ -19,6 +19,7 @@ from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS
CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"]
IS_TARGET_PLATFORM = True
COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_BK72XX,

View File

@ -64,6 +64,7 @@ from .gpio import esp32_pin_to_code # noqa
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
CONF_RELEASE = "release"

View File

@ -34,6 +34,7 @@ from .gpio import PinInitialState, add_pin_initial_states_array
CODEOWNERS = ["@esphome/core"]
_LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def set_core_data(config):

View File

@ -17,6 +17,7 @@ from .gpio import host_pin_to_code # noqa
CODEOWNERS = ["@esphome/core", "@clydebarrow"]
AUTO_LOAD = ["network", "preferences"]
IS_TARGET_PLATFORM = True
def set_core_data(config):

View File

@ -47,6 +47,7 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def _detect_variant(value):

View File

@ -27,6 +27,7 @@ from .gpio import rp2040_pin_to_code # noqa
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@jesserockz"]
AUTO_LOAD = ["preferences"]
IS_TARGET_PLATFORM = True
def set_core_data(config):

View File

@ -19,6 +19,8 @@ from .boards import RTL87XX_BOARD_PINS, RTL87XX_BOARDS
CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"]
IS_TARGET_PLATFORM = True
COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_RTL87XX,

View File

@ -22,7 +22,6 @@ from esphome.const import (
CONF_PACKAGES,
CONF_PLATFORM,
CONF_SUBSTITUTIONS,
TARGET_PLATFORMS,
)
from esphome.core import CORE, DocumentRange, EsphomeError
import esphome.core.config as core_config
@ -833,7 +832,7 @@ def validate_config(
result[CONF_ESPHOME] = config[CONF_ESPHOME]
result.add_output_path([CONF_ESPHOME], CONF_ESPHOME)
try:
core_config.preload_core_config(config, result)
target_platform = core_config.preload_core_config(config, result)
except vol.Invalid as err:
result.add_error(err)
return result
@ -845,9 +844,9 @@ def validate_config(
cv.All(cv.version_number, cv.validate_esphome_version)(min_version)
# First run platform validation steps
for key in TARGET_PLATFORMS:
if key in config:
result.add_validation_step(LoadValidationStep(key, config[key]))
result.add_validation_step(
LoadValidationStep(target_platform, config[target_platform])
)
result.run_validation_steps()
if result.errors:

View File

@ -15,15 +15,6 @@ PLATFORM_LIBRETINY_OLDSTYLE = "libretiny"
PLATFORM_RP2040 = "rp2040"
PLATFORM_RTL87XX = "rtl87xx"
TARGET_PLATFORMS = [
PLATFORM_BK72XX,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_HOST,
PLATFORM_LIBRETINY_OLDSTYLE,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
]
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}

View File

@ -1,6 +1,7 @@
import logging
import multiprocessing
import os
from pathlib import Path
from esphome import automation
import esphome.codegen as cg
@ -28,7 +29,6 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_VERSION,
KEY_CORE,
TARGET_PLATFORMS,
__version__ as ESPHOME_VERSION,
)
from esphome.core import CORE, coroutine_with_priority
@ -174,7 +174,31 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema(
)
def preload_core_config(config, result):
def _is_target_platform(name):
from esphome.loader import get_component
try:
if get_component(name, True).is_target_platform:
return True
except KeyError:
pass
return False
def _list_target_platforms():
target_platforms = []
root = Path(__file__).parents[1]
for path in (root / "components").iterdir():
if not path.is_dir():
continue
if not (path / "__init__.py").is_file():
continue
if _is_target_platform(path.name):
target_platforms += [path.name]
return target_platforms
def preload_core_config(config, result) -> str:
with cv.prepend_path(CONF_ESPHOME):
conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME])
@ -187,12 +211,16 @@ def preload_core_config(config, result):
conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name)
CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH])
target_platforms = [key for key in TARGET_PLATFORMS if key in config]
target_platforms = []
for domain, _ in config.items():
if _is_target_platform(domain):
target_platforms += [domain]
if not target_platforms:
raise cv.Invalid(
"Platform missing. You must include one of the available platform keys: "
+ ", ".join(TARGET_PLATFORMS),
+ ", ".join(_list_target_platforms()),
[CONF_ESPHOME],
)
if len(target_platforms) > 1:
@ -202,6 +230,7 @@ def preload_core_config(config, result):
)
config[CONF_ESPHOME] = conf
return target_platforms[0]
def include_file(path, basename):

View File

@ -52,6 +52,10 @@ class ComponentManifest:
def is_platform_component(self) -> bool:
return getattr(self.module, "IS_PLATFORM_COMPONENT", False)
@property
def is_target_platform(self) -> bool:
return getattr(self.module, "IS_TARGET_PLATFORM", False)
@property
def config_schema(self) -> Optional[Any]:
return getattr(self.module, "CONFIG_SCHEMA", None)
@ -169,13 +173,15 @@ def install_custom_components_meta_finder():
install_meta_finder(custom_components_dir)
def _lookup_module(domain):
def _lookup_module(domain, exception):
if domain in _COMPONENT_CACHE:
return _COMPONENT_CACHE[domain]
try:
module = importlib.import_module(f"esphome.components.{domain}")
except ImportError as e:
if exception:
raise
if "No module named" in str(e):
_LOGGER.info(
"Unable to import component %s: %s", domain, str(e), exc_info=False
@ -184,6 +190,8 @@ def _lookup_module(domain):
_LOGGER.error("Unable to import component %s:", domain, exc_info=True)
return None
except Exception: # pylint: disable=broad-except
if exception:
raise
_LOGGER.error("Unable to load component %s:", domain, exc_info=True)
return None
@ -192,14 +200,14 @@ def _lookup_module(domain):
return manif
def get_component(domain):
def get_component(domain, exception=False):
assert "." not in domain
return _lookup_module(domain)
return _lookup_module(domain, exception)
def get_platform(domain, platform):
full = f"{platform}.{domain}"
return _lookup_module(full)
return _lookup_module(full, False)
_COMPONENT_CACHE = {}