Merge branch 'esphome:dev' into lsm6ds_test_001

This commit is contained in:
NonaSuomy authored and GitHub committed 2026-06-03 10:28:21 -04:00
commit 31a80652fa
2371 files changed
+43404 -14921

No files matched your search

+623 -145
View File
File diff suppressed because it is too large. Load diff
+14 -7
View File
@@ -24,7 +24,7 @@ from .helpers import (
from .toolchain import find_tool, resolve_tool_path, run_tool
if TYPE_CHECKING:
from esphome.platformio_api import IDEData
from esphome.platformio.toolchain import IDEData
_LOGGER = logging.getLogger(__name__)
@@ -793,8 +793,11 @@ class MemoryAnalyzer:
"""Scan ESPHome source object files to map extern "C" symbols to components.
When no linker map file is available, this uses ``nm`` to scan ``.o`` files
under ``src/esphome/`` and build a symbol-to-component mapping. This catches
``extern "C"`` functions and other symbols that lack C++ namespace prefixes.
under ``src/`` (including ``src/main.cpp.o`` and everything beneath
``src/esphome/``) and build a symbol-to-component mapping. This catches
``extern "C"`` functions, the ESPHome-generated ``setup()``/``loop()``
entry points in ``main.cpp``, and other symbols that lack C++ namespace
prefixes.
Skips scanning if ``_source_symbol_map`` was already populated by
``_parse_map_file()``.
@@ -806,12 +809,12 @@ class MemoryAnalyzer:
if obj_dir is None:
return
# Find ESPHome source object files
esphome_src_dir = obj_dir / "src" / "esphome"
if not esphome_src_dir.is_dir():
# Scan all ESPHome-owned source object files: src/main.cpp.o and src/esphome/...
src_dir = obj_dir / "src"
if not src_dir.is_dir():
return
obj_files = sorted(esphome_src_dir.rglob("*.o"))
obj_files = sorted(src_dir.rglob("*.o"))
if not obj_files:
return
@@ -1064,6 +1067,10 @@ class MemoryAnalyzer:
if component_name in self.external_components:
return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}"
# ESPHome-generated entry point: src/main.cpp.o (contains setup()/loop())
if len(parts) >= 2 and parts[-2:] == ("src", "main.cpp.o"):
return _COMPONENT_CORE
# ESPHome core: src/esphome/core/... or src/esphome/...
if "core" in parts and "esphome" in parts:
return _COMPONENT_CORE
+7 -7
View File
@@ -6,6 +6,7 @@ from collections import defaultdict
from collections.abc import Callable
import heapq
from operator import itemgetter
from pathlib import Path
import sys
from typing import TYPE_CHECKING
@@ -509,7 +510,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append(
f"{_COMPONENT_CORE} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_core_symbols)} symbols):"
)
for i, (symbol, demangled, size) in enumerate(large_core_symbols):
for i, (_symbol, demangled, size) in enumerate(large_core_symbols):
# Core symbols only track (symbol, demangled, size) without section info,
# so we don't show section labels here
lines.append(
@@ -601,7 +602,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append(
f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B & storage ({len(large_symbols)} symbols):"
)
for i, (symbol, demangled, size, section) in enumerate(large_symbols):
for i, (_symbol, demangled, size, section) in enumerate(large_symbols):
lines.append(
f"{i + 1}. {self._format_symbol_with_section(demangled, size, section)}"
)
@@ -640,7 +641,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append(
f" Symbols > {self.RAM_SYMBOL_SIZE_THRESHOLD} B ({len(large_ram_syms)}):"
)
for symbol, demangled, size, section in large_ram_syms[:10]:
for _symbol, demangled, size, section in large_ram_syms[:10]:
# Format section label consistently by stripping leading dot
section_label = section.lstrip(".") if section else ""
display_name = _format_pstorage_name(demangled)
@@ -699,7 +700,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
content = "\n".join(lines)
if output_file:
with open(output_file, "w", encoding="utf-8") as f:
with Path(output_file).open("w", encoding="utf-8") as f:
f.write(content)
else:
print(content)
@@ -737,9 +738,8 @@ def main():
# Load build directory
import json
from pathlib import Path
from esphome.platformio_api import IDEData
from esphome.platformio.toolchain import IDEData
build_path = Path(build_dir)
@@ -785,7 +785,7 @@ def main():
if not idedata_path.exists():
continue
try:
with open(idedata_path, encoding="utf-8") as f:
with idedata_path.open(encoding="utf-8") as f:
raw_data = json.load(f)
idedata = IDEData(raw_data)
print(f"Loaded idedata from: {idedata_path}", file=sys.stderr)
+1 -1
View File
@@ -154,7 +154,7 @@ def batch_demangle(
failed_count = 0
for original, stripped, prefix, demangled in zip(
symbols, symbols_stripped, symbols_prefixes, demangled_lines
symbols, symbols_stripped, symbols_prefixes, demangled_lines, strict=True
):
# Add back any prefix that was removed
demangled = _restore_symbol_prefix(prefix, stripped, demangled)
+1 -2
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import logging
import os
from pathlib import Path
import subprocess
from typing import TYPE_CHECKING
@@ -37,7 +36,7 @@ def _find_in_platformio_packages(tool_name: str) -> str | None:
Full path to the tool or None if not found
"""
# Get PlatformIO packages directory
platformio_home = Path(os.path.expanduser("~/.platformio/packages"))
platformio_home = Path("~/.platformio/packages").expanduser()
if not platformio_home.exists():
return None
+1 -1
View File
@@ -45,7 +45,7 @@ class AsyncThreadRunner(threading.Thread, Generic[_T]):
async def _runner(self) -> None:
try:
self.result = await self._coro_factory()
except Exception as exc: # pylint: disable=broad-except
except Exception as exc: # noqa: BLE001 # pylint: disable=broad-except
# Capture all exceptions so ``event`` is always set — otherwise a
# crash would hang the waiter forever.
self.exception = exc
+2 -3
View File
@@ -127,7 +127,7 @@ def validate_potentially_or_condition(value):
return validate_condition(value)
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
DelayAction = cg.esphome_ns.class_("DelayAction", Action)
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
StatelessLambdaAction = cg.esphome_ns.class_("StatelessLambdaAction", Action)
IfAction = cg.esphome_ns.class_("IfAction", Action)
@@ -396,7 +396,6 @@ async def delay_action_to_code(
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_component(var, {})
template_ = await cg.templatable(config, args, cg.uint32)
cg.add(var.set_delay(template_))
return var
@@ -597,7 +596,7 @@ async def component_resume_action_to_code(
comp = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, comp)
if CONF_UPDATE_INTERVAL in config:
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int)
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, cg.uint32)
cg.add(var.set_update_interval(template_))
return var
+128 -40
View File
@@ -3,23 +3,27 @@
import json
from pathlib import Path
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32 import get_esp32_variant, idf_version
import esphome.config_validation as cv
from esphome.core import CORE
from esphome.helpers import mkdir_p, write_file_if_changed
def get_available_components() -> list[str] | None:
"""Get list of available ESP-IDF components from project_description.json.
"""Get list of built-in ESP-IDF components from project_description.json.
Returns only internal ESP-IDF components, excluding external/managed
components (from idf_component.yml).
Excludes ``src``, IDF-managed components (``managed_components/``), and
converted PIO libs (``pio_components/``). Returns ``None`` if the build
dir or ``project_description.json`` isn't ready yet.
"""
if CORE.build_path is None:
return None
project_desc = Path(CORE.build_path) / "build" / "project_description.json"
if not project_desc.exists():
return None
try:
with open(project_desc, encoding="utf-8") as f:
with project_desc.open(encoding="utf-8") as f:
data = json.load(f)
component_info = data.get("build_component_info", {})
@@ -30,9 +34,9 @@ def get_available_components() -> list[str] | None:
if name == "src":
continue
# Exclude managed/external components
# Exclude IDF-managed and converted-PIO components (external).
comp_dir = info.get("dir", "")
if "managed_components" in comp_dir:
if "managed_components" in comp_dir or "pio_components" in comp_dir:
continue
result.append(name)
@@ -47,23 +51,90 @@ def has_discovered_components() -> bool:
return get_available_components() is not None
def get_project_cmakelists() -> str:
"""Generate the top-level CMakeLists.txt for ESP-IDF project."""
def get_project_cmakelists(minimal: bool = False) -> str:
"""Generate the top-level CMakeLists.txt for ESP-IDF project.
When ``minimal`` is true, omit ``ESPHOME_PROJECT_BUILTIN_COMPONENTS``
since ``project_description.json`` may be stale on the first write.
"""
# Get IDF target from ESP32 variant (e.g., ESP32S3 -> esp32s3)
variant = get_esp32_variant()
idf_target = variant.lower().replace("-", "")
# Extract compile definitions from build flags (-DXXX -> XXX)
compile_defs = [flag for flag in CORE.build_flags if flag.startswith("-D")]
# esp_idf_size 2.x (bundled with IDF >=6.0) made NG the default and
# removed the --ng flag; on 1.x (IDF 5.5) --ng is required to get
# --format=raw because the legacy mode doesn't support it.
size_ng_flag = "--ng" if idf_version() < cv.Version(6, 0, 0) else ""
# Project-wide compile options: -D defines and -W warning flags (skip
# -Wl, linker flags — those go on the src component via
# target_link_options below). Emitted via idf_build_set_property so the
# flags propagate to every IDF component (including managed ones like
# esphome__micro-mp3) rather than just src/. Required so suppressions
# like ``-Wno-error=maybe-uninitialized`` actually silence warnings in
# third-party components we don't author.
project_compile_opts = [
flag
for flag in sorted(CORE.build_flags)
if flag.startswith("-D")
or (flag.startswith("-W") and not flag.startswith("-Wl,"))
]
extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{compile_def}" APPEND)'
for compile_def in compile_defs
f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)'
for flag in project_compile_opts
)
# Per-project list exposed as a CMake variable so converted PIO libs
# can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking
# project-specific names into their cached CMakeLists.
#
# Emit via idf_build_set_property (not plain set()) so the value is
# serialised into build_properties.temp.cmake and visible to IDF's
# early requirements-expansion pass (component_get_requirements.cmake
# runs as a separate CMake script invocation that doesn't load the
# project's top-level CMakeLists; without this, ${ESPHOME_PROJECT_
# MANAGED_COMPONENTS} in a converted-lib REQUIRES expands to empty).
from esphome.components.esp32 import get_managed_component_require_names
managed_components_property = "\n".join(
f"idf_build_set_property(ESPHOME_PROJECT_MANAGED_COMPONENTS {name} APPEND)"
for name in get_managed_component_require_names()
)
# Built-in IDF components exposed via our own property (not IDF's
# __COMPONENT_REQUIRES_COMMON, which would append them to every
# component's REQUIRES including real IDF components). Referenced by
# src/CMakeLists and by each converted PIO lib's CMakeLists. Skipped
# on minimal writes because project_description.json may be stale.
builtin_components_property = (
""
if minimal
else "\n".join(
f"idf_build_set_property(ESPHOME_PROJECT_BUILTIN_COMPONENTS {name} APPEND)"
for name in sorted(get_available_components() or [])
)
)
return f"""\
# Auto-generated by ESPHome
cmake_minimum_required(VERSION 3.16)
# On Windows, Ninja can fail with:
# "CreateProcess: The parameter is incorrect (is the command line too long?)"
# when compiler/linker command lines exceed the OS length limit.
#
# The following settings force CMake/Ninja to use *response files* (@file.rsp)
# to pass long lists of includes, objects, and other arguments indirectly,
# avoiding command-line length limits and fixing the build failure.
#
# This is especially useful for large ESP-IDF / ESPHome projects with many
# source files or include directories.
set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
set(IDF_TARGET {idf_target})
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
@@ -71,50 +142,67 @@ include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
{extra_compile_options}
{managed_components_property}
{builtin_components_property}
project({CORE.name})
# Emit raw JSON size data for ESPHome to read post-build.
add_custom_command(
TARGET ${{CMAKE_PROJECT_NAME}}.elf POST_BUILD
COMMAND ${{PYTHON}} -m esp_idf_size {size_ng_flag} --format=raw
-o ${{CMAKE_BINARY_DIR}}/esp_idf_size.json
${{CMAKE_PROJECT_NAME}}.map
WORKING_DIRECTORY ${{CMAKE_BINARY_DIR}}
VERBATIM
)
"""
def get_component_cmakelists(minimal: bool = False) -> str:
"""Generate the main component CMakeLists.txt."""
idf_requires = [] if minimal else (get_available_components() or [])
requires_str = " ".join(idf_requires)
def get_component_cmakelists() -> str:
"""Generate the main component CMakeLists.txt.
# Extract compile options (-W flags, excluding linker flags)
compile_opts = [
flag
for flag in CORE.build_flags
if flag.startswith("-W") and not flag.startswith("-Wl,")
]
compile_opts_str = "\n ".join(sorted(compile_opts)) if compile_opts else ""
# Extract linker options (-Wl, flags)
REQUIRES pulls in the discovered built-in IDF components via the
project-level variables set in the top-level CMakeLists.
"""
# Extract linker options (-Wl, flags). Compile flags (-D, -W) are
# emitted project-wide via idf_build_set_property in
# get_project_cmakelists so they reach every component, not just src/.
link_opts = [flag for flag in CORE.build_flags if flag.startswith("-Wl,")]
link_opts_str = "\n ".join(sorted(link_opts)) if link_opts else ""
return f"""\
# Auto-generated by ESPHome
file(GLOB_RECURSE app_sources
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
)
# CONFIGURE_DEPENDS asks CMake to re-check the glob each build so test
# runs that reuse the build dir don't compile stale source paths. It's
# invalid in script mode (cmake -P), which is how IDF's
# component_get_requirements.cmake includes us, so skip it there.
if(CMAKE_SCRIPT_MODE_FILE)
file(GLOB_RECURSE app_sources
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
)
else()
file(GLOB_RECURSE app_sources CONFIGURE_DEPENDS
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
)
endif()
idf_component_register(
SRCS ${{app_sources}}
INCLUDE_DIRS "." "esphome"
REQUIRES {requires_str}
REQUIRES ${{ESPHOME_PROJECT_BUILTIN_COMPONENTS}}
)
# Apply C++ standard
target_compile_features(${{COMPONENT_LIB}} PUBLIC cxx_std_20)
# ESPHome compile options
target_compile_options(${{COMPONENT_LIB}} PUBLIC
{compile_opts_str}
)
# ESPHome linker options
target_link_options(${{COMPONENT_LIB}} PUBLIC
{link_opts_str}
@@ -130,11 +218,11 @@ def write_project(minimal: bool = False) -> None:
# Write top-level CMakeLists.txt
write_file_if_changed(
CORE.relative_build_path("CMakeLists.txt"),
get_project_cmakelists(),
get_project_cmakelists(minimal=minimal),
)
# Write component CMakeLists.txt in src/
write_file_if_changed(
CORE.relative_src_path("CMakeLists.txt"),
get_component_cmakelists(minimal=minimal),
get_component_cmakelists(),
)
+1 -2
View File
@@ -1,7 +1,7 @@
from esphome.const import __version__
from esphome.core import CORE
from esphome.helpers import mkdir_p, read_file, write_file_if_changed
from esphome.writer import find_begin_end, update_storage_json
from esphome.writer import find_begin_end
INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ==========="
INI_AUTO_GENERATE_END = "; =========== AUTO GENERATED CODE END ============"
@@ -58,7 +58,6 @@ def get_ini_content():
def write_ini(content):
update_storage_json()
path = CORE.relative_build_path("platformio.ini")
if path.is_file():
+20 -91
View File
@@ -98,11 +98,13 @@ _KNOWN_FILE_EXTENSIONS = frozenset(
)
# Matches !secret references in YAML text. This is intentionally a simple
# regex scan rather than a YAML parse — it may match inside comments or
# multi-line strings, which is the conservative direction (include more
# secrets rather than fewer).
_SECRET_RE = re.compile(r"!secret\s+(\S+)")
# Matches !secret references in YAML text. An optional surrounding
# quote pair around the key is allowed and ignored: YAML treats
# ``!secret 'foo'`` and ``!secret foo`` as the same key. This is
# intentionally a simple regex scan rather than a YAML parse — it may
# match inside comments or multi-line strings, which is the conservative
# direction (include more secrets rather than fewer).
_SECRET_RE = re.compile(r"""!secret\s+['"]?([^\s'"]+)""")
def _find_used_secret_keys(yaml_files: list[Path]) -> set[str]:
@@ -258,42 +260,20 @@ class ConfigBundleCreator:
def _discover_yaml_includes(self) -> None:
"""Discover YAML files loaded during config parsing.
Deliberately uses a fresh re-parse and force-loads every deferred
``IncludeFile`` to include *all* potentially-reachable includes,
even branches not selected by the local substitutions. Bundles are
meant to be compiled on another system where command-line
substitution overrides may choose a different branch — e.g.
``!include network/${eth_model}/config.yaml`` must ship every
candidate so the remote build can pick any one.
Entries with unresolved substitution variables in the filename
path are skipped with a warning (they cannot be resolved without
the substitution pass).
Secrets files are tracked separately so we can filter them to
only include the keys this config actually references.
Delegates to :func:`yaml_util.discover_user_yaml_files`, which does a
fresh re-parse and force-loads every deferred ``IncludeFile`` so that
*all* potentially-reachable includes are captured (even branches not
selected by local substitutions). Bundles are meant to be compiled on
another system where command-line substitution overrides may choose a
different branch — e.g. ``!include network/${eth_model}/config.yaml``
must ship every candidate so the remote build can pick any one.
"""
# Must be a fresh parse: IncludeFile.load() caches its result in
# _content, and we discover files by listening for loader calls. On
# an already-parsed tree the cache is populated, .load() returns
# without calling the loader, the listener never fires, and the
# referenced files would be silently dropped from the bundle.
with yaml_util.track_yaml_loads() as loaded_files:
try:
data = yaml_util.load_yaml(self._config_path)
except EsphomeError:
_LOGGER.debug(
"Bundle: re-loading YAML for include discovery failed, "
"proceeding with partial file list"
)
else:
_force_load_include_files(data)
for fpath in loaded_files:
if fpath == self._config_path.resolve():
discovered = yaml_util.discover_user_yaml_files(self._config_path)
self._secrets_paths.update(discovered.secrets)
config_resolved = self._config_path.resolve()
for fpath in discovered.files:
if fpath == config_resolved:
continue # Already added as config
if fpath.name in const.SECRETS_FILES:
self._secrets_paths.add(fpath)
self._add_file(fpath)
def _discover_component_files(self) -> None:
@@ -432,7 +412,7 @@ class ConfigBundleCreator:
@staticmethod
def _add_to_tar(tar: tarfile.TarFile, bf: BundleFile) -> None:
"""Add a BundleFile to the tar archive with deterministic metadata."""
with open(bf.source, "rb") as f:
with bf.source.open("rb") as f:
_add_bytes_to_tar(tar, bf.path, f.read())
@@ -623,57 +603,6 @@ def _add_bytes_to_tar(tar: tarfile.TarFile, name: str, data: bytes) -> None:
tar.addfile(info, io.BytesIO(data))
def _force_load_include_files(obj: Any, _seen: set[int] | None = None) -> None:
"""Recursively resolve any ``IncludeFile`` instances in a YAML tree.
Nested ``!include`` returns a deferred ``IncludeFile`` that is only
resolved during the substitution pass. During bundle discovery we need
the referenced files to actually load so the ``track_yaml_loads``
listener fires for them.
``IncludeFile`` instances with unresolved substitution variables in the
filename cannot be loaded — we skip and warn about those.
"""
if _seen is None:
_seen = set()
if isinstance(obj, yaml_util.IncludeFile):
if id(obj) in _seen:
return
_seen.add(id(obj))
if obj.has_unresolved_expressions():
_LOGGER.warning(
"Bundle: cannot resolve !include %s (referenced from %s) "
"with substitutions in path",
obj.file,
obj.parent_file,
)
return
try:
loaded = obj.load()
except EsphomeError as err:
_LOGGER.warning(
"Bundle: failed to load !include %s (referenced from %s): %s",
obj.file,
obj.parent_file,
err,
)
return
_force_load_include_files(loaded, _seen)
elif isinstance(obj, dict):
if id(obj) in _seen:
return
_seen.add(id(obj))
for value in obj.values():
_force_load_include_files(value, _seen)
elif isinstance(obj, (list, tuple)):
if id(obj) in _seen:
return
_seen.add(id(obj))
for item in obj:
_force_load_include_files(item, _seen)
def _resolve_include_path(include_path: Any) -> Path | None:
"""Resolve an include path to absolute, skipping system includes."""
if isinstance(include_path, str) and include_path.startswith("<"):
+76
View File
@@ -0,0 +1,76 @@
"""Validated-config cache for the upload/logs fast path.
compile dumps the validated config to <data_dir>/storage/<file>.validated.yaml;
the next upload/logs for that YAML reuses it instead of running the full
read_config pipeline. YAML round-trip (yaml_util.dump/load_yaml) keeps
!lambda/!include/IDs/paths intact; mtime gates staleness.
"""
from __future__ import annotations
import logging
from pathlib import Path
from esphome.core import CORE
from esphome.helpers import write_file
from esphome.storage_json import StorageJSON, ext_storage_path
from esphome.types import ConfigType
_LOGGER = logging.getLogger(__name__)
def compiled_config_path(config_filename: str) -> Path:
"""Path to the cached validated config alongside the storage sidecar."""
return CORE.data_dir / "storage" / f"{config_filename}.validated.yaml"
def _cache_is_fresh(cache_path: Path, source_path: Path) -> bool:
"""True iff the cache file exists and isn't older than the source."""
try:
return cache_path.stat().st_mtime >= source_path.stat().st_mtime
except OSError:
return False
def save_compiled_config(config: ConfigType) -> None:
"""Write the validated-config cache. Always-write so mtime stays fresh.
Mode 0600 because show_secrets=True resolves !secret inline.
Failures are non-fatal: the fast path falls back to read_config.
"""
from esphome import yaml_util
try:
rendered = yaml_util.dump(config, show_secrets=True)
write_file(compiled_config_path(CORE.config_filename), rendered, private=True)
except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
_LOGGER.debug("Skipping compiled config cache write: %s", err)
def load_compiled_config(conf_path: Path) -> ConfigType | None:
"""Load the cached validated config and apply storage metadata to CORE.
Returns None (caller falls back to read_config) when the cache is
missing, older than the source YAML, unparseable, or the sidecar
is incomplete.
"""
cache_path = compiled_config_path(conf_path.name)
if not _cache_is_fresh(cache_path, conf_path):
return None
from esphome import yaml_util
try:
config = yaml_util.load_yaml(cache_path, clear_secrets=False)
except Exception: # noqa: BLE001 # pylint: disable=broad-except
return None
storage = StorageJSON.load(ext_storage_path(conf_path.name))
if storage is None:
return None
# apply_to_core assumes a real compile wrote the sidecar; wizard-only
# sidecars leave both of these unset and can't drive upload/logs.
if not storage.core_platform and not storage.target_platform:
return None
storage.apply_to_core()
return config
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace a01nyub {
namespace esphome::a01nyub {
static const char *const TAG = "a01nyub.sensor";
@@ -42,5 +41,4 @@ void A01nyubComponent::check_buffer_() {
void A01nyubComponent::dump_config() { LOG_SENSOR("", "A01nyub Sensor", this); }
} // namespace a01nyub
} // namespace esphome
} // namespace esphome::a01nyub
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace a01nyub {
namespace esphome::a01nyub {
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
@@ -23,5 +22,4 @@ class A01nyubComponent : public sensor::Sensor, public Component, public uart::U
std::vector<uint8_t> buffer_;
};
} // namespace a01nyub
} // namespace esphome
} // namespace esphome::a01nyub
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace a02yyuw {
namespace esphome::a02yyuw {
static const char *const TAG = "a02yyuw.sensor";
@@ -41,5 +40,4 @@ void A02yyuwComponent::check_buffer_() {
void A02yyuwComponent::dump_config() { LOG_SENSOR("", "A02yyuw Sensor", this); }
} // namespace a02yyuw
} // namespace esphome
} // namespace esphome::a02yyuw
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace a02yyuw {
namespace esphome::a02yyuw {
class A02yyuwComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
@@ -23,5 +22,4 @@ class A02yyuwComponent : public sensor::Sensor, public Component, public uart::U
std::vector<uint8_t> buffer_;
};
} // namespace a02yyuw
} // namespace esphome
} // namespace esphome::a02yyuw
+2 -4
View File
@@ -1,8 +1,7 @@
#include "a4988.h"
#include "esphome/core/log.h"
namespace esphome {
namespace a4988 {
namespace esphome::a4988 {
static const char *const TAG = "a4988.stepper";
@@ -51,5 +50,4 @@ void A4988::loop() {
this->step_pin_->digital_write(false);
}
} // namespace a4988
} // namespace esphome
} // namespace esphome::a4988
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/hal.h"
#include "esphome/components/stepper/stepper.h"
namespace esphome {
namespace a4988 {
namespace esphome::a4988 {
class A4988 : public stepper::Stepper, public Component {
public:
@@ -25,5 +24,4 @@ class A4988 : public stepper::Stepper, public Component {
HighFrequencyLoopRequester high_freq_;
};
} // namespace a4988
} // namespace esphome
} // namespace esphome::a4988
@@ -1,8 +1,7 @@
#include "adalight_light_effect.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adalight {
namespace esphome::adalight {
static const char *const TAG = "adalight_light_effect";
@@ -129,7 +128,7 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
uint8_t *led_data = &frame_[6];
for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
auto white = std::min({led_data[0], led_data[1], led_data[2]});
it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
}
@@ -138,5 +137,4 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
return CONSUMED;
}
} // namespace adalight
} // namespace esphome
} // namespace esphome::adalight
@@ -6,8 +6,7 @@
#include <vector>
namespace esphome {
namespace adalight {
namespace esphome::adalight {
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
public:
@@ -35,5 +34,4 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
std::vector<uint8_t> frame_;
};
} // namespace adalight
} // namespace esphome
} // namespace esphome::adalight
+2 -4
View File
@@ -17,8 +17,7 @@
#include <zephyr/drivers/adc.h>
#endif
namespace esphome {
namespace adc {
namespace esphome::adc {
#ifdef USE_ESP32
// clang-format off
@@ -162,5 +161,4 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#endif
};
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
+2 -4
View File
@@ -1,8 +1,7 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.common";
@@ -79,5 +78,4 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.esp32";
@@ -364,7 +363,6 @@ float ADCSensor::sample_autorange_() {
return final_result;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_ESP32
@@ -11,8 +11,7 @@ ADC_MODE(ADC_VCC)
#include <Arduino.h>
#endif // USE_ADC_SENSOR_VCC
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.esp8266";
@@ -55,7 +54,6 @@ float ADCSensor::sample() {
return aggr.aggregate() / 1024.0f;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_ESP8266
@@ -3,8 +3,7 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.libretiny";
@@ -48,7 +47,6 @@ float ADCSensor::sample() {
return aggr.aggregate() / 1000.0f;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_LIBRETINY
+2 -4
View File
@@ -15,8 +15,7 @@
#define PICO_VSYS_PIN 29 // NOLINT(cppcoreguidelines-macro-usage)
#endif
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.rp2040";
@@ -98,7 +97,6 @@ float ADCSensor::sample() {
return aggr.aggregate() * 3.3f / 4096.0f * coeff;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_RP2040
+2 -4
View File
@@ -5,8 +5,7 @@
#include "hal/nrf_saadc.h"
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.zephyr";
@@ -202,6 +201,5 @@ float ADCSensor::sample() {
return val_mv / 1000.0f;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif
+2 -4
View File
@@ -1,8 +1,7 @@
#include "adc128s102.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
static const char *const TAG = "adc128s102";
@@ -28,5 +27,4 @@ uint16_t ADC128S102::read_data(uint8_t channel) {
return digital_value;
}
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/hal.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
class ADC128S102 : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
@@ -19,5 +18,4 @@ class ADC128S102 : public Component,
uint16_t read_data(uint8_t channel);
};
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
static const char *const TAG = "adc128s102.sensor";
@@ -18,5 +17,4 @@ void ADC128S102Sensor::dump_config() {
float ADC128S102Sensor::sample() { return this->parent_->read_data(this->channel_); }
void ADC128S102Sensor::update() { this->publish_state(this->sample()); }
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
@@ -7,8 +7,7 @@
#include "../adc128s102.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
class ADC128S102Sensor : public PollingComponent,
public Parented<ADC128S102>,
@@ -24,5 +23,4 @@ class ADC128S102Sensor : public PollingComponent,
protected:
uint8_t channel_;
};
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
@@ -1,8 +1,7 @@
#include "addressable_light_display.h"
#include "esphome/core/log.h"
namespace esphome {
namespace addressable_light {
namespace esphome::addressable_light {
static const char *const TAG = "addressable_light.display";
@@ -66,5 +65,4 @@ void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Col
this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
}
}
} // namespace addressable_light
} // namespace esphome
} // namespace esphome::addressable_light
@@ -7,8 +7,7 @@
#include <vector>
namespace esphome {
namespace addressable_light {
namespace esphome::addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer {
public:
@@ -61,5 +60,4 @@ class AddressableLightDisplay : public display::DisplayBuffer {
optional<uint32_t> last_effect_index_;
optional<std::function<int(int, int)>> pixel_mapper_f_;
};
} // namespace addressable_light
} // namespace esphome
} // namespace esphome::addressable_light
+2 -4
View File
@@ -13,8 +13,7 @@
#include <cinttypes>
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
static const char *const TAG = "ade7880";
@@ -313,5 +312,4 @@ void ADE7880::reset_device_() {
this->store_.reset_pending = true;
}
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
+2 -4
View File
@@ -16,8 +16,7 @@
#include "ade7880_registers.h"
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
struct NeutralChannel {
void set_current(sensor::Sensor *sens) { this->current = sens; }
@@ -125,5 +124,4 @@ class ADE7880 : public i2c::I2CDevice, public PollingComponent {
void write_u32_register16_(uint16_t a_register, uint32_t value);
};
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
+2 -4
View File
@@ -9,8 +9,7 @@
#include "ade7880.h"
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
// adapted from https://stackoverflow.com/a/55912127/1886371
template<size_t Bits, typename T> inline T sign_extend(const T &v) noexcept {
@@ -97,5 +96,4 @@ void ADE7880::write_u32_register16_(uint16_t a_register, uint32_t value) {
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
}
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
@@ -4,8 +4,7 @@
// Source: https://www.analog.com/media/en/technical-documentation/application-notes/AN-1127.pdf
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
// DSP Data Memory RAM registers
constexpr uint16_t AIGAIN = 0x4380;
@@ -242,5 +241,4 @@ constexpr uint8_t DSPWP_SET_RO = (1 << 7);
// DSPWP_SEL Register Bits
constexpr uint8_t DSPWP_SEL_SET = 0xAD;
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
@@ -3,8 +3,7 @@
#include <cinttypes>
namespace esphome {
namespace ade7953_base {
namespace esphome::ade7953_base {
static const char *const TAG = "ade7953";
@@ -160,5 +159,4 @@ void ADE7953::update() {
ADE_PUBLISH(frequency, 223750.0f, 1 + val_16);
}
} // namespace ade7953_base
} // namespace esphome
} // namespace esphome::ade7953_base
@@ -6,8 +6,7 @@
#include <vector>
namespace esphome {
namespace ade7953_base {
namespace esphome::ade7953_base {
static constexpr uint8_t PGA_V_8 =
0x007; // PGA_V, (R/W) Default: 0x00, Unsigned, Voltage channel gain configuration (Bits[2:0])
@@ -131,5 +130,4 @@ class ADE7953 : public PollingComponent, public sensor::Sensor {
virtual bool ade_read_32(uint16_t reg, uint32_t *value) = 0;
};
} // namespace ade7953_base
} // namespace esphome
} // namespace esphome::ade7953_base
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_i2c {
namespace esphome::ade7953_i2c {
static const char *const TAG = "ade7953";
@@ -76,5 +75,4 @@ bool AdE7953I2c::ade_read_32(uint16_t reg, uint32_t *value) {
return false;
}
} // namespace ade7953_i2c
} // namespace esphome
} // namespace esphome::ade7953_i2c
+2 -4
View File
@@ -8,8 +8,7 @@
#include <vector>
namespace esphome {
namespace ade7953_i2c {
namespace esphome::ade7953_i2c {
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
public:
@@ -24,5 +23,4 @@ class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
bool ade_read_32(uint16_t reg, uint32_t *value) override;
};
} // namespace ade7953_i2c
} // namespace esphome
} // namespace esphome::ade7953_i2c
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_spi {
namespace esphome::ade7953_spi {
static const char *const TAG = "ade7953";
@@ -83,5 +82,4 @@ bool AdE7953Spi::ade_read_32(uint16_t reg, uint32_t *value) {
return false;
}
} // namespace ade7953_spi
} // namespace esphome
} // namespace esphome::ade7953_spi
+2 -4
View File
@@ -8,8 +8,7 @@
#include <vector>
namespace esphome {
namespace ade7953_spi {
namespace esphome::ade7953_spi {
class AdE7953Spi : public ade7953_base::ADE7953,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
@@ -28,5 +27,4 @@ class AdE7953Spi : public ade7953_base::ADE7953,
bool ade_read_32(uint16_t reg, uint32_t *value) override;
};
} // namespace ade7953_spi
} // namespace esphome
} // namespace esphome::ade7953_spi
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
static const char *const TAG = "ads1115";
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
@@ -208,5 +207,4 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
return millivolts / 1e3f;
}
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
+2 -4
View File
@@ -5,8 +5,7 @@
#include <vector>
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
enum ADS1115Multiplexer {
ADS1115_MULTIPLEXER_P0_N1 = 0b000,
@@ -60,5 +59,4 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
bool continuous_mode_;
};
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
static const char *const TAG = "ads1115.sensor";
@@ -29,5 +28,4 @@ void ADS1115Sensor::dump_config() {
this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
}
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
@@ -8,8 +8,7 @@
#include "../ads1115.h"
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor,
@@ -33,5 +32,4 @@ class ADS1115Sensor : public sensor::Sensor,
ADS1115Samplerate samplerate_;
};
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
static const char *const TAG = "ads1118";
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
@@ -122,5 +121,4 @@ float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain g
}
}
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
enum ADS1118Multiplexer {
ADS1118_MULTIPLEXER_P0_N1 = 0b000,
@@ -41,5 +40,4 @@ class ADS1118 : public Component,
uint16_t config_{0};
};
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
static const char *const TAG = "ads1118.sensor";
@@ -27,5 +26,4 @@ void ADS1118Sensor::update() {
}
}
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
@@ -8,8 +8,7 @@
#include "../ads1118.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
class ADS1118Sensor : public PollingComponent,
public sensor::Sensor,
@@ -32,5 +31,4 @@ class ADS1118Sensor : public PollingComponent,
bool temperature_mode_;
};
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
+2 -4
View File
@@ -3,8 +3,7 @@
#include <cinttypes>
namespace esphome {
namespace ags10 {
namespace esphome::ags10 {
static const char *const TAG = "ags10";
// Data acquisition.
@@ -192,5 +191,4 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
return data;
}
} // namespace ags10
} // namespace esphome
} // namespace esphome::ags10
+2 -4
View File
@@ -5,8 +5,7 @@
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
namespace esphome {
namespace ags10 {
namespace esphome::ags10 {
class AGS10Component : public PollingComponent, public i2c::I2CDevice {
public:
@@ -136,5 +135,4 @@ template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, p
}
}
};
} // namespace ags10
} // namespace esphome
} // namespace esphome::ags10
+2 -4
View File
@@ -17,8 +17,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace aht10 {
namespace esphome::aht10 {
static const char *const TAG = "aht10";
static const uint8_t AHT10_INITIALIZE_CMD[] = {0xE1, 0x08, 0x00};
@@ -160,5 +159,4 @@ void AHT10Component::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
} // namespace aht10
} // namespace esphome
} // namespace esphome::aht10
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace aht10 {
namespace esphome::aht10 {
enum AHT10Variant { AHT10, AHT20 };
@@ -31,5 +30,4 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
uint32_t start_time_{};
};
} // namespace aht10
} // namespace esphome
} // namespace esphome::aht10
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace aic3204 {
namespace esphome::aic3204 {
static const char *const TAG = "aic3204";
@@ -167,5 +166,4 @@ bool AIC3204::write_volume_() {
return true;
}
} // namespace aic3204
} // namespace esphome
} // namespace esphome::aic3204
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace aic3204 {
namespace esphome::aic3204 {
// TLV320AIC3204 Register Addresses
// Page 0
@@ -83,5 +82,4 @@ class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDev
float volume_{0};
};
} // namespace aic3204
} // namespace esphome
} // namespace esphome::aic3204
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/component.h"
#include "aic3204.h"
namespace esphome {
namespace aic3204 {
namespace esphome::aic3204 {
template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
public:
@@ -19,5 +18,4 @@ template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
AIC3204 *aic3204_;
};
} // namespace aic3204
} // namespace esphome
} // namespace esphome::aic3204
@@ -4,8 +4,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_ble {
namespace esphome::airthings_ble {
static const char *const TAG = "airthings_ble";
@@ -29,7 +28,6 @@ bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
return false;
}
} // namespace airthings_ble
} // namespace esphome
} // namespace esphome::airthings_ble
#endif
@@ -5,15 +5,13 @@
#include "esphome/core/component.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
namespace esphome {
namespace airthings_ble {
namespace esphome::airthings_ble {
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
} // namespace airthings_ble
} // namespace esphome
} // namespace esphome::airthings_ble
#endif
@@ -6,8 +6,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_base {
namespace esphome::airthings_wave_base {
static const char *const TAG = "airthings_wave_base";
@@ -211,7 +210,6 @@ void AirthingsWaveBase::set_response_timeout_() {
});
}
} // namespace airthings_wave_base
} // namespace esphome
} // namespace esphome::airthings_wave_base
#endif // USE_ESP32
@@ -14,8 +14,7 @@
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace airthings_wave_base {
namespace esphome::airthings_wave_base {
namespace espbt = esphome::esp32_ble_tracker;
@@ -84,7 +83,6 @@ class AirthingsWaveBase : public PollingComponent, public ble_client::BLEClientN
};
};
} // namespace airthings_wave_base
} // namespace esphome
} // namespace esphome::airthings_wave_base
#endif // USE_ESP32
@@ -2,8 +2,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_mini {
namespace esphome::airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
@@ -49,7 +48,6 @@ AirthingsWaveMini::AirthingsWaveMini() {
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
}
} // namespace airthings_wave_mini
} // namespace esphome
} // namespace esphome::airthings_wave_mini
#endif // USE_ESP32
@@ -4,8 +4,7 @@
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
namespace esphome {
namespace airthings_wave_mini {
namespace esphome::airthings_wave_mini {
namespace espbt = esphome::esp32_ble_tracker;
@@ -34,7 +33,6 @@ class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
};
};
} // namespace airthings_wave_mini
} // namespace esphome
} // namespace esphome::airthings_wave_mini
#endif // USE_ESP32
@@ -2,8 +2,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_plus {
namespace esphome::airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
@@ -98,7 +97,6 @@ void AirthingsWavePlus::setup() {
espbt::ESPBTUUID::from_raw(access_control_point_characteristic_uuid);
}
} // namespace airthings_wave_plus
} // namespace esphome
} // namespace esphome::airthings_wave_plus
#endif // USE_ESP32
@@ -4,8 +4,7 @@
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
namespace esphome {
namespace airthings_wave_plus {
namespace esphome::airthings_wave_plus {
namespace espbt = esphome::esp32_ble_tracker;
@@ -58,7 +57,6 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
};
};
} // namespace airthings_wave_plus
} // namespace esphome
} // namespace esphome::airthings_wave_plus
#endif // USE_ESP32
@@ -13,7 +13,11 @@ from esphome.const import (
CONF_WEB_SERVER,
)
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,
queue_entity_register,
setup_entity,
)
from esphome.cpp_generator import MockObjClass
CODEOWNERS = ["@grahambrown11", "@hwstar"]
@@ -181,7 +185,7 @@ async def setup_alarm_control_panel_core_(var, config):
async def register_alarm_control_panel(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_alarm_control_panel(var))
queue_entity_register("alarm_control_panel", config)
CORE.register_platform_component("alarm_control_panel", var)
await setup_alarm_control_panel_core_(var, config)
+2 -4
View File
@@ -5,8 +5,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace alpha3 {
namespace esphome::alpha3 {
static const char *const TAG = "alpha3";
@@ -185,7 +184,6 @@ void Alpha3::update() {
delay(25); // need to wait between requests
}
}
} // namespace alpha3
} // namespace esphome
} // namespace esphome::alpha3
#endif
+2 -4
View File
@@ -9,8 +9,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace alpha3 {
namespace esphome::alpha3 {
namespace espbt = esphome::esp32_ble_tracker;
@@ -64,7 +63,6 @@ class Alpha3 : public esphome::ble_client::BLEClientNode, public PollingComponen
void send_request_(uint8_t *request, size_t len);
bool is_current_response_type_(const uint8_t *response_type);
};
} // namespace alpha3
} // namespace esphome
} // namespace esphome::alpha3
#endif
+2 -4
View File
@@ -24,8 +24,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am2315c {
namespace esphome::am2315c {
static const char *const TAG = "am2315c";
@@ -176,5 +175,4 @@ void AM2315C::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
} // namespace am2315c
} // namespace esphome
} // namespace esphome::am2315c
+2 -4
View File
@@ -25,8 +25,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace am2315c {
namespace esphome::am2315c {
class AM2315C : public PollingComponent, public i2c::I2CDevice {
public:
@@ -45,5 +44,4 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace am2315c
} // namespace esphome
} // namespace esphome::am2315c
+2 -4
View File
@@ -8,8 +8,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am2320 {
namespace esphome::am2320 {
static const char *const TAG = "am2320";
@@ -86,5 +85,4 @@ bool AM2320Component::read_data_(uint8_t *data) {
return true;
}
} // namespace am2320
} // namespace esphome
} // namespace esphome::am2320
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace am2320 {
namespace esphome::am2320 {
class AM2320Component : public PollingComponent, public i2c::I2CDevice {
public:
@@ -24,5 +23,4 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace am2320
} // namespace esphome
} // namespace esphome::am2320
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include <cstring>
namespace esphome {
namespace am43 {
namespace esphome::am43 {
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
@@ -134,5 +133,4 @@ void Am43Decoder::decode(const uint8_t *data, uint16_t length) {
}
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
+2 -4
View File
@@ -3,8 +3,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am43 {
namespace esphome::am43 {
static const uint16_t AM43_SERVICE_UUID = 0xFE50;
static const uint16_t AM43_CHARACTERISTIC_UUID = 0xFE51;
@@ -74,5 +73,4 @@ class Am43Decoder {
bool has_pin_response_;
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
+2 -4
View File
@@ -3,8 +3,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
namespace esphome::am43 {
static const char *const TAG = "am43_cover";
@@ -154,7 +153,6 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
}
}
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
+2 -4
View File
@@ -10,8 +10,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace am43 {
namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
@@ -38,7 +37,6 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
float position_;
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
@@ -4,8 +4,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
namespace esphome::am43 {
static const char *const TAG = "am43";
@@ -111,7 +110,6 @@ void Am43::update() {
}
}
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
+2 -4
View File
@@ -10,8 +10,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace am43 {
namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
@@ -38,7 +37,6 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
uint32_t last_battery_update_;
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
@@ -1,8 +1,7 @@
#include "analog_threshold_binary_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace analog_threshold {
namespace esphome::analog_threshold {
static const char *const TAG = "analog_threshold.binary_sensor";
@@ -43,5 +42,4 @@ void AnalogThresholdBinarySensor::dump_config() {
this->upper_threshold_.value(), this->lower_threshold_.value());
}
} // namespace analog_threshold
} // namespace esphome
} // namespace esphome::analog_threshold
@@ -5,8 +5,7 @@
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace analog_threshold {
namespace esphome::analog_threshold {
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
public:
@@ -24,5 +23,4 @@ class AnalogThresholdBinarySensor : public Component, public binary_sensor::Bina
bool raw_state_{false}; // Pre-filter state for hysteresis logic
};
} // namespace analog_threshold
} // namespace esphome
} // namespace esphome::analog_threshold
+8 -5
View File
@@ -2,8 +2,7 @@
#include "esphome/core/hal.h"
namespace esphome {
namespace animation {
namespace esphome::animation {
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
image::ImageType type, image::Transparency transparent)
@@ -62,9 +61,13 @@ void Animation::set_frame(int frame) {
}
void Animation::update_data_start_() {
const uint32_t image_size = this->get_width_stride() * this->height_;
uint32_t image_size = this->get_width_stride() * this->height_;
// RGB565 with an alpha channel stores the alpha plane immediately after the RGB
// plane within each frame, so the per-frame stride includes the alpha bytes.
if (this->type_ == image::IMAGE_TYPE_RGB565 && this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) {
image_size += static_cast<uint32_t>(this->width_) * this->height_;
}
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
}
} // namespace animation
} // namespace esphome
} // namespace esphome::animation
+2 -4
View File
@@ -3,8 +3,7 @@
#include "esphome/core/automation.h"
namespace esphome {
namespace animation {
namespace esphome::animation {
class Animation : public image::Image {
public:
@@ -64,5 +63,4 @@ template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
Animation *parent_;
};
} // namespace animation
} // namespace esphome
} // namespace esphome::animation
+2 -4
View File
@@ -3,8 +3,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace anova {
namespace esphome::anova {
static const char *const TAG = "anova";
@@ -160,7 +159,6 @@ void Anova::update() {
}
}
} // namespace anova
} // namespace esphome
} // namespace esphome::anova
#endif
+2 -4
View File
@@ -10,8 +10,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace anova {
namespace esphome::anova {
namespace espbt = esphome::esp32_ble_tracker;
@@ -45,7 +44,6 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
bool fahrenheit_;
};
} // namespace anova
} // namespace esphome
} // namespace esphome::anova
#endif
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/alloc_helpers.h"
namespace esphome {
namespace anova {
namespace esphome::anova {
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
@@ -132,5 +131,4 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
}
}
} // namespace anova
} // namespace esphome
} // namespace esphome::anova
+2 -4
View File
@@ -3,8 +3,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace anova {
namespace esphome::anova {
enum CurrentQuery {
NONE,
@@ -75,5 +74,4 @@ class AnovaCodec {
CurrentQuery current_query_;
};
} // namespace anova
} // namespace esphome
} // namespace esphome::anova
+2 -4
View File
@@ -5,8 +5,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace apds9306 {
namespace esphome::apds9306 {
static const char *const TAG = "apds9306";
@@ -147,5 +146,4 @@ void APDS9306::update() {
this->publish_state(lux);
}
} // namespace apds9306
} // namespace esphome
} // namespace esphome::apds9306
+2 -4
View File
@@ -7,8 +7,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace apds9306 {
namespace esphome::apds9306 {
enum MeasurementBitWidth : uint8_t {
MEASUREMENT_BIT_WIDTH_20 = 0,
@@ -62,5 +61,4 @@ class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2C
AmbientLightGain gain_;
};
} // namespace apds9306
} // namespace esphome
} // namespace esphome::apds9306
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace apds9960 {
namespace esphome::apds9960 {
static const char *const TAG = "apds9960";
@@ -402,5 +401,4 @@ bool APDS9960::is_gesture_enabled_() const {
#endif
}
} // namespace apds9960
} // namespace esphome
} // namespace esphome::apds9960
+2 -4
View File
@@ -10,8 +10,7 @@
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
namespace esphome {
namespace apds9960 {
namespace esphome::apds9960 {
class APDS9960 : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
@@ -71,5 +70,4 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
uint32_t gesture_start_{0};
};
} // namespace apds9960
} // namespace esphome
} // namespace esphome::apds9960
+34 -13
View File
@@ -72,17 +72,35 @@ APIUnregisterServiceCallAction = api_ns.class_(
UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
ListEntitiesServicesArgument = api_ns.class_("ListEntitiesServicesArgument")
SERVICE_ARG_NATIVE_TYPES: dict[str, MockObj] = {
# Owning element type for each YAML service variable type. Used to derive both
# the zero-copy native types and the owning fallback types below.
_SERVICE_ARG_SCALAR_TYPES: dict[str, MockObj] = {
"bool": cg.bool_,
"int": cg.int32,
"float": cg.float_,
"string": cg.std_string,
}
SERVICE_ARG_NATIVE_TYPES: dict[str, MockObj] = {
# Scalars are passed by value; string uses a non-owning view into rx_buf_.
**_SERVICE_ARG_SCALAR_TYPES,
"string": cg.StringRef,
"bool[]": cg.FixedVector.template(cg.bool_).operator("const").operator("ref"),
"int[]": cg.FixedVector.template(cg.int32).operator("const").operator("ref"),
"float[]": cg.FixedVector.template(cg.float_).operator("const").operator("ref"),
"string[]": cg.FixedVector.template(cg.std_string)
.operator("const")
.operator("ref"),
# Arrays are passed as non-owning const references into rx_buf_.
**{
f"{name}[]": cg.FixedVector.template(t).operator("const").operator("ref")
for name, t in _SERVICE_ARG_SCALAR_TYPES.items()
},
}
# Owning fallback types used when the action chain contains non-synchronous actions
# (delay, wait_until, script.wait, etc.). The default non-owning types reference
# storage in the receive buffer, which is reused once the synchronous portion of
# the chain returns. FixedVector is also non-copyable, so the deferred lambda
# capture in DelayAction::play_complex would fail to compile.
SERVICE_ARG_FALLBACK_TYPES: dict[str, MockObj] = {
"string": cg.std_string,
**{
f"{name}[]": cg.std_vector.template(t)
for name, t in _SERVICE_ARG_SCALAR_TYPES.items()
},
}
CONF_ENCRYPTION = "encryption"
CONF_BATCH_DELAY = "batch_delay"
@@ -216,7 +234,7 @@ ACTIONS_SCHEMA = automation.validate_automation(
ENCRYPTION_SCHEMA = cv.Schema(
{
cv.Optional(CONF_KEY): validate_encryption_key,
cv.Optional(CONF_KEY): cv.sensitive(validate_encryption_key),
}
)
@@ -381,17 +399,20 @@ async def to_code(config: ConfigType) -> None:
func_args.append((cg.bool_, "return_response"))
# Check if action chain has non-synchronous actions that would make
# non-owning StringRef dangle (rx_buf_ reused after delay)
# non-owning args (StringRef, const FixedVector&) dangle once the
# rx_buf_ is reused after a delay/wait_until/script.wait/etc. The
# FixedVector references would also fail to compile because they
# are non-copyable and DelayAction captures args by value.
has_non_synchronous = automation.has_non_synchronous_actions(
conf.get(CONF_THEN, [])
)
service_arg_names: list[str] = []
for name, var_ in conf[CONF_VARIABLES].items():
native = SERVICE_ARG_NATIVE_TYPES[var_]
# Fall back to std::string for string args if non-synchronous actions exist
if has_non_synchronous and native is cg.StringRef:
native = cg.std_string
if has_non_synchronous and var_ in SERVICE_ARG_FALLBACK_TYPES:
native = SERVICE_ARG_FALLBACK_TYPES[var_]
else:
native = SERVICE_ARG_NATIVE_TYPES[var_]
service_template_args.append(native)
func_args.append((native, name))
service_arg_names.append(name)
+16 -1
View File
@@ -1025,6 +1025,13 @@ message CameraImageRequest {
bool stream = 2;
}
// ==================== TEMPERATURE UNIT ====================
enum TemperatureUnit {
TEMPERATURE_UNIT_CELSIUS = 0;
TEMPERATURE_UNIT_FAHRENHEIT = 1;
TEMPERATURE_UNIT_KELVIN = 2;
}
// ==================== CLIMATE ====================
enum ClimateMode {
CLIMATE_MODE_OFF = 0;
@@ -1110,6 +1117,7 @@ message ListEntitiesClimateResponse {
float visual_max_humidity = 25;
uint32 device_id = 26 [(field_ifdef) = "USE_DEVICES"];
uint32 feature_flags = 27;
TemperatureUnit temperature_unit = 28;
}
message ClimateStateResponse {
option (id) = 47;
@@ -1203,6 +1211,7 @@ message ListEntitiesWaterHeaterResponse {
repeated WaterHeaterMode supported_modes = 11 [(container_pointer_no_template) = "water_heater::WaterHeaterModeMask"];
// Bitmask of WaterHeaterFeature flags
uint32 supported_features = 12;
TemperatureUnit temperature_unit = 13;
}
message WaterHeaterStateResponse {
@@ -1410,6 +1419,8 @@ enum LockState {
LOCK_STATE_JAMMED = 3;
LOCK_STATE_LOCKING = 4;
LOCK_STATE_UNLOCKING = 5;
LOCK_STATE_OPENING = 6;
LOCK_STATE_OPEN = 7;
}
enum LockCommand {
LOCK_UNLOCK = 0;
@@ -1628,7 +1639,7 @@ message BluetoothLEAdvertisementResponse {
message BluetoothLERawAdvertisement {
option (inline_encode) = true;
uint64 address = 1 [(force) = true];
uint64 address = 1 [(force) = true, (mac_address) = true];
sint32 rssi = 2 [(force) = true];
uint32 address_type = 3 [(max_value) = 4];
@@ -2015,6 +2026,7 @@ message VoiceAssistantAudio {
bytes data = 1 [(pointer_to_buffer) = true];
bool end = 2;
bytes data2 = 3 [(pointer_to_buffer) = true];
}
enum VoiceAssistantTimerEvent {
@@ -2500,6 +2512,7 @@ message ZWaveProxyFrame {
option (source) = SOURCE_BOTH;
option (ifdef) = "USE_ZWAVE_PROXY";
option (no_delay) = true;
option (speed_optimized) = true;
bytes data = 1;
}
@@ -2560,6 +2573,7 @@ message InfraredRFReceiveEvent {
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_IR_RF || USE_RADIO_FREQUENCY";
option (no_delay) = true;
option (speed_optimized) = true;
uint32 device_id = 1 [(field_ifdef) = "USE_DEVICES"];
fixed32 key = 2 [(force) = true]; // Key identifying the receiver instance
@@ -2616,6 +2630,7 @@ message SerialProxyDataReceived {
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SERIAL_PROXY";
option (no_delay) = true;
option (speed_optimized) = true;
uint32 instance = 1; // Instance index (0-based)
bytes data = 2; // Raw data received from the serial device
+5 -1
View File
@@ -1,5 +1,6 @@
#include "api_connection.h"
#ifdef USE_API
#include "api_connection_buffer.h" // for encode_to_buffer / get_batch_delay_ms_ inlines
#ifdef USE_API_NOISE
#include "api_frame_helper_noise.h"
#endif
@@ -1168,7 +1169,7 @@ void APIConnection::on_camera_image_request(const CameraImageRequest &msg) {
void APIConnection::on_get_time_response(const GetTimeResponse &value) {
if (homeassistant::global_homeassistant_time != nullptr) {
homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds);
#ifdef USE_TIME_TIMEZONE
#if defined(USE_HOMEASSISTANT_TIMEZONE) && defined(USE_TIME_TIMEZONE)
if (!value.timezone.empty()) {
// Check if the sender provided pre-parsed timezone data.
// If std_offset is non-zero or DST rules are present, the parsed data was populated.
@@ -1305,6 +1306,9 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno
bool APIConnection::send_voice_assistant_get_configuration_response_(const VoiceAssistantConfigurationRequest &msg) {
VoiceAssistantConfigurationResponse resp;
if (!this->check_voice_assistant_api_connection_()) {
// send_message encodes synchronously, so this stack local outlives the encode
const std::vector<std::string> empty_wake_words;
resp.active_wake_words = &empty_wake_words;
return this->send_message(resp);
}
+11 -40
View File
@@ -11,7 +11,8 @@
#endif
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "api_server.h"
#include "list_entities.h"
#include "subscribe_state.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#ifdef USE_ESP32_CRASH_HANDLER
@@ -36,6 +37,9 @@ class ComponentIterator;
namespace esphome::api {
// Forward-declared to break the api_server.h cycle; full-type inlines are in api_connection_buffer.h.
class APIServer;
// Keepalive timeout in milliseconds
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
// Maximum number of entities to process in a single batch during initial state/info sending
@@ -411,44 +415,10 @@ class APIConnection final : public APIServerConnectionBase {
// Non-template buffer management for send_message
bool send_message_(uint32_t payload_size, uint8_t message_type, MessageEncodeFn encode_fn, const void *msg);
// Core batch encoding logic. Computes header size, checks fit, resizes buffer, encodes.
// ALWAYS_INLINE so the compiler can devirtualize encode_fn at hot call sites.
static inline uint16_t ESPHOME_ALWAYS_INLINE encode_to_buffer(uint32_t calculated_size, MessageEncodeFn encode_fn,
const void *msg, APIConnection *conn,
uint32_t remaining_size) {
#ifdef HAS_PROTO_MESSAGE_DUMP
if (conn->flags_.log_only_mode) {
auto *proto_msg = static_cast<const ProtoMessage *>(msg);
DumpBuffer dump_buf;
conn->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf));
return 1;
}
#endif
const uint8_t footer_size = conn->helper_->frame_footer_size();
// First message uses max padding (already in buffer), subsequent use exact header size
size_t to_add;
if (conn->flags_.batch_first_message) {
conn->flags_.batch_first_message = false;
conn->batch_header_size_ = conn->helper_->frame_header_padding();
to_add = calculated_size;
} else {
conn->batch_header_size_ = conn->helper_->frame_header_size(calculated_size, conn->batch_message_type_);
to_add = calculated_size + conn->batch_header_size_ + footer_size;
}
// Check if it fits (using actual header size, not max padding)
uint16_t total_calculated_size = calculated_size + conn->batch_header_size_ + footer_size;
if (total_calculated_size > remaining_size)
return 0;
auto &shared_buf = conn->parent_->get_shared_buffer_ref();
shared_buf.resize(shared_buf.size() + to_add);
ProtoWriteBuffer buffer{&shared_buf, shared_buf.size() - calculated_size};
encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf));
return total_calculated_size;
}
// Core batch encoding logic. ALWAYS_INLINE so encode_fn devirtualizes at hot call sites.
// Defined in api_connection_buffer.h (needs APIServer complete).
static uint16_t ESPHOME_ALWAYS_INLINE encode_to_buffer(uint32_t calculated_size, MessageEncodeFn encode_fn,
const void *msg, APIConnection *conn, uint32_t remaining_size);
// Noinline version of encode_to_buffer for cold paths (entity info, zero-payload messages).
// All cold callers share this single copy instead of each getting an ALWAYS_INLINE expansion.
@@ -792,7 +762,8 @@ class APIConnection final : public APIServerConnectionBase {
// Read by process_batch_multi_ to pass into MessageInfo.
uint8_t batch_header_size_{0};
uint32_t get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
// Defined in api_connection_buffer.h (needs APIServer complete).
uint32_t get_batch_delay_ms_() const;
// Message will use 8 more bytes than the minimum size, and typical
// MTU is 1500. Sometimes users will see as low as 1460 MTU.
// If its IPv6 the header is 40 bytes, and if its IPv4
@@ -0,0 +1,54 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_API
// Inline APIConnection methods that need APIServer complete. Include this
// instead of api_connection.h when calling encode_to_buffer or get_batch_delay_ms_.
#include "api_connection.h"
#include "api_server.h"
namespace esphome::api {
inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t calculated_size,
MessageEncodeFn encode_fn, const void *msg,
APIConnection *conn, uint32_t remaining_size) {
#ifdef HAS_PROTO_MESSAGE_DUMP
if (conn->flags_.log_only_mode) {
auto *proto_msg = static_cast<const ProtoMessage *>(msg);
DumpBuffer dump_buf;
conn->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf));
return 1;
}
#endif
const uint8_t footer_size = conn->helper_->frame_footer_size();
// First message uses max padding (already in buffer), subsequent use exact header size
size_t to_add;
if (conn->flags_.batch_first_message) {
conn->flags_.batch_first_message = false;
conn->batch_header_size_ = conn->helper_->frame_header_padding();
to_add = calculated_size;
} else {
conn->batch_header_size_ = conn->helper_->frame_header_size(calculated_size, conn->batch_message_type_);
to_add = calculated_size + conn->batch_header_size_ + footer_size;
}
// Check if it fits (using actual header size, not max padding)
uint16_t total_calculated_size = calculated_size + conn->batch_header_size_ + footer_size;
if (total_calculated_size > remaining_size)
return 0;
auto &shared_buf = conn->parent_->get_shared_buffer_ref();
shared_buf.resize(shared_buf.size() + to_add);
ProtoWriteBuffer buffer{&shared_buf, shared_buf.size() - calculated_size};
encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf));
return total_calculated_size;
}
inline uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
} // namespace esphome::api
#endif
+6
View File
@@ -110,4 +110,10 @@ extend google.protobuf.FieldOptions {
// length varint calculations and direct byte writes, since the length
// varint is guaranteed to be 1 byte.
optional uint32 max_data_length = 50018;
// mac_address: Field is a 48-bit MAC address stored in a uint64.
// Emits encode_varint_raw_48bit which has a 7-byte fast path that avoids
// the per-byte loop when the upper bits are non-zero (the common case
// for real MAC addresses, since OUIs occupy the top 24 bits).
optional bool mac_address = 50019 [default=false];
}
+31 -8
View File
@@ -1439,6 +1439,7 @@ uint8_t *ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCO
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 26, this->device_id);
#endif
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 27, this->feature_flags);
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 28, static_cast<uint32_t>(this->temperature_unit));
return pos;
}
uint32_t ListEntitiesClimateResponse::calculate_size() const {
@@ -1488,6 +1489,7 @@ uint32_t ListEntitiesClimateResponse::calculate_size() const {
size += ProtoSize::calc_uint32(2, this->device_id);
#endif
size += ProtoSize::calc_uint32(2, this->feature_flags);
size += this->temperature_unit ? 3 : 0;
return size;
}
uint8_t *ClimateStateResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -1645,6 +1647,7 @@ uint8_t *ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer PROTO_
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 11, static_cast<uint32_t>(it), true);
}
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 12, this->supported_features);
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 13, static_cast<uint32_t>(this->temperature_unit));
return pos;
}
uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const {
@@ -1667,6 +1670,7 @@ uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const {
size += this->supported_modes->size() * 2;
}
size += ProtoSize::calc_uint32(1, this->supported_features);
size += this->temperature_unit ? 2 : 0;
return size;
}
uint8_t *WaterHeaterStateResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -2348,7 +2352,7 @@ BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCO
uint8_t *len_pos = pos;
ProtoEncode::reserve_byte(pos PROTO_ENCODE_DEBUG_ARG);
ProtoEncode::write_raw_byte(pos PROTO_ENCODE_DEBUG_ARG, 8);
ProtoEncode::encode_varint_raw_64(pos PROTO_ENCODE_DEBUG_ARG, sub_msg.address);
ProtoEncode::encode_varint_raw_48bit(pos PROTO_ENCODE_DEBUG_ARG, sub_msg.address);
ProtoEncode::write_raw_byte(pos PROTO_ENCODE_DEBUG_ARG, 16);
ProtoEncode::encode_varint_raw_short(pos PROTO_ENCODE_DEBUG_ARG, encode_zigzag32(sub_msg.rssi));
if (sub_msg.address_type) {
@@ -2369,7 +2373,7 @@ BluetoothLERawAdvertisementsResponse::calculate_size() const {
for (uint16_t i = 0; i < this->advertisements_len; i++) {
auto &sub_msg = this->advertisements[i];
size += 2;
size += ProtoSize::calc_uint64_force(1, sub_msg.address);
size += ProtoSize::calc_uint64_48bit_force(1, sub_msg.address);
size += ProtoSize::calc_sint32_force(1, sub_msg.rssi);
size += sub_msg.address_type ? 2 : 0;
size += 2 + sub_msg.data_len;
@@ -2889,6 +2893,11 @@ bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited
this->data_len = value.size();
break;
}
case 3: {
this->data2 = value.data();
this->data2_len = value.size();
break;
}
default:
return false;
}
@@ -2898,12 +2907,14 @@ uint8_t *VoiceAssistantAudio::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG
uint8_t *__restrict__ pos = buffer.get_pos();
ProtoEncode::encode_bytes(pos PROTO_ENCODE_DEBUG_ARG, 1, this->data, this->data_len);
ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 2, this->end);
ProtoEncode::encode_bytes(pos PROTO_ENCODE_DEBUG_ARG, 3, this->data2, this->data2_len);
return pos;
}
uint32_t VoiceAssistantAudio::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_length(1, this->data_len);
size += ProtoSize::calc_bool(1, this->end);
size += ProtoSize::calc_length(1, this->data2_len);
return size;
}
bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, proto_varint_value_t value) {
@@ -3780,12 +3791,16 @@ bool ZWaveProxyFrame::decode_length(uint32_t field_id, ProtoLengthDelimited valu
}
return true;
}
uint8_t *ZWaveProxyFrame::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
uint8_t *
ZWaveProxyFrame::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
uint8_t *__restrict__ pos = buffer.get_pos();
ProtoEncode::encode_bytes(pos PROTO_ENCODE_DEBUG_ARG, 1, this->data, this->data_len);
return pos;
}
uint32_t ZWaveProxyFrame::calculate_size() const {
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
uint32_t
ZWaveProxyFrame::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_length(1, this->data_len);
return size;
@@ -3906,7 +3921,9 @@ bool InfraredRFTransmitRawTimingsRequest::decode_32bit(uint32_t field_id, Proto3
}
return true;
}
uint8_t *InfraredRFReceiveEvent::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
uint8_t *
InfraredRFReceiveEvent::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
uint8_t *__restrict__ pos = buffer.get_pos();
#ifdef USE_DEVICES
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 1, this->device_id);
@@ -3917,7 +3934,9 @@ uint8_t *InfraredRFReceiveEvent::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DE
}
return pos;
}
uint32_t InfraredRFReceiveEvent::calculate_size() const {
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
uint32_t
InfraredRFReceiveEvent::calculate_size() const {
uint32_t size = 0;
#ifdef USE_DEVICES
size += ProtoSize::calc_uint32(1, this->device_id);
@@ -3997,13 +4016,17 @@ bool SerialProxyConfigureRequest::decode_varint(uint32_t field_id, proto_varint_
}
return true;
}
uint8_t *SerialProxyDataReceived::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
uint8_t *
SerialProxyDataReceived::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
uint8_t *__restrict__ pos = buffer.get_pos();
ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 1, this->instance);
ProtoEncode::encode_bytes(pos PROTO_ENCODE_DEBUG_ARG, 2, this->data_ptr_, this->data_len_);
return pos;
}
uint32_t SerialProxyDataReceived::calculate_size() const {
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
uint32_t
SerialProxyDataReceived::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_uint32(1, this->instance);
size += ProtoSize::calc_length(1, this->data_len_);
+14 -3
View File
@@ -92,6 +92,11 @@ enum SupportsResponseType : uint32_t {
SUPPORTS_RESPONSE_STATUS = 100,
};
#endif
enum TemperatureUnit : uint32_t {
TEMPERATURE_UNIT_CELSIUS = 0,
TEMPERATURE_UNIT_FAHRENHEIT = 1,
TEMPERATURE_UNIT_KELVIN = 2,
};
#ifdef USE_CLIMATE
enum ClimateMode : uint32_t {
CLIMATE_MODE_OFF = 0,
@@ -176,6 +181,8 @@ enum LockState : uint32_t {
LOCK_STATE_JAMMED = 3,
LOCK_STATE_LOCKING = 4,
LOCK_STATE_UNLOCKING = 5,
LOCK_STATE_OPENING = 6,
LOCK_STATE_OPEN = 7,
};
enum LockCommand : uint32_t {
LOCK_UNLOCK = 0,
@@ -1372,7 +1379,7 @@ class CameraImageRequest final : public ProtoDecodableMessage {
class ListEntitiesClimateResponse final : public InfoResponseProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 46;
static constexpr uint8_t ESTIMATED_SIZE = 150;
static constexpr uint8_t ESTIMATED_SIZE = 153;
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_climate_response"); }
#endif
@@ -1394,6 +1401,7 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage {
float visual_min_humidity{0.0f};
float visual_max_humidity{0.0f};
uint32_t feature_flags{0};
enums::TemperatureUnit temperature_unit{};
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1471,7 +1479,7 @@ class ClimateCommandRequest final : public CommandProtoMessage {
class ListEntitiesWaterHeaterResponse final : public InfoResponseProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 132;
static constexpr uint8_t ESTIMATED_SIZE = 63;
static constexpr uint8_t ESTIMATED_SIZE = 65;
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_water_heater_response"); }
#endif
@@ -1480,6 +1488,7 @@ class ListEntitiesWaterHeaterResponse final : public InfoResponseProtoMessage {
float target_temperature_step{0.0f};
const water_heater::WaterHeaterModeMask *supported_modes{};
uint32_t supported_features{0};
enums::TemperatureUnit temperature_unit{};
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -2427,13 +2436,15 @@ class VoiceAssistantEventResponse final : public ProtoDecodableMessage {
class VoiceAssistantAudio final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 106;
static constexpr uint8_t ESTIMATED_SIZE = 21;
static constexpr uint8_t ESTIMATED_SIZE = 40;
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("voice_assistant_audio"); }
#endif
const uint8_t *data{nullptr};
uint16_t data_len{0};
bool end{false};
const uint8_t *data2{nullptr};
uint16_t data2_len{0};
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
+19
View File
@@ -297,6 +297,18 @@ template<> const char *proto_enum_to_string<enums::SupportsResponseType>(enums::
}
}
#endif
template<> const char *proto_enum_to_string<enums::TemperatureUnit>(enums::TemperatureUnit value) {
switch (value) {
case enums::TEMPERATURE_UNIT_CELSIUS:
return ESPHOME_PSTR("TEMPERATURE_UNIT_CELSIUS");
case enums::TEMPERATURE_UNIT_FAHRENHEIT:
return ESPHOME_PSTR("TEMPERATURE_UNIT_FAHRENHEIT");
case enums::TEMPERATURE_UNIT_KELVIN:
return ESPHOME_PSTR("TEMPERATURE_UNIT_KELVIN");
default:
return ESPHOME_PSTR("UNKNOWN");
}
}
#ifdef USE_CLIMATE
template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMode value) {
switch (value) {
@@ -475,6 +487,10 @@ template<> const char *proto_enum_to_string<enums::LockState>(enums::LockState v
return ESPHOME_PSTR("LOCK_STATE_LOCKING");
case enums::LOCK_STATE_UNLOCKING:
return ESPHOME_PSTR("LOCK_STATE_UNLOCKING");
case enums::LOCK_STATE_OPENING:
return ESPHOME_PSTR("LOCK_STATE_OPENING");
case enums::LOCK_STATE_OPEN:
return ESPHOME_PSTR("LOCK_STATE_OPEN");
default:
return ESPHOME_PSTR("UNKNOWN");
}
@@ -1539,6 +1555,7 @@ const char *ListEntitiesClimateResponse::dump_to(DumpBuffer &out) const {
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
dump_field(out, ESPHOME_PSTR("feature_flags"), this->feature_flags);
dump_field(out, ESPHOME_PSTR("temperature_unit"), static_cast<enums::TemperatureUnit>(this->temperature_unit));
return out.c_str();
}
const char *ClimateStateResponse::dump_to(DumpBuffer &out) const {
@@ -1612,6 +1629,7 @@ const char *ListEntitiesWaterHeaterResponse::dump_to(DumpBuffer &out) const {
dump_field(out, ESPHOME_PSTR("supported_modes"), static_cast<enums::WaterHeaterMode>(it), 4);
}
dump_field(out, ESPHOME_PSTR("supported_features"), this->supported_features);
dump_field(out, ESPHOME_PSTR("temperature_unit"), static_cast<enums::TemperatureUnit>(this->temperature_unit));
return out.c_str();
}
const char *WaterHeaterStateResponse::dump_to(DumpBuffer &out) const {
@@ -2156,6 +2174,7 @@ const char *VoiceAssistantAudio::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("VoiceAssistantAudio"));
dump_bytes_field(out, ESPHOME_PSTR("data"), this->data, this->data_len);
dump_field(out, ESPHOME_PSTR("end"), this->end);
dump_bytes_field(out, ESPHOME_PSTR("data2"), this->data2, this->data2_len);
return out.c_str();
}
const char *VoiceAssistantTimerEventResponse::dump_to(DumpBuffer &out) const {
@@ -21,6 +21,7 @@ void APIServerConnectionBase::log_receive_message_(const LogString *name) {
}
#endif
#ifdef USE_API
void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
// Check authentication/connection requirements
switch (msg_type) {
@@ -706,5 +707,6 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui
break;
}
}
#endif // USE_API
} // namespace esphome::api
Loaded 100 of 2371 files, more files were not shown because too many files have changed in this diff. Show more