mirror of
https://github.com/esphome/esphome.git
synced 2026-02-10 17:51:53 +00:00
Compare commits
16 Commits
compact_st
...
json_web_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
840ad30880 | ||
|
|
cfe121b38b | ||
|
|
5fbd9d5b14 | ||
|
|
2b1783ce61 | ||
|
|
904072ce79 | ||
|
|
0a4b98d74a | ||
|
|
b8017de724 | ||
|
|
ca96604582 | ||
|
|
d18d378f06 | ||
|
|
83e3752544 | ||
|
|
0490b2d450 | ||
|
|
55ff740e4e | ||
|
|
aba8a83cba | ||
|
|
a23809d5db | ||
|
|
32fc3ea6f5 | ||
|
|
deb8ffd348 |
@@ -1 +1 @@
|
|||||||
37ec8d5a343c8d0a485fd2118cbdabcbccd7b9bca197e4a392be75087974dced
|
cf3d341206b4184ec8b7fe85141aef4fe4696aa720c3f8a06d4e57930574bdab
|
||||||
|
|||||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
|
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
build-mode: ${{ matrix.build-mode }}
|
build-mode: ${{ matrix.build-mode }}
|
||||||
@@ -86,6 +86,6 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
|
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ ci:
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.15.0
|
rev: v0.14.14
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ esphome/components/dfplayer/* @glmnet
|
|||||||
esphome/components/dfrobot_sen0395/* @niklasweber
|
esphome/components/dfrobot_sen0395/* @niklasweber
|
||||||
esphome/components/dht/* @OttoWinter
|
esphome/components/dht/* @OttoWinter
|
||||||
esphome/components/display_menu_base/* @numo68
|
esphome/components/display_menu_base/* @numo68
|
||||||
esphome/components/dlms_meter/* @SimonFischer04
|
|
||||||
esphome/components/dps310/* @kbx81
|
esphome/components/dps310/* @kbx81
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
esphome/components/ds2484/* @mrk-its
|
esphome/components/ds2484/* @mrk-its
|
||||||
@@ -532,7 +531,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
|
|||||||
esphome/components/udp/* @clydebarrow
|
esphome/components/udp/* @clydebarrow
|
||||||
esphome/components/ufire_ec/* @pvizeli
|
esphome/components/ufire_ec/* @pvizeli
|
||||||
esphome/components/ufire_ise/* @pvizeli
|
esphome/components/ufire_ise/* @pvizeli
|
||||||
esphome/components/ultrasonic/* @ssieb @swoboda1337
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
esphome/components/update/* @jesserockz
|
esphome/components/update/* @jesserockz
|
||||||
esphome/components/uponor_smatrix/* @kroimon
|
esphome/components/uponor_smatrix/* @kroimon
|
||||||
esphome/components/usb_cdc_acm/* @kbx81
|
esphome/components/usb_cdc_acm/* @kbx81
|
||||||
|
|||||||
@@ -294,13 +294,8 @@ def has_api() -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def has_ota() -> bool:
|
def has_ota() -> bool:
|
||||||
"""Check if OTA upload is available (requires platform: esphome)."""
|
"""Check if OTA is available."""
|
||||||
if CONF_OTA not in CORE.config:
|
return CONF_OTA in CORE.config
|
||||||
return False
|
|
||||||
return any(
|
|
||||||
ota_item.get(CONF_PLATFORM) == CONF_ESPHOME
|
|
||||||
for ota_item in CORE.config[CONF_OTA]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def has_mqtt_ip_lookup() -> bool:
|
def has_mqtt_ip_lookup() -> bool:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from .const import (
|
|||||||
CORE_SUBCATEGORY_PATTERNS,
|
CORE_SUBCATEGORY_PATTERNS,
|
||||||
DEMANGLED_PATTERNS,
|
DEMANGLED_PATTERNS,
|
||||||
ESPHOME_COMPONENT_PATTERN,
|
ESPHOME_COMPONENT_PATTERN,
|
||||||
|
SECTION_TO_ATTR,
|
||||||
SYMBOL_PATTERNS,
|
SYMBOL_PATTERNS,
|
||||||
)
|
)
|
||||||
from .demangle import batch_demangle
|
from .demangle import batch_demangle
|
||||||
@@ -90,17 +91,6 @@ class ComponentMemory:
|
|||||||
bss_size: int = 0 # Uninitialized data (ram only)
|
bss_size: int = 0 # Uninitialized data (ram only)
|
||||||
symbol_count: int = 0
|
symbol_count: int = 0
|
||||||
|
|
||||||
def add_section_size(self, section_name: str, size: int) -> None:
|
|
||||||
"""Add size to the appropriate attribute for a section."""
|
|
||||||
if section_name == ".text":
|
|
||||||
self.text_size += size
|
|
||||||
elif section_name == ".rodata":
|
|
||||||
self.rodata_size += size
|
|
||||||
elif section_name == ".data":
|
|
||||||
self.data_size += size
|
|
||||||
elif section_name == ".bss":
|
|
||||||
self.bss_size += size
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def flash_total(self) -> int:
|
def flash_total(self) -> int:
|
||||||
"""Total flash usage (text + rodata + data)."""
|
"""Total flash usage (text + rodata + data)."""
|
||||||
@@ -177,15 +167,12 @@ class MemoryAnalyzer:
|
|||||||
self._elf_symbol_names: set[str] = set()
|
self._elf_symbol_names: set[str] = set()
|
||||||
# SDK symbols not in ELF (static/local symbols from closed-source libs)
|
# SDK symbols not in ELF (static/local symbols from closed-source libs)
|
||||||
self._sdk_symbols: list[SDKSymbol] = []
|
self._sdk_symbols: list[SDKSymbol] = []
|
||||||
# CSWTCH symbols: list of (name, size, source_file, component)
|
|
||||||
self._cswtch_symbols: list[tuple[str, int, str, str]] = []
|
|
||||||
|
|
||||||
def analyze(self) -> dict[str, ComponentMemory]:
|
def analyze(self) -> dict[str, ComponentMemory]:
|
||||||
"""Analyze the ELF file and return component memory usage."""
|
"""Analyze the ELF file and return component memory usage."""
|
||||||
self._parse_sections()
|
self._parse_sections()
|
||||||
self._parse_symbols()
|
self._parse_symbols()
|
||||||
self._categorize_symbols()
|
self._categorize_symbols()
|
||||||
self._analyze_cswtch_symbols()
|
|
||||||
self._analyze_sdk_libraries()
|
self._analyze_sdk_libraries()
|
||||||
return dict(self.components)
|
return dict(self.components)
|
||||||
|
|
||||||
@@ -268,7 +255,8 @@ class MemoryAnalyzer:
|
|||||||
comp_mem.symbol_count += 1
|
comp_mem.symbol_count += 1
|
||||||
|
|
||||||
# Update the appropriate size attribute based on section
|
# Update the appropriate size attribute based on section
|
||||||
comp_mem.add_section_size(section_name, size)
|
if attr_name := SECTION_TO_ATTR.get(section_name):
|
||||||
|
setattr(comp_mem, attr_name, getattr(comp_mem, attr_name) + size)
|
||||||
|
|
||||||
# Track uncategorized symbols
|
# Track uncategorized symbols
|
||||||
if component == "other" and size > 0:
|
if component == "other" and size > 0:
|
||||||
@@ -384,277 +372,6 @@ class MemoryAnalyzer:
|
|||||||
|
|
||||||
return "Other Core"
|
return "Other Core"
|
||||||
|
|
||||||
def _find_object_files_dir(self) -> Path | None:
|
|
||||||
"""Find the directory containing object files for this build.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Path to the directory containing .o files, or None if not found.
|
|
||||||
"""
|
|
||||||
# The ELF is typically at .pioenvs/<env>/firmware.elf
|
|
||||||
# Object files are in .pioenvs/<env>/src/ and .pioenvs/<env>/lib*/
|
|
||||||
pioenvs_dir = self.elf_path.parent
|
|
||||||
if pioenvs_dir.exists() and any(pioenvs_dir.glob("src/*.o")):
|
|
||||||
return pioenvs_dir
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_nm_cswtch_output(
|
|
||||||
output: str,
|
|
||||||
base_dir: Path | None,
|
|
||||||
cswtch_map: dict[str, list[tuple[str, int]]],
|
|
||||||
) -> None:
|
|
||||||
"""Parse nm output for CSWTCH symbols and add to cswtch_map.
|
|
||||||
|
|
||||||
Handles both ``.o`` files and ``.a`` archives.
|
|
||||||
|
|
||||||
nm output formats::
|
|
||||||
|
|
||||||
.o files: /path/file.o:hex_addr hex_size type name
|
|
||||||
.a files: /path/lib.a:member.o:hex_addr hex_size type name
|
|
||||||
|
|
||||||
For ``.o`` files, paths are made relative to *base_dir* when possible.
|
|
||||||
For ``.a`` archives (detected by ``:`` in the file portion), paths are
|
|
||||||
formatted as ``archive_stem/member.o`` (e.g. ``liblwip2-536-feat/lwip-esp.o``).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output: Raw stdout from ``nm --print-file-name -S``.
|
|
||||||
base_dir: Base directory for computing relative paths of ``.o`` files.
|
|
||||||
Pass ``None`` when scanning archives outside the build tree.
|
|
||||||
cswtch_map: Dict to populate, mapping ``"CSWTCH$N:size"`` to source list.
|
|
||||||
"""
|
|
||||||
for line in output.splitlines():
|
|
||||||
if "CSWTCH$" not in line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Split on last ":" that precedes a hex address.
|
|
||||||
# For .o: "filepath.o" : "hex_addr hex_size type name"
|
|
||||||
# For .a: "filepath.a:member.o" : "hex_addr hex_size type name"
|
|
||||||
parts_after_colon = line.rsplit(":", 1)
|
|
||||||
if len(parts_after_colon) != 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
file_path = parts_after_colon[0]
|
|
||||||
fields = parts_after_colon[1].split()
|
|
||||||
# fields: [address, size, type, name]
|
|
||||||
if len(fields) < 4:
|
|
||||||
continue
|
|
||||||
|
|
||||||
sym_name = fields[3]
|
|
||||||
if not sym_name.startswith("CSWTCH$"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
size = int(fields[1], 16)
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Determine readable source path
|
|
||||||
# Use ".a:" to detect archive format (not bare ":" which matches
|
|
||||||
# Windows drive letters like "C:\...\file.o").
|
|
||||||
if ".a:" in file_path:
|
|
||||||
# Archive format: "archive.a:member.o" → "archive_stem/member.o"
|
|
||||||
archive_part, member = file_path.rsplit(":", 1)
|
|
||||||
archive_name = Path(archive_part).stem
|
|
||||||
rel_path = f"{archive_name}/{member}"
|
|
||||||
elif base_dir is not None:
|
|
||||||
try:
|
|
||||||
rel_path = str(Path(file_path).relative_to(base_dir))
|
|
||||||
except ValueError:
|
|
||||||
rel_path = file_path
|
|
||||||
else:
|
|
||||||
rel_path = file_path
|
|
||||||
|
|
||||||
key = f"{sym_name}:{size}"
|
|
||||||
cswtch_map[key].append((rel_path, size))
|
|
||||||
|
|
||||||
def _run_nm_cswtch_scan(
|
|
||||||
self,
|
|
||||||
files: list[Path],
|
|
||||||
base_dir: Path | None,
|
|
||||||
cswtch_map: dict[str, list[tuple[str, int]]],
|
|
||||||
) -> None:
|
|
||||||
"""Run nm on *files* and add any CSWTCH symbols to *cswtch_map*.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
files: Object (``.o``) or archive (``.a``) files to scan.
|
|
||||||
base_dir: Base directory for relative path computation (see
|
|
||||||
:meth:`_parse_nm_cswtch_output`).
|
|
||||||
cswtch_map: Dict to populate with results.
|
|
||||||
"""
|
|
||||||
if not self.nm_path or not files:
|
|
||||||
return
|
|
||||||
|
|
||||||
_LOGGER.debug("Scanning %d files for CSWTCH symbols", len(files))
|
|
||||||
|
|
||||||
result = run_tool(
|
|
||||||
[self.nm_path, "--print-file-name", "-S"] + [str(f) for f in files],
|
|
||||||
timeout=30,
|
|
||||||
)
|
|
||||||
if result is None or result.returncode != 0:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"nm failed or timed out scanning %d files for CSWTCH symbols",
|
|
||||||
len(files),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
self._parse_nm_cswtch_output(result.stdout, base_dir, cswtch_map)
|
|
||||||
|
|
||||||
def _scan_cswtch_in_sdk_archives(
|
|
||||||
self, cswtch_map: dict[str, list[tuple[str, int]]]
|
|
||||||
) -> None:
|
|
||||||
"""Scan SDK library archives (.a) for CSWTCH symbols.
|
|
||||||
|
|
||||||
Prebuilt SDK libraries (e.g. lwip, bearssl) are not compiled from source,
|
|
||||||
so their CSWTCH symbols only exist inside ``.a`` archives. Results are
|
|
||||||
merged into *cswtch_map* for keys not already found in ``.o`` files.
|
|
||||||
|
|
||||||
The same source file (e.g. ``lwip-esp.o``) often appears in multiple
|
|
||||||
library variants (``liblwip2-536.a``, ``liblwip2-1460-feat.a``, etc.),
|
|
||||||
so results are deduplicated by member name.
|
|
||||||
"""
|
|
||||||
sdk_dirs = self._find_sdk_library_dirs()
|
|
||||||
if not sdk_dirs:
|
|
||||||
return
|
|
||||||
|
|
||||||
sdk_archives = sorted(a for sdk_dir in sdk_dirs for a in sdk_dir.glob("*.a"))
|
|
||||||
|
|
||||||
sdk_map: dict[str, list[tuple[str, int]]] = defaultdict(list)
|
|
||||||
self._run_nm_cswtch_scan(sdk_archives, None, sdk_map)
|
|
||||||
|
|
||||||
# Merge SDK results, deduplicating by member name.
|
|
||||||
for key, sources in sdk_map.items():
|
|
||||||
if key in cswtch_map:
|
|
||||||
continue
|
|
||||||
seen: dict[str, tuple[str, int]] = {}
|
|
||||||
for path, sz in sources:
|
|
||||||
member = Path(path).name
|
|
||||||
if member not in seen:
|
|
||||||
seen[member] = (path, sz)
|
|
||||||
cswtch_map[key] = list(seen.values())
|
|
||||||
|
|
||||||
def _source_file_to_component(self, source_file: str) -> str:
|
|
||||||
"""Map a source object file path to its component name.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
source_file: Relative path like 'src/esphome/components/wifi/wifi_component.cpp.o'
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Component name like '[esphome]wifi' or the source file if unknown.
|
|
||||||
"""
|
|
||||||
parts = Path(source_file).parts
|
|
||||||
|
|
||||||
# ESPHome component: src/esphome/components/<name>/...
|
|
||||||
if "components" in parts:
|
|
||||||
idx = parts.index("components")
|
|
||||||
if idx + 1 < len(parts):
|
|
||||||
component_name = parts[idx + 1]
|
|
||||||
if component_name in get_esphome_components():
|
|
||||||
return f"{_COMPONENT_PREFIX_ESPHOME}{component_name}"
|
|
||||||
if component_name in self.external_components:
|
|
||||||
return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}"
|
|
||||||
|
|
||||||
# ESPHome core: src/esphome/core/... or src/esphome/...
|
|
||||||
if "core" in parts and "esphome" in parts:
|
|
||||||
return _COMPONENT_CORE
|
|
||||||
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
|
|
||||||
return parts[0] if parts else source_file
|
|
||||||
|
|
||||||
def _analyze_cswtch_symbols(self) -> None:
|
|
||||||
"""Analyze CSWTCH (GCC switch table) symbols by tracing to source objects.
|
|
||||||
|
|
||||||
CSWTCH symbols are compiler-generated lookup tables for switch statements.
|
|
||||||
They are local symbols, so the same name can appear in different object files.
|
|
||||||
This method scans .o files and SDK archives to attribute them to their
|
|
||||||
source components.
|
|
||||||
"""
|
|
||||||
obj_dir = self._find_object_files_dir()
|
|
||||||
if obj_dir is None:
|
|
||||||
_LOGGER.debug("No object files directory found, skipping CSWTCH analysis")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Scan build-dir object files for CSWTCH symbols
|
|
||||||
cswtch_map: dict[str, list[tuple[str, int]]] = defaultdict(list)
|
|
||||||
self._run_nm_cswtch_scan(sorted(obj_dir.rglob("*.o")), obj_dir, cswtch_map)
|
|
||||||
|
|
||||||
# Also scan SDK library archives (.a) for CSWTCH symbols.
|
|
||||||
# Prebuilt SDK libraries (e.g. lwip, bearssl) are not compiled from source
|
|
||||||
# so their symbols only exist inside .a archives, not as loose .o files.
|
|
||||||
self._scan_cswtch_in_sdk_archives(cswtch_map)
|
|
||||||
|
|
||||||
if not cswtch_map:
|
|
||||||
_LOGGER.debug("No CSWTCH symbols found in object files or SDK archives")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Collect CSWTCH symbols from the ELF (already parsed in sections)
|
|
||||||
# Include section_name for re-attribution of component totals
|
|
||||||
elf_cswtch = [
|
|
||||||
(symbol_name, size, section_name)
|
|
||||||
for section_name, section in self.sections.items()
|
|
||||||
for symbol_name, size, _ in section.symbols
|
|
||||||
if symbol_name.startswith("CSWTCH$")
|
|
||||||
]
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Found %d CSWTCH symbols in ELF, %d unique in object files",
|
|
||||||
len(elf_cswtch),
|
|
||||||
len(cswtch_map),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Match ELF CSWTCH symbols to source files and re-attribute component totals.
|
|
||||||
# _categorize_symbols() already ran and put these into "other" since CSWTCH$
|
|
||||||
# names don't match any component pattern. We move the bytes to the correct
|
|
||||||
# component based on the object file mapping.
|
|
||||||
other_mem = self.components.get("other")
|
|
||||||
|
|
||||||
for sym_name, size, section_name in elf_cswtch:
|
|
||||||
key = f"{sym_name}:{size}"
|
|
||||||
sources = cswtch_map.get(key, [])
|
|
||||||
|
|
||||||
if len(sources) == 1:
|
|
||||||
source_file = sources[0][0]
|
|
||||||
component = self._source_file_to_component(source_file)
|
|
||||||
elif len(sources) > 1:
|
|
||||||
# Ambiguous - multiple object files have same CSWTCH name+size
|
|
||||||
source_file = "ambiguous"
|
|
||||||
component = "ambiguous"
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Ambiguous CSWTCH %s (%d B) found in %d files: %s",
|
|
||||||
sym_name,
|
|
||||||
size,
|
|
||||||
len(sources),
|
|
||||||
", ".join(src for src, _ in sources),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
source_file = "unknown"
|
|
||||||
component = "unknown"
|
|
||||||
|
|
||||||
self._cswtch_symbols.append((sym_name, size, source_file, component))
|
|
||||||
|
|
||||||
# Re-attribute from "other" to the correct component
|
|
||||||
if (
|
|
||||||
component not in ("other", "unknown", "ambiguous")
|
|
||||||
and other_mem is not None
|
|
||||||
):
|
|
||||||
other_mem.add_section_size(section_name, -size)
|
|
||||||
if component not in self.components:
|
|
||||||
self.components[component] = ComponentMemory(component)
|
|
||||||
self.components[component].add_section_size(section_name, size)
|
|
||||||
|
|
||||||
# Sort by size descending
|
|
||||||
self._cswtch_symbols.sort(key=lambda x: x[1], reverse=True)
|
|
||||||
|
|
||||||
total_size = sum(size for _, size, _, _ in self._cswtch_symbols)
|
|
||||||
_LOGGER.debug(
|
|
||||||
"CSWTCH analysis: %d symbols, %d bytes total",
|
|
||||||
len(self._cswtch_symbols),
|
|
||||||
total_size,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_unattributed_ram(self) -> tuple[int, int, int]:
|
def get_unattributed_ram(self) -> tuple[int, int, int]:
|
||||||
"""Get unattributed RAM sizes (SDK/framework overhead).
|
"""Get unattributed RAM sizes (SDK/framework overhead).
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
import heapq
|
|
||||||
from operator import itemgetter
|
|
||||||
import sys
|
import sys
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
@@ -31,10 +29,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
|||||||
)
|
)
|
||||||
# Lower threshold for RAM symbols (RAM is more constrained)
|
# Lower threshold for RAM symbols (RAM is more constrained)
|
||||||
RAM_SYMBOL_SIZE_THRESHOLD: int = 24
|
RAM_SYMBOL_SIZE_THRESHOLD: int = 24
|
||||||
# Number of top symbols to show in the largest symbols report
|
|
||||||
TOP_SYMBOLS_LIMIT: int = 30
|
|
||||||
# Width for symbol name display in top symbols report
|
|
||||||
COL_TOP_SYMBOL_NAME: int = 55
|
|
||||||
|
|
||||||
# Column width constants
|
# Column width constants
|
||||||
COL_COMPONENT: int = 29
|
COL_COMPONENT: int = 29
|
||||||
@@ -153,83 +147,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
|||||||
section_label = f" [{section[1:]}]" # .data -> [data], .bss -> [bss]
|
section_label = f" [{section[1:]}]" # .data -> [data], .bss -> [bss]
|
||||||
return f"{demangled} ({size:,} B){section_label}"
|
return f"{demangled} ({size:,} B){section_label}"
|
||||||
|
|
||||||
def _add_top_symbols(self, lines: list[str]) -> None:
|
|
||||||
"""Add a section showing the top largest symbols in the binary."""
|
|
||||||
# Collect all symbols from all components: (symbol, demangled, size, section, component)
|
|
||||||
all_symbols = [
|
|
||||||
(symbol, demangled, size, section, component)
|
|
||||||
for component, symbols in self._component_symbols.items()
|
|
||||||
for symbol, demangled, size, section in symbols
|
|
||||||
]
|
|
||||||
|
|
||||||
# Get top N symbols by size using heapq for efficiency
|
|
||||||
top_symbols = heapq.nlargest(
|
|
||||||
self.TOP_SYMBOLS_LIMIT, all_symbols, key=itemgetter(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
lines.append("")
|
|
||||||
lines.append(f"Top {self.TOP_SYMBOLS_LIMIT} Largest Symbols:")
|
|
||||||
# Calculate truncation limit from column width (leaving room for "...")
|
|
||||||
truncate_limit = self.COL_TOP_SYMBOL_NAME - 3
|
|
||||||
for i, (_, demangled, size, section, component) in enumerate(top_symbols):
|
|
||||||
# Format section label
|
|
||||||
section_label = f"[{section[1:]}]" if section else ""
|
|
||||||
# Truncate demangled name if too long
|
|
||||||
demangled_display = (
|
|
||||||
f"{demangled[:truncate_limit]}..."
|
|
||||||
if len(demangled) > self.COL_TOP_SYMBOL_NAME
|
|
||||||
else demangled
|
|
||||||
)
|
|
||||||
lines.append(
|
|
||||||
f"{i + 1:>2}. {size:>7,} B {section_label:<8} {demangled_display:<{self.COL_TOP_SYMBOL_NAME}} {component}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _add_cswtch_analysis(self, lines: list[str]) -> None:
|
|
||||||
"""Add CSWTCH (GCC switch table lookup) analysis section."""
|
|
||||||
self._add_section_header(lines, "CSWTCH Analysis (GCC Switch Table Lookups)")
|
|
||||||
|
|
||||||
total_size = sum(size for _, size, _, _ in self._cswtch_symbols)
|
|
||||||
lines.append(
|
|
||||||
f"Total: {len(self._cswtch_symbols)} switch table(s), {total_size:,} B"
|
|
||||||
)
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
# Group by component
|
|
||||||
by_component: dict[str, list[tuple[str, int, str]]] = defaultdict(list)
|
|
||||||
for sym_name, size, source_file, component in self._cswtch_symbols:
|
|
||||||
by_component[component].append((sym_name, size, source_file))
|
|
||||||
|
|
||||||
# Sort components by total size descending
|
|
||||||
sorted_components = sorted(
|
|
||||||
by_component.items(),
|
|
||||||
key=lambda x: sum(s[1] for s in x[1]),
|
|
||||||
reverse=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
for component, symbols in sorted_components:
|
|
||||||
comp_total = sum(s[1] for s in symbols)
|
|
||||||
lines.append(f"{component} ({comp_total:,} B, {len(symbols)} tables):")
|
|
||||||
|
|
||||||
# Group by source file within component
|
|
||||||
by_file: dict[str, list[tuple[str, int]]] = defaultdict(list)
|
|
||||||
for sym_name, size, source_file in symbols:
|
|
||||||
by_file[source_file].append((sym_name, size))
|
|
||||||
|
|
||||||
for source_file, file_symbols in sorted(
|
|
||||||
by_file.items(),
|
|
||||||
key=lambda x: sum(s[1] for s in x[1]),
|
|
||||||
reverse=True,
|
|
||||||
):
|
|
||||||
file_total = sum(s[1] for s in file_symbols)
|
|
||||||
lines.append(
|
|
||||||
f" {source_file} ({file_total:,} B, {len(file_symbols)} tables)"
|
|
||||||
)
|
|
||||||
for sym_name, size in sorted(
|
|
||||||
file_symbols, key=lambda x: x[1], reverse=True
|
|
||||||
):
|
|
||||||
lines.append(f" {size:>6,} B {sym_name}")
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
def generate_report(self, detailed: bool = False) -> str:
|
def generate_report(self, detailed: bool = False) -> str:
|
||||||
"""Generate a formatted memory report."""
|
"""Generate a formatted memory report."""
|
||||||
components = sorted(
|
components = sorted(
|
||||||
@@ -331,9 +248,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
|||||||
"RAM",
|
"RAM",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Top largest symbols in the binary
|
|
||||||
self._add_top_symbols(lines)
|
|
||||||
|
|
||||||
# Add ESPHome core detailed analysis if there are core symbols
|
# Add ESPHome core detailed analysis if there are core symbols
|
||||||
if self._esphome_core_symbols:
|
if self._esphome_core_symbols:
|
||||||
self._add_section_header(lines, f"{_COMPONENT_CORE} Detailed Analysis")
|
self._add_section_header(lines, f"{_COMPONENT_CORE} Detailed Analysis")
|
||||||
@@ -517,10 +431,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
|||||||
lines.append(f" ... and {len(large_ram_syms) - 10} more")
|
lines.append(f" ... and {len(large_ram_syms) - 10} more")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
# CSWTCH (GCC switch table) analysis
|
|
||||||
if self._cswtch_symbols:
|
|
||||||
self._add_cswtch_analysis(lines)
|
|
||||||
|
|
||||||
lines.append(
|
lines.append(
|
||||||
"Note: This analysis covers symbols in the ELF file. Some runtime allocations may not be included."
|
"Note: This analysis covers symbols in the ELF file. Some runtime allocations may not be included."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -66,6 +66,15 @@ SECTION_MAPPING = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Section to ComponentMemory attribute mapping
|
||||||
|
# Maps section names to the attribute name in ComponentMemory dataclass
|
||||||
|
SECTION_TO_ATTR = {
|
||||||
|
".text": "text_size",
|
||||||
|
".rodata": "rodata_size",
|
||||||
|
".data": "data_size",
|
||||||
|
".bss": "bss_size",
|
||||||
|
}
|
||||||
|
|
||||||
# Component identification rules
|
# Component identification rules
|
||||||
# Symbol patterns: patterns found in raw symbol names
|
# Symbol patterns: patterns found in raw symbol names
|
||||||
SYMBOL_PATTERNS = {
|
SYMBOL_PATTERNS = {
|
||||||
@@ -504,9 +513,7 @@ SYMBOL_PATTERNS = {
|
|||||||
"__FUNCTION__$",
|
"__FUNCTION__$",
|
||||||
"DAYS_IN_MONTH",
|
"DAYS_IN_MONTH",
|
||||||
"_DAYS_BEFORE_MONTH",
|
"_DAYS_BEFORE_MONTH",
|
||||||
# Note: CSWTCH$ symbols are GCC switch table lookup tables.
|
"CSWTCH$",
|
||||||
# They are attributed to their source object files via _analyze_cswtch_symbols()
|
|
||||||
# rather than being lumped into libc.
|
|
||||||
"dst$",
|
"dst$",
|
||||||
"sulp",
|
"sulp",
|
||||||
"_strtol_l", # String to long with locale
|
"_strtol_l", # String to long with locale
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ from esphome.cpp_types import ( # noqa: F401
|
|||||||
size_t,
|
size_t,
|
||||||
std_ns,
|
std_ns,
|
||||||
std_shared_ptr,
|
std_shared_ptr,
|
||||||
std_span,
|
|
||||||
std_string,
|
std_string,
|
||||||
std_string_ref,
|
std_string_ref,
|
||||||
std_vector,
|
std_vector,
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ void AbsoluteHumidityComponent::dump_config() {
|
|||||||
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
|
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void AbsoluteHumidityComponent::loop() {
|
void AbsoluteHumidityComponent::loop() {
|
||||||
if (!this->next_update_) {
|
if (!this->next_update_) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
|
|||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
/// This method is called during the ESPHome setup process to log the configuration.
|
/// This method is called during the ESPHome setup process to log the configuration.
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
|
/// Return the setup priority for this component.
|
||||||
|
/// Components with higher priority are initialized earlier during setup.
|
||||||
|
/// @return A float representing the setup priority.
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
#ifdef USE_ZEPHYR
|
#ifdef USE_ZEPHYR
|
||||||
/// Set the ADC channel to be used by the ADC sensor.
|
/// Set the ADC channel to be used by the ADC sensor.
|
||||||
/// @param channel Pointer to an adc_dt_spec structure representing the ADC channel.
|
/// @param channel Pointer to an adc_dt_spec structure representing the ADC channel.
|
||||||
|
|||||||
@@ -79,5 +79,7 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
|
|||||||
|
|
||||||
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
|
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
|
||||||
|
|
||||||
|
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
} // namespace adc
|
} // namespace adc
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ void ADCSensor::setup() {
|
|||||||
adc_oneshot_unit_init_cfg_t init_config = {}; // Zero initialize
|
adc_oneshot_unit_init_cfg_t init_config = {}; // Zero initialize
|
||||||
init_config.unit_id = this->adc_unit_;
|
init_config.unit_id = this->adc_unit_;
|
||||||
init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
|
init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
|
||||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
|
||||||
init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
|
init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
|
||||||
#endif // USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 ||
|
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 ||
|
||||||
// USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
|
// USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
|
||||||
esp_err_t err = adc_oneshot_new_unit(&init_config, &ADCSensor::shared_adc_handles[this->adc_unit_]);
|
esp_err_t err = adc_oneshot_new_unit(&init_config, &ADCSensor::shared_adc_handles[this->adc_unit_]);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error initializing %s: %d", LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), err);
|
ESP_LOGE(TAG, "Error initializing %s: %d", LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), err);
|
||||||
@@ -76,7 +76,7 @@ void ADCSensor::setup() {
|
|||||||
|
|
||||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||||
// RISC-V variants (except C2) and S3 use curve fitting calibration
|
// RISC-V variants and S3 use curve fitting calibration
|
||||||
adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
|
adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||||
cali_config.chan = this->channel_;
|
cali_config.chan = this->channel_;
|
||||||
@@ -94,14 +94,14 @@ void ADCSensor::setup() {
|
|||||||
ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
|
ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
|
||||||
this->setup_flags_.calibration_complete = false;
|
this->setup_flags_.calibration_complete = false;
|
||||||
}
|
}
|
||||||
#else // ESP32, ESP32-S2, and ESP32-C2 use line fitting calibration
|
#else // Other ESP32 variants use line fitting calibration
|
||||||
adc_cali_line_fitting_config_t cali_config = {
|
adc_cali_line_fitting_config_t cali_config = {
|
||||||
.unit_id = this->adc_unit_,
|
.unit_id = this->adc_unit_,
|
||||||
.atten = this->attenuation_,
|
.atten = this->attenuation_,
|
||||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
|
#if !defined(USE_ESP32_VARIANT_ESP32S2)
|
||||||
.default_vref = 1100, // Default reference voltage in mV
|
.default_vref = 1100, // Default reference voltage in mV
|
||||||
#endif // !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
|
#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
|
||||||
};
|
};
|
||||||
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
@@ -112,7 +112,7 @@ void ADCSensor::setup() {
|
|||||||
ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
|
ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
|
||||||
this->setup_flags_.calibration_complete = false;
|
this->setup_flags_.calibration_complete = false;
|
||||||
}
|
}
|
||||||
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
||||||
}
|
}
|
||||||
|
|
||||||
this->setup_flags_.init_complete = true;
|
this->setup_flags_.init_complete = true;
|
||||||
@@ -189,7 +189,7 @@ float ADCSensor::sample_fixed_attenuation_() {
|
|||||||
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
|
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
|
||||||
#else // Other ESP32 variants use line fitting calibration
|
#else // Other ESP32 variants use line fitting calibration
|
||||||
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
|
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
|
||||||
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
||||||
this->calibration_handle_ = nullptr;
|
this->calibration_handle_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +247,7 @@ float ADCSensor::sample_autorange_() {
|
|||||||
.unit_id = this->adc_unit_,
|
.unit_id = this->adc_unit_,
|
||||||
.atten = atten,
|
.atten = atten,
|
||||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
|
#if !defined(USE_ESP32_VARIANT_ESP32S2)
|
||||||
.default_vref = 1100,
|
.default_vref = 1100,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ static const char *const TAG = "adc128s102.sensor";
|
|||||||
|
|
||||||
ADC128S102Sensor::ADC128S102Sensor(uint8_t channel) : channel_(channel) {}
|
ADC128S102Sensor::ADC128S102Sensor(uint8_t channel) : channel_(channel) {}
|
||||||
|
|
||||||
|
float ADC128S102Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void ADC128S102Sensor::dump_config() {
|
void ADC128S102Sensor::dump_config() {
|
||||||
LOG_SENSOR("", "ADC128S102 Sensor", this);
|
LOG_SENSOR("", "ADC128S102 Sensor", this);
|
||||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->channel_);
|
ESP_LOGCONFIG(TAG, " Pin: %u", this->channel_);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class ADC128S102Sensor : public PollingComponent,
|
|||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
float sample() override;
|
float sample() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ void AHT10Component::update() {
|
|||||||
this->restart_read_();
|
this->restart_read_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void AHT10Component::dump_config() {
|
void AHT10Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "AHT10:");
|
ESP_LOGCONFIG(TAG, "AHT10:");
|
||||||
LOG_I2C_DEVICE(this);
|
LOG_I2C_DEVICE(this);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void set_variant(AHT10Variant variant) { this->variant_ = variant; }
|
void set_variant(AHT10Variant variant) { this->variant_ = variant; }
|
||||||
|
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
|||||||
@@ -1,15 +1,32 @@
|
|||||||
#include "alarm_control_panel_state.h"
|
#include "alarm_control_panel_state.h"
|
||||||
#include "esphome/core/progmem.h"
|
|
||||||
|
|
||||||
namespace esphome::alarm_control_panel {
|
namespace esphome::alarm_control_panel {
|
||||||
|
|
||||||
// Alarm control panel state strings indexed by AlarmControlPanelState enum (0-9)
|
|
||||||
PROGMEM_STRING_TABLE(AlarmControlPanelStateStrings, "DISARMED", "ARMED_HOME", "ARMED_AWAY", "ARMED_NIGHT",
|
|
||||||
"ARMED_VACATION", "ARMED_CUSTOM_BYPASS", "PENDING", "ARMING", "DISARMING", "TRIGGERED", "UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state) {
|
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state) {
|
||||||
return AlarmControlPanelStateStrings::get_log_str(static_cast<uint8_t>(state),
|
switch (state) {
|
||||||
AlarmControlPanelStateStrings::LAST_INDEX);
|
case ACP_STATE_DISARMED:
|
||||||
|
return LOG_STR("DISARMED");
|
||||||
|
case ACP_STATE_ARMED_HOME:
|
||||||
|
return LOG_STR("ARMED_HOME");
|
||||||
|
case ACP_STATE_ARMED_AWAY:
|
||||||
|
return LOG_STR("ARMED_AWAY");
|
||||||
|
case ACP_STATE_ARMED_NIGHT:
|
||||||
|
return LOG_STR("ARMED_NIGHT");
|
||||||
|
case ACP_STATE_ARMED_VACATION:
|
||||||
|
return LOG_STR("ARMED_VACATION");
|
||||||
|
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
||||||
|
return LOG_STR("ARMED_CUSTOM_BYPASS");
|
||||||
|
case ACP_STATE_PENDING:
|
||||||
|
return LOG_STR("PENDING");
|
||||||
|
case ACP_STATE_ARMING:
|
||||||
|
return LOG_STR("ARMING");
|
||||||
|
case ACP_STATE_DISARMING:
|
||||||
|
return LOG_STR("DISARMING");
|
||||||
|
case ACP_STATE_TRIGGERED:
|
||||||
|
return LOG_STR("TRIGGERED");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome::alarm_control_panel
|
} // namespace esphome::alarm_control_panel
|
||||||
|
|||||||
@@ -176,5 +176,7 @@ void AM2315C::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AM2315C::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
} // namespace am2315c
|
} // namespace am2315c
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
||||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ void AM2320Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
}
|
}
|
||||||
|
float AM2320Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) {
|
bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) {
|
||||||
if (!this->write_bytes(a_register, data, 2)) {
|
if (!this->write_bytes(a_register, data, 2)) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ void APDS9960::process_dataset_(int up, int down, int left, int right) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
float APDS9960::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
bool APDS9960::is_proximity_enabled_() const {
|
bool APDS9960::is_proximity_enabled_() const {
|
||||||
return
|
return
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ service APIConnection {
|
|||||||
rpc time_command (TimeCommandRequest) returns (void) {}
|
rpc time_command (TimeCommandRequest) returns (void) {}
|
||||||
rpc update_command (UpdateCommandRequest) returns (void) {}
|
rpc update_command (UpdateCommandRequest) returns (void) {}
|
||||||
rpc valve_command (ValveCommandRequest) returns (void) {}
|
rpc valve_command (ValveCommandRequest) returns (void) {}
|
||||||
rpc water_heater_command (WaterHeaterCommandRequest) returns (void) {}
|
|
||||||
|
|
||||||
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||||
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ void APIConnection::loop() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APIConnection::send_disconnect_response() {
|
bool APIConnection::send_disconnect_response(const DisconnectRequest &msg) {
|
||||||
// remote initiated disconnect_client
|
// remote initiated disconnect_client
|
||||||
// don't close yet, we still need to send the disconnect response
|
// don't close yet, we still need to send the disconnect response
|
||||||
// close will happen on next loop
|
// close will happen on next loop
|
||||||
@@ -292,7 +292,7 @@ bool APIConnection::send_disconnect_response() {
|
|||||||
DisconnectResponse resp;
|
DisconnectResponse resp;
|
||||||
return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
|
return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
void APIConnection::on_disconnect_response() {
|
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
||||||
this->helper_->close();
|
this->helper_->close();
|
||||||
this->flags_.remove = true;
|
this->flags_.remove = true;
|
||||||
}
|
}
|
||||||
@@ -300,7 +300,7 @@ void APIConnection::on_disconnect_response() {
|
|||||||
// Encodes a message to the buffer and returns the total number of bytes used,
|
// Encodes a message to the buffer and returns the total number of bytes used,
|
||||||
// including header and footer overhead. Returns 0 if the message doesn't fit.
|
// including header and footer overhead. Returns 0 if the message doesn't fit.
|
||||||
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn,
|
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn,
|
||||||
uint32_t remaining_size) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
// If in log-only mode, just log and return
|
// If in log-only mode, just log and return
|
||||||
if (conn->flags_.log_only_mode) {
|
if (conn->flags_.log_only_mode) {
|
||||||
@@ -330,9 +330,12 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
|||||||
// Get buffer size after allocation (which includes header padding)
|
// Get buffer size after allocation (which includes header padding)
|
||||||
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
|
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
|
||||||
|
|
||||||
if (conn->flags_.batch_first_message) {
|
if (is_single || conn->flags_.batch_first_message) {
|
||||||
// First message - buffer already prepared by caller, just clear flag
|
// Single message or first batch message
|
||||||
conn->flags_.batch_first_message = false;
|
conn->prepare_first_message_buffer(shared_buf, header_padding, total_calculated_size);
|
||||||
|
if (conn->flags_.batch_first_message) {
|
||||||
|
conn->flags_.batch_first_message = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Batch message second or later
|
// Batch message second or later
|
||||||
// Add padding for previous message footer + this message header
|
// Add padding for previous message footer + this message header
|
||||||
@@ -362,22 +365,24 @@ bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary
|
|||||||
BinarySensorStateResponse::ESTIMATED_SIZE);
|
BinarySensorStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
|
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
|
||||||
BinarySensorStateResponse resp;
|
BinarySensorStateResponse resp;
|
||||||
resp.state = binary_sensor->state;
|
resp.state = binary_sensor->state;
|
||||||
resp.missing_state = !binary_sensor->has_state();
|
resp.missing_state = !binary_sensor->has_state();
|
||||||
return fill_and_encode_entity_state(binary_sensor, resp, BinarySensorStateResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_state(binary_sensor, resp, BinarySensorStateResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size);
|
remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
|
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
|
||||||
ListEntitiesBinarySensorResponse msg;
|
ListEntitiesBinarySensorResponse msg;
|
||||||
msg.device_class = binary_sensor->get_device_class_ref();
|
msg.device_class = binary_sensor->get_device_class_ref();
|
||||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||||
return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size);
|
remaining_size, is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -385,7 +390,8 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne
|
|||||||
bool APIConnection::send_cover_state(cover::Cover *cover) {
|
bool APIConnection::send_cover_state(cover::Cover *cover) {
|
||||||
return this->send_message_smart_(cover, CoverStateResponse::MESSAGE_TYPE, CoverStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(cover, CoverStateResponse::MESSAGE_TYPE, CoverStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *cover = static_cast<cover::Cover *>(entity);
|
auto *cover = static_cast<cover::Cover *>(entity);
|
||||||
CoverStateResponse msg;
|
CoverStateResponse msg;
|
||||||
auto traits = cover->get_traits();
|
auto traits = cover->get_traits();
|
||||||
@@ -393,9 +399,10 @@ uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *
|
|||||||
if (traits.get_supports_tilt())
|
if (traits.get_supports_tilt())
|
||||||
msg.tilt = cover->tilt;
|
msg.tilt = cover->tilt;
|
||||||
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
|
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
|
||||||
return fill_and_encode_entity_state(cover, msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(cover, msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *cover = static_cast<cover::Cover *>(entity);
|
auto *cover = static_cast<cover::Cover *>(entity);
|
||||||
ListEntitiesCoverResponse msg;
|
ListEntitiesCoverResponse msg;
|
||||||
auto traits = cover->get_traits();
|
auto traits = cover->get_traits();
|
||||||
@@ -404,7 +411,8 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
|
|||||||
msg.supports_tilt = traits.get_supports_tilt();
|
msg.supports_tilt = traits.get_supports_tilt();
|
||||||
msg.supports_stop = traits.get_supports_stop();
|
msg.supports_stop = traits.get_supports_stop();
|
||||||
msg.device_class = cover->get_device_class_ref();
|
msg.device_class = cover->get_device_class_ref();
|
||||||
return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
|
ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
|
||||||
@@ -422,7 +430,8 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
|||||||
bool APIConnection::send_fan_state(fan::Fan *fan) {
|
bool APIConnection::send_fan_state(fan::Fan *fan) {
|
||||||
return this->send_message_smart_(fan, FanStateResponse::MESSAGE_TYPE, FanStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(fan, FanStateResponse::MESSAGE_TYPE, FanStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *fan = static_cast<fan::Fan *>(entity);
|
auto *fan = static_cast<fan::Fan *>(entity);
|
||||||
FanStateResponse msg;
|
FanStateResponse msg;
|
||||||
auto traits = fan->get_traits();
|
auto traits = fan->get_traits();
|
||||||
@@ -436,9 +445,10 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co
|
|||||||
msg.direction = static_cast<enums::FanDirection>(fan->direction);
|
msg.direction = static_cast<enums::FanDirection>(fan->direction);
|
||||||
if (traits.supports_preset_modes() && fan->has_preset_mode())
|
if (traits.supports_preset_modes() && fan->has_preset_mode())
|
||||||
msg.preset_mode = fan->get_preset_mode();
|
msg.preset_mode = fan->get_preset_mode();
|
||||||
return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *fan = static_cast<fan::Fan *>(entity);
|
auto *fan = static_cast<fan::Fan *>(entity);
|
||||||
ListEntitiesFanResponse msg;
|
ListEntitiesFanResponse msg;
|
||||||
auto traits = fan->get_traits();
|
auto traits = fan->get_traits();
|
||||||
@@ -447,7 +457,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
|
|||||||
msg.supports_direction = traits.supports_direction();
|
msg.supports_direction = traits.supports_direction();
|
||||||
msg.supported_speed_count = traits.supported_speed_count();
|
msg.supported_speed_count = traits.supported_speed_count();
|
||||||
msg.supported_preset_modes = &traits.supported_preset_modes();
|
msg.supported_preset_modes = &traits.supported_preset_modes();
|
||||||
return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(fan::Fan, fan, fan)
|
ENTITY_COMMAND_MAKE_CALL(fan::Fan, fan, fan)
|
||||||
@@ -471,7 +481,8 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
|||||||
bool APIConnection::send_light_state(light::LightState *light) {
|
bool APIConnection::send_light_state(light::LightState *light) {
|
||||||
return this->send_message_smart_(light, LightStateResponse::MESSAGE_TYPE, LightStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(light, LightStateResponse::MESSAGE_TYPE, LightStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *light = static_cast<light::LightState *>(entity);
|
auto *light = static_cast<light::LightState *>(entity);
|
||||||
LightStateResponse resp;
|
LightStateResponse resp;
|
||||||
auto values = light->remote_values;
|
auto values = light->remote_values;
|
||||||
@@ -490,9 +501,10 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *
|
|||||||
if (light->supports_effects()) {
|
if (light->supports_effects()) {
|
||||||
resp.effect = light->get_effect_name();
|
resp.effect = light->get_effect_name();
|
||||||
}
|
}
|
||||||
return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *light = static_cast<light::LightState *>(entity);
|
auto *light = static_cast<light::LightState *>(entity);
|
||||||
ListEntitiesLightResponse msg;
|
ListEntitiesLightResponse msg;
|
||||||
auto traits = light->get_traits();
|
auto traits = light->get_traits();
|
||||||
@@ -515,7 +527,8 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg.effects = &effects_list;
|
msg.effects = &effects_list;
|
||||||
return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::light_command(const LightCommandRequest &msg) {
|
void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(light::LightState, light, light)
|
ENTITY_COMMAND_MAKE_CALL(light::LightState, light, light)
|
||||||
@@ -555,15 +568,17 @@ bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
|
|||||||
return this->send_message_smart_(sensor, SensorStateResponse::MESSAGE_TYPE, SensorStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(sensor, SensorStateResponse::MESSAGE_TYPE, SensorStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *sensor = static_cast<sensor::Sensor *>(entity);
|
auto *sensor = static_cast<sensor::Sensor *>(entity);
|
||||||
SensorStateResponse resp;
|
SensorStateResponse resp;
|
||||||
resp.state = sensor->state;
|
resp.state = sensor->state;
|
||||||
resp.missing_state = !sensor->has_state();
|
resp.missing_state = !sensor->has_state();
|
||||||
return fill_and_encode_entity_state(sensor, resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(sensor, resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *sensor = static_cast<sensor::Sensor *>(entity);
|
auto *sensor = static_cast<sensor::Sensor *>(entity);
|
||||||
ListEntitiesSensorResponse msg;
|
ListEntitiesSensorResponse msg;
|
||||||
msg.unit_of_measurement = sensor->get_unit_of_measurement_ref();
|
msg.unit_of_measurement = sensor->get_unit_of_measurement_ref();
|
||||||
@@ -571,7 +586,8 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
|
|||||||
msg.force_update = sensor->get_force_update();
|
msg.force_update = sensor->get_force_update();
|
||||||
msg.device_class = sensor->get_device_class_ref();
|
msg.device_class = sensor->get_device_class_ref();
|
||||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
||||||
return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -580,19 +596,23 @@ bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
|
|||||||
return this->send_message_smart_(a_switch, SwitchStateResponse::MESSAGE_TYPE, SwitchStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(a_switch, SwitchStateResponse::MESSAGE_TYPE, SwitchStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
||||||
SwitchStateResponse resp;
|
SwitchStateResponse resp;
|
||||||
resp.state = a_switch->state;
|
resp.state = a_switch->state;
|
||||||
return fill_and_encode_entity_state(a_switch, resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(a_switch, resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
||||||
ListEntitiesSwitchResponse msg;
|
ListEntitiesSwitchResponse msg;
|
||||||
msg.assumed_state = a_switch->assumed_state();
|
msg.assumed_state = a_switch->assumed_state();
|
||||||
msg.device_class = a_switch->get_device_class_ref();
|
msg.device_class = a_switch->get_device_class_ref();
|
||||||
return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_GET(switch_::Switch, a_switch, switch)
|
ENTITY_COMMAND_GET(switch_::Switch, a_switch, switch)
|
||||||
@@ -611,19 +631,22 @@ bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor)
|
|||||||
TextSensorStateResponse::ESTIMATED_SIZE);
|
TextSensorStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
||||||
TextSensorStateResponse resp;
|
TextSensorStateResponse resp;
|
||||||
resp.state = StringRef(text_sensor->state);
|
resp.state = StringRef(text_sensor->state);
|
||||||
resp.missing_state = !text_sensor->has_state();
|
resp.missing_state = !text_sensor->has_state();
|
||||||
return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
||||||
ListEntitiesTextSensorResponse msg;
|
ListEntitiesTextSensorResponse msg;
|
||||||
msg.device_class = text_sensor->get_device_class_ref();
|
msg.device_class = text_sensor->get_device_class_ref();
|
||||||
return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size);
|
remaining_size, is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -631,7 +654,8 @@ uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnect
|
|||||||
bool APIConnection::send_climate_state(climate::Climate *climate) {
|
bool APIConnection::send_climate_state(climate::Climate *climate) {
|
||||||
return this->send_message_smart_(climate, ClimateStateResponse::MESSAGE_TYPE, ClimateStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(climate, ClimateStateResponse::MESSAGE_TYPE, ClimateStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *climate = static_cast<climate::Climate *>(entity);
|
auto *climate = static_cast<climate::Climate *>(entity);
|
||||||
ClimateStateResponse resp;
|
ClimateStateResponse resp;
|
||||||
auto traits = climate->get_traits();
|
auto traits = climate->get_traits();
|
||||||
@@ -663,9 +687,11 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
|
|||||||
resp.current_humidity = climate->current_humidity;
|
resp.current_humidity = climate->current_humidity;
|
||||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY))
|
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY))
|
||||||
resp.target_humidity = climate->target_humidity;
|
resp.target_humidity = climate->target_humidity;
|
||||||
return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *climate = static_cast<climate::Climate *>(entity);
|
auto *climate = static_cast<climate::Climate *>(entity);
|
||||||
ListEntitiesClimateResponse msg;
|
ListEntitiesClimateResponse msg;
|
||||||
auto traits = climate->get_traits();
|
auto traits = climate->get_traits();
|
||||||
@@ -690,7 +716,8 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
|
|||||||
msg.supported_presets = &traits.get_supported_presets();
|
msg.supported_presets = &traits.get_supported_presets();
|
||||||
msg.supported_custom_presets = &traits.get_supported_custom_presets();
|
msg.supported_custom_presets = &traits.get_supported_custom_presets();
|
||||||
msg.supported_swing_modes = &traits.get_supported_swing_modes();
|
msg.supported_swing_modes = &traits.get_supported_swing_modes();
|
||||||
return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(climate::Climate, climate, climate)
|
ENTITY_COMMAND_MAKE_CALL(climate::Climate, climate, climate)
|
||||||
@@ -723,15 +750,17 @@ bool APIConnection::send_number_state(number::Number *number) {
|
|||||||
return this->send_message_smart_(number, NumberStateResponse::MESSAGE_TYPE, NumberStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(number, NumberStateResponse::MESSAGE_TYPE, NumberStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *number = static_cast<number::Number *>(entity);
|
auto *number = static_cast<number::Number *>(entity);
|
||||||
NumberStateResponse resp;
|
NumberStateResponse resp;
|
||||||
resp.state = number->state;
|
resp.state = number->state;
|
||||||
resp.missing_state = !number->has_state();
|
resp.missing_state = !number->has_state();
|
||||||
return fill_and_encode_entity_state(number, resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(number, resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *number = static_cast<number::Number *>(entity);
|
auto *number = static_cast<number::Number *>(entity);
|
||||||
ListEntitiesNumberResponse msg;
|
ListEntitiesNumberResponse msg;
|
||||||
msg.unit_of_measurement = number->traits.get_unit_of_measurement_ref();
|
msg.unit_of_measurement = number->traits.get_unit_of_measurement_ref();
|
||||||
@@ -740,7 +769,8 @@ uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *
|
|||||||
msg.min_value = number->traits.get_min_value();
|
msg.min_value = number->traits.get_min_value();
|
||||||
msg.max_value = number->traits.get_max_value();
|
msg.max_value = number->traits.get_max_value();
|
||||||
msg.step = number->traits.get_step();
|
msg.step = number->traits.get_step();
|
||||||
return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::number_command(const NumberCommandRequest &msg) {
|
void APIConnection::number_command(const NumberCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(number::Number, number, number)
|
ENTITY_COMMAND_MAKE_CALL(number::Number, number, number)
|
||||||
@@ -753,19 +783,22 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
|
|||||||
bool APIConnection::send_date_state(datetime::DateEntity *date) {
|
bool APIConnection::send_date_state(datetime::DateEntity *date) {
|
||||||
return this->send_message_smart_(date, DateStateResponse::MESSAGE_TYPE, DateStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(date, DateStateResponse::MESSAGE_TYPE, DateStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *date = static_cast<datetime::DateEntity *>(entity);
|
auto *date = static_cast<datetime::DateEntity *>(entity);
|
||||||
DateStateResponse resp;
|
DateStateResponse resp;
|
||||||
resp.missing_state = !date->has_state();
|
resp.missing_state = !date->has_state();
|
||||||
resp.year = date->year;
|
resp.year = date->year;
|
||||||
resp.month = date->month;
|
resp.month = date->month;
|
||||||
resp.day = date->day;
|
resp.day = date->day;
|
||||||
return fill_and_encode_entity_state(date, resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(date, resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *date = static_cast<datetime::DateEntity *>(entity);
|
auto *date = static_cast<datetime::DateEntity *>(entity);
|
||||||
ListEntitiesDateResponse msg;
|
ListEntitiesDateResponse msg;
|
||||||
return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::date_command(const DateCommandRequest &msg) {
|
void APIConnection::date_command(const DateCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(datetime::DateEntity, date, date)
|
ENTITY_COMMAND_MAKE_CALL(datetime::DateEntity, date, date)
|
||||||
@@ -778,19 +811,22 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
|
|||||||
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
|
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
|
||||||
return this->send_message_smart_(time, TimeStateResponse::MESSAGE_TYPE, TimeStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(time, TimeStateResponse::MESSAGE_TYPE, TimeStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *time = static_cast<datetime::TimeEntity *>(entity);
|
auto *time = static_cast<datetime::TimeEntity *>(entity);
|
||||||
TimeStateResponse resp;
|
TimeStateResponse resp;
|
||||||
resp.missing_state = !time->has_state();
|
resp.missing_state = !time->has_state();
|
||||||
resp.hour = time->hour;
|
resp.hour = time->hour;
|
||||||
resp.minute = time->minute;
|
resp.minute = time->minute;
|
||||||
resp.second = time->second;
|
resp.second = time->second;
|
||||||
return fill_and_encode_entity_state(time, resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(time, resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *time = static_cast<datetime::TimeEntity *>(entity);
|
auto *time = static_cast<datetime::TimeEntity *>(entity);
|
||||||
ListEntitiesTimeResponse msg;
|
ListEntitiesTimeResponse msg;
|
||||||
return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::time_command(const TimeCommandRequest &msg) {
|
void APIConnection::time_command(const TimeCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(datetime::TimeEntity, time, time)
|
ENTITY_COMMAND_MAKE_CALL(datetime::TimeEntity, time, time)
|
||||||
@@ -804,7 +840,8 @@ bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
|
|||||||
return this->send_message_smart_(datetime, DateTimeStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(datetime, DateTimeStateResponse::MESSAGE_TYPE,
|
||||||
DateTimeStateResponse::ESTIMATED_SIZE);
|
DateTimeStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
|
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
|
||||||
DateTimeStateResponse resp;
|
DateTimeStateResponse resp;
|
||||||
resp.missing_state = !datetime->has_state();
|
resp.missing_state = !datetime->has_state();
|
||||||
@@ -812,12 +849,15 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
|
|||||||
ESPTime state = datetime->state_as_esptime();
|
ESPTime state = datetime->state_as_esptime();
|
||||||
resp.epoch_seconds = state.timestamp;
|
resp.epoch_seconds = state.timestamp;
|
||||||
}
|
}
|
||||||
return fill_and_encode_entity_state(datetime, resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(datetime, resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
|
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
|
||||||
ListEntitiesDateTimeResponse msg;
|
ListEntitiesDateTimeResponse msg;
|
||||||
return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
|
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(datetime::DateTimeEntity, datetime, datetime)
|
ENTITY_COMMAND_MAKE_CALL(datetime::DateTimeEntity, datetime, datetime)
|
||||||
@@ -831,22 +871,25 @@ bool APIConnection::send_text_state(text::Text *text) {
|
|||||||
return this->send_message_smart_(text, TextStateResponse::MESSAGE_TYPE, TextStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(text, TextStateResponse::MESSAGE_TYPE, TextStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *text = static_cast<text::Text *>(entity);
|
auto *text = static_cast<text::Text *>(entity);
|
||||||
TextStateResponse resp;
|
TextStateResponse resp;
|
||||||
resp.state = StringRef(text->state);
|
resp.state = StringRef(text->state);
|
||||||
resp.missing_state = !text->has_state();
|
resp.missing_state = !text->has_state();
|
||||||
return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *text = static_cast<text::Text *>(entity);
|
auto *text = static_cast<text::Text *>(entity);
|
||||||
ListEntitiesTextResponse msg;
|
ListEntitiesTextResponse msg;
|
||||||
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
|
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
|
||||||
msg.min_length = text->traits.get_min_length();
|
msg.min_length = text->traits.get_min_length();
|
||||||
msg.max_length = text->traits.get_max_length();
|
msg.max_length = text->traits.get_max_length();
|
||||||
msg.pattern = text->traits.get_pattern_ref();
|
msg.pattern = text->traits.get_pattern_ref();
|
||||||
return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::text_command(const TextCommandRequest &msg) {
|
void APIConnection::text_command(const TextCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(text::Text, text, text)
|
ENTITY_COMMAND_MAKE_CALL(text::Text, text, text)
|
||||||
@@ -860,19 +903,22 @@ bool APIConnection::send_select_state(select::Select *select) {
|
|||||||
return this->send_message_smart_(select, SelectStateResponse::MESSAGE_TYPE, SelectStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(select, SelectStateResponse::MESSAGE_TYPE, SelectStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *select = static_cast<select::Select *>(entity);
|
auto *select = static_cast<select::Select *>(entity);
|
||||||
SelectStateResponse resp;
|
SelectStateResponse resp;
|
||||||
resp.state = select->current_option();
|
resp.state = select->current_option();
|
||||||
resp.missing_state = !select->has_state();
|
resp.missing_state = !select->has_state();
|
||||||
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *select = static_cast<select::Select *>(entity);
|
auto *select = static_cast<select::Select *>(entity);
|
||||||
ListEntitiesSelectResponse msg;
|
ListEntitiesSelectResponse msg;
|
||||||
msg.options = &select->traits.get_options();
|
msg.options = &select->traits.get_options();
|
||||||
return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
|
ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
|
||||||
@@ -882,11 +928,13 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *button = static_cast<button::Button *>(entity);
|
auto *button = static_cast<button::Button *>(entity);
|
||||||
ListEntitiesButtonResponse msg;
|
ListEntitiesButtonResponse msg;
|
||||||
msg.device_class = button->get_device_class_ref();
|
msg.device_class = button->get_device_class_ref();
|
||||||
return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
|
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_GET(button::Button, button, button)
|
ENTITY_COMMAND_GET(button::Button, button, button)
|
||||||
@@ -899,20 +947,23 @@ bool APIConnection::send_lock_state(lock::Lock *a_lock) {
|
|||||||
return this->send_message_smart_(a_lock, LockStateResponse::MESSAGE_TYPE, LockStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(a_lock, LockStateResponse::MESSAGE_TYPE, LockStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *a_lock = static_cast<lock::Lock *>(entity);
|
auto *a_lock = static_cast<lock::Lock *>(entity);
|
||||||
LockStateResponse resp;
|
LockStateResponse resp;
|
||||||
resp.state = static_cast<enums::LockState>(a_lock->state);
|
resp.state = static_cast<enums::LockState>(a_lock->state);
|
||||||
return fill_and_encode_entity_state(a_lock, resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(a_lock, resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *a_lock = static_cast<lock::Lock *>(entity);
|
auto *a_lock = static_cast<lock::Lock *>(entity);
|
||||||
ListEntitiesLockResponse msg;
|
ListEntitiesLockResponse msg;
|
||||||
msg.assumed_state = a_lock->traits.get_assumed_state();
|
msg.assumed_state = a_lock->traits.get_assumed_state();
|
||||||
msg.supports_open = a_lock->traits.get_supports_open();
|
msg.supports_open = a_lock->traits.get_supports_open();
|
||||||
msg.requires_code = a_lock->traits.get_requires_code();
|
msg.requires_code = a_lock->traits.get_requires_code();
|
||||||
return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::lock_command(const LockCommandRequest &msg) {
|
void APIConnection::lock_command(const LockCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_GET(lock::Lock, a_lock, lock)
|
ENTITY_COMMAND_GET(lock::Lock, a_lock, lock)
|
||||||
@@ -935,14 +986,16 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
|
|||||||
bool APIConnection::send_valve_state(valve::Valve *valve) {
|
bool APIConnection::send_valve_state(valve::Valve *valve) {
|
||||||
return this->send_message_smart_(valve, ValveStateResponse::MESSAGE_TYPE, ValveStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(valve, ValveStateResponse::MESSAGE_TYPE, ValveStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *valve = static_cast<valve::Valve *>(entity);
|
auto *valve = static_cast<valve::Valve *>(entity);
|
||||||
ValveStateResponse resp;
|
ValveStateResponse resp;
|
||||||
resp.position = valve->position;
|
resp.position = valve->position;
|
||||||
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
|
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
|
||||||
return fill_and_encode_entity_state(valve, resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(valve, resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *valve = static_cast<valve::Valve *>(entity);
|
auto *valve = static_cast<valve::Valve *>(entity);
|
||||||
ListEntitiesValveResponse msg;
|
ListEntitiesValveResponse msg;
|
||||||
auto traits = valve->get_traits();
|
auto traits = valve->get_traits();
|
||||||
@@ -950,7 +1003,8 @@ uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *c
|
|||||||
msg.assumed_state = traits.get_is_assumed_state();
|
msg.assumed_state = traits.get_is_assumed_state();
|
||||||
msg.supports_position = traits.get_supports_position();
|
msg.supports_position = traits.get_supports_position();
|
||||||
msg.supports_stop = traits.get_supports_stop();
|
msg.supports_stop = traits.get_supports_stop();
|
||||||
return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::valve_command(const ValveCommandRequest &msg) {
|
void APIConnection::valve_command(const ValveCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(valve::Valve, valve, valve)
|
ENTITY_COMMAND_MAKE_CALL(valve::Valve, valve, valve)
|
||||||
@@ -967,7 +1021,8 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla
|
|||||||
return this->send_message_smart_(media_player, MediaPlayerStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(media_player, MediaPlayerStateResponse::MESSAGE_TYPE,
|
||||||
MediaPlayerStateResponse::ESTIMATED_SIZE);
|
MediaPlayerStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
|
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
|
||||||
MediaPlayerStateResponse resp;
|
MediaPlayerStateResponse resp;
|
||||||
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
|
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
|
||||||
@@ -976,9 +1031,11 @@ uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConne
|
|||||||
resp.state = static_cast<enums::MediaPlayerState>(report_state);
|
resp.state = static_cast<enums::MediaPlayerState>(report_state);
|
||||||
resp.volume = media_player->volume;
|
resp.volume = media_player->volume;
|
||||||
resp.muted = media_player->is_muted();
|
resp.muted = media_player->is_muted();
|
||||||
return fill_and_encode_entity_state(media_player, resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(media_player, resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
|
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
|
||||||
ListEntitiesMediaPlayerResponse msg;
|
ListEntitiesMediaPlayerResponse msg;
|
||||||
auto traits = media_player->get_traits();
|
auto traits = media_player->get_traits();
|
||||||
@@ -994,7 +1051,7 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
|
|||||||
media_format.sample_bytes = supported_format.sample_bytes;
|
media_format.sample_bytes = supported_format.sample_bytes;
|
||||||
}
|
}
|
||||||
return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size);
|
remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
|
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(media_player::MediaPlayer, media_player, media_player)
|
ENTITY_COMMAND_MAKE_CALL(media_player::MediaPlayer, media_player, media_player)
|
||||||
@@ -1035,7 +1092,7 @@ void APIConnection::try_send_camera_image_() {
|
|||||||
msg.device_id = camera::Camera::instance()->get_device_id();
|
msg.device_id = camera::Camera::instance()->get_device_id();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!this->send_message_impl(msg, CameraImageResponse::MESSAGE_TYPE)) {
|
if (!this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) {
|
||||||
return; // Send failed, try again later
|
return; // Send failed, try again later
|
||||||
}
|
}
|
||||||
this->image_reader_->consume_data(to_send);
|
this->image_reader_->consume_data(to_send);
|
||||||
@@ -1058,10 +1115,12 @@ void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image)
|
|||||||
this->try_send_camera_image_();
|
this->try_send_camera_image_();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *camera = static_cast<camera::Camera *>(entity);
|
auto *camera = static_cast<camera::Camera *>(entity);
|
||||||
ListEntitiesCameraResponse msg;
|
ListEntitiesCameraResponse msg;
|
||||||
return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||||
if (camera::Camera::instance() == nullptr)
|
if (camera::Camera::instance() == nullptr)
|
||||||
@@ -1095,7 +1154,7 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
|
|||||||
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
|
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
|
||||||
}
|
}
|
||||||
void APIConnection::unsubscribe_bluetooth_le_advertisements() {
|
void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
|
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
|
||||||
}
|
}
|
||||||
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||||
@@ -1121,7 +1180,8 @@ void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg)
|
|||||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(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(
|
||||||
|
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
||||||
bluetooth_proxy::global_bluetooth_proxy->send_connections_free(this);
|
bluetooth_proxy::global_bluetooth_proxy->send_connections_free(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1245,22 +1305,22 @@ bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmCon
|
|||||||
AlarmControlPanelStateResponse::ESTIMATED_SIZE);
|
AlarmControlPanelStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
|
uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
|
||||||
uint32_t remaining_size) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
||||||
AlarmControlPanelStateResponse resp;
|
AlarmControlPanelStateResponse resp;
|
||||||
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
|
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
|
||||||
return fill_and_encode_entity_state(a_alarm_control_panel, resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_state(a_alarm_control_panel, resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size);
|
remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
|
uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
|
||||||
uint32_t remaining_size) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
||||||
ListEntitiesAlarmControlPanelResponse msg;
|
ListEntitiesAlarmControlPanelResponse msg;
|
||||||
msg.supported_features = a_alarm_control_panel->get_supported_features();
|
msg.supported_features = a_alarm_control_panel->get_supported_features();
|
||||||
msg.requires_code = a_alarm_control_panel->get_requires_code();
|
msg.requires_code = a_alarm_control_panel->get_requires_code();
|
||||||
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
|
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
|
||||||
return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
|
return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
|
||||||
conn, remaining_size);
|
conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
|
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(alarm_control_panel::AlarmControlPanel, a_alarm_control_panel, alarm_control_panel)
|
ENTITY_COMMAND_MAKE_CALL(alarm_control_panel::AlarmControlPanel, a_alarm_control_panel, alarm_control_panel)
|
||||||
@@ -1297,7 +1357,8 @@ bool APIConnection::send_water_heater_state(water_heater::WaterHeater *water_hea
|
|||||||
return this->send_message_smart_(water_heater, WaterHeaterStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(water_heater, WaterHeaterStateResponse::MESSAGE_TYPE,
|
||||||
WaterHeaterStateResponse::ESTIMATED_SIZE);
|
WaterHeaterStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *wh = static_cast<water_heater::WaterHeater *>(entity);
|
auto *wh = static_cast<water_heater::WaterHeater *>(entity);
|
||||||
WaterHeaterStateResponse resp;
|
WaterHeaterStateResponse resp;
|
||||||
resp.mode = static_cast<enums::WaterHeaterMode>(wh->get_mode());
|
resp.mode = static_cast<enums::WaterHeaterMode>(wh->get_mode());
|
||||||
@@ -1308,9 +1369,10 @@ uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConne
|
|||||||
resp.state = wh->get_state();
|
resp.state = wh->get_state();
|
||||||
resp.key = wh->get_object_id_hash();
|
resp.key = wh->get_object_id_hash();
|
||||||
|
|
||||||
return encode_message_to_buffer(resp, WaterHeaterStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return encode_message_to_buffer(resp, WaterHeaterStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *wh = static_cast<water_heater::WaterHeater *>(entity);
|
auto *wh = static_cast<water_heater::WaterHeater *>(entity);
|
||||||
ListEntitiesWaterHeaterResponse msg;
|
ListEntitiesWaterHeaterResponse msg;
|
||||||
auto traits = wh->get_traits();
|
auto traits = wh->get_traits();
|
||||||
@@ -1319,10 +1381,11 @@ uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnec
|
|||||||
msg.target_temperature_step = traits.get_target_temperature_step();
|
msg.target_temperature_step = traits.get_target_temperature_step();
|
||||||
msg.supported_modes = &traits.get_supported_modes();
|
msg.supported_modes = &traits.get_supported_modes();
|
||||||
msg.supported_features = traits.get_feature_flags();
|
msg.supported_features = traits.get_feature_flags();
|
||||||
return fill_and_encode_entity_info(wh, msg, ListEntitiesWaterHeaterResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(wh, msg, ListEntitiesWaterHeaterResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
ENTITY_COMMAND_MAKE_CALL(water_heater::WaterHeater, water_heater, water_heater)
|
||||||
if (msg.has_fields & enums::WATER_HEATER_COMMAND_HAS_MODE)
|
if (msg.has_fields & enums::WATER_HEATER_COMMAND_HAS_MODE)
|
||||||
call.set_mode(static_cast<water_heater::WaterHeaterMode>(msg.mode));
|
call.set_mode(static_cast<water_heater::WaterHeaterMode>(msg.mode));
|
||||||
@@ -1348,18 +1411,20 @@ void APIConnection::send_event(event::Event *event) {
|
|||||||
event->get_last_event_type_index());
|
event->get_last_event_type_index());
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
|
uint16_t APIConnection::try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
|
||||||
uint32_t remaining_size) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
EventResponse resp;
|
EventResponse resp;
|
||||||
resp.event_type = event_type;
|
resp.event_type = event_type;
|
||||||
return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *event = static_cast<event::Event *>(entity);
|
auto *event = static_cast<event::Event *>(entity);
|
||||||
ListEntitiesEventResponse msg;
|
ListEntitiesEventResponse msg;
|
||||||
msg.device_class = event->get_device_class_ref();
|
msg.device_class = event->get_device_class_ref();
|
||||||
msg.event_types = &event->get_event_types();
|
msg.event_types = &event->get_event_types();
|
||||||
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1382,11 +1447,13 @@ void APIConnection::send_infrared_rf_receive_event(const InfraredRFReceiveEvent
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_INFRARED
|
#ifdef USE_INFRARED
|
||||||
uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *infrared = static_cast<infrared::Infrared *>(entity);
|
auto *infrared = static_cast<infrared::Infrared *>(entity);
|
||||||
ListEntitiesInfraredResponse msg;
|
ListEntitiesInfraredResponse msg;
|
||||||
msg.capabilities = infrared->get_capability_flags();
|
msg.capabilities = infrared->get_capability_flags();
|
||||||
return fill_and_encode_entity_info(infrared, msg, ListEntitiesInfraredResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(infrared, msg, ListEntitiesInfraredResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1394,7 +1461,8 @@ uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection
|
|||||||
bool APIConnection::send_update_state(update::UpdateEntity *update) {
|
bool APIConnection::send_update_state(update::UpdateEntity *update) {
|
||||||
return this->send_message_smart_(update, UpdateStateResponse::MESSAGE_TYPE, UpdateStateResponse::ESTIMATED_SIZE);
|
return this->send_message_smart_(update, UpdateStateResponse::MESSAGE_TYPE, UpdateStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *update = static_cast<update::UpdateEntity *>(entity);
|
auto *update = static_cast<update::UpdateEntity *>(entity);
|
||||||
UpdateStateResponse resp;
|
UpdateStateResponse resp;
|
||||||
resp.missing_state = !update->has_state();
|
resp.missing_state = !update->has_state();
|
||||||
@@ -1410,13 +1478,15 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
|
|||||||
resp.release_summary = StringRef(update->update_info.summary);
|
resp.release_summary = StringRef(update->update_info.summary);
|
||||||
resp.release_url = StringRef(update->update_info.release_url);
|
resp.release_url = StringRef(update->update_info.release_url);
|
||||||
}
|
}
|
||||||
return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
auto *update = static_cast<update::UpdateEntity *>(entity);
|
auto *update = static_cast<update::UpdateEntity *>(entity);
|
||||||
ListEntitiesUpdateResponse msg;
|
ListEntitiesUpdateResponse msg;
|
||||||
msg.device_class = update->get_device_class_ref();
|
msg.device_class = update->get_device_class_ref();
|
||||||
return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size);
|
return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
|
is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::update_command(const UpdateCommandRequest &msg) {
|
void APIConnection::update_command(const UpdateCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_GET(update::UpdateEntity, update, update)
|
ENTITY_COMMAND_GET(update::UpdateEntity, update, update)
|
||||||
@@ -1442,7 +1512,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
|
|||||||
SubscribeLogsResponse msg;
|
SubscribeLogsResponse msg;
|
||||||
msg.level = static_cast<enums::LogLevel>(level);
|
msg.level = static_cast<enums::LogLevel>(level);
|
||||||
msg.set_message(reinterpret_cast<const uint8_t *>(line), message_len);
|
msg.set_message(reinterpret_cast<const uint8_t *>(line), message_len);
|
||||||
return this->send_message_impl(msg, SubscribeLogsResponse::MESSAGE_TYPE);
|
return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIConnection::complete_authentication_() {
|
void APIConnection::complete_authentication_() {
|
||||||
@@ -1490,12 +1560,12 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
|||||||
return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
|
return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APIConnection::send_ping_response() {
|
bool APIConnection::send_ping_response(const PingRequest &msg) {
|
||||||
PingResponse resp;
|
PingResponse resp;
|
||||||
return this->send_message(resp, PingResponse::MESSAGE_TYPE);
|
return this->send_message(resp, PingResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APIConnection::send_device_info_response() {
|
bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
|
||||||
DeviceInfoResponse resp{};
|
DeviceInfoResponse resp{};
|
||||||
resp.name = StringRef(App.get_name());
|
resp.name = StringRef(App.get_name());
|
||||||
resp.friendly_name = StringRef(App.get_friendly_name());
|
resp.friendly_name = StringRef(App.get_friendly_name());
|
||||||
@@ -1745,7 +1815,9 @@ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryption
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void APIConnection::subscribe_home_assistant_states() { state_subs_at_ = 0; }
|
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
|
||||||
|
state_subs_at_ = 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
||||||
if (this->flags_.remove)
|
if (this->flags_.remove)
|
||||||
@@ -1765,14 +1837,6 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool APIConnection::send_message_impl(const ProtoMessage &msg, uint8_t message_type) {
|
|
||||||
ProtoSize size;
|
|
||||||
msg.calculate_size(size);
|
|
||||||
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
|
||||||
this->prepare_first_message_buffer(shared_buf, size.get_size());
|
|
||||||
msg.encode({&shared_buf});
|
|
||||||
return this->send_buffer({&shared_buf}, message_type);
|
|
||||||
}
|
|
||||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
|
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
|
||||||
const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);
|
const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);
|
||||||
|
|
||||||
@@ -1833,23 +1897,6 @@ void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, uint8_t me
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APIConnection::send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
|
||||||
uint8_t aux_data_index) {
|
|
||||||
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
|
|
||||||
auto &shared_buf = this->parent_->get_shared_buffer_ref();
|
|
||||||
this->prepare_first_message_buffer(shared_buf, estimated_size);
|
|
||||||
DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index};
|
|
||||||
if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) &&
|
|
||||||
this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type)) {
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
this->log_batch_item_(item);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APIConnection::schedule_batch_() {
|
bool APIConnection::schedule_batch_() {
|
||||||
if (!this->flags_.batch_scheduled) {
|
if (!this->flags_.batch_scheduled) {
|
||||||
this->flags_.batch_scheduled = true;
|
this->flags_.batch_scheduled = true;
|
||||||
@@ -1878,21 +1925,10 @@ void APIConnection::process_batch_() {
|
|||||||
auto &shared_buf = this->parent_->get_shared_buffer_ref();
|
auto &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||||
size_t num_items = this->deferred_batch_.size();
|
size_t num_items = this->deferred_batch_.size();
|
||||||
|
|
||||||
// Cache these values to avoid repeated virtual calls
|
// Fast path for single message - allocate exact size needed
|
||||||
const uint8_t header_padding = this->helper_->frame_header_padding();
|
|
||||||
const uint8_t footer_size = this->helper_->frame_footer_size();
|
|
||||||
|
|
||||||
// Pre-calculate exact buffer size needed based on message types
|
|
||||||
uint32_t total_estimated_size = num_items * (header_padding + footer_size);
|
|
||||||
for (size_t i = 0; i < num_items; i++) {
|
|
||||||
total_estimated_size += this->deferred_batch_[i].estimated_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size);
|
|
||||||
|
|
||||||
// Fast path for single message - buffer already allocated above
|
|
||||||
if (num_items == 1) {
|
if (num_items == 1) {
|
||||||
const auto &item = this->deferred_batch_[0];
|
const auto &item = this->deferred_batch_[0];
|
||||||
|
|
||||||
// Let dispatch_message_ calculate size and encode if it fits
|
// Let dispatch_message_ calculate size and encode if it fits
|
||||||
uint16_t payload_size = this->dispatch_message_(item, std::numeric_limits<uint16_t>::max(), true);
|
uint16_t payload_size = this->dispatch_message_(item, std::numeric_limits<uint16_t>::max(), true);
|
||||||
|
|
||||||
@@ -1915,8 +1951,30 @@ void APIConnection::process_batch_() {
|
|||||||
// Stack-allocated array for message info
|
// Stack-allocated array for message info
|
||||||
alignas(MessageInfo) char message_info_storage[MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo)];
|
alignas(MessageInfo) char message_info_storage[MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo)];
|
||||||
MessageInfo *message_info = reinterpret_cast<MessageInfo *>(message_info_storage);
|
MessageInfo *message_info = reinterpret_cast<MessageInfo *>(message_info_storage);
|
||||||
|
size_t message_count = 0;
|
||||||
|
|
||||||
|
// Cache these values to avoid repeated virtual calls
|
||||||
|
const uint8_t header_padding = this->helper_->frame_header_padding();
|
||||||
|
const uint8_t footer_size = this->helper_->frame_footer_size();
|
||||||
|
|
||||||
|
// Initialize buffer and tracking variables
|
||||||
|
shared_buf.clear();
|
||||||
|
|
||||||
|
// Pre-calculate exact buffer size needed based on message types
|
||||||
|
uint32_t total_estimated_size = num_items * (header_padding + footer_size);
|
||||||
|
for (size_t i = 0; i < this->deferred_batch_.size(); i++) {
|
||||||
|
const auto &item = this->deferred_batch_[i];
|
||||||
|
total_estimated_size += item.estimated_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total overhead for all messages
|
||||||
|
// Reserve based on estimated size (much more accurate than 24-byte worst-case)
|
||||||
|
shared_buf.reserve(total_estimated_size);
|
||||||
|
this->flags_.batch_first_message = true;
|
||||||
|
|
||||||
size_t items_processed = 0;
|
size_t items_processed = 0;
|
||||||
uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
|
uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
|
||||||
|
|
||||||
// Track where each message's header padding begins in the buffer
|
// Track where each message's header padding begins in the buffer
|
||||||
// For plaintext: this is where the 6-byte header padding starts
|
// For plaintext: this is where the 6-byte header padding starts
|
||||||
// For noise: this is where the 7-byte header padding starts
|
// For noise: this is where the 7-byte header padding starts
|
||||||
@@ -1928,7 +1986,7 @@ void APIConnection::process_batch_() {
|
|||||||
const auto &item = this->deferred_batch_[i];
|
const auto &item = this->deferred_batch_[i];
|
||||||
// Try to encode message via dispatch
|
// Try to encode message via dispatch
|
||||||
// The dispatch function calculates overhead to determine if the message fits
|
// The dispatch function calculates overhead to determine if the message fits
|
||||||
uint16_t payload_size = this->dispatch_message_(item, remaining_size, i == 0);
|
uint16_t payload_size = this->dispatch_message_(item, remaining_size, false);
|
||||||
|
|
||||||
if (payload_size == 0) {
|
if (payload_size == 0) {
|
||||||
// Message won't fit, stop processing
|
// Message won't fit, stop processing
|
||||||
@@ -1942,7 +2000,10 @@ void APIConnection::process_batch_() {
|
|||||||
// This avoids default-constructing all MAX_MESSAGES_PER_BATCH elements
|
// This avoids default-constructing all MAX_MESSAGES_PER_BATCH elements
|
||||||
// Explicit destruction is not needed because MessageInfo is trivially destructible,
|
// Explicit destruction is not needed because MessageInfo is trivially destructible,
|
||||||
// as ensured by the static_assert in its definition.
|
// as ensured by the static_assert in its definition.
|
||||||
new (&message_info[items_processed++]) MessageInfo(item.message_type, current_offset, proto_payload_size);
|
new (&message_info[message_count++]) MessageInfo(item.message_type, current_offset, proto_payload_size);
|
||||||
|
|
||||||
|
// Update tracking variables
|
||||||
|
items_processed++;
|
||||||
// After first message, set remaining size to MAX_BATCH_PACKET_SIZE to avoid fragmentation
|
// After first message, set remaining size to MAX_BATCH_PACKET_SIZE to avoid fragmentation
|
||||||
if (items_processed == 1) {
|
if (items_processed == 1) {
|
||||||
remaining_size = MAX_BATCH_PACKET_SIZE;
|
remaining_size = MAX_BATCH_PACKET_SIZE;
|
||||||
@@ -1965,7 +2026,7 @@ void APIConnection::process_batch_() {
|
|||||||
|
|
||||||
// Send all collected messages
|
// Send all collected messages
|
||||||
APIError err = this->helper_->write_protobuf_messages(ProtoWriteBuffer{&shared_buf},
|
APIError err = this->helper_->write_protobuf_messages(ProtoWriteBuffer{&shared_buf},
|
||||||
std::span<const MessageInfo>(message_info, items_processed));
|
std::span<const MessageInfo>(message_info, message_count));
|
||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||||
this->fatal_error_with_log_(LOG_STR("Batch write failed"), err);
|
this->fatal_error_with_log_(LOG_STR("Batch write failed"), err);
|
||||||
}
|
}
|
||||||
@@ -1994,8 +2055,7 @@ void APIConnection::process_batch_() {
|
|||||||
// Dispatch message encoding based on message_type
|
// Dispatch message encoding based on message_type
|
||||||
// Switch assigns function pointer, single call site for smaller code size
|
// Switch assigns function pointer, single call site for smaller code size
|
||||||
uint16_t APIConnection::dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size,
|
uint16_t APIConnection::dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size,
|
||||||
bool batch_first) {
|
bool is_single) {
|
||||||
this->flags_.batch_first_message = batch_first;
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
// Events need aux_data_index to look up event type from entity
|
// Events need aux_data_index to look up event type from entity
|
||||||
if (item.message_type == EventResponse::MESSAGE_TYPE) {
|
if (item.message_type == EventResponse::MESSAGE_TYPE) {
|
||||||
@@ -2004,7 +2064,7 @@ uint16_t APIConnection::dispatch_message_(const DeferredBatch::BatchItem &item,
|
|||||||
return 0;
|
return 0;
|
||||||
auto *event = static_cast<event::Event *>(item.entity);
|
auto *event = static_cast<event::Event *>(item.entity);
|
||||||
return try_send_event_response(event, StringRef::from_maybe_nullptr(event->get_event_type(item.aux_data_index)),
|
return try_send_event_response(event, StringRef::from_maybe_nullptr(event->get_event_type(item.aux_data_index)),
|
||||||
this, remaining_size);
|
this, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2114,22 +2174,25 @@ uint16_t APIConnection::dispatch_message_(const DeferredBatch::BatchItem &item,
|
|||||||
#undef CASE_STATE_INFO
|
#undef CASE_STATE_INFO
|
||||||
#undef CASE_INFO_ONLY
|
#undef CASE_INFO_ONLY
|
||||||
|
|
||||||
return func(item.entity, this, remaining_size);
|
return func(item.entity, this, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
ListEntitiesDoneResponse resp;
|
ListEntitiesDoneResponse resp;
|
||||||
return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size);
|
return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
DisconnectRequest req;
|
DisconnectRequest req;
|
||||||
return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size);
|
return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
PingRequest req;
|
PingRequest req;
|
||||||
return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size);
|
return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
#endif // USE_API_HOMEASSISTANT_SERVICES
|
#endif // USE_API_HOMEASSISTANT_SERVICES
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
void unsubscribe_bluetooth_le_advertisements() override;
|
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
|
|
||||||
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||||
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
|
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
|
||||||
@@ -136,7 +136,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
||||||
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
|
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
|
||||||
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
||||||
bool send_subscribe_bluetooth_connections_free_response() override;
|
bool send_subscribe_bluetooth_connections_free_response(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
||||||
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
|
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -170,7 +170,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
|
|
||||||
#ifdef USE_WATER_HEATER
|
#ifdef USE_WATER_HEATER
|
||||||
bool send_water_heater_state(water_heater::WaterHeater *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
|
#endif
|
||||||
|
|
||||||
#ifdef USE_IR_RF
|
#ifdef USE_IR_RF
|
||||||
@@ -187,8 +187,8 @@ class APIConnection final : public APIServerConnection {
|
|||||||
void update_command(const UpdateCommandRequest &msg) override;
|
void update_command(const UpdateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void on_disconnect_response() override;
|
void on_disconnect_response(const DisconnectResponse &value) override;
|
||||||
void on_ping_response() override {
|
void on_ping_response(const PingResponse &value) override {
|
||||||
// we initiated ping
|
// we initiated ping
|
||||||
this->flags_.sent_ping = false;
|
this->flags_.sent_ping = false;
|
||||||
}
|
}
|
||||||
@@ -199,11 +199,11 @@ class APIConnection final : public APIServerConnection {
|
|||||||
void on_get_time_response(const GetTimeResponse &value) override;
|
void on_get_time_response(const GetTimeResponse &value) override;
|
||||||
#endif
|
#endif
|
||||||
bool send_hello_response(const HelloRequest &msg) override;
|
bool send_hello_response(const HelloRequest &msg) override;
|
||||||
bool send_disconnect_response() override;
|
bool send_disconnect_response(const DisconnectRequest &msg) override;
|
||||||
bool send_ping_response() override;
|
bool send_ping_response(const PingRequest &msg) override;
|
||||||
bool send_device_info_response() override;
|
bool send_device_info_response(const DeviceInfoRequest &msg) override;
|
||||||
void list_entities() override { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
|
void list_entities(const ListEntitiesRequest &msg) override { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
|
||||||
void subscribe_states() override {
|
void subscribe_states(const SubscribeStatesRequest &msg) override {
|
||||||
this->flags_.state_subscription = true;
|
this->flags_.state_subscription = true;
|
||||||
// Start initial state iterator only if no iterator is active
|
// Start initial state iterator only if no iterator is active
|
||||||
// If list_entities is running, we'll start initial_state when it completes
|
// If list_entities is running, we'll start initial_state when it completes
|
||||||
@@ -217,10 +217,12 @@ class APIConnection final : public APIServerConnection {
|
|||||||
App.schedule_dump_config();
|
App.schedule_dump_config();
|
||||||
}
|
}
|
||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void subscribe_homeassistant_services() override { this->flags_.service_call_subscription = true; }
|
void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
|
||||||
|
this->flags_.service_call_subscription = true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void subscribe_home_assistant_states() override;
|
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||||
@@ -253,7 +255,17 @@ class APIConnection final : public APIServerConnection {
|
|||||||
|
|
||||||
void on_fatal_error() override;
|
void on_fatal_error() override;
|
||||||
void on_no_setup_connection() override;
|
void on_no_setup_connection() override;
|
||||||
bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) override;
|
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
|
||||||
|
// FIXME: ensure no recursive writes can happen
|
||||||
|
|
||||||
|
// Get header padding size - used for both reserve and insert
|
||||||
|
uint8_t header_padding = this->helper_->frame_header_padding();
|
||||||
|
// Get shared buffer from parent server
|
||||||
|
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||||
|
this->prepare_first_message_buffer(shared_buf, header_padding,
|
||||||
|
reserve_size + header_padding + this->helper_->frame_footer_size());
|
||||||
|
return {&shared_buf};
|
||||||
|
}
|
||||||
|
|
||||||
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
|
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
|
||||||
shared_buf.clear();
|
shared_buf.clear();
|
||||||
@@ -265,13 +277,6 @@ class APIConnection final : public APIServerConnection {
|
|||||||
shared_buf.resize(header_padding);
|
shared_buf.resize(header_padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience overload - computes frame overhead internally
|
|
||||||
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t payload_size) {
|
|
||||||
const uint8_t header_padding = this->helper_->frame_header_padding();
|
|
||||||
const uint8_t footer_size = this->helper_->frame_footer_size();
|
|
||||||
this->prepare_first_message_buffer(shared_buf, header_padding, payload_size + header_padding + footer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_to_clear_buffer(bool log_out_of_space);
|
bool try_to_clear_buffer(bool log_out_of_space);
|
||||||
bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override;
|
bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override;
|
||||||
|
|
||||||
@@ -293,21 +298,21 @@ class APIConnection final : public APIServerConnection {
|
|||||||
|
|
||||||
// Non-template helper to encode any ProtoMessage
|
// Non-template helper to encode any ProtoMessage
|
||||||
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn,
|
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn,
|
||||||
uint32_t remaining_size);
|
uint32_t remaining_size, bool is_single);
|
||||||
|
|
||||||
// Helper to fill entity state base and encode message
|
// Helper to fill entity state base and encode message
|
||||||
static uint16_t fill_and_encode_entity_state(EntityBase *entity, StateResponseProtoMessage &msg, uint8_t message_type,
|
static uint16_t fill_and_encode_entity_state(EntityBase *entity, StateResponseProtoMessage &msg, uint8_t message_type,
|
||||||
APIConnection *conn, uint32_t remaining_size) {
|
APIConnection *conn, uint32_t remaining_size, bool is_single) {
|
||||||
msg.key = entity->get_object_id_hash();
|
msg.key = entity->get_object_id_hash();
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
msg.device_id = entity->get_device_id();
|
msg.device_id = entity->get_device_id();
|
||||||
#endif
|
#endif
|
||||||
return encode_message_to_buffer(msg, message_type, conn, remaining_size);
|
return encode_message_to_buffer(msg, message_type, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to fill entity info base and encode message
|
// Helper to fill entity info base and encode message
|
||||||
static uint16_t fill_and_encode_entity_info(EntityBase *entity, InfoResponseProtoMessage &msg, uint8_t message_type,
|
static uint16_t fill_and_encode_entity_info(EntityBase *entity, InfoResponseProtoMessage &msg, uint8_t message_type,
|
||||||
APIConnection *conn, uint32_t remaining_size) {
|
APIConnection *conn, uint32_t remaining_size, bool is_single) {
|
||||||
// Set common fields that are shared by all entity types
|
// Set common fields that are shared by all entity types
|
||||||
msg.key = entity->get_object_id_hash();
|
msg.key = entity->get_object_id_hash();
|
||||||
|
|
||||||
@@ -334,7 +339,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
msg.device_id = entity->get_device_id();
|
msg.device_id = entity->get_device_id();
|
||||||
#endif
|
#endif
|
||||||
return encode_message_to_buffer(msg, message_type, conn, remaining_size);
|
return encode_message_to_buffer(msg, message_type, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
@@ -365,108 +370,141 @@ class APIConnection final : public APIServerConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_WATER_HEATER
|
#ifdef USE_WATER_HEATER
|
||||||
static uint16_t try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_INFRARED
|
#ifdef USE_INFRARED
|
||||||
static uint16_t try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
static uint16_t try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
|
static uint16_t try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
|
||||||
uint32_t remaining_size);
|
uint32_t remaining_size, bool is_single);
|
||||||
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
bool is_single);
|
||||||
|
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CAMERA
|
#ifdef USE_CAMERA
|
||||||
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Method for ListEntitiesDone batching
|
// Method for ListEntitiesDone batching
|
||||||
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
|
|
||||||
// Method for DisconnectRequest batching
|
// Method for DisconnectRequest batching
|
||||||
static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
|
|
||||||
// Batch message method for ping requests
|
// Batch message method for ping requests
|
||||||
static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
bool is_single);
|
||||||
|
|
||||||
// === Optimal member ordering for 32-bit systems ===
|
// === Optimal member ordering for 32-bit systems ===
|
||||||
|
|
||||||
@@ -501,7 +539,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Function pointer type for message encoding
|
// Function pointer type for message encoding
|
||||||
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size);
|
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
|
||||||
|
|
||||||
// Generic batching mechanism for both state updates and entity info
|
// Generic batching mechanism for both state updates and entity info
|
||||||
struct DeferredBatch {
|
struct DeferredBatch {
|
||||||
@@ -614,7 +652,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
|
|
||||||
// Dispatch message encoding based on message_type - replaces function pointer storage
|
// Dispatch message encoding based on message_type - replaces function pointer storage
|
||||||
// Switch assigns pointer, single call site for smaller code size
|
// Switch assigns pointer, single call site for smaller code size
|
||||||
uint16_t dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size, bool batch_first);
|
uint16_t dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size, bool is_single);
|
||||||
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void log_batch_item_(const DeferredBatch::BatchItem &item) {
|
void log_batch_item_(const DeferredBatch::BatchItem &item) {
|
||||||
@@ -646,7 +684,19 @@ class APIConnection final : public APIServerConnection {
|
|||||||
// Tries immediate send if should_send_immediately_() returns true and buffer has space
|
// Tries immediate send if should_send_immediately_() returns true and buffer has space
|
||||||
// Falls back to batching if immediate send fails or isn't applicable
|
// Falls back to batching if immediate send fails or isn't applicable
|
||||||
bool send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
bool send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||||
uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED);
|
uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED) {
|
||||||
|
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
|
||||||
|
DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index};
|
||||||
|
if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) &&
|
||||||
|
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
this->log_batch_item_(item);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to schedule a deferred message with known message type
|
// Helper function to schedule a deferred message with known message type
|
||||||
bool schedule_message_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
bool schedule_message_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||||
|
|||||||
@@ -23,8 +23,15 @@ static inline void append_field_prefix(DumpBuffer &out, const char *field_name,
|
|||||||
out.append(indent, ' ').append(field_name).append(": ");
|
out.append(indent, ' ').append(field_name).append(": ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void append_with_newline(DumpBuffer &out, const char *str) {
|
||||||
|
out.append(str);
|
||||||
|
out.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
static inline void append_uint(DumpBuffer &out, uint32_t value) {
|
static inline void append_uint(DumpBuffer &out, uint32_t value) {
|
||||||
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu32, value));
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%" PRIu32, value);
|
||||||
|
out.append(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RAII helper for message dump formatting
|
// RAII helper for message dump formatting
|
||||||
@@ -42,23 +49,31 @@ class MessageDumpHelper {
|
|||||||
|
|
||||||
// Helper functions to reduce code duplication in dump methods
|
// Helper functions to reduce code duplication in dump methods
|
||||||
static void dump_field(DumpBuffer &out, const char *field_name, int32_t value, int indent = 2) {
|
static void dump_field(DumpBuffer &out, const char *field_name, int32_t value, int indent = 2) {
|
||||||
|
char buffer[64];
|
||||||
append_field_prefix(out, field_name, indent);
|
append_field_prefix(out, field_name, indent);
|
||||||
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRId32 "\n", value));
|
snprintf(buffer, 64, "%" PRId32, value);
|
||||||
|
append_with_newline(out, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_field(DumpBuffer &out, const char *field_name, uint32_t value, int indent = 2) {
|
static void dump_field(DumpBuffer &out, const char *field_name, uint32_t value, int indent = 2) {
|
||||||
|
char buffer[64];
|
||||||
append_field_prefix(out, field_name, indent);
|
append_field_prefix(out, field_name, indent);
|
||||||
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu32 "\n", value));
|
snprintf(buffer, 64, "%" PRIu32, value);
|
||||||
|
append_with_newline(out, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_field(DumpBuffer &out, const char *field_name, float value, int indent = 2) {
|
static void dump_field(DumpBuffer &out, const char *field_name, float value, int indent = 2) {
|
||||||
|
char buffer[64];
|
||||||
append_field_prefix(out, field_name, indent);
|
append_field_prefix(out, field_name, indent);
|
||||||
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%g\n", value));
|
snprintf(buffer, 64, "%g", value);
|
||||||
|
append_with_newline(out, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_field(DumpBuffer &out, const char *field_name, uint64_t value, int indent = 2) {
|
static void dump_field(DumpBuffer &out, const char *field_name, uint64_t value, int indent = 2) {
|
||||||
|
char buffer[64];
|
||||||
append_field_prefix(out, field_name, indent);
|
append_field_prefix(out, field_name, indent);
|
||||||
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu64 "\n", value));
|
snprintf(buffer, 64, "%" PRIu64, value);
|
||||||
|
append_with_newline(out, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_field(DumpBuffer &out, const char *field_name, bool value, int indent = 2) {
|
static void dump_field(DumpBuffer &out, const char *field_name, bool value, int indent = 2) {
|
||||||
@@ -97,7 +112,7 @@ static void dump_bytes_field(DumpBuffer &out, const char *field_name, const uint
|
|||||||
char hex_buf[format_hex_pretty_size(160)];
|
char hex_buf[format_hex_pretty_size(160)];
|
||||||
append_field_prefix(out, field_name, indent);
|
append_field_prefix(out, field_name, indent);
|
||||||
format_hex_pretty_to(hex_buf, data, len);
|
format_hex_pretty_to(hex_buf, data, len);
|
||||||
out.append(hex_buf).append("\n");
|
append_with_newline(out, hex_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
|
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ void APIServerConnectionBase::log_receive_message_(const LogString *name, const
|
|||||||
DumpBuffer dump_buf;
|
DumpBuffer dump_buf;
|
||||||
ESP_LOGVV(TAG, "%s: %s", LOG_STR_ARG(name), msg.dump_to(dump_buf));
|
ESP_LOGVV(TAG, "%s: %s", LOG_STR_ARG(name), msg.dump_to(dump_buf));
|
||||||
}
|
}
|
||||||
void APIServerConnectionBase::log_receive_message_(const LogString *name) {
|
|
||||||
ESP_LOGVV(TAG, "%s: {}", LOG_STR_ARG(name));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
|
void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
|
||||||
@@ -32,52 +29,66 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DisconnectRequest::MESSAGE_TYPE: {
|
case DisconnectRequest::MESSAGE_TYPE: {
|
||||||
|
DisconnectRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_disconnect_request"));
|
this->log_receive_message_(LOG_STR("on_disconnect_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_disconnect_request();
|
this->on_disconnect_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DisconnectResponse::MESSAGE_TYPE: {
|
case DisconnectResponse::MESSAGE_TYPE: {
|
||||||
|
DisconnectResponse msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_disconnect_response"));
|
this->log_receive_message_(LOG_STR("on_disconnect_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_disconnect_response();
|
this->on_disconnect_response(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PingRequest::MESSAGE_TYPE: {
|
case PingRequest::MESSAGE_TYPE: {
|
||||||
|
PingRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_ping_request"));
|
this->log_receive_message_(LOG_STR("on_ping_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_ping_request();
|
this->on_ping_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PingResponse::MESSAGE_TYPE: {
|
case PingResponse::MESSAGE_TYPE: {
|
||||||
|
PingResponse msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_ping_response"));
|
this->log_receive_message_(LOG_STR("on_ping_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_ping_response();
|
this->on_ping_response(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DeviceInfoRequest::MESSAGE_TYPE: {
|
case DeviceInfoRequest::MESSAGE_TYPE: {
|
||||||
|
DeviceInfoRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_device_info_request"));
|
this->log_receive_message_(LOG_STR("on_device_info_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_device_info_request();
|
this->on_device_info_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ListEntitiesRequest::MESSAGE_TYPE: {
|
case ListEntitiesRequest::MESSAGE_TYPE: {
|
||||||
|
ListEntitiesRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_list_entities_request"));
|
this->log_receive_message_(LOG_STR("on_list_entities_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_list_entities_request();
|
this->on_list_entities_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SubscribeStatesRequest::MESSAGE_TYPE: {
|
case SubscribeStatesRequest::MESSAGE_TYPE: {
|
||||||
|
SubscribeStatesRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_subscribe_states_request"));
|
this->log_receive_message_(LOG_STR("on_subscribe_states_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_states_request();
|
this->on_subscribe_states_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SubscribeLogsRequest::MESSAGE_TYPE: {
|
case SubscribeLogsRequest::MESSAGE_TYPE: {
|
||||||
@@ -135,10 +146,12 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: {
|
case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: {
|
||||||
|
SubscribeHomeassistantServicesRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"));
|
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_homeassistant_services_request();
|
this->on_subscribe_homeassistant_services_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -153,10 +166,12 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
case SubscribeHomeAssistantStatesRequest::MESSAGE_TYPE: {
|
case SubscribeHomeAssistantStatesRequest::MESSAGE_TYPE: {
|
||||||
|
SubscribeHomeAssistantStatesRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"));
|
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_home_assistant_states_request();
|
this->on_subscribe_home_assistant_states_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -360,19 +375,23 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: {
|
case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: {
|
||||||
|
SubscribeBluetoothConnectionsFreeRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"));
|
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_bluetooth_connections_free_request();
|
this->on_subscribe_bluetooth_connections_free_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: {
|
case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: {
|
||||||
|
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
||||||
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"));
|
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_unsubscribe_bluetooth_le_advertisements_request();
|
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -628,29 +647,36 @@ void APIServerConnection::on_hello_request(const HelloRequest &msg) {
|
|||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_disconnect_request() {
|
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
|
||||||
if (!this->send_disconnect_response()) {
|
if (!this->send_disconnect_response(msg)) {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_ping_request() {
|
void APIServerConnection::on_ping_request(const PingRequest &msg) {
|
||||||
if (!this->send_ping_response()) {
|
if (!this->send_ping_response(msg)) {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_device_info_request() {
|
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
|
||||||
if (!this->send_device_info_response()) {
|
if (!this->send_device_info_response(msg)) {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_list_entities_request() { this->list_entities(); }
|
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) { this->list_entities(msg); }
|
||||||
void APIServerConnection::on_subscribe_states_request() { this->subscribe_states(); }
|
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
|
||||||
|
this->subscribe_states(msg);
|
||||||
|
}
|
||||||
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) { this->subscribe_logs(msg); }
|
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) { this->subscribe_logs(msg); }
|
||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void APIServerConnection::on_subscribe_homeassistant_services_request() { this->subscribe_homeassistant_services(); }
|
void APIServerConnection::on_subscribe_homeassistant_services_request(
|
||||||
|
const SubscribeHomeassistantServicesRequest &msg) {
|
||||||
|
this->subscribe_homeassistant_services(msg);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void APIServerConnection::on_subscribe_home_assistant_states_request() { this->subscribe_home_assistant_states(); }
|
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
|
||||||
|
this->subscribe_home_assistant_states(msg);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||||
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { this->execute_service(msg); }
|
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { this->execute_service(msg); }
|
||||||
@@ -720,11 +746,6 @@ void APIServerConnection::on_update_command_request(const UpdateCommandRequest &
|
|||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { this->valve_command(msg); }
|
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { this->valve_command(msg); }
|
||||||
#endif
|
#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
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
@@ -767,15 +788,17 @@ void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNo
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_connections_free_request() {
|
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
|
||||||
if (!this->send_subscribe_bluetooth_connections_free_response()) {
|
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
||||||
|
if (!this->send_subscribe_bluetooth_connections_free_response(msg)) {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request() {
|
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
|
||||||
this->unsubscribe_bluetooth_le_advertisements();
|
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
|
this->unsubscribe_bluetooth_le_advertisements(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
protected:
|
protected:
|
||||||
void log_send_message_(const char *name, const char *dump);
|
void log_send_message_(const char *name, const char *dump);
|
||||||
void log_receive_message_(const LogString *name, const ProtoMessage &msg);
|
void log_receive_message_(const LogString *name, const ProtoMessage &msg);
|
||||||
void log_receive_message_(const LogString *name);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#endif
|
#endif
|
||||||
@@ -24,20 +23,20 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
DumpBuffer dump_buf;
|
DumpBuffer dump_buf;
|
||||||
this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
|
this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
|
||||||
#endif
|
#endif
|
||||||
return this->send_message_impl(msg, message_type);
|
return this->send_message_(msg, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void on_hello_request(const HelloRequest &value){};
|
virtual void on_hello_request(const HelloRequest &value){};
|
||||||
|
|
||||||
virtual void on_disconnect_request(){};
|
virtual void on_disconnect_request(const DisconnectRequest &value){};
|
||||||
virtual void on_disconnect_response(){};
|
virtual void on_disconnect_response(const DisconnectResponse &value){};
|
||||||
virtual void on_ping_request(){};
|
virtual void on_ping_request(const PingRequest &value){};
|
||||||
virtual void on_ping_response(){};
|
virtual void on_ping_response(const PingResponse &value){};
|
||||||
virtual void on_device_info_request(){};
|
virtual void on_device_info_request(const DeviceInfoRequest &value){};
|
||||||
|
|
||||||
virtual void on_list_entities_request(){};
|
virtual void on_list_entities_request(const ListEntitiesRequest &value){};
|
||||||
|
|
||||||
virtual void on_subscribe_states_request(){};
|
virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
|
||||||
|
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
virtual void on_cover_command_request(const CoverCommandRequest &value){};
|
virtual void on_cover_command_request(const CoverCommandRequest &value){};
|
||||||
@@ -62,14 +61,14 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
virtual void on_subscribe_homeassistant_services_request(){};
|
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||||
virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){};
|
virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
virtual void on_subscribe_home_assistant_states_request(){};
|
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
@@ -148,11 +147,12 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void on_subscribe_bluetooth_connections_free_request(){};
|
virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void on_unsubscribe_bluetooth_le_advertisements_request(){};
|
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
|
||||||
|
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
@@ -231,17 +231,17 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
class APIServerConnection : public APIServerConnectionBase {
|
class APIServerConnection : public APIServerConnectionBase {
|
||||||
public:
|
public:
|
||||||
virtual bool send_hello_response(const HelloRequest &msg) = 0;
|
virtual bool send_hello_response(const HelloRequest &msg) = 0;
|
||||||
virtual bool send_disconnect_response() = 0;
|
virtual bool send_disconnect_response(const DisconnectRequest &msg) = 0;
|
||||||
virtual bool send_ping_response() = 0;
|
virtual bool send_ping_response(const PingRequest &msg) = 0;
|
||||||
virtual bool send_device_info_response() = 0;
|
virtual bool send_device_info_response(const DeviceInfoRequest &msg) = 0;
|
||||||
virtual void list_entities() = 0;
|
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
|
||||||
virtual void subscribe_states() = 0;
|
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
|
||||||
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
|
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
|
||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
virtual void subscribe_homeassistant_services() = 0;
|
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
virtual void subscribe_home_assistant_states() = 0;
|
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||||
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
||||||
@@ -303,9 +303,6 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
virtual void valve_command(const ValveCommandRequest &msg) = 0;
|
virtual void valve_command(const ValveCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_WATER_HEATER
|
|
||||||
virtual void water_heater_command(const WaterHeaterCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -331,10 +328,11 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
|
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual bool send_subscribe_bluetooth_connections_free_response() = 0;
|
virtual bool send_subscribe_bluetooth_connections_free_response(
|
||||||
|
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void unsubscribe_bluetooth_le_advertisements() = 0;
|
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
|
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
|
||||||
@@ -362,17 +360,17 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
void on_hello_request(const HelloRequest &msg) override;
|
void on_hello_request(const HelloRequest &msg) override;
|
||||||
void on_disconnect_request() override;
|
void on_disconnect_request(const DisconnectRequest &msg) override;
|
||||||
void on_ping_request() override;
|
void on_ping_request(const PingRequest &msg) override;
|
||||||
void on_device_info_request() override;
|
void on_device_info_request(const DeviceInfoRequest &msg) override;
|
||||||
void on_list_entities_request() override;
|
void on_list_entities_request(const ListEntitiesRequest &msg) override;
|
||||||
void on_subscribe_states_request() override;
|
void on_subscribe_states_request(const SubscribeStatesRequest &msg) override;
|
||||||
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
|
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
|
||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void on_subscribe_homeassistant_services_request() override;
|
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void on_subscribe_home_assistant_states_request() override;
|
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||||
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
||||||
@@ -434,9 +432,6 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_WATER_HEATER
|
|
||||||
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
@@ -462,10 +457,11 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
|
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_subscribe_bluetooth_connections_free_request() override;
|
void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_unsubscribe_bluetooth_le_advertisements_request() override;
|
void on_unsubscribe_bluetooth_le_advertisements_request(
|
||||||
|
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
|
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ void APIServer::loop() {
|
|||||||
|
|
||||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
// Fire trigger after client is removed so api.connected reflects the true state
|
// Fire trigger after client is removed so api.connected reflects the true state
|
||||||
this->client_disconnected_trigger_.trigger(client_name, client_peername);
|
this->client_disconnected_trigger_->trigger(client_name, client_peername);
|
||||||
#endif
|
#endif
|
||||||
// Don't increment client_index since we need to process the swapped element
|
// Don't increment client_index since we need to process the swapped element
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,10 +227,12 @@ class APIServer : public Component,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||||
Trigger<std::string, std::string> *get_client_connected_trigger() { return &this->client_connected_trigger_; }
|
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
Trigger<std::string, std::string> *get_client_disconnected_trigger() { return &this->client_disconnected_trigger_; }
|
Trigger<std::string, std::string> *get_client_disconnected_trigger() const {
|
||||||
|
return this->client_disconnected_trigger_;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -251,10 +253,10 @@ class APIServer : public Component,
|
|||||||
// Pointers and pointer-like types first (4 bytes each)
|
// Pointers and pointer-like types first (4 bytes each)
|
||||||
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
||||||
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||||
Trigger<std::string, std::string> client_connected_trigger_;
|
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
Trigger<std::string, std::string> client_disconnected_trigger_;
|
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 4-byte aligned types
|
// 4-byte aligned types
|
||||||
|
|||||||
@@ -136,10 +136,12 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
void set_wants_response() { this->flags_.wants_response = true; }
|
void set_wants_response() { this->flags_.wants_response = true; }
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||||
Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() { return &this->success_trigger_with_response_; }
|
Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() const {
|
||||||
|
return this->success_trigger_with_response_;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
Trigger<Ts...> *get_success_trigger() { return &this->success_trigger_; }
|
Trigger<Ts...> *get_success_trigger() const { return this->success_trigger_; }
|
||||||
Trigger<std::string, Ts...> *get_error_trigger() { return &this->error_trigger_; }
|
Trigger<std::string, Ts...> *get_error_trigger() const { return this->error_trigger_; }
|
||||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||||
|
|
||||||
void play(const Ts &...x) override {
|
void play(const Ts &...x) override {
|
||||||
@@ -185,14 +187,14 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
if (response.is_success()) {
|
if (response.is_success()) {
|
||||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||||
if (this->flags_.wants_response) {
|
if (this->flags_.wants_response) {
|
||||||
this->success_trigger_with_response_.trigger(response.get_json(), args...);
|
this->success_trigger_with_response_->trigger(response.get_json(), args...);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
this->success_trigger_.trigger(args...);
|
this->success_trigger_->trigger(args...);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this->error_trigger_.trigger(response.get_error_message(), args...);
|
this->error_trigger_->trigger(response.get_error_message(), args...);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
captured_args);
|
captured_args);
|
||||||
@@ -249,10 +251,10 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||||
TemplatableStringValue<Ts...> response_template_{""};
|
TemplatableStringValue<Ts...> response_template_{""};
|
||||||
Trigger<JsonObjectConst, Ts...> success_trigger_with_response_;
|
Trigger<JsonObjectConst, Ts...> *success_trigger_with_response_ = new Trigger<JsonObjectConst, Ts...>();
|
||||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||||
Trigger<Ts...> success_trigger_;
|
Trigger<Ts...> *success_trigger_ = new Trigger<Ts...>();
|
||||||
Trigger<std::string, Ts...> error_trigger_;
|
Trigger<std::string, Ts...> *error_trigger_ = new Trigger<std::string, Ts...>();
|
||||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||||
|
|
||||||
struct Flags {
|
struct Flags {
|
||||||
|
|||||||
@@ -112,12 +112,8 @@ class ProtoVarInt {
|
|||||||
uint64_t result = buffer[0] & 0x7F;
|
uint64_t result = buffer[0] & 0x7F;
|
||||||
uint8_t bitpos = 7;
|
uint8_t bitpos = 7;
|
||||||
|
|
||||||
// A 64-bit varint is at most 10 bytes (ceil(64/7)). Reject overlong encodings
|
|
||||||
// to avoid undefined behavior from shifting uint64_t by >= 64 bits.
|
|
||||||
uint32_t max_len = std::min(len, uint32_t(10));
|
|
||||||
|
|
||||||
// Start from the second byte since we've already processed the first
|
// Start from the second byte since we've already processed the first
|
||||||
for (uint32_t i = 1; i < max_len; i++) {
|
for (uint32_t i = 1; i < len; i++) {
|
||||||
uint8_t val = buffer[i];
|
uint8_t val = buffer[i];
|
||||||
result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
|
result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
|
||||||
bitpos += 7;
|
bitpos += 7;
|
||||||
@@ -406,20 +402,6 @@ class DumpBuffer {
|
|||||||
const char *c_str() const { return buf_; }
|
const char *c_str() const { return buf_; }
|
||||||
size_t size() const { return pos_; }
|
size_t size() const { return pos_; }
|
||||||
|
|
||||||
/// Get writable buffer pointer for use with buf_append_printf
|
|
||||||
char *data() { return buf_; }
|
|
||||||
/// Get current position for use with buf_append_printf
|
|
||||||
size_t pos() const { return pos_; }
|
|
||||||
/// Update position after buf_append_printf call
|
|
||||||
void set_pos(size_t pos) {
|
|
||||||
if (pos >= CAPACITY) {
|
|
||||||
pos_ = CAPACITY - 1;
|
|
||||||
} else {
|
|
||||||
pos_ = pos;
|
|
||||||
}
|
|
||||||
buf_[pos_] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void append_impl_(const char *str, size_t len) {
|
void append_impl_(const char *str, size_t len) {
|
||||||
size_t space = CAPACITY - 1 - pos_;
|
size_t space = CAPACITY - 1 - pos_;
|
||||||
@@ -961,16 +943,32 @@ class ProtoService {
|
|||||||
virtual bool is_connection_setup() = 0;
|
virtual bool is_connection_setup() = 0;
|
||||||
virtual void on_fatal_error() = 0;
|
virtual void on_fatal_error() = 0;
|
||||||
virtual void on_no_setup_connection() = 0;
|
virtual void on_no_setup_connection() = 0;
|
||||||
|
/**
|
||||||
|
* Create a buffer with a reserved size.
|
||||||
|
* @param reserve_size The number of bytes to pre-allocate in the buffer. This is a hint
|
||||||
|
* to optimize memory usage and avoid reallocations during encoding.
|
||||||
|
* Implementations should aim to allocate at least this size.
|
||||||
|
* @return A ProtoWriteBuffer object with the reserved size.
|
||||||
|
*/
|
||||||
|
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
|
||||||
virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
|
virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
|
||||||
virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
|
virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
|
||||||
/**
|
|
||||||
* Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending.
|
// Optimized method that pre-allocates buffer based on message size
|
||||||
* This is the implementation method - callers should use send_message() which adds logging.
|
bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
|
||||||
* @param msg The protobuf message to send.
|
ProtoSize size;
|
||||||
* @param message_type The message type identifier.
|
msg.calculate_size(size);
|
||||||
* @return True if the message was sent successfully, false otherwise.
|
uint32_t msg_size = size.get_size();
|
||||||
*/
|
|
||||||
virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0;
|
// Create a pre-sized buffer
|
||||||
|
auto buffer = this->create_buffer(msg_size);
|
||||||
|
|
||||||
|
// Encode message into the buffer
|
||||||
|
msg.encode(buffer);
|
||||||
|
|
||||||
|
// Send the buffer
|
||||||
|
return this->send_buffer(buffer, message_type);
|
||||||
|
}
|
||||||
|
|
||||||
// Authentication helper methods
|
// Authentication helper methods
|
||||||
inline bool check_connection_setup_() {
|
inline bool check_connection_setup_() {
|
||||||
|
|||||||
@@ -264,9 +264,9 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
|
|||||||
// Build and send JSON response
|
// Build and send JSON response
|
||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
this->json_builder_(x..., builder.root());
|
this->json_builder_(x..., builder.root());
|
||||||
std::string json_str = builder.serialize();
|
auto json_buf = builder.serialize();
|
||||||
this->parent_->send_action_response(call_id, success, StringRef(error_message),
|
this->parent_->send_action_response(call_id, success, StringRef(error_message),
|
||||||
reinterpret_cast<const uint8_t *>(json_str.data()), json_str.size());
|
reinterpret_cast<const uint8_t *>(json_buf.data()), json_buf.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class AQISensor : public sensor::Sensor, public Component {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
void set_pm_2_5_sensor(sensor::Sensor *sensor) { this->pm_2_5_sensor_ = sensor; }
|
void set_pm_2_5_sensor(sensor::Sensor *sensor) { this->pm_2_5_sensor_ = sensor; }
|
||||||
void set_pm_10_0_sensor(sensor::Sensor *sensor) { this->pm_10_0_sensor_ = sensor; }
|
void set_pm_10_0_sensor(sensor::Sensor *sensor) { this->pm_10_0_sensor_ = sensor; }
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ void AS3935Component::dump_config() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void AS3935Component::loop() {
|
void AS3935Component::loop() {
|
||||||
if (!this->irq_pin_->digital_read())
|
if (!this->irq_pin_->digital_read())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class AS3935Component : public Component {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
|
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ static const uint8_t REGISTER_STATUS = 0x0B; // 8 bytes / R
|
|||||||
static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R
|
static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R
|
||||||
static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R
|
static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R
|
||||||
|
|
||||||
|
float AS5600Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void AS5600Sensor::dump_config() {
|
void AS5600Sensor::dump_config() {
|
||||||
LOG_SENSOR("", "AS5600 Sensor", this);
|
LOG_SENSOR("", "AS5600 Sensor", this);
|
||||||
ESP_LOGCONFIG(TAG, " Out of Range Mode: %u", this->out_of_range_mode_);
|
ESP_LOGCONFIG(TAG, " Out of Range Mode: %u", this->out_of_range_mode_);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class AS5600Sensor : public PollingComponent, public Parented<AS5600Component>,
|
|||||||
public:
|
public:
|
||||||
void update() override;
|
void update() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
void set_angle_sensor(sensor::Sensor *angle_sensor) { this->angle_sensor_ = angle_sensor; }
|
void set_angle_sensor(sensor::Sensor *angle_sensor) { this->angle_sensor_ = angle_sensor; }
|
||||||
void set_raw_angle_sensor(sensor::Sensor *raw_angle_sensor) { this->raw_angle_sensor_ = raw_angle_sensor; }
|
void set_raw_angle_sensor(sensor::Sensor *raw_angle_sensor) { this->raw_angle_sensor_ = raw_angle_sensor; }
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ void AS7341Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "NIR", this->nir_);
|
LOG_SENSOR(" ", "NIR", this->nir_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AS7341Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void AS7341Component::update() {
|
void AS7341Component::update() {
|
||||||
this->read_channels(this->channel_readings_);
|
this->read_channels(this->channel_readings_);
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class AS7341Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_f1_sensor(sensor::Sensor *f1_sensor) { this->f1_ = f1_sensor; }
|
void set_f1_sensor(sensor::Sensor *f1_sensor) { this->f1_ = f1_sensor; }
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ void ATM90E26Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Active Reverse Energy A", this->reverse_active_energy_sensor_);
|
LOG_SENSOR(" ", "Active Reverse Energy A", this->reverse_active_energy_sensor_);
|
||||||
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
||||||
}
|
}
|
||||||
|
float ATM90E26Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
uint16_t ATM90E26Component::read16_(uint8_t a_register) {
|
uint16_t ATM90E26Component::read16_(uint8_t a_register) {
|
||||||
uint8_t data[2];
|
uint8_t data[2];
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class ATM90E26Component : public PollingComponent,
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_voltage_sensor(sensor::Sensor *obj) { this->voltage_sensor_ = obj; }
|
void set_voltage_sensor(sensor::Sensor *obj) { this->voltage_sensor_ = obj; }
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ namespace bang_bang {
|
|||||||
|
|
||||||
static const char *const TAG = "bang_bang.climate";
|
static const char *const TAG = "bang_bang.climate";
|
||||||
|
|
||||||
BangBangClimate::BangBangClimate() = default;
|
BangBangClimate::BangBangClimate()
|
||||||
|
: idle_trigger_(new Trigger<>()), cool_trigger_(new Trigger<>()), heat_trigger_(new Trigger<>()) {}
|
||||||
|
|
||||||
void BangBangClimate::setup() {
|
void BangBangClimate::setup() {
|
||||||
this->sensor_->add_on_state_callback([this](float state) {
|
this->sensor_->add_on_state_callback([this](float state) {
|
||||||
@@ -159,13 +160,13 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
|
|||||||
switch (action) {
|
switch (action) {
|
||||||
case climate::CLIMATE_ACTION_OFF:
|
case climate::CLIMATE_ACTION_OFF:
|
||||||
case climate::CLIMATE_ACTION_IDLE:
|
case climate::CLIMATE_ACTION_IDLE:
|
||||||
trig = &this->idle_trigger_;
|
trig = this->idle_trigger_;
|
||||||
break;
|
break;
|
||||||
case climate::CLIMATE_ACTION_COOLING:
|
case climate::CLIMATE_ACTION_COOLING:
|
||||||
trig = &this->cool_trigger_;
|
trig = this->cool_trigger_;
|
||||||
break;
|
break;
|
||||||
case climate::CLIMATE_ACTION_HEATING:
|
case climate::CLIMATE_ACTION_HEATING:
|
||||||
trig = &this->heat_trigger_;
|
trig = this->heat_trigger_;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
trig = nullptr;
|
trig = nullptr;
|
||||||
@@ -203,9 +204,9 @@ void BangBangClimate::set_away_config(const BangBangClimateTargetTempConfig &awa
|
|||||||
void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
|
void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
|
||||||
void BangBangClimate::set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
void BangBangClimate::set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
||||||
|
|
||||||
Trigger<> *BangBangClimate::get_idle_trigger() { return &this->idle_trigger_; }
|
Trigger<> *BangBangClimate::get_idle_trigger() const { return this->idle_trigger_; }
|
||||||
Trigger<> *BangBangClimate::get_cool_trigger() { return &this->cool_trigger_; }
|
Trigger<> *BangBangClimate::get_cool_trigger() const { return this->cool_trigger_; }
|
||||||
Trigger<> *BangBangClimate::get_heat_trigger() { return &this->heat_trigger_; }
|
Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger_; }
|
||||||
|
|
||||||
void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
|
void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
|
||||||
void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
|
void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ class BangBangClimate : public climate::Climate, public Component {
|
|||||||
void set_normal_config(const BangBangClimateTargetTempConfig &normal_config);
|
void set_normal_config(const BangBangClimateTargetTempConfig &normal_config);
|
||||||
void set_away_config(const BangBangClimateTargetTempConfig &away_config);
|
void set_away_config(const BangBangClimateTargetTempConfig &away_config);
|
||||||
|
|
||||||
Trigger<> *get_idle_trigger();
|
Trigger<> *get_idle_trigger() const;
|
||||||
Trigger<> *get_cool_trigger();
|
Trigger<> *get_cool_trigger() const;
|
||||||
Trigger<> *get_heat_trigger();
|
Trigger<> *get_heat_trigger() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Override control to change settings of the climate device.
|
/// Override control to change settings of the climate device.
|
||||||
@@ -57,13 +57,17 @@ class BangBangClimate : public climate::Climate, public Component {
|
|||||||
*
|
*
|
||||||
* In idle mode, the controller is assumed to have both heating and cooling disabled.
|
* In idle mode, the controller is assumed to have both heating and cooling disabled.
|
||||||
*/
|
*/
|
||||||
Trigger<> idle_trigger_;
|
Trigger<> *idle_trigger_{nullptr};
|
||||||
/** The trigger to call when the controller should switch to cooling mode.
|
/** The trigger to call when the controller should switch to cooling mode.
|
||||||
*/
|
*/
|
||||||
Trigger<> cool_trigger_;
|
Trigger<> *cool_trigger_{nullptr};
|
||||||
/** The trigger to call when the controller should switch to heating mode.
|
/** The trigger to call when the controller should switch to heating mode.
|
||||||
|
*
|
||||||
|
* A null value for this attribute means that the controller has no heating action
|
||||||
|
* For example window blinds, where only cooling (blinds closed) and not-cooling
|
||||||
|
* (blinds open) is possible.
|
||||||
*/
|
*/
|
||||||
Trigger<> heat_trigger_;
|
Trigger<> *heat_trigger_{nullptr};
|
||||||
/** A reference to the trigger that was previously active.
|
/** A reference to the trigger that was previously active.
|
||||||
*
|
*
|
||||||
* This is so that the previous trigger can be stopped before enabling a new one.
|
* This is so that the previous trigger can be stopped before enabling a new one.
|
||||||
|
|||||||
@@ -265,4 +265,6 @@ void BH1750Sensor::fail_and_reset_() {
|
|||||||
this->state_ = IDLE;
|
this->state_ = IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
} // namespace esphome::bh1750
|
} // namespace esphome::bh1750
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// State machine states
|
// State machine states
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ void BME280Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->humidity_oversampling_));
|
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->humidity_oversampling_));
|
||||||
}
|
}
|
||||||
|
float BME280Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }
|
inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class BME280Component : public PollingComponent {
|
|||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -233,6 +233,8 @@ void BME680Component::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BME680Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void BME680Component::update() {
|
void BME680Component::update() {
|
||||||
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
|
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
|
||||||
meas_control |= (this->temperature_oversampling_ & 0b111) << 5;
|
meas_control |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -89,9 +89,8 @@ async def to_code(config):
|
|||||||
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
|
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Although this component does not use SPI/Wire directly, the BSEC library requires them
|
# Although this component does not use SPI, the BSEC library requires the SPI library
|
||||||
cg.add_library("SPI", None)
|
cg.add_library("SPI", None)
|
||||||
cg.add_library("Wire", None)
|
|
||||||
|
|
||||||
cg.add_define("USE_BSEC")
|
cg.add_define("USE_BSEC")
|
||||||
cg.add_library("boschsensortec/BSEC Software Library", "1.6.1480")
|
cg.add_library("boschsensortec/BSEC Software Library", "1.6.1480")
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ void BME680BSECComponent::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Breath VOC Equivalent", this->breath_voc_equivalent_sensor_);
|
LOG_SENSOR(" ", "Breath VOC Equivalent", this->breath_voc_equivalent_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BME680BSECComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void BME680BSECComponent::loop() {
|
void BME680BSECComponent::loop() {
|
||||||
this->run_();
|
this->run_();
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
|
|||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ void BME68xBSEC2Component::dump_config() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BME68xBSEC2Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void BME68xBSEC2Component::loop() {
|
void BME68xBSEC2Component::loop() {
|
||||||
this->run_();
|
this->run_();
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class BME68xBSEC2Component : public Component {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void set_algorithm_output(AlgorithmOutput algorithm_output) { this->algorithm_output_ = algorithm_output; }
|
void set_algorithm_output(AlgorithmOutput algorithm_output) { this->algorithm_output_ = algorithm_output; }
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ void BMI160Component::update() {
|
|||||||
|
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
}
|
}
|
||||||
|
float BMI160Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
} // namespace bmi160
|
} // namespace bmi160
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class BMI160Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
void set_accel_x_sensor(sensor::Sensor *accel_x_sensor) { accel_x_sensor_ = accel_x_sensor; }
|
void set_accel_x_sensor(sensor::Sensor *accel_x_sensor) { accel_x_sensor_ = accel_x_sensor; }
|
||||||
void set_accel_y_sensor(sensor::Sensor *accel_y_sensor) { accel_y_sensor_ = accel_y_sensor; }
|
void set_accel_y_sensor(sensor::Sensor *accel_y_sensor) { accel_y_sensor_ = accel_y_sensor; }
|
||||||
void set_accel_z_sensor(sensor::Sensor *accel_z_sensor) { accel_z_sensor_ = accel_z_sensor; }
|
void set_accel_z_sensor(sensor::Sensor *accel_z_sensor) { accel_z_sensor_ = accel_z_sensor; }
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ bool BMP085Component::set_mode_(uint8_t mode) {
|
|||||||
ESP_LOGV(TAG, "Setting mode to 0x%02X", mode);
|
ESP_LOGV(TAG, "Setting mode to 0x%02X", mode);
|
||||||
return this->write_byte(BMP085_REGISTER_CONTROL, mode);
|
return this->write_byte(BMP085_REGISTER_CONTROL, mode);
|
||||||
}
|
}
|
||||||
|
float BMP085Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
} // namespace bmp085
|
} // namespace bmp085
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ class BMP085Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct CalibrationData {
|
struct CalibrationData {
|
||||||
int16_t ac1, ac2, ac3;
|
int16_t ac1, ac2, ac3;
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ void BMP280Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
|
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
|
||||||
}
|
}
|
||||||
|
float BMP280Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }
|
inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class BMP280Component : public PollingComponent {
|
|||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ void BMP3XXComponent::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
|
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
float BMP3XXComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
|
inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class BMP3XXComponent : public PollingComponent {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ void CC1101Component::call_listeners_(const std::vector<uint8_t> &packet, float
|
|||||||
for (auto &listener : this->listeners_) {
|
for (auto &listener : this->listeners_) {
|
||||||
listener->on_packet(packet, freq_offset, rssi, lqi);
|
listener->on_packet(packet, freq_offset, rssi, lqi);
|
||||||
}
|
}
|
||||||
this->packet_trigger_.trigger(packet, freq_offset, rssi, lqi);
|
this->packet_trigger_->trigger(packet, freq_offset, rssi, lqi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CC1101Component::loop() {
|
void CC1101Component::loop() {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class CC1101Component : public Component,
|
|||||||
// Packet mode operations
|
// Packet mode operations
|
||||||
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
|
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
|
||||||
void register_listener(CC1101Listener *listener) { this->listeners_.push_back(listener); }
|
void register_listener(CC1101Listener *listener) { this->listeners_.push_back(listener); }
|
||||||
Trigger<std::vector<uint8_t>, float, float, uint8_t> *get_packet_trigger() { return &this->packet_trigger_; }
|
Trigger<std::vector<uint8_t>, float, float, uint8_t> *get_packet_trigger() const { return this->packet_trigger_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t chip_id_{0};
|
uint16_t chip_id_{0};
|
||||||
@@ -96,7 +96,8 @@ class CC1101Component : public Component,
|
|||||||
|
|
||||||
// Packet handling
|
// Packet handling
|
||||||
void call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi);
|
void call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi);
|
||||||
Trigger<std::vector<uint8_t>, float, float, uint8_t> packet_trigger_;
|
Trigger<std::vector<uint8_t>, float, float, uint8_t> *packet_trigger_{
|
||||||
|
new Trigger<std::vector<uint8_t>, float, float, uint8_t>()};
|
||||||
std::vector<uint8_t> packet_;
|
std::vector<uint8_t> packet_;
|
||||||
std::vector<CC1101Listener *> listeners_;
|
std::vector<CC1101Listener *> listeners_;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ namespace cd74hc4067 {
|
|||||||
|
|
||||||
static const char *const TAG = "cd74hc4067";
|
static const char *const TAG = "cd74hc4067";
|
||||||
|
|
||||||
|
float CD74HC4067Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void CD74HC4067Component::setup() {
|
void CD74HC4067Component::setup() {
|
||||||
this->pin_s0_->setup();
|
this->pin_s0_->setup();
|
||||||
this->pin_s1_->setup();
|
this->pin_s1_->setup();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class CD74HC4067Component : public Component {
|
|||||||
/// Set up the internal sensor array.
|
/// Set up the internal sensor array.
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
/// setting pin active by setting the right combination of the four multiplexer input pins
|
/// setting pin active by setting the right combination of the four multiplexer input pins
|
||||||
void activate_pin(uint8_t pin);
|
void activate_pin(uint8_t pin);
|
||||||
|
|||||||
@@ -1,44 +1,109 @@
|
|||||||
#include "climate_mode.h"
|
#include "climate_mode.h"
|
||||||
#include "esphome/core/progmem.h"
|
|
||||||
|
|
||||||
namespace esphome::climate {
|
namespace esphome::climate {
|
||||||
|
|
||||||
// Climate mode strings indexed by ClimateMode enum (0-6): OFF, HEAT_COOL, COOL, HEAT, FAN_ONLY, DRY, AUTO
|
|
||||||
PROGMEM_STRING_TABLE(ClimateModeStrings, "OFF", "HEAT_COOL", "COOL", "HEAT", "FAN_ONLY", "DRY", "AUTO", "UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *climate_mode_to_string(ClimateMode mode) {
|
const LogString *climate_mode_to_string(ClimateMode mode) {
|
||||||
return ClimateModeStrings::get_log_str(static_cast<uint8_t>(mode), ClimateModeStrings::LAST_INDEX);
|
switch (mode) {
|
||||||
|
case CLIMATE_MODE_OFF:
|
||||||
|
return LOG_STR("OFF");
|
||||||
|
case CLIMATE_MODE_HEAT_COOL:
|
||||||
|
return LOG_STR("HEAT_COOL");
|
||||||
|
case CLIMATE_MODE_AUTO:
|
||||||
|
return LOG_STR("AUTO");
|
||||||
|
case CLIMATE_MODE_COOL:
|
||||||
|
return LOG_STR("COOL");
|
||||||
|
case CLIMATE_MODE_HEAT:
|
||||||
|
return LOG_STR("HEAT");
|
||||||
|
case CLIMATE_MODE_FAN_ONLY:
|
||||||
|
return LOG_STR("FAN_ONLY");
|
||||||
|
case CLIMATE_MODE_DRY:
|
||||||
|
return LOG_STR("DRY");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Climate action strings indexed by ClimateAction enum (0,2-6): OFF, (gap), COOLING, HEATING, IDLE, DRYING, FAN
|
|
||||||
PROGMEM_STRING_TABLE(ClimateActionStrings, "OFF", "UNKNOWN", "COOLING", "HEATING", "IDLE", "DRYING", "FAN", "UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *climate_action_to_string(ClimateAction action) {
|
const LogString *climate_action_to_string(ClimateAction action) {
|
||||||
return ClimateActionStrings::get_log_str(static_cast<uint8_t>(action), ClimateActionStrings::LAST_INDEX);
|
switch (action) {
|
||||||
|
case CLIMATE_ACTION_OFF:
|
||||||
|
return LOG_STR("OFF");
|
||||||
|
case CLIMATE_ACTION_COOLING:
|
||||||
|
return LOG_STR("COOLING");
|
||||||
|
case CLIMATE_ACTION_HEATING:
|
||||||
|
return LOG_STR("HEATING");
|
||||||
|
case CLIMATE_ACTION_IDLE:
|
||||||
|
return LOG_STR("IDLE");
|
||||||
|
case CLIMATE_ACTION_DRYING:
|
||||||
|
return LOG_STR("DRYING");
|
||||||
|
case CLIMATE_ACTION_FAN:
|
||||||
|
return LOG_STR("FAN");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Climate fan mode strings indexed by ClimateFanMode enum (0-9): ON, OFF, AUTO, LOW, MEDIUM, HIGH, MIDDLE, FOCUS,
|
|
||||||
// DIFFUSE, QUIET
|
|
||||||
PROGMEM_STRING_TABLE(ClimateFanModeStrings, "ON", "OFF", "AUTO", "LOW", "MEDIUM", "HIGH", "MIDDLE", "FOCUS", "DIFFUSE",
|
|
||||||
"QUIET", "UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
|
const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
|
||||||
return ClimateFanModeStrings::get_log_str(static_cast<uint8_t>(fan_mode), ClimateFanModeStrings::LAST_INDEX);
|
switch (fan_mode) {
|
||||||
|
case climate::CLIMATE_FAN_ON:
|
||||||
|
return LOG_STR("ON");
|
||||||
|
case climate::CLIMATE_FAN_OFF:
|
||||||
|
return LOG_STR("OFF");
|
||||||
|
case climate::CLIMATE_FAN_AUTO:
|
||||||
|
return LOG_STR("AUTO");
|
||||||
|
case climate::CLIMATE_FAN_LOW:
|
||||||
|
return LOG_STR("LOW");
|
||||||
|
case climate::CLIMATE_FAN_MEDIUM:
|
||||||
|
return LOG_STR("MEDIUM");
|
||||||
|
case climate::CLIMATE_FAN_HIGH:
|
||||||
|
return LOG_STR("HIGH");
|
||||||
|
case climate::CLIMATE_FAN_MIDDLE:
|
||||||
|
return LOG_STR("MIDDLE");
|
||||||
|
case climate::CLIMATE_FAN_FOCUS:
|
||||||
|
return LOG_STR("FOCUS");
|
||||||
|
case climate::CLIMATE_FAN_DIFFUSE:
|
||||||
|
return LOG_STR("DIFFUSE");
|
||||||
|
case climate::CLIMATE_FAN_QUIET:
|
||||||
|
return LOG_STR("QUIET");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Climate swing mode strings indexed by ClimateSwingMode enum (0-3): OFF, BOTH, VERTICAL, HORIZONTAL
|
|
||||||
PROGMEM_STRING_TABLE(ClimateSwingModeStrings, "OFF", "BOTH", "VERTICAL", "HORIZONTAL", "UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
|
const LogString *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
|
||||||
return ClimateSwingModeStrings::get_log_str(static_cast<uint8_t>(swing_mode), ClimateSwingModeStrings::LAST_INDEX);
|
switch (swing_mode) {
|
||||||
|
case climate::CLIMATE_SWING_OFF:
|
||||||
|
return LOG_STR("OFF");
|
||||||
|
case climate::CLIMATE_SWING_BOTH:
|
||||||
|
return LOG_STR("BOTH");
|
||||||
|
case climate::CLIMATE_SWING_VERTICAL:
|
||||||
|
return LOG_STR("VERTICAL");
|
||||||
|
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||||
|
return LOG_STR("HORIZONTAL");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Climate preset strings indexed by ClimatePreset enum (0-7): NONE, HOME, AWAY, BOOST, COMFORT, ECO, SLEEP, ACTIVITY
|
|
||||||
PROGMEM_STRING_TABLE(ClimatePresetStrings, "NONE", "HOME", "AWAY", "BOOST", "COMFORT", "ECO", "SLEEP", "ACTIVITY",
|
|
||||||
"UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *climate_preset_to_string(ClimatePreset preset) {
|
const LogString *climate_preset_to_string(ClimatePreset preset) {
|
||||||
return ClimatePresetStrings::get_log_str(static_cast<uint8_t>(preset), ClimatePresetStrings::LAST_INDEX);
|
switch (preset) {
|
||||||
|
case climate::CLIMATE_PRESET_NONE:
|
||||||
|
return LOG_STR("NONE");
|
||||||
|
case climate::CLIMATE_PRESET_HOME:
|
||||||
|
return LOG_STR("HOME");
|
||||||
|
case climate::CLIMATE_PRESET_ECO:
|
||||||
|
return LOG_STR("ECO");
|
||||||
|
case climate::CLIMATE_PRESET_AWAY:
|
||||||
|
return LOG_STR("AWAY");
|
||||||
|
case climate::CLIMATE_PRESET_BOOST:
|
||||||
|
return LOG_STR("BOOST");
|
||||||
|
case climate::CLIMATE_PRESET_COMFORT:
|
||||||
|
return LOG_STR("COMFORT");
|
||||||
|
case climate::CLIMATE_PRESET_SLEEP:
|
||||||
|
return LOG_STR("SLEEP");
|
||||||
|
case climate::CLIMATE_PRESET_ACTIVITY:
|
||||||
|
return LOG_STR("ACTIVITY");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome::climate
|
} // namespace esphome::climate
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace cm1106 {
|
|||||||
|
|
||||||
class CM1106Component : public PollingComponent, public uart::UARTDevice {
|
class CM1106Component : public PollingComponent, public uart::UARTDevice {
|
||||||
public:
|
public:
|
||||||
|
float get_setup_priority() const override { return esphome::setup_priority::DATA; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace combination {
|
|||||||
|
|
||||||
class CombinationComponent : public Component, public sensor::Sensor {
|
class CombinationComponent : public Component, public sensor::Sensor {
|
||||||
public:
|
public:
|
||||||
|
float get_setup_priority() const override { return esphome::setup_priority::DATA; }
|
||||||
|
|
||||||
/// @brief Logs all source sensor's names
|
/// @brief Logs all source sensor's names
|
||||||
virtual void log_source_sensors() = 0;
|
virtual void log_source_sensors() = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,3 @@ CONF_ON_STATE_CHANGE = "on_state_change"
|
|||||||
CONF_REQUEST_HEADERS = "request_headers"
|
CONF_REQUEST_HEADERS = "request_headers"
|
||||||
CONF_ROWS = "rows"
|
CONF_ROWS = "rows"
|
||||||
CONF_USE_PSRAM = "use_psram"
|
CONF_USE_PSRAM = "use_psram"
|
||||||
|
|
||||||
ICON_CURRENT_DC = "mdi:current-dc"
|
|
||||||
ICON_SOLAR_PANEL = "mdi:solar-panel"
|
|
||||||
ICON_SOLAR_POWER = "mdi:solar-power"
|
|
||||||
|
|
||||||
UNIT_AMPERE_HOUR = "Ah"
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition, maybe_simple_id
|
from esphome.automation import Condition, maybe_simple_id
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
@@ -11,7 +9,6 @@ from esphome.const import (
|
|||||||
CONF_ICON,
|
CONF_ICON,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
CONF_ON_IDLE,
|
|
||||||
CONF_ON_OPEN,
|
CONF_ON_OPEN,
|
||||||
CONF_POSITION,
|
CONF_POSITION,
|
||||||
CONF_POSITION_COMMAND_TOPIC,
|
CONF_POSITION_COMMAND_TOPIC,
|
||||||
@@ -35,10 +32,9 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_SHUTTER,
|
DEVICE_CLASS_SHUTTER,
|
||||||
DEVICE_CLASS_WINDOW,
|
DEVICE_CLASS_WINDOW,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObj, MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.types import ConfigType, TemplateArgsType
|
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
@@ -57,8 +53,6 @@ DEVICE_CLASSES = [
|
|||||||
DEVICE_CLASS_WINDOW,
|
DEVICE_CLASS_WINDOW,
|
||||||
]
|
]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
cover_ns = cg.esphome_ns.namespace("cover")
|
cover_ns = cg.esphome_ns.namespace("cover")
|
||||||
|
|
||||||
Cover = cover_ns.class_("Cover", cg.EntityBase)
|
Cover = cover_ns.class_("Cover", cg.EntityBase)
|
||||||
@@ -89,29 +83,14 @@ ControlAction = cover_ns.class_("ControlAction", automation.Action)
|
|||||||
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
|
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
|
||||||
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
|
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
|
||||||
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
|
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
|
||||||
CoverOpenedTrigger = cover_ns.class_(
|
|
||||||
"CoverOpenedTrigger", automation.Trigger.template()
|
# Triggers
|
||||||
)
|
CoverOpenTrigger = cover_ns.class_("CoverOpenTrigger", automation.Trigger.template())
|
||||||
CoverClosedTrigger = cover_ns.class_(
|
CoverClosedTrigger = cover_ns.class_(
|
||||||
"CoverClosedTrigger", automation.Trigger.template()
|
"CoverClosedTrigger", automation.Trigger.template()
|
||||||
)
|
)
|
||||||
CoverTrigger = cover_ns.class_("CoverTrigger", automation.Trigger.template())
|
|
||||||
|
|
||||||
# Cover-specific constants
|
|
||||||
CONF_ON_CLOSED = "on_closed"
|
CONF_ON_CLOSED = "on_closed"
|
||||||
CONF_ON_OPENED = "on_opened"
|
|
||||||
CONF_ON_OPENING = "on_opening"
|
|
||||||
CONF_ON_CLOSING = "on_closing"
|
|
||||||
|
|
||||||
TRIGGERS = {
|
|
||||||
CONF_ON_OPEN: CoverOpenedTrigger, # Deprecated, use on_opened
|
|
||||||
CONF_ON_OPENED: CoverOpenedTrigger,
|
|
||||||
CONF_ON_CLOSED: CoverClosedTrigger,
|
|
||||||
CONF_ON_CLOSING: CoverTrigger.template(CoverOperation.COVER_OPERATION_CLOSING),
|
|
||||||
CONF_ON_OPENING: CoverTrigger.template(CoverOperation.COVER_OPERATION_OPENING),
|
|
||||||
CONF_ON_IDLE: CoverTrigger.template(CoverOperation.COVER_OPERATION_IDLE),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_COVER_SCHEMA = (
|
_COVER_SCHEMA = (
|
||||||
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
@@ -132,14 +111,16 @@ _COVER_SCHEMA = (
|
|||||||
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
**{
|
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
|
||||||
cv.Optional(conf): automation.validate_automation(
|
{
|
||||||
{
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(trigger_class),
|
}
|
||||||
}
|
),
|
||||||
)
|
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
|
||||||
for conf, trigger_class in TRIGGERS.items()
|
{
|
||||||
},
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -176,14 +157,12 @@ async def setup_cover_core_(var, config):
|
|||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||||
cg.add(var.set_device_class(device_class))
|
cg.add(var.set_device_class(device_class))
|
||||||
|
|
||||||
if CONF_ON_OPEN in config:
|
for conf in config.get(CONF_ON_OPEN, []):
|
||||||
_LOGGER.warning(
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
"'on_open' is deprecated, use 'on_opened'. Will be removed in 2026.8.0"
|
await automation.build_automation(trigger, [], conf)
|
||||||
)
|
for conf in config.get(CONF_ON_CLOSED, []):
|
||||||
for trigger_conf in TRIGGERS:
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
for conf in config.get(trigger_conf, []):
|
await automation.build_automation(trigger, [], conf)
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
|
||||||
await automation.build_automation(trigger, [], conf)
|
|
||||||
|
|
||||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
@@ -279,26 +258,6 @@ async def cover_control_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
COVER_CONDITION_SCHEMA = cv.maybe_simple_value(
|
|
||||||
{cv.Required(CONF_ID): cv.use_id(Cover)}, key=CONF_ID
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def cover_condition_to_code(
|
|
||||||
config: ConfigType, condition_id: ID, template_arg: MockObj, args: TemplateArgsType
|
|
||||||
) -> MockObj:
|
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
|
||||||
return cg.new_Pvariable(condition_id, template_arg, paren)
|
|
||||||
|
|
||||||
|
|
||||||
automation.register_condition(
|
|
||||||
"cover.is_open", CoverIsOpenCondition, COVER_CONDITION_SCHEMA
|
|
||||||
)(cover_condition_to_code)
|
|
||||||
automation.register_condition(
|
|
||||||
"cover.is_closed", CoverIsClosedCondition, COVER_CONDITION_SCHEMA
|
|
||||||
)(cover_condition_to_code)
|
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(CoroPriority.CORE)
|
@coroutine_with_priority(CoroPriority.CORE)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(cover_ns.using)
|
cg.add_global(cover_ns.using)
|
||||||
|
|||||||
@@ -90,53 +90,44 @@ template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
|
|||||||
Cover *cover_;
|
Cover *cover_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<bool OPEN, typename... Ts> class CoverPositionCondition : public Condition<Ts...> {
|
template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
|
||||||
public:
|
public:
|
||||||
CoverPositionCondition(Cover *cover) : cover_(cover) {}
|
CoverIsOpenCondition(Cover *cover) : cover_(cover) {}
|
||||||
|
bool check(const Ts &...x) override { return this->cover_->is_fully_open(); }
|
||||||
bool check(const Ts &...x) override { return this->cover_->position == (OPEN ? COVER_OPEN : COVER_CLOSED); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Cover *cover_;
|
Cover *cover_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> using CoverIsOpenCondition = CoverPositionCondition<true, Ts...>;
|
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
|
||||||
template<typename... Ts> using CoverIsClosedCondition = CoverPositionCondition<false, Ts...>;
|
|
||||||
|
|
||||||
template<bool OPEN> class CoverPositionTrigger : public Trigger<> {
|
|
||||||
public:
|
public:
|
||||||
CoverPositionTrigger(Cover *a_cover) {
|
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
|
||||||
|
bool check(const Ts &...x) override { return this->cover_->is_fully_closed(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Cover *cover_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CoverOpenTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
CoverOpenTrigger(Cover *a_cover) {
|
||||||
a_cover->add_on_state_callback([this, a_cover]() {
|
a_cover->add_on_state_callback([this, a_cover]() {
|
||||||
if (a_cover->position != this->last_position_) {
|
if (a_cover->is_fully_open()) {
|
||||||
this->last_position_ = a_cover->position;
|
this->trigger();
|
||||||
if (a_cover->position == (OPEN ? COVER_OPEN : COVER_CLOSED))
|
|
||||||
this->trigger();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
float last_position_{NAN};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using CoverOpenedTrigger = CoverPositionTrigger<true>;
|
class CoverClosedTrigger : public Trigger<> {
|
||||||
using CoverClosedTrigger = CoverPositionTrigger<false>;
|
|
||||||
|
|
||||||
template<CoverOperation OP> class CoverTrigger : public Trigger<> {
|
|
||||||
public:
|
public:
|
||||||
CoverTrigger(Cover *a_cover) {
|
CoverClosedTrigger(Cover *a_cover) {
|
||||||
a_cover->add_on_state_callback([this, a_cover]() {
|
a_cover->add_on_state_callback([this, a_cover]() {
|
||||||
auto current_op = a_cover->current_operation;
|
if (a_cover->is_fully_closed()) {
|
||||||
if (current_op == OP) {
|
this->trigger();
|
||||||
if (!this->last_operation_.has_value() || this->last_operation_.value() != OP) {
|
|
||||||
this->trigger();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this->last_operation_ = current_op;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
optional<CoverOperation> last_operation_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::cover
|
} // namespace esphome::cover
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ namespace esphome::cover {
|
|||||||
|
|
||||||
static const char *const TAG = "cover";
|
static const char *const TAG = "cover";
|
||||||
|
|
||||||
|
const float COVER_OPEN = 1.0f;
|
||||||
|
const float COVER_CLOSED = 0.0f;
|
||||||
|
|
||||||
const LogString *cover_command_to_str(float pos) {
|
const LogString *cover_command_to_str(float pos) {
|
||||||
if (pos == COVER_OPEN) {
|
if (pos == COVER_OPEN) {
|
||||||
return LOG_STR("OPEN");
|
return LOG_STR("OPEN");
|
||||||
@@ -19,11 +22,17 @@ const LogString *cover_command_to_str(float pos) {
|
|||||||
return LOG_STR("UNKNOWN");
|
return LOG_STR("UNKNOWN");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cover operation strings indexed by CoverOperation enum (0-2): IDLE, OPENING, CLOSING, plus UNKNOWN
|
|
||||||
PROGMEM_STRING_TABLE(CoverOperationStrings, "IDLE", "OPENING", "CLOSING", "UNKNOWN");
|
|
||||||
|
|
||||||
const LogString *cover_operation_to_str(CoverOperation op) {
|
const LogString *cover_operation_to_str(CoverOperation op) {
|
||||||
return CoverOperationStrings::get_log_str(static_cast<uint8_t>(op), CoverOperationStrings::LAST_INDEX);
|
switch (op) {
|
||||||
|
case COVER_OPERATION_IDLE:
|
||||||
|
return LOG_STR("IDLE");
|
||||||
|
case COVER_OPERATION_OPENING:
|
||||||
|
return LOG_STR("OPENING");
|
||||||
|
case COVER_OPERATION_CLOSING:
|
||||||
|
return LOG_STR("CLOSING");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Cover::Cover() : position{COVER_OPEN} {}
|
Cover::Cover() : position{COVER_OPEN} {}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
namespace esphome::cover {
|
namespace esphome::cover {
|
||||||
|
|
||||||
static constexpr float COVER_OPEN = 1.0f;
|
const extern float COVER_OPEN;
|
||||||
static constexpr float COVER_CLOSED = 0.0f;
|
const extern float COVER_CLOSED;
|
||||||
|
|
||||||
#define LOG_COVER(prefix, type, obj) \
|
#define LOG_COVER(prefix, type, obj) \
|
||||||
if ((obj) != nullptr) { \
|
if ((obj) != nullptr) { \
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ void CSE7761Component::dump_config() {
|
|||||||
this->check_uart_settings(38400, 1, uart::UART_CONFIG_PARITY_EVEN, 8);
|
this->check_uart_settings(38400, 1, uart::UART_CONFIG_PARITY_EVEN, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float CSE7761Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void CSE7761Component::update() {
|
void CSE7761Component::update() {
|
||||||
if (this->data_.ready) {
|
if (this->data_.ready) {
|
||||||
this->get_data_();
|
this->get_data_();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class CSE7761Component : public PollingComponent, public uart::UARTDevice {
|
|||||||
void set_current_2_sensor(sensor::Sensor *current_sensor_2) { current_sensor_2_ = current_sensor_2; }
|
void set_current_2_sensor(sensor::Sensor *current_sensor_2) { current_sensor_2_ = current_sensor_2; }
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ void CSE7766Component::loop() {
|
|||||||
this->raw_data_index_ = (this->raw_data_index_ + 1) % 24;
|
this->raw_data_index_ = (this->raw_data_index_ + 1) % 24;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
float CSE7766Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
bool CSE7766Component::check_byte_() {
|
bool CSE7766Component::check_byte_() {
|
||||||
uint8_t index = this->raw_data_index_;
|
uint8_t index = this->raw_data_index_;
|
||||||
@@ -151,10 +152,6 @@ void CSE7766Component::parse_data_() {
|
|||||||
if (this->power_sensor_ != nullptr) {
|
if (this->power_sensor_ != nullptr) {
|
||||||
this->power_sensor_->publish_state(power);
|
this->power_sensor_->publish_state(power);
|
||||||
}
|
}
|
||||||
} else if (this->power_sensor_ != nullptr) {
|
|
||||||
// No valid power measurement from chip - publish 0W to avoid stale readings
|
|
||||||
// This typically happens when current is below the measurable threshold (~50mA)
|
|
||||||
this->power_sensor_->publish_state(0.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float current = 0.0f;
|
float current = 0.0f;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
|||||||
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
|
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
|
||||||
|
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ void CurrentBasedCover::loop() {
|
|||||||
if (this->current_operation == COVER_OPERATION_OPENING) {
|
if (this->current_operation == COVER_OPERATION_OPENING) {
|
||||||
if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
|
if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
|
||||||
this->direction_idle_();
|
this->direction_idle_();
|
||||||
this->malfunction_trigger_.trigger();
|
this->malfunction_trigger_->trigger();
|
||||||
ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
|
ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
|
||||||
this->name_.c_str());
|
this->name_.c_str());
|
||||||
} else if (this->is_opening_blocked_()) { // Blocked
|
} else if (this->is_opening_blocked_()) { // Blocked
|
||||||
@@ -87,7 +87,7 @@ void CurrentBasedCover::loop() {
|
|||||||
} else if (this->current_operation == COVER_OPERATION_CLOSING) {
|
} else if (this->current_operation == COVER_OPERATION_CLOSING) {
|
||||||
if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
|
if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
|
||||||
this->direction_idle_();
|
this->direction_idle_();
|
||||||
this->malfunction_trigger_.trigger();
|
this->malfunction_trigger_->trigger();
|
||||||
ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
|
ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
|
||||||
this->name_.c_str());
|
this->name_.c_str());
|
||||||
} else if (this->is_closing_blocked_()) { // Blocked
|
} else if (this->is_closing_blocked_()) { // Blocked
|
||||||
@@ -159,6 +159,7 @@ void CurrentBasedCover::dump_config() {
|
|||||||
this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
|
this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
void CurrentBasedCover::stop_prev_trigger_() {
|
void CurrentBasedCover::stop_prev_trigger_() {
|
||||||
if (this->prev_command_trigger_ != nullptr) {
|
if (this->prev_command_trigger_ != nullptr) {
|
||||||
this->prev_command_trigger_->stop_action();
|
this->prev_command_trigger_->stop_action();
|
||||||
@@ -220,15 +221,15 @@ void CurrentBasedCover::start_direction_(CoverOperation dir) {
|
|||||||
Trigger<> *trig;
|
Trigger<> *trig;
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case COVER_OPERATION_IDLE:
|
case COVER_OPERATION_IDLE:
|
||||||
trig = &this->stop_trigger_;
|
trig = this->stop_trigger_;
|
||||||
break;
|
break;
|
||||||
case COVER_OPERATION_OPENING:
|
case COVER_OPERATION_OPENING:
|
||||||
this->last_operation_ = dir;
|
this->last_operation_ = dir;
|
||||||
trig = &this->open_trigger_;
|
trig = this->open_trigger_;
|
||||||
break;
|
break;
|
||||||
case COVER_OPERATION_CLOSING:
|
case COVER_OPERATION_CLOSING:
|
||||||
this->last_operation_ = dir;
|
this->last_operation_ = dir;
|
||||||
trig = &this->close_trigger_;
|
trig = this->close_trigger_;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ class CurrentBasedCover : public cover::Cover, public Component {
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
Trigger<> *get_stop_trigger() { return &this->stop_trigger_; }
|
Trigger<> *get_stop_trigger() const { return this->stop_trigger_; }
|
||||||
|
|
||||||
Trigger<> *get_open_trigger() { return &this->open_trigger_; }
|
Trigger<> *get_open_trigger() const { return this->open_trigger_; }
|
||||||
void set_open_sensor(sensor::Sensor *open_sensor) { this->open_sensor_ = open_sensor; }
|
void set_open_sensor(sensor::Sensor *open_sensor) { this->open_sensor_ = open_sensor; }
|
||||||
void set_open_moving_current_threshold(float open_moving_current_threshold) {
|
void set_open_moving_current_threshold(float open_moving_current_threshold) {
|
||||||
this->open_moving_current_threshold_ = open_moving_current_threshold;
|
this->open_moving_current_threshold_ = open_moving_current_threshold;
|
||||||
@@ -27,7 +28,7 @@ class CurrentBasedCover : public cover::Cover, public Component {
|
|||||||
}
|
}
|
||||||
void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; }
|
void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; }
|
||||||
|
|
||||||
Trigger<> *get_close_trigger() { return &this->close_trigger_; }
|
Trigger<> *get_close_trigger() const { return this->close_trigger_; }
|
||||||
void set_close_sensor(sensor::Sensor *close_sensor) { this->close_sensor_ = close_sensor; }
|
void set_close_sensor(sensor::Sensor *close_sensor) { this->close_sensor_ = close_sensor; }
|
||||||
void set_close_moving_current_threshold(float close_moving_current_threshold) {
|
void set_close_moving_current_threshold(float close_moving_current_threshold) {
|
||||||
this->close_moving_current_threshold_ = close_moving_current_threshold;
|
this->close_moving_current_threshold_ = close_moving_current_threshold;
|
||||||
@@ -43,7 +44,7 @@ class CurrentBasedCover : public cover::Cover, public Component {
|
|||||||
void set_malfunction_detection(bool malfunction_detection) { this->malfunction_detection_ = malfunction_detection; }
|
void set_malfunction_detection(bool malfunction_detection) { this->malfunction_detection_ = malfunction_detection; }
|
||||||
void set_start_sensing_delay(uint32_t start_sensing_delay) { this->start_sensing_delay_ = start_sensing_delay; }
|
void set_start_sensing_delay(uint32_t start_sensing_delay) { this->start_sensing_delay_ = start_sensing_delay; }
|
||||||
|
|
||||||
Trigger<> *get_malfunction_trigger() { return &this->malfunction_trigger_; }
|
Trigger<> *get_malfunction_trigger() const { return this->malfunction_trigger_; }
|
||||||
|
|
||||||
cover::CoverTraits get_traits() override;
|
cover::CoverTraits get_traits() override;
|
||||||
|
|
||||||
@@ -63,23 +64,23 @@ class CurrentBasedCover : public cover::Cover, public Component {
|
|||||||
|
|
||||||
void recompute_position_();
|
void recompute_position_();
|
||||||
|
|
||||||
Trigger<> stop_trigger_;
|
Trigger<> *stop_trigger_{new Trigger<>()};
|
||||||
|
|
||||||
sensor::Sensor *open_sensor_{nullptr};
|
sensor::Sensor *open_sensor_{nullptr};
|
||||||
Trigger<> open_trigger_;
|
Trigger<> *open_trigger_{new Trigger<>()};
|
||||||
float open_moving_current_threshold_;
|
float open_moving_current_threshold_;
|
||||||
float open_obstacle_current_threshold_{FLT_MAX};
|
float open_obstacle_current_threshold_{FLT_MAX};
|
||||||
uint32_t open_duration_;
|
uint32_t open_duration_;
|
||||||
|
|
||||||
sensor::Sensor *close_sensor_{nullptr};
|
sensor::Sensor *close_sensor_{nullptr};
|
||||||
Trigger<> close_trigger_;
|
Trigger<> *close_trigger_{new Trigger<>()};
|
||||||
float close_moving_current_threshold_;
|
float close_moving_current_threshold_;
|
||||||
float close_obstacle_current_threshold_{FLT_MAX};
|
float close_obstacle_current_threshold_{FLT_MAX};
|
||||||
uint32_t close_duration_;
|
uint32_t close_duration_;
|
||||||
|
|
||||||
uint32_t max_duration_{UINT32_MAX};
|
uint32_t max_duration_{UINT32_MAX};
|
||||||
bool malfunction_detection_{true};
|
bool malfunction_detection_{true};
|
||||||
Trigger<> malfunction_trigger_;
|
Trigger<> *malfunction_trigger_{new Trigger<>()};
|
||||||
uint32_t start_sensing_delay_;
|
uint32_t start_sensing_delay_;
|
||||||
float obstacle_rollback_;
|
float obstacle_rollback_;
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ void DalyBmsComponent::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float DalyBmsComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void DalyBmsComponent::request_data_(uint8_t data_id) {
|
void DalyBmsComponent::request_data_(uint8_t data_id) {
|
||||||
uint8_t request_message[DALY_FRAME_SIZE];
|
uint8_t request_message[DALY_FRAME_SIZE];
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
|
|||||||
void update() override;
|
void update() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
void set_address(uint8_t address) { this->addr_ = address; }
|
void set_address(uint8_t address) { this->addr_ = address; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
from esphome.components.const import ICON_CURRENT_DC, UNIT_AMPERE_HOUR
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BATTERY_LEVEL,
|
CONF_BATTERY_LEVEL,
|
||||||
@@ -56,11 +55,14 @@ CONF_CELL_15_VOLTAGE = "cell_15_voltage"
|
|||||||
CONF_CELL_16_VOLTAGE = "cell_16_voltage"
|
CONF_CELL_16_VOLTAGE = "cell_16_voltage"
|
||||||
CONF_CELL_17_VOLTAGE = "cell_17_voltage"
|
CONF_CELL_17_VOLTAGE = "cell_17_voltage"
|
||||||
CONF_CELL_18_VOLTAGE = "cell_18_voltage"
|
CONF_CELL_18_VOLTAGE = "cell_18_voltage"
|
||||||
|
ICON_CURRENT_DC = "mdi:current-dc"
|
||||||
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
|
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
|
||||||
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
|
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
|
||||||
ICON_THERMOMETER_CHEVRON_DOWN = "mdi:thermometer-chevron-down"
|
ICON_THERMOMETER_CHEVRON_DOWN = "mdi:thermometer-chevron-down"
|
||||||
ICON_CAR_BATTERY = "mdi:car-battery"
|
ICON_CAR_BATTERY = "mdi:car-battery"
|
||||||
|
|
||||||
|
UNIT_AMPERE_HOUR = "Ah"
|
||||||
|
|
||||||
TYPES = [
|
TYPES = [
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
CONF_CURRENT,
|
CONF_CURRENT,
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ void DHT::update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float DHT::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void DHT::set_dht_model(DHTModel model) {
|
void DHT::set_dht_model(DHTModel model) {
|
||||||
this->model_ = model;
|
this->model_ = model;
|
||||||
this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;
|
this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ class DHT : public PollingComponent {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// Update sensor values and push them to the frontend.
|
/// Update sensor values and push them to the frontend.
|
||||||
void update() override;
|
void update() override;
|
||||||
|
/// HARDWARE_LATE setup priority.
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool read_sensor_(float *temperature, float *humidity, bool report_errors);
|
bool read_sensor_(float *temperature, float *humidity, bool report_errors);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ void DHT12Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
}
|
}
|
||||||
|
float DHT12Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
bool DHT12Component::read_data_(uint8_t *data) {
|
bool DHT12Component::read_data_(uint8_t *data) {
|
||||||
if (!this->read_bytes(0, data, 5)) {
|
if (!this->read_bytes(0, data, 5)) {
|
||||||
ESP_LOGW(TAG, "Updating DHT12 failed!");
|
ESP_LOGW(TAG, "Updating DHT12 failed!");
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class DHT12Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import uart
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266
|
|
||||||
|
|
||||||
CODEOWNERS = ["@SimonFischer04"]
|
|
||||||
DEPENDENCIES = ["uart"]
|
|
||||||
|
|
||||||
CONF_DLMS_METER_ID = "dlms_meter_id"
|
|
||||||
CONF_DECRYPTION_KEY = "decryption_key"
|
|
||||||
CONF_PROVIDER = "provider"
|
|
||||||
|
|
||||||
PROVIDERS = {"generic": 0, "netznoe": 1}
|
|
||||||
|
|
||||||
dlms_meter_component_ns = cg.esphome_ns.namespace("dlms_meter")
|
|
||||||
DlmsMeterComponent = dlms_meter_component_ns.class_(
|
|
||||||
"DlmsMeterComponent", cg.Component, uart.UARTDevice
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_key(value):
|
|
||||||
value = cv.string_strict(value)
|
|
||||||
if len(value) != 32:
|
|
||||||
raise cv.Invalid("Decryption key must be 32 hex characters (16 bytes)")
|
|
||||||
try:
|
|
||||||
return [int(value[i : i + 2], 16) for i in range(0, 32, 2)]
|
|
||||||
except ValueError as exc:
|
|
||||||
raise cv.Invalid("Decryption key must be hex values from 00 to FF") from exc
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(DlmsMeterComponent),
|
|
||||||
cv.Required(CONF_DECRYPTION_KEY): validate_key,
|
|
||||||
cv.Optional(CONF_PROVIDER, default="generic"): cv.enum(
|
|
||||||
PROVIDERS, lower=True
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
.extend(cv.COMPONENT_SCHEMA),
|
|
||||||
cv.only_on([PLATFORM_ESP8266, PLATFORM_ESP32]),
|
|
||||||
)
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
|
||||||
"dlms_meter", baud_rate=2400, require_rx=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await uart.register_uart_device(var, config)
|
|
||||||
key = ", ".join(str(b) for b in config[CONF_DECRYPTION_KEY])
|
|
||||||
cg.add(var.set_decryption_key(cg.RawExpression(f"{{{key}}}")))
|
|
||||||
cg.add(var.set_provider(PROVIDERS[config[CONF_PROVIDER]]))
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace esphome::dlms_meter {
|
|
||||||
|
|
||||||
/*
|
|
||||||
+-------------------------------+
|
|
||||||
| Ciphering Service |
|
|
||||||
+-------------------------------+
|
|
||||||
| System Title Length |
|
|
||||||
+-------------------------------+
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
| System |
|
|
||||||
| Title |
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
+-------------------------------+
|
|
||||||
| Length | (1 or 3 Bytes)
|
|
||||||
+-------------------------------+
|
|
||||||
| Security Control Byte |
|
|
||||||
+-------------------------------+
|
|
||||||
| |
|
|
||||||
| Frame |
|
|
||||||
| Counter |
|
|
||||||
| |
|
|
||||||
+-------------------------------+
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
Encrypted Payload
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+-------------------------------+
|
|
||||||
|
|
||||||
Ciphering Service: 0xDB (General-Glo-Ciphering)
|
|
||||||
System Title Length: 0x08
|
|
||||||
System Title: Unique ID of meter
|
|
||||||
Length: 1 Byte=Length <= 127, 3 Bytes=Length > 127 (0x82 & 2 Bytes length)
|
|
||||||
Security Control Byte:
|
|
||||||
- Bit 3…0: Security_Suite_Id
|
|
||||||
- Bit 4: "A" subfield: indicates that authentication is applied
|
|
||||||
- Bit 5: "E" subfield: indicates that encryption is applied
|
|
||||||
- Bit 6: Key_Set subfield: 0 = Unicast, 1 = Broadcast
|
|
||||||
- Bit 7: Indicates the use of compression.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static constexpr uint8_t DLMS_HEADER_LENGTH = 16;
|
|
||||||
static constexpr uint8_t DLMS_HEADER_EXT_OFFSET = 2; // Extra offset for extended length header
|
|
||||||
static constexpr uint8_t DLMS_CIPHER_OFFSET = 0;
|
|
||||||
static constexpr uint8_t DLMS_SYST_OFFSET = 1;
|
|
||||||
static constexpr uint8_t DLMS_LENGTH_OFFSET = 10;
|
|
||||||
static constexpr uint8_t TWO_BYTE_LENGTH = 0x82;
|
|
||||||
static constexpr uint8_t DLMS_LENGTH_CORRECTION = 5; // Header bytes included in length field
|
|
||||||
static constexpr uint8_t DLMS_SECBYTE_OFFSET = 11;
|
|
||||||
static constexpr uint8_t DLMS_FRAMECOUNTER_OFFSET = 12;
|
|
||||||
static constexpr uint8_t DLMS_FRAMECOUNTER_LENGTH = 4;
|
|
||||||
static constexpr uint8_t DLMS_PAYLOAD_OFFSET = 16;
|
|
||||||
static constexpr uint8_t GLO_CIPHERING = 0xDB;
|
|
||||||
static constexpr uint8_t DATA_NOTIFICATION = 0x0F;
|
|
||||||
static constexpr uint8_t TIMESTAMP_DATETIME = 0x0C;
|
|
||||||
static constexpr uint16_t MAX_MESSAGE_LENGTH = 512; // Maximum size of message (when having 2 bytes length in header).
|
|
||||||
|
|
||||||
// Provider specific quirks
|
|
||||||
static constexpr uint8_t NETZ_NOE_MAGIC_BYTE = 0x81; // Magic length byte used by Netz NOE
|
|
||||||
static constexpr uint8_t NETZ_NOE_EXPECTED_MESSAGE_LENGTH = 0xF8;
|
|
||||||
static constexpr uint8_t NETZ_NOE_EXPECTED_SECURITY_CONTROL_BYTE = 0x20;
|
|
||||||
|
|
||||||
} // namespace esphome::dlms_meter
|
|
||||||
@@ -1,468 +0,0 @@
|
|||||||
#include "dlms_meter.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#if defined(USE_ESP8266_FRAMEWORK_ARDUINO)
|
|
||||||
#include <bearssl/bearssl.h>
|
|
||||||
#elif defined(USE_ESP32)
|
|
||||||
#include "mbedtls/esp_config.h"
|
|
||||||
#include "mbedtls/gcm.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome::dlms_meter {
|
|
||||||
|
|
||||||
static constexpr const char *TAG = "dlms_meter";
|
|
||||||
|
|
||||||
void DlmsMeterComponent::dump_config() {
|
|
||||||
const char *provider_name = this->provider_ == PROVIDER_NETZNOE ? "Netz NOE" : "Generic";
|
|
||||||
ESP_LOGCONFIG(TAG,
|
|
||||||
"DLMS Meter:\n"
|
|
||||||
" Provider: %s\n"
|
|
||||||
" Read Timeout: %u ms",
|
|
||||||
provider_name, this->read_timeout_);
|
|
||||||
#define DLMS_METER_LOG_SENSOR(s) LOG_SENSOR(" ", #s, this->s##_sensor_);
|
|
||||||
DLMS_METER_SENSOR_LIST(DLMS_METER_LOG_SENSOR, )
|
|
||||||
#define DLMS_METER_LOG_TEXT_SENSOR(s) LOG_TEXT_SENSOR(" ", #s, this->s##_text_sensor_);
|
|
||||||
DLMS_METER_TEXT_SENSOR_LIST(DLMS_METER_LOG_TEXT_SENSOR, )
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
ESP_LOGW(TAG, "Receive buffer full, dropping remaining bytes");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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_) {
|
|
||||||
this->mbus_payload_.clear();
|
|
||||||
if (!this->parse_mbus_(this->mbus_payload_))
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint16_t message_length;
|
|
||||||
uint8_t systitle_length;
|
|
||||||
uint16_t header_offset;
|
|
||||||
if (!this->parse_dlms_(this->mbus_payload_, message_length, systitle_length, header_offset))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (message_length < DECODER_START_OFFSET || message_length > MAX_MESSAGE_LENGTH) {
|
|
||||||
ESP_LOGE(TAG, "DLMS: Message length invalid: %u", message_length);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt in place and then decode the OBIS codes
|
|
||||||
if (!this->decrypt_(this->mbus_payload_, message_length, systitle_length, header_offset))
|
|
||||||
return;
|
|
||||||
this->decode_obis_(&this->mbus_payload_[header_offset + DLMS_PAYLOAD_OFFSET], message_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DlmsMeterComponent::parse_mbus_(std::vector<uint8_t> &mbus_payload) {
|
|
||||||
ESP_LOGV(TAG, "Parsing M-Bus frames");
|
|
||||||
uint16_t frame_offset = 0; // Offset is used if the M-Bus message is split into multiple frames
|
|
||||||
|
|
||||||
while (frame_offset < this->receive_buffer_.size()) {
|
|
||||||
// Ensure enough bytes remain for the minimal intro header before accessing indices
|
|
||||||
if (this->receive_buffer_.size() - frame_offset < MBUS_HEADER_INTRO_LENGTH) {
|
|
||||||
ESP_LOGE(TAG, "MBUS: Not enough data for frame header (need %d, have %d)", MBUS_HEADER_INTRO_LENGTH,
|
|
||||||
(this->receive_buffer_.size() - frame_offset));
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check start bytes
|
|
||||||
if (this->receive_buffer_[frame_offset + MBUS_START1_OFFSET] != START_BYTE_LONG_FRAME ||
|
|
||||||
this->receive_buffer_[frame_offset + MBUS_START2_OFFSET] != START_BYTE_LONG_FRAME) {
|
|
||||||
ESP_LOGE(TAG, "MBUS: Start bytes do not match");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both length bytes must be identical
|
|
||||||
if (this->receive_buffer_[frame_offset + MBUS_LENGTH1_OFFSET] !=
|
|
||||||
this->receive_buffer_[frame_offset + MBUS_LENGTH2_OFFSET]) {
|
|
||||||
ESP_LOGE(TAG, "MBUS: Length bytes do not match");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t frame_length = this->receive_buffer_[frame_offset + MBUS_LENGTH1_OFFSET]; // Get length of this frame
|
|
||||||
|
|
||||||
// Check if received data is enough for the given frame length
|
|
||||||
if (this->receive_buffer_.size() - frame_offset <
|
|
||||||
frame_length + 3) { // length field inside packet does not account for second start- + checksum- + stop- byte
|
|
||||||
ESP_LOGE(TAG, "MBUS: Frame too big for received data");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we have full frame (header + payload + checksum + stop byte) before accessing stop byte
|
|
||||||
size_t required_total =
|
|
||||||
frame_length + MBUS_HEADER_INTRO_LENGTH + MBUS_FOOTER_LENGTH; // payload + header + 2 footer bytes
|
|
||||||
if (this->receive_buffer_.size() - frame_offset < required_total) {
|
|
||||||
ESP_LOGE(TAG, "MBUS: Incomplete frame (need %d, have %d)", (unsigned int) required_total,
|
|
||||||
this->receive_buffer_.size() - frame_offset);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH + MBUS_FOOTER_LENGTH - 1] !=
|
|
||||||
STOP_BYTE) {
|
|
||||||
ESP_LOGE(TAG, "MBUS: Invalid stop byte");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify checksum: sum of all bytes starting at MBUS_HEADER_INTRO_LENGTH, take last byte
|
|
||||||
uint8_t checksum = 0; // use uint8_t so only the 8 least significant bits are stored
|
|
||||||
for (uint16_t i = 0; i < frame_length; i++) {
|
|
||||||
checksum += this->receive_buffer_[frame_offset + MBUS_HEADER_INTRO_LENGTH + i];
|
|
||||||
}
|
|
||||||
if (checksum != this->receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH]) {
|
|
||||||
ESP_LOGE(TAG, "MBUS: Invalid checksum: %x != %x", checksum,
|
|
||||||
this->receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH]);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mbus_payload.insert(mbus_payload.end(), &this->receive_buffer_[frame_offset + MBUS_FULL_HEADER_LENGTH],
|
|
||||||
&this->receive_buffer_[frame_offset + MBUS_HEADER_INTRO_LENGTH + frame_length]);
|
|
||||||
|
|
||||||
frame_offset += MBUS_HEADER_INTRO_LENGTH + frame_length + MBUS_FOOTER_LENGTH;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DlmsMeterComponent::parse_dlms_(const std::vector<uint8_t> &mbus_payload, uint16_t &message_length,
|
|
||||||
uint8_t &systitle_length, uint16_t &header_offset) {
|
|
||||||
ESP_LOGV(TAG, "Parsing DLMS header");
|
|
||||||
if (mbus_payload.size() < DLMS_HEADER_LENGTH + DLMS_HEADER_EXT_OFFSET) {
|
|
||||||
ESP_LOGE(TAG, "DLMS: Payload too short");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mbus_payload[DLMS_CIPHER_OFFSET] != GLO_CIPHERING) { // Only general-glo-ciphering is supported (0xDB)
|
|
||||||
ESP_LOGE(TAG, "DLMS: Unsupported cipher");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
systitle_length = mbus_payload[DLMS_SYST_OFFSET];
|
|
||||||
|
|
||||||
if (systitle_length != 0x08) { // Only system titles with length of 8 are supported
|
|
||||||
ESP_LOGE(TAG, "DLMS: Unsupported system title length");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
message_length = mbus_payload[DLMS_LENGTH_OFFSET];
|
|
||||||
header_offset = 0;
|
|
||||||
|
|
||||||
if (this->provider_ == PROVIDER_NETZNOE) {
|
|
||||||
// for some reason EVN seems to set the standard "length" field to 0x81 and then the actual length is in the next
|
|
||||||
// byte. Check some bytes to see if received data still matches expectation
|
|
||||||
if (message_length == NETZ_NOE_MAGIC_BYTE &&
|
|
||||||
mbus_payload[DLMS_LENGTH_OFFSET + 1] == NETZ_NOE_EXPECTED_MESSAGE_LENGTH &&
|
|
||||||
mbus_payload[DLMS_LENGTH_OFFSET + 2] == NETZ_NOE_EXPECTED_SECURITY_CONTROL_BYTE) {
|
|
||||||
message_length = mbus_payload[DLMS_LENGTH_OFFSET + 1];
|
|
||||||
header_offset = 1;
|
|
||||||
} else {
|
|
||||||
ESP_LOGE(TAG, "Wrong Length - Security Control Byte sequence detected for provider EVN");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (message_length == TWO_BYTE_LENGTH) {
|
|
||||||
message_length = encode_uint16(mbus_payload[DLMS_LENGTH_OFFSET + 1], mbus_payload[DLMS_LENGTH_OFFSET + 2]);
|
|
||||||
header_offset = DLMS_HEADER_EXT_OFFSET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (message_length < DLMS_LENGTH_CORRECTION) {
|
|
||||||
ESP_LOGE(TAG, "DLMS: Message length too short: %u", message_length);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
message_length -= DLMS_LENGTH_CORRECTION; // Correct message length due to part of header being included in length
|
|
||||||
|
|
||||||
if (mbus_payload.size() - DLMS_HEADER_LENGTH - header_offset != message_length) {
|
|
||||||
ESP_LOGV(TAG, "DLMS: Length mismatch - payload=%d, header=%d, offset=%d, message=%d", mbus_payload.size(),
|
|
||||||
DLMS_HEADER_LENGTH, header_offset, message_length);
|
|
||||||
ESP_LOGE(TAG, "DLMS: Message has invalid length");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mbus_payload[header_offset + DLMS_SECBYTE_OFFSET] != 0x21 &&
|
|
||||||
mbus_payload[header_offset + DLMS_SECBYTE_OFFSET] !=
|
|
||||||
0x20) { // Only certain security suite is supported (0x21 || 0x20)
|
|
||||||
ESP_LOGE(TAG, "DLMS: Unsupported security control byte");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DlmsMeterComponent::decrypt_(std::vector<uint8_t> &mbus_payload, uint16_t message_length, uint8_t systitle_length,
|
|
||||||
uint16_t header_offset) {
|
|
||||||
ESP_LOGV(TAG, "Decrypting payload");
|
|
||||||
uint8_t iv[12]; // Reserve space for the IV, always 12 bytes
|
|
||||||
// Copy system title to IV (System title is before length; no header offset needed!)
|
|
||||||
// Add 1 to the offset in order to skip the system title length byte
|
|
||||||
memcpy(&iv[0], &mbus_payload[DLMS_SYST_OFFSET + 1], systitle_length);
|
|
||||||
memcpy(&iv[8], &mbus_payload[header_offset + DLMS_FRAMECOUNTER_OFFSET],
|
|
||||||
DLMS_FRAMECOUNTER_LENGTH); // Copy frame counter to IV
|
|
||||||
|
|
||||||
uint8_t *payload_ptr = &mbus_payload[header_offset + DLMS_PAYLOAD_OFFSET];
|
|
||||||
|
|
||||||
#if defined(USE_ESP8266_FRAMEWORK_ARDUINO)
|
|
||||||
br_gcm_context gcm_ctx;
|
|
||||||
br_aes_ct_ctr_keys bc;
|
|
||||||
br_aes_ct_ctr_init(&bc, this->decryption_key_.data(), this->decryption_key_.size());
|
|
||||||
br_gcm_init(&gcm_ctx, &bc.vtable, br_ghash_ctmul32);
|
|
||||||
br_gcm_reset(&gcm_ctx, iv, sizeof(iv));
|
|
||||||
br_gcm_flip(&gcm_ctx);
|
|
||||||
br_gcm_run(&gcm_ctx, 0, payload_ptr, message_length);
|
|
||||||
#elif defined(USE_ESP32)
|
|
||||||
size_t outlen = 0;
|
|
||||||
mbedtls_gcm_context gcm_ctx;
|
|
||||||
mbedtls_gcm_init(&gcm_ctx);
|
|
||||||
mbedtls_gcm_setkey(&gcm_ctx, MBEDTLS_CIPHER_ID_AES, this->decryption_key_.data(), this->decryption_key_.size() * 8);
|
|
||||||
mbedtls_gcm_starts(&gcm_ctx, MBEDTLS_GCM_DECRYPT, iv, sizeof(iv));
|
|
||||||
auto ret = mbedtls_gcm_update(&gcm_ctx, payload_ptr, message_length, payload_ptr, message_length, &outlen);
|
|
||||||
mbedtls_gcm_free(&gcm_ctx);
|
|
||||||
if (ret != 0) {
|
|
||||||
ESP_LOGE(TAG, "Decryption failed with error: %d", ret);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error "Invalid Platform"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (payload_ptr[0] != DATA_NOTIFICATION || payload_ptr[5] != TIMESTAMP_DATETIME) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Packet was decrypted but data is invalid");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ESP_LOGV(TAG, "Decrypted payload: %d bytes", message_length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DlmsMeterComponent::decode_obis_(uint8_t *plaintext, uint16_t message_length) {
|
|
||||||
ESP_LOGV(TAG, "Decoding payload");
|
|
||||||
MeterData data{};
|
|
||||||
uint16_t current_position = DECODER_START_OFFSET;
|
|
||||||
bool power_factor_found = false;
|
|
||||||
|
|
||||||
while (current_position + OBIS_CODE_OFFSET <= message_length) {
|
|
||||||
if (plaintext[current_position + OBIS_TYPE_OFFSET] != DataType::OCTET_STRING) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Unsupported OBIS header type: %x", plaintext[current_position + OBIS_TYPE_OFFSET]);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t obis_code_length = plaintext[current_position + OBIS_LENGTH_OFFSET];
|
|
||||||
if (obis_code_length != OBIS_CODE_LENGTH_STANDARD && obis_code_length != OBIS_CODE_LENGTH_EXTENDED) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Unsupported OBIS header length: %x", obis_code_length);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (current_position + OBIS_CODE_OFFSET + obis_code_length > message_length) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Buffer too short for OBIS code");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *obis_code = &plaintext[current_position + OBIS_CODE_OFFSET];
|
|
||||||
uint8_t obis_medium = obis_code[OBIS_A];
|
|
||||||
uint16_t obis_cd = encode_uint16(obis_code[OBIS_C], obis_code[OBIS_D]);
|
|
||||||
|
|
||||||
bool timestamp_found = false;
|
|
||||||
bool meter_number_found = false;
|
|
||||||
if (this->provider_ == PROVIDER_NETZNOE) {
|
|
||||||
// Do not advance Position when reading the Timestamp at DECODER_START_OFFSET
|
|
||||||
if ((obis_code_length == OBIS_CODE_LENGTH_EXTENDED) && (current_position == DECODER_START_OFFSET)) {
|
|
||||||
timestamp_found = true;
|
|
||||||
} else if (power_factor_found) {
|
|
||||||
meter_number_found = true;
|
|
||||||
power_factor_found = false;
|
|
||||||
} else {
|
|
||||||
current_position += obis_code_length + OBIS_CODE_OFFSET; // Advance past code and position
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
current_position += obis_code_length + OBIS_CODE_OFFSET; // Advance past code, position and type
|
|
||||||
}
|
|
||||||
if (!timestamp_found && !meter_number_found && obis_medium != Medium::ELECTRICITY &&
|
|
||||||
obis_medium != Medium::ABSTRACT) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Unsupported OBIS medium: %x", obis_medium);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current_position >= message_length) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Buffer too short for data type");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float value = 0.0f;
|
|
||||||
uint8_t value_size = 0;
|
|
||||||
uint8_t data_type = plaintext[current_position];
|
|
||||||
current_position++;
|
|
||||||
|
|
||||||
switch (data_type) {
|
|
||||||
case DataType::DOUBLE_LONG_UNSIGNED: {
|
|
||||||
value_size = 4;
|
|
||||||
if (current_position + value_size > message_length) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Buffer too short for DOUBLE_LONG_UNSIGNED");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = encode_uint32(plaintext[current_position + 0], plaintext[current_position + 1],
|
|
||||||
plaintext[current_position + 2], plaintext[current_position + 3]);
|
|
||||||
current_position += value_size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DataType::LONG_UNSIGNED: {
|
|
||||||
value_size = 2;
|
|
||||||
if (current_position + value_size > message_length) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Buffer too short for LONG_UNSIGNED");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = encode_uint16(plaintext[current_position + 0], plaintext[current_position + 1]);
|
|
||||||
current_position += value_size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DataType::OCTET_STRING: {
|
|
||||||
uint8_t data_length = plaintext[current_position];
|
|
||||||
current_position++; // Advance past string length
|
|
||||||
if (current_position + data_length > message_length) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Buffer too short for OCTET_STRING");
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Handle timestamp (normal OBIS code or NETZNOE special case)
|
|
||||||
if (obis_cd == OBIS_TIMESTAMP || timestamp_found) {
|
|
||||||
if (data_length < 8) {
|
|
||||||
ESP_LOGE(TAG, "OBIS: Timestamp data too short: %u", data_length);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint16_t year = encode_uint16(plaintext[current_position + 0], plaintext[current_position + 1]);
|
|
||||||
uint8_t month = plaintext[current_position + 2];
|
|
||||||
uint8_t day = plaintext[current_position + 3];
|
|
||||||
uint8_t hour = plaintext[current_position + 5];
|
|
||||||
uint8_t minute = plaintext[current_position + 6];
|
|
||||||
uint8_t second = plaintext[current_position + 7];
|
|
||||||
if (year > 9999 || month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59) {
|
|
||||||
ESP_LOGE(TAG, "Invalid timestamp values: %04u-%02u-%02uT%02u:%02u:%02uZ", year, month, day, hour, minute,
|
|
||||||
second);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
snprintf(data.timestamp, sizeof(data.timestamp), "%04u-%02u-%02uT%02u:%02u:%02uZ", year, month, day, hour,
|
|
||||||
minute, second);
|
|
||||||
} else if (meter_number_found) {
|
|
||||||
snprintf(data.meternumber, sizeof(data.meternumber), "%.*s", data_length, &plaintext[current_position]);
|
|
||||||
}
|
|
||||||
current_position += data_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ESP_LOGE(TAG, "OBIS: Unsupported OBIS data type: %x", data_type);
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip break after data
|
|
||||||
if (this->provider_ == PROVIDER_NETZNOE) {
|
|
||||||
// Don't skip the break on the first timestamp, as there's none
|
|
||||||
if (!timestamp_found) {
|
|
||||||
current_position += 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
current_position += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for additional data (scaler-unit structure)
|
|
||||||
if (current_position < message_length && plaintext[current_position] == DataType::INTEGER) {
|
|
||||||
// Apply scaler: real_value = raw_value × 10^scaler
|
|
||||||
if (current_position + 1 < message_length) {
|
|
||||||
int8_t scaler = static_cast<int8_t>(plaintext[current_position + 1]);
|
|
||||||
if (scaler != 0) {
|
|
||||||
value *= powf(10.0f, scaler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// on EVN Meters there is no additional break
|
|
||||||
if (this->provider_ == PROVIDER_NETZNOE) {
|
|
||||||
current_position += 4;
|
|
||||||
} else {
|
|
||||||
current_position += 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle numeric values (LONG_UNSIGNED and DOUBLE_LONG_UNSIGNED)
|
|
||||||
if (value_size > 0) {
|
|
||||||
switch (obis_cd) {
|
|
||||||
case OBIS_VOLTAGE_L1:
|
|
||||||
data.voltage_l1 = value;
|
|
||||||
break;
|
|
||||||
case OBIS_VOLTAGE_L2:
|
|
||||||
data.voltage_l2 = value;
|
|
||||||
break;
|
|
||||||
case OBIS_VOLTAGE_L3:
|
|
||||||
data.voltage_l3 = value;
|
|
||||||
break;
|
|
||||||
case OBIS_CURRENT_L1:
|
|
||||||
data.current_l1 = value;
|
|
||||||
break;
|
|
||||||
case OBIS_CURRENT_L2:
|
|
||||||
data.current_l2 = value;
|
|
||||||
break;
|
|
||||||
case OBIS_CURRENT_L3:
|
|
||||||
data.current_l3 = value;
|
|
||||||
break;
|
|
||||||
case OBIS_ACTIVE_POWER_PLUS:
|
|
||||||
data.active_power_plus = value;
|
|
||||||
break;
|
|
||||||
case OBIS_ACTIVE_POWER_MINUS:
|
|
||||||
data.active_power_minus = value;
|
|
||||||
break;
|
|
||||||
case OBIS_ACTIVE_ENERGY_PLUS:
|
|
||||||
data.active_energy_plus = value;
|
|
||||||
break;
|
|
||||||
case OBIS_ACTIVE_ENERGY_MINUS:
|
|
||||||
data.active_energy_minus = value;
|
|
||||||
break;
|
|
||||||
case OBIS_REACTIVE_ENERGY_PLUS:
|
|
||||||
data.reactive_energy_plus = value;
|
|
||||||
break;
|
|
||||||
case OBIS_REACTIVE_ENERGY_MINUS:
|
|
||||||
data.reactive_energy_minus = value;
|
|
||||||
break;
|
|
||||||
case OBIS_POWER_FACTOR:
|
|
||||||
data.power_factor = value;
|
|
||||||
power_factor_found = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ESP_LOGW(TAG, "Unsupported OBIS code 0x%04X", obis_cd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->receive_buffer_.clear();
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Received valid data");
|
|
||||||
this->publish_sensors(data);
|
|
||||||
this->status_clear_warning();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace esphome::dlms_meter
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#endif
|
|
||||||
#ifdef USE_TEXT_SENSOR
|
|
||||||
#include "esphome/components/text_sensor/text_sensor.h"
|
|
||||||
#endif
|
|
||||||
#include "esphome/components/uart/uart.h"
|
|
||||||
|
|
||||||
#include "mbus.h"
|
|
||||||
#include "dlms.h"
|
|
||||||
#include "obis.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace esphome::dlms_meter {
|
|
||||||
|
|
||||||
#ifndef DLMS_METER_SENSOR_LIST
|
|
||||||
#define DLMS_METER_SENSOR_LIST(F, SEP)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DLMS_METER_TEXT_SENSOR_LIST
|
|
||||||
#define DLMS_METER_TEXT_SENSOR_LIST(F, SEP)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct MeterData {
|
|
||||||
float voltage_l1 = 0.0f; // Voltage L1
|
|
||||||
float voltage_l2 = 0.0f; // Voltage L2
|
|
||||||
float voltage_l3 = 0.0f; // Voltage L3
|
|
||||||
float current_l1 = 0.0f; // Current L1
|
|
||||||
float current_l2 = 0.0f; // Current L2
|
|
||||||
float current_l3 = 0.0f; // Current L3
|
|
||||||
float active_power_plus = 0.0f; // Active power taken from grid
|
|
||||||
float active_power_minus = 0.0f; // Active power put into grid
|
|
||||||
float active_energy_plus = 0.0f; // Active energy taken from grid
|
|
||||||
float active_energy_minus = 0.0f; // Active energy put into grid
|
|
||||||
float reactive_energy_plus = 0.0f; // Reactive energy taken from grid
|
|
||||||
float reactive_energy_minus = 0.0f; // Reactive energy put into grid
|
|
||||||
char timestamp[27]{}; // Text sensor for the timestamp value
|
|
||||||
|
|
||||||
// Netz NOE
|
|
||||||
float power_factor = 0.0f; // Power Factor
|
|
||||||
char meternumber[13]{}; // Text sensor for the meterNumber value
|
|
||||||
};
|
|
||||||
|
|
||||||
// Provider constants
|
|
||||||
enum Providers : uint32_t { PROVIDER_GENERIC = 0x00, PROVIDER_NETZNOE = 0x01 };
|
|
||||||
|
|
||||||
class DlmsMeterComponent : public Component, public uart::UARTDevice {
|
|
||||||
public:
|
|
||||||
DlmsMeterComponent() = default;
|
|
||||||
|
|
||||||
void dump_config() override;
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
void set_decryption_key(const std::array<uint8_t, 16> &key) { this->decryption_key_ = key; }
|
|
||||||
void set_provider(uint32_t provider) { this->provider_ = provider; }
|
|
||||||
|
|
||||||
void publish_sensors(MeterData &data) {
|
|
||||||
#define DLMS_METER_PUBLISH_SENSOR(s) \
|
|
||||||
if (this->s##_sensor_ != nullptr) \
|
|
||||||
s##_sensor_->publish_state(data.s);
|
|
||||||
DLMS_METER_SENSOR_LIST(DLMS_METER_PUBLISH_SENSOR, )
|
|
||||||
|
|
||||||
#define DLMS_METER_PUBLISH_TEXT_SENSOR(s) \
|
|
||||||
if (this->s##_text_sensor_ != nullptr) \
|
|
||||||
s##_text_sensor_->publish_state(data.s);
|
|
||||||
DLMS_METER_TEXT_SENSOR_LIST(DLMS_METER_PUBLISH_TEXT_SENSOR, )
|
|
||||||
}
|
|
||||||
|
|
||||||
DLMS_METER_SENSOR_LIST(SUB_SENSOR, )
|
|
||||||
DLMS_METER_TEXT_SENSOR_LIST(SUB_TEXT_SENSOR, )
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool parse_mbus_(std::vector<uint8_t> &mbus_payload);
|
|
||||||
bool parse_dlms_(const std::vector<uint8_t> &mbus_payload, uint16_t &message_length, uint8_t &systitle_length,
|
|
||||||
uint16_t &header_offset);
|
|
||||||
bool decrypt_(std::vector<uint8_t> &mbus_payload, uint16_t message_length, uint8_t systitle_length,
|
|
||||||
uint16_t header_offset);
|
|
||||||
void decode_obis_(uint8_t *plaintext, uint16_t message_length);
|
|
||||||
|
|
||||||
std::vector<uint8_t> receive_buffer_; // Stores the packet currently being received
|
|
||||||
std::vector<uint8_t> mbus_payload_; // Parsed M-Bus payload, reused to avoid heap churn
|
|
||||||
uint32_t last_read_ = 0; // Timestamp when data was last read
|
|
||||||
uint32_t read_timeout_ = 1000; // Time to wait after last byte before considering data complete
|
|
||||||
|
|
||||||
uint32_t provider_ = PROVIDER_GENERIC; // Provider of the meter / your grid operator
|
|
||||||
std::array<uint8_t, 16> decryption_key_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace esphome::dlms_meter
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace esphome::dlms_meter {
|
|
||||||
|
|
||||||
/*
|
|
||||||
+----------------------------------------------------+ -
|
|
||||||
| Start Character [0x68] | \
|
|
||||||
+----------------------------------------------------+ |
|
|
||||||
| Data Length (L) | |
|
|
||||||
+----------------------------------------------------+ |
|
|
||||||
| Data Length Repeat (L) | |
|
|
||||||
+----------------------------------------------------+ > M-Bus Data link layer
|
|
||||||
| Start Character Repeat [0x68] | |
|
|
||||||
+----------------------------------------------------+ |
|
|
||||||
| Control/Function Field (C) | |
|
|
||||||
+----------------------------------------------------+ |
|
|
||||||
| Address Field (A) | /
|
|
||||||
+----------------------------------------------------+ -
|
|
||||||
| Control Information Field (CI) | \
|
|
||||||
+----------------------------------------------------+ |
|
|
||||||
| Source Transport Service Access Point (STSAP) | > DLMS/COSEM M-Bus transport layer
|
|
||||||
+----------------------------------------------------+ |
|
|
||||||
| Destination Transport Service Access Point (DTSAP) | /
|
|
||||||
+----------------------------------------------------+ -
|
|
||||||
| | \
|
|
||||||
~ ~ |
|
|
||||||
Data > DLMS/COSEM Application Layer
|
|
||||||
~ ~ |
|
|
||||||
| | /
|
|
||||||
+----------------------------------------------------+ -
|
|
||||||
| Checksum | \
|
|
||||||
+----------------------------------------------------+ > M-Bus Data link layer
|
|
||||||
| Stop Character [0x16] | /
|
|
||||||
+----------------------------------------------------+ -
|
|
||||||
|
|
||||||
Data_Length = L - C - A - CI
|
|
||||||
Each line (except Data) is one Byte
|
|
||||||
|
|
||||||
Possible Values found in publicly available docs:
|
|
||||||
- C: 0x53/0x73 (SND_UD)
|
|
||||||
- A: FF (Broadcast)
|
|
||||||
- CI: 0x00-0x1F/0x60/0x61/0x7C/0x7D
|
|
||||||
- STSAP: 0x01 (Management Logical Device ID 1 of the meter)
|
|
||||||
- DTSAP: 0x67 (Consumer Information Push Client ID 103)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// MBUS start bytes for different telegram formats:
|
|
||||||
// - Single Character: 0xE5 (length=1)
|
|
||||||
// - Short Frame: 0x10 (length=5)
|
|
||||||
// - Control Frame: 0x68 (length=9)
|
|
||||||
// - Long Frame: 0x68 (length=9+data_length)
|
|
||||||
// This component currently only uses Long Frame.
|
|
||||||
static constexpr uint8_t START_BYTE_SINGLE_CHARACTER = 0xE5;
|
|
||||||
static constexpr uint8_t START_BYTE_SHORT_FRAME = 0x10;
|
|
||||||
static constexpr uint8_t START_BYTE_CONTROL_FRAME = 0x68;
|
|
||||||
static constexpr uint8_t START_BYTE_LONG_FRAME = 0x68;
|
|
||||||
static constexpr uint8_t MBUS_HEADER_INTRO_LENGTH = 4; // Header length for the intro (0x68, length, length, 0x68)
|
|
||||||
static constexpr uint8_t MBUS_FULL_HEADER_LENGTH = 9; // Total header length
|
|
||||||
static constexpr uint8_t MBUS_FOOTER_LENGTH = 2; // Footer after frame
|
|
||||||
static constexpr uint8_t MBUS_MAX_FRAME_LENGTH = 250; // Maximum size of frame
|
|
||||||
static constexpr uint8_t MBUS_START1_OFFSET = 0; // Offset of first start byte
|
|
||||||
static constexpr uint8_t MBUS_LENGTH1_OFFSET = 1; // Offset of first length byte
|
|
||||||
static constexpr uint8_t MBUS_LENGTH2_OFFSET = 2; // Offset of (duplicated) second length byte
|
|
||||||
static constexpr uint8_t MBUS_START2_OFFSET = 3; // Offset of (duplicated) second start byte
|
|
||||||
static constexpr uint8_t STOP_BYTE = 0x16;
|
|
||||||
|
|
||||||
} // namespace esphome::dlms_meter
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace esphome::dlms_meter {
|
|
||||||
|
|
||||||
// Data types as per specification
|
|
||||||
enum DataType {
|
|
||||||
NULL_DATA = 0x00,
|
|
||||||
BOOLEAN = 0x03,
|
|
||||||
BIT_STRING = 0x04,
|
|
||||||
DOUBLE_LONG = 0x05,
|
|
||||||
DOUBLE_LONG_UNSIGNED = 0x06,
|
|
||||||
OCTET_STRING = 0x09,
|
|
||||||
VISIBLE_STRING = 0x0A,
|
|
||||||
UTF8_STRING = 0x0C,
|
|
||||||
BINARY_CODED_DECIMAL = 0x0D,
|
|
||||||
INTEGER = 0x0F,
|
|
||||||
LONG = 0x10,
|
|
||||||
UNSIGNED = 0x11,
|
|
||||||
LONG_UNSIGNED = 0x12,
|
|
||||||
LONG64 = 0x14,
|
|
||||||
LONG64_UNSIGNED = 0x15,
|
|
||||||
ENUM = 0x16,
|
|
||||||
FLOAT32 = 0x17,
|
|
||||||
FLOAT64 = 0x18,
|
|
||||||
DATE_TIME = 0x19,
|
|
||||||
DATE = 0x1A,
|
|
||||||
TIME = 0x1B,
|
|
||||||
|
|
||||||
ARRAY = 0x01,
|
|
||||||
STRUCTURE = 0x02,
|
|
||||||
COMPACT_ARRAY = 0x13
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Medium {
|
|
||||||
ABSTRACT = 0x00,
|
|
||||||
ELECTRICITY = 0x01,
|
|
||||||
HEAT_COST_ALLOCATOR = 0x04,
|
|
||||||
COOLING = 0x05,
|
|
||||||
HEAT = 0x06,
|
|
||||||
GAS = 0x07,
|
|
||||||
COLD_WATER = 0x08,
|
|
||||||
HOT_WATER = 0x09,
|
|
||||||
OIL = 0x10,
|
|
||||||
COMPRESSED_AIR = 0x11,
|
|
||||||
NITROGEN = 0x12
|
|
||||||
};
|
|
||||||
|
|
||||||
// Data structure
|
|
||||||
static constexpr uint8_t DECODER_START_OFFSET = 20; // Skip header, timestamp and break block
|
|
||||||
static constexpr uint8_t OBIS_TYPE_OFFSET = 0;
|
|
||||||
static constexpr uint8_t OBIS_LENGTH_OFFSET = 1;
|
|
||||||
static constexpr uint8_t OBIS_CODE_OFFSET = 2;
|
|
||||||
static constexpr uint8_t OBIS_CODE_LENGTH_STANDARD = 0x06; // 6-byte OBIS code (A.B.C.D.E.F)
|
|
||||||
static constexpr uint8_t OBIS_CODE_LENGTH_EXTENDED = 0x0C; // 12-byte extended OBIS code
|
|
||||||
static constexpr uint8_t OBIS_A = 0;
|
|
||||||
static constexpr uint8_t OBIS_B = 1;
|
|
||||||
static constexpr uint8_t OBIS_C = 2;
|
|
||||||
static constexpr uint8_t OBIS_D = 3;
|
|
||||||
static constexpr uint8_t OBIS_E = 4;
|
|
||||||
static constexpr uint8_t OBIS_F = 5;
|
|
||||||
|
|
||||||
// Metadata
|
|
||||||
static constexpr uint16_t OBIS_TIMESTAMP = 0x0100;
|
|
||||||
static constexpr uint16_t OBIS_SERIAL_NUMBER = 0x6001;
|
|
||||||
static constexpr uint16_t OBIS_DEVICE_NAME = 0x2A00;
|
|
||||||
|
|
||||||
// Voltage
|
|
||||||
static constexpr uint16_t OBIS_VOLTAGE_L1 = 0x2007;
|
|
||||||
static constexpr uint16_t OBIS_VOLTAGE_L2 = 0x3407;
|
|
||||||
static constexpr uint16_t OBIS_VOLTAGE_L3 = 0x4807;
|
|
||||||
|
|
||||||
// Current
|
|
||||||
static constexpr uint16_t OBIS_CURRENT_L1 = 0x1F07;
|
|
||||||
static constexpr uint16_t OBIS_CURRENT_L2 = 0x3307;
|
|
||||||
static constexpr uint16_t OBIS_CURRENT_L3 = 0x4707;
|
|
||||||
|
|
||||||
// Power
|
|
||||||
static constexpr uint16_t OBIS_ACTIVE_POWER_PLUS = 0x0107;
|
|
||||||
static constexpr uint16_t OBIS_ACTIVE_POWER_MINUS = 0x0207;
|
|
||||||
|
|
||||||
// Active energy
|
|
||||||
static constexpr uint16_t OBIS_ACTIVE_ENERGY_PLUS = 0x0108;
|
|
||||||
static constexpr uint16_t OBIS_ACTIVE_ENERGY_MINUS = 0x0208;
|
|
||||||
|
|
||||||
// Reactive energy
|
|
||||||
static constexpr uint16_t OBIS_REACTIVE_ENERGY_PLUS = 0x0308;
|
|
||||||
static constexpr uint16_t OBIS_REACTIVE_ENERGY_MINUS = 0x0408;
|
|
||||||
|
|
||||||
// Netz NOE specific
|
|
||||||
static constexpr uint16_t OBIS_POWER_FACTOR = 0x0D07;
|
|
||||||
|
|
||||||
} // namespace esphome::dlms_meter
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import (
|
|
||||||
CONF_ID,
|
|
||||||
DEVICE_CLASS_CURRENT,
|
|
||||||
DEVICE_CLASS_ENERGY,
|
|
||||||
DEVICE_CLASS_POWER,
|
|
||||||
DEVICE_CLASS_POWER_FACTOR,
|
|
||||||
DEVICE_CLASS_VOLTAGE,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
|
||||||
UNIT_AMPERE,
|
|
||||||
UNIT_VOLT,
|
|
||||||
UNIT_WATT,
|
|
||||||
UNIT_WATT_HOURS,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .. import CONF_DLMS_METER_ID, DlmsMeterComponent
|
|
||||||
|
|
||||||
AUTO_LOAD = ["dlms_meter"]
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(CONF_DLMS_METER_ID): cv.use_id(DlmsMeterComponent),
|
|
||||||
cv.Optional("voltage_l1"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_VOLT,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_VOLTAGE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("voltage_l2"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_VOLT,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_VOLTAGE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("voltage_l3"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_VOLT,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_VOLTAGE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("current_l1"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_AMPERE,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
device_class=DEVICE_CLASS_CURRENT,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("current_l2"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_AMPERE,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
device_class=DEVICE_CLASS_CURRENT,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("current_l3"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_AMPERE,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
device_class=DEVICE_CLASS_CURRENT,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("active_power_plus"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_WATT,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_POWER,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("active_power_minus"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_WATT,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_POWER,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional("active_energy_plus"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_WATT_HOURS,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
|
||||||
),
|
|
||||||
cv.Optional("active_energy_minus"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_WATT_HOURS,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
|
||||||
),
|
|
||||||
cv.Optional("reactive_energy_plus"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_WATT_HOURS,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
|
||||||
),
|
|
||||||
cv.Optional("reactive_energy_minus"): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_WATT_HOURS,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
|
||||||
),
|
|
||||||
# Netz NOE
|
|
||||||
cv.Optional("power_factor"): sensor.sensor_schema(
|
|
||||||
accuracy_decimals=3,
|
|
||||||
device_class=DEVICE_CLASS_POWER_FACTOR,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
hub = await cg.get_variable(config[CONF_DLMS_METER_ID])
|
|
||||||
|
|
||||||
sensors = []
|
|
||||||
for key, conf in config.items():
|
|
||||||
if not isinstance(conf, dict):
|
|
||||||
continue
|
|
||||||
id = conf[CONF_ID]
|
|
||||||
if id and id.type == sensor.Sensor:
|
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
|
|
||||||
sensors.append(f"F({key})")
|
|
||||||
|
|
||||||
if sensors:
|
|
||||||
cg.add_define(
|
|
||||||
"DLMS_METER_SENSOR_LIST(F, sep)", cg.RawExpression(" sep ".join(sensors))
|
|
||||||
)
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import text_sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
from .. import CONF_DLMS_METER_ID, DlmsMeterComponent
|
|
||||||
|
|
||||||
AUTO_LOAD = ["dlms_meter"]
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(CONF_DLMS_METER_ID): cv.use_id(DlmsMeterComponent),
|
|
||||||
cv.Optional("timestamp"): text_sensor.text_sensor_schema(),
|
|
||||||
# Netz NOE
|
|
||||||
cv.Optional("meternumber"): text_sensor.text_sensor_schema(),
|
|
||||||
}
|
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
hub = await cg.get_variable(config[CONF_DLMS_METER_ID])
|
|
||||||
|
|
||||||
text_sensors = []
|
|
||||||
for key, conf in config.items():
|
|
||||||
if not isinstance(conf, dict):
|
|
||||||
continue
|
|
||||||
id = conf[CONF_ID]
|
|
||||||
if id and id.type == text_sensor.TextSensor:
|
|
||||||
sens = await text_sensor.new_text_sensor(conf)
|
|
||||||
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
|
|
||||||
text_sensors.append(f"F({key})")
|
|
||||||
|
|
||||||
if text_sensors:
|
|
||||||
cg.add_define(
|
|
||||||
"DLMS_METER_TEXT_SENSOR_LIST(F, sep)",
|
|
||||||
cg.RawExpression(" sep ".join(text_sensors)),
|
|
||||||
)
|
|
||||||
@@ -98,6 +98,8 @@ void DPS310Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float DPS310Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void DPS310Component::update() {
|
void DPS310Component::update() {
|
||||||
if (!this->update_in_progress_) {
|
if (!this->update_in_progress_) {
|
||||||
this->update_in_progress_ = true;
|
this->update_in_progress_ = true;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user