mirror of
https://github.com/esphome/esphome.git
synced 2026-02-18 15:35:59 -07:00
Merge remote-tracking branch 'origin/integration' into integration
This commit is contained in:
@@ -1 +1 @@
|
||||
bb17a9237b701c18a0ec885a709409305d2b777e9d4a3cf49b2e71e2144b3fe0
|
||||
0f2b9a65dce7c59289d3aeb40936360a62a7be937b585147b45bb1509eaafb36
|
||||
|
||||
@@ -43,6 +43,7 @@ _READELF_SECTION_PATTERN = re.compile(
|
||||
# Component category prefixes
|
||||
_COMPONENT_PREFIX_ESPHOME = "[esphome]"
|
||||
_COMPONENT_PREFIX_EXTERNAL = "[external]"
|
||||
_COMPONENT_PREFIX_LIB = "[lib]"
|
||||
_COMPONENT_CORE = f"{_COMPONENT_PREFIX_ESPHOME}core"
|
||||
_COMPONENT_API = f"{_COMPONENT_PREFIX_ESPHOME}api"
|
||||
|
||||
@@ -56,6 +57,16 @@ SymbolInfoType = tuple[str, int, str]
|
||||
# RAM sections - symbols in these sections consume RAM
|
||||
RAM_SECTIONS = frozenset([".data", ".bss"])
|
||||
|
||||
# nm symbol types for global/weak defined symbols (used for library symbol mapping)
|
||||
# Only global (uppercase) and weak symbols are safe to use - local symbols (lowercase)
|
||||
# can have name collisions across compilation units
|
||||
_NM_DEFINED_GLOBAL_TYPES = frozenset({"T", "D", "B", "R", "W", "V"})
|
||||
|
||||
# Pattern matching compiler-generated local names that can collide across compilation
|
||||
# units (e.g., packet$19, buf$20, flag$5261). These are unsafe for name-based lookup.
|
||||
# Does NOT match mangled C++ names with optimization suffixes (e.g., func$isra$0).
|
||||
_COMPILER_LOCAL_PATTERN = re.compile(r"^[a-zA-Z_]\w*\$\d+$")
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemorySection:
|
||||
@@ -179,11 +190,19 @@ class MemoryAnalyzer:
|
||||
self._sdk_symbols: list[SDKSymbol] = []
|
||||
# CSWTCH symbols: list of (name, size, source_file, component)
|
||||
self._cswtch_symbols: list[tuple[str, int, str, str]] = []
|
||||
# Library symbol mapping: symbol_name -> library_name
|
||||
self._lib_symbol_map: dict[str, str] = {}
|
||||
# Library dir to name mapping: "lib641" -> "espsoftwareserial",
|
||||
# "espressif__mdns" -> "mdns"
|
||||
self._lib_hash_to_name: dict[str, str] = {}
|
||||
# Heuristic category to library redirect: "mdns_lib" -> "[lib]mdns"
|
||||
self._heuristic_to_lib: dict[str, str] = {}
|
||||
|
||||
def analyze(self) -> dict[str, ComponentMemory]:
|
||||
"""Analyze the ELF file and return component memory usage."""
|
||||
self._parse_sections()
|
||||
self._parse_symbols()
|
||||
self._scan_libraries()
|
||||
self._categorize_symbols()
|
||||
self._analyze_cswtch_symbols()
|
||||
self._analyze_sdk_libraries()
|
||||
@@ -328,15 +347,19 @@ class MemoryAnalyzer:
|
||||
# If no component match found, it's core
|
||||
return _COMPONENT_CORE
|
||||
|
||||
# Check library symbol map (more accurate than heuristic patterns)
|
||||
if lib_name := self._lib_symbol_map.get(symbol_name):
|
||||
return f"{_COMPONENT_PREFIX_LIB}{lib_name}"
|
||||
|
||||
# Check against symbol patterns
|
||||
for component, patterns in SYMBOL_PATTERNS.items():
|
||||
if any(pattern in symbol_name for pattern in patterns):
|
||||
return component
|
||||
return self._heuristic_to_lib.get(component, component)
|
||||
|
||||
# Check against demangled patterns
|
||||
for component, patterns in DEMANGLED_PATTERNS.items():
|
||||
if any(pattern in demangled for pattern in patterns):
|
||||
return component
|
||||
return self._heuristic_to_lib.get(component, component)
|
||||
|
||||
# Special cases that need more complex logic
|
||||
|
||||
@@ -384,6 +407,327 @@ class MemoryAnalyzer:
|
||||
|
||||
return "Other Core"
|
||||
|
||||
def _discover_pio_libraries(
|
||||
self,
|
||||
libraries: dict[str, list[Path]],
|
||||
hash_to_name: dict[str, str],
|
||||
) -> None:
|
||||
"""Discover PlatformIO third-party libraries from the build directory.
|
||||
|
||||
Scans ``lib<hex>/`` directories under ``.pioenvs/<env>/`` to find
|
||||
library names and their ``.a`` archive or ``.o`` file paths.
|
||||
|
||||
Args:
|
||||
libraries: Dict to populate with library name -> file path list mappings.
|
||||
Prefers ``.a`` archives when available, falls back to ``.o`` files
|
||||
(e.g., pioarduino ESP32 Arduino builds only produce ``.o`` files).
|
||||
hash_to_name: Dict to populate with dir name -> library name mappings
|
||||
for CSWTCH attribution (e.g., ``lib641`` -> ``espsoftwareserial``).
|
||||
"""
|
||||
build_dir = self.elf_path.parent
|
||||
|
||||
for entry in build_dir.iterdir():
|
||||
if not entry.is_dir() or not entry.name.startswith("lib"):
|
||||
continue
|
||||
# Validate that the suffix after "lib" is a hex hash
|
||||
hex_part = entry.name[3:]
|
||||
if not hex_part:
|
||||
continue
|
||||
try:
|
||||
int(hex_part, 16)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# Each lib<hex>/ directory contains a subdirectory named after the library
|
||||
for lib_subdir in entry.iterdir():
|
||||
if not lib_subdir.is_dir():
|
||||
continue
|
||||
lib_name = lib_subdir.name.lower()
|
||||
|
||||
# Prefer .a archive (lib<LibraryName>.a), fall back to .o files
|
||||
# e.g., lib72a/ESPAsyncTCP/... has lib72a/libESPAsyncTCP.a
|
||||
archive = entry / f"lib{lib_subdir.name}.a"
|
||||
if archive.exists():
|
||||
file_paths = [archive]
|
||||
elif archives := list(entry.glob("*.a")):
|
||||
# Case-insensitive fallback
|
||||
file_paths = [archives[0]]
|
||||
else:
|
||||
# No .a archive (e.g., pioarduino CMake builds) - use .o files
|
||||
file_paths = sorted(lib_subdir.rglob("*.o"))
|
||||
|
||||
if file_paths:
|
||||
libraries[lib_name] = file_paths
|
||||
hash_to_name[entry.name] = lib_name
|
||||
_LOGGER.debug(
|
||||
"Discovered PlatformIO library: %s -> %s",
|
||||
lib_subdir.name,
|
||||
file_paths[0],
|
||||
)
|
||||
|
||||
def _discover_idf_managed_components(
|
||||
self,
|
||||
libraries: dict[str, list[Path]],
|
||||
hash_to_name: dict[str, str],
|
||||
) -> None:
|
||||
"""Discover ESP-IDF managed component libraries from the build directory.
|
||||
|
||||
ESP-IDF managed components (from the IDF component registry) use a
|
||||
``<vendor>__<name>`` naming convention. Source files live under
|
||||
``managed_components/<vendor>__<name>/`` and the compiled archives are at
|
||||
``esp-idf/<vendor>__<name>/lib<vendor>__<name>.a``.
|
||||
|
||||
Args:
|
||||
libraries: Dict to populate with library name -> file path list mappings.
|
||||
hash_to_name: Dict to populate with dir name -> library name mappings
|
||||
for CSWTCH attribution (e.g., ``espressif__mdns`` -> ``mdns``).
|
||||
"""
|
||||
build_dir = self.elf_path.parent
|
||||
|
||||
managed_dir = build_dir / "managed_components"
|
||||
if not managed_dir.is_dir():
|
||||
return
|
||||
|
||||
espidf_dir = build_dir / "esp-idf"
|
||||
|
||||
for entry in managed_dir.iterdir():
|
||||
if not entry.is_dir() or "__" not in entry.name:
|
||||
continue
|
||||
|
||||
# Extract the short name: espressif__mdns -> mdns
|
||||
full_name = entry.name # e.g., espressif__mdns
|
||||
short_name = full_name.split("__", 1)[1].lower()
|
||||
|
||||
# Find the .a archive under esp-idf/<vendor>__<name>/
|
||||
archive = espidf_dir / full_name / f"lib{full_name}.a"
|
||||
if archive.exists():
|
||||
libraries[short_name] = [archive]
|
||||
hash_to_name[full_name] = short_name
|
||||
_LOGGER.debug(
|
||||
"Discovered IDF managed component: %s -> %s",
|
||||
short_name,
|
||||
archive,
|
||||
)
|
||||
|
||||
def _build_library_symbol_map(
|
||||
self, libraries: dict[str, list[Path]]
|
||||
) -> dict[str, str]:
|
||||
"""Build a symbol-to-library mapping from library archives or object files.
|
||||
|
||||
Runs ``nm --defined-only`` on each ``.a`` or ``.o`` file to collect
|
||||
global and weak defined symbols.
|
||||
|
||||
Args:
|
||||
libraries: Dictionary mapping library name to list of file paths
|
||||
(``.a`` archives or ``.o`` object files).
|
||||
|
||||
Returns:
|
||||
Dictionary mapping symbol name to library name.
|
||||
"""
|
||||
symbol_map: dict[str, str] = {}
|
||||
|
||||
if not self.nm_path:
|
||||
return symbol_map
|
||||
|
||||
for lib_name, file_paths in libraries.items():
|
||||
result = run_tool(
|
||||
[self.nm_path, "--defined-only", *(str(p) for p in file_paths)],
|
||||
timeout=10,
|
||||
)
|
||||
if result is None or result.returncode != 0:
|
||||
continue
|
||||
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
|
||||
sym_type = parts[-2]
|
||||
sym_name = parts[-1]
|
||||
|
||||
# Include global defined symbols (uppercase) and weak symbols (W/V)
|
||||
if sym_type in _NM_DEFINED_GLOBAL_TYPES:
|
||||
symbol_map[sym_name] = lib_name
|
||||
|
||||
return symbol_map
|
||||
|
||||
@staticmethod
|
||||
def _build_heuristic_to_lib_mapping(
|
||||
library_names: set[str],
|
||||
) -> dict[str, str]:
|
||||
"""Build mapping from heuristic pattern categories to discovered libraries.
|
||||
|
||||
Heuristic categories like ``mdns_lib``, ``web_server_lib``, ``async_tcp``
|
||||
exist as approximations for library attribution. When we discover the
|
||||
actual library, symbols matching those heuristics should be redirected
|
||||
to the ``[lib]`` category instead.
|
||||
|
||||
The mapping is built by checking if the normalized category name
|
||||
(stripped of ``_lib`` suffix and underscores) appears as a substring
|
||||
of any discovered library name.
|
||||
|
||||
Examples::
|
||||
|
||||
mdns_lib -> mdns -> in "mdns" or "esp8266mdns" -> [lib]mdns
|
||||
web_server_lib -> webserver -> in "espasyncwebserver" -> [lib]espasyncwebserver
|
||||
async_tcp -> asynctcp -> in "espasynctcp" -> [lib]espasynctcp
|
||||
|
||||
Args:
|
||||
library_names: Set of discovered library names (lowercase).
|
||||
|
||||
Returns:
|
||||
Dictionary mapping heuristic category to ``[lib]<name>`` string.
|
||||
"""
|
||||
mapping: dict[str, str] = {}
|
||||
all_categories = set(SYMBOL_PATTERNS) | set(DEMANGLED_PATTERNS)
|
||||
|
||||
for category in all_categories:
|
||||
base = category.removesuffix("_lib").replace("_", "")
|
||||
# Collect all libraries whose name contains the base string
|
||||
candidates = [lib_name for lib_name in library_names if base in lib_name]
|
||||
if not candidates:
|
||||
continue
|
||||
|
||||
# Choose a deterministic "best" match:
|
||||
# 1. Prefer exact name matches over substring matches.
|
||||
# 2. Among non-exact matches, prefer the shortest library name.
|
||||
# 3. Break remaining ties lexicographically.
|
||||
best_lib = min(
|
||||
candidates,
|
||||
key=lambda lib_name, _base=base: (
|
||||
lib_name != _base,
|
||||
len(lib_name),
|
||||
lib_name,
|
||||
),
|
||||
)
|
||||
mapping[category] = f"{_COMPONENT_PREFIX_LIB}{best_lib}"
|
||||
|
||||
if mapping:
|
||||
_LOGGER.debug(
|
||||
"Heuristic-to-library redirects: %s",
|
||||
", ".join(f"{k} -> {v}" for k, v in sorted(mapping.items())),
|
||||
)
|
||||
|
||||
return mapping
|
||||
|
||||
def _parse_map_file(self) -> dict[str, str] | None:
|
||||
"""Parse linker map file to build authoritative symbol-to-library mapping.
|
||||
|
||||
The linker map file contains the definitive source attribution for every
|
||||
symbol, including local/static ones that ``nm`` cannot safely export.
|
||||
|
||||
Map file format (GNU ld)::
|
||||
|
||||
.text._mdns_service_task
|
||||
0x400e9fdc 0x65c .pioenvs/env/esp-idf/espressif__mdns/libespressif__mdns.a(mdns.c.o)
|
||||
|
||||
Each section entry has a ``.section.symbol_name`` line followed by an
|
||||
indented line with address, size, and source path.
|
||||
|
||||
Returns:
|
||||
Symbol-to-library dict, or ``None`` if no usable map file exists.
|
||||
"""
|
||||
map_path = self.elf_path.with_suffix(".map")
|
||||
if not map_path.exists() or map_path.stat().st_size < 10000:
|
||||
return None
|
||||
|
||||
_LOGGER.info("Parsing linker map file: %s", map_path.name)
|
||||
|
||||
try:
|
||||
map_text = map_path.read_text(encoding="utf-8", errors="replace")
|
||||
except OSError as err:
|
||||
_LOGGER.warning("Failed to read map file: %s", err)
|
||||
return None
|
||||
|
||||
symbol_map: dict[str, str] = {}
|
||||
current_symbol: str | None = None
|
||||
section_prefixes = (".text.", ".rodata.", ".data.", ".bss.", ".literal.")
|
||||
|
||||
for line in map_text.splitlines():
|
||||
# Match section.symbol line: " .text.symbol_name"
|
||||
# Single space indent, starts with dot
|
||||
if len(line) > 2 and line[0] == " " and line[1] == ".":
|
||||
stripped = line.strip()
|
||||
for prefix in section_prefixes:
|
||||
if stripped.startswith(prefix):
|
||||
current_symbol = stripped[len(prefix) :]
|
||||
break
|
||||
else:
|
||||
current_symbol = None
|
||||
continue
|
||||
|
||||
# Match source attribution line: " 0xADDR 0xSIZE source_path"
|
||||
if current_symbol is None:
|
||||
continue
|
||||
|
||||
fields = line.split()
|
||||
# Skip compiler-generated local names (e.g., packet$19, buf$20)
|
||||
# that can collide across compilation units
|
||||
if (
|
||||
len(fields) >= 3
|
||||
and fields[0].startswith("0x")
|
||||
and fields[1].startswith("0x")
|
||||
and not _COMPILER_LOCAL_PATTERN.match(current_symbol)
|
||||
):
|
||||
source_path = fields[2]
|
||||
# Check if source path contains a known library directory
|
||||
for dir_key, lib_name in self._lib_hash_to_name.items():
|
||||
if dir_key in source_path:
|
||||
symbol_map[current_symbol] = lib_name
|
||||
break
|
||||
|
||||
current_symbol = None
|
||||
|
||||
return symbol_map or None
|
||||
|
||||
def _scan_libraries(self) -> None:
|
||||
"""Discover third-party libraries and build symbol mapping.
|
||||
|
||||
Scans both PlatformIO ``lib<hex>/`` directories (Arduino builds) and
|
||||
ESP-IDF ``managed_components/`` (IDF builds) to find library archives.
|
||||
|
||||
Uses the linker map file for authoritative symbol attribution when
|
||||
available, falling back to ``nm`` scanning with heuristic redirects.
|
||||
"""
|
||||
libraries: dict[str, list[Path]] = {}
|
||||
self._discover_pio_libraries(libraries, self._lib_hash_to_name)
|
||||
self._discover_idf_managed_components(libraries, self._lib_hash_to_name)
|
||||
|
||||
if not libraries:
|
||||
_LOGGER.debug("No third-party libraries found")
|
||||
return
|
||||
|
||||
_LOGGER.info(
|
||||
"Scanning %d libraries: %s",
|
||||
len(libraries),
|
||||
", ".join(sorted(libraries)),
|
||||
)
|
||||
|
||||
# Heuristic redirect catches local symbols (e.g., mdns_task_buffer$14)
|
||||
# that can't be safely added to the symbol map due to name collisions
|
||||
self._heuristic_to_lib = self._build_heuristic_to_lib_mapping(
|
||||
set(libraries.keys())
|
||||
)
|
||||
|
||||
# Try linker map file first (authoritative, includes local symbols)
|
||||
map_symbols = self._parse_map_file()
|
||||
if map_symbols is not None:
|
||||
self._lib_symbol_map = map_symbols
|
||||
_LOGGER.info(
|
||||
"Built library symbol map from linker map: %d symbols",
|
||||
len(self._lib_symbol_map),
|
||||
)
|
||||
return
|
||||
|
||||
# Fall back to nm scanning (global symbols only)
|
||||
self._lib_symbol_map = self._build_library_symbol_map(libraries)
|
||||
|
||||
_LOGGER.info(
|
||||
"Built library symbol map from nm: %d symbols from %d libraries",
|
||||
len(self._lib_symbol_map),
|
||||
len(libraries),
|
||||
)
|
||||
|
||||
def _find_object_files_dir(self) -> Path | None:
|
||||
"""Find the directory containing object files for this build.
|
||||
|
||||
@@ -559,9 +903,21 @@ class MemoryAnalyzer:
|
||||
if "esphome" in parts and "components" not in parts:
|
||||
return _COMPONENT_CORE
|
||||
|
||||
# Framework/library files - return the first path component
|
||||
# e.g., lib65b/ESPAsyncTCP/... -> lib65b
|
||||
# FrameworkArduino/... -> FrameworkArduino
|
||||
# Framework/library files - check for PlatformIO library hash dirs
|
||||
# e.g., lib65b/ESPAsyncTCP/... -> [lib]espasynctcp
|
||||
if parts and parts[0] in self._lib_hash_to_name:
|
||||
return f"{_COMPONENT_PREFIX_LIB}{self._lib_hash_to_name[parts[0]]}"
|
||||
|
||||
# ESP-IDF managed components: managed_components/espressif__mdns/... -> [lib]mdns
|
||||
if (
|
||||
len(parts) >= 2
|
||||
and parts[0] == "managed_components"
|
||||
and parts[1] in self._lib_hash_to_name
|
||||
):
|
||||
return f"{_COMPONENT_PREFIX_LIB}{self._lib_hash_to_name[parts[1]]}"
|
||||
|
||||
# Other framework/library files - return the first path component
|
||||
# e.g., FrameworkArduino/... -> FrameworkArduino
|
||||
return parts[0] if parts else source_file
|
||||
|
||||
def _analyze_cswtch_symbols(self) -> None:
|
||||
|
||||
@@ -15,6 +15,7 @@ from . import (
|
||||
_COMPONENT_CORE,
|
||||
_COMPONENT_PREFIX_ESPHOME,
|
||||
_COMPONENT_PREFIX_EXTERNAL,
|
||||
_COMPONENT_PREFIX_LIB,
|
||||
RAM_SECTIONS,
|
||||
MemoryAnalyzer,
|
||||
)
|
||||
@@ -408,6 +409,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
for name, mem in components
|
||||
if name.startswith(_COMPONENT_PREFIX_EXTERNAL)
|
||||
]
|
||||
library_components = [
|
||||
(name, mem)
|
||||
for name, mem in components
|
||||
if name.startswith(_COMPONENT_PREFIX_LIB)
|
||||
]
|
||||
|
||||
top_esphome_components = sorted(
|
||||
esphome_components, key=lambda x: x[1].flash_total, reverse=True
|
||||
@@ -418,6 +424,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
external_components, key=lambda x: x[1].flash_total, reverse=True
|
||||
)
|
||||
|
||||
# Include all library components
|
||||
top_library_components = sorted(
|
||||
library_components, key=lambda x: x[1].flash_total, reverse=True
|
||||
)
|
||||
|
||||
# Check if API component exists and ensure it's included
|
||||
api_component = None
|
||||
for name, mem in components:
|
||||
@@ -436,10 +447,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
if name in system_components_to_include
|
||||
]
|
||||
|
||||
# Combine all components to analyze: top ESPHome + all external + API if not already included + system components
|
||||
# Combine all components to analyze: top ESPHome + all external + libraries + API if not already included + system components
|
||||
components_to_analyze = (
|
||||
list(top_esphome_components)
|
||||
+ list(top_external_components)
|
||||
+ list(top_library_components)
|
||||
+ system_components
|
||||
)
|
||||
if api_component and api_component not in components_to_analyze:
|
||||
|
||||
@@ -283,7 +283,7 @@ void APIConnection::loop() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool APIConnection::send_disconnect_response() {
|
||||
bool APIConnection::send_disconnect_response_() {
|
||||
// remote initiated disconnect_client
|
||||
// don't close yet, we still need to send the disconnect response
|
||||
// close will happen on next loop
|
||||
@@ -407,7 +407,7 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
|
||||
msg.device_class = cover->get_device_class_ref();
|
||||
return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||
void APIConnection::on_cover_command_request(const CoverCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
|
||||
if (msg.has_position)
|
||||
call.set_position(msg.position);
|
||||
@@ -450,7 +450,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
|
||||
msg.supported_preset_modes = &traits.supported_preset_modes();
|
||||
return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
void APIConnection::on_fan_command_request(const FanCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(fan::Fan, fan, fan)
|
||||
if (msg.has_state)
|
||||
call.set_state(msg.state);
|
||||
@@ -518,7 +518,7 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
|
||||
msg.effects = &effects_list;
|
||||
return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
void APIConnection::on_light_command_request(const LightCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(light::LightState, light, light)
|
||||
if (msg.has_state)
|
||||
call.set_state(msg.state);
|
||||
@@ -595,7 +595,7 @@ uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *
|
||||
msg.device_class = a_switch->get_device_class_ref();
|
||||
return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
||||
void APIConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
|
||||
ENTITY_COMMAND_GET(switch_::Switch, a_switch, switch)
|
||||
|
||||
if (msg.state) {
|
||||
@@ -693,7 +693,7 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
|
||||
msg.supported_swing_modes = &traits.get_supported_swing_modes();
|
||||
return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
void APIConnection::on_climate_command_request(const ClimateCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(climate::Climate, climate, climate)
|
||||
if (msg.has_mode)
|
||||
call.set_mode(static_cast<climate::ClimateMode>(msg.mode));
|
||||
@@ -743,7 +743,7 @@ uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *
|
||||
msg.step = number->traits.get_step();
|
||||
return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::number_command(const NumberCommandRequest &msg) {
|
||||
void APIConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(number::Number, number, number)
|
||||
call.set_value(msg.state);
|
||||
call.perform();
|
||||
@@ -768,7 +768,7 @@ uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *co
|
||||
ListEntitiesDateResponse msg;
|
||||
return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::date_command(const DateCommandRequest &msg) {
|
||||
void APIConnection::on_date_command_request(const DateCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(datetime::DateEntity, date, date)
|
||||
call.set_date(msg.year, msg.month, msg.day);
|
||||
call.perform();
|
||||
@@ -793,7 +793,7 @@ uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *co
|
||||
ListEntitiesTimeResponse msg;
|
||||
return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::time_command(const TimeCommandRequest &msg) {
|
||||
void APIConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(datetime::TimeEntity, time, time)
|
||||
call.set_time(msg.hour, msg.minute, msg.second);
|
||||
call.perform();
|
||||
@@ -820,7 +820,7 @@ uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection
|
||||
ListEntitiesDateTimeResponse msg;
|
||||
return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
|
||||
void APIConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(datetime::DateTimeEntity, datetime, datetime)
|
||||
call.set_datetime(msg.epoch_seconds);
|
||||
call.perform();
|
||||
@@ -849,7 +849,7 @@ uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *co
|
||||
msg.pattern = text->traits.get_pattern_ref();
|
||||
return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::text_command(const TextCommandRequest &msg) {
|
||||
void APIConnection::on_text_command_request(const TextCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(text::Text, text, text)
|
||||
call.set_value(msg.state);
|
||||
call.perform();
|
||||
@@ -875,7 +875,7 @@ uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *
|
||||
msg.options = &select->traits.get_options();
|
||||
return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||
void APIConnection::on_select_command_request(const SelectCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
|
||||
call.set_option(msg.state.c_str(), msg.state.size());
|
||||
call.perform();
|
||||
@@ -889,7 +889,7 @@ uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *
|
||||
msg.device_class = button->get_device_class_ref();
|
||||
return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
|
||||
void esphome::api::APIConnection::on_button_command_request(const ButtonCommandRequest &msg) {
|
||||
ENTITY_COMMAND_GET(button::Button, button, button)
|
||||
button->press();
|
||||
}
|
||||
@@ -915,7 +915,7 @@ uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *co
|
||||
msg.requires_code = a_lock->traits.get_requires_code();
|
||||
return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::lock_command(const LockCommandRequest &msg) {
|
||||
void APIConnection::on_lock_command_request(const LockCommandRequest &msg) {
|
||||
ENTITY_COMMAND_GET(lock::Lock, a_lock, lock)
|
||||
|
||||
switch (msg.command) {
|
||||
@@ -953,7 +953,7 @@ uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *c
|
||||
msg.supports_stop = traits.get_supports_stop();
|
||||
return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::valve_command(const ValveCommandRequest &msg) {
|
||||
void APIConnection::on_valve_command_request(const ValveCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(valve::Valve, valve, valve)
|
||||
if (msg.has_position)
|
||||
call.set_position(msg.position);
|
||||
@@ -997,7 +997,7 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
|
||||
return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
|
||||
remaining_size);
|
||||
}
|
||||
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
|
||||
void APIConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(media_player::MediaPlayer, media_player, media_player)
|
||||
if (msg.has_command) {
|
||||
call.set_command(static_cast<media_player::MediaPlayerCommand>(msg.command));
|
||||
@@ -1064,7 +1064,7 @@ uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *
|
||||
ListEntitiesCameraResponse msg;
|
||||
return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||
void APIConnection::on_camera_image_request(const CameraImageRequest &msg) {
|
||||
if (camera::Camera::instance() == nullptr)
|
||||
return;
|
||||
|
||||
@@ -1093,41 +1093,47 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
|
||||
#endif
|
||||
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
void APIConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
|
||||
}
|
||||
void APIConnection::unsubscribe_bluetooth_le_advertisements() {
|
||||
void APIConnection::on_unsubscribe_bluetooth_le_advertisements_request() {
|
||||
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
|
||||
}
|
||||
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||
void APIConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
|
||||
}
|
||||
void APIConnection::bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) {
|
||||
void APIConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read(msg);
|
||||
}
|
||||
void APIConnection::bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) {
|
||||
void APIConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write(msg);
|
||||
}
|
||||
void APIConnection::bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) {
|
||||
void APIConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read_descriptor(msg);
|
||||
}
|
||||
void APIConnection::bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) {
|
||||
void APIConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write_descriptor(msg);
|
||||
}
|
||||
void APIConnection::bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) {
|
||||
void APIConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_send_services(msg);
|
||||
}
|
||||
|
||||
void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) {
|
||||
void APIConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg);
|
||||
}
|
||||
|
||||
bool APIConnection::send_subscribe_bluetooth_connections_free_response() {
|
||||
bool APIConnection::send_subscribe_bluetooth_connections_free_response_() {
|
||||
bluetooth_proxy::global_bluetooth_proxy->send_connections_free(this);
|
||||
return true;
|
||||
}
|
||||
void APIConnection::on_subscribe_bluetooth_connections_free_request() {
|
||||
if (!this->send_subscribe_bluetooth_connections_free_response_()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
|
||||
void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
|
||||
void APIConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_scanner_set_mode(
|
||||
msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
|
||||
}
|
||||
@@ -1139,7 +1145,7 @@ bool APIConnection::check_voice_assistant_api_connection_() const {
|
||||
voice_assistant::global_voice_assistant->get_api_connection() == this;
|
||||
}
|
||||
|
||||
void APIConnection::subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) {
|
||||
void APIConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
||||
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||
voice_assistant::global_voice_assistant->client_subscription(this, msg.subscribe);
|
||||
}
|
||||
@@ -1185,7 +1191,7 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno
|
||||
}
|
||||
}
|
||||
|
||||
bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) {
|
||||
bool APIConnection::send_voice_assistant_get_configuration_response_(const VoiceAssistantConfigurationRequest &msg) {
|
||||
VoiceAssistantConfigurationResponse resp;
|
||||
if (!this->check_voice_assistant_api_connection_()) {
|
||||
return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
|
||||
@@ -1222,8 +1228,13 @@ bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceA
|
||||
resp.max_active_wake_words = config.max_active_wake_words;
|
||||
return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
|
||||
if (!this->send_voice_assistant_get_configuration_response_(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
|
||||
void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
||||
void APIConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
||||
if (this->check_voice_assistant_api_connection_()) {
|
||||
voice_assistant::global_voice_assistant->on_set_configuration(msg.active_wake_words);
|
||||
}
|
||||
@@ -1231,11 +1242,11 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
|
||||
#endif
|
||||
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
void APIConnection::zwave_proxy_frame(const ZWaveProxyFrame &msg) {
|
||||
void APIConnection::on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) {
|
||||
zwave_proxy::global_zwave_proxy->send_frame(msg.data, msg.data_len);
|
||||
}
|
||||
|
||||
void APIConnection::zwave_proxy_request(const ZWaveProxyRequest &msg) {
|
||||
void APIConnection::on_z_wave_proxy_request(const ZWaveProxyRequest &msg) {
|
||||
zwave_proxy::global_zwave_proxy->zwave_proxy_request(this, msg.type);
|
||||
}
|
||||
#endif
|
||||
@@ -1263,7 +1274,7 @@ uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, AP
|
||||
return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
|
||||
conn, remaining_size);
|
||||
}
|
||||
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
|
||||
void APIConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(alarm_control_panel::AlarmControlPanel, a_alarm_control_panel, alarm_control_panel)
|
||||
switch (msg.command) {
|
||||
case enums::ALARM_CONTROL_PANEL_DISARM:
|
||||
@@ -1323,7 +1334,7 @@ uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnec
|
||||
return fill_and_encode_entity_info(wh, msg, ListEntitiesWaterHeaterResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
|
||||
void APIConnection::water_heater_command(const WaterHeaterCommandRequest &msg) {
|
||||
void APIConnection::on_water_heater_command_request(const WaterHeaterCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(water_heater::WaterHeater, water_heater, water_heater)
|
||||
if (msg.has_fields & enums::WATER_HEATER_COMMAND_HAS_MODE)
|
||||
call.set_mode(static_cast<water_heater::WaterHeaterMode>(msg.mode));
|
||||
@@ -1365,7 +1376,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
|
||||
#endif
|
||||
|
||||
#ifdef USE_IR_RF
|
||||
void APIConnection::infrared_rf_transmit_raw_timings(const InfraredRFTransmitRawTimingsRequest &msg) {
|
||||
void APIConnection::on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) {
|
||||
// TODO: When RF is implemented, add a field to the message to distinguish IR vs RF
|
||||
// and dispatch to the appropriate entity type based on that field.
|
||||
#ifdef USE_INFRARED
|
||||
@@ -1419,7 +1430,7 @@ uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *
|
||||
msg.device_class = update->get_device_class_ref();
|
||||
return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
void APIConnection::update_command(const UpdateCommandRequest &msg) {
|
||||
void APIConnection::on_update_command_request(const UpdateCommandRequest &msg) {
|
||||
ENTITY_COMMAND_GET(update::UpdateEntity, update, update)
|
||||
|
||||
switch (msg.command) {
|
||||
@@ -1473,7 +1484,7 @@ void APIConnection::complete_authentication_() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
||||
bool APIConnection::send_hello_response_(const HelloRequest &msg) {
|
||||
// Copy client name with truncation if needed (set_client_name handles truncation)
|
||||
this->helper_->set_client_name(msg.client_info.c_str(), msg.client_info.size());
|
||||
this->client_api_version_major_ = msg.api_version_major;
|
||||
@@ -1495,12 +1506,12 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
||||
return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
bool APIConnection::send_ping_response() {
|
||||
bool APIConnection::send_ping_response_() {
|
||||
PingResponse resp;
|
||||
return this->send_message(resp, PingResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
bool APIConnection::send_device_info_response() {
|
||||
bool APIConnection::send_device_info_response_() {
|
||||
DeviceInfoResponse resp{};
|
||||
resp.name = StringRef(App.get_name());
|
||||
resp.friendly_name = StringRef(App.get_friendly_name());
|
||||
@@ -1623,6 +1634,26 @@ bool APIConnection::send_device_info_response() {
|
||||
|
||||
return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::on_hello_request(const HelloRequest &msg) {
|
||||
if (!this->send_hello_response_(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIConnection::on_disconnect_request() {
|
||||
if (!this->send_disconnect_response_()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIConnection::on_ping_request() {
|
||||
if (!this->send_ping_response_()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIConnection::on_device_info_request() {
|
||||
if (!this->send_device_info_response_()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
||||
@@ -1661,7 +1692,7 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
||||
void APIConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
|
||||
bool found = false;
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
// Register the call and get a unique server-generated action_call_id
|
||||
@@ -1727,7 +1758,7 @@ void APIConnection::on_homeassistant_action_response(const HomeassistantActionRe
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
|
||||
bool APIConnection::send_noise_encryption_set_key_response_(const NoiseEncryptionSetKeyRequest &msg) {
|
||||
NoiseEncryptionSetKeyResponse resp;
|
||||
resp.success = false;
|
||||
|
||||
@@ -1748,9 +1779,14 @@ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryption
|
||||
|
||||
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
||||
if (!this->send_noise_encryption_set_key_response_(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void APIConnection::subscribe_home_assistant_states() { state_subs_at_ = 0; }
|
||||
void APIConnection::on_subscribe_home_assistant_states_request() { state_subs_at_ = 0; }
|
||||
#endif
|
||||
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
||||
if (this->flags_.remove)
|
||||
|
||||
@@ -28,7 +28,7 @@ static constexpr size_t MAX_INITIAL_PER_BATCH = 34; // For clients >= AP
|
||||
static_assert(MAX_MESSAGES_PER_BATCH >= MAX_INITIAL_PER_BATCH,
|
||||
"MAX_MESSAGES_PER_BATCH must be >= MAX_INITIAL_PER_BATCH");
|
||||
|
||||
class APIConnection final : public APIServerConnection {
|
||||
class APIConnection final : public APIServerConnectionBase {
|
||||
public:
|
||||
friend class APIServer;
|
||||
friend class ListEntitiesIterator;
|
||||
@@ -47,72 +47,72 @@ class APIConnection final : public APIServerConnection {
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool send_cover_state(cover::Cover *cover);
|
||||
void cover_command(const CoverCommandRequest &msg) override;
|
||||
void on_cover_command_request(const CoverCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool send_fan_state(fan::Fan *fan);
|
||||
void fan_command(const FanCommandRequest &msg) override;
|
||||
void on_fan_command_request(const FanCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool send_light_state(light::LightState *light);
|
||||
void light_command(const LightCommandRequest &msg) override;
|
||||
void on_light_command_request(const LightCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool send_sensor_state(sensor::Sensor *sensor);
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool send_switch_state(switch_::Switch *a_switch);
|
||||
void switch_command(const SwitchCommandRequest &msg) override;
|
||||
void on_switch_command_request(const SwitchCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
void set_camera_state(std::shared_ptr<camera::CameraImage> image);
|
||||
void camera_image(const CameraImageRequest &msg) override;
|
||||
void on_camera_image_request(const CameraImageRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool send_climate_state(climate::Climate *climate);
|
||||
void climate_command(const ClimateCommandRequest &msg) override;
|
||||
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_number_state(number::Number *number);
|
||||
void number_command(const NumberCommandRequest &msg) override;
|
||||
void on_number_command_request(const NumberCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
bool send_date_state(datetime::DateEntity *date);
|
||||
void date_command(const DateCommandRequest &msg) override;
|
||||
void on_date_command_request(const DateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool send_time_state(datetime::TimeEntity *time);
|
||||
void time_command(const TimeCommandRequest &msg) override;
|
||||
void on_time_command_request(const TimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool send_datetime_state(datetime::DateTimeEntity *datetime);
|
||||
void datetime_command(const DateTimeCommandRequest &msg) override;
|
||||
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool send_text_state(text::Text *text);
|
||||
void text_command(const TextCommandRequest &msg) override;
|
||||
void on_text_command_request(const TextCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_select_state(select::Select *select);
|
||||
void select_command(const SelectCommandRequest &msg) override;
|
||||
void on_select_command_request(const SelectCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void button_command(const ButtonCommandRequest &msg) override;
|
||||
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
bool send_lock_state(lock::Lock *a_lock);
|
||||
void lock_command(const LockCommandRequest &msg) override;
|
||||
void on_lock_command_request(const LockCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool send_valve_state(valve::Valve *valve);
|
||||
void valve_command(const ValveCommandRequest &msg) override;
|
||||
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
bool send_media_player_state(media_player::MediaPlayer *media_player);
|
||||
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
||||
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
||||
#endif
|
||||
bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len);
|
||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||
@@ -126,18 +126,18 @@ class APIConnection final : public APIServerConnection {
|
||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||
#endif // USE_API_HOMEASSISTANT_SERVICES
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
void unsubscribe_bluetooth_le_advertisements() override;
|
||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
void on_unsubscribe_bluetooth_le_advertisements_request() override;
|
||||
|
||||
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
|
||||
void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) override;
|
||||
void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) override;
|
||||
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
||||
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
|
||||
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
||||
bool send_subscribe_bluetooth_connections_free_response() override;
|
||||
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
|
||||
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||
void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
|
||||
void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
|
||||
void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
|
||||
void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
||||
void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
|
||||
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
|
||||
void on_subscribe_bluetooth_connections_free_request() override;
|
||||
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
|
||||
|
||||
#endif
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
@@ -148,33 +148,33 @@ class APIConnection final : public APIServerConnection {
|
||||
#endif
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
|
||||
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
||||
void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
|
||||
void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override;
|
||||
bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) override;
|
||||
void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
|
||||
void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override;
|
||||
void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
void zwave_proxy_frame(const ZWaveProxyFrame &msg) override;
|
||||
void zwave_proxy_request(const ZWaveProxyRequest &msg) override;
|
||||
void on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) override;
|
||||
void on_z_wave_proxy_request(const ZWaveProxyRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
||||
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WATER_HEATER
|
||||
bool send_water_heater_state(water_heater::WaterHeater *water_heater);
|
||||
void water_heater_command(const WaterHeaterCommandRequest &msg) override;
|
||||
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_IR_RF
|
||||
void infrared_rf_transmit_raw_timings(const InfraredRFTransmitRawTimingsRequest &msg) override;
|
||||
void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) override;
|
||||
void send_infrared_rf_receive_event(const InfraredRFReceiveEvent &msg);
|
||||
#endif
|
||||
|
||||
@@ -184,7 +184,7 @@ class APIConnection final : public APIServerConnection {
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
bool send_update_state(update::UpdateEntity *update);
|
||||
void update_command(const UpdateCommandRequest &msg) override;
|
||||
void on_update_command_request(const UpdateCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
void on_disconnect_response() override;
|
||||
@@ -198,12 +198,12 @@ class APIConnection final : public APIServerConnection {
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
void on_get_time_response(const GetTimeResponse &value) override;
|
||||
#endif
|
||||
bool send_hello_response(const HelloRequest &msg) override;
|
||||
bool send_disconnect_response() override;
|
||||
bool send_ping_response() override;
|
||||
bool send_device_info_response() override;
|
||||
void list_entities() override { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
|
||||
void subscribe_states() override {
|
||||
void on_hello_request(const HelloRequest &msg) override;
|
||||
void on_disconnect_request() override;
|
||||
void on_ping_request() override;
|
||||
void on_device_info_request() override;
|
||||
void on_list_entities_request() override { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
|
||||
void on_subscribe_states_request() override {
|
||||
this->flags_.state_subscription = true;
|
||||
// Start initial state iterator only if no iterator is active
|
||||
// If list_entities is running, we'll start initial_state when it completes
|
||||
@@ -211,19 +211,19 @@ class APIConnection final : public APIServerConnection {
|
||||
this->begin_iterator_(ActiveIterator::INITIAL_STATE);
|
||||
}
|
||||
}
|
||||
void subscribe_logs(const SubscribeLogsRequest &msg) override {
|
||||
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override {
|
||||
this->flags_.log_subscription = msg.level;
|
||||
if (msg.dump_config)
|
||||
App.schedule_dump_config();
|
||||
}
|
||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||
void subscribe_homeassistant_services() override { this->flags_.service_call_subscription = true; }
|
||||
void on_subscribe_homeassistant_services_request() override { this->flags_.service_call_subscription = true; }
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void subscribe_home_assistant_states() override;
|
||||
void on_subscribe_home_assistant_states_request() override;
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
void send_execute_service_response(uint32_t call_id, bool success, StringRef error_message);
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
@@ -233,7 +233,7 @@ class APIConnection final : public APIServerConnection {
|
||||
#endif // USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||
#endif
|
||||
|
||||
bool is_authenticated() override {
|
||||
@@ -285,6 +285,21 @@ class APIConnection final : public APIServerConnection {
|
||||
// Helper function to handle authentication completion
|
||||
void complete_authentication_();
|
||||
|
||||
// Pattern B helpers: send response and return success/failure
|
||||
bool send_hello_response_(const HelloRequest &msg);
|
||||
bool send_disconnect_response_();
|
||||
bool send_ping_response_();
|
||||
bool send_device_info_response_();
|
||||
#ifdef USE_API_NOISE
|
||||
bool send_noise_encryption_set_key_response_(const NoiseEncryptionSetKeyRequest &msg);
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool send_subscribe_bluetooth_connections_free_response_();
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
bool send_voice_assistant_get_configuration_response_(const VoiceAssistantConfigurationRequest &msg);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CAMERA
|
||||
void try_send_camera_image_();
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,23 @@ void APIServerConnectionBase::log_receive_message_(const LogString *name) {
|
||||
#endif
|
||||
|
||||
void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
|
||||
// Check authentication/connection requirements
|
||||
switch (msg_type) {
|
||||
case HelloRequest::MESSAGE_TYPE: // No setup required
|
||||
case DisconnectRequest::MESSAGE_TYPE: // No setup required
|
||||
case PingRequest::MESSAGE_TYPE: // No setup required
|
||||
break;
|
||||
case DeviceInfoRequest::MESSAGE_TYPE: // Connection setup only
|
||||
if (!this->check_connection_setup_()) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!this->check_authenticated_()) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (msg_type) {
|
||||
case HelloRequest::MESSAGE_TYPE: {
|
||||
HelloRequest msg;
|
||||
@@ -623,222 +640,4 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
}
|
||||
}
|
||||
|
||||
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
|
||||
if (!this->send_hello_response(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIServerConnection::on_disconnect_request() {
|
||||
if (!this->send_disconnect_response()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIServerConnection::on_ping_request() {
|
||||
if (!this->send_ping_response()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIServerConnection::on_device_info_request() {
|
||||
if (!this->send_device_info_response()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
void APIServerConnection::on_list_entities_request() { this->list_entities(); }
|
||||
void APIServerConnection::on_subscribe_states_request() { this->subscribe_states(); }
|
||||
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) { this->subscribe_logs(msg); }
|
||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||
void APIServerConnection::on_subscribe_homeassistant_services_request() { this->subscribe_homeassistant_services(); }
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void APIServerConnection::on_subscribe_home_assistant_states_request() { this->subscribe_home_assistant_states(); }
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { this->execute_service(msg); }
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
||||
if (!this->send_noise_encryption_set_key_response(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) { this->button_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { this->camera_image(msg); }
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) { this->climate_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) { this->cover_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) { this->date_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
||||
this->datetime_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) { this->fan_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) { this->light_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) { this->lock_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
|
||||
this->media_player_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) { this->number_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) { this->select_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_SIREN
|
||||
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) { this->siren_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) { this->switch_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) { this->text_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) { this->time_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) { this->update_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { this->valve_command(msg); }
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
void APIServerConnection::on_water_heater_command_request(const WaterHeaterCommandRequest &msg) {
|
||||
this->water_heater_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
this->subscribe_bluetooth_le_advertisements(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||
this->bluetooth_device_request(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
|
||||
this->bluetooth_gatt_get_services(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
|
||||
this->bluetooth_gatt_read(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
|
||||
this->bluetooth_gatt_write(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
|
||||
this->bluetooth_gatt_read_descriptor(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
|
||||
this->bluetooth_gatt_write_descriptor(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
|
||||
this->bluetooth_gatt_notify(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_subscribe_bluetooth_connections_free_request() {
|
||||
if (!this->send_subscribe_bluetooth_connections_free_response()) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request() {
|
||||
this->unsubscribe_bluetooth_le_advertisements();
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
|
||||
this->bluetooth_scanner_set_mode(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
||||
this->subscribe_voice_assistant(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
|
||||
if (!this->send_voice_assistant_get_configuration_response(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
||||
this->voice_assistant_set_configuration(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
|
||||
this->alarm_control_panel_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
void APIServerConnection::on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) { this->zwave_proxy_frame(msg); }
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
void APIServerConnection::on_z_wave_proxy_request(const ZWaveProxyRequest &msg) { this->zwave_proxy_request(msg); }
|
||||
#endif
|
||||
#ifdef USE_IR_RF
|
||||
void APIServerConnection::on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) {
|
||||
this->infrared_rf_transmit_raw_timings(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
void APIServerConnection::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
|
||||
// Check authentication/connection requirements for messages
|
||||
switch (msg_type) {
|
||||
case HelloRequest::MESSAGE_TYPE: // No setup required
|
||||
case DisconnectRequest::MESSAGE_TYPE: // No setup required
|
||||
case PingRequest::MESSAGE_TYPE: // No setup required
|
||||
break; // Skip all checks for these messages
|
||||
case DeviceInfoRequest::MESSAGE_TYPE: // Connection setup only
|
||||
if (!this->check_connection_setup_()) {
|
||||
return; // Connection not setup
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// All other messages require authentication (which includes connection check)
|
||||
if (!this->check_authenticated_()) {
|
||||
return; // Authentication failed
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Call base implementation to process the message
|
||||
APIServerConnectionBase::read_message(msg_size, msg_type, msg_data);
|
||||
}
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
@@ -228,270 +228,4 @@ class APIServerConnectionBase : public ProtoService {
|
||||
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
|
||||
};
|
||||
|
||||
class APIServerConnection : public APIServerConnectionBase {
|
||||
public:
|
||||
virtual bool send_hello_response(const HelloRequest &msg) = 0;
|
||||
virtual bool send_disconnect_response() = 0;
|
||||
virtual bool send_ping_response() = 0;
|
||||
virtual bool send_device_info_response() = 0;
|
||||
virtual void list_entities() = 0;
|
||||
virtual void subscribe_states() = 0;
|
||||
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
|
||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||
virtual void subscribe_homeassistant_services() = 0;
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
virtual void subscribe_home_assistant_states() = 0;
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
virtual bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
virtual void cover_command(const CoverCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
virtual void date_command(const DateCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
virtual void fan_command(const FanCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
virtual void light_command(const LightCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
virtual void lock_command(const LockCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual void select_command(const SelectCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_SIREN
|
||||
virtual void siren_command(const SirenCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
virtual void switch_command(const SwitchCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
virtual void text_command(const TextCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
virtual void time_command(const TimeCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
virtual void update_command(const UpdateCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
virtual void valve_command(const ValveCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
virtual void water_heater_command(const WaterHeaterCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual bool send_subscribe_bluetooth_connections_free_response() = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void unsubscribe_bluetooth_le_advertisements() = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
virtual void zwave_proxy_frame(const ZWaveProxyFrame &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
virtual void zwave_proxy_request(const ZWaveProxyRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_IR_RF
|
||||
virtual void infrared_rf_transmit_raw_timings(const InfraredRFTransmitRawTimingsRequest &msg) = 0;
|
||||
#endif
|
||||
protected:
|
||||
void on_hello_request(const HelloRequest &msg) override;
|
||||
void on_disconnect_request() override;
|
||||
void on_ping_request() override;
|
||||
void on_device_info_request() override;
|
||||
void on_list_entities_request() override;
|
||||
void on_subscribe_states_request() override;
|
||||
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
|
||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||
void on_subscribe_homeassistant_services_request() override;
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void on_subscribe_home_assistant_states_request() override;
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
void on_camera_image_request(const CameraImageRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
void on_cover_command_request(const CoverCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void on_date_command_request(const DateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
void on_fan_command_request(const FanCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
void on_light_command_request(const LightCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
void on_lock_command_request(const LockCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void on_number_command_request(const NumberCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void on_select_command_request(const SelectCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SIREN
|
||||
void on_siren_command_request(const SirenCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void on_switch_command_request(const SwitchCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void on_text_command_request(const TextCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void on_time_command_request(const TimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
void on_update_command_request(const UpdateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_subscribe_bluetooth_connections_free_request() override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_unsubscribe_bluetooth_le_advertisements_request() override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
void on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) override;
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
void on_z_wave_proxy_request(const ZWaveProxyRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_IR_RF
|
||||
void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) override;
|
||||
#endif
|
||||
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
@@ -15,8 +15,10 @@ void CSE7766Component::loop() {
|
||||
this->raw_data_index_ = 0;
|
||||
}
|
||||
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail == 0) {
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "dfplayer.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -132,139 +133,153 @@ void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) {
|
||||
|
||||
void DFPlayer::loop() {
|
||||
// Read message
|
||||
while (this->available()) {
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail <= 0)
|
||||
return;
|
||||
|
||||
if (this->read_pos_ == DFPLAYER_READ_BUFFER_LENGTH)
|
||||
this->read_pos_ = 0;
|
||||
|
||||
switch (this->read_pos_) {
|
||||
case 0: // Start mark
|
||||
if (byte != 0x7E)
|
||||
continue;
|
||||
break;
|
||||
case 1: // Version
|
||||
if (byte != 0xFF) {
|
||||
ESP_LOGW(TAG, "Expected Version 0xFF, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 2: // Buffer length
|
||||
if (byte != 0x06) {
|
||||
ESP_LOGW(TAG, "Expected Buffer length 0x06, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 9: // End byte
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
char byte_sequence[100];
|
||||
byte_sequence[0] = '\0';
|
||||
for (size_t i = 0; i < this->read_pos_ + 1; ++i) {
|
||||
snprintf(byte_sequence + strlen(byte_sequence), sizeof(byte_sequence) - strlen(byte_sequence), "%02X ",
|
||||
this->read_buffer_[i]);
|
||||
}
|
||||
ESP_LOGVV(TAG, "Received byte sequence: %s", byte_sequence);
|
||||
#endif
|
||||
if (byte != 0xEF) {
|
||||
ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
// Parse valid received command
|
||||
uint8_t cmd = this->read_buffer_[3];
|
||||
uint16_t argument = (this->read_buffer_[5] << 8) | this->read_buffer_[6];
|
||||
|
||||
ESP_LOGV(TAG, "Received message cmd: %#02x arg %#04x", cmd, argument);
|
||||
|
||||
switch (cmd) {
|
||||
case 0x3A:
|
||||
if (argument == 1) {
|
||||
ESP_LOGI(TAG, "USB loaded");
|
||||
} else if (argument == 2) {
|
||||
ESP_LOGI(TAG, "TF Card loaded");
|
||||
}
|
||||
break;
|
||||
case 0x3B:
|
||||
if (argument == 1) {
|
||||
ESP_LOGI(TAG, "USB unloaded");
|
||||
} else if (argument == 2) {
|
||||
ESP_LOGI(TAG, "TF Card unloaded");
|
||||
}
|
||||
break;
|
||||
case 0x3F:
|
||||
if (argument == 1) {
|
||||
ESP_LOGI(TAG, "USB available");
|
||||
} else if (argument == 2) {
|
||||
ESP_LOGI(TAG, "TF Card available");
|
||||
} else if (argument == 3) {
|
||||
ESP_LOGI(TAG, "USB, TF Card available");
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
ESP_LOGV(TAG, "Nack");
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
switch (argument) {
|
||||
case 0x01:
|
||||
ESP_LOGE(TAG, "Module is busy or uninitialized");
|
||||
break;
|
||||
case 0x02:
|
||||
ESP_LOGE(TAG, "Module is in sleep mode");
|
||||
break;
|
||||
case 0x03:
|
||||
ESP_LOGE(TAG, "Serial receive error");
|
||||
break;
|
||||
case 0x04:
|
||||
ESP_LOGE(TAG, "Checksum incorrect");
|
||||
break;
|
||||
case 0x05:
|
||||
ESP_LOGE(TAG, "Specified track is out of current track scope");
|
||||
this->is_playing_ = false;
|
||||
break;
|
||||
case 0x06:
|
||||
ESP_LOGE(TAG, "Specified track is not found");
|
||||
this->is_playing_ = false;
|
||||
break;
|
||||
case 0x07:
|
||||
ESP_LOGE(TAG, "Insertion error (an inserting operation only can be done when a track is being played)");
|
||||
break;
|
||||
case 0x08:
|
||||
ESP_LOGE(TAG, "SD card reading failed (SD card pulled out or damaged)");
|
||||
break;
|
||||
case 0x09:
|
||||
ESP_LOGE(TAG, "Entered into sleep mode");
|
||||
this->is_playing_ = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x41:
|
||||
ESP_LOGV(TAG, "Ack ok");
|
||||
this->is_playing_ |= this->ack_set_is_playing_;
|
||||
this->is_playing_ &= !this->ack_reset_is_playing_;
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
break;
|
||||
case 0x3C:
|
||||
ESP_LOGV(TAG, "Playback finished (USB drive)");
|
||||
this->is_playing_ = false;
|
||||
this->on_finished_playback_callback_.call();
|
||||
case 0x3D:
|
||||
ESP_LOGV(TAG, "Playback finished (SD card)");
|
||||
this->is_playing_ = false;
|
||||
this->on_finished_playback_callback_.call();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument);
|
||||
}
|
||||
this->sent_cmd_ = 0;
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
for (size_t bi = 0; bi < to_read; bi++) {
|
||||
uint8_t byte = buf[bi];
|
||||
|
||||
if (this->read_pos_ == DFPLAYER_READ_BUFFER_LENGTH)
|
||||
this->read_pos_ = 0;
|
||||
|
||||
switch (this->read_pos_) {
|
||||
case 0: // Start mark
|
||||
if (byte != 0x7E)
|
||||
continue;
|
||||
break;
|
||||
case 1: // Version
|
||||
if (byte != 0xFF) {
|
||||
ESP_LOGW(TAG, "Expected Version 0xFF, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 2: // Buffer length
|
||||
if (byte != 0x06) {
|
||||
ESP_LOGW(TAG, "Expected Buffer length 0x06, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 9: // End byte
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
char byte_sequence[100];
|
||||
byte_sequence[0] = '\0';
|
||||
for (size_t i = 0; i < this->read_pos_ + 1; ++i) {
|
||||
snprintf(byte_sequence + strlen(byte_sequence), sizeof(byte_sequence) - strlen(byte_sequence), "%02X ",
|
||||
this->read_buffer_[i]);
|
||||
}
|
||||
ESP_LOGVV(TAG, "Received byte sequence: %s", byte_sequence);
|
||||
#endif
|
||||
if (byte != 0xEF) {
|
||||
ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
// Parse valid received command
|
||||
uint8_t cmd = this->read_buffer_[3];
|
||||
uint16_t argument = (this->read_buffer_[5] << 8) | this->read_buffer_[6];
|
||||
|
||||
ESP_LOGV(TAG, "Received message cmd: %#02x arg %#04x", cmd, argument);
|
||||
|
||||
switch (cmd) {
|
||||
case 0x3A:
|
||||
if (argument == 1) {
|
||||
ESP_LOGI(TAG, "USB loaded");
|
||||
} else if (argument == 2) {
|
||||
ESP_LOGI(TAG, "TF Card loaded");
|
||||
}
|
||||
break;
|
||||
case 0x3B:
|
||||
if (argument == 1) {
|
||||
ESP_LOGI(TAG, "USB unloaded");
|
||||
} else if (argument == 2) {
|
||||
ESP_LOGI(TAG, "TF Card unloaded");
|
||||
}
|
||||
break;
|
||||
case 0x3F:
|
||||
if (argument == 1) {
|
||||
ESP_LOGI(TAG, "USB available");
|
||||
} else if (argument == 2) {
|
||||
ESP_LOGI(TAG, "TF Card available");
|
||||
} else if (argument == 3) {
|
||||
ESP_LOGI(TAG, "USB, TF Card available");
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
ESP_LOGV(TAG, "Nack");
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
switch (argument) {
|
||||
case 0x01:
|
||||
ESP_LOGE(TAG, "Module is busy or uninitialized");
|
||||
break;
|
||||
case 0x02:
|
||||
ESP_LOGE(TAG, "Module is in sleep mode");
|
||||
break;
|
||||
case 0x03:
|
||||
ESP_LOGE(TAG, "Serial receive error");
|
||||
break;
|
||||
case 0x04:
|
||||
ESP_LOGE(TAG, "Checksum incorrect");
|
||||
break;
|
||||
case 0x05:
|
||||
ESP_LOGE(TAG, "Specified track is out of current track scope");
|
||||
this->is_playing_ = false;
|
||||
break;
|
||||
case 0x06:
|
||||
ESP_LOGE(TAG, "Specified track is not found");
|
||||
this->is_playing_ = false;
|
||||
break;
|
||||
case 0x07:
|
||||
ESP_LOGE(TAG,
|
||||
"Insertion error (an inserting operation only can be done when a track is being played)");
|
||||
break;
|
||||
case 0x08:
|
||||
ESP_LOGE(TAG, "SD card reading failed (SD card pulled out or damaged)");
|
||||
break;
|
||||
case 0x09:
|
||||
ESP_LOGE(TAG, "Entered into sleep mode");
|
||||
this->is_playing_ = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x41:
|
||||
ESP_LOGV(TAG, "Ack ok");
|
||||
this->is_playing_ |= this->ack_set_is_playing_;
|
||||
this->is_playing_ &= !this->ack_reset_is_playing_;
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
break;
|
||||
case 0x3C:
|
||||
ESP_LOGV(TAG, "Playback finished (USB drive)");
|
||||
this->is_playing_ = false;
|
||||
this->on_finished_playback_callback_.call();
|
||||
case 0x3D:
|
||||
ESP_LOGV(TAG, "Playback finished (SD card)");
|
||||
this->is_playing_ = false;
|
||||
this->on_finished_playback_callback_.call();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument);
|
||||
}
|
||||
this->sent_cmd_ = 0;
|
||||
this->read_pos_ = 0;
|
||||
continue;
|
||||
}
|
||||
this->read_buffer_[this->read_pos_] = byte;
|
||||
this->read_pos_++;
|
||||
}
|
||||
this->read_buffer_[this->read_pos_] = byte;
|
||||
this->read_pos_++;
|
||||
}
|
||||
}
|
||||
void DFPlayer::dump_config() {
|
||||
|
||||
@@ -28,15 +28,28 @@ void DlmsMeterComponent::dump_config() {
|
||||
|
||||
void DlmsMeterComponent::loop() {
|
||||
// Read while data is available, netznoe uses two frames so allow 2x max frame length
|
||||
while (this->available()) {
|
||||
if (this->receive_buffer_.size() >= MBUS_MAX_FRAME_LENGTH * 2) {
|
||||
int avail = this->available();
|
||||
if (avail > 0) {
|
||||
size_t remaining = MBUS_MAX_FRAME_LENGTH * 2 - this->receive_buffer_.size();
|
||||
if (remaining == 0) {
|
||||
ESP_LOGW(TAG, "Receive buffer full, dropping remaining bytes");
|
||||
break;
|
||||
} else {
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
// Cap reads to remaining buffer capacity.
|
||||
if (static_cast<size_t>(avail) > remaining) {
|
||||
avail = remaining;
|
||||
}
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
this->receive_buffer_.insert(this->receive_buffer_.end(), buf, buf + to_read);
|
||||
this->last_read_ = millis();
|
||||
}
|
||||
}
|
||||
uint8_t c;
|
||||
this->read_byte(&c);
|
||||
this->receive_buffer_.push_back(c);
|
||||
this->last_read_ = millis();
|
||||
}
|
||||
|
||||
if (!this->receive_buffer_.empty() && millis() - this->last_read_ > this->read_timeout_) {
|
||||
|
||||
@@ -40,9 +40,7 @@ bool Dsmr::ready_to_request_data_() {
|
||||
this->start_requesting_data_();
|
||||
}
|
||||
if (!this->requesting_data_) {
|
||||
while (this->available()) {
|
||||
this->read();
|
||||
}
|
||||
this->drain_rx_buffer_();
|
||||
}
|
||||
}
|
||||
return this->requesting_data_;
|
||||
@@ -115,13 +113,21 @@ void Dsmr::stop_requesting_data_() {
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Stop reading data from P1 port");
|
||||
}
|
||||
while (this->available()) {
|
||||
this->read();
|
||||
}
|
||||
this->drain_rx_buffer_();
|
||||
this->requesting_data_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::drain_rx_buffer_() {
|
||||
uint8_t buf[64];
|
||||
int avail;
|
||||
while ((avail = this->available()) > 0) {
|
||||
if (!this->read_array(buf, std::min(static_cast<size_t>(avail), sizeof(buf)))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::reset_telegram_() {
|
||||
this->header_found_ = false;
|
||||
this->footer_found_ = false;
|
||||
@@ -133,120 +139,144 @@ void Dsmr::reset_telegram_() {
|
||||
|
||||
void Dsmr::receive_telegram_() {
|
||||
while (this->available_within_timeout_()) {
|
||||
const char c = this->read();
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
int avail = this->available();
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read))
|
||||
return;
|
||||
avail -= to_read;
|
||||
|
||||
// Find a new telegram header, i.e. forward slash.
|
||||
if (c == '/') {
|
||||
ESP_LOGV(TAG, "Header of telegram found");
|
||||
this->reset_telegram_();
|
||||
this->header_found_ = true;
|
||||
}
|
||||
if (!this->header_found_)
|
||||
continue;
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
const char c = static_cast<char>(buf[i]);
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (this->bytes_read_ >= this->max_telegram_len_) {
|
||||
this->reset_telegram_();
|
||||
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
||||
return;
|
||||
}
|
||||
// Find a new telegram header, i.e. forward slash.
|
||||
if (c == '/') {
|
||||
ESP_LOGV(TAG, "Header of telegram found");
|
||||
this->reset_telegram_();
|
||||
this->header_found_ = true;
|
||||
}
|
||||
if (!this->header_found_)
|
||||
continue;
|
||||
|
||||
// Some v2.2 or v3 meters will send a new value which starts with '('
|
||||
// in a new line, while the value belongs to the previous ObisId. For
|
||||
// proper parsing, remove these new line characters.
|
||||
if (c == '(') {
|
||||
while (true) {
|
||||
auto previous_char = this->telegram_[this->bytes_read_ - 1];
|
||||
if (previous_char == '\n' || previous_char == '\r') {
|
||||
this->bytes_read_--;
|
||||
} else {
|
||||
break;
|
||||
// Check for buffer overflow.
|
||||
if (this->bytes_read_ >= this->max_telegram_len_) {
|
||||
this->reset_telegram_();
|
||||
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Some v2.2 or v3 meters will send a new value which starts with '('
|
||||
// in a new line, while the value belongs to the previous ObisId. For
|
||||
// proper parsing, remove these new line characters.
|
||||
if (c == '(') {
|
||||
while (true) {
|
||||
auto previous_char = this->telegram_[this->bytes_read_ - 1];
|
||||
if (previous_char == '\n' || previous_char == '\r') {
|
||||
this->bytes_read_--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the byte in the buffer.
|
||||
this->telegram_[this->bytes_read_] = c;
|
||||
this->bytes_read_++;
|
||||
|
||||
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
|
||||
if (c == '!') {
|
||||
ESP_LOGV(TAG, "Footer of telegram found");
|
||||
this->footer_found_ = true;
|
||||
continue;
|
||||
}
|
||||
// Check for the end of the hex checksum, i.e. a newline.
|
||||
if (this->footer_found_ && c == '\n') {
|
||||
// Parse the telegram and publish sensor values.
|
||||
this->parse_telegram();
|
||||
this->reset_telegram_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the byte in the buffer.
|
||||
this->telegram_[this->bytes_read_] = c;
|
||||
this->bytes_read_++;
|
||||
|
||||
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
|
||||
if (c == '!') {
|
||||
ESP_LOGV(TAG, "Footer of telegram found");
|
||||
this->footer_found_ = true;
|
||||
continue;
|
||||
}
|
||||
// Check for the end of the hex checksum, i.e. a newline.
|
||||
if (this->footer_found_ && c == '\n') {
|
||||
// Parse the telegram and publish sensor values.
|
||||
this->parse_telegram();
|
||||
this->reset_telegram_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::receive_encrypted_telegram_() {
|
||||
while (this->available_within_timeout_()) {
|
||||
const char c = this->read();
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
int avail = this->available();
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read))
|
||||
return;
|
||||
avail -= to_read;
|
||||
|
||||
// Find a new telegram start byte.
|
||||
if (!this->header_found_) {
|
||||
if ((uint8_t) c != 0xDB) {
|
||||
continue;
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
const char c = static_cast<char>(buf[i]);
|
||||
|
||||
// Find a new telegram start byte.
|
||||
if (!this->header_found_) {
|
||||
if ((uint8_t) c != 0xDB) {
|
||||
continue;
|
||||
}
|
||||
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
|
||||
this->reset_telegram_();
|
||||
this->header_found_ = true;
|
||||
}
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
|
||||
this->reset_telegram_();
|
||||
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the byte in the buffer.
|
||||
this->crypt_telegram_[this->crypt_bytes_read_] = c;
|
||||
this->crypt_bytes_read_++;
|
||||
|
||||
// Read the length of the incoming encrypted telegram.
|
||||
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
|
||||
// Complete header + data bytes
|
||||
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
|
||||
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
|
||||
}
|
||||
|
||||
// Check for the end of the encrypted telegram.
|
||||
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
|
||||
continue;
|
||||
}
|
||||
ESP_LOGV(TAG, "End of encrypted telegram found");
|
||||
|
||||
// Decrypt the encrypted telegram.
|
||||
GCM<AES128> *gcmaes128{new GCM<AES128>()};
|
||||
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
|
||||
// the iv is 8 bytes of the system title + 4 bytes frame counter
|
||||
// system title is at byte 2 and frame counter at byte 15
|
||||
for (int i = 10; i < 14; i++)
|
||||
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
|
||||
constexpr uint16_t iv_size{12};
|
||||
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
|
||||
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
|
||||
// the ciphertext start at byte 18
|
||||
&this->crypt_telegram_[18],
|
||||
// cipher size
|
||||
this->crypt_bytes_read_ - 17);
|
||||
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
|
||||
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
|
||||
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
|
||||
|
||||
// Parse the decrypted telegram and publish sensor values.
|
||||
this->parse_telegram();
|
||||
this->reset_telegram_();
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
|
||||
this->reset_telegram_();
|
||||
this->header_found_ = true;
|
||||
}
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
|
||||
this->reset_telegram_();
|
||||
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the byte in the buffer.
|
||||
this->crypt_telegram_[this->crypt_bytes_read_] = c;
|
||||
this->crypt_bytes_read_++;
|
||||
|
||||
// Read the length of the incoming encrypted telegram.
|
||||
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
|
||||
// Complete header + data bytes
|
||||
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
|
||||
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
|
||||
}
|
||||
|
||||
// Check for the end of the encrypted telegram.
|
||||
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
|
||||
continue;
|
||||
}
|
||||
ESP_LOGV(TAG, "End of encrypted telegram found");
|
||||
|
||||
// Decrypt the encrypted telegram.
|
||||
GCM<AES128> *gcmaes128{new GCM<AES128>()};
|
||||
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
|
||||
// the iv is 8 bytes of the system title + 4 bytes frame counter
|
||||
// system title is at byte 2 and frame counter at byte 15
|
||||
for (int i = 10; i < 14; i++)
|
||||
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
|
||||
constexpr uint16_t iv_size{12};
|
||||
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
|
||||
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
|
||||
// the ciphertext start at byte 18
|
||||
&this->crypt_telegram_[18],
|
||||
// cipher size
|
||||
this->crypt_bytes_read_ - 17);
|
||||
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
|
||||
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
|
||||
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
|
||||
|
||||
// Parse the decrypted telegram and publish sensor values.
|
||||
this->parse_telegram();
|
||||
this->reset_telegram_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ class Dsmr : public Component, public uart::UARTDevice {
|
||||
void receive_telegram_();
|
||||
void receive_encrypted_telegram_();
|
||||
void reset_telegram_();
|
||||
void drain_rx_buffer_();
|
||||
|
||||
/// Wait for UART data to become available within the read timeout.
|
||||
///
|
||||
|
||||
@@ -275,8 +275,10 @@ void LD2410Component::restart_and_read_all_info() {
|
||||
}
|
||||
|
||||
void LD2410Component::loop() {
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail == 0) {
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -310,8 +310,10 @@ void LD2412Component::restart_and_read_all_info() {
|
||||
}
|
||||
|
||||
void LD2412Component::loop() {
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail == 0) {
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -335,9 +335,10 @@ void LD2420Component::revert_config_action() {
|
||||
|
||||
void LD2420Component::loop() {
|
||||
// If there is a active send command do not process it here, the send command call will handle it.
|
||||
while (!this->cmd_active_ && this->available()) {
|
||||
this->readline_(this->read(), this->buffer_data_, MAX_LINE_LENGTH);
|
||||
if (this->cmd_active_) {
|
||||
return;
|
||||
}
|
||||
this->read_batch_(this->buffer_data_);
|
||||
}
|
||||
|
||||
void LD2420Component::update_radar_data(uint16_t const *gate_energy, uint8_t sample_number) {
|
||||
@@ -539,6 +540,29 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::read_batch_(std::span<uint8_t, MAX_LINE_LENGTH> buffer) {
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[MAX_LINE_LENGTH];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
this->readline_(buf[i], buffer.data(), buffer.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
|
||||
this->cmd_reply_.command = buffer[CMD_FRAME_COMMAND];
|
||||
this->cmd_reply_.length = buffer[CMD_FRAME_DATA_LENGTH];
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include <span>
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
@@ -165,6 +166,7 @@ class LD2420Component : public Component, public uart::UARTDevice {
|
||||
void handle_energy_mode_(uint8_t *buffer, int len);
|
||||
void handle_ack_data_(uint8_t *buffer, int len);
|
||||
void readline_(int rx_data, uint8_t *buffer, int len);
|
||||
void read_batch_(std::span<uint8_t, MAX_LINE_LENGTH> buffer);
|
||||
void set_calibration_(bool state) { this->calibration_ = state; };
|
||||
bool get_calibration_() { return this->calibration_; };
|
||||
|
||||
|
||||
@@ -276,8 +276,10 @@ void LD2450Component::dump_config() {
|
||||
}
|
||||
|
||||
void LD2450Component::loop() {
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail == 0) {
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,16 +192,15 @@ def _notify_old_style(config):
|
||||
|
||||
# The dev and latest branches will be at *least* this version, which is what matters.
|
||||
# Use GitHub releases directly to avoid PlatformIO moderation delays.
|
||||
# TODO: Revert to v1.12.0 tag once https://github.com/libretiny-eu/libretiny/pull/361 is released
|
||||
ARDUINO_VERSIONS = {
|
||||
"dev": (cv.Version(1, 12, 0), "https://github.com/libretiny-eu/libretiny.git"),
|
||||
"dev": (cv.Version(1, 12, 1), "https://github.com/libretiny-eu/libretiny.git"),
|
||||
"latest": (
|
||||
cv.Version(1, 12, 0),
|
||||
"https://github.com/bdraco/libretiny.git#7f52d41",
|
||||
cv.Version(1, 12, 1),
|
||||
"https://github.com/libretiny-eu/libretiny.git#v1.12.1",
|
||||
),
|
||||
"recommended": (
|
||||
cv.Version(1, 12, 0),
|
||||
"https://github.com/bdraco/libretiny.git#7f52d41",
|
||||
cv.Version(1, 12, 1),
|
||||
"https://github.com/libretiny-eu/libretiny.git#v1.12.1",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -19,16 +19,27 @@ void Modbus::setup() {
|
||||
void Modbus::loop() {
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
|
||||
while (this->available()) {
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
if (this->parse_modbus_byte_(byte)) {
|
||||
this->last_modbus_byte_ = now;
|
||||
} else {
|
||||
size_t at = this->rx_buffer_.size();
|
||||
if (at > 0) {
|
||||
ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at);
|
||||
this->rx_buffer_.clear();
|
||||
int avail = this->available();
|
||||
if (avail > 0) {
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
if (this->parse_modbus_byte_(buf[i])) {
|
||||
this->last_modbus_byte_ = now;
|
||||
} else {
|
||||
size_t at = this->rx_buffer_.size();
|
||||
if (at > 0) {
|
||||
ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at);
|
||||
this->rx_buffer_.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,11 +397,23 @@ bool Nextion::remove_from_q_(bool report_empty) {
|
||||
}
|
||||
|
||||
void Nextion::process_serial_() {
|
||||
uint8_t d;
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (this->available()) {
|
||||
read_byte(&d);
|
||||
this->command_data_ += d;
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
this->command_data_.append(reinterpret_cast<const char *>(buf), to_read);
|
||||
}
|
||||
}
|
||||
// nextion.tech/instruction-set/
|
||||
|
||||
@@ -13,9 +13,12 @@ void Pipsolar::setup() {
|
||||
}
|
||||
|
||||
void Pipsolar::empty_uart_buffer_() {
|
||||
uint8_t byte;
|
||||
while (this->available()) {
|
||||
this->read_byte(&byte);
|
||||
uint8_t buf[64];
|
||||
int avail;
|
||||
while ((avail = this->available()) > 0) {
|
||||
if (!this->read_array(buf, std::min(static_cast<size_t>(avail), sizeof(buf)))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,32 +97,47 @@ void Pipsolar::loop() {
|
||||
}
|
||||
|
||||
if (this->state_ == STATE_COMMAND || this->state_ == STATE_POLL) {
|
||||
while (this->available()) {
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
|
||||
// make sure data and null terminator fit in buffer
|
||||
if (this->read_pos_ >= PIPSOLAR_READ_BUFFER_LENGTH - 1) {
|
||||
this->read_pos_ = 0;
|
||||
this->empty_uart_buffer_();
|
||||
ESP_LOGW(TAG, "response data too long, discarding.");
|
||||
int avail = this->available();
|
||||
while (avail > 0) {
|
||||
uint8_t buf[64];
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
this->read_buffer_[this->read_pos_] = byte;
|
||||
this->read_pos_++;
|
||||
avail -= to_read;
|
||||
bool done = false;
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
uint8_t byte = buf[i];
|
||||
|
||||
// end of answer
|
||||
if (byte == 0x0D) {
|
||||
this->read_buffer_[this->read_pos_] = 0;
|
||||
this->empty_uart_buffer_();
|
||||
if (this->state_ == STATE_POLL) {
|
||||
this->state_ = STATE_POLL_COMPLETE;
|
||||
// make sure data and null terminator fit in buffer
|
||||
if (this->read_pos_ >= PIPSOLAR_READ_BUFFER_LENGTH - 1) {
|
||||
this->read_pos_ = 0;
|
||||
this->empty_uart_buffer_();
|
||||
ESP_LOGW(TAG, "response data too long, discarding.");
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
if (this->state_ == STATE_COMMAND) {
|
||||
this->state_ = STATE_COMMAND_COMPLETE;
|
||||
this->read_buffer_[this->read_pos_] = byte;
|
||||
this->read_pos_++;
|
||||
|
||||
// end of answer
|
||||
if (byte == 0x0D) {
|
||||
this->read_buffer_[this->read_pos_] = 0;
|
||||
this->empty_uart_buffer_();
|
||||
if (this->state_ == STATE_POLL) {
|
||||
this->state_ = STATE_POLL_COMPLETE;
|
||||
}
|
||||
if (this->state_ == STATE_COMMAND) {
|
||||
this->state_ = STATE_COMMAND_COMPLETE;
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // available
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this->state_ == STATE_COMMAND) {
|
||||
if (millis() - this->command_start_millis_ > esphome::pipsolar::Pipsolar::COMMAND_TIMEOUT) {
|
||||
|
||||
@@ -56,17 +56,23 @@ void PylontechComponent::setup() {
|
||||
void PylontechComponent::update() { this->write_str("pwr\n"); }
|
||||
|
||||
void PylontechComponent::loop() {
|
||||
if (this->available() > 0) {
|
||||
int avail = this->available();
|
||||
if (avail > 0) {
|
||||
// pylontech sends a lot of data very suddenly
|
||||
// we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow
|
||||
uint8_t data;
|
||||
int recv = 0;
|
||||
while (this->available() > 0) {
|
||||
if (this->read_byte(&data)) {
|
||||
buffer_[buffer_index_write_] += (char) data;
|
||||
recv++;
|
||||
if (buffer_[buffer_index_write_].back() == static_cast<char>(ASCII_LF) ||
|
||||
buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
recv += to_read;
|
||||
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
buffer_[buffer_index_write_] += (char) buf[i];
|
||||
if (buf[i] == ASCII_LF || buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
|
||||
// complete line received
|
||||
buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "rd03d.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cmath>
|
||||
|
||||
@@ -80,37 +81,51 @@ void RD03DComponent::dump_config() {
|
||||
}
|
||||
|
||||
void RD03DComponent::loop() {
|
||||
while (this->available()) {
|
||||
uint8_t byte = this->read();
|
||||
ESP_LOGVV(TAG, "Received byte: 0x%02X, buffer_pos: %d", byte, this->buffer_pos_);
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail <= 0)
|
||||
return;
|
||||
|
||||
// Check if we're looking for frame header
|
||||
if (this->buffer_pos_ < FRAME_HEADER_SIZE) {
|
||||
if (byte == FRAME_HEADER[this->buffer_pos_]) {
|
||||
this->buffer_[this->buffer_pos_++] = byte;
|
||||
} else if (byte == FRAME_HEADER[0]) {
|
||||
// Start over if we see a potential new header
|
||||
this->buffer_[0] = byte;
|
||||
this->buffer_pos_ = 1;
|
||||
} else {
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
uint8_t byte = buf[i];
|
||||
ESP_LOGVV(TAG, "Received byte: 0x%02X, buffer_pos: %d", byte, this->buffer_pos_);
|
||||
|
||||
// Check if we're looking for frame header
|
||||
if (this->buffer_pos_ < FRAME_HEADER_SIZE) {
|
||||
if (byte == FRAME_HEADER[this->buffer_pos_]) {
|
||||
this->buffer_[this->buffer_pos_++] = byte;
|
||||
} else if (byte == FRAME_HEADER[0]) {
|
||||
// Start over if we see a potential new header
|
||||
this->buffer_[0] = byte;
|
||||
this->buffer_pos_ = 1;
|
||||
} else {
|
||||
this->buffer_pos_ = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Accumulate data bytes
|
||||
this->buffer_[this->buffer_pos_++] = byte;
|
||||
|
||||
// Check if we have a complete frame
|
||||
if (this->buffer_pos_ == FRAME_SIZE) {
|
||||
// Validate footer
|
||||
if (this->buffer_[FRAME_SIZE - 2] == FRAME_FOOTER[0] && this->buffer_[FRAME_SIZE - 1] == FRAME_FOOTER[1]) {
|
||||
this->process_frame_();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid frame footer: 0x%02X 0x%02X (expected 0x55 0xCC)", this->buffer_[FRAME_SIZE - 2],
|
||||
this->buffer_[FRAME_SIZE - 1]);
|
||||
}
|
||||
this->buffer_pos_ = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Accumulate data bytes
|
||||
this->buffer_[this->buffer_pos_++] = byte;
|
||||
|
||||
// Check if we have a complete frame
|
||||
if (this->buffer_pos_ == FRAME_SIZE) {
|
||||
// Validate footer
|
||||
if (this->buffer_[FRAME_SIZE - 2] == FRAME_FOOTER[0] && this->buffer_[FRAME_SIZE - 1] == FRAME_FOOTER[1]) {
|
||||
this->process_frame_();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid frame footer: 0x%02X 0x%02X (expected 0x55 0xCC)", this->buffer_[FRAME_SIZE - 2],
|
||||
this->buffer_[FRAME_SIZE - 1]);
|
||||
}
|
||||
this->buffer_pos_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,14 +136,21 @@ void RFBridgeComponent::loop() {
|
||||
this->last_bridge_byte_ = now;
|
||||
}
|
||||
|
||||
while (this->available()) {
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
if (this->parse_bridge_byte_(byte)) {
|
||||
ESP_LOGVV(TAG, "Parsed: 0x%02X", byte);
|
||||
this->last_bridge_byte_ = now;
|
||||
} else {
|
||||
this->rx_buffer_.clear();
|
||||
int avail = this->available();
|
||||
while (avail > 0) {
|
||||
uint8_t buf[64];
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
if (this->parse_bridge_byte_(buf[i])) {
|
||||
ESP_LOGVV(TAG, "Parsed: 0x%02X", buf[i]);
|
||||
this->last_bridge_byte_ = now;
|
||||
} else {
|
||||
this->rx_buffer_.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,12 +106,21 @@ void MR24HPC1Component::update_() {
|
||||
|
||||
// main loop
|
||||
void MR24HPC1Component::loop() {
|
||||
uint8_t byte;
|
||||
int avail = this->available();
|
||||
if (avail > 0) {
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
// Is there data on the serial port
|
||||
while (this->available()) {
|
||||
this->read_byte(&byte);
|
||||
this->r24_split_data_frame_(byte); // split data frame
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
this->r24_split_data_frame_(buf[i]); // split data frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((this->s_output_info_switch_flag_ == OUTPUT_SWTICH_OFF) &&
|
||||
|
||||
@@ -30,14 +30,27 @@ void MR60BHA2Component::dump_config() {
|
||||
|
||||
// main loop
|
||||
void MR60BHA2Component::loop() {
|
||||
uint8_t byte;
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is there data on the serial port
|
||||
while (this->available()) {
|
||||
this->read_byte(&byte);
|
||||
this->rx_message_.push_back(byte);
|
||||
if (!this->validate_message_()) {
|
||||
this->rx_message_.clear();
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
this->rx_message_.push_back(buf[i]);
|
||||
if (!this->validate_message_()) {
|
||||
this->rx_message_.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,25 @@ void MR60FDA2Component::setup() {
|
||||
|
||||
// main loop
|
||||
void MR60FDA2Component::loop() {
|
||||
uint8_t byte;
|
||||
// All current UART available() implementations return >= 0,
|
||||
// use <= 0 to future-proof against any that may return negative on error.
|
||||
int avail = this->available();
|
||||
if (avail <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is there data on the serial port
|
||||
while (this->available()) {
|
||||
this->read_byte(&byte);
|
||||
this->split_frame_(byte); // split data frame
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
this->split_frame_(buf[i]); // split data frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,21 @@ void Tuya::setup() {
|
||||
}
|
||||
|
||||
void Tuya::loop() {
|
||||
while (this->available()) {
|
||||
uint8_t c;
|
||||
this->read_byte(&c);
|
||||
this->handle_char_(c);
|
||||
int avail = this->available();
|
||||
if (avail > 0) {
|
||||
// Read all available bytes in batches to reduce UART call overhead.
|
||||
uint8_t buf[64];
|
||||
while (avail > 0) {
|
||||
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
||||
if (!this->read_array(buf, to_read)) {
|
||||
break;
|
||||
}
|
||||
avail -= to_read;
|
||||
|
||||
for (size_t i = 0; i < to_read; i++) {
|
||||
this->handle_char_(buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
process_command_queue_();
|
||||
}
|
||||
|
||||
@@ -213,8 +213,7 @@ build_unflags =
|
||||
; This are common settings for the LibreTiny (all variants) using Arduino.
|
||||
[common:libretiny-arduino]
|
||||
extends = common:arduino
|
||||
# TODO: Revert to v1.12.0 tag once https://github.com/libretiny-eu/libretiny/pull/361 is released
|
||||
platform = https://github.com/bdraco/libretiny.git#7f52d41
|
||||
platform = https://github.com/libretiny-eu/libretiny.git#v1.12.1
|
||||
framework = arduino
|
||||
lib_compat_mode = soft
|
||||
lib_deps =
|
||||
|
||||
@@ -2881,9 +2881,78 @@ static const char *const TAG = "api.service";
|
||||
|
||||
cases = list(RECEIVE_CASES.items())
|
||||
cases.sort()
|
||||
|
||||
serv = file.service[0]
|
||||
|
||||
# Build a mapping of message input types to their authentication requirements
|
||||
message_auth_map: dict[str, bool] = {}
|
||||
message_conn_map: dict[str, bool] = {}
|
||||
|
||||
for m in serv.method:
|
||||
inp = m.input_type[1:]
|
||||
needs_conn = get_opt(m, pb.needs_setup_connection, True)
|
||||
needs_auth = get_opt(m, pb.needs_authentication, True)
|
||||
|
||||
# Store authentication requirements for message types
|
||||
message_auth_map[inp] = needs_auth
|
||||
message_conn_map[inp] = needs_conn
|
||||
|
||||
# Categorize messages by their authentication requirements
|
||||
no_conn_ids: set[int] = set()
|
||||
conn_only_ids: set[int] = set()
|
||||
|
||||
for id_, (_, _, case_msg_name) in cases:
|
||||
if case_msg_name in message_auth_map:
|
||||
needs_auth = message_auth_map[case_msg_name]
|
||||
needs_conn = message_conn_map[case_msg_name]
|
||||
|
||||
if not needs_conn:
|
||||
no_conn_ids.add(id_)
|
||||
elif not needs_auth:
|
||||
conn_only_ids.add(id_)
|
||||
|
||||
# Helper to generate case statements with ifdefs
|
||||
def generate_cases(ids: set[int], comment: str) -> str:
|
||||
result = ""
|
||||
for id_ in sorted(ids):
|
||||
_, ifdef, msg_name = RECEIVE_CASES[id_]
|
||||
if ifdef:
|
||||
result += f"#ifdef {ifdef}\n"
|
||||
result += f" case {msg_name}::MESSAGE_TYPE: {comment}\n"
|
||||
if ifdef:
|
||||
result += "#endif\n"
|
||||
return result
|
||||
|
||||
# Generate read_message with auth check before dispatch
|
||||
hpp += " protected:\n"
|
||||
hpp += " void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;\n"
|
||||
|
||||
out = f"void {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {{\n"
|
||||
|
||||
# Auth check block before dispatch switch
|
||||
if no_conn_ids or conn_only_ids:
|
||||
out += " // Check authentication/connection requirements\n"
|
||||
out += " switch (msg_type) {\n"
|
||||
|
||||
if no_conn_ids:
|
||||
out += generate_cases(no_conn_ids, "// No setup required")
|
||||
out += " break;\n"
|
||||
|
||||
if conn_only_ids:
|
||||
out += generate_cases(conn_only_ids, "// Connection setup only")
|
||||
out += " if (!this->check_connection_setup_()) {\n"
|
||||
out += " return;\n"
|
||||
out += " }\n"
|
||||
out += " break;\n"
|
||||
|
||||
out += " default:\n"
|
||||
out += " if (!this->check_authenticated_()) {\n"
|
||||
out += " return;\n"
|
||||
out += " }\n"
|
||||
out += " break;\n"
|
||||
out += " }\n"
|
||||
|
||||
# Dispatch switch
|
||||
out += " switch (msg_type) {\n"
|
||||
for i, (case, ifdef, message_name) in cases:
|
||||
if ifdef is not None:
|
||||
@@ -2902,129 +2971,6 @@ static const char *const TAG = "api.service";
|
||||
cpp += out
|
||||
hpp += "};\n"
|
||||
|
||||
serv = file.service[0]
|
||||
class_name = "APIServerConnection"
|
||||
hpp += "\n"
|
||||
hpp += f"class {class_name} : public {class_name}Base {{\n"
|
||||
hpp += " public:\n"
|
||||
hpp_protected = ""
|
||||
cpp += "\n"
|
||||
|
||||
# Build a mapping of message input types to their authentication requirements
|
||||
message_auth_map: dict[str, bool] = {}
|
||||
message_conn_map: dict[str, bool] = {}
|
||||
|
||||
m = serv.method[0]
|
||||
for m in serv.method:
|
||||
func = m.name
|
||||
inp = m.input_type[1:]
|
||||
ret = m.output_type[1:]
|
||||
is_void = ret == "void"
|
||||
snake = camel_to_snake(inp)
|
||||
on_func = f"on_{snake}"
|
||||
needs_conn = get_opt(m, pb.needs_setup_connection, True)
|
||||
needs_auth = get_opt(m, pb.needs_authentication, True)
|
||||
|
||||
# Store authentication requirements for message types
|
||||
message_auth_map[inp] = needs_auth
|
||||
message_conn_map[inp] = needs_conn
|
||||
|
||||
ifdef = message_ifdef_map.get(inp, ifdefs.get(inp))
|
||||
|
||||
if ifdef is not None:
|
||||
hpp += f"#ifdef {ifdef}\n"
|
||||
hpp_protected += f"#ifdef {ifdef}\n"
|
||||
cpp += f"#ifdef {ifdef}\n"
|
||||
|
||||
is_empty = inp in EMPTY_MESSAGES
|
||||
param = "" if is_empty else f"const {inp} &msg"
|
||||
arg = "" if is_empty else "msg"
|
||||
|
||||
hpp_protected += f" void {on_func}({param}) override;\n"
|
||||
if is_void:
|
||||
hpp += f" virtual void {func}({param}) = 0;\n"
|
||||
else:
|
||||
hpp += f" virtual bool send_{func}_response({param}) = 0;\n"
|
||||
|
||||
cpp += f"void {class_name}::{on_func}({param}) {{\n"
|
||||
body = ""
|
||||
if is_void:
|
||||
body += f"this->{func}({arg});\n"
|
||||
else:
|
||||
body += f"if (!this->send_{func}_response({arg})) {{\n"
|
||||
body += " this->on_fatal_error();\n"
|
||||
body += "}\n"
|
||||
|
||||
cpp += indent(body) + "\n" + "}\n"
|
||||
|
||||
if ifdef is not None:
|
||||
hpp += "#endif\n"
|
||||
hpp_protected += "#endif\n"
|
||||
cpp += "#endif\n"
|
||||
|
||||
# Generate optimized read_message with authentication checking
|
||||
# Categorize messages by their authentication requirements
|
||||
no_conn_ids: set[int] = set()
|
||||
conn_only_ids: set[int] = set()
|
||||
|
||||
for id_, (_, _, case_msg_name) in cases:
|
||||
if case_msg_name in message_auth_map:
|
||||
needs_auth = message_auth_map[case_msg_name]
|
||||
needs_conn = message_conn_map[case_msg_name]
|
||||
|
||||
if not needs_conn:
|
||||
no_conn_ids.add(id_)
|
||||
elif not needs_auth:
|
||||
conn_only_ids.add(id_)
|
||||
|
||||
# Generate override if we have messages that skip checks
|
||||
if no_conn_ids or conn_only_ids:
|
||||
# Helper to generate case statements with ifdefs
|
||||
def generate_cases(ids: set[int], comment: str) -> str:
|
||||
result = ""
|
||||
for id_ in sorted(ids):
|
||||
_, ifdef, msg_name = RECEIVE_CASES[id_]
|
||||
if ifdef:
|
||||
result += f"#ifdef {ifdef}\n"
|
||||
result += f" case {msg_name}::MESSAGE_TYPE: {comment}\n"
|
||||
if ifdef:
|
||||
result += "#endif\n"
|
||||
return result
|
||||
|
||||
hpp_protected += " void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;\n"
|
||||
|
||||
cpp += f"\nvoid {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {{\n"
|
||||
cpp += " // Check authentication/connection requirements for messages\n"
|
||||
cpp += " switch (msg_type) {\n"
|
||||
|
||||
# Messages that don't need any checks
|
||||
if no_conn_ids:
|
||||
cpp += generate_cases(no_conn_ids, "// No setup required")
|
||||
cpp += " break; // Skip all checks for these messages\n"
|
||||
|
||||
# Messages that only need connection setup
|
||||
if conn_only_ids:
|
||||
cpp += generate_cases(conn_only_ids, "// Connection setup only")
|
||||
cpp += " if (!this->check_connection_setup_()) {\n"
|
||||
cpp += " return; // Connection not setup\n"
|
||||
cpp += " }\n"
|
||||
cpp += " break;\n"
|
||||
|
||||
cpp += " default:\n"
|
||||
cpp += " // All other messages require authentication (which includes connection check)\n"
|
||||
cpp += " if (!this->check_authenticated_()) {\n"
|
||||
cpp += " return; // Authentication failed\n"
|
||||
cpp += " }\n"
|
||||
cpp += " break;\n"
|
||||
cpp += " }\n\n"
|
||||
cpp += " // Call base implementation to process the message\n"
|
||||
cpp += f" {class_name}Base::read_message(msg_size, msg_type, msg_data);\n"
|
||||
cpp += "}\n"
|
||||
|
||||
hpp += " protected:\n"
|
||||
hpp += hpp_protected
|
||||
hpp += "};\n"
|
||||
|
||||
hpp += """\
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
Reference in New Issue
Block a user