Merge branch 'dev' into posix_tz

This commit is contained in:
J. Nick Koston
2026-02-12 05:40:07 -06:00
committed by GitHub
694 changed files with 13157 additions and 6840 deletions
+1 -1
View File
@@ -1 +1 @@
cf3d341206b4184ec8b7fe85141aef4fe4696aa720c3f8a06d4e57930574bdab
ce05c28e9dc0b12c4f6e7454986ffea5123ac974a949da841be698c535f2083e
+2 -2
View File
@@ -47,7 +47,7 @@ runs:
- name: Build and push to ghcr by digest
id: build-ghcr
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
uses: docker/build-push-action@601a80b39c9405e50806ae38af30926f9d957c47 # v6.19.1
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
@@ -73,7 +73,7 @@ runs:
- name: Build and push to dockerhub by digest
id: build-dockerhub
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
uses: docker/build-push-action@601a80b39c9405e50806ae38af30926f9d957c47 # v6.19.1
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
+1
View File
@@ -115,6 +115,7 @@ jobs:
python-version:
- "3.11"
- "3.13"
- "3.14"
os:
- ubuntu-latest
- macOS-latest
+2 -2
View File
@@ -58,7 +58,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -86,6 +86,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
with:
category: "/language:${{matrix.language}}"
+1 -1
View File
@@ -11,7 +11,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.14
rev: v0.15.0
hooks:
# Run the linter.
- id: ruff
+2 -1
View File
@@ -134,6 +134,7 @@ esphome/components/dfplayer/* @glmnet
esphome/components/dfrobot_sen0395/* @niklasweber
esphome/components/dht/* @OttoWinter
esphome/components/display_menu_base/* @numo68
esphome/components/dlms_meter/* @SimonFischer04
esphome/components/dps310/* @kbx81
esphome/components/ds1307/* @badbadc0ffee
esphome/components/ds2484/* @mrk-its
@@ -531,7 +532,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli
esphome/components/ultrasonic/* @OttoWinter
esphome/components/ultrasonic/* @ssieb @swoboda1337
esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon
esphome/components/usb_cdc_acm/* @kbx81
+1 -1
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2026.2.0-dev
PROJECT_NUMBER = 2026.3.0-dev
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+1 -1
View File
@@ -23,7 +23,7 @@ RUN if command -v apk > /dev/null; then \
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
RUN pip install --no-cache-dir -U pip uv==0.6.14
RUN pip install --no-cache-dir -U pip uv==0.10.1
COPY requirements.txt /
+7 -2
View File
@@ -294,8 +294,13 @@ def has_api() -> bool:
def has_ota() -> bool:
"""Check if OTA is available."""
return CONF_OTA in CORE.config
"""Check if OTA upload is available (requires platform: esphome)."""
if CONF_OTA not 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:
File diff suppressed because it is too large Load Diff
+103 -1
View File
@@ -4,6 +4,8 @@ from __future__ import annotations
from collections import defaultdict
from collections.abc import Callable
import heapq
from operator import itemgetter
import sys
from typing import TYPE_CHECKING
@@ -12,6 +14,7 @@ from . import (
_COMPONENT_CORE,
_COMPONENT_PREFIX_ESPHOME,
_COMPONENT_PREFIX_EXTERNAL,
_COMPONENT_PREFIX_LIB,
RAM_SECTIONS,
MemoryAnalyzer,
)
@@ -29,6 +32,10 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
)
# Lower threshold for RAM symbols (RAM is more constrained)
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
COL_COMPONENT: int = 29
@@ -147,6 +154,83 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
section_label = f" [{section[1:]}]" # .data -> [data], .bss -> [bss]
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:
"""Generate a formatted memory report."""
components = sorted(
@@ -248,6 +332,9 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
"RAM",
)
# Top largest symbols in the binary
self._add_top_symbols(lines)
# Add ESPHome core detailed analysis if there are core symbols
if self._esphome_core_symbols:
self._add_section_header(lines, f"{_COMPONENT_CORE} Detailed Analysis")
@@ -321,6 +408,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
for name, mem in components
if name.startswith(_COMPONENT_PREFIX_EXTERNAL)
]
library_components = [
(name, mem)
for name, mem in components
if name.startswith(_COMPONENT_PREFIX_LIB)
]
top_esphome_components = sorted(
esphome_components, key=lambda x: x[1].flash_total, reverse=True
@@ -331,6 +423,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
external_components, key=lambda x: x[1].flash_total, reverse=True
)
# Include all library components
top_library_components = sorted(
library_components, key=lambda x: x[1].flash_total, reverse=True
)
# Check if API component exists and ensure it's included
api_component = None
for name, mem in components:
@@ -349,10 +446,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
if name in system_components_to_include
]
# Combine all components to analyze: top ESPHome + all external + API if not already included + system components
# Combine all components to analyze: top ESPHome + all external + libraries + API if not already included + system components
components_to_analyze = (
list(top_esphome_components)
+ list(top_external_components)
+ list(top_library_components)
+ system_components
)
if api_component and api_component not in components_to_analyze:
@@ -431,6 +529,10 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append(f" ... and {len(large_ram_syms) - 10} more")
lines.append("")
# CSWTCH (GCC switch table) analysis
if self._cswtch_symbols:
self._add_cswtch_analysis(lines)
lines.append(
"Note: This analysis covers symbols in the ELF file. Some runtime allocations may not be included."
)
+3 -10
View File
@@ -66,15 +66,6 @@ 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
# Symbol patterns: patterns found in raw symbol names
SYMBOL_PATTERNS = {
@@ -513,7 +504,9 @@ SYMBOL_PATTERNS = {
"__FUNCTION__$",
"DAYS_IN_MONTH",
"_DAYS_BEFORE_MONTH",
"CSWTCH$",
# Note: CSWTCH$ symbols are GCC switch table lookup tables.
# They are attributed to their source object files via _analyze_cswtch_symbols()
# rather than being lumped into libc.
"dst$",
"sulp",
"_strtol_l", # String to long with locale
+1
View File
@@ -87,6 +87,7 @@ from esphome.cpp_types import ( # noqa: F401
size_t,
std_ns,
std_shared_ptr,
std_span,
std_string,
std_string_ref,
std_vector,
@@ -45,8 +45,6 @@ void AbsoluteHumidityComponent::dump_config() {
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() {
if (!this->next_update_) {
return;
@@ -24,7 +24,6 @@ class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
protected:
-5
View File
@@ -68,11 +68,6 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
/// This method is called during the ESPHome setup process to log the configuration.
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
/// Set the ADC channel to be used by the ADC sensor.
/// @param channel Pointer to an adc_dt_spec structure representing the ADC channel.
@@ -79,7 +79,5 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
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 esphome
+11 -11
View File
@@ -42,11 +42,11 @@ void ADCSensor::setup() {
adc_oneshot_unit_init_cfg_t init_config = {}; // Zero initialize
init_config.unit_id = this->adc_unit_;
init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 ||
// USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
#endif // USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 ||
// USE_ESP32_VARIANT_ESP32C6 || 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_]);
if (err != ESP_OK) {
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 || \
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
// RISC-V variants and S3 use curve fitting calibration
// RISC-V variants (except C2) and S3 use curve fitting calibration
adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
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);
this->setup_flags_.calibration_complete = false;
}
#else // Other ESP32 variants use line fitting calibration
#else // ESP32, ESP32-S2, and ESP32-C2 use line fitting calibration
adc_cali_line_fitting_config_t cali_config = {
.unit_id = this->adc_unit_,
.atten = this->attenuation_,
.bitwidth = ADC_BITWIDTH_DEFAULT,
#if !defined(USE_ESP32_VARIANT_ESP32S2)
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
.default_vref = 1100, // Default reference voltage in mV
#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
#endif // !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
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);
this->setup_flags_.calibration_complete = false;
}
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
}
this->setup_flags_.init_complete = true;
@@ -189,7 +189,7 @@ float ADCSensor::sample_fixed_attenuation_() {
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
#else // Other ESP32 variants use line fitting calibration
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
this->calibration_handle_ = nullptr;
}
}
@@ -247,7 +247,7 @@ float ADCSensor::sample_autorange_() {
.unit_id = this->adc_unit_,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
#if !defined(USE_ESP32_VARIANT_ESP32S2)
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
.default_vref = 1100,
#endif
};
@@ -9,8 +9,6 @@ static const char *const TAG = "adc128s102.sensor";
ADC128S102Sensor::ADC128S102Sensor(uint8_t channel) : channel_(channel) {}
float ADC128S102Sensor::get_setup_priority() const { return setup_priority::DATA; }
void ADC128S102Sensor::dump_config() {
LOG_SENSOR("", "ADC128S102 Sensor", this);
ESP_LOGCONFIG(TAG, " Pin: %u", this->channel_);
@@ -19,7 +19,6 @@ class ADC128S102Sensor : public PollingComponent,
void update() override;
void dump_config() override;
float get_setup_priority() const override;
float sample() override;
protected:
-2
View File
@@ -150,8 +150,6 @@ void AHT10Component::update() {
this->restart_read_();
}
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
void AHT10Component::dump_config() {
ESP_LOGCONFIG(TAG, "AHT10:");
LOG_I2C_DEVICE(this);
-1
View File
@@ -16,7 +16,6 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override;
void set_variant(AHT10Variant variant) { this->variant_ = variant; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
@@ -1,32 +1,15 @@
#include "alarm_control_panel_state.h"
#include "esphome/core/progmem.h"
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) {
switch (state) {
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");
}
return AlarmControlPanelStateStrings::get_log_str(static_cast<uint8_t>(state),
AlarmControlPanelStateStrings::LAST_INDEX);
}
} // namespace esphome::alarm_control_panel
-2
View File
@@ -176,7 +176,5 @@ void AM2315C::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
float AM2315C::get_setup_priority() const { return setup_priority::DATA; }
} // namespace am2315c
} // namespace esphome
-1
View File
@@ -33,7 +33,6 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
void dump_config() override;
void update() 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_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
-1
View File
@@ -51,7 +51,6 @@ void AM2320Component::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_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) {
if (!this->write_bytes(a_register, data, 2)) {
-1
View File
@@ -11,7 +11,6 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
-1
View File
@@ -384,7 +384,6 @@ 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 {
return
#ifdef USE_SENSOR
-1
View File
@@ -32,7 +32,6 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void loop() override;
+4 -1
View File
@@ -45,6 +45,7 @@ service APIConnection {
rpc time_command (TimeCommandRequest) returns (void) {}
rpc update_command (UpdateCommandRequest) returns (void) {}
rpc valve_command (ValveCommandRequest) returns (void) {}
rpc water_heater_command (WaterHeaterCommandRequest) returns (void) {}
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
@@ -1154,9 +1155,11 @@ enum WaterHeaterCommandHasField {
WATER_HEATER_COMMAND_HAS_NONE = 0;
WATER_HEATER_COMMAND_HAS_MODE = 1;
WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE = 2;
WATER_HEATER_COMMAND_HAS_STATE = 4;
WATER_HEATER_COMMAND_HAS_STATE = 4 [deprecated=true];
WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_LOW = 8;
WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH = 16;
WATER_HEATER_COMMAND_HAS_ON_STATE = 32;
WATER_HEATER_COMMAND_HAS_AWAY_STATE = 64;
}
message WaterHeaterCommandRequest {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+15 -3
View File
@@ -16,7 +16,12 @@ static const char *const TAG = "api.frame_helper";
static constexpr size_t API_MAX_LOG_BYTES = 168;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, this->client_peername_, ##__VA_ARGS__)
#define HELPER_LOG(msg, ...) \
do { \
char peername_buf[socket::SOCKADDR_STR_LEN]; \
this->get_peername_to(peername_buf); \
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername_buf, ##__VA_ARGS__); \
} while (0)
#else
#define HELPER_LOG(msg, ...) ((void) 0)
#endif
@@ -240,13 +245,20 @@ APIError APIFrameHelper::try_send_tx_buf_() {
return APIError::OK; // All buffers sent successfully
}
const char *APIFrameHelper::get_peername_to(std::span<char, socket::SOCKADDR_STR_LEN> buf) const {
if (this->socket_) {
this->socket_->getpeername_to(buf);
} else {
buf[0] = '\0';
}
return buf.data();
}
APIError APIFrameHelper::init_common_() {
if (state_ != State::INITIALIZE || this->socket_ == nullptr) {
HELPER_LOG("Bad state for init %d", (int) state_);
return APIError::BAD_STATE;
}
// Cache peername now while socket is valid - needed for error logging after socket failure
this->socket_->getpeername_to(this->client_peername_);
int err = this->socket_->setblocking(false);
if (err != 0) {
state_ = State::FAILED;
+5 -4
View File
@@ -90,8 +90,9 @@ class APIFrameHelper {
// Get client name (null-terminated)
const char *get_client_name() const { return this->client_name_; }
// Get client peername/IP (null-terminated, cached at init time for availability after socket failure)
const char *get_client_peername() const { return this->client_peername_; }
// Get client peername/IP into caller-provided buffer (fetches on-demand from socket)
// Returns pointer to buf for convenience in printf-style calls
const char *get_peername_to(std::span<char, socket::SOCKADDR_STR_LEN> buf) const;
// Set client name from buffer with length (truncates if needed)
void set_client_name(const char *name, size_t len) {
size_t copy_len = std::min(len, sizeof(this->client_name_) - 1);
@@ -105,6 +106,8 @@ class APIFrameHelper {
bool can_write_without_blocking() { return this->state_ == State::DATA && this->tx_buf_count_ == 0; }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
APIError close() {
if (state_ == State::CLOSED)
return APIError::OK; // Already closed
state_ = State::CLOSED;
int err = this->socket_->close();
if (err == -1)
@@ -231,8 +234,6 @@ class APIFrameHelper {
// Client name buffer - stores name from Hello message or initial peername
char client_name_[CLIENT_INFO_NAME_MAX_LEN]{};
// Cached peername/IP address - captured at init time for availability after socket failure
char client_peername_[socket::SOCKADDR_STR_LEN]{};
// Group smaller types together
uint16_t rx_buf_len_ = 0;
@@ -29,7 +29,12 @@ static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
static constexpr size_t API_MAX_LOG_BYTES = 168;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, this->client_peername_, ##__VA_ARGS__)
#define HELPER_LOG(msg, ...) \
do { \
char peername_buf[socket::SOCKADDR_STR_LEN]; \
this->get_peername_to(peername_buf); \
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername_buf, ##__VA_ARGS__); \
} while (0)
#else
#define HELPER_LOG(msg, ...) ((void) 0)
#endif
@@ -21,7 +21,12 @@ static const char *const TAG = "api.plaintext";
static constexpr size_t API_MAX_LOG_BYTES = 168;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, this->client_peername_, ##__VA_ARGS__)
#define HELPER_LOG(msg, ...) \
do { \
char peername_buf[socket::SOCKADDR_STR_LEN]; \
this->get_peername_to(peername_buf); \
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername_buf, ##__VA_ARGS__); \
} while (0)
#else
#define HELPER_LOG(msg, ...) ((void) 0)
#endif
@@ -290,9 +295,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffe
buf_start[header_offset] = 0x00; // indicator
// Encode varints directly into buffer
ProtoVarInt(msg.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
ProtoVarInt(msg.message_type)
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
encode_varint_to_buffer(msg.payload_size, buf_start + header_offset + 1);
encode_varint_to_buffer(msg.message_type, buf_start + header_offset + 1 + size_varint_len);
// Add iovec for this message (header + payload)
size_t msg_len = static_cast<size_t>(total_header_len + msg.payload_size);
+2 -91
View File
@@ -147,6 +147,8 @@ enum WaterHeaterCommandHasField : uint32_t {
WATER_HEATER_COMMAND_HAS_STATE = 4,
WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_LOW = 8,
WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH = 16,
WATER_HEATER_COMMAND_HAS_ON_STATE = 32,
WATER_HEATER_COMMAND_HAS_AWAY_STATE = 64,
};
#ifdef USE_NUMBER
enum NumberMode : uint32_t {
@@ -440,19 +442,6 @@ class PingResponse final : public ProtoMessage {
protected:
};
class DeviceInfoRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 9;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "device_info_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
#ifdef USE_AREAS
class AreaInfo final : public ProtoMessage {
public:
@@ -546,19 +535,6 @@ class DeviceInfoResponse final : public ProtoMessage {
protected:
};
class ListEntitiesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 11;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class ListEntitiesDoneResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 19;
@@ -572,19 +548,6 @@ class ListEntitiesDoneResponse final : public ProtoMessage {
protected:
};
class SubscribeStatesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 20;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_states_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
#ifdef USE_BINARY_SENSOR
class ListEntitiesBinarySensorResponse final : public InfoResponseProtoMessage {
public:
@@ -1037,19 +1000,6 @@ class NoiseEncryptionSetKeyResponse final : public ProtoMessage {
};
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
class SubscribeHomeassistantServicesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 34;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_homeassistant_services_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class HomeassistantServiceMap final : public ProtoMessage {
public:
StringRef key{};
@@ -1117,19 +1067,6 @@ class HomeassistantActionResponse final : public ProtoDecodableMessage {
};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
class SubscribeHomeAssistantStatesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 38;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_home_assistant_states_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class SubscribeHomeAssistantStateResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 39;
@@ -2160,19 +2097,6 @@ class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
protected:
};
class SubscribeBluetoothConnectionsFreeRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 80;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_bluetooth_connections_free_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class BluetoothConnectionsFreeResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 81;
@@ -2279,19 +2203,6 @@ class BluetoothDeviceUnpairingResponse final : public ProtoMessage {
protected:
};
class UnsubscribeBluetoothLEAdvertisementsRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 87;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "unsubscribe_bluetooth_le_advertisements_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class BluetoothDeviceClearCacheResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 88;
+10 -49
View File
@@ -23,15 +23,8 @@ static inline void append_field_prefix(DumpBuffer &out, const char *field_name,
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) {
char buf[16];
snprintf(buf, sizeof(buf), "%" PRIu32, value);
out.append(buf);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu32, value));
}
// RAII helper for message dump formatting
@@ -49,31 +42,23 @@ class MessageDumpHelper {
// 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) {
char buffer[64];
append_field_prefix(out, field_name, indent);
snprintf(buffer, 64, "%" PRId32, value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRId32 "\n", value));
}
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);
snprintf(buffer, 64, "%" PRIu32, value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu32 "\n", value));
}
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);
snprintf(buffer, 64, "%g", value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%g\n", value));
}
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);
snprintf(buffer, 64, "%" PRIu64, value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu64 "\n", value));
}
static void dump_field(DumpBuffer &out, const char *field_name, bool value, int indent = 2) {
@@ -112,7 +97,7 @@ static void dump_bytes_field(DumpBuffer &out, const char *field_name, const uint
char hex_buf[format_hex_pretty_size(160)];
append_field_prefix(out, field_name, indent);
format_hex_pretty_to(hex_buf, data, len);
append_with_newline(out, hex_buf);
out.append(hex_buf).append("\n");
}
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
@@ -400,6 +385,10 @@ const char *proto_enum_to_string<enums::WaterHeaterCommandHasField>(enums::Water
return "WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_LOW";
case enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH:
return "WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH";
case enums::WATER_HEATER_COMMAND_HAS_ON_STATE:
return "WATER_HEATER_COMMAND_HAS_ON_STATE";
case enums::WATER_HEATER_COMMAND_HAS_AWAY_STATE:
return "WATER_HEATER_COMMAND_HAS_AWAY_STATE";
default:
return "UNKNOWN";
}
@@ -779,10 +768,6 @@ const char *PingResponse::dump_to(DumpBuffer &out) const {
out.append("PingResponse {}");
return out.c_str();
}
const char *DeviceInfoRequest::dump_to(DumpBuffer &out) const {
out.append("DeviceInfoRequest {}");
return out.c_str();
}
#ifdef USE_AREAS
const char *AreaInfo::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "AreaInfo");
@@ -863,18 +848,10 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
#endif
return out.c_str();
}
const char *ListEntitiesRequest::dump_to(DumpBuffer &out) const {
out.append("ListEntitiesRequest {}");
return out.c_str();
}
const char *ListEntitiesDoneResponse::dump_to(DumpBuffer &out) const {
out.append("ListEntitiesDoneResponse {}");
return out.c_str();
}
const char *SubscribeStatesRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeStatesRequest {}");
return out.c_str();
}
#ifdef USE_BINARY_SENSOR
const char *ListEntitiesBinarySensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "ListEntitiesBinarySensorResponse");
@@ -1206,10 +1183,6 @@ const char *NoiseEncryptionSetKeyResponse::dump_to(DumpBuffer &out) const {
}
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
const char *SubscribeHomeassistantServicesRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeHomeassistantServicesRequest {}");
return out.c_str();
}
const char *HomeassistantServiceMap::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "HomeassistantServiceMap");
dump_field(out, "key", this->key);
@@ -1260,10 +1233,6 @@ const char *HomeassistantActionResponse::dump_to(DumpBuffer &out) const {
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
const char *SubscribeHomeAssistantStatesRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeHomeAssistantStatesRequest {}");
return out.c_str();
}
const char *SubscribeHomeAssistantStateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SubscribeHomeAssistantStateResponse");
dump_field(out, "entity_id", this->entity_id);
@@ -1939,10 +1908,6 @@ const char *BluetoothGATTNotifyDataResponse::dump_to(DumpBuffer &out) const {
dump_bytes_field(out, "data", this->data_ptr_, this->data_len_);
return out.c_str();
}
const char *SubscribeBluetoothConnectionsFreeRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeBluetoothConnectionsFreeRequest {}");
return out.c_str();
}
const char *BluetoothConnectionsFreeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "BluetoothConnectionsFreeResponse");
dump_field(out, "free", this->free);
@@ -1985,10 +1950,6 @@ const char *BluetoothDeviceUnpairingResponse::dump_to(DumpBuffer &out) const {
dump_field(out, "error", this->error);
return out.c_str();
}
const char *UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(DumpBuffer &out) const {
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
return out.c_str();
}
const char *BluetoothDeviceClearCacheResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "BluetoothDeviceClearCacheResponse");
dump_field(out, "address", this->address);
+49 -273
View File
@@ -15,9 +15,29 @@ void APIServerConnectionBase::log_receive_message_(const LogString *name, const
DumpBuffer 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
void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
// Check authentication/connection requirements
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: // No setup required
case DisconnectRequest::MESSAGE_TYPE: // No setup required
case PingRequest::MESSAGE_TYPE: // No setup required
break;
case 9 /* DeviceInfoRequest is empty */: // Connection setup only
if (!this->check_connection_setup_()) {
return;
}
break;
default:
if (!this->check_authenticated_()) {
return;
}
break;
}
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: {
HelloRequest msg;
@@ -29,66 +49,52 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
break;
}
case DisconnectRequest::MESSAGE_TYPE: {
DisconnectRequest msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_disconnect_request"), msg);
this->log_receive_message_(LOG_STR("on_disconnect_request"));
#endif
this->on_disconnect_request(msg);
this->on_disconnect_request();
break;
}
case DisconnectResponse::MESSAGE_TYPE: {
DisconnectResponse msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_disconnect_response"), msg);
this->log_receive_message_(LOG_STR("on_disconnect_response"));
#endif
this->on_disconnect_response(msg);
this->on_disconnect_response();
break;
}
case PingRequest::MESSAGE_TYPE: {
PingRequest msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_ping_request"), msg);
this->log_receive_message_(LOG_STR("on_ping_request"));
#endif
this->on_ping_request(msg);
this->on_ping_request();
break;
}
case PingResponse::MESSAGE_TYPE: {
PingResponse msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_ping_response"), msg);
this->log_receive_message_(LOG_STR("on_ping_response"));
#endif
this->on_ping_response(msg);
this->on_ping_response();
break;
}
case DeviceInfoRequest::MESSAGE_TYPE: {
DeviceInfoRequest msg;
// Empty message: no decode needed
case 9 /* DeviceInfoRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_device_info_request"), msg);
this->log_receive_message_(LOG_STR("on_device_info_request"));
#endif
this->on_device_info_request(msg);
this->on_device_info_request();
break;
}
case ListEntitiesRequest::MESSAGE_TYPE: {
ListEntitiesRequest msg;
// Empty message: no decode needed
case 11 /* ListEntitiesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_list_entities_request"), msg);
this->log_receive_message_(LOG_STR("on_list_entities_request"));
#endif
this->on_list_entities_request(msg);
this->on_list_entities_request();
break;
}
case SubscribeStatesRequest::MESSAGE_TYPE: {
SubscribeStatesRequest msg;
// Empty message: no decode needed
case 20 /* SubscribeStatesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_states_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_states_request"));
#endif
this->on_subscribe_states_request(msg);
this->on_subscribe_states_request();
break;
}
case SubscribeLogsRequest::MESSAGE_TYPE: {
@@ -145,13 +151,11 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
}
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: {
SubscribeHomeassistantServicesRequest msg;
// Empty message: no decode needed
case 34 /* SubscribeHomeassistantServicesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"));
#endif
this->on_subscribe_homeassistant_services_request(msg);
this->on_subscribe_homeassistant_services_request();
break;
}
#endif
@@ -165,13 +169,11 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
break;
}
#ifdef USE_API_HOMEASSISTANT_STATES
case SubscribeHomeAssistantStatesRequest::MESSAGE_TYPE: {
SubscribeHomeAssistantStatesRequest msg;
// Empty message: no decode needed
case 38 /* SubscribeHomeAssistantStatesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"));
#endif
this->on_subscribe_home_assistant_states_request(msg);
this->on_subscribe_home_assistant_states_request();
break;
}
#endif
@@ -374,24 +376,20 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
}
#endif
#ifdef USE_BLUETOOTH_PROXY
case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: {
SubscribeBluetoothConnectionsFreeRequest msg;
// Empty message: no decode needed
case 80 /* SubscribeBluetoothConnectionsFreeRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"));
#endif
this->on_subscribe_bluetooth_connections_free_request(msg);
this->on_subscribe_bluetooth_connections_free_request();
break;
}
#endif
#ifdef USE_BLUETOOTH_PROXY
case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: {
UnsubscribeBluetoothLEAdvertisementsRequest msg;
// Empty message: no decode needed
case 87 /* UnsubscribeBluetoothLEAdvertisementsRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"), msg);
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"));
#endif
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
this->on_unsubscribe_bluetooth_le_advertisements_request();
break;
}
#endif
@@ -642,226 +640,4 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
}
}
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
if (!this->send_hello_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
if (!this->send_disconnect_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_ping_request(const PingRequest &msg) {
if (!this->send_ping_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
if (!this->send_device_info_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) { this->list_entities(msg); }
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); }
#ifdef USE_API_HOMEASSISTANT_SERVICES
void APIServerConnection::on_subscribe_homeassistant_services_request(
const SubscribeHomeassistantServicesRequest &msg) {
this->subscribe_homeassistant_services(msg);
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
this->subscribe_home_assistant_states(msg);
}
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { this->execute_service(msg); }
#endif
#ifdef USE_API_NOISE
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
if (!this->send_noise_encryption_set_key_response(msg)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) { this->button_command(msg); }
#endif
#ifdef USE_CAMERA
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { this->camera_image(msg); }
#endif
#ifdef USE_CLIMATE
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) { this->climate_command(msg); }
#endif
#ifdef USE_COVER
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) { this->cover_command(msg); }
#endif
#ifdef USE_DATETIME_DATE
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) { this->date_command(msg); }
#endif
#ifdef USE_DATETIME_DATETIME
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
this->datetime_command(msg);
}
#endif
#ifdef USE_FAN
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) { this->fan_command(msg); }
#endif
#ifdef USE_LIGHT
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) { this->light_command(msg); }
#endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) { this->lock_command(msg); }
#endif
#ifdef USE_MEDIA_PLAYER
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
this->media_player_command(msg);
}
#endif
#ifdef USE_NUMBER
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) { this->number_command(msg); }
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) { this->select_command(msg); }
#endif
#ifdef USE_SIREN
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) { this->siren_command(msg); }
#endif
#ifdef USE_SWITCH
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) { this->switch_command(msg); }
#endif
#ifdef USE_TEXT
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) { this->text_command(msg); }
#endif
#ifdef USE_DATETIME_TIME
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) { this->time_command(msg); }
#endif
#ifdef USE_UPDATE
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) { this->update_command(msg); }
#endif
#ifdef USE_VALVE
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { this->valve_command(msg); }
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
this->subscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
this->bluetooth_device_request(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
this->bluetooth_gatt_get_services(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
this->bluetooth_gatt_read(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
this->bluetooth_gatt_write(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
this->bluetooth_gatt_read_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
this->bluetooth_gatt_write_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
this->bluetooth_gatt_notify(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
const SubscribeBluetoothConnectionsFreeRequest &msg) {
if (!this->send_subscribe_bluetooth_connections_free_response(msg)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
this->unsubscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
this->bluetooth_scanner_set_mode(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
this->subscribe_voice_assistant(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
if (!this->send_voice_assistant_get_configuration_response(msg)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
this->voice_assistant_set_configuration(msg);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
this->alarm_control_panel_command(msg);
}
#endif
#ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) { this->zwave_proxy_frame(msg); }
#endif
#ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_request(const ZWaveProxyRequest &msg) { this->zwave_proxy_request(msg); }
#endif
#ifdef USE_IR_RF
void APIServerConnection::on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) {
this->infrared_rf_transmit_raw_timings(msg);
}
#endif
void APIServerConnection::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
// Check authentication/connection requirements for messages
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: // No setup required
case DisconnectRequest::MESSAGE_TYPE: // No setup required
case PingRequest::MESSAGE_TYPE: // No setup required
break; // Skip all checks for these messages
case DeviceInfoRequest::MESSAGE_TYPE: // Connection setup only
if (!this->check_connection_setup_()) {
return; // Connection not setup
}
break;
default:
// All other messages require authentication (which includes connection check)
if (!this->check_authenticated_()) {
return; // Authentication failed
}
break;
}
// Call base implementation to process the message
APIServerConnectionBase::read_message(msg_size, msg_type, msg_data);
}
} // namespace esphome::api
+13 -275
View File
@@ -14,6 +14,7 @@ class APIServerConnectionBase : public ProtoService {
protected:
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);
public:
#endif
@@ -23,20 +24,20 @@ class APIServerConnectionBase : public ProtoService {
DumpBuffer dump_buf;
this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
#endif
return this->send_message_(msg, message_type);
return this->send_message_impl(msg, message_type);
}
virtual void on_hello_request(const HelloRequest &value){};
virtual void on_disconnect_request(const DisconnectRequest &value){};
virtual void on_disconnect_response(const DisconnectResponse &value){};
virtual void on_ping_request(const PingRequest &value){};
virtual void on_ping_response(const PingResponse &value){};
virtual void on_device_info_request(const DeviceInfoRequest &value){};
virtual void on_disconnect_request(){};
virtual void on_disconnect_response(){};
virtual void on_ping_request(){};
virtual void on_ping_response(){};
virtual void on_device_info_request(){};
virtual void on_list_entities_request(const ListEntitiesRequest &value){};
virtual void on_list_entities_request(){};
virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
virtual void on_subscribe_states_request(){};
#ifdef USE_COVER
virtual void on_cover_command_request(const CoverCommandRequest &value){};
@@ -61,14 +62,14 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
virtual void on_subscribe_homeassistant_services_request(){};
#endif
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
virtual void on_subscribe_home_assistant_states_request(){};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
@@ -147,12 +148,11 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
virtual void on_subscribe_bluetooth_connections_free_request(){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
virtual void on_unsubscribe_bluetooth_le_advertisements_request(){};
#endif
#ifdef USE_BLUETOOTH_PROXY
@@ -228,266 +228,4 @@ class APIServerConnectionBase : public ProtoService {
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
};
class APIServerConnection : public APIServerConnectionBase {
public:
virtual bool send_hello_response(const HelloRequest &msg) = 0;
virtual bool send_disconnect_response(const DisconnectRequest &msg) = 0;
virtual bool send_ping_response(const PingRequest &msg) = 0;
virtual bool send_device_info_response(const DeviceInfoRequest &msg) = 0;
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
#ifdef USE_API_HOMEASSISTANT_SERVICES
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
#endif
#ifdef USE_API_NOISE
virtual bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) = 0;
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
#ifdef USE_CAMERA
virtual void camera_image(const CameraImageRequest &msg) = 0;
#endif
#ifdef USE_CLIMATE
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
#endif
#ifdef USE_COVER
virtual void cover_command(const CoverCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_DATE
virtual void date_command(const DateCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_DATETIME
virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
#endif
#ifdef USE_FAN
virtual void fan_command(const FanCommandRequest &msg) = 0;
#endif
#ifdef USE_LIGHT
virtual void light_command(const LightCommandRequest &msg) = 0;
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#endif
#ifdef USE_MEDIA_PLAYER
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
#endif
#ifdef USE_NUMBER
virtual void number_command(const NumberCommandRequest &msg) = 0;
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
#ifdef USE_SIREN
virtual void siren_command(const SirenCommandRequest &msg) = 0;
#endif
#ifdef USE_SWITCH
virtual void switch_command(const SwitchCommandRequest &msg) = 0;
#endif
#ifdef USE_TEXT
virtual void text_command(const TextCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_TIME
virtual void time_command(const TimeCommandRequest &msg) = 0;
#endif
#ifdef USE_UPDATE
virtual void update_command(const UpdateCommandRequest &msg) = 0;
#endif
#ifdef USE_VALVE
virtual void valve_command(const ValveCommandRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual bool send_subscribe_bluetooth_connections_free_response(
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
#endif
#ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_frame(const ZWaveProxyFrame &msg) = 0;
#endif
#ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_request(const ZWaveProxyRequest &msg) = 0;
#endif
#ifdef USE_IR_RF
virtual void infrared_rf_transmit_raw_timings(const InfraredRFTransmitRawTimingsRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
void on_disconnect_request(const DisconnectRequest &msg) override;
void on_ping_request(const PingRequest &msg) override;
void on_device_info_request(const DeviceInfoRequest &msg) override;
void on_list_entities_request(const ListEntitiesRequest &msg) override;
void on_subscribe_states_request(const SubscribeStatesRequest &msg) override;
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
#ifdef USE_API_HOMEASSISTANT_SERVICES
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
#endif
#ifdef USE_API_NOISE
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
#endif
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_CAMERA
void on_camera_image_request(const CameraImageRequest &msg) override;
#endif
#ifdef USE_CLIMATE
void on_climate_command_request(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_COVER
void on_cover_command_request(const CoverCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_DATE
void on_date_command_request(const DateCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_DATETIME
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
#endif
#ifdef USE_FAN
void on_fan_command_request(const FanCommandRequest &msg) override;
#endif
#ifdef USE_LIGHT
void on_light_command_request(const LightCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
#ifdef USE_MEDIA_PLAYER
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_SIREN
void on_siren_command_request(const SirenCommandRequest &msg) override;
#endif
#ifdef USE_SWITCH
void on_switch_command_request(const SwitchCommandRequest &msg) override;
#endif
#ifdef USE_TEXT
void on_text_command_request(const TextCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_TIME
void on_time_command_request(const TimeCommandRequest &msg) override;
#endif
#ifdef USE_UPDATE
void on_update_command_request(const UpdateCommandRequest &msg) override;
#endif
#ifdef USE_VALVE
void on_valve_command_request(const ValveCommandRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_request(const ZWaveProxyRequest &msg) override;
#endif
#ifdef USE_IR_RF
void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) override;
#endif
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
};
} // namespace esphome::api
+7 -3
View File
@@ -192,11 +192,15 @@ void APIServer::loop() {
ESP_LOGV(TAG, "Remove connection %s", client->get_name());
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
// Save client info before removal for the trigger
// Save client info before closing socket and removal for the trigger
char peername_buf[socket::SOCKADDR_STR_LEN];
std::string client_name(client->get_name());
std::string client_peername(client->get_peername());
std::string client_peername(client->get_peername_to(peername_buf));
#endif
// Close socket now (was deferred from on_fatal_error to allow getpeername)
client->helper_->close();
// Swap with the last element and pop (avoids expensive vector shifts)
if (client_index < this->clients_.size() - 1) {
std::swap(this->clients_[client_index], this->clients_.back());
@@ -211,7 +215,7 @@ void APIServer::loop() {
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
// 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
// Don't increment client_index since we need to process the swapped element
}
+4 -6
View File
@@ -227,12 +227,10 @@ class APIServer : public Component,
#endif
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
Trigger<std::string, std::string> *get_client_connected_trigger() { return &this->client_connected_trigger_; }
#endif
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
Trigger<std::string, std::string> *get_client_disconnected_trigger() const {
return this->client_disconnected_trigger_;
}
Trigger<std::string, std::string> *get_client_disconnected_trigger() { return &this->client_disconnected_trigger_; }
#endif
protected:
@@ -253,10 +251,10 @@ class APIServer : public Component,
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> client_connected_trigger_;
#endif
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> client_disconnected_trigger_;
#endif
// 4-byte aligned types
+12 -12
View File
@@ -25,7 +25,9 @@ template<typename... X> class TemplatableStringValue : public TemplatableValue<s
private:
// Helper to convert value to string - handles the case where value is already a string
template<typename T> static std::string value_to_string(T &&val) { return to_string(std::forward<T>(val)); }
template<typename T> static std::string value_to_string(T &&val) {
return to_string(std::forward<T>(val)); // NOLINT
}
// Overloads for string types - needed because std::to_string doesn't support them
static std::string value_to_string(char *val) {
@@ -136,12 +138,10 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
void set_wants_response() { this->flags_.wants_response = true; }
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() const {
return this->success_trigger_with_response_;
}
Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() { return &this->success_trigger_with_response_; }
#endif
Trigger<Ts...> *get_success_trigger() const { return this->success_trigger_; }
Trigger<std::string, Ts...> *get_error_trigger() const { return this->error_trigger_; }
Trigger<Ts...> *get_success_trigger() { return &this->success_trigger_; }
Trigger<std::string, Ts...> *get_error_trigger() { return &this->error_trigger_; }
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
void play(const Ts &...x) override {
@@ -187,14 +187,14 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
if (response.is_success()) {
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
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
#endif
{
this->success_trigger_->trigger(args...);
this->success_trigger_.trigger(args...);
}
} else {
this->error_trigger_->trigger(response.get_error_message(), args...);
this->error_trigger_.trigger(response.get_error_message(), args...);
}
},
captured_args);
@@ -251,10 +251,10 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
TemplatableStringValue<Ts...> response_template_{""};
Trigger<JsonObjectConst, Ts...> *success_trigger_with_response_ = new Trigger<JsonObjectConst, Ts...>();
Trigger<JsonObjectConst, Ts...> success_trigger_with_response_;
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
Trigger<Ts...> *success_trigger_ = new Trigger<Ts...>();
Trigger<std::string, Ts...> *error_trigger_ = new Trigger<std::string, Ts...>();
Trigger<Ts...> success_trigger_;
Trigger<std::string, Ts...> error_trigger_;
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
struct Flags {
-1
View File
@@ -94,7 +94,6 @@ class ListEntitiesIterator : public ComponentIterator {
bool on_update(update::UpdateEntity *entity) override;
#endif
bool on_end() override;
bool completed() { return this->state_ == IteratorState::NONE; }
protected:
APIConnection *client_;
+1 -1
View File
@@ -133,7 +133,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
break;
}
default:
ESP_LOGV(TAG, "Invalid field type %u at offset %ld", field_type, (long) (ptr - buffer));
ESP_LOGV(TAG, "Invalid field type %" PRIu32 " at offset %ld", field_type, (long) (ptr - buffer));
return;
}
}
+63 -84
View File
@@ -57,6 +57,16 @@ inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
return count;
}
/// Encode a varint directly into a pre-allocated buffer.
/// Caller must ensure buffer has space (use ProtoSize::varint() to calculate).
inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
while (val > 0x7F) {
*buffer++ = static_cast<uint8_t>(val | 0x80);
val >>= 7;
}
*buffer = static_cast<uint8_t>(val);
}
/*
* StringRef Ownership Model for API Protocol Messages
* ===================================================
@@ -93,17 +103,17 @@ class ProtoVarInt {
ProtoVarInt() : value_(0) {}
explicit ProtoVarInt(uint64_t value) : value_(value) {}
/// Parse a varint from buffer. consumed must be a valid pointer (not null).
static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
if (len == 0) {
if (consumed != nullptr)
*consumed = 0;
#ifdef ESPHOME_DEBUG_API
assert(consumed != nullptr);
#endif
if (len == 0)
return {};
}
// Most common case: single-byte varint (values 0-127)
if ((buffer[0] & 0x80) == 0) {
if (consumed != nullptr)
*consumed = 1;
*consumed = 1;
return ProtoVarInt(buffer[0]);
}
@@ -112,20 +122,21 @@ class ProtoVarInt {
uint64_t result = buffer[0] & 0x7F;
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
for (uint32_t i = 1; i < len; i++) {
for (uint32_t i = 1; i < max_len; i++) {
uint8_t val = buffer[i];
result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
bitpos += 7;
if ((val & 0x80) == 0) {
if (consumed != nullptr)
*consumed = i + 1;
*consumed = i + 1;
return ProtoVarInt(result);
}
}
if (consumed != nullptr)
*consumed = 0;
return {}; // Incomplete or invalid varint
}
@@ -149,50 +160,6 @@ class ProtoVarInt {
// with ZigZag encoding
return decode_zigzag64(this->value_);
}
/**
* Encode the varint value to a pre-allocated buffer without bounds checking.
*
* @param buffer The pre-allocated buffer to write the encoded varint to
* @param len The size of the buffer in bytes
*
* @note The caller is responsible for ensuring the buffer is large enough
* to hold the encoded value. Use ProtoSize::varint() to calculate
* the exact size needed before calling this method.
* @note No bounds checking is performed for performance reasons.
*/
void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) {
uint64_t val = this->value_;
if (val <= 0x7F) {
buffer[0] = val;
return;
}
size_t i = 0;
while (val && i < len) {
uint8_t temp = val & 0x7F;
val >>= 7;
if (val) {
buffer[i++] = temp | 0x80;
} else {
buffer[i++] = temp;
}
}
}
void encode(std::vector<uint8_t> &out) {
uint64_t val = this->value_;
if (val <= 0x7F) {
out.push_back(val);
return;
}
while (val) {
uint8_t temp = val & 0x7F;
val >>= 7;
if (val) {
out.push_back(temp | 0x80);
} else {
out.push_back(temp);
}
}
}
protected:
uint64_t value_;
@@ -252,8 +219,20 @@ class ProtoWriteBuffer {
public:
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
void write(uint8_t value) { this->buffer_->push_back(value); }
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
void encode_varint_raw(uint32_t value) {
while (value > 0x7F) {
this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
value >>= 7;
}
this->buffer_->push_back(static_cast<uint8_t>(value));
}
void encode_varint_raw_64(uint64_t value) {
while (value > 0x7F) {
this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
value >>= 7;
}
this->buffer_->push_back(static_cast<uint8_t>(value));
}
/**
* Encode a field key (tag/wire type combination).
*
@@ -303,13 +282,13 @@ class ProtoWriteBuffer {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
this->encode_varint_raw(ProtoVarInt(value));
this->encode_varint_raw_64(value);
}
void encode_bool(uint32_t field_id, bool value, bool force = false) {
if (!value && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
this->write(0x01);
this->buffer_->push_back(value ? 0x01 : 0x00);
}
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
if (value == 0 && !force)
@@ -402,6 +381,20 @@ class DumpBuffer {
const char *c_str() const { return buf_; }
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:
void append_impl_(const char *str, size_t len) {
size_t space = CAPACITY - 1 - pos_;
@@ -920,13 +913,15 @@ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessa
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
// Write the length varint directly
ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
encode_varint_to_buffer(msg_length_bytes, this->buffer_->data() + begin);
// Now encode the message content - it will append to the buffer
value.encode(*this);
#ifdef ESPHOME_DEBUG_API
// Verify that the encoded size matches what we calculated
assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
#endif
}
// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
@@ -943,32 +938,16 @@ class ProtoService {
virtual bool is_connection_setup() = 0;
virtual void on_fatal_error() = 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 void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
// Optimized method that pre-allocates buffer based on message size
bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
ProtoSize size;
msg.calculate_size(size);
uint32_t msg_size = size.get_size();
// 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);
}
/**
* Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending.
* This is the implementation method - callers should use send_message() which adds logging.
* @param msg The protobuf message to send.
* @param message_type The message type identifier.
* @return True if the message was sent successfully, false otherwise.
*/
virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0;
// Authentication helper methods
inline bool check_connection_setup_() {
-1
View File
@@ -88,7 +88,6 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_UPDATE
bool on_update(update::UpdateEntity *entity) override;
#endif
bool completed() { return this->state_ == IteratorState::NONE; }
protected:
APIConnection *client_;
+30 -8
View File
@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <limits>
#include "abstract_aqi_calculator.h"
@@ -14,7 +15,11 @@ class AQICalculator : public AbstractAQICalculator {
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
return static_cast<uint16_t>(std::round((pm2_5_index < pm10_0_index) ? pm10_0_index : pm2_5_index));
float aqi = std::max(pm2_5_index, pm10_0_index);
if (aqi < 0.0f) {
aqi = 0.0f;
}
return static_cast<uint16_t>(std::lround(aqi));
}
protected:
@@ -22,13 +27,27 @@ class AQICalculator : public AbstractAQICalculator {
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {{0.0f, 9.0f}, {9.1f, 35.4f},
{35.5f, 55.4f}, {55.5f, 125.4f},
{125.5f, 225.4f}, {225.5f, std::numeric_limits<float>::max()}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 9.1f},
{9.1f, 35.5f},
{35.5f, 55.5f},
{55.5f, 125.5f},
{125.5f, 225.5f},
{225.5f, std::numeric_limits<float>::max()}
// clang-format on
};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {{0.0f, 54.0f}, {55.0f, 154.0f},
{155.0f, 254.0f}, {255.0f, 354.0f},
{355.0f, 424.0f}, {425.0f, std::numeric_limits<float>::max()}};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 55.0f},
{55.0f, 155.0f},
{155.0f, 255.0f},
{255.0f, 355.0f},
{355.0f, 425.0f},
{425.0f, std::numeric_limits<float>::max()}
// clang-format on
};
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
int grid_index = get_grid_index(value, array);
@@ -45,7 +64,10 @@ class AQICalculator : public AbstractAQICalculator {
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
for (int i = 0; i < NUM_LEVELS; i++) {
if (value >= array[i][0] && value <= array[i][1]) {
const bool in_range =
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
: (value < array[i][1])); // others exclusive on hi
if (in_range) {
return i;
}
}
-1
View File
@@ -10,7 +10,6 @@ class AQISensor : public sensor::Sensor, public Component {
public:
void setup() 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_10_0_sensor(sensor::Sensor *sensor) { this->pm_10_0_sensor_ = sensor; }
+26 -4
View File
@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <limits>
#include "abstract_aqi_calculator.h"
@@ -12,7 +13,11 @@ class CAQICalculator : public AbstractAQICalculator {
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
return static_cast<uint16_t>(std::round((pm2_5_index < pm10_0_index) ? pm10_0_index : pm2_5_index));
float aqi = std::max(pm2_5_index, pm10_0_index);
if (aqi < 0.0f) {
aqi = 0.0f;
}
return static_cast<uint16_t>(std::lround(aqi));
}
protected:
@@ -21,10 +26,24 @@ class CAQICalculator : public AbstractAQICalculator {
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}, {101, 400}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
{0.0f, 15.0f}, {15.1f, 30.0f}, {30.1f, 55.0f}, {55.1f, 110.0f}, {110.1f, std::numeric_limits<float>::max()}};
// clang-format off
{0.0f, 15.1f},
{15.1f, 30.1f},
{30.1f, 55.1f},
{55.1f, 110.1f},
{110.1f, std::numeric_limits<float>::max()}
// clang-format on
};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {
{0.0f, 25.0f}, {25.1f, 50.0f}, {50.1f, 90.0f}, {90.1f, 180.0f}, {180.1f, std::numeric_limits<float>::max()}};
// clang-format off
{0.0f, 25.1f},
{25.1f, 50.1f},
{50.1f, 90.1f},
{90.1f, 180.1f},
{180.1f, std::numeric_limits<float>::max()}
// clang-format on
};
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
int grid_index = get_grid_index(value, array);
@@ -42,7 +61,10 @@ class CAQICalculator : public AbstractAQICalculator {
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
for (int i = 0; i < NUM_LEVELS; i++) {
if (value >= array[i][0] && value <= array[i][1]) {
const bool in_range =
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
: (value < array[i][1])); // others exclusive on hi
if (in_range) {
return i;
}
}

Some files were not shown because too many files have changed in this diff Show More