mirror of
https://github.com/esphome/esphome.git
synced 2025-09-06 13:22:19 +01:00
Merge remote-tracking branch 'upstream/dev' into ruff_ret
This commit is contained in:
@@ -1 +1 @@
|
|||||||
32b0db73b3ae01ba18c9cbb1dabbd8156bc14dded500471919bd0a3dc33916e0
|
f84518ea4140c194b21cc516aae05aaa0cf876ab866f89e22e91842df46333ed
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import functools
|
import functools
|
||||||
|
import getpass
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@@ -335,7 +336,7 @@ def check_permissions(port):
|
|||||||
raise EsphomeError(
|
raise EsphomeError(
|
||||||
"You do not have read or write permission on the selected serial port. "
|
"You do not have read or write permission on the selected serial port. "
|
||||||
"To resolve this issue, you can add your user to the dialout group "
|
"To resolve this issue, you can add your user to the dialout group "
|
||||||
f"by running the following command: sudo usermod -a -G dialout {os.getlogin()}. "
|
f"by running the following command: sudo usermod -a -G dialout {getpass.getuser()}. "
|
||||||
"You will need to log out & back in or reboot to activate the new group access."
|
"You will need to log out & back in or reboot to activate the new group access."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
from esphome import pins
|
from esphome import pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import VARIANT_ESP32P4, get_esp32_variant
|
||||||
from esphome.components.esp32.const import (
|
from esphome.components.esp32.const import (
|
||||||
VARIANT_ESP32,
|
VARIANT_ESP32,
|
||||||
VARIANT_ESP32C2,
|
VARIANT_ESP32C2,
|
||||||
@@ -140,6 +140,16 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
|||||||
9: adc_channel_t.ADC_CHANNEL_8,
|
9: adc_channel_t.ADC_CHANNEL_8,
|
||||||
10: adc_channel_t.ADC_CHANNEL_9,
|
10: adc_channel_t.ADC_CHANNEL_9,
|
||||||
},
|
},
|
||||||
|
VARIANT_ESP32P4: {
|
||||||
|
16: adc_channel_t.ADC_CHANNEL_0,
|
||||||
|
17: adc_channel_t.ADC_CHANNEL_1,
|
||||||
|
18: adc_channel_t.ADC_CHANNEL_2,
|
||||||
|
19: adc_channel_t.ADC_CHANNEL_3,
|
||||||
|
20: adc_channel_t.ADC_CHANNEL_4,
|
||||||
|
21: adc_channel_t.ADC_CHANNEL_5,
|
||||||
|
22: adc_channel_t.ADC_CHANNEL_6,
|
||||||
|
23: adc_channel_t.ADC_CHANNEL_7,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# pin to adc2 channel mapping
|
# pin to adc2 channel mapping
|
||||||
@@ -198,6 +208,14 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
|||||||
19: adc_channel_t.ADC_CHANNEL_8,
|
19: adc_channel_t.ADC_CHANNEL_8,
|
||||||
20: adc_channel_t.ADC_CHANNEL_9,
|
20: adc_channel_t.ADC_CHANNEL_9,
|
||||||
},
|
},
|
||||||
|
VARIANT_ESP32P4: {
|
||||||
|
49: adc_channel_t.ADC_CHANNEL_0,
|
||||||
|
50: adc_channel_t.ADC_CHANNEL_1,
|
||||||
|
51: adc_channel_t.ADC_CHANNEL_2,
|
||||||
|
52: adc_channel_t.ADC_CHANNEL_3,
|
||||||
|
53: adc_channel_t.ADC_CHANNEL_4,
|
||||||
|
54: adc_channel_t.ADC_CHANNEL_5,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -136,8 +136,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
adc_oneshot_unit_handle_t adc_handle_{nullptr};
|
adc_oneshot_unit_handle_t adc_handle_{nullptr};
|
||||||
adc_cali_handle_t calibration_handle_{nullptr};
|
adc_cali_handle_t calibration_handle_{nullptr};
|
||||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
adc_channel_t channel_;
|
adc_channel_t channel_{};
|
||||||
adc_unit_t adc_unit_;
|
adc_unit_t adc_unit_{};
|
||||||
struct SetupFlags {
|
struct SetupFlags {
|
||||||
uint8_t init_complete : 1;
|
uint8_t init_complete : 1;
|
||||||
uint8_t config_complete : 1;
|
uint8_t config_complete : 1;
|
||||||
|
@@ -72,10 +72,9 @@ void ADCSensor::setup() {
|
|||||||
// Initialize ADC calibration
|
// Initialize ADC calibration
|
||||||
if (this->calibration_handle_ == nullptr) {
|
if (this->calibration_handle_ == nullptr) {
|
||||||
adc_cali_handle_t handle = nullptr;
|
adc_cali_handle_t handle = nullptr;
|
||||||
esp_err_t err;
|
|
||||||
|
|
||||||
#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_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
|
||||||
// RISC-V variants 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)
|
||||||
@@ -187,7 +186,7 @@ float ADCSensor::sample_fixed_attenuation_() {
|
|||||||
ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err);
|
ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err);
|
||||||
if (this->calibration_handle_ != nullptr) {
|
if (this->calibration_handle_ != nullptr) {
|
||||||
#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_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
|
||||||
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_);
|
||||||
@@ -220,7 +219,7 @@ float ADCSensor::sample_autorange_() {
|
|||||||
if (this->calibration_handle_ != nullptr) {
|
if (this->calibration_handle_ != nullptr) {
|
||||||
// Delete old calibration handle
|
// Delete old calibration handle
|
||||||
#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_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
|
||||||
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
|
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
|
||||||
#else
|
#else
|
||||||
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
|
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
|
||||||
@@ -232,7 +231,7 @@ float ADCSensor::sample_autorange_() {
|
|||||||
adc_cali_handle_t handle = nullptr;
|
adc_cali_handle_t handle = nullptr;
|
||||||
|
|
||||||
#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_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
|
||||||
adc_cali_curve_fitting_config_t cali_config = {};
|
adc_cali_curve_fitting_config_t cali_config = {};
|
||||||
#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_;
|
||||||
@@ -261,7 +260,7 @@ float ADCSensor::sample_autorange_() {
|
|||||||
ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
|
ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
|
||||||
if (handle != nullptr) {
|
if (handle != nullptr) {
|
||||||
#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_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
|
||||||
adc_cali_delete_scheme_curve_fitting(handle);
|
adc_cali_delete_scheme_curve_fitting(handle);
|
||||||
#else
|
#else
|
||||||
adc_cali_delete_scheme_line_fitting(handle);
|
adc_cali_delete_scheme_line_fitting(handle);
|
||||||
@@ -281,7 +280,7 @@ float ADCSensor::sample_autorange_() {
|
|||||||
}
|
}
|
||||||
// Clean up calibration handle
|
// Clean up calibration handle
|
||||||
#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_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
|
USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
|
||||||
adc_cali_delete_scheme_curve_fitting(handle);
|
adc_cali_delete_scheme_curve_fitting(handle);
|
||||||
#else
|
#else
|
||||||
adc_cali_delete_scheme_line_fitting(handle);
|
adc_cali_delete_scheme_line_fitting(handle);
|
||||||
|
@@ -53,6 +53,7 @@ SERVICE_ARG_NATIVE_TYPES = {
|
|||||||
CONF_ENCRYPTION = "encryption"
|
CONF_ENCRYPTION = "encryption"
|
||||||
CONF_BATCH_DELAY = "batch_delay"
|
CONF_BATCH_DELAY = "batch_delay"
|
||||||
CONF_CUSTOM_SERVICES = "custom_services"
|
CONF_CUSTOM_SERVICES = "custom_services"
|
||||||
|
CONF_HOMEASSISTANT_SERVICES = "homeassistant_services"
|
||||||
CONF_HOMEASSISTANT_STATES = "homeassistant_states"
|
CONF_HOMEASSISTANT_STATES = "homeassistant_states"
|
||||||
|
|
||||||
|
|
||||||
@@ -119,6 +120,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
|
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_CUSTOM_SERVICES, default=False): cv.boolean,
|
cv.Optional(CONF_CUSTOM_SERVICES, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_HOMEASSISTANT_SERVICES, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_HOMEASSISTANT_STATES, default=False): cv.boolean,
|
cv.Optional(CONF_HOMEASSISTANT_STATES, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
||||||
single=True
|
single=True
|
||||||
@@ -148,6 +150,9 @@ async def to_code(config):
|
|||||||
if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]:
|
if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]:
|
||||||
cg.add_define("USE_API_SERVICES")
|
cg.add_define("USE_API_SERVICES")
|
||||||
|
|
||||||
|
if config[CONF_HOMEASSISTANT_SERVICES]:
|
||||||
|
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
|
||||||
|
|
||||||
if config[CONF_HOMEASSISTANT_STATES]:
|
if config[CONF_HOMEASSISTANT_STATES]:
|
||||||
cg.add_define("USE_API_HOMEASSISTANT_STATES")
|
cg.add_define("USE_API_HOMEASSISTANT_STATES")
|
||||||
|
|
||||||
@@ -240,6 +245,7 @@ HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
|
|||||||
HOMEASSISTANT_ACTION_ACTION_SCHEMA,
|
HOMEASSISTANT_ACTION_ACTION_SCHEMA,
|
||||||
)
|
)
|
||||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
|
async def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||||
|
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
|
||||||
serv = await cg.get_variable(config[CONF_ID])
|
serv = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
||||||
templ = await cg.templatable(config[CONF_ACTION], args, None)
|
templ = await cg.templatable(config[CONF_ACTION], args, None)
|
||||||
@@ -283,6 +289,7 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
|
|||||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
|
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
|
||||||
)
|
)
|
||||||
async def homeassistant_event_to_code(config, action_id, template_arg, args):
|
async def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||||
|
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
|
||||||
serv = await cg.get_variable(config[CONF_ID])
|
serv = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||||
templ = await cg.templatable(config[CONF_EVENT], args, None)
|
templ = await cg.templatable(config[CONF_EVENT], args, None)
|
||||||
|
@@ -419,7 +419,7 @@ message ListEntitiesFanResponse {
|
|||||||
bool disabled_by_default = 9;
|
bool disabled_by_default = 9;
|
||||||
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
|
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
|
||||||
EntityCategory entity_category = 11;
|
EntityCategory entity_category = 11;
|
||||||
repeated string supported_preset_modes = 12;
|
repeated string supported_preset_modes = 12 [(container_pointer) = "std::set"];
|
||||||
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
|
||||||
}
|
}
|
||||||
// Deprecated in API version 1.6 - only used in deprecated fields
|
// Deprecated in API version 1.6 - only used in deprecated fields
|
||||||
@@ -500,7 +500,7 @@ message ListEntitiesLightResponse {
|
|||||||
string name = 3;
|
string name = 3;
|
||||||
reserved 4; // Deprecated: was string unique_id
|
reserved 4; // Deprecated: was string unique_id
|
||||||
|
|
||||||
repeated ColorMode supported_color_modes = 12;
|
repeated ColorMode supported_color_modes = 12 [(container_pointer) = "std::set<light::ColorMode>"];
|
||||||
// next four supports_* are for legacy clients, newer clients should use color modes
|
// next four supports_* are for legacy clients, newer clients should use color modes
|
||||||
// Deprecated in API version 1.6
|
// Deprecated in API version 1.6
|
||||||
bool legacy_supports_brightness = 5 [deprecated=true];
|
bool legacy_supports_brightness = 5 [deprecated=true];
|
||||||
@@ -755,17 +755,19 @@ message NoiseEncryptionSetKeyResponse {
|
|||||||
message SubscribeHomeassistantServicesRequest {
|
message SubscribeHomeassistantServicesRequest {
|
||||||
option (id) = 34;
|
option (id) = 34;
|
||||||
option (source) = SOURCE_CLIENT;
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES";
|
||||||
}
|
}
|
||||||
|
|
||||||
message HomeassistantServiceMap {
|
message HomeassistantServiceMap {
|
||||||
string key = 1;
|
string key = 1;
|
||||||
string value = 2;
|
string value = 2 [(no_zero_copy) = true];
|
||||||
}
|
}
|
||||||
|
|
||||||
message HomeassistantServiceResponse {
|
message HomeassistantServiceResponse {
|
||||||
option (id) = 35;
|
option (id) = 35;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
|
option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES";
|
||||||
|
|
||||||
string service = 1;
|
string service = 1;
|
||||||
repeated HomeassistantServiceMap data = 2;
|
repeated HomeassistantServiceMap data = 2;
|
||||||
@@ -964,7 +966,7 @@ message ListEntitiesClimateResponse {
|
|||||||
|
|
||||||
bool supports_current_temperature = 5;
|
bool supports_current_temperature = 5;
|
||||||
bool supports_two_point_target_temperature = 6;
|
bool supports_two_point_target_temperature = 6;
|
||||||
repeated ClimateMode supported_modes = 7;
|
repeated ClimateMode supported_modes = 7 [(container_pointer) = "std::set<climate::ClimateMode>"];
|
||||||
float visual_min_temperature = 8;
|
float visual_min_temperature = 8;
|
||||||
float visual_max_temperature = 9;
|
float visual_max_temperature = 9;
|
||||||
float visual_target_temperature_step = 10;
|
float visual_target_temperature_step = 10;
|
||||||
@@ -973,11 +975,11 @@ message ListEntitiesClimateResponse {
|
|||||||
// Deprecated in API version 1.5
|
// Deprecated in API version 1.5
|
||||||
bool legacy_supports_away = 11 [deprecated=true];
|
bool legacy_supports_away = 11 [deprecated=true];
|
||||||
bool supports_action = 12;
|
bool supports_action = 12;
|
||||||
repeated ClimateFanMode supported_fan_modes = 13;
|
repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer) = "std::set<climate::ClimateFanMode>"];
|
||||||
repeated ClimateSwingMode supported_swing_modes = 14;
|
repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer) = "std::set<climate::ClimateSwingMode>"];
|
||||||
repeated string supported_custom_fan_modes = 15;
|
repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::set"];
|
||||||
repeated ClimatePreset supported_presets = 16;
|
repeated ClimatePreset supported_presets = 16 [(container_pointer) = "std::set<climate::ClimatePreset>"];
|
||||||
repeated string supported_custom_presets = 17;
|
repeated string supported_custom_presets = 17 [(container_pointer) = "std::set"];
|
||||||
bool disabled_by_default = 18;
|
bool disabled_by_default = 18;
|
||||||
string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"];
|
string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"];
|
||||||
EntityCategory entity_category = 20;
|
EntityCategory entity_category = 20;
|
||||||
@@ -1117,7 +1119,7 @@ message ListEntitiesSelectResponse {
|
|||||||
reserved 4; // Deprecated: was string unique_id
|
reserved 4; // Deprecated: was string unique_id
|
||||||
|
|
||||||
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
|
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
|
||||||
repeated string options = 6;
|
repeated string options = 6 [(container_pointer) = "std::vector"];
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
EntityCategory entity_category = 8;
|
EntityCategory entity_category = 8;
|
||||||
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
|
||||||
@@ -1295,6 +1297,7 @@ enum MediaPlayerState {
|
|||||||
MEDIA_PLAYER_STATE_IDLE = 1;
|
MEDIA_PLAYER_STATE_IDLE = 1;
|
||||||
MEDIA_PLAYER_STATE_PLAYING = 2;
|
MEDIA_PLAYER_STATE_PLAYING = 2;
|
||||||
MEDIA_PLAYER_STATE_PAUSED = 3;
|
MEDIA_PLAYER_STATE_PAUSED = 3;
|
||||||
|
MEDIA_PLAYER_STATE_ANNOUNCING = 4;
|
||||||
}
|
}
|
||||||
enum MediaPlayerCommand {
|
enum MediaPlayerCommand {
|
||||||
MEDIA_PLAYER_COMMAND_PLAY = 0;
|
MEDIA_PLAYER_COMMAND_PLAY = 0;
|
||||||
@@ -1302,6 +1305,13 @@ enum MediaPlayerCommand {
|
|||||||
MEDIA_PLAYER_COMMAND_STOP = 2;
|
MEDIA_PLAYER_COMMAND_STOP = 2;
|
||||||
MEDIA_PLAYER_COMMAND_MUTE = 3;
|
MEDIA_PLAYER_COMMAND_MUTE = 3;
|
||||||
MEDIA_PLAYER_COMMAND_UNMUTE = 4;
|
MEDIA_PLAYER_COMMAND_UNMUTE = 4;
|
||||||
|
MEDIA_PLAYER_COMMAND_TOGGLE = 5;
|
||||||
|
MEDIA_PLAYER_COMMAND_VOLUME_UP = 6;
|
||||||
|
MEDIA_PLAYER_COMMAND_VOLUME_DOWN = 7;
|
||||||
|
MEDIA_PLAYER_COMMAND_ENQUEUE = 8;
|
||||||
|
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9;
|
||||||
|
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10;
|
||||||
|
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11;
|
||||||
}
|
}
|
||||||
enum MediaPlayerFormatPurpose {
|
enum MediaPlayerFormatPurpose {
|
||||||
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0;
|
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0;
|
||||||
@@ -1336,6 +1346,8 @@ message ListEntitiesMediaPlayerResponse {
|
|||||||
repeated MediaPlayerSupportedFormat supported_formats = 9;
|
repeated MediaPlayerSupportedFormat supported_formats = 9;
|
||||||
|
|
||||||
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
|
||||||
|
|
||||||
|
uint32 feature_flags = 11;
|
||||||
}
|
}
|
||||||
message MediaPlayerStateResponse {
|
message MediaPlayerStateResponse {
|
||||||
option (id) = 64;
|
option (id) = 64;
|
||||||
@@ -1832,7 +1844,7 @@ message VoiceAssistantConfigurationResponse {
|
|||||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
repeated VoiceAssistantWakeWord available_wake_words = 1;
|
repeated VoiceAssistantWakeWord available_wake_words = 1;
|
||||||
repeated string active_wake_words = 2;
|
repeated string active_wake_words = 2 [(container_pointer) = "std::vector"];
|
||||||
uint32 max_active_wake_words = 3;
|
uint32 max_active_wake_words = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -244,21 +244,7 @@ void APIConnection::loop() {
|
|||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
if (state_subs_at_ >= 0) {
|
if (state_subs_at_ >= 0) {
|
||||||
const auto &subs = this->parent_->get_state_subs();
|
this->process_state_subscriptions_();
|
||||||
if (state_subs_at_ < static_cast<int>(subs.size())) {
|
|
||||||
auto &it = subs[state_subs_at_];
|
|
||||||
SubscribeHomeAssistantStateResponse resp;
|
|
||||||
resp.set_entity_id(StringRef(it.entity_id));
|
|
||||||
// attribute.value() returns temporary - must store it
|
|
||||||
std::string attribute_value = it.attribute.value();
|
|
||||||
resp.set_attribute(StringRef(attribute_value));
|
|
||||||
resp.once = it.once;
|
|
||||||
if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
|
|
||||||
state_subs_at_++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state_subs_at_ = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -290,8 +276,9 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Calculate size
|
// Calculate size
|
||||||
uint32_t calculated_size = 0;
|
ProtoSize size_calc;
|
||||||
msg.calculate_size(calculated_size);
|
msg.calculate_size(size_calc);
|
||||||
|
uint32_t calculated_size = size_calc.get_size();
|
||||||
|
|
||||||
// Cache frame sizes to avoid repeated virtual calls
|
// Cache frame sizes to avoid repeated virtual calls
|
||||||
const uint8_t header_padding = conn->helper_->frame_header_padding();
|
const uint8_t header_padding = conn->helper_->frame_header_padding();
|
||||||
@@ -426,8 +413,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
|
|||||||
msg.supports_speed = traits.supports_speed();
|
msg.supports_speed = traits.supports_speed();
|
||||||
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();
|
||||||
for (auto const &preset : traits.supported_preset_modes())
|
msg.supported_preset_modes = &traits.supported_preset_modes_for_api_();
|
||||||
msg.supported_preset_modes.push_back(preset);
|
|
||||||
return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
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) {
|
||||||
@@ -483,8 +469,7 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
|
|||||||
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();
|
||||||
for (auto mode : traits.get_supported_color_modes())
|
msg.supported_color_modes = &traits.get_supported_color_modes_for_api_();
|
||||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
|
||||||
if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
||||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) {
|
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) {
|
||||||
msg.min_mireds = traits.get_min_mireds();
|
msg.min_mireds = traits.get_min_mireds();
|
||||||
@@ -644,17 +629,13 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
|
|||||||
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
||||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
||||||
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) {
|
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) {
|
||||||
// custom_fan_mode.value() returns temporary - must store it
|
resp.set_custom_fan_mode(StringRef(climate->custom_fan_mode.value()));
|
||||||
std::string custom_fan_mode = climate->custom_fan_mode.value();
|
|
||||||
resp.set_custom_fan_mode(StringRef(custom_fan_mode));
|
|
||||||
}
|
}
|
||||||
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
||||||
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
||||||
}
|
}
|
||||||
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) {
|
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) {
|
||||||
// custom_preset.value() returns temporary - must store it
|
resp.set_custom_preset(StringRef(climate->custom_preset.value()));
|
||||||
std::string custom_preset = climate->custom_preset.value();
|
|
||||||
resp.set_custom_preset(StringRef(custom_preset));
|
|
||||||
}
|
}
|
||||||
if (traits.get_supports_swing_modes())
|
if (traits.get_supports_swing_modes())
|
||||||
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
||||||
@@ -674,8 +655,7 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
|
|||||||
msg.supports_current_humidity = traits.get_supports_current_humidity();
|
msg.supports_current_humidity = traits.get_supports_current_humidity();
|
||||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||||
msg.supports_target_humidity = traits.get_supports_target_humidity();
|
msg.supports_target_humidity = traits.get_supports_target_humidity();
|
||||||
for (auto mode : traits.get_supported_modes())
|
msg.supported_modes = &traits.get_supported_modes_for_api_();
|
||||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
|
||||||
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
||||||
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
||||||
msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
|
msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
|
||||||
@@ -683,16 +663,11 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
|
|||||||
msg.visual_min_humidity = traits.get_visual_min_humidity();
|
msg.visual_min_humidity = traits.get_visual_min_humidity();
|
||||||
msg.visual_max_humidity = traits.get_visual_max_humidity();
|
msg.visual_max_humidity = traits.get_visual_max_humidity();
|
||||||
msg.supports_action = traits.get_supports_action();
|
msg.supports_action = traits.get_supports_action();
|
||||||
for (auto fan_mode : traits.get_supported_fan_modes())
|
msg.supported_fan_modes = &traits.get_supported_fan_modes_for_api_();
|
||||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes_for_api_();
|
||||||
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
|
msg.supported_presets = &traits.get_supported_presets_for_api_();
|
||||||
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
|
msg.supported_custom_presets = &traits.get_supported_custom_presets_for_api_();
|
||||||
for (auto preset : traits.get_supported_presets())
|
msg.supported_swing_modes = &traits.get_supported_swing_modes_for_api_();
|
||||||
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
|
|
||||||
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
|
||||||
msg.supported_custom_presets.push_back(custom_preset);
|
|
||||||
for (auto swing_mode : traits.get_supported_swing_modes())
|
|
||||||
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
|
||||||
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);
|
is_single);
|
||||||
}
|
}
|
||||||
@@ -898,8 +873,7 @@ uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *select = static_cast<select::Select *>(entity);
|
auto *select = static_cast<select::Select *>(entity);
|
||||||
ListEntitiesSelectResponse msg;
|
ListEntitiesSelectResponse msg;
|
||||||
for (const auto &option : select->traits.get_options())
|
msg.options = &select->traits.get_options();
|
||||||
msg.options.push_back(option);
|
|
||||||
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);
|
is_single);
|
||||||
}
|
}
|
||||||
@@ -1025,6 +999,7 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
|
|||||||
ListEntitiesMediaPlayerResponse msg;
|
ListEntitiesMediaPlayerResponse msg;
|
||||||
auto traits = media_player->get_traits();
|
auto traits = media_player->get_traits();
|
||||||
msg.supports_pause = traits.get_supports_pause();
|
msg.supports_pause = traits.get_supports_pause();
|
||||||
|
msg.feature_flags = traits.get_feature_flags();
|
||||||
for (auto &supported_format : traits.get_supported_formats()) {
|
for (auto &supported_format : traits.get_supported_formats()) {
|
||||||
msg.supported_formats.emplace_back();
|
msg.supported_formats.emplace_back();
|
||||||
auto &media_format = msg.supported_formats.back();
|
auto &media_format = msg.supported_formats.back();
|
||||||
@@ -1213,9 +1188,7 @@ bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceA
|
|||||||
resp_wake_word.trained_languages.push_back(lang);
|
resp_wake_word.trained_languages.push_back(lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto &wake_word_id : config.active_wake_words) {
|
resp.active_wake_words = &config.active_wake_words;
|
||||||
resp.active_wake_words.push_back(wake_word_id);
|
|
||||||
}
|
|
||||||
resp.max_active_wake_words = config.max_active_wake_words;
|
resp.max_active_wake_words = config.max_active_wake_words;
|
||||||
return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
|
return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
@@ -1538,19 +1511,18 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
|
bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
|
||||||
psk_t psk{};
|
|
||||||
NoiseEncryptionSetKeyResponse resp;
|
NoiseEncryptionSetKeyResponse resp;
|
||||||
|
resp.success = false;
|
||||||
|
|
||||||
|
psk_t psk{};
|
||||||
if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
|
if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
|
||||||
ESP_LOGW(TAG, "Invalid encryption key length");
|
ESP_LOGW(TAG, "Invalid encryption key length");
|
||||||
resp.success = false;
|
} else if (!this->parent_->save_noise_psk(psk, true)) {
|
||||||
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
|
||||||
}
|
|
||||||
if (!this->parent_->save_noise_psk(psk, true)) {
|
|
||||||
ESP_LOGW(TAG, "Failed to save encryption key");
|
ESP_LOGW(TAG, "Failed to save encryption key");
|
||||||
resp.success = false;
|
} else {
|
||||||
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
resp.success = true;
|
||||||
}
|
}
|
||||||
resp.success = true;
|
|
||||||
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1844,5 +1816,27 @@ uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection
|
|||||||
return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
|
void APIConnection::process_state_subscriptions_() {
|
||||||
|
const auto &subs = this->parent_->get_state_subs();
|
||||||
|
if (this->state_subs_at_ >= static_cast<int>(subs.size())) {
|
||||||
|
this->state_subs_at_ = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &it = subs[this->state_subs_at_];
|
||||||
|
SubscribeHomeAssistantStateResponse resp;
|
||||||
|
resp.set_entity_id(StringRef(it.entity_id));
|
||||||
|
|
||||||
|
// Avoid string copy by directly using the optional's value if it exists
|
||||||
|
resp.set_attribute(it.attribute.has_value() ? StringRef(it.attribute.value()) : StringRef(""));
|
||||||
|
|
||||||
|
resp.once = it.once;
|
||||||
|
if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
|
||||||
|
this->state_subs_at_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_API_HOMEASSISTANT_STATES
|
||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
#endif
|
#endif
|
||||||
|
@@ -131,11 +131,13 @@ class APIConnection : public APIServerConnection {
|
|||||||
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len);
|
bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len);
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
if (!this->flags_.service_call_subscription)
|
if (!this->flags_.service_call_subscription)
|
||||||
return;
|
return;
|
||||||
this->send_message(call, HomeassistantServiceResponse::MESSAGE_TYPE);
|
this->send_message(call, HomeassistantServiceResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#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(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
@@ -209,9 +211,11 @@ class APIConnection : public APIServerConnection {
|
|||||||
if (msg.dump_config)
|
if (msg.dump_config)
|
||||||
App.schedule_dump_config();
|
App.schedule_dump_config();
|
||||||
}
|
}
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
|
void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
|
||||||
this->flags_.service_call_subscription = true;
|
this->flags_.service_call_subscription = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
|
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
@@ -294,6 +298,10 @@ class APIConnection : public APIServerConnection {
|
|||||||
// Helper function to handle authentication completion
|
// Helper function to handle authentication completion
|
||||||
void complete_authentication_();
|
void complete_authentication_();
|
||||||
|
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
|
void process_state_subscriptions_();
|
||||||
|
#endif
|
||||||
|
|
||||||
// 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, bool is_single);
|
uint32_t remaining_size, bool is_single);
|
||||||
|
@@ -27,4 +27,31 @@ extend google.protobuf.MessageOptions {
|
|||||||
extend google.protobuf.FieldOptions {
|
extend google.protobuf.FieldOptions {
|
||||||
optional string field_ifdef = 1042;
|
optional string field_ifdef = 1042;
|
||||||
optional uint32 fixed_array_size = 50007;
|
optional uint32 fixed_array_size = 50007;
|
||||||
|
optional bool no_zero_copy = 50008 [default=false];
|
||||||
|
|
||||||
|
// container_pointer: Zero-copy optimization for repeated fields.
|
||||||
|
//
|
||||||
|
// When container_pointer is set on a repeated field, the generated message will
|
||||||
|
// store a pointer to an existing container instead of copying the data into the
|
||||||
|
// message's own repeated field. This eliminates heap allocations and improves performance.
|
||||||
|
//
|
||||||
|
// Requirements for safe usage:
|
||||||
|
// 1. The source container must remain valid until the message is encoded
|
||||||
|
// 2. Messages must be encoded immediately (which ESPHome does by default)
|
||||||
|
// 3. The container type must match the field type exactly
|
||||||
|
//
|
||||||
|
// Supported container types:
|
||||||
|
// - "std::vector<T>" for most repeated fields
|
||||||
|
// - "std::set<T>" for unique/sorted data
|
||||||
|
// - Full type specification required for enums (e.g., "std::set<climate::ClimateMode>")
|
||||||
|
//
|
||||||
|
// Example usage in .proto file:
|
||||||
|
// repeated string supported_modes = 12 [(container_pointer) = "std::set"];
|
||||||
|
// repeated ColorMode color_modes = 13 [(container_pointer) = "std::set<light::ColorMode>"];
|
||||||
|
//
|
||||||
|
// The corresponding C++ code must provide const reference access to a container
|
||||||
|
// that matches the specified type and remains valid during message encoding.
|
||||||
|
// This is typically done through methods returning const T& or special accessor
|
||||||
|
// methods like get_options() or supported_modes_for_api_().
|
||||||
|
optional string container_pointer = 50001;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
|||||||
#include "esphome/core/string_ref.h"
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include "api_pb2_includes.h"
|
||||||
|
|
||||||
namespace esphome::api {
|
namespace esphome::api {
|
||||||
|
|
||||||
@@ -149,6 +150,7 @@ enum MediaPlayerState : uint32_t {
|
|||||||
MEDIA_PLAYER_STATE_IDLE = 1,
|
MEDIA_PLAYER_STATE_IDLE = 1,
|
||||||
MEDIA_PLAYER_STATE_PLAYING = 2,
|
MEDIA_PLAYER_STATE_PLAYING = 2,
|
||||||
MEDIA_PLAYER_STATE_PAUSED = 3,
|
MEDIA_PLAYER_STATE_PAUSED = 3,
|
||||||
|
MEDIA_PLAYER_STATE_ANNOUNCING = 4,
|
||||||
};
|
};
|
||||||
enum MediaPlayerCommand : uint32_t {
|
enum MediaPlayerCommand : uint32_t {
|
||||||
MEDIA_PLAYER_COMMAND_PLAY = 0,
|
MEDIA_PLAYER_COMMAND_PLAY = 0,
|
||||||
@@ -156,6 +158,13 @@ enum MediaPlayerCommand : uint32_t {
|
|||||||
MEDIA_PLAYER_COMMAND_STOP = 2,
|
MEDIA_PLAYER_COMMAND_STOP = 2,
|
||||||
MEDIA_PLAYER_COMMAND_MUTE = 3,
|
MEDIA_PLAYER_COMMAND_MUTE = 3,
|
||||||
MEDIA_PLAYER_COMMAND_UNMUTE = 4,
|
MEDIA_PLAYER_COMMAND_UNMUTE = 4,
|
||||||
|
MEDIA_PLAYER_COMMAND_TOGGLE = 5,
|
||||||
|
MEDIA_PLAYER_COMMAND_VOLUME_UP = 6,
|
||||||
|
MEDIA_PLAYER_COMMAND_VOLUME_DOWN = 7,
|
||||||
|
MEDIA_PLAYER_COMMAND_ENQUEUE = 8,
|
||||||
|
MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9,
|
||||||
|
MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10,
|
||||||
|
MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11,
|
||||||
};
|
};
|
||||||
enum MediaPlayerFormatPurpose : uint32_t {
|
enum MediaPlayerFormatPurpose : uint32_t {
|
||||||
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0,
|
MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0,
|
||||||
@@ -340,7 +349,7 @@ class HelloResponse : public ProtoMessage {
|
|||||||
StringRef name_ref_{};
|
StringRef name_ref_{};
|
||||||
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -371,14 +380,14 @@ class ConnectResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
bool invalid_password{false};
|
bool invalid_password{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class DisconnectRequest : public ProtoDecodableMessage {
|
class DisconnectRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 5;
|
static constexpr uint8_t MESSAGE_TYPE = 5;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -391,7 +400,7 @@ class DisconnectRequest : public ProtoDecodableMessage {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class DisconnectResponse : public ProtoDecodableMessage {
|
class DisconnectResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 6;
|
static constexpr uint8_t MESSAGE_TYPE = 6;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -404,7 +413,7 @@ class DisconnectResponse : public ProtoDecodableMessage {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class PingRequest : public ProtoDecodableMessage {
|
class PingRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 7;
|
static constexpr uint8_t MESSAGE_TYPE = 7;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -417,7 +426,7 @@ class PingRequest : public ProtoDecodableMessage {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class PingResponse : public ProtoDecodableMessage {
|
class PingResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 8;
|
static constexpr uint8_t MESSAGE_TYPE = 8;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -430,7 +439,7 @@ class PingResponse : public ProtoDecodableMessage {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class DeviceInfoRequest : public ProtoDecodableMessage {
|
class DeviceInfoRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 9;
|
static constexpr uint8_t MESSAGE_TYPE = 9;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -450,7 +459,7 @@ class AreaInfo : public ProtoMessage {
|
|||||||
StringRef name_ref_{};
|
StringRef name_ref_{};
|
||||||
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -466,7 +475,7 @@ class DeviceInfo : public ProtoMessage {
|
|||||||
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
uint32_t area_id{0};
|
uint32_t area_id{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -539,14 +548,14 @@ class DeviceInfoResponse : public ProtoMessage {
|
|||||||
AreaInfo area{};
|
AreaInfo area{};
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class ListEntitiesRequest : public ProtoDecodableMessage {
|
class ListEntitiesRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 11;
|
static constexpr uint8_t MESSAGE_TYPE = 11;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -572,7 +581,7 @@ class ListEntitiesDoneResponse : public ProtoMessage {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class SubscribeStatesRequest : public ProtoDecodableMessage {
|
class SubscribeStatesRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 20;
|
static constexpr uint8_t MESSAGE_TYPE = 20;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -597,7 +606,7 @@ class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage {
|
|||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
bool is_status_binary_sensor{false};
|
bool is_status_binary_sensor{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -614,7 +623,7 @@ class BinarySensorStateResponse : public StateResponseProtoMessage {
|
|||||||
bool state{false};
|
bool state{false};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -637,7 +646,7 @@ class ListEntitiesCoverResponse : public InfoResponseProtoMessage {
|
|||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
bool supports_stop{false};
|
bool supports_stop{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -655,7 +664,7 @@ class CoverStateResponse : public StateResponseProtoMessage {
|
|||||||
float tilt{0.0f};
|
float tilt{0.0f};
|
||||||
enums::CoverOperation current_operation{};
|
enums::CoverOperation current_operation{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -695,9 +704,9 @@ class ListEntitiesFanResponse : public InfoResponseProtoMessage {
|
|||||||
bool supports_speed{false};
|
bool supports_speed{false};
|
||||||
bool supports_direction{false};
|
bool supports_direction{false};
|
||||||
int32_t supported_speed_count{0};
|
int32_t supported_speed_count{0};
|
||||||
std::vector<std::string> supported_preset_modes{};
|
const std::set<std::string> *supported_preset_modes{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -718,7 +727,7 @@ class FanStateResponse : public StateResponseProtoMessage {
|
|||||||
StringRef preset_mode_ref_{};
|
StringRef preset_mode_ref_{};
|
||||||
void set_preset_mode(const StringRef &ref) { this->preset_mode_ref_ = ref; }
|
void set_preset_mode(const StringRef &ref) { this->preset_mode_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -760,12 +769,12 @@ class ListEntitiesLightResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_light_response"; }
|
const char *message_name() const override { return "list_entities_light_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::vector<enums::ColorMode> supported_color_modes{};
|
const std::set<light::ColorMode> *supported_color_modes{};
|
||||||
float min_mireds{0.0f};
|
float min_mireds{0.0f};
|
||||||
float max_mireds{0.0f};
|
float max_mireds{0.0f};
|
||||||
std::vector<std::string> effects{};
|
std::vector<std::string> effects{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -793,7 +802,7 @@ class LightStateResponse : public StateResponseProtoMessage {
|
|||||||
StringRef effect_ref_{};
|
StringRef effect_ref_{};
|
||||||
void set_effect(const StringRef &ref) { this->effect_ref_ = ref; }
|
void set_effect(const StringRef &ref) { this->effect_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -859,7 +868,7 @@ class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
|
|||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
enums::SensorStateClass state_class{};
|
enums::SensorStateClass state_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -876,7 +885,7 @@ class SensorStateResponse : public StateResponseProtoMessage {
|
|||||||
float state{0.0f};
|
float state{0.0f};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -896,7 +905,7 @@ class ListEntitiesSwitchResponse : public InfoResponseProtoMessage {
|
|||||||
StringRef device_class_ref_{};
|
StringRef device_class_ref_{};
|
||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -912,7 +921,7 @@ class SwitchStateResponse : public StateResponseProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
bool state{false};
|
bool state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -947,7 +956,7 @@ class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage {
|
|||||||
StringRef device_class_ref_{};
|
StringRef device_class_ref_{};
|
||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -965,7 +974,7 @@ class TextSensorStateResponse : public StateResponseProtoMessage {
|
|||||||
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1004,7 +1013,7 @@ class SubscribeLogsResponse : public ProtoMessage {
|
|||||||
this->message_len_ = len;
|
this->message_len_ = len;
|
||||||
}
|
}
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1036,7 +1045,7 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
bool success{false};
|
bool success{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1044,7 +1053,8 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
class SubscribeHomeassistantServicesRequest : public ProtoDecodableMessage {
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
|
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 34;
|
static constexpr uint8_t MESSAGE_TYPE = 34;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -1061,10 +1071,9 @@ class HomeassistantServiceMap : public ProtoMessage {
|
|||||||
public:
|
public:
|
||||||
StringRef key_ref_{};
|
StringRef key_ref_{};
|
||||||
void set_key(const StringRef &ref) { this->key_ref_ = ref; }
|
void set_key(const StringRef &ref) { this->key_ref_ = ref; }
|
||||||
StringRef value_ref_{};
|
std::string value{};
|
||||||
void set_value(const StringRef &ref) { this->value_ref_ = ref; }
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1085,15 +1094,16 @@ class HomeassistantServiceResponse : public ProtoMessage {
|
|||||||
std::vector<HomeassistantServiceMap> variables{};
|
std::vector<HomeassistantServiceMap> variables{};
|
||||||
bool is_event{false};
|
bool is_event{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
class SubscribeHomeAssistantStatesRequest : public ProtoDecodableMessage {
|
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 38;
|
static constexpr uint8_t MESSAGE_TYPE = 38;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -1119,7 +1129,7 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
|
|||||||
void set_attribute(const StringRef &ref) { this->attribute_ref_ = ref; }
|
void set_attribute(const StringRef &ref) { this->attribute_ref_ = ref; }
|
||||||
bool once{false};
|
bool once{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1144,7 +1154,7 @@ class HomeAssistantStateResponse : public ProtoDecodableMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
class GetTimeRequest : public ProtoDecodableMessage {
|
class GetTimeRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 36;
|
static constexpr uint8_t MESSAGE_TYPE = 36;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -1166,7 +1176,7 @@ class GetTimeResponse : public ProtoDecodableMessage {
|
|||||||
#endif
|
#endif
|
||||||
uint32_t epoch_seconds{0};
|
uint32_t epoch_seconds{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1181,7 +1191,7 @@ class ListEntitiesServicesArgument : public ProtoMessage {
|
|||||||
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
enums::ServiceArgType type{};
|
enums::ServiceArgType type{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1200,7 +1210,7 @@ class ListEntitiesServicesResponse : public ProtoMessage {
|
|||||||
uint32_t key{0};
|
uint32_t key{0};
|
||||||
std::vector<ListEntitiesServicesArgument> args{};
|
std::vector<ListEntitiesServicesArgument> args{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1254,7 +1264,7 @@ class ListEntitiesCameraResponse : public InfoResponseProtoMessage {
|
|||||||
const char *message_name() const override { return "list_entities_camera_response"; }
|
const char *message_name() const override { return "list_entities_camera_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1276,7 +1286,7 @@ class CameraImageResponse : public StateResponseProtoMessage {
|
|||||||
}
|
}
|
||||||
bool done{false};
|
bool done{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1310,23 +1320,23 @@ class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
bool supports_current_temperature{false};
|
bool supports_current_temperature{false};
|
||||||
bool supports_two_point_target_temperature{false};
|
bool supports_two_point_target_temperature{false};
|
||||||
std::vector<enums::ClimateMode> supported_modes{};
|
const std::set<climate::ClimateMode> *supported_modes{};
|
||||||
float visual_min_temperature{0.0f};
|
float visual_min_temperature{0.0f};
|
||||||
float visual_max_temperature{0.0f};
|
float visual_max_temperature{0.0f};
|
||||||
float visual_target_temperature_step{0.0f};
|
float visual_target_temperature_step{0.0f};
|
||||||
bool supports_action{false};
|
bool supports_action{false};
|
||||||
std::vector<enums::ClimateFanMode> supported_fan_modes{};
|
const std::set<climate::ClimateFanMode> *supported_fan_modes{};
|
||||||
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
|
const std::set<climate::ClimateSwingMode> *supported_swing_modes{};
|
||||||
std::vector<std::string> supported_custom_fan_modes{};
|
const std::set<std::string> *supported_custom_fan_modes{};
|
||||||
std::vector<enums::ClimatePreset> supported_presets{};
|
const std::set<climate::ClimatePreset> *supported_presets{};
|
||||||
std::vector<std::string> supported_custom_presets{};
|
const std::set<std::string> *supported_custom_presets{};
|
||||||
float visual_current_temperature_step{0.0f};
|
float visual_current_temperature_step{0.0f};
|
||||||
bool supports_current_humidity{false};
|
bool supports_current_humidity{false};
|
||||||
bool supports_target_humidity{false};
|
bool supports_target_humidity{false};
|
||||||
float visual_min_humidity{0.0f};
|
float visual_min_humidity{0.0f};
|
||||||
float visual_max_humidity{0.0f};
|
float visual_max_humidity{0.0f};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1356,7 +1366,7 @@ class ClimateStateResponse : public StateResponseProtoMessage {
|
|||||||
float current_humidity{0.0f};
|
float current_humidity{0.0f};
|
||||||
float target_humidity{0.0f};
|
float target_humidity{0.0f};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1417,7 +1427,7 @@ class ListEntitiesNumberResponse : public InfoResponseProtoMessage {
|
|||||||
StringRef device_class_ref_{};
|
StringRef device_class_ref_{};
|
||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1434,7 +1444,7 @@ class NumberStateResponse : public StateResponseProtoMessage {
|
|||||||
float state{0.0f};
|
float state{0.0f};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1466,9 +1476,9 @@ class ListEntitiesSelectResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_select_response"; }
|
const char *message_name() const override { return "list_entities_select_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::vector<std::string> options{};
|
const std::vector<std::string> *options{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1486,7 +1496,7 @@ class SelectStateResponse : public StateResponseProtoMessage {
|
|||||||
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1523,7 +1533,7 @@ class ListEntitiesSirenResponse : public InfoResponseProtoMessage {
|
|||||||
bool supports_duration{false};
|
bool supports_duration{false};
|
||||||
bool supports_volume{false};
|
bool supports_volume{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1539,7 +1549,7 @@ class SirenStateResponse : public StateResponseProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
bool state{false};
|
bool state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1585,7 +1595,7 @@ class ListEntitiesLockResponse : public InfoResponseProtoMessage {
|
|||||||
StringRef code_format_ref_{};
|
StringRef code_format_ref_{};
|
||||||
void set_code_format(const StringRef &ref) { this->code_format_ref_ = ref; }
|
void set_code_format(const StringRef &ref) { this->code_format_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1601,7 +1611,7 @@ class LockStateResponse : public StateResponseProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
enums::LockState state{};
|
enums::LockState state{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1639,7 +1649,7 @@ class ListEntitiesButtonResponse : public InfoResponseProtoMessage {
|
|||||||
StringRef device_class_ref_{};
|
StringRef device_class_ref_{};
|
||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1672,7 +1682,7 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
|
|||||||
enums::MediaPlayerFormatPurpose purpose{};
|
enums::MediaPlayerFormatPurpose purpose{};
|
||||||
uint32_t sample_bytes{0};
|
uint32_t sample_bytes{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1682,14 +1692,15 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
|
|||||||
class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage {
|
class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 63;
|
static constexpr uint8_t MESSAGE_TYPE = 63;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 76;
|
static constexpr uint8_t ESTIMATED_SIZE = 80;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_media_player_response"; }
|
const char *message_name() const override { return "list_entities_media_player_response"; }
|
||||||
#endif
|
#endif
|
||||||
bool supports_pause{false};
|
bool supports_pause{false};
|
||||||
std::vector<MediaPlayerSupportedFormat> supported_formats{};
|
std::vector<MediaPlayerSupportedFormat> supported_formats{};
|
||||||
|
uint32_t feature_flags{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1707,7 +1718,7 @@ class MediaPlayerStateResponse : public StateResponseProtoMessage {
|
|||||||
float volume{0.0f};
|
float volume{0.0f};
|
||||||
bool muted{false};
|
bool muted{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1763,7 +1774,7 @@ class BluetoothLERawAdvertisement : public ProtoMessage {
|
|||||||
uint8_t data[62]{};
|
uint8_t data[62]{};
|
||||||
uint8_t data_len{0};
|
uint8_t data_len{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1779,7 +1790,7 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
std::vector<BluetoothLERawAdvertisement> advertisements{};
|
std::vector<BluetoothLERawAdvertisement> advertisements{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1816,7 +1827,7 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage {
|
|||||||
uint32_t mtu{0};
|
uint32_t mtu{0};
|
||||||
int32_t error{0};
|
int32_t error{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1843,7 +1854,7 @@ class BluetoothGATTDescriptor : public ProtoMessage {
|
|||||||
std::array<uint64_t, 2> uuid{};
|
std::array<uint64_t, 2> uuid{};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1857,7 +1868,7 @@ class BluetoothGATTCharacteristic : public ProtoMessage {
|
|||||||
uint32_t properties{0};
|
uint32_t properties{0};
|
||||||
std::vector<BluetoothGATTDescriptor> descriptors{};
|
std::vector<BluetoothGATTDescriptor> descriptors{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1870,7 +1881,7 @@ class BluetoothGATTService : public ProtoMessage {
|
|||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
std::vector<BluetoothGATTCharacteristic> characteristics{};
|
std::vector<BluetoothGATTCharacteristic> characteristics{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1887,7 +1898,7 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage {
|
|||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
std::array<BluetoothGATTService, 1> services{};
|
std::array<BluetoothGATTService, 1> services{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1903,7 +1914,7 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1942,7 +1953,7 @@ class BluetoothGATTReadResponse : public ProtoMessage {
|
|||||||
this->data_len_ = len;
|
this->data_len_ = len;
|
||||||
}
|
}
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2035,14 +2046,14 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage {
|
|||||||
this->data_len_ = len;
|
this->data_len_ = len;
|
||||||
}
|
}
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class SubscribeBluetoothConnectionsFreeRequest : public ProtoDecodableMessage {
|
class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 80;
|
static constexpr uint8_t MESSAGE_TYPE = 80;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -2066,7 +2077,7 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
|
|||||||
uint32_t limit{0};
|
uint32_t limit{0};
|
||||||
std::vector<uint64_t> allocated{};
|
std::vector<uint64_t> allocated{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2084,7 +2095,7 @@ class BluetoothGATTErrorResponse : public ProtoMessage {
|
|||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
int32_t error{0};
|
int32_t error{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2101,7 +2112,7 @@ class BluetoothGATTWriteResponse : public ProtoMessage {
|
|||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2118,7 +2129,7 @@ class BluetoothGATTNotifyResponse : public ProtoMessage {
|
|||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2136,7 +2147,7 @@ class BluetoothDevicePairingResponse : public ProtoMessage {
|
|||||||
bool paired{false};
|
bool paired{false};
|
||||||
int32_t error{0};
|
int32_t error{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2154,14 +2165,14 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
|
|||||||
bool success{false};
|
bool success{false};
|
||||||
int32_t error{0};
|
int32_t error{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoDecodableMessage {
|
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 87;
|
static constexpr uint8_t MESSAGE_TYPE = 87;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -2185,7 +2196,7 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
|
|||||||
bool success{false};
|
bool success{false};
|
||||||
int32_t error{0};
|
int32_t error{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2202,7 +2213,7 @@ class BluetoothScannerStateResponse : public ProtoMessage {
|
|||||||
enums::BluetoothScannerState state{};
|
enums::BluetoothScannerState state{};
|
||||||
enums::BluetoothScannerMode mode{};
|
enums::BluetoothScannerMode mode{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2248,7 +2259,7 @@ class VoiceAssistantAudioSettings : public ProtoMessage {
|
|||||||
uint32_t auto_gain{0};
|
uint32_t auto_gain{0};
|
||||||
float volume_multiplier{0.0f};
|
float volume_multiplier{0.0f};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2270,7 +2281,7 @@ class VoiceAssistantRequest : public ProtoMessage {
|
|||||||
StringRef wake_word_phrase_ref_{};
|
StringRef wake_word_phrase_ref_{};
|
||||||
void set_wake_word_phrase(const StringRef &ref) { this->wake_word_phrase_ref_ = ref; }
|
void set_wake_word_phrase(const StringRef &ref) { this->wake_word_phrase_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2337,7 +2348,7 @@ class VoiceAssistantAudio : public ProtoDecodableMessage {
|
|||||||
}
|
}
|
||||||
bool end{false};
|
bool end{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2395,7 +2406,7 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
bool success{false};
|
bool success{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2410,14 +2421,14 @@ class VoiceAssistantWakeWord : public ProtoMessage {
|
|||||||
void set_wake_word(const StringRef &ref) { this->wake_word_ref_ = ref; }
|
void set_wake_word(const StringRef &ref) { this->wake_word_ref_ = ref; }
|
||||||
std::vector<std::string> trained_languages{};
|
std::vector<std::string> trained_languages{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class VoiceAssistantConfigurationRequest : public ProtoDecodableMessage {
|
class VoiceAssistantConfigurationRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 121;
|
static constexpr uint8_t MESSAGE_TYPE = 121;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
static constexpr uint8_t ESTIMATED_SIZE = 0;
|
||||||
@@ -2438,10 +2449,10 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage {
|
|||||||
const char *message_name() const override { return "voice_assistant_configuration_response"; }
|
const char *message_name() const override { return "voice_assistant_configuration_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::vector<VoiceAssistantWakeWord> available_wake_words{};
|
std::vector<VoiceAssistantWakeWord> available_wake_words{};
|
||||||
std::vector<std::string> active_wake_words{};
|
const std::vector<std::string> *active_wake_words{};
|
||||||
uint32_t max_active_wake_words{0};
|
uint32_t max_active_wake_words{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2476,7 +2487,7 @@ class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage {
|
|||||||
bool requires_code{false};
|
bool requires_code{false};
|
||||||
bool requires_code_to_arm{false};
|
bool requires_code_to_arm{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2492,7 +2503,7 @@ class AlarmControlPanelStateResponse : public StateResponseProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
enums::AlarmControlPanelState state{};
|
enums::AlarmControlPanelState state{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2532,7 +2543,7 @@ class ListEntitiesTextResponse : public InfoResponseProtoMessage {
|
|||||||
void set_pattern(const StringRef &ref) { this->pattern_ref_ = ref; }
|
void set_pattern(const StringRef &ref) { this->pattern_ref_ = ref; }
|
||||||
enums::TextMode mode{};
|
enums::TextMode mode{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2550,7 +2561,7 @@ class TextStateResponse : public StateResponseProtoMessage {
|
|||||||
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2584,7 +2595,7 @@ class ListEntitiesDateResponse : public InfoResponseProtoMessage {
|
|||||||
const char *message_name() const override { return "list_entities_date_response"; }
|
const char *message_name() const override { return "list_entities_date_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2603,7 +2614,7 @@ class DateStateResponse : public StateResponseProtoMessage {
|
|||||||
uint32_t month{0};
|
uint32_t month{0};
|
||||||
uint32_t day{0};
|
uint32_t day{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2638,7 +2649,7 @@ class ListEntitiesTimeResponse : public InfoResponseProtoMessage {
|
|||||||
const char *message_name() const override { return "list_entities_time_response"; }
|
const char *message_name() const override { return "list_entities_time_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2657,7 +2668,7 @@ class TimeStateResponse : public StateResponseProtoMessage {
|
|||||||
uint32_t minute{0};
|
uint32_t minute{0};
|
||||||
uint32_t second{0};
|
uint32_t second{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2695,7 +2706,7 @@ class ListEntitiesEventResponse : public InfoResponseProtoMessage {
|
|||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
std::vector<std::string> event_types{};
|
std::vector<std::string> event_types{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2712,7 +2723,7 @@ class EventResponse : public StateResponseProtoMessage {
|
|||||||
StringRef event_type_ref_{};
|
StringRef event_type_ref_{};
|
||||||
void set_event_type(const StringRef &ref) { this->event_type_ref_ = ref; }
|
void set_event_type(const StringRef &ref) { this->event_type_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2734,7 +2745,7 @@ class ListEntitiesValveResponse : public InfoResponseProtoMessage {
|
|||||||
bool supports_position{false};
|
bool supports_position{false};
|
||||||
bool supports_stop{false};
|
bool supports_stop{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2751,7 +2762,7 @@ class ValveStateResponse : public StateResponseProtoMessage {
|
|||||||
float position{0.0f};
|
float position{0.0f};
|
||||||
enums::ValveOperation current_operation{};
|
enums::ValveOperation current_operation{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2786,7 +2797,7 @@ class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage {
|
|||||||
const char *message_name() const override { return "list_entities_date_time_response"; }
|
const char *message_name() const override { return "list_entities_date_time_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2803,7 +2814,7 @@ class DateTimeStateResponse : public StateResponseProtoMessage {
|
|||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
uint32_t epoch_seconds{0};
|
uint32_t epoch_seconds{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2838,7 +2849,7 @@ class ListEntitiesUpdateResponse : public InfoResponseProtoMessage {
|
|||||||
StringRef device_class_ref_{};
|
StringRef device_class_ref_{};
|
||||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2867,7 +2878,7 @@ class UpdateStateResponse : public StateResponseProtoMessage {
|
|||||||
StringRef release_url_ref_{};
|
StringRef release_url_ref_{};
|
||||||
void set_release_url(const StringRef &ref) { this->release_url_ref_ = ref; }
|
void set_release_url(const StringRef &ref) { this->release_url_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
@@ -383,6 +383,8 @@ template<> const char *proto_enum_to_string<enums::MediaPlayerState>(enums::Medi
|
|||||||
return "MEDIA_PLAYER_STATE_PLAYING";
|
return "MEDIA_PLAYER_STATE_PLAYING";
|
||||||
case enums::MEDIA_PLAYER_STATE_PAUSED:
|
case enums::MEDIA_PLAYER_STATE_PAUSED:
|
||||||
return "MEDIA_PLAYER_STATE_PAUSED";
|
return "MEDIA_PLAYER_STATE_PAUSED";
|
||||||
|
case enums::MEDIA_PLAYER_STATE_ANNOUNCING:
|
||||||
|
return "MEDIA_PLAYER_STATE_ANNOUNCING";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
@@ -399,6 +401,20 @@ template<> const char *proto_enum_to_string<enums::MediaPlayerCommand>(enums::Me
|
|||||||
return "MEDIA_PLAYER_COMMAND_MUTE";
|
return "MEDIA_PLAYER_COMMAND_MUTE";
|
||||||
case enums::MEDIA_PLAYER_COMMAND_UNMUTE:
|
case enums::MEDIA_PLAYER_COMMAND_UNMUTE:
|
||||||
return "MEDIA_PLAYER_COMMAND_UNMUTE";
|
return "MEDIA_PLAYER_COMMAND_UNMUTE";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_TOGGLE:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_TOGGLE";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_VOLUME_UP:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_VOLUME_UP";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_VOLUME_DOWN:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_VOLUME_DOWN";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_ENQUEUE:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_ENQUEUE";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_REPEAT_ONE:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_REPEAT_ONE";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_REPEAT_OFF:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_REPEAT_OFF";
|
||||||
|
case enums::MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST:
|
||||||
|
return "MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
@@ -814,7 +830,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
|||||||
dump_field(out, "icon", this->icon_ref_);
|
dump_field(out, "icon", this->icon_ref_);
|
||||||
#endif
|
#endif
|
||||||
dump_field(out, "entity_category", static_cast<enums::EntityCategory>(this->entity_category));
|
dump_field(out, "entity_category", static_cast<enums::EntityCategory>(this->entity_category));
|
||||||
for (const auto &it : this->supported_preset_modes) {
|
for (const auto &it : *this->supported_preset_modes) {
|
||||||
dump_field(out, "supported_preset_modes", it, 4);
|
dump_field(out, "supported_preset_modes", it, 4);
|
||||||
}
|
}
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
@@ -857,7 +873,7 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
|||||||
dump_field(out, "object_id", this->object_id_ref_);
|
dump_field(out, "object_id", this->object_id_ref_);
|
||||||
dump_field(out, "key", this->key);
|
dump_field(out, "key", this->key);
|
||||||
dump_field(out, "name", this->name_ref_);
|
dump_field(out, "name", this->name_ref_);
|
||||||
for (const auto &it : this->supported_color_modes) {
|
for (const auto &it : *this->supported_color_modes) {
|
||||||
dump_field(out, "supported_color_modes", static_cast<enums::ColorMode>(it), 4);
|
dump_field(out, "supported_color_modes", static_cast<enums::ColorMode>(it), 4);
|
||||||
}
|
}
|
||||||
dump_field(out, "min_mireds", this->min_mireds);
|
dump_field(out, "min_mireds", this->min_mireds);
|
||||||
@@ -1038,13 +1054,14 @@ void NoiseEncryptionSetKeyRequest::dump_to(std::string &out) const {
|
|||||||
}
|
}
|
||||||
void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const { dump_field(out, "success", this->success); }
|
void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const { dump_field(out, "success", this->success); }
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
|
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
|
||||||
out.append("SubscribeHomeassistantServicesRequest {}");
|
out.append("SubscribeHomeassistantServicesRequest {}");
|
||||||
}
|
}
|
||||||
void HomeassistantServiceMap::dump_to(std::string &out) const {
|
void HomeassistantServiceMap::dump_to(std::string &out) const {
|
||||||
MessageDumpHelper helper(out, "HomeassistantServiceMap");
|
MessageDumpHelper helper(out, "HomeassistantServiceMap");
|
||||||
dump_field(out, "key", this->key_ref_);
|
dump_field(out, "key", this->key_ref_);
|
||||||
dump_field(out, "value", this->value_ref_);
|
dump_field(out, "value", this->value);
|
||||||
}
|
}
|
||||||
void HomeassistantServiceResponse::dump_to(std::string &out) const {
|
void HomeassistantServiceResponse::dump_to(std::string &out) const {
|
||||||
MessageDumpHelper helper(out, "HomeassistantServiceResponse");
|
MessageDumpHelper helper(out, "HomeassistantServiceResponse");
|
||||||
@@ -1066,6 +1083,7 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const {
|
|||||||
}
|
}
|
||||||
dump_field(out, "is_event", this->is_event);
|
dump_field(out, "is_event", this->is_event);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
|
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
|
||||||
out.append("SubscribeHomeAssistantStatesRequest {}");
|
out.append("SubscribeHomeAssistantStatesRequest {}");
|
||||||
@@ -1171,26 +1189,26 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
|||||||
dump_field(out, "name", this->name_ref_);
|
dump_field(out, "name", this->name_ref_);
|
||||||
dump_field(out, "supports_current_temperature", this->supports_current_temperature);
|
dump_field(out, "supports_current_temperature", this->supports_current_temperature);
|
||||||
dump_field(out, "supports_two_point_target_temperature", this->supports_two_point_target_temperature);
|
dump_field(out, "supports_two_point_target_temperature", this->supports_two_point_target_temperature);
|
||||||
for (const auto &it : this->supported_modes) {
|
for (const auto &it : *this->supported_modes) {
|
||||||
dump_field(out, "supported_modes", static_cast<enums::ClimateMode>(it), 4);
|
dump_field(out, "supported_modes", static_cast<enums::ClimateMode>(it), 4);
|
||||||
}
|
}
|
||||||
dump_field(out, "visual_min_temperature", this->visual_min_temperature);
|
dump_field(out, "visual_min_temperature", this->visual_min_temperature);
|
||||||
dump_field(out, "visual_max_temperature", this->visual_max_temperature);
|
dump_field(out, "visual_max_temperature", this->visual_max_temperature);
|
||||||
dump_field(out, "visual_target_temperature_step", this->visual_target_temperature_step);
|
dump_field(out, "visual_target_temperature_step", this->visual_target_temperature_step);
|
||||||
dump_field(out, "supports_action", this->supports_action);
|
dump_field(out, "supports_action", this->supports_action);
|
||||||
for (const auto &it : this->supported_fan_modes) {
|
for (const auto &it : *this->supported_fan_modes) {
|
||||||
dump_field(out, "supported_fan_modes", static_cast<enums::ClimateFanMode>(it), 4);
|
dump_field(out, "supported_fan_modes", static_cast<enums::ClimateFanMode>(it), 4);
|
||||||
}
|
}
|
||||||
for (const auto &it : this->supported_swing_modes) {
|
for (const auto &it : *this->supported_swing_modes) {
|
||||||
dump_field(out, "supported_swing_modes", static_cast<enums::ClimateSwingMode>(it), 4);
|
dump_field(out, "supported_swing_modes", static_cast<enums::ClimateSwingMode>(it), 4);
|
||||||
}
|
}
|
||||||
for (const auto &it : this->supported_custom_fan_modes) {
|
for (const auto &it : *this->supported_custom_fan_modes) {
|
||||||
dump_field(out, "supported_custom_fan_modes", it, 4);
|
dump_field(out, "supported_custom_fan_modes", it, 4);
|
||||||
}
|
}
|
||||||
for (const auto &it : this->supported_presets) {
|
for (const auto &it : *this->supported_presets) {
|
||||||
dump_field(out, "supported_presets", static_cast<enums::ClimatePreset>(it), 4);
|
dump_field(out, "supported_presets", static_cast<enums::ClimatePreset>(it), 4);
|
||||||
}
|
}
|
||||||
for (const auto &it : this->supported_custom_presets) {
|
for (const auto &it : *this->supported_custom_presets) {
|
||||||
dump_field(out, "supported_custom_presets", it, 4);
|
dump_field(out, "supported_custom_presets", it, 4);
|
||||||
}
|
}
|
||||||
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
||||||
@@ -1303,7 +1321,7 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const {
|
|||||||
#ifdef USE_ENTITY_ICON
|
#ifdef USE_ENTITY_ICON
|
||||||
dump_field(out, "icon", this->icon_ref_);
|
dump_field(out, "icon", this->icon_ref_);
|
||||||
#endif
|
#endif
|
||||||
for (const auto &it : this->options) {
|
for (const auto &it : *this->options) {
|
||||||
dump_field(out, "options", it, 4);
|
dump_field(out, "options", it, 4);
|
||||||
}
|
}
|
||||||
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
||||||
@@ -1464,6 +1482,7 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
|
|||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
dump_field(out, "device_id", this->device_id);
|
dump_field(out, "device_id", this->device_id);
|
||||||
#endif
|
#endif
|
||||||
|
dump_field(out, "feature_flags", this->feature_flags);
|
||||||
}
|
}
|
||||||
void MediaPlayerStateResponse::dump_to(std::string &out) const {
|
void MediaPlayerStateResponse::dump_to(std::string &out) const {
|
||||||
MessageDumpHelper helper(out, "MediaPlayerStateResponse");
|
MessageDumpHelper helper(out, "MediaPlayerStateResponse");
|
||||||
@@ -1767,7 +1786,7 @@ void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const {
|
|||||||
it.dump_to(out);
|
it.dump_to(out);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
}
|
}
|
||||||
for (const auto &it : this->active_wake_words) {
|
for (const auto &it : *this->active_wake_words) {
|
||||||
dump_field(out, "active_wake_words", it, 4);
|
dump_field(out, "active_wake_words", it, 4);
|
||||||
}
|
}
|
||||||
dump_field(out, "max_active_wake_words", this->max_active_wake_words);
|
dump_field(out, "max_active_wake_words", this->max_active_wake_words);
|
||||||
|
34
esphome/components/api/api_pb2_includes.h
Normal file
34
esphome/components/api/api_pb2_includes.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
// This file provides includes needed by the generated protobuf code
|
||||||
|
// when using pointer optimizations for component-specific types
|
||||||
|
|
||||||
|
#ifdef USE_CLIMATE
|
||||||
|
#include "esphome/components/climate/climate_mode.h"
|
||||||
|
#include "esphome/components/climate/climate_traits.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_LIGHT
|
||||||
|
#include "esphome/components/light/light_traits.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_FAN
|
||||||
|
#include "esphome/components/fan/fan_traits.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
#include "esphome/components/select/select_traits.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Standard library includes that might be needed
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace esphome::api {
|
||||||
|
|
||||||
|
// This file only provides includes, no actual code
|
||||||
|
|
||||||
|
} // namespace esphome::api
|
@@ -35,7 +35,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case DisconnectRequest::MESSAGE_TYPE: {
|
case DisconnectRequest::MESSAGE_TYPE: {
|
||||||
DisconnectRequest msg;
|
DisconnectRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -44,7 +44,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case DisconnectResponse::MESSAGE_TYPE: {
|
case DisconnectResponse::MESSAGE_TYPE: {
|
||||||
DisconnectResponse msg;
|
DisconnectResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -53,7 +53,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case PingRequest::MESSAGE_TYPE: {
|
case PingRequest::MESSAGE_TYPE: {
|
||||||
PingRequest msg;
|
PingRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -62,7 +62,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case PingResponse::MESSAGE_TYPE: {
|
case PingResponse::MESSAGE_TYPE: {
|
||||||
PingResponse msg;
|
PingResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -71,7 +71,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case DeviceInfoRequest::MESSAGE_TYPE: {
|
case DeviceInfoRequest::MESSAGE_TYPE: {
|
||||||
DeviceInfoRequest msg;
|
DeviceInfoRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -80,7 +80,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case ListEntitiesRequest::MESSAGE_TYPE: {
|
case ListEntitiesRequest::MESSAGE_TYPE: {
|
||||||
ListEntitiesRequest msg;
|
ListEntitiesRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -89,7 +89,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
case SubscribeStatesRequest::MESSAGE_TYPE: {
|
case SubscribeStatesRequest::MESSAGE_TYPE: {
|
||||||
SubscribeStatesRequest msg;
|
SubscribeStatesRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -149,18 +149,20 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: {
|
case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: {
|
||||||
SubscribeHomeassistantServicesRequest msg;
|
SubscribeHomeassistantServicesRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_homeassistant_services_request(msg);
|
this->on_subscribe_homeassistant_services_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
case GetTimeRequest::MESSAGE_TYPE: {
|
case GetTimeRequest::MESSAGE_TYPE: {
|
||||||
GetTimeRequest msg;
|
GetTimeRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -179,7 +181,7 @@ 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;
|
SubscribeHomeAssistantStatesRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -388,7 +390,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: {
|
case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: {
|
||||||
SubscribeBluetoothConnectionsFreeRequest msg;
|
SubscribeBluetoothConnectionsFreeRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -399,7 +401,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: {
|
case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: {
|
||||||
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -553,7 +555,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
case VoiceAssistantConfigurationRequest::MESSAGE_TYPE: {
|
case VoiceAssistantConfigurationRequest::MESSAGE_TYPE: {
|
||||||
VoiceAssistantConfigurationRequest msg;
|
VoiceAssistantConfigurationRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -639,12 +641,14 @@ void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &
|
|||||||
this->subscribe_logs(msg);
|
this->subscribe_logs(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void APIServerConnection::on_subscribe_homeassistant_services_request(
|
void APIServerConnection::on_subscribe_homeassistant_services_request(
|
||||||
const SubscribeHomeassistantServicesRequest &msg) {
|
const SubscribeHomeassistantServicesRequest &msg) {
|
||||||
if (this->check_authenticated_()) {
|
if (this->check_authenticated_()) {
|
||||||
this->subscribe_homeassistant_services(msg);
|
this->subscribe_homeassistant_services(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
|
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
|
||||||
if (this->check_authenticated_()) {
|
if (this->check_authenticated_()) {
|
||||||
|
@@ -60,7 +60,9 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
|
virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
|
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
|
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
|
||||||
@@ -218,7 +220,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
|
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
|
||||||
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 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
|
||||||
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
|
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -338,7 +342,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
void on_list_entities_request(const ListEntitiesRequest &msg) override;
|
void on_list_entities_request(const ListEntitiesRequest &msg) override;
|
||||||
void on_subscribe_states_request(const SubscribeStatesRequest &msg) 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
|
||||||
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
|
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
@@ -369,11 +369,13 @@ void APIServer::set_password(const std::string &password) { this->password_ = pa
|
|||||||
|
|
||||||
void APIServer::set_batch_delay(uint16_t batch_delay) { this->batch_delay_ = batch_delay; }
|
void APIServer::set_batch_delay(uint16_t batch_delay) { this->batch_delay_ = batch_delay; }
|
||||||
|
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
for (auto &client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
client->send_homeassistant_service_call(call);
|
client->send_homeassistant_service_call(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||||
|
@@ -106,7 +106,9 @@ class APIServer : public Component, public Controller {
|
|||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
void on_media_player_update(media_player::MediaPlayer *obj) override;
|
void on_media_player_update(media_player::MediaPlayer *obj) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||||
|
#endif
|
||||||
#ifdef USE_API_SERVICES
|
#ifdef USE_API_SERVICES
|
||||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
||||||
#endif
|
#endif
|
||||||
|
@@ -137,6 +137,7 @@ class CustomAPIDevice {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
/** Call a Home Assistant service from ESPHome.
|
/** Call a Home Assistant service from ESPHome.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
@@ -174,7 +175,7 @@ class CustomAPIDevice {
|
|||||||
resp.data.emplace_back();
|
resp.data.emplace_back();
|
||||||
auto &kv = resp.data.back();
|
auto &kv = resp.data.back();
|
||||||
kv.set_key(StringRef(it.first));
|
kv.set_key(StringRef(it.first));
|
||||||
kv.set_value(StringRef(it.second));
|
kv.value = it.second;
|
||||||
}
|
}
|
||||||
global_api_server->send_homeassistant_service_call(resp);
|
global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
@@ -217,10 +218,11 @@ class CustomAPIDevice {
|
|||||||
resp.data.emplace_back();
|
resp.data.emplace_back();
|
||||||
auto &kv = resp.data.back();
|
auto &kv = resp.data.back();
|
||||||
kv.set_key(StringRef(it.first));
|
kv.set_key(StringRef(it.first));
|
||||||
kv.set_value(StringRef(it.second));
|
kv.value = it.second;
|
||||||
}
|
}
|
||||||
global_api_server->send_homeassistant_service_call(resp);
|
global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "api_server.h"
|
#include "api_server.h"
|
||||||
#ifdef USE_API
|
#ifdef USE_API
|
||||||
|
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||||
#include "api_pb2.h"
|
#include "api_pb2.h"
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
@@ -69,22 +70,19 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
resp.data.emplace_back();
|
resp.data.emplace_back();
|
||||||
auto &kv = resp.data.back();
|
auto &kv = resp.data.back();
|
||||||
kv.set_key(StringRef(it.key));
|
kv.set_key(StringRef(it.key));
|
||||||
std::string value = it.value.value(x...);
|
kv.value = it.value.value(x...);
|
||||||
kv.set_value(StringRef(value));
|
|
||||||
}
|
}
|
||||||
for (auto &it : this->data_template_) {
|
for (auto &it : this->data_template_) {
|
||||||
resp.data_template.emplace_back();
|
resp.data_template.emplace_back();
|
||||||
auto &kv = resp.data_template.back();
|
auto &kv = resp.data_template.back();
|
||||||
kv.set_key(StringRef(it.key));
|
kv.set_key(StringRef(it.key));
|
||||||
std::string value = it.value.value(x...);
|
kv.value = it.value.value(x...);
|
||||||
kv.set_value(StringRef(value));
|
|
||||||
}
|
}
|
||||||
for (auto &it : this->variables_) {
|
for (auto &it : this->variables_) {
|
||||||
resp.variables.emplace_back();
|
resp.variables.emplace_back();
|
||||||
auto &kv = resp.variables.back();
|
auto &kv = resp.variables.back();
|
||||||
kv.set_key(StringRef(it.key));
|
kv.set_key(StringRef(it.key));
|
||||||
std::string value = it.value.value(x...);
|
kv.value = it.value.value(x...);
|
||||||
kv.set_value(StringRef(value));
|
|
||||||
}
|
}
|
||||||
this->parent_->send_homeassistant_service_call(resp);
|
this->parent_->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
@@ -100,3 +98,4 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
@@ -35,11 +35,10 @@ namespace esphome::api {
|
|||||||
*
|
*
|
||||||
* Unsafe Patterns (WILL cause crashes/corruption):
|
* Unsafe Patterns (WILL cause crashes/corruption):
|
||||||
* 1. Temporaries: msg.set_field(StringRef(obj.get_string())) // get_string() returns by value
|
* 1. Temporaries: msg.set_field(StringRef(obj.get_string())) // get_string() returns by value
|
||||||
* 2. Optional values: msg.set_field(StringRef(optional.value())) // value() returns a copy
|
* 2. Concatenation: msg.set_field(StringRef(str1 + str2)) // Result is temporary
|
||||||
* 3. Concatenation: msg.set_field(StringRef(str1 + str2)) // Result is temporary
|
|
||||||
*
|
*
|
||||||
* For unsafe patterns, store in a local variable first:
|
* For unsafe patterns, store in a local variable first:
|
||||||
* std::string temp = optional.value(); // or get_string() or str1 + str2
|
* std::string temp = get_string(); // or str1 + str2
|
||||||
* msg.set_field(StringRef(temp));
|
* msg.set_field(StringRef(temp));
|
||||||
*
|
*
|
||||||
* The send_*_response pattern ensures proper lifetime management by encoding
|
* The send_*_response pattern ensures proper lifetime management by encoding
|
||||||
@@ -334,13 +333,16 @@ class ProtoWriteBuffer {
|
|||||||
std::vector<uint8_t> *buffer_;
|
std::vector<uint8_t> *buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class ProtoSize;
|
||||||
|
|
||||||
class ProtoMessage {
|
class ProtoMessage {
|
||||||
public:
|
public:
|
||||||
virtual ~ProtoMessage() = default;
|
virtual ~ProtoMessage() = default;
|
||||||
// Default implementation for messages with no fields
|
// Default implementation for messages with no fields
|
||||||
virtual void encode(ProtoWriteBuffer buffer) const {}
|
virtual void encode(ProtoWriteBuffer buffer) const {}
|
||||||
// Default implementation for messages with no fields
|
// Default implementation for messages with no fields
|
||||||
virtual void calculate_size(uint32_t &total_size) const {}
|
virtual void calculate_size(ProtoSize &size) const {}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string dump() const;
|
std::string dump() const;
|
||||||
virtual void dump_to(std::string &out) const = 0;
|
virtual void dump_to(std::string &out) const = 0;
|
||||||
@@ -361,24 +363,32 @@ class ProtoDecodableMessage : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class ProtoSize {
|
class ProtoSize {
|
||||||
|
private:
|
||||||
|
uint32_t total_size_ = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief ProtoSize class for Protocol Buffer serialization size calculation
|
* @brief ProtoSize class for Protocol Buffer serialization size calculation
|
||||||
*
|
*
|
||||||
* This class provides static methods to calculate the exact byte counts needed
|
* This class provides methods to calculate the exact byte counts needed
|
||||||
* for encoding various Protocol Buffer field types. All methods are designed to be
|
* for encoding various Protocol Buffer field types. The class now uses an
|
||||||
* efficient for the common case where many fields have default values.
|
* object-based approach to reduce parameter passing overhead while keeping
|
||||||
|
* varint calculation methods static for external use.
|
||||||
*
|
*
|
||||||
* Implements Protocol Buffer encoding size calculation according to:
|
* Implements Protocol Buffer encoding size calculation according to:
|
||||||
* https://protobuf.dev/programming-guides/encoding/
|
* https://protobuf.dev/programming-guides/encoding/
|
||||||
*
|
*
|
||||||
* Key features:
|
* Key features:
|
||||||
|
* - Object-based approach reduces flash usage by eliminating parameter passing
|
||||||
* - Early-return optimization for zero/default values
|
* - Early-return optimization for zero/default values
|
||||||
* - Direct total_size updates to avoid unnecessary additions
|
* - Static varint methods for external callers
|
||||||
* - Specialized handling for different field types according to protobuf spec
|
* - Specialized handling for different field types according to protobuf spec
|
||||||
* - Templated helpers for repeated fields and messages
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
ProtoSize() = default;
|
||||||
|
|
||||||
|
uint32_t get_size() const { return total_size_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
||||||
*
|
*
|
||||||
@@ -479,9 +489,7 @@ class ProtoSize {
|
|||||||
* @brief Common parameters for all add_*_field methods
|
* @brief Common parameters for all add_*_field methods
|
||||||
*
|
*
|
||||||
* All add_*_field methods follow these common patterns:
|
* All add_*_field methods follow these common patterns:
|
||||||
*
|
* * @param field_id_size Pre-calculated size of the field ID in bytes
|
||||||
* @param total_size Reference to the total message size to update
|
|
||||||
* @param field_id_size Pre-calculated size of the field ID in bytes
|
|
||||||
* @param value The value to calculate size for (type varies)
|
* @param value The value to calculate size for (type varies)
|
||||||
* @param force Whether to calculate size even if the value is default/zero/empty
|
* @param force Whether to calculate size even if the value is default/zero/empty
|
||||||
*
|
*
|
||||||
@@ -494,85 +502,63 @@ class ProtoSize {
|
|||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of an int32 field to the total message size
|
* @brief Calculates and adds the size of an int32 field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
|
inline void add_int32(uint32_t field_id_size, int32_t value) {
|
||||||
// Skip calculation if value is zero
|
if (value != 0) {
|
||||||
if (value == 0) {
|
add_int32_force(field_id_size, value);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and directly add to total_size
|
|
||||||
if (value < 0) {
|
|
||||||
// Negative values are encoded as 10-byte varints in protobuf
|
|
||||||
total_size += field_id_size + 10;
|
|
||||||
} else {
|
|
||||||
// For non-negative values, use the standard varint size
|
|
||||||
total_size += field_id_size + varint(static_cast<uint32_t>(value));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of an int32 field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of an int32 field to the total message size (force version)
|
||||||
*/
|
*/
|
||||||
static inline void add_int32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
|
inline void add_int32_force(uint32_t field_id_size, int32_t value) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when forced
|
||||||
if (value < 0) {
|
// Negative values are encoded as 10-byte varints in protobuf
|
||||||
// Negative values are encoded as 10-byte varints in protobuf
|
total_size_ += field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
|
||||||
total_size += field_id_size + 10;
|
|
||||||
} else {
|
|
||||||
// For non-negative values, use the standard varint size
|
|
||||||
total_size += field_id_size + varint(static_cast<uint32_t>(value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a uint32 field to the total message size
|
* @brief Calculates and adds the size of a uint32 field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
|
inline void add_uint32(uint32_t field_id_size, uint32_t value) {
|
||||||
// Skip calculation if value is zero
|
if (value != 0) {
|
||||||
if (value == 0) {
|
add_uint32_force(field_id_size, value);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and directly add to total_size
|
|
||||||
total_size += field_id_size + varint(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a uint32 field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a uint32 field to the total message size (force version)
|
||||||
*/
|
*/
|
||||||
static inline void add_uint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
|
inline void add_uint32_force(uint32_t field_id_size, uint32_t value) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
total_size += field_id_size + varint(value);
|
total_size_ += field_id_size + varint(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a boolean field to the total message size
|
* @brief Calculates and adds the size of a boolean field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value) {
|
inline void add_bool(uint32_t field_id_size, bool value) {
|
||||||
// Skip calculation if value is false
|
if (value) {
|
||||||
if (!value) {
|
// Boolean fields always use 1 byte when true
|
||||||
return; // No need to update total_size
|
total_size_ += field_id_size + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boolean fields always use 1 byte when true
|
|
||||||
total_size += field_id_size + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a boolean field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a boolean field to the total message size (force version)
|
||||||
*/
|
*/
|
||||||
static inline void add_bool_field_repeated(uint32_t &total_size, uint32_t field_id_size, bool value) {
|
inline void add_bool_force(uint32_t field_id_size, bool value) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
// Boolean fields always use 1 byte
|
// Boolean fields always use 1 byte
|
||||||
total_size += field_id_size + 1;
|
total_size_ += field_id_size + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a float field to the total message size
|
* @brief Calculates and adds the size of a float field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_float_field(uint32_t &total_size, uint32_t field_id_size, float value) {
|
inline void add_float(uint32_t field_id_size, float value) {
|
||||||
if (value != 0.0f) {
|
if (value != 0.0f) {
|
||||||
total_size += field_id_size + 4;
|
total_size_ += field_id_size + 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,9 +568,9 @@ class ProtoSize {
|
|||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a fixed32 field to the total message size
|
* @brief Calculates and adds the size of a fixed32 field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_fixed32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
|
inline void add_fixed32(uint32_t field_id_size, uint32_t value) {
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
total_size += field_id_size + 4;
|
total_size_ += field_id_size + 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,149 +580,104 @@ class ProtoSize {
|
|||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a sfixed32 field to the total message size
|
* @brief Calculates and adds the size of a sfixed32 field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_sfixed32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
|
inline void add_sfixed32(uint32_t field_id_size, int32_t value) {
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
total_size += field_id_size + 4;
|
total_size_ += field_id_size + 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
|
// NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
|
||||||
// to reduce overhead on embedded systems
|
// to reduce overhead on embedded systems
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of an enum field to the total message size
|
|
||||||
*
|
|
||||||
* Enum fields are encoded as uint32 varints.
|
|
||||||
*/
|
|
||||||
static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
|
|
||||||
// Skip calculation if value is zero
|
|
||||||
if (value == 0) {
|
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums are encoded as uint32
|
|
||||||
total_size += field_id_size + varint(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of an enum field to the total message size (repeated field version)
|
|
||||||
*
|
|
||||||
* Enum fields are encoded as uint32 varints.
|
|
||||||
*/
|
|
||||||
static inline void add_enum_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
|
|
||||||
// Always calculate size for repeated fields
|
|
||||||
// Enums are encoded as uint32
|
|
||||||
total_size += field_id_size + varint(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a sint32 field to the total message size
|
* @brief Calculates and adds the size of a sint32 field to the total message size
|
||||||
*
|
*
|
||||||
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
||||||
*/
|
*/
|
||||||
static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
|
inline void add_sint32(uint32_t field_id_size, int32_t value) {
|
||||||
// Skip calculation if value is zero
|
if (value != 0) {
|
||||||
if (value == 0) {
|
add_sint32_force(field_id_size, value);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
|
|
||||||
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
|
|
||||||
total_size += field_id_size + varint(zigzag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a sint32 field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a sint32 field to the total message size (force version)
|
||||||
*
|
*
|
||||||
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
||||||
*/
|
*/
|
||||||
static inline void add_sint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
|
inline void add_sint32_force(uint32_t field_id_size, int32_t value) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
|
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
|
||||||
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
|
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
|
||||||
total_size += field_id_size + varint(zigzag);
|
total_size_ += field_id_size + varint(zigzag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of an int64 field to the total message size
|
* @brief Calculates and adds the size of an int64 field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
|
inline void add_int64(uint32_t field_id_size, int64_t value) {
|
||||||
// Skip calculation if value is zero
|
if (value != 0) {
|
||||||
if (value == 0) {
|
add_int64_force(field_id_size, value);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and directly add to total_size
|
|
||||||
total_size += field_id_size + varint(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of an int64 field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of an int64 field to the total message size (force version)
|
||||||
*/
|
*/
|
||||||
static inline void add_int64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
|
inline void add_int64_force(uint32_t field_id_size, int64_t value) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
total_size += field_id_size + varint(value);
|
total_size_ += field_id_size + varint(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a uint64 field to the total message size
|
* @brief Calculates and adds the size of a uint64 field to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value) {
|
inline void add_uint64(uint32_t field_id_size, uint64_t value) {
|
||||||
// Skip calculation if value is zero
|
if (value != 0) {
|
||||||
if (value == 0) {
|
add_uint64_force(field_id_size, value);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and directly add to total_size
|
|
||||||
total_size += field_id_size + varint(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a uint64 field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a uint64 field to the total message size (force version)
|
||||||
*/
|
*/
|
||||||
static inline void add_uint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint64_t value) {
|
inline void add_uint64_force(uint32_t field_id_size, uint64_t value) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
total_size += field_id_size + varint(value);
|
total_size_ += field_id_size + varint(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: sint64 support functions (add_sint64_field, add_sint64_field_repeated) removed
|
// NOTE: sint64 support functions (add_sint64_field, add_sint64_field_force) removed
|
||||||
// sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
|
// sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a string field using length
|
* @brief Calculates and adds the size of a length-delimited field (string/bytes) to the total message size
|
||||||
*/
|
*/
|
||||||
static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, size_t len) {
|
inline void add_length(uint32_t field_id_size, size_t len) {
|
||||||
// Skip calculation if string is empty
|
if (len != 0) {
|
||||||
if (len == 0) {
|
add_length_force(field_id_size, len);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field ID + length varint + string bytes
|
|
||||||
total_size += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a length-delimited field (string/bytes) to the total message size (repeated
|
||||||
|
* field version)
|
||||||
*/
|
*/
|
||||||
static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) {
|
inline void add_length_force(uint32_t field_id_size, size_t len) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
const uint32_t str_size = static_cast<uint32_t>(str.size());
|
|
||||||
total_size += field_id_size + varint(str_size) + str_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a bytes field to the total message size
|
|
||||||
*/
|
|
||||||
static inline void add_bytes_field(uint32_t &total_size, uint32_t field_id_size, size_t len) {
|
|
||||||
// Skip calculation if bytes is empty
|
|
||||||
if (len == 0) {
|
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field ID + length varint + data bytes
|
// Field ID + length varint + data bytes
|
||||||
total_size += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
total_size_ += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a pre-calculated size directly to the total
|
||||||
|
*
|
||||||
|
* This is used when we can calculate the total size by multiplying the number
|
||||||
|
* of elements by the bytes per element (for repeated fixed-size types like float, fixed32, etc.)
|
||||||
|
*
|
||||||
|
* @param size The pre-calculated total size to add
|
||||||
|
*/
|
||||||
|
inline void add_precalculated_size(uint32_t size) { total_size_ += size; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size
|
* @brief Calculates and adds the size of a nested message field to the total message size
|
||||||
*
|
*
|
||||||
@@ -745,26 +686,21 @@ class ProtoSize {
|
|||||||
*
|
*
|
||||||
* @param nested_size The pre-calculated size of the nested message
|
* @param nested_size The pre-calculated size of the nested message
|
||||||
*/
|
*/
|
||||||
static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) {
|
inline void add_message_field(uint32_t field_id_size, uint32_t nested_size) {
|
||||||
// Skip calculation if nested message is empty
|
if (nested_size != 0) {
|
||||||
if (nested_size == 0) {
|
add_message_field_force(field_id_size, nested_size);
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and directly add to total_size
|
|
||||||
// Field ID + length varint + nested message content
|
|
||||||
total_size += field_id_size + varint(nested_size) + nested_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a nested message field to the total message size (force version)
|
||||||
*
|
*
|
||||||
* @param nested_size The pre-calculated size of the nested message
|
* @param nested_size The pre-calculated size of the nested message
|
||||||
*/
|
*/
|
||||||
static inline void add_message_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) {
|
inline void add_message_field_force(uint32_t field_id_size, uint32_t nested_size) {
|
||||||
// Always calculate size for repeated fields
|
// Always calculate size when force is true
|
||||||
// Field ID + length varint + nested message content
|
// Field ID + length varint + nested message content
|
||||||
total_size += field_id_size + varint(nested_size) + nested_size;
|
total_size_ += field_id_size + varint(nested_size) + nested_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -776,26 +712,29 @@ class ProtoSize {
|
|||||||
*
|
*
|
||||||
* @param message The nested message object
|
* @param message The nested message object
|
||||||
*/
|
*/
|
||||||
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message) {
|
inline void add_message_object(uint32_t field_id_size, const ProtoMessage &message) {
|
||||||
uint32_t nested_size = 0;
|
// Calculate nested message size by creating a temporary ProtoSize
|
||||||
message.calculate_size(nested_size);
|
ProtoSize nested_calc;
|
||||||
|
message.calculate_size(nested_calc);
|
||||||
|
uint32_t nested_size = nested_calc.get_size();
|
||||||
|
|
||||||
// Use the base implementation with the calculated nested_size
|
// Use the base implementation with the calculated nested_size
|
||||||
add_message_field(total_size, field_id_size, nested_size);
|
add_message_field(field_id_size, nested_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size (repeated field version)
|
* @brief Calculates and adds the size of a nested message field to the total message size (force version)
|
||||||
*
|
*
|
||||||
* @param message The nested message object
|
* @param message The nested message object
|
||||||
*/
|
*/
|
||||||
static inline void add_message_object_repeated(uint32_t &total_size, uint32_t field_id_size,
|
inline void add_message_object_force(uint32_t field_id_size, const ProtoMessage &message) {
|
||||||
const ProtoMessage &message) {
|
// Calculate nested message size by creating a temporary ProtoSize
|
||||||
uint32_t nested_size = 0;
|
ProtoSize nested_calc;
|
||||||
message.calculate_size(nested_size);
|
message.calculate_size(nested_calc);
|
||||||
|
uint32_t nested_size = nested_calc.get_size();
|
||||||
|
|
||||||
// Use the base implementation with the calculated nested_size
|
// Use the base implementation with the calculated nested_size
|
||||||
add_message_field_repeated(total_size, field_id_size, nested_size);
|
add_message_field_force(field_id_size, nested_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -808,16 +747,15 @@ class ProtoSize {
|
|||||||
* @param messages Vector of message objects
|
* @param messages Vector of message objects
|
||||||
*/
|
*/
|
||||||
template<typename MessageType>
|
template<typename MessageType>
|
||||||
static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size,
|
inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
|
||||||
const std::vector<MessageType> &messages) {
|
|
||||||
// Skip if the vector is empty
|
// Skip if the vector is empty
|
||||||
if (messages.empty()) {
|
if (messages.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the repeated field version for all messages
|
// Use the force version for all messages in the repeated field
|
||||||
for (const auto &message : messages) {
|
for (const auto &message : messages) {
|
||||||
add_message_object_repeated(total_size, field_id_size, message);
|
add_message_object_force(field_id_size, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -827,8 +765,9 @@ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessa
|
|||||||
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
|
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
|
||||||
|
|
||||||
// Calculate the message size first
|
// Calculate the message size first
|
||||||
uint32_t msg_length_bytes = 0;
|
ProtoSize msg_size;
|
||||||
value.calculate_size(msg_length_bytes);
|
value.calculate_size(msg_size);
|
||||||
|
uint32_t msg_length_bytes = msg_size.get_size();
|
||||||
|
|
||||||
// Calculate how many bytes the length varint needs
|
// Calculate how many bytes the length varint needs
|
||||||
uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
|
uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
|
||||||
@@ -877,8 +816,9 @@ class ProtoService {
|
|||||||
|
|
||||||
// Optimized method that pre-allocates buffer based on message size
|
// Optimized method that pre-allocates buffer based on message size
|
||||||
bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
|
bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
|
||||||
uint32_t msg_size = 0;
|
ProtoSize size;
|
||||||
msg.calculate_size(msg_size);
|
msg.calculate_size(size);
|
||||||
|
uint32_t msg_size = size.get_size();
|
||||||
|
|
||||||
// Create a pre-sized buffer
|
// Create a pre-sized buffer
|
||||||
auto buffer = this->create_buffer(msg_size);
|
auto buffer = this->create_buffer(msg_size);
|
||||||
|
@@ -516,6 +516,7 @@ def binary_sensor_schema(
|
|||||||
icon: str = cv.UNDEFINED,
|
icon: str = cv.UNDEFINED,
|
||||||
entity_category: str = cv.UNDEFINED,
|
entity_category: str = cv.UNDEFINED,
|
||||||
device_class: str = cv.UNDEFINED,
|
device_class: str = cv.UNDEFINED,
|
||||||
|
filters: list = cv.UNDEFINED,
|
||||||
) -> cv.Schema:
|
) -> cv.Schema:
|
||||||
schema = {}
|
schema = {}
|
||||||
|
|
||||||
@@ -527,6 +528,7 @@ def binary_sensor_schema(
|
|||||||
(CONF_ICON, icon, cv.icon),
|
(CONF_ICON, icon, cv.icon),
|
||||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||||
|
(CONF_FILTERS, filters, validate_filters),
|
||||||
]:
|
]:
|
||||||
if default is not cv.UNDEFINED:
|
if default is not cv.UNDEFINED:
|
||||||
schema[cv.Optional(key, default=default)] = validator
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
|
@@ -80,15 +80,10 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
&service_result, &service_count, this->send_service_);
|
&service_result, &service_count, this->send_service_);
|
||||||
this->send_service_++;
|
this->send_service_++;
|
||||||
|
|
||||||
if (service_status != ESP_GATT_OK) {
|
if (service_status != ESP_GATT_OK || service_count == 0) {
|
||||||
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service error at offset=%d, status=%d", this->connection_index_,
|
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service %s, status=%d, service_count=%d, offset=%d",
|
||||||
this->address_str().c_str(), this->send_service_ - 1, service_status);
|
this->connection_index_, this->address_str().c_str(), service_status != ESP_GATT_OK ? "error" : "missing",
|
||||||
return;
|
service_status, service_count, this->send_service_ - 1);
|
||||||
}
|
|
||||||
|
|
||||||
if (service_count == 0) {
|
|
||||||
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service missing, service_count=%d", this->connection_index_,
|
|
||||||
this->address_str().c_str(), service_count);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,15 +99,20 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_CHARACTERISTIC,
|
esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_CHARACTERISTIC,
|
||||||
service_result.start_handle, service_result.end_handle, 0, &total_char_count);
|
service_result.start_handle, service_result.end_handle, 0, &total_char_count);
|
||||||
|
|
||||||
if (char_count_status == ESP_GATT_OK && total_char_count > 0) {
|
if (char_count_status != ESP_GATT_OK) {
|
||||||
// Only reserve if we successfully got a count
|
|
||||||
service_resp.characteristics.reserve(total_char_count);
|
|
||||||
} else if (char_count_status != ESP_GATT_OK) {
|
|
||||||
ESP_LOGW(TAG, "[%d] [%s] Error getting characteristic count, status=%d", this->connection_index_,
|
ESP_LOGW(TAG, "[%d] [%s] Error getting characteristic count, status=%d", this->connection_index_,
|
||||||
this->address_str().c_str(), char_count_status);
|
this->address_str().c_str(), char_count_status);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now process characteristics
|
if (total_char_count == 0) {
|
||||||
|
// No characteristics, just send the service response
|
||||||
|
api_conn->send_message(resp, api::BluetoothGATTGetServicesResponse::MESSAGE_TYPE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve space and process characteristics
|
||||||
|
service_resp.characteristics.reserve(total_char_count);
|
||||||
uint16_t char_offset = 0;
|
uint16_t char_offset = 0;
|
||||||
esp_gattc_char_elem_t char_result;
|
esp_gattc_char_elem_t char_result;
|
||||||
while (true) { // characteristics
|
while (true) { // characteristics
|
||||||
@@ -126,7 +126,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
if (char_status != ESP_GATT_OK) {
|
if (char_status != ESP_GATT_OK) {
|
||||||
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", this->connection_index_,
|
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", this->connection_index_,
|
||||||
this->address_str().c_str(), char_status);
|
this->address_str().c_str(), char_status);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
if (char_count == 0) {
|
if (char_count == 0) {
|
||||||
break;
|
break;
|
||||||
@@ -141,19 +141,21 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
|
|
||||||
// Get the number of descriptors directly with one call
|
// Get the number of descriptors directly with one call
|
||||||
uint16_t total_desc_count = 0;
|
uint16_t total_desc_count = 0;
|
||||||
esp_gatt_status_t desc_count_status =
|
esp_gatt_status_t desc_count_status = esp_ble_gattc_get_attr_count(
|
||||||
esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_DESCRIPTOR, char_result.char_handle,
|
this->gattc_if_, this->conn_id_, ESP_GATT_DB_DESCRIPTOR, 0, 0, char_result.char_handle, &total_desc_count);
|
||||||
service_result.end_handle, 0, &total_desc_count);
|
|
||||||
|
|
||||||
if (desc_count_status == ESP_GATT_OK && total_desc_count > 0) {
|
if (desc_count_status != ESP_GATT_OK) {
|
||||||
// Only reserve if we successfully got a count
|
|
||||||
characteristic_resp.descriptors.reserve(total_desc_count);
|
|
||||||
} else if (desc_count_status != ESP_GATT_OK) {
|
|
||||||
ESP_LOGW(TAG, "[%d] [%s] Error getting descriptor count for char handle %d, status=%d", this->connection_index_,
|
ESP_LOGW(TAG, "[%d] [%s] Error getting descriptor count for char handle %d, status=%d", this->connection_index_,
|
||||||
this->address_str().c_str(), char_result.char_handle, desc_count_status);
|
this->address_str().c_str(), char_result.char_handle, desc_count_status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (total_desc_count == 0) {
|
||||||
|
// No descriptors, continue to next characteristic
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now process descriptors
|
// Reserve space and process descriptors
|
||||||
|
characteristic_resp.descriptors.reserve(total_desc_count);
|
||||||
uint16_t desc_offset = 0;
|
uint16_t desc_offset = 0;
|
||||||
esp_gattc_descr_elem_t desc_result;
|
esp_gattc_descr_elem_t desc_result;
|
||||||
while (true) { // descriptors
|
while (true) { // descriptors
|
||||||
@@ -166,10 +168,10 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
if (desc_status != ESP_GATT_OK) {
|
if (desc_status != ESP_GATT_OK) {
|
||||||
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_descr error, status=%d", this->connection_index_,
|
ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_descr error, status=%d", this->connection_index_,
|
||||||
this->address_str().c_str(), desc_status);
|
this->address_str().c_str(), desc_status);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
if (desc_count == 0) {
|
if (desc_count == 0) {
|
||||||
break;
|
break; // No more descriptors
|
||||||
}
|
}
|
||||||
|
|
||||||
characteristic_resp.descriptors.emplace_back();
|
characteristic_resp.descriptors.emplace_back();
|
||||||
|
@@ -5,6 +5,13 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
#ifdef USE_API
|
||||||
|
namespace api {
|
||||||
|
class APIConnection;
|
||||||
|
} // namespace api
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace climate {
|
namespace climate {
|
||||||
|
|
||||||
/** This class contains all static data for climate devices.
|
/** This class contains all static data for climate devices.
|
||||||
@@ -173,6 +180,23 @@ class ClimateTraits {
|
|||||||
void set_visual_max_humidity(float visual_max_humidity) { this->visual_max_humidity_ = visual_max_humidity; }
|
void set_visual_max_humidity(float visual_max_humidity) { this->visual_max_humidity_ = visual_max_humidity; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#ifdef USE_API
|
||||||
|
// The API connection is a friend class to access internal methods
|
||||||
|
friend class api::APIConnection;
|
||||||
|
// These methods return references to internal data structures.
|
||||||
|
// They are used by the API to avoid copying data when encoding messages.
|
||||||
|
// Warning: Do not use these methods outside of the API connection code.
|
||||||
|
// They return references to internal data that can be invalidated.
|
||||||
|
const std::set<ClimateMode> &get_supported_modes_for_api_() const { return this->supported_modes_; }
|
||||||
|
const std::set<ClimateFanMode> &get_supported_fan_modes_for_api_() const { return this->supported_fan_modes_; }
|
||||||
|
const std::set<std::string> &get_supported_custom_fan_modes_for_api_() const {
|
||||||
|
return this->supported_custom_fan_modes_;
|
||||||
|
}
|
||||||
|
const std::set<climate::ClimatePreset> &get_supported_presets_for_api_() const { return this->supported_presets_; }
|
||||||
|
const std::set<std::string> &get_supported_custom_presets_for_api_() const { return this->supported_custom_presets_; }
|
||||||
|
const std::set<ClimateSwingMode> &get_supported_swing_modes_for_api_() const { return this->supported_swing_modes_; }
|
||||||
|
#endif
|
||||||
|
|
||||||
void set_mode_support_(climate::ClimateMode mode, bool supported) {
|
void set_mode_support_(climate::ClimateMode mode, bool supported) {
|
||||||
if (supported) {
|
if (supported) {
|
||||||
this->supported_modes_.insert(mode);
|
this->supported_modes_.insert(mode);
|
||||||
|
@@ -313,7 +313,7 @@ def _format_framework_espidf_version(
|
|||||||
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 2, 1)
|
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 2, 1)
|
||||||
# The platform-espressif32 version to use for arduino frameworks
|
# The platform-espressif32 version to use for arduino frameworks
|
||||||
# - https://github.com/pioarduino/platform-espressif32/releases
|
# - https://github.com/pioarduino/platform-espressif32/releases
|
||||||
ARDUINO_PLATFORM_VERSION = cv.Version(54, 3, 21)
|
ARDUINO_PLATFORM_VERSION = cv.Version(54, 3, 21, "1")
|
||||||
|
|
||||||
# The default/recommended esp-idf framework version
|
# The default/recommended esp-idf framework version
|
||||||
# - https://github.com/espressif/esp-idf/releases
|
# - https://github.com/espressif/esp-idf/releases
|
||||||
@@ -322,7 +322,7 @@ RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 4, 2)
|
|||||||
# The platformio/espressif32 version to use for esp-idf frameworks
|
# The platformio/espressif32 version to use for esp-idf frameworks
|
||||||
# - https://github.com/platformio/platform-espressif32/releases
|
# - https://github.com/platformio/platform-espressif32/releases
|
||||||
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
|
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
|
||||||
ESP_IDF_PLATFORM_VERSION = cv.Version(54, 3, 21)
|
ESP_IDF_PLATFORM_VERSION = cv.Version(54, 3, 21, "1")
|
||||||
|
|
||||||
# List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions
|
# List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions
|
||||||
SUPPORTED_PLATFORMIO_ESP_IDF_5X = [
|
SUPPORTED_PLATFORMIO_ESP_IDF_5X = [
|
||||||
@@ -468,10 +468,10 @@ def _parse_platform_version(value):
|
|||||||
try:
|
try:
|
||||||
ver = cv.Version.parse(cv.version_number(value))
|
ver = cv.Version.parse(cv.version_number(value))
|
||||||
if ver.major >= 50: # a pioarduino version
|
if ver.major >= 50: # a pioarduino version
|
||||||
if "-" in value:
|
release = f"{ver.major}.{ver.minor:02d}.{ver.patch:02d}"
|
||||||
# maybe a release candidate?...definitely not our default, just use it as-is...
|
if ver.extra:
|
||||||
return f"https://github.com/pioarduino/platform-espressif32/releases/download/{value}/platform-espressif32.zip"
|
release += f"-{ver.extra}"
|
||||||
return f"https://github.com/pioarduino/platform-espressif32/releases/download/{ver.major}.{ver.minor:02d}.{ver.patch:02d}/platform-espressif32.zip"
|
return f"https://github.com/pioarduino/platform-espressif32/releases/download/{release}/platform-espressif32.zip"
|
||||||
# if platform version is a valid version constraint, prefix the default package
|
# if platform version is a valid version constraint, prefix the default package
|
||||||
cv.platformio_version_constraint(value)
|
cv.platformio_version_constraint(value)
|
||||||
return f"platformio/espressif32@{value}"
|
return f"platformio/espressif32@{value}"
|
||||||
@@ -571,6 +571,8 @@ CONF_SDKCONFIG_OPTIONS = "sdkconfig_options"
|
|||||||
CONF_ENABLE_LWIP_DHCP_SERVER = "enable_lwip_dhcp_server"
|
CONF_ENABLE_LWIP_DHCP_SERVER = "enable_lwip_dhcp_server"
|
||||||
CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries"
|
CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries"
|
||||||
CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface"
|
CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface"
|
||||||
|
CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING = "enable_lwip_tcpip_core_locking"
|
||||||
|
CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY = "enable_lwip_check_thread_safety"
|
||||||
|
|
||||||
|
|
||||||
def _validate_idf_component(config: ConfigType) -> ConfigType:
|
def _validate_idf_component(config: ConfigType) -> ConfigType:
|
||||||
@@ -619,6 +621,12 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
|
|||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False
|
CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False
|
||||||
): cv.boolean,
|
): cv.boolean,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, default=True
|
||||||
|
): cv.boolean,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, default=True
|
||||||
|
): cv.boolean,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
|
cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
|
||||||
@@ -785,6 +793,18 @@ async def to_code(config):
|
|||||||
if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False):
|
if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False):
|
||||||
add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)
|
add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)
|
||||||
|
|
||||||
|
# Apply LWIP core locking for better socket performance
|
||||||
|
# This is already enabled by default in Arduino framework, where it provides
|
||||||
|
# significant performance benefits. Our benchmarks show socket operations are
|
||||||
|
# 24-200% faster with core locking enabled:
|
||||||
|
# - select() on 4 sockets: ~190μs (Arduino/core locking) vs ~235μs (ESP-IDF default)
|
||||||
|
# - Up to 200% slower under load when all operations queue through tcpip_thread
|
||||||
|
# Enabling this makes ESP-IDF socket performance match Arduino framework.
|
||||||
|
if advanced.get(CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, True):
|
||||||
|
add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_CORE_LOCKING", True)
|
||||||
|
if advanced.get(CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, True):
|
||||||
|
add_idf_sdkconfig_option("CONFIG_LWIP_CHECK_THREAD_SAFETY", True)
|
||||||
|
|
||||||
cg.add_platformio_option("board_build.partitions", "partitions.csv")
|
cg.add_platformio_option("board_build.partitions", "partitions.csv")
|
||||||
if CONF_PARTITIONS in config:
|
if CONF_PARTITIONS in config:
|
||||||
add_extra_build_file(
|
add_extra_build_file(
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
Import("env")
|
Import("env") # noqa: F821
|
||||||
|
|
||||||
|
import itertools # noqa: E402
|
||||||
|
import json # noqa: E402
|
||||||
|
import os # noqa: E402
|
||||||
|
import pathlib # noqa: E402
|
||||||
|
import shutil # noqa: E402
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import shutil
|
|
||||||
import pathlib
|
|
||||||
import itertools
|
|
||||||
|
|
||||||
def merge_factory_bin(source, target, env):
|
def merge_factory_bin(source, target, env):
|
||||||
"""
|
"""
|
||||||
@@ -25,7 +26,9 @@ def merge_factory_bin(source, target, env):
|
|||||||
try:
|
try:
|
||||||
with flasher_args_path.open() as f:
|
with flasher_args_path.open() as f:
|
||||||
flash_data = json.load(f)
|
flash_data = json.load(f)
|
||||||
for addr, fname in sorted(flash_data["flash_files"].items(), key=lambda kv: int(kv[0], 16)):
|
for addr, fname in sorted(
|
||||||
|
flash_data["flash_files"].items(), key=lambda kv: int(kv[0], 16)
|
||||||
|
):
|
||||||
file_path = pathlib.Path(fname)
|
file_path = pathlib.Path(fname)
|
||||||
if file_path.exists():
|
if file_path.exists():
|
||||||
sections.append((addr, str(file_path)))
|
sections.append((addr, str(file_path)))
|
||||||
@@ -40,20 +43,27 @@ def merge_factory_bin(source, target, env):
|
|||||||
if flash_images:
|
if flash_images:
|
||||||
print("Using FLASH_EXTRA_IMAGES from PlatformIO environment")
|
print("Using FLASH_EXTRA_IMAGES from PlatformIO environment")
|
||||||
# flatten any nested lists
|
# flatten any nested lists
|
||||||
flat = list(itertools.chain.from_iterable(
|
flat = list(
|
||||||
x if isinstance(x, (list, tuple)) else [x] for x in flash_images
|
itertools.chain.from_iterable(
|
||||||
))
|
x if isinstance(x, (list, tuple)) else [x] for x in flash_images
|
||||||
|
)
|
||||||
|
)
|
||||||
entries = [env.subst(x) for x in flat]
|
entries = [env.subst(x) for x in flat]
|
||||||
for i in range(0, len(entries) - 1, 2):
|
for i in range(0, len(entries) - 1, 2):
|
||||||
addr, fname = entries[i], entries[i + 1]
|
addr, fname = entries[i], entries[i + 1]
|
||||||
if isinstance(fname, (list, tuple)):
|
if isinstance(fname, (list, tuple)):
|
||||||
print(f"Warning: Skipping malformed FLASH_EXTRA_IMAGES entry: {fname}")
|
print(
|
||||||
|
f"Warning: Skipping malformed FLASH_EXTRA_IMAGES entry: {fname}"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
file_path = pathlib.Path(str(fname))
|
file_path = pathlib.Path(str(fname))
|
||||||
if file_path.exists():
|
if file_path.exists():
|
||||||
sections.append((addr, str(file_path)))
|
sections.append((addr, file_path))
|
||||||
else:
|
else:
|
||||||
print(f"Info: {file_path.name} not found — skipping")
|
print(f"Info: {file_path.name} not found — skipping")
|
||||||
|
if sections:
|
||||||
|
# Append main firmware to sections
|
||||||
|
sections.append(("0x10000", firmware_path))
|
||||||
|
|
||||||
# 3. Final fallback: guess standard image locations
|
# 3. Final fallback: guess standard image locations
|
||||||
if not sections:
|
if not sections:
|
||||||
@@ -62,11 +72,11 @@ def merge_factory_bin(source, target, env):
|
|||||||
("0x0", build_dir / "bootloader" / "bootloader.bin"),
|
("0x0", build_dir / "bootloader" / "bootloader.bin"),
|
||||||
("0x8000", build_dir / "partition_table" / "partition-table.bin"),
|
("0x8000", build_dir / "partition_table" / "partition-table.bin"),
|
||||||
("0xe000", build_dir / "ota_data_initial.bin"),
|
("0xe000", build_dir / "ota_data_initial.bin"),
|
||||||
("0x10000", firmware_path)
|
("0x10000", firmware_path),
|
||||||
]
|
]
|
||||||
for addr, file_path in guesses:
|
for addr, file_path in guesses:
|
||||||
if file_path.exists():
|
if file_path.exists():
|
||||||
sections.append((addr, str(file_path)))
|
sections.append((addr, file_path))
|
||||||
else:
|
else:
|
||||||
print(f"Info: {file_path.name} not found — skipping")
|
print(f"Info: {file_path.name} not found — skipping")
|
||||||
|
|
||||||
@@ -76,21 +86,25 @@ def merge_factory_bin(source, target, env):
|
|||||||
return
|
return
|
||||||
|
|
||||||
output_path = firmware_path.with_suffix(".factory.bin")
|
output_path = firmware_path.with_suffix(".factory.bin")
|
||||||
|
python_exe = f'"{env.subst("$PYTHONEXE")}"'
|
||||||
cmd = [
|
cmd = [
|
||||||
"--chip", chip,
|
python_exe,
|
||||||
|
"-m",
|
||||||
|
"esptool",
|
||||||
|
"--chip",
|
||||||
|
chip,
|
||||||
"merge_bin",
|
"merge_bin",
|
||||||
"--flash_size", flash_size,
|
"--flash_size",
|
||||||
"--output", str(output_path)
|
flash_size,
|
||||||
|
"--output",
|
||||||
|
str(output_path),
|
||||||
]
|
]
|
||||||
for addr, file_path in sections:
|
for addr, file_path in sections:
|
||||||
cmd += [addr, file_path]
|
cmd += [addr, str(file_path)]
|
||||||
|
|
||||||
print(f"Merging binaries into {output_path}")
|
print(f"Merging binaries into {output_path}")
|
||||||
result = env.Execute(
|
result = env.Execute(
|
||||||
env.VerboseAction(
|
env.VerboseAction(" ".join(cmd), "Merging binaries with esptool")
|
||||||
f"{env.subst('$PYTHONEXE')} -m esptool " + " ".join(cmd),
|
|
||||||
"Merging binaries with esptool"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result == 0:
|
if result == 0:
|
||||||
@@ -98,6 +112,7 @@ def merge_factory_bin(source, target, env):
|
|||||||
else:
|
else:
|
||||||
print(f"Error: esptool merge_bin failed with code {result}")
|
print(f"Error: esptool merge_bin failed with code {result}")
|
||||||
|
|
||||||
|
|
||||||
def esp32_copy_ota_bin(source, target, env):
|
def esp32_copy_ota_bin(source, target, env):
|
||||||
"""
|
"""
|
||||||
Copy the main firmware to a .ota.bin file for compatibility with ESPHome OTA tools.
|
Copy the main firmware to a .ota.bin file for compatibility with ESPHome OTA tools.
|
||||||
@@ -107,6 +122,7 @@ def esp32_copy_ota_bin(source, target, env):
|
|||||||
shutil.copyfile(firmware_name, new_file_name)
|
shutil.copyfile(firmware_name, new_file_name)
|
||||||
print(f"Copied firmware to {new_file_name}")
|
print(f"Copied firmware to {new_file_name}")
|
||||||
|
|
||||||
|
|
||||||
# Run merge first, then ota copy second
|
# Run merge first, then ota copy second
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_factory_bin)
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_factory_bin) # noqa: F821
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_copy_ota_bin)
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_copy_ota_bin) # noqa: F821
|
||||||
|
@@ -5,10 +5,23 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gap_ble_api.h>
|
||||||
|
#include <esp_gatt_defs.h>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble_client {
|
namespace esp32_ble_client {
|
||||||
|
|
||||||
static const char *const TAG = "esp32_ble_client";
|
static const char *const TAG = "esp32_ble_client";
|
||||||
|
|
||||||
|
// Connection interval defaults matching ESP-IDF's BTM_BLE_CONN_INT_*_DEF
|
||||||
|
static const uint16_t DEFAULT_MIN_CONN_INTERVAL = 0x0A; // 10 * 1.25ms = 12.5ms
|
||||||
|
static const uint16_t DEFAULT_MAX_CONN_INTERVAL = 0x0C; // 12 * 1.25ms = 15ms
|
||||||
|
static const uint16_t DEFAULT_CONN_TIMEOUT = 600; // 600 * 10ms = 6s
|
||||||
|
|
||||||
|
// Fastest connection parameters for devices with short discovery timeouts
|
||||||
|
static const uint16_t FAST_MIN_CONN_INTERVAL = 0x06; // 6 * 1.25ms = 7.5ms (BLE minimum)
|
||||||
|
static const uint16_t FAST_MAX_CONN_INTERVAL = 0x06; // 6 * 1.25ms = 7.5ms
|
||||||
|
static const uint16_t FAST_CONN_TIMEOUT = 1000; // 1000 * 10ms = 10s
|
||||||
static const esp_bt_uuid_t NOTIFY_DESC_UUID = {
|
static const esp_bt_uuid_t NOTIFY_DESC_UUID = {
|
||||||
.len = ESP_UUID_LEN_16,
|
.len = ESP_UUID_LEN_16,
|
||||||
.uuid =
|
.uuid =
|
||||||
@@ -129,6 +142,7 @@ void BLEClientBase::connect() {
|
|||||||
ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),
|
ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),
|
||||||
this->remote_addr_type_);
|
this->remote_addr_type_);
|
||||||
this->paired_ = false;
|
this->paired_ = false;
|
||||||
|
|
||||||
auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true);
|
auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_open error, status=%d", this->connection_index_, this->address_str_.c_str(),
|
ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_open error, status=%d", this->connection_index_, this->address_str_.c_str(),
|
||||||
@@ -136,6 +150,22 @@ void BLEClientBase::connect() {
|
|||||||
this->set_state(espbt::ClientState::IDLE);
|
this->set_state(espbt::ClientState::IDLE);
|
||||||
} else {
|
} else {
|
||||||
this->set_state(espbt::ClientState::CONNECTING);
|
this->set_state(espbt::ClientState::CONNECTING);
|
||||||
|
|
||||||
|
// For connections without cache, set fast connection parameters after initiating connection
|
||||||
|
// This ensures service discovery completes within the 10-second timeout that
|
||||||
|
// some devices like HomeKit BLE sensors enforce
|
||||||
|
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
||||||
|
auto param_ret =
|
||||||
|
esp_ble_gap_set_prefer_conn_params(this->remote_bda_, FAST_MIN_CONN_INTERVAL, FAST_MAX_CONN_INTERVAL,
|
||||||
|
0, // latency: 0
|
||||||
|
FAST_CONN_TIMEOUT);
|
||||||
|
if (param_ret != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "[%d] [%s] esp_ble_gap_set_prefer_conn_params failed: %d", this->connection_index_,
|
||||||
|
this->address_str_.c_str(), param_ret);
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "[%d] [%s] Set fast conn params", this->connection_index_, this->address_str_.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,12 +308,14 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
this->address_str_.c_str(), ret);
|
this->address_str_.c_str(), ret);
|
||||||
}
|
}
|
||||||
this->set_state(espbt::ClientState::CONNECTED);
|
this->set_state(espbt::ClientState::CONNECTED);
|
||||||
|
ESP_LOGI(TAG, "[%d] [%s] Connection open", this->connection_index_, this->address_str_.c_str());
|
||||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
||||||
ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str());
|
ESP_LOGI(TAG, "[%d] [%s] Using cached services", this->connection_index_, this->address_str_.c_str());
|
||||||
// only set our state, subclients might have more stuff to do yet.
|
// only set our state, subclients might have more stuff to do yet.
|
||||||
this->state_ = espbt::ClientState::ESTABLISHED;
|
this->state_ = espbt::ClientState::ESTABLISHED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ESP_LOGD(TAG, "[%d] [%s] Searching for services", this->connection_index_, this->address_str_.c_str());
|
||||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
|
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -296,8 +328,15 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
case ESP_GATTC_DISCONNECT_EVT: {
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
if (!this->check_addr(param->disconnect.remote_bda))
|
if (!this->check_addr(param->disconnect.remote_bda))
|
||||||
return false;
|
return false;
|
||||||
ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_,
|
// Check if we were disconnected while waiting for service discovery
|
||||||
this->address_str_.c_str(), param->disconnect.reason);
|
if (param->disconnect.reason == ESP_GATT_CONN_TERMINATE_PEER_USER &&
|
||||||
|
this->state_ == espbt::ClientState::CONNECTED) {
|
||||||
|
ESP_LOGW(TAG, "[%d] [%s] Disconnected by remote during service discovery", this->connection_index_,
|
||||||
|
this->address_str_.c_str());
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason 0x%02x", this->connection_index_,
|
||||||
|
this->address_str_.c_str(), param->disconnect.reason);
|
||||||
|
}
|
||||||
this->release_services();
|
this->release_services();
|
||||||
this->set_state(espbt::ClientState::IDLE);
|
this->set_state(espbt::ClientState::IDLE);
|
||||||
break;
|
break;
|
||||||
@@ -353,7 +392,22 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_,
|
ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_,
|
||||||
this->address_str_.c_str(), svc->start_handle, svc->end_handle);
|
this->address_str_.c_str(), svc->start_handle, svc->end_handle);
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str());
|
ESP_LOGI(TAG, "[%d] [%s] Service discovery complete", this->connection_index_, this->address_str_.c_str());
|
||||||
|
|
||||||
|
// For non-cached connections, restore default connection parameters after service discovery
|
||||||
|
// Now that we've discovered all services, we can use more balanced parameters
|
||||||
|
// that save power and reduce interference
|
||||||
|
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
||||||
|
esp_ble_conn_update_params_t conn_params = {{0}};
|
||||||
|
memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t));
|
||||||
|
conn_params.min_int = DEFAULT_MIN_CONN_INTERVAL;
|
||||||
|
conn_params.max_int = DEFAULT_MAX_CONN_INTERVAL;
|
||||||
|
conn_params.latency = 0;
|
||||||
|
conn_params.timeout = DEFAULT_CONN_TIMEOUT;
|
||||||
|
ESP_LOGD(TAG, "[%d] [%s] Restored default conn params", this->connection_index_, this->address_str_.c_str());
|
||||||
|
esp_ble_gap_update_conn_params(&conn_params);
|
||||||
|
}
|
||||||
|
|
||||||
this->state_ = espbt::ClientState::ESTABLISHED;
|
this->state_ = espbt::ClientState::ESTABLISHED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
#ifdef USE_API
|
||||||
|
namespace api {
|
||||||
|
class APIConnection;
|
||||||
|
} // namespace api
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace fan {
|
namespace fan {
|
||||||
|
|
||||||
class FanTraits {
|
class FanTraits {
|
||||||
@@ -36,6 +43,15 @@ class FanTraits {
|
|||||||
bool supports_preset_modes() const { return !this->preset_modes_.empty(); }
|
bool supports_preset_modes() const { return !this->preset_modes_.empty(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#ifdef USE_API
|
||||||
|
// The API connection is a friend class to access internal methods
|
||||||
|
friend class api::APIConnection;
|
||||||
|
// This method returns a reference to the internal preset modes set.
|
||||||
|
// It is used by the API to avoid copying data when encoding messages.
|
||||||
|
// Warning: Do not use this method outside of the API connection code.
|
||||||
|
// It returns a reference to internal data that can be invalidated.
|
||||||
|
const std::set<std::string> &supported_preset_modes_for_api_() const { return this->preset_modes_; }
|
||||||
|
#endif
|
||||||
bool oscillation_{false};
|
bool oscillation_{false};
|
||||||
bool speed_{false};
|
bool speed_{false};
|
||||||
bool direction_{false};
|
bool direction_{false};
|
||||||
|
@@ -52,7 +52,7 @@ void GPS::update() {
|
|||||||
void GPS::loop() {
|
void GPS::loop() {
|
||||||
while (this->available() > 0 && !this->has_time_) {
|
while (this->available() > 0 && !this->has_time_) {
|
||||||
if (!this->tiny_gps_.encode(this->read())) {
|
if (!this->tiny_gps_.encode(this->read())) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (this->tiny_gps_.location.isUpdated()) {
|
if (this->tiny_gps_.location.isUpdated()) {
|
||||||
this->latitude_ = this->tiny_gps_.location.lat();
|
this->latitude_ = this->tiny_gps_.location.lat();
|
||||||
|
@@ -126,6 +126,6 @@ async def to_code(config):
|
|||||||
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
|
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
|
||||||
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
|
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
|
||||||
|
|
||||||
cg.add_library("tonia/HeatpumpIR", "1.0.35")
|
cg.add_library("tonia/HeatpumpIR", "1.0.37")
|
||||||
if CORE.is_libretiny:
|
if CORE.is_libretiny or CORE.is_esp32:
|
||||||
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")
|
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")
|
||||||
|
@@ -23,6 +23,7 @@ CONFIG_SCHEMA = (
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
|
||||||
var = await number.new_number(
|
var = await number.new_number(
|
||||||
config,
|
config,
|
||||||
min_value=0,
|
min_value=0,
|
||||||
|
@@ -93,14 +93,12 @@ void HomeassistantNumber::control(float value) {
|
|||||||
resp.data.emplace_back();
|
resp.data.emplace_back();
|
||||||
auto &entity_id = resp.data.back();
|
auto &entity_id = resp.data.back();
|
||||||
entity_id.set_key(ENTITY_ID_KEY);
|
entity_id.set_key(ENTITY_ID_KEY);
|
||||||
entity_id.set_value(StringRef(this->entity_id_));
|
entity_id.value = this->entity_id_;
|
||||||
|
|
||||||
resp.data.emplace_back();
|
resp.data.emplace_back();
|
||||||
auto &entity_value = resp.data.back();
|
auto &entity_value = resp.data.back();
|
||||||
entity_value.set_key(VALUE_KEY);
|
entity_value.set_key(VALUE_KEY);
|
||||||
// to_string() returns a temporary - must store it to avoid dangling reference
|
entity_value.value = to_string(value);
|
||||||
std::string value_str = to_string(value);
|
|
||||||
entity_value.set_value(StringRef(value_str));
|
|
||||||
|
|
||||||
api::global_api_server->send_homeassistant_service_call(resp);
|
api::global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await switch.register_switch(var, config)
|
await switch.register_switch(var, config)
|
||||||
|
@@ -54,7 +54,7 @@ void HomeassistantSwitch::write_state(bool state) {
|
|||||||
resp.data.emplace_back();
|
resp.data.emplace_back();
|
||||||
auto &entity_id_kv = resp.data.back();
|
auto &entity_id_kv = resp.data.back();
|
||||||
entity_id_kv.set_key(ENTITY_ID_KEY);
|
entity_id_kv.set_key(ENTITY_ID_KEY);
|
||||||
entity_id_kv.set_value(StringRef(this->entity_id_));
|
entity_id_kv.value = this->entity_id_;
|
||||||
|
|
||||||
api::global_api_server->send_homeassistant_service_call(resp);
|
api::global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
@@ -9,11 +9,28 @@ namespace light {
|
|||||||
|
|
||||||
static const char *const TAG = "light";
|
static const char *const TAG = "light";
|
||||||
|
|
||||||
// Helper function to reduce code size for validation warnings
|
// Helper functions to reduce code size for logging
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
|
||||||
static void log_validation_warning(const char *name, const char *param_name, float val, float min, float max) {
|
static void log_validation_warning(const char *name, const char *param_name, float val, float min, float max) {
|
||||||
ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, param_name, val, min, max);
|
ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, param_name, val, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void log_feature_not_supported(const char *name, const char *feature) {
|
||||||
|
ESP_LOGW(TAG, "'%s': %s not supported", name, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_color_mode_not_supported(const char *name, const char *feature) {
|
||||||
|
ESP_LOGW(TAG, "'%s': color mode does not support setting %s", name, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_invalid_parameter(const char *name, const char *message) { ESP_LOGW(TAG, "'%s': %s", name, message); }
|
||||||
|
#else
|
||||||
|
#define log_validation_warning(name, param_name, val, min, max)
|
||||||
|
#define log_feature_not_supported(name, feature)
|
||||||
|
#define log_color_mode_not_supported(name, feature)
|
||||||
|
#define log_invalid_parameter(name, message)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Macro to reduce repetitive setter code
|
// Macro to reduce repetitive setter code
|
||||||
#define IMPLEMENT_LIGHT_CALL_SETTER(name, type, flag) \
|
#define IMPLEMENT_LIGHT_CALL_SETTER(name, type, flag) \
|
||||||
LightCall &LightCall::set_##name(optional<type>(name)) { \
|
LightCall &LightCall::set_##name(optional<type>(name)) { \
|
||||||
@@ -49,11 +66,21 @@ static const LogString *color_mode_to_human(ColorMode color_mode) {
|
|||||||
return LOG_STR("");
|
return LOG_STR("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to log percentage values
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||||
|
static void log_percent(const char *name, const char *param, float value) {
|
||||||
|
ESP_LOGD(TAG, " %s: %.0f%%", param, value * 100.0f);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define log_percent(name, param, value)
|
||||||
|
#endif
|
||||||
|
|
||||||
void LightCall::perform() {
|
void LightCall::perform() {
|
||||||
const char *name = this->parent_->get_name().c_str();
|
const char *name = this->parent_->get_name().c_str();
|
||||||
LightColorValues v = this->validate_();
|
LightColorValues v = this->validate_();
|
||||||
|
const bool publish = this->get_publish_();
|
||||||
|
|
||||||
if (this->get_publish_()) {
|
if (publish) {
|
||||||
ESP_LOGD(TAG, "'%s' Setting:", name);
|
ESP_LOGD(TAG, "'%s' Setting:", name);
|
||||||
|
|
||||||
// Only print color mode when it's being changed
|
// Only print color mode when it's being changed
|
||||||
@@ -71,11 +98,11 @@ void LightCall::perform() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_brightness()) {
|
if (this->has_brightness()) {
|
||||||
ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f);
|
log_percent(name, "Brightness", v.get_brightness());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_color_brightness()) {
|
if (this->has_color_brightness()) {
|
||||||
ESP_LOGD(TAG, " Color brightness: %.0f%%", v.get_color_brightness() * 100.0f);
|
log_percent(name, "Color brightness", v.get_color_brightness());
|
||||||
}
|
}
|
||||||
if (this->has_red() || this->has_green() || this->has_blue()) {
|
if (this->has_red() || this->has_green() || this->has_blue()) {
|
||||||
ESP_LOGD(TAG, " Red: %.0f%%, Green: %.0f%%, Blue: %.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
|
ESP_LOGD(TAG, " Red: %.0f%%, Green: %.0f%%, Blue: %.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
|
||||||
@@ -83,7 +110,7 @@ void LightCall::perform() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_white()) {
|
if (this->has_white()) {
|
||||||
ESP_LOGD(TAG, " White: %.0f%%", v.get_white() * 100.0f);
|
log_percent(name, "White", v.get_white());
|
||||||
}
|
}
|
||||||
if (this->has_color_temperature()) {
|
if (this->has_color_temperature()) {
|
||||||
ESP_LOGD(TAG, " Color temperature: %.1f mireds", v.get_color_temperature());
|
ESP_LOGD(TAG, " Color temperature: %.1f mireds", v.get_color_temperature());
|
||||||
@@ -97,26 +124,26 @@ void LightCall::perform() {
|
|||||||
|
|
||||||
if (this->has_flash_()) {
|
if (this->has_flash_()) {
|
||||||
// FLASH
|
// FLASH
|
||||||
if (this->get_publish_()) {
|
if (publish) {
|
||||||
ESP_LOGD(TAG, " Flash length: %.1fs", this->flash_length_ / 1e3f);
|
ESP_LOGD(TAG, " Flash length: %.1fs", this->flash_length_ / 1e3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->parent_->start_flash_(v, this->flash_length_, this->get_publish_());
|
this->parent_->start_flash_(v, this->flash_length_, publish);
|
||||||
} else if (this->has_transition_()) {
|
} else if (this->has_transition_()) {
|
||||||
// TRANSITION
|
// TRANSITION
|
||||||
if (this->get_publish_()) {
|
if (publish) {
|
||||||
ESP_LOGD(TAG, " Transition length: %.1fs", this->transition_length_ / 1e3f);
|
ESP_LOGD(TAG, " Transition length: %.1fs", this->transition_length_ / 1e3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case: Transition and effect can be set when turning off
|
// Special case: Transition and effect can be set when turning off
|
||||||
if (this->has_effect_()) {
|
if (this->has_effect_()) {
|
||||||
if (this->get_publish_()) {
|
if (publish) {
|
||||||
ESP_LOGD(TAG, " Effect: 'None'");
|
ESP_LOGD(TAG, " Effect: 'None'");
|
||||||
}
|
}
|
||||||
this->parent_->stop_effect_();
|
this->parent_->stop_effect_();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->parent_->start_transition_(v, this->transition_length_, this->get_publish_());
|
this->parent_->start_transition_(v, this->transition_length_, publish);
|
||||||
|
|
||||||
} else if (this->has_effect_()) {
|
} else if (this->has_effect_()) {
|
||||||
// EFFECT
|
// EFFECT
|
||||||
@@ -127,7 +154,7 @@ void LightCall::perform() {
|
|||||||
effect_s = this->parent_->effects_[this->effect_ - 1]->get_name().c_str();
|
effect_s = this->parent_->effects_[this->effect_ - 1]->get_name().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->get_publish_()) {
|
if (publish) {
|
||||||
ESP_LOGD(TAG, " Effect: '%s'", effect_s);
|
ESP_LOGD(TAG, " Effect: '%s'", effect_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,13 +165,13 @@ void LightCall::perform() {
|
|||||||
this->parent_->set_immediately_(v, true);
|
this->parent_->set_immediately_(v, true);
|
||||||
} else {
|
} else {
|
||||||
// INSTANT CHANGE
|
// INSTANT CHANGE
|
||||||
this->parent_->set_immediately_(v, this->get_publish_());
|
this->parent_->set_immediately_(v, publish);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->has_transition_()) {
|
if (!this->has_transition_()) {
|
||||||
this->parent_->target_state_reached_callback_.call();
|
this->parent_->target_state_reached_callback_.call();
|
||||||
}
|
}
|
||||||
if (this->get_publish_()) {
|
if (publish) {
|
||||||
this->parent_->publish_state();
|
this->parent_->publish_state();
|
||||||
}
|
}
|
||||||
if (this->get_save_()) {
|
if (this->get_save_()) {
|
||||||
@@ -174,19 +201,19 @@ LightColorValues LightCall::validate_() {
|
|||||||
|
|
||||||
// Brightness exists check
|
// Brightness exists check
|
||||||
if (this->has_brightness() && this->brightness_ > 0.0f && !(color_mode & ColorCapability::BRIGHTNESS)) {
|
if (this->has_brightness() && this->brightness_ > 0.0f && !(color_mode & ColorCapability::BRIGHTNESS)) {
|
||||||
ESP_LOGW(TAG, "'%s': setting brightness not supported", name);
|
log_feature_not_supported(name, "brightness");
|
||||||
this->set_flag_(FLAG_HAS_BRIGHTNESS, false);
|
this->set_flag_(FLAG_HAS_BRIGHTNESS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transition length possible check
|
// Transition length possible check
|
||||||
if (this->has_transition_() && this->transition_length_ != 0 && !(color_mode & ColorCapability::BRIGHTNESS)) {
|
if (this->has_transition_() && this->transition_length_ != 0 && !(color_mode & ColorCapability::BRIGHTNESS)) {
|
||||||
ESP_LOGW(TAG, "'%s': transitions not supported", name);
|
log_feature_not_supported(name, "transitions");
|
||||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color brightness exists check
|
// Color brightness exists check
|
||||||
if (this->has_color_brightness() && this->color_brightness_ > 0.0f && !(color_mode & ColorCapability::RGB)) {
|
if (this->has_color_brightness() && this->color_brightness_ > 0.0f && !(color_mode & ColorCapability::RGB)) {
|
||||||
ESP_LOGW(TAG, "'%s': color mode does not support setting RGB brightness", name);
|
log_color_mode_not_supported(name, "RGB brightness");
|
||||||
this->set_flag_(FLAG_HAS_COLOR_BRIGHTNESS, false);
|
this->set_flag_(FLAG_HAS_COLOR_BRIGHTNESS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +221,7 @@ LightColorValues LightCall::validate_() {
|
|||||||
if ((this->has_red() && this->red_ > 0.0f) || (this->has_green() && this->green_ > 0.0f) ||
|
if ((this->has_red() && this->red_ > 0.0f) || (this->has_green() && this->green_ > 0.0f) ||
|
||||||
(this->has_blue() && this->blue_ > 0.0f)) {
|
(this->has_blue() && this->blue_ > 0.0f)) {
|
||||||
if (!(color_mode & ColorCapability::RGB)) {
|
if (!(color_mode & ColorCapability::RGB)) {
|
||||||
ESP_LOGW(TAG, "'%s': color mode does not support setting RGB color", name);
|
log_color_mode_not_supported(name, "RGB color");
|
||||||
this->set_flag_(FLAG_HAS_RED, false);
|
this->set_flag_(FLAG_HAS_RED, false);
|
||||||
this->set_flag_(FLAG_HAS_GREEN, false);
|
this->set_flag_(FLAG_HAS_GREEN, false);
|
||||||
this->set_flag_(FLAG_HAS_BLUE, false);
|
this->set_flag_(FLAG_HAS_BLUE, false);
|
||||||
@@ -204,21 +231,21 @@ LightColorValues LightCall::validate_() {
|
|||||||
// White value exists check
|
// White value exists check
|
||||||
if (this->has_white() && this->white_ > 0.0f &&
|
if (this->has_white() && this->white_ > 0.0f &&
|
||||||
!(color_mode & ColorCapability::WHITE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
!(color_mode & ColorCapability::WHITE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||||
ESP_LOGW(TAG, "'%s': color mode does not support setting white value", name);
|
log_color_mode_not_supported(name, "white value");
|
||||||
this->set_flag_(FLAG_HAS_WHITE, false);
|
this->set_flag_(FLAG_HAS_WHITE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color temperature exists check
|
// Color temperature exists check
|
||||||
if (this->has_color_temperature() &&
|
if (this->has_color_temperature() &&
|
||||||
!(color_mode & ColorCapability::COLOR_TEMPERATURE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
!(color_mode & ColorCapability::COLOR_TEMPERATURE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||||
ESP_LOGW(TAG, "'%s': color mode does not support setting color temperature", name);
|
log_color_mode_not_supported(name, "color temperature");
|
||||||
this->set_flag_(FLAG_HAS_COLOR_TEMPERATURE, false);
|
this->set_flag_(FLAG_HAS_COLOR_TEMPERATURE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cold/warm white value exists check
|
// Cold/warm white value exists check
|
||||||
if ((this->has_cold_white() && this->cold_white_ > 0.0f) || (this->has_warm_white() && this->warm_white_ > 0.0f)) {
|
if ((this->has_cold_white() && this->cold_white_ > 0.0f) || (this->has_warm_white() && this->warm_white_ > 0.0f)) {
|
||||||
if (!(color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
if (!(color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||||
ESP_LOGW(TAG, "'%s': color mode does not support setting cold/warm white value", name);
|
log_color_mode_not_supported(name, "cold/warm white value");
|
||||||
this->set_flag_(FLAG_HAS_COLD_WHITE, false);
|
this->set_flag_(FLAG_HAS_COLD_WHITE, false);
|
||||||
this->set_flag_(FLAG_HAS_WARM_WHITE, false);
|
this->set_flag_(FLAG_HAS_WARM_WHITE, false);
|
||||||
}
|
}
|
||||||
@@ -292,7 +319,7 @@ LightColorValues LightCall::validate_() {
|
|||||||
|
|
||||||
// Flash length check
|
// Flash length check
|
||||||
if (this->has_flash_() && this->flash_length_ == 0) {
|
if (this->has_flash_() && this->flash_length_ == 0) {
|
||||||
ESP_LOGW(TAG, "'%s': flash length must be greater than zero", name);
|
log_invalid_parameter(name, "flash length must be greater than zero");
|
||||||
this->set_flag_(FLAG_HAS_FLASH, false);
|
this->set_flag_(FLAG_HAS_FLASH, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,13 +338,13 @@ LightColorValues LightCall::validate_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) {
|
if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) {
|
||||||
ESP_LOGW(TAG, "'%s': effect cannot be used with transition/flash", name);
|
log_invalid_parameter(name, "effect cannot be used with transition/flash");
|
||||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||||
this->set_flag_(FLAG_HAS_FLASH, false);
|
this->set_flag_(FLAG_HAS_FLASH, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_flash_() && this->has_transition_()) {
|
if (this->has_flash_() && this->has_transition_()) {
|
||||||
ESP_LOGW(TAG, "'%s': flash cannot be used with transition", name);
|
log_invalid_parameter(name, "flash cannot be used with transition");
|
||||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +361,7 @@ LightColorValues LightCall::validate_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_transition_() && !supports_transition) {
|
if (this->has_transition_() && !supports_transition) {
|
||||||
ESP_LOGW(TAG, "'%s': transitions not supported", name);
|
log_feature_not_supported(name, "transitions");
|
||||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +371,7 @@ LightColorValues LightCall::validate_() {
|
|||||||
bool target_state = this->has_state() ? this->state_ : v.is_on();
|
bool target_state = this->has_state() ? this->state_ : v.is_on();
|
||||||
if (!this->has_flash_() && !target_state) {
|
if (!this->has_flash_() && !target_state) {
|
||||||
if (this->has_effect_()) {
|
if (this->has_effect_()) {
|
||||||
ESP_LOGW(TAG, "'%s': cannot start effect when turning off", name);
|
log_invalid_parameter(name, "cannot start effect when turning off");
|
||||||
this->set_flag_(FLAG_HAS_EFFECT, false);
|
this->set_flag_(FLAG_HAS_EFFECT, false);
|
||||||
} else if (this->parent_->active_effect_index_ != 0 && explicit_turn_off_request) {
|
} else if (this->parent_->active_effect_index_ != 0 && explicit_turn_off_request) {
|
||||||
// Auto turn off effect
|
// Auto turn off effect
|
||||||
@@ -368,21 +395,27 @@ void LightCall::transform_parameters_() {
|
|||||||
// - RGBWW lights with color_interlock=true, which also sets "brightness" and
|
// - RGBWW lights with color_interlock=true, which also sets "brightness" and
|
||||||
// "color_temperature" (without color_interlock, CW/WW are set directly)
|
// "color_temperature" (without color_interlock, CW/WW are set directly)
|
||||||
// - Legacy Home Assistant (pre-colormode), which sets "white" and "color_temperature"
|
// - Legacy Home Assistant (pre-colormode), which sets "white" and "color_temperature"
|
||||||
|
|
||||||
|
// Cache min/max mireds to avoid repeated calls
|
||||||
|
const float min_mireds = traits.get_min_mireds();
|
||||||
|
const float max_mireds = traits.get_max_mireds();
|
||||||
|
|
||||||
if (((this->has_white() && this->white_ > 0.0f) || this->has_color_temperature()) && //
|
if (((this->has_white() && this->white_ > 0.0f) || this->has_color_temperature()) && //
|
||||||
(this->color_mode_ & ColorCapability::COLD_WARM_WHITE) && //
|
(this->color_mode_ & ColorCapability::COLD_WARM_WHITE) && //
|
||||||
!(this->color_mode_ & ColorCapability::WHITE) && //
|
!(this->color_mode_ & ColorCapability::WHITE) && //
|
||||||
!(this->color_mode_ & ColorCapability::COLOR_TEMPERATURE) && //
|
!(this->color_mode_ & ColorCapability::COLOR_TEMPERATURE) && //
|
||||||
traits.get_min_mireds() > 0.0f && traits.get_max_mireds() > 0.0f) {
|
min_mireds > 0.0f && max_mireds > 0.0f) {
|
||||||
ESP_LOGD(TAG, "'%s': setting cold/warm white channels using white/color temperature values",
|
ESP_LOGD(TAG, "'%s': setting cold/warm white channels using white/color temperature values",
|
||||||
this->parent_->get_name().c_str());
|
this->parent_->get_name().c_str());
|
||||||
if (this->has_color_temperature()) {
|
if (this->has_color_temperature()) {
|
||||||
const float color_temp = clamp(this->color_temperature_, traits.get_min_mireds(), traits.get_max_mireds());
|
const float color_temp = clamp(this->color_temperature_, min_mireds, max_mireds);
|
||||||
const float ww_fraction =
|
const float range = max_mireds - min_mireds;
|
||||||
(color_temp - traits.get_min_mireds()) / (traits.get_max_mireds() - traits.get_min_mireds());
|
const float ww_fraction = (color_temp - min_mireds) / range;
|
||||||
const float cw_fraction = 1.0f - ww_fraction;
|
const float cw_fraction = 1.0f - ww_fraction;
|
||||||
const float max_cw_ww = std::max(ww_fraction, cw_fraction);
|
const float max_cw_ww = std::max(ww_fraction, cw_fraction);
|
||||||
this->cold_white_ = gamma_uncorrect(cw_fraction / max_cw_ww, this->parent_->get_gamma_correct());
|
const float gamma = this->parent_->get_gamma_correct();
|
||||||
this->warm_white_ = gamma_uncorrect(ww_fraction / max_cw_ww, this->parent_->get_gamma_correct());
|
this->cold_white_ = gamma_uncorrect(cw_fraction / max_cw_ww, gamma);
|
||||||
|
this->warm_white_ = gamma_uncorrect(ww_fraction / max_cw_ww, gamma);
|
||||||
this->set_flag_(FLAG_HAS_COLD_WHITE, true);
|
this->set_flag_(FLAG_HAS_COLD_WHITE, true);
|
||||||
this->set_flag_(FLAG_HAS_WARM_WHITE, true);
|
this->set_flag_(FLAG_HAS_WARM_WHITE, true);
|
||||||
}
|
}
|
||||||
|
@@ -84,18 +84,23 @@ class LightColorValues {
|
|||||||
* @return The linearly interpolated LightColorValues.
|
* @return The linearly interpolated LightColorValues.
|
||||||
*/
|
*/
|
||||||
static LightColorValues lerp(const LightColorValues &start, const LightColorValues &end, float completion) {
|
static LightColorValues lerp(const LightColorValues &start, const LightColorValues &end, float completion) {
|
||||||
|
// Directly interpolate the raw values to avoid getter/setter overhead.
|
||||||
|
// This is safe because:
|
||||||
|
// - All LightColorValues have their values clamped when set via the setters
|
||||||
|
// - std::lerp guarantees output is in the same range as inputs
|
||||||
|
// - Therefore the output doesn't need clamping, so we can skip the setters
|
||||||
LightColorValues v;
|
LightColorValues v;
|
||||||
v.set_color_mode(end.color_mode_);
|
v.color_mode_ = end.color_mode_;
|
||||||
v.set_state(std::lerp(start.get_state(), end.get_state(), completion));
|
v.state_ = std::lerp(start.state_, end.state_, completion);
|
||||||
v.set_brightness(std::lerp(start.get_brightness(), end.get_brightness(), completion));
|
v.brightness_ = std::lerp(start.brightness_, end.brightness_, completion);
|
||||||
v.set_color_brightness(std::lerp(start.get_color_brightness(), end.get_color_brightness(), completion));
|
v.color_brightness_ = std::lerp(start.color_brightness_, end.color_brightness_, completion);
|
||||||
v.set_red(std::lerp(start.get_red(), end.get_red(), completion));
|
v.red_ = std::lerp(start.red_, end.red_, completion);
|
||||||
v.set_green(std::lerp(start.get_green(), end.get_green(), completion));
|
v.green_ = std::lerp(start.green_, end.green_, completion);
|
||||||
v.set_blue(std::lerp(start.get_blue(), end.get_blue(), completion));
|
v.blue_ = std::lerp(start.blue_, end.blue_, completion);
|
||||||
v.set_white(std::lerp(start.get_white(), end.get_white(), completion));
|
v.white_ = std::lerp(start.white_, end.white_, completion);
|
||||||
v.set_color_temperature(std::lerp(start.get_color_temperature(), end.get_color_temperature(), completion));
|
v.color_temperature_ = std::lerp(start.color_temperature_, end.color_temperature_, completion);
|
||||||
v.set_cold_white(std::lerp(start.get_cold_white(), end.get_cold_white(), completion));
|
v.cold_white_ = std::lerp(start.cold_white_, end.cold_white_, completion);
|
||||||
v.set_warm_white(std::lerp(start.get_warm_white(), end.get_warm_white(), completion));
|
v.warm_white_ = std::lerp(start.warm_white_, end.warm_white_, completion);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,6 +8,32 @@ namespace light {
|
|||||||
|
|
||||||
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
|
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
|
||||||
|
|
||||||
|
// Lookup table for color mode strings
|
||||||
|
static constexpr const char *get_color_mode_json_str(ColorMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case ColorMode::ON_OFF:
|
||||||
|
return "onoff";
|
||||||
|
case ColorMode::BRIGHTNESS:
|
||||||
|
return "brightness";
|
||||||
|
case ColorMode::WHITE:
|
||||||
|
return "white"; // not supported by HA in MQTT
|
||||||
|
case ColorMode::COLOR_TEMPERATURE:
|
||||||
|
return "color_temp";
|
||||||
|
case ColorMode::COLD_WARM_WHITE:
|
||||||
|
return "cwww"; // not supported by HA
|
||||||
|
case ColorMode::RGB:
|
||||||
|
return "rgb";
|
||||||
|
case ColorMode::RGB_WHITE:
|
||||||
|
return "rgbw";
|
||||||
|
case ColorMode::RGB_COLOR_TEMPERATURE:
|
||||||
|
return "rgbct"; // not supported by HA
|
||||||
|
case ColorMode::RGB_COLD_WARM_WHITE:
|
||||||
|
return "rgbww";
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
||||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
if (state.supports_effects())
|
if (state.supports_effects())
|
||||||
@@ -16,60 +42,36 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
|||||||
auto values = state.remote_values;
|
auto values = state.remote_values;
|
||||||
auto traits = state.get_output()->get_traits();
|
auto traits = state.get_output()->get_traits();
|
||||||
|
|
||||||
switch (values.get_color_mode()) {
|
const auto color_mode = values.get_color_mode();
|
||||||
case ColorMode::UNKNOWN: // don't need to set color mode if we don't know it
|
const char *mode_str = get_color_mode_json_str(color_mode);
|
||||||
break;
|
if (mode_str != nullptr) {
|
||||||
case ColorMode::ON_OFF:
|
root["color_mode"] = mode_str;
|
||||||
root["color_mode"] = "onoff";
|
|
||||||
break;
|
|
||||||
case ColorMode::BRIGHTNESS:
|
|
||||||
root["color_mode"] = "brightness";
|
|
||||||
break;
|
|
||||||
case ColorMode::WHITE: // not supported by HA in MQTT
|
|
||||||
root["color_mode"] = "white";
|
|
||||||
break;
|
|
||||||
case ColorMode::COLOR_TEMPERATURE:
|
|
||||||
root["color_mode"] = "color_temp";
|
|
||||||
break;
|
|
||||||
case ColorMode::COLD_WARM_WHITE: // not supported by HA
|
|
||||||
root["color_mode"] = "cwww";
|
|
||||||
break;
|
|
||||||
case ColorMode::RGB:
|
|
||||||
root["color_mode"] = "rgb";
|
|
||||||
break;
|
|
||||||
case ColorMode::RGB_WHITE:
|
|
||||||
root["color_mode"] = "rgbw";
|
|
||||||
break;
|
|
||||||
case ColorMode::RGB_COLOR_TEMPERATURE: // not supported by HA
|
|
||||||
root["color_mode"] = "rgbct";
|
|
||||||
break;
|
|
||||||
case ColorMode::RGB_COLD_WARM_WHITE:
|
|
||||||
root["color_mode"] = "rgbww";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.get_color_mode() & ColorCapability::ON_OFF)
|
if (color_mode & ColorCapability::ON_OFF)
|
||||||
root["state"] = (values.get_state() != 0.0f) ? "ON" : "OFF";
|
root["state"] = (values.get_state() != 0.0f) ? "ON" : "OFF";
|
||||||
if (values.get_color_mode() & ColorCapability::BRIGHTNESS)
|
if (color_mode & ColorCapability::BRIGHTNESS)
|
||||||
root["brightness"] = uint8_t(values.get_brightness() * 255);
|
root["brightness"] = to_uint8_scale(values.get_brightness());
|
||||||
|
|
||||||
JsonObject color = root["color"].to<JsonObject>();
|
JsonObject color = root["color"].to<JsonObject>();
|
||||||
if (values.get_color_mode() & ColorCapability::RGB) {
|
if (color_mode & ColorCapability::RGB) {
|
||||||
color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255);
|
float color_brightness = values.get_color_brightness();
|
||||||
color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255);
|
color["r"] = to_uint8_scale(color_brightness * values.get_red());
|
||||||
color["b"] = uint8_t(values.get_color_brightness() * values.get_blue() * 255);
|
color["g"] = to_uint8_scale(color_brightness * values.get_green());
|
||||||
|
color["b"] = to_uint8_scale(color_brightness * values.get_blue());
|
||||||
}
|
}
|
||||||
if (values.get_color_mode() & ColorCapability::WHITE) {
|
if (color_mode & ColorCapability::WHITE) {
|
||||||
color["w"] = uint8_t(values.get_white() * 255);
|
uint8_t white_val = to_uint8_scale(values.get_white());
|
||||||
root["white_value"] = uint8_t(values.get_white() * 255); // legacy API
|
color["w"] = white_val;
|
||||||
|
root["white_value"] = white_val; // legacy API
|
||||||
}
|
}
|
||||||
if (values.get_color_mode() & ColorCapability::COLOR_TEMPERATURE) {
|
if (color_mode & ColorCapability::COLOR_TEMPERATURE) {
|
||||||
// this one isn't under the color subkey for some reason
|
// this one isn't under the color subkey for some reason
|
||||||
root["color_temp"] = uint32_t(values.get_color_temperature());
|
root["color_temp"] = uint32_t(values.get_color_temperature());
|
||||||
}
|
}
|
||||||
if (values.get_color_mode() & ColorCapability::COLD_WARM_WHITE) {
|
if (color_mode & ColorCapability::COLD_WARM_WHITE) {
|
||||||
color["c"] = uint8_t(values.get_cold_white() * 255);
|
color["c"] = to_uint8_scale(values.get_cold_white());
|
||||||
color["w"] = uint8_t(values.get_warm_white() * 255);
|
color["w"] = to_uint8_scale(values.get_warm_white());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,8 @@ void LightState::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When supported color temperature range is known, initialize color temperature setting within bounds.
|
// When supported color temperature range is known, initialize color temperature setting within bounds.
|
||||||
float min_mireds = this->get_traits().get_min_mireds();
|
auto traits = this->get_traits();
|
||||||
|
float min_mireds = traits.get_min_mireds();
|
||||||
if (min_mireds > 0) {
|
if (min_mireds > 0) {
|
||||||
this->remote_values.set_color_temperature(min_mireds);
|
this->remote_values.set_color_temperature(min_mireds);
|
||||||
this->current_values.set_color_temperature(min_mireds);
|
this->current_values.set_color_temperature(min_mireds);
|
||||||
@@ -43,11 +44,8 @@ void LightState::setup() {
|
|||||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
|
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
|
||||||
// Attempt to load from preferences, else fall back to default values
|
// Attempt to load from preferences, else fall back to default values
|
||||||
if (!this->rtc_.load(&recovered)) {
|
if (!this->rtc_.load(&recovered)) {
|
||||||
recovered.state = false;
|
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
|
||||||
if (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
|
this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_ON);
|
||||||
this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_ON) {
|
|
||||||
recovered.state = true;
|
|
||||||
}
|
|
||||||
} else if (this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_OFF ||
|
} else if (this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_OFF ||
|
||||||
this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_ON) {
|
this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_ON) {
|
||||||
// Inverted restore state
|
// Inverted restore state
|
||||||
@@ -88,17 +86,18 @@ void LightState::setup() {
|
|||||||
}
|
}
|
||||||
void LightState::dump_config() {
|
void LightState::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
|
ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
|
||||||
if (this->get_traits().supports_color_capability(ColorCapability::BRIGHTNESS)) {
|
auto traits = this->get_traits();
|
||||||
|
if (traits.supports_color_capability(ColorCapability::BRIGHTNESS)) {
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
" Default Transition Length: %.1fs\n"
|
" Default Transition Length: %.1fs\n"
|
||||||
" Gamma Correct: %.2f",
|
" Gamma Correct: %.2f",
|
||||||
this->default_transition_length_ / 1e3f, this->gamma_correct_);
|
this->default_transition_length_ / 1e3f, this->gamma_correct_);
|
||||||
}
|
}
|
||||||
if (this->get_traits().supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) {
|
if (traits.supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) {
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
" Min Mireds: %.1f\n"
|
" Min Mireds: %.1f\n"
|
||||||
" Max Mireds: %.1f",
|
" Max Mireds: %.1f",
|
||||||
this->get_traits().get_min_mireds(), this->get_traits().get_max_mireds());
|
traits.get_min_mireds(), traits.get_max_mireds());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void LightState::loop() {
|
void LightState::loop() {
|
||||||
|
@@ -5,6 +5,13 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
#ifdef USE_API
|
||||||
|
namespace api {
|
||||||
|
class APIConnection;
|
||||||
|
} // namespace api
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace light {
|
namespace light {
|
||||||
|
|
||||||
/// This class is used to represent the capabilities of a light.
|
/// This class is used to represent the capabilities of a light.
|
||||||
@@ -52,6 +59,16 @@ class LightTraits {
|
|||||||
void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; }
|
void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#ifdef USE_API
|
||||||
|
// The API connection is a friend class to access internal methods
|
||||||
|
friend class api::APIConnection;
|
||||||
|
// This method returns a reference to the internal color modes set.
|
||||||
|
// It is used by the API to avoid copying data when encoding messages.
|
||||||
|
// Warning: Do not use this method outside of the API connection code.
|
||||||
|
// It returns a reference to internal data that can be invalidated.
|
||||||
|
const std::set<ColorMode> &get_supported_color_modes_for_api_() const { return this->supported_color_modes_; }
|
||||||
|
#endif
|
||||||
|
|
||||||
std::set<ColorMode> supported_color_modes_{};
|
std::set<ColorMode> supported_color_modes_{};
|
||||||
float min_mireds_{0};
|
float min_mireds_{0};
|
||||||
float max_mireds_{0};
|
float max_mireds_{0};
|
||||||
|
@@ -15,7 +15,7 @@ from ..defines import (
|
|||||||
TILE_DIRECTIONS,
|
TILE_DIRECTIONS,
|
||||||
literal,
|
literal,
|
||||||
)
|
)
|
||||||
from ..lv_validation import animated, lv_int
|
from ..lv_validation import animated, lv_int, lv_pct
|
||||||
from ..lvcode import lv, lv_assign, lv_expr, lv_obj, lv_Pvariable
|
from ..lvcode import lv, lv_assign, lv_expr, lv_obj, lv_Pvariable
|
||||||
from ..schemas import container_schema
|
from ..schemas import container_schema
|
||||||
from ..types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr
|
from ..types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr
|
||||||
@@ -41,8 +41,8 @@ TILEVIEW_SCHEMA = cv.Schema(
|
|||||||
container_schema(
|
container_schema(
|
||||||
obj_spec,
|
obj_spec,
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ROW): lv_int,
|
cv.Required(CONF_ROW): cv.positive_int,
|
||||||
cv.Required(CONF_COLUMN): lv_int,
|
cv.Required(CONF_COLUMN): cv.positive_int,
|
||||||
cv.GenerateID(): cv.declare_id(lv_tile_t),
|
cv.GenerateID(): cv.declare_id(lv_tile_t),
|
||||||
cv.Optional(CONF_DIR, default="ALL"): TILE_DIRECTIONS.several_of,
|
cv.Optional(CONF_DIR, default="ALL"): TILE_DIRECTIONS.several_of,
|
||||||
},
|
},
|
||||||
@@ -63,21 +63,29 @@ class TileviewType(WidgetType):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def to_code(self, w: Widget, config: dict):
|
async def to_code(self, w: Widget, config: dict):
|
||||||
for tile_conf in config.get(CONF_TILES, ()):
|
tiles = config[CONF_TILES]
|
||||||
|
for tile_conf in tiles:
|
||||||
w_id = tile_conf[CONF_ID]
|
w_id = tile_conf[CONF_ID]
|
||||||
tile_obj = lv_Pvariable(lv_obj_t, w_id)
|
tile_obj = lv_Pvariable(lv_obj_t, w_id)
|
||||||
tile = Widget.create(w_id, tile_obj, tile_spec, tile_conf)
|
tile = Widget.create(w_id, tile_obj, tile_spec, tile_conf)
|
||||||
dirs = tile_conf[CONF_DIR]
|
dirs = tile_conf[CONF_DIR]
|
||||||
if isinstance(dirs, list):
|
if isinstance(dirs, list):
|
||||||
dirs = "|".join(dirs)
|
dirs = "|".join(dirs)
|
||||||
|
row_pos = tile_conf[CONF_ROW]
|
||||||
|
col_pos = tile_conf[CONF_COLUMN]
|
||||||
lv_assign(
|
lv_assign(
|
||||||
tile_obj,
|
tile_obj,
|
||||||
lv_expr.tileview_add_tile(
|
lv_expr.tileview_add_tile(w.obj, col_pos, row_pos, literal(dirs)),
|
||||||
w.obj, tile_conf[CONF_COLUMN], tile_conf[CONF_ROW], literal(dirs)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
# Bugfix for LVGL 8.x
|
||||||
|
lv_obj.set_pos(tile_obj, lv_pct(col_pos * 100), lv_pct(row_pos * 100))
|
||||||
await set_obj_properties(tile, tile_conf)
|
await set_obj_properties(tile, tile_conf)
|
||||||
await add_widgets(tile, tile_conf)
|
await add_widgets(tile, tile_conf)
|
||||||
|
if tiles:
|
||||||
|
# Set the first tile as active
|
||||||
|
lv_obj.set_tile_id(
|
||||||
|
w.obj, tiles[0][CONF_COLUMN], tiles[0][CONF_ROW], literal("LV_ANIM_OFF")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
tileview_spec = TileviewType()
|
tileview_spec = TileviewType()
|
||||||
|
@@ -6,12 +6,38 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace media_player {
|
namespace media_player {
|
||||||
|
|
||||||
|
enum MediaPlayerEntityFeature : uint32_t {
|
||||||
|
PAUSE = 1 << 0,
|
||||||
|
SEEK = 1 << 1,
|
||||||
|
VOLUME_SET = 1 << 2,
|
||||||
|
VOLUME_MUTE = 1 << 3,
|
||||||
|
PREVIOUS_TRACK = 1 << 4,
|
||||||
|
NEXT_TRACK = 1 << 5,
|
||||||
|
|
||||||
|
TURN_ON = 1 << 7,
|
||||||
|
TURN_OFF = 1 << 8,
|
||||||
|
PLAY_MEDIA = 1 << 9,
|
||||||
|
VOLUME_STEP = 1 << 10,
|
||||||
|
SELECT_SOURCE = 1 << 11,
|
||||||
|
STOP = 1 << 12,
|
||||||
|
CLEAR_PLAYLIST = 1 << 13,
|
||||||
|
PLAY = 1 << 14,
|
||||||
|
SHUFFLE_SET = 1 << 15,
|
||||||
|
SELECT_SOUND_MODE = 1 << 16,
|
||||||
|
BROWSE_MEDIA = 1 << 17,
|
||||||
|
REPEAT_SET = 1 << 18,
|
||||||
|
GROUPING = 1 << 19,
|
||||||
|
MEDIA_ANNOUNCE = 1 << 20,
|
||||||
|
MEDIA_ENQUEUE = 1 << 21,
|
||||||
|
SEARCH_MEDIA = 1 << 22,
|
||||||
|
};
|
||||||
|
|
||||||
enum MediaPlayerState : uint8_t {
|
enum MediaPlayerState : uint8_t {
|
||||||
MEDIA_PLAYER_STATE_NONE = 0,
|
MEDIA_PLAYER_STATE_NONE = 0,
|
||||||
MEDIA_PLAYER_STATE_IDLE = 1,
|
MEDIA_PLAYER_STATE_IDLE = 1,
|
||||||
MEDIA_PLAYER_STATE_PLAYING = 2,
|
MEDIA_PLAYER_STATE_PLAYING = 2,
|
||||||
MEDIA_PLAYER_STATE_PAUSED = 3,
|
MEDIA_PLAYER_STATE_PAUSED = 3,
|
||||||
MEDIA_PLAYER_STATE_ANNOUNCING = 4
|
MEDIA_PLAYER_STATE_ANNOUNCING = 4,
|
||||||
};
|
};
|
||||||
const char *media_player_state_to_string(MediaPlayerState state);
|
const char *media_player_state_to_string(MediaPlayerState state);
|
||||||
|
|
||||||
@@ -56,6 +82,17 @@ class MediaPlayerTraits {
|
|||||||
|
|
||||||
std::vector<MediaPlayerSupportedFormat> &get_supported_formats() { return this->supported_formats_; }
|
std::vector<MediaPlayerSupportedFormat> &get_supported_formats() { return this->supported_formats_; }
|
||||||
|
|
||||||
|
uint32_t get_feature_flags() const {
|
||||||
|
uint32_t flags = 0;
|
||||||
|
flags |= MediaPlayerEntityFeature::PLAY_MEDIA | MediaPlayerEntityFeature::BROWSE_MEDIA |
|
||||||
|
MediaPlayerEntityFeature::STOP | MediaPlayerEntityFeature::VOLUME_SET |
|
||||||
|
MediaPlayerEntityFeature::VOLUME_MUTE | MediaPlayerEntityFeature::MEDIA_ANNOUNCE;
|
||||||
|
if (this->get_supports_pause()) {
|
||||||
|
flags |= MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY;
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool supports_pause_{false};
|
bool supports_pause_{false};
|
||||||
std::vector<MediaPlayerSupportedFormat> supported_formats_{};
|
std::vector<MediaPlayerSupportedFormat> supported_formats_{};
|
||||||
|
@@ -57,7 +57,8 @@ from esphome.final_validate import full_config
|
|||||||
|
|
||||||
from . import mipi_dsi_ns, models
|
from . import mipi_dsi_ns, models
|
||||||
|
|
||||||
DEPENDENCIES = ["esp32"]
|
# Currently only ESP32-P4 is supported, so esp_ldo and psram are required
|
||||||
|
DEPENDENCIES = ["esp32", "esp_ldo", "psram"]
|
||||||
DOMAIN = "mipi_dsi"
|
DOMAIN = "mipi_dsi"
|
||||||
|
|
||||||
LOGGER = logging.getLogger(DOMAIN)
|
LOGGER = logging.getLogger(DOMAIN)
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#ifdef USE_OPENTHREAD
|
#ifdef USE_OPENTHREAD
|
||||||
#include "openthread.h"
|
#include "openthread.h"
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||||
|
#include "esp_openthread.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <freertos/portmacro.h>
|
#include <freertos/portmacro.h>
|
||||||
|
|
||||||
@@ -28,18 +31,6 @@ OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines-
|
|||||||
|
|
||||||
OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; }
|
OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; }
|
||||||
|
|
||||||
OpenThreadComponent::~OpenThreadComponent() {
|
|
||||||
auto lock = InstanceLock::try_acquire(100);
|
|
||||||
if (!lock) {
|
|
||||||
ESP_LOGW(TAG, "Failed to acquire OpenThread lock in destructor, leaking memory");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
otInstance *instance = lock->get_instance();
|
|
||||||
otSrpClientClearHostAndServices(instance);
|
|
||||||
otSrpClientBuffersFreeAllServices(instance);
|
|
||||||
global_openthread_component = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenThreadComponent::is_connected() {
|
bool OpenThreadComponent::is_connected() {
|
||||||
auto lock = InstanceLock::try_acquire(100);
|
auto lock = InstanceLock::try_acquire(100);
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
@@ -199,6 +190,33 @@ void *OpenThreadSrpComponent::pool_alloc_(size_t size) {
|
|||||||
|
|
||||||
void OpenThreadSrpComponent::set_mdns(esphome::mdns::MDNSComponent *mdns) { this->mdns_ = mdns; }
|
void OpenThreadSrpComponent::set_mdns(esphome::mdns::MDNSComponent *mdns) { this->mdns_ = mdns; }
|
||||||
|
|
||||||
|
bool OpenThreadComponent::teardown() {
|
||||||
|
if (!this->teardown_started_) {
|
||||||
|
this->teardown_started_ = true;
|
||||||
|
ESP_LOGD(TAG, "Clear Srp");
|
||||||
|
auto lock = InstanceLock::try_acquire(100);
|
||||||
|
if (!lock) {
|
||||||
|
ESP_LOGW(TAG, "Failed to acquire OpenThread lock during teardown, leaking memory");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
otInstance *instance = lock->get_instance();
|
||||||
|
otSrpClientClearHostAndServices(instance);
|
||||||
|
otSrpClientBuffersFreeAllServices(instance);
|
||||||
|
global_openthread_component = nullptr;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||||
|
ESP_LOGD(TAG, "Exit main loop ");
|
||||||
|
int error = esp_openthread_mainloop_exit();
|
||||||
|
if (error != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Failed attempt to stop main loop %d", error);
|
||||||
|
this->teardown_complete_ = true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
this->teardown_complete_ = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return this->teardown_complete_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace openthread
|
} // namespace openthread
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ class OpenThreadComponent : public Component {
|
|||||||
OpenThreadComponent();
|
OpenThreadComponent();
|
||||||
~OpenThreadComponent();
|
~OpenThreadComponent();
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
bool teardown() override;
|
||||||
float get_setup_priority() const override { return setup_priority::WIFI; }
|
float get_setup_priority() const override { return setup_priority::WIFI; }
|
||||||
|
|
||||||
bool is_connected();
|
bool is_connected();
|
||||||
@@ -30,6 +31,8 @@ class OpenThreadComponent : public Component {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
|
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
|
||||||
|
bool teardown_started_{false};
|
||||||
|
bool teardown_complete_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
@@ -143,10 +143,13 @@ void OpenThreadComponent::ot_main() {
|
|||||||
esp_openthread_launch_mainloop();
|
esp_openthread_launch_mainloop();
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
|
esp_openthread_deinit();
|
||||||
esp_openthread_netif_glue_deinit();
|
esp_openthread_netif_glue_deinit();
|
||||||
esp_netif_destroy(openthread_netif);
|
esp_netif_destroy(openthread_netif);
|
||||||
|
|
||||||
esp_vfs_eventfd_unregister();
|
esp_vfs_eventfd_unregister();
|
||||||
|
this->teardown_complete_ = true;
|
||||||
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
network::IPAddresses OpenThreadComponent::get_ip_addresses() {
|
network::IPAddresses OpenThreadComponent::get_ip_addresses() {
|
||||||
|
@@ -43,6 +43,8 @@ FloatOutputPtr = FloatOutput.operator("ptr")
|
|||||||
TurnOffAction = output_ns.class_("TurnOffAction", automation.Action)
|
TurnOffAction = output_ns.class_("TurnOffAction", automation.Action)
|
||||||
TurnOnAction = output_ns.class_("TurnOnAction", automation.Action)
|
TurnOnAction = output_ns.class_("TurnOnAction", automation.Action)
|
||||||
SetLevelAction = output_ns.class_("SetLevelAction", automation.Action)
|
SetLevelAction = output_ns.class_("SetLevelAction", automation.Action)
|
||||||
|
SetMinPowerAction = output_ns.class_("SetMinPowerAction", automation.Action)
|
||||||
|
SetMaxPowerAction = output_ns.class_("SetMaxPowerAction", automation.Action)
|
||||||
|
|
||||||
|
|
||||||
async def setup_output_platform_(obj, config):
|
async def setup_output_platform_(obj, config):
|
||||||
@@ -104,6 +106,42 @@ async def output_set_level_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"output.set_min_power",
|
||||||
|
SetMinPowerAction,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(FloatOutput),
|
||||||
|
cv.Required(CONF_MIN_POWER): cv.templatable(cv.percentage),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def output_set_min_power_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
template_ = await cg.templatable(config[CONF_MIN_POWER], args, float)
|
||||||
|
cg.add(var.set_min_power(template_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"output.set_max_power",
|
||||||
|
SetMaxPowerAction,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(FloatOutput),
|
||||||
|
cv.Required(CONF_MAX_POWER): cv.templatable(cv.percentage),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def output_set_max_power_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
template_ = await cg.templatable(config[CONF_MAX_POWER], args, float)
|
||||||
|
cg.add(var.set_max_power(template_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_define("USE_OUTPUT")
|
cg.add_define("USE_OUTPUT")
|
||||||
cg.add_global(output_ns.using)
|
cg.add_global(output_ns.using)
|
||||||
|
@@ -40,5 +40,29 @@ template<typename... Ts> class SetLevelAction : public Action<Ts...> {
|
|||||||
FloatOutput *output_;
|
FloatOutput *output_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class SetMinPowerAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
SetMinPowerAction(FloatOutput *output) : output_(output) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(float, min_power)
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->output_->set_min_power(this->min_power_.value(x...)); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FloatOutput *output_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class SetMaxPowerAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
SetMaxPowerAction(FloatOutput *output) : output_(output) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(float, max_power)
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->output_->set_max_power(this->max_power_.value(x...)); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FloatOutput *output_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace output
|
} // namespace output
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -5,7 +5,7 @@ namespace select {
|
|||||||
|
|
||||||
void SelectTraits::set_options(std::vector<std::string> options) { this->options_ = std::move(options); }
|
void SelectTraits::set_options(std::vector<std::string> options) { this->options_ = std::move(options); }
|
||||||
|
|
||||||
std::vector<std::string> SelectTraits::get_options() const { return this->options_; }
|
const std::vector<std::string> &SelectTraits::get_options() const { return this->options_; }
|
||||||
|
|
||||||
} // namespace select
|
} // namespace select
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -9,7 +9,7 @@ namespace select {
|
|||||||
class SelectTraits {
|
class SelectTraits {
|
||||||
public:
|
public:
|
||||||
void set_options(std::vector<std::string> options);
|
void set_options(std::vector<std::string> options);
|
||||||
std::vector<std::string> get_options() const;
|
const std::vector<std::string> &get_options() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::string> options_;
|
std::vector<std::string> options_;
|
||||||
|
@@ -256,6 +256,7 @@ OffsetFilter = sensor_ns.class_("OffsetFilter", Filter)
|
|||||||
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
|
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
|
||||||
FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter)
|
FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter)
|
||||||
ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter)
|
ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter)
|
||||||
|
ThrottleWithPriorityFilter = sensor_ns.class_("ThrottleWithPriorityFilter", Filter)
|
||||||
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
|
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
|
||||||
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component)
|
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component)
|
||||||
HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
|
HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
|
||||||
@@ -332,6 +333,7 @@ def sensor_schema(
|
|||||||
device_class: str = cv.UNDEFINED,
|
device_class: str = cv.UNDEFINED,
|
||||||
state_class: str = cv.UNDEFINED,
|
state_class: str = cv.UNDEFINED,
|
||||||
entity_category: str = cv.UNDEFINED,
|
entity_category: str = cv.UNDEFINED,
|
||||||
|
filters: list = cv.UNDEFINED,
|
||||||
) -> cv.Schema:
|
) -> cv.Schema:
|
||||||
schema = {}
|
schema = {}
|
||||||
|
|
||||||
@@ -346,6 +348,7 @@ def sensor_schema(
|
|||||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||||
(CONF_STATE_CLASS, state_class, validate_state_class),
|
(CONF_STATE_CLASS, state_class, validate_state_class),
|
||||||
(CONF_ENTITY_CATEGORY, entity_category, sensor_entity_category),
|
(CONF_ENTITY_CATEGORY, entity_category, sensor_entity_category),
|
||||||
|
(CONF_FILTERS, filters, validate_filters),
|
||||||
]:
|
]:
|
||||||
if default is not cv.UNDEFINED:
|
if default is not cv.UNDEFINED:
|
||||||
schema[cv.Optional(key, default=default)] = validator
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
@@ -593,6 +596,25 @@ async def throttle_filter_to_code(config, filter_id):
|
|||||||
return cg.new_Pvariable(filter_id, config)
|
return cg.new_Pvariable(filter_id, config)
|
||||||
|
|
||||||
|
|
||||||
|
TIMEOUT_WITH_PRIORITY_SCHEMA = cv.maybe_simple_value(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_TIMEOUT): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(CONF_VALUE, default="nan"): cv.ensure_list(cv.float_),
|
||||||
|
},
|
||||||
|
key=CONF_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@FILTER_REGISTRY.register(
|
||||||
|
"throttle_with_priority",
|
||||||
|
ThrottleWithPriorityFilter,
|
||||||
|
TIMEOUT_WITH_PRIORITY_SCHEMA,
|
||||||
|
)
|
||||||
|
async def throttle_with_priority_filter_to_code(config, filter_id):
|
||||||
|
template_ = [await cg.templatable(x, [], float) for x in config[CONF_VALUE]]
|
||||||
|
return cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_)
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register(
|
@FILTER_REGISTRY.register(
|
||||||
"heartbeat", HeartbeatFilter, cv.positive_time_period_milliseconds
|
"heartbeat", HeartbeatFilter, cv.positive_time_period_milliseconds
|
||||||
)
|
)
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "sensor.h"
|
#include "sensor.h"
|
||||||
@@ -332,6 +333,40 @@ optional<float> ThrottleFilter::new_value(float value) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThrottleWithPriorityFilter
|
||||||
|
ThrottleWithPriorityFilter::ThrottleWithPriorityFilter(uint32_t min_time_between_inputs,
|
||||||
|
std::vector<TemplatableValue<float>> prioritized_values)
|
||||||
|
: min_time_between_inputs_(min_time_between_inputs), prioritized_values_(std::move(prioritized_values)) {}
|
||||||
|
|
||||||
|
optional<float> ThrottleWithPriorityFilter::new_value(float value) {
|
||||||
|
bool is_prioritized_value = false;
|
||||||
|
int8_t accuracy = this->parent_->get_accuracy_decimals();
|
||||||
|
float accuracy_mult = powf(10.0f, accuracy);
|
||||||
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
|
// First, determine if the new value is one of the prioritized values
|
||||||
|
for (auto prioritized_value : this->prioritized_values_) {
|
||||||
|
if (std::isnan(prioritized_value.value())) {
|
||||||
|
if (std::isnan(value)) {
|
||||||
|
is_prioritized_value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float rounded_prioritized_value = roundf(accuracy_mult * prioritized_value.value());
|
||||||
|
float rounded_value = roundf(accuracy_mult * value);
|
||||||
|
if (rounded_prioritized_value == rounded_value) {
|
||||||
|
is_prioritized_value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finally, determine if the new value should be throttled and pass it through if not
|
||||||
|
if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ || is_prioritized_value) {
|
||||||
|
this->last_input_ = now;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// DeltaFilter
|
// DeltaFilter
|
||||||
DeltaFilter::DeltaFilter(float delta, bool percentage_mode)
|
DeltaFilter::DeltaFilter(float delta, bool percentage_mode)
|
||||||
: delta_(delta), current_delta_(delta), percentage_mode_(percentage_mode), last_value_(NAN) {}
|
: delta_(delta), current_delta_(delta), percentage_mode_(percentage_mode), last_value_(NAN) {}
|
||||||
|
@@ -314,6 +314,20 @@ class ThrottleFilter : public Filter {
|
|||||||
uint32_t min_time_between_inputs_;
|
uint32_t min_time_between_inputs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Same as 'throttle' but will immediately publish values contained in `value_to_prioritize`.
|
||||||
|
class ThrottleWithPriorityFilter : public Filter {
|
||||||
|
public:
|
||||||
|
explicit ThrottleWithPriorityFilter(uint32_t min_time_between_inputs,
|
||||||
|
std::vector<TemplatableValue<float>> prioritized_values);
|
||||||
|
|
||||||
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t last_input_{0};
|
||||||
|
uint32_t min_time_between_inputs_;
|
||||||
|
std::vector<TemplatableValue<float>> prioritized_values_;
|
||||||
|
};
|
||||||
|
|
||||||
class TimeoutFilter : public Filter, public Component {
|
class TimeoutFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit TimeoutFilter(uint32_t time_period, TemplatableValue<float> new_value);
|
explicit TimeoutFilter(uint32_t time_period, TemplatableValue<float> new_value);
|
||||||
|
@@ -162,6 +162,7 @@ def text_sensor_schema(
|
|||||||
device_class: str = cv.UNDEFINED,
|
device_class: str = cv.UNDEFINED,
|
||||||
entity_category: str = cv.UNDEFINED,
|
entity_category: str = cv.UNDEFINED,
|
||||||
icon: str = cv.UNDEFINED,
|
icon: str = cv.UNDEFINED,
|
||||||
|
filters: list = cv.UNDEFINED,
|
||||||
) -> cv.Schema:
|
) -> cv.Schema:
|
||||||
schema = {}
|
schema = {}
|
||||||
|
|
||||||
@@ -172,6 +173,7 @@ def text_sensor_schema(
|
|||||||
(CONF_ICON, icon, cv.icon),
|
(CONF_ICON, icon, cv.icon),
|
||||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
|
(CONF_FILTERS, filters, validate_filters),
|
||||||
]:
|
]:
|
||||||
if default is not cv.UNDEFINED:
|
if default is not cv.UNDEFINED:
|
||||||
schema[cv.Optional(key, default=default)] = validator
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
|
@@ -6,6 +6,7 @@ from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_
|
|||||||
from esphome.components.network import IPAddress
|
from esphome.components.network import IPAddress
|
||||||
from esphome.config_helpers import filter_source_files_from_platform
|
from esphome.config_helpers import filter_source_files_from_platform
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome.config_validation import only_with_esp_idf
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_AP,
|
CONF_AP,
|
||||||
CONF_BSSID,
|
CONF_BSSID,
|
||||||
@@ -336,7 +337,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
single=True
|
single=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_USE_PSRAM): cv.All(
|
cv.Optional(CONF_USE_PSRAM): cv.All(
|
||||||
cv.requires_component("psram"), cv.boolean
|
only_with_esp_idf, cv.requires_component("psram"), cv.boolean
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@@ -533,9 +533,17 @@ void WiFiComponent::check_scanning_finished() {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (a.get_matches() && b.get_matches()) {
|
if (a.get_matches() && b.get_matches()) {
|
||||||
// if both match, check priority
|
// For APs with the same SSID, always prefer stronger signal
|
||||||
|
// This helps with mesh networks and multiple APs
|
||||||
|
if (a.get_ssid() == b.get_ssid()) {
|
||||||
|
return a.get_rssi() > b.get_rssi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For different SSIDs, check priority first
|
||||||
if (a.get_priority() != b.get_priority())
|
if (a.get_priority() != b.get_priority())
|
||||||
return a.get_priority() > b.get_priority();
|
return a.get_priority() > b.get_priority();
|
||||||
|
// If priorities are equal, prefer stronger signal
|
||||||
|
return a.get_rssi() > b.get_rssi();
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.get_rssi() > b.get_rssi();
|
return a.get_rssi() > b.get_rssi();
|
||||||
|
@@ -474,8 +474,20 @@ const char *get_disconnect_reason_str(uint8_t reason) {
|
|||||||
return "Handshake Failed";
|
return "Handshake Failed";
|
||||||
case WIFI_REASON_CONNECTION_FAIL:
|
case WIFI_REASON_CONNECTION_FAIL:
|
||||||
return "Connection Failed";
|
return "Connection Failed";
|
||||||
|
case WIFI_REASON_AP_TSF_RESET:
|
||||||
|
return "AP TSF reset";
|
||||||
case WIFI_REASON_ROAMING:
|
case WIFI_REASON_ROAMING:
|
||||||
return "Station Roaming";
|
return "Station Roaming";
|
||||||
|
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG:
|
||||||
|
return "Association comeback time too long";
|
||||||
|
case WIFI_REASON_SA_QUERY_TIMEOUT:
|
||||||
|
return "SA query timeout";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY:
|
||||||
|
return "No AP found with compatible security";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD:
|
||||||
|
return "No AP found in auth mode threshold";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD:
|
||||||
|
return "No AP found in RSSI threshold";
|
||||||
case WIFI_REASON_UNSPECIFIED:
|
case WIFI_REASON_UNSPECIFIED:
|
||||||
default:
|
default:
|
||||||
return "Unspecified";
|
return "Unspecified";
|
||||||
|
@@ -640,8 +640,20 @@ const char *get_disconnect_reason_str(uint8_t reason) {
|
|||||||
return "Handshake Failed";
|
return "Handshake Failed";
|
||||||
case WIFI_REASON_CONNECTION_FAIL:
|
case WIFI_REASON_CONNECTION_FAIL:
|
||||||
return "Connection Failed";
|
return "Connection Failed";
|
||||||
|
case WIFI_REASON_AP_TSF_RESET:
|
||||||
|
return "AP TSF reset";
|
||||||
case WIFI_REASON_ROAMING:
|
case WIFI_REASON_ROAMING:
|
||||||
return "Station Roaming";
|
return "Station Roaming";
|
||||||
|
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG:
|
||||||
|
return "Association comeback time too long";
|
||||||
|
case WIFI_REASON_SA_QUERY_TIMEOUT:
|
||||||
|
return "SA query timeout";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY:
|
||||||
|
return "No AP found with compatible security";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD:
|
||||||
|
return "No AP found in auth mode threshold";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND_IN_RSSI_THRESHOLD:
|
||||||
|
return "No AP found in RSSI threshold";
|
||||||
case WIFI_REASON_UNSPECIFIED:
|
case WIFI_REASON_UNSPECIFIED:
|
||||||
default:
|
default:
|
||||||
return "Unspecified";
|
return "Unspecified";
|
||||||
|
@@ -291,6 +291,8 @@ class Version:
|
|||||||
extra: str = ""
|
extra: str = ""
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
if self.extra:
|
||||||
|
return f"{self.major}.{self.minor}.{self.patch}-{self.extra}"
|
||||||
return f"{self.major}.{self.minor}.{self.patch}"
|
return f"{self.major}.{self.minor}.{self.patch}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
static const char *const TAG = "component";
|
static const char *const TAG = "component";
|
||||||
|
static const char *const UNSPECIFIED_MESSAGE = "unspecified";
|
||||||
|
|
||||||
// Global vectors for component data that doesn't belong in every instance.
|
// Global vectors for component data that doesn't belong in every instance.
|
||||||
// Using vector instead of unordered_map for both because:
|
// Using vector instead of unordered_map for both because:
|
||||||
@@ -132,7 +133,7 @@ void Component::call_dump_config() {
|
|||||||
this->dump_config();
|
this->dump_config();
|
||||||
if (this->is_failed()) {
|
if (this->is_failed()) {
|
||||||
// Look up error message from global vector
|
// Look up error message from global vector
|
||||||
const char *error_msg = "unspecified";
|
const char *error_msg = nullptr;
|
||||||
if (component_error_messages) {
|
if (component_error_messages) {
|
||||||
for (const auto &pair : *component_error_messages) {
|
for (const auto &pair : *component_error_messages) {
|
||||||
if (pair.first == this) {
|
if (pair.first == this) {
|
||||||
@@ -141,7 +142,8 @@ void Component::call_dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(), error_msg);
|
ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(),
|
||||||
|
error_msg ? error_msg : UNSPECIFIED_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +161,7 @@ void Component::call() {
|
|||||||
this->call_setup();
|
this->call_setup();
|
||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||||
uint32_t setup_time = millis() - start_time;
|
uint32_t setup_time = millis() - start_time;
|
||||||
ESP_LOGD(TAG, "Setup %s took %ums", this->get_component_source(), (unsigned) setup_time);
|
ESP_LOGCONFIG(TAG, "Setup %s took %ums", this->get_component_source(), (unsigned) setup_time);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -284,15 +286,15 @@ void Component::status_set_warning(const char *message) {
|
|||||||
return;
|
return;
|
||||||
this->component_state_ |= STATUS_LED_WARNING;
|
this->component_state_ |= STATUS_LED_WARNING;
|
||||||
App.app_state_ |= STATUS_LED_WARNING;
|
App.app_state_ |= STATUS_LED_WARNING;
|
||||||
ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message);
|
ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message ? message : UNSPECIFIED_MESSAGE);
|
||||||
}
|
}
|
||||||
void Component::status_set_error(const char *message) {
|
void Component::status_set_error(const char *message) {
|
||||||
if ((this->component_state_ & STATUS_LED_ERROR) != 0)
|
if ((this->component_state_ & STATUS_LED_ERROR) != 0)
|
||||||
return;
|
return;
|
||||||
this->component_state_ |= STATUS_LED_ERROR;
|
this->component_state_ |= STATUS_LED_ERROR;
|
||||||
App.app_state_ |= STATUS_LED_ERROR;
|
App.app_state_ |= STATUS_LED_ERROR;
|
||||||
ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message);
|
ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message ? message : UNSPECIFIED_MESSAGE);
|
||||||
if (strcmp(message, "unspecified") != 0) {
|
if (message != nullptr) {
|
||||||
// Lazy allocate the error messages vector if needed
|
// Lazy allocate the error messages vector if needed
|
||||||
if (!component_error_messages) {
|
if (!component_error_messages) {
|
||||||
component_error_messages = std::make_unique<std::vector<std::pair<const Component *, const char *>>>();
|
component_error_messages = std::make_unique<std::vector<std::pair<const Component *, const char *>>>();
|
||||||
|
@@ -202,9 +202,9 @@ class Component {
|
|||||||
|
|
||||||
bool status_has_error() const;
|
bool status_has_error() const;
|
||||||
|
|
||||||
void status_set_warning(const char *message = "unspecified");
|
void status_set_warning(const char *message = nullptr);
|
||||||
|
|
||||||
void status_set_error(const char *message = "unspecified");
|
void status_set_error(const char *message = nullptr);
|
||||||
|
|
||||||
void status_clear_warning();
|
void status_clear_warning();
|
||||||
|
|
||||||
|
@@ -109,6 +109,7 @@
|
|||||||
#define USE_API
|
#define USE_API
|
||||||
#define USE_API_CLIENT_CONNECTED_TRIGGER
|
#define USE_API_CLIENT_CONNECTED_TRIGGER
|
||||||
#define USE_API_CLIENT_DISCONNECTED_TRIGGER
|
#define USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
|
#define USE_API_HOMEASSISTANT_SERVICES
|
||||||
#define USE_API_HOMEASSISTANT_STATES
|
#define USE_API_HOMEASSISTANT_STATES
|
||||||
#define USE_API_NOISE
|
#define USE_API_NOISE
|
||||||
#define USE_API_PLAINTEXT
|
#define USE_API_PLAINTEXT
|
||||||
|
@@ -68,7 +68,10 @@ To bit_cast(const From &src) {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
using std::lerp;
|
|
||||||
|
// clang-format off
|
||||||
|
inline float lerp(float completion, float start, float end) = delete; // Please use std::lerp. Notice that it has different order on arguments!
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// std::byteswap from C++23
|
// std::byteswap from C++23
|
||||||
template<typename T> constexpr T byteswap(T n) {
|
template<typename T> constexpr T byteswap(T n) {
|
||||||
|
@@ -83,6 +83,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|||||||
item->type = type;
|
item->type = type;
|
||||||
item->callback = std::move(func);
|
item->callback = std::move(func);
|
||||||
item->remove = false;
|
item->remove = false;
|
||||||
|
item->is_retry = is_retry;
|
||||||
|
|
||||||
#ifndef ESPHOME_THREAD_SINGLE
|
#ifndef ESPHOME_THREAD_SINGLE
|
||||||
// Special handling for defer() (delay = 0, type = TIMEOUT)
|
// Special handling for defer() (delay = 0, type = TIMEOUT)
|
||||||
@@ -134,8 +135,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|||||||
|
|
||||||
// For retries, check if there's a cancelled timeout first
|
// For retries, check if there's a cancelled timeout first
|
||||||
if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
|
if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
|
||||||
(has_cancelled_timeout_in_container_(this->items_, component, name_cstr) ||
|
(has_cancelled_timeout_in_container_(this->items_, component, name_cstr, /* match_retry= */ true) ||
|
||||||
has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr))) {
|
has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr, /* match_retry= */ true))) {
|
||||||
// Skip scheduling - the retry was cancelled
|
// Skip scheduling - the retry was cancelled
|
||||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||||
ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
|
ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
|
||||||
@@ -198,25 +199,27 @@ void retry_handler(const std::shared_ptr<RetryArgs> &args) {
|
|||||||
// second execution of `func` happens after `initial_wait_time`
|
// second execution of `func` happens after `initial_wait_time`
|
||||||
args->scheduler->set_timer_common_(
|
args->scheduler->set_timer_common_(
|
||||||
args->component, Scheduler::SchedulerItem::TIMEOUT, false, &args->name, args->current_interval,
|
args->component, Scheduler::SchedulerItem::TIMEOUT, false, &args->name, args->current_interval,
|
||||||
[args]() { retry_handler(args); }, true);
|
[args]() { retry_handler(args); }, /* is_retry= */ true);
|
||||||
// backoff_increase_factor applied to third & later executions
|
// backoff_increase_factor applied to third & later executions
|
||||||
args->current_interval *= args->backoff_increase_factor;
|
args->current_interval *= args->backoff_increase_factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
|
void HOT Scheduler::set_retry_common_(Component *component, bool is_static_string, const void *name_ptr,
|
||||||
uint8_t max_attempts, std::function<RetryResult(uint8_t)> func,
|
uint32_t initial_wait_time, uint8_t max_attempts,
|
||||||
float backoff_increase_factor) {
|
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
|
||||||
if (!name.empty())
|
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
||||||
this->cancel_retry(component, name);
|
|
||||||
|
if (name_cstr != nullptr)
|
||||||
|
this->cancel_retry(component, name_cstr);
|
||||||
|
|
||||||
if (initial_wait_time == SCHEDULER_DONT_RUN)
|
if (initial_wait_time == SCHEDULER_DONT_RUN)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
|
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
|
||||||
name.c_str(), initial_wait_time, max_attempts, backoff_increase_factor);
|
name_cstr ? name_cstr : "", initial_wait_time, max_attempts, backoff_increase_factor);
|
||||||
|
|
||||||
if (backoff_increase_factor < 0.0001) {
|
if (backoff_increase_factor < 0.0001) {
|
||||||
ESP_LOGE(TAG, "backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor, name.c_str());
|
ESP_LOGE(TAG, "backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor, name_cstr ? name_cstr : "");
|
||||||
backoff_increase_factor = 1;
|
backoff_increase_factor = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,15 +228,36 @@ void HOT Scheduler::set_retry(Component *component, const std::string &name, uin
|
|||||||
args->retry_countdown = max_attempts;
|
args->retry_countdown = max_attempts;
|
||||||
args->current_interval = initial_wait_time;
|
args->current_interval = initial_wait_time;
|
||||||
args->component = component;
|
args->component = component;
|
||||||
args->name = "retry$" + name;
|
args->name = name_cstr ? name_cstr : ""; // Convert to std::string for RetryArgs
|
||||||
args->backoff_increase_factor = backoff_increase_factor;
|
args->backoff_increase_factor = backoff_increase_factor;
|
||||||
args->scheduler = this;
|
args->scheduler = this;
|
||||||
|
|
||||||
// First execution of `func` immediately
|
// First execution of `func` immediately - use set_timer_common_ with is_retry=true
|
||||||
this->set_timeout(component, args->name, 0, [args]() { retry_handler(args); });
|
this->set_timer_common_(
|
||||||
|
component, SchedulerItem::TIMEOUT, false, &args->name, 0, [args]() { retry_handler(args); },
|
||||||
|
/* is_retry= */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
|
||||||
|
uint8_t max_attempts, std::function<RetryResult(uint8_t)> func,
|
||||||
|
float backoff_increase_factor) {
|
||||||
|
this->set_retry_common_(component, false, &name, initial_wait_time, max_attempts, std::move(func),
|
||||||
|
backoff_increase_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HOT Scheduler::set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts,
|
||||||
|
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
|
||||||
|
this->set_retry_common_(component, true, name, initial_wait_time, max_attempts, std::move(func),
|
||||||
|
backoff_increase_factor);
|
||||||
}
|
}
|
||||||
bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) {
|
bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) {
|
||||||
return this->cancel_timeout(component, "retry$" + name);
|
return this->cancel_retry(component, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HOT Scheduler::cancel_retry(Component *component, const char *name) {
|
||||||
|
// Cancel timeouts that have is_retry flag set
|
||||||
|
LockGuard guard{this->lock_};
|
||||||
|
return this->cancel_item_locked_(component, name, SchedulerItem::TIMEOUT, /* match_retry= */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
|
optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
|
||||||
@@ -479,7 +503,8 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper to cancel items by name - must be called with lock held
|
// Helper to cancel items by name - must be called with lock held
|
||||||
bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type) {
|
bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type,
|
||||||
|
bool match_retry) {
|
||||||
// Early return if name is invalid - no items to cancel
|
// Early return if name is invalid - no items to cancel
|
||||||
if (name_cstr == nullptr) {
|
if (name_cstr == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -492,7 +517,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
// Only check defer queue for timeouts (intervals never go there)
|
// Only check defer queue for timeouts (intervals never go there)
|
||||||
if (type == SchedulerItem::TIMEOUT) {
|
if (type == SchedulerItem::TIMEOUT) {
|
||||||
for (auto &item : this->defer_queue_) {
|
for (auto &item : this->defer_queue_) {
|
||||||
if (this->matches_item_(item, component, name_cstr, type)) {
|
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
||||||
item->remove = true;
|
item->remove = true;
|
||||||
total_cancelled++;
|
total_cancelled++;
|
||||||
}
|
}
|
||||||
@@ -502,7 +527,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
|
|
||||||
// Cancel items in the main heap
|
// Cancel items in the main heap
|
||||||
for (auto &item : this->items_) {
|
for (auto &item : this->items_) {
|
||||||
if (this->matches_item_(item, component, name_cstr, type)) {
|
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
||||||
item->remove = true;
|
item->remove = true;
|
||||||
total_cancelled++;
|
total_cancelled++;
|
||||||
this->to_remove_++; // Track removals for heap items
|
this->to_remove_++; // Track removals for heap items
|
||||||
@@ -511,7 +536,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
|
|
||||||
// Cancel items in to_add_
|
// Cancel items in to_add_
|
||||||
for (auto &item : this->to_add_) {
|
for (auto &item : this->to_add_) {
|
||||||
if (this->matches_item_(item, component, name_cstr, type)) {
|
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
||||||
item->remove = true;
|
item->remove = true;
|
||||||
total_cancelled++;
|
total_cancelled++;
|
||||||
// Don't track removals for to_add_ items
|
// Don't track removals for to_add_ items
|
||||||
|
@@ -61,7 +61,10 @@ class Scheduler {
|
|||||||
bool cancel_interval(Component *component, const char *name);
|
bool cancel_interval(Component *component, const char *name);
|
||||||
void set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts,
|
void set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts,
|
||||||
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor = 1.0f);
|
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor = 1.0f);
|
||||||
|
void set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts,
|
||||||
|
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor = 1.0f);
|
||||||
bool cancel_retry(Component *component, const std::string &name);
|
bool cancel_retry(Component *component, const std::string &name);
|
||||||
|
bool cancel_retry(Component *component, const char *name);
|
||||||
|
|
||||||
// Calculate when the next scheduled item should run
|
// Calculate when the next scheduled item should run
|
||||||
// @param now Fresh timestamp from millis() - must not be stale/cached
|
// @param now Fresh timestamp from millis() - must not be stale/cached
|
||||||
@@ -98,11 +101,18 @@ class Scheduler {
|
|||||||
enum Type : uint8_t { TIMEOUT, INTERVAL } type : 1;
|
enum Type : uint8_t { TIMEOUT, INTERVAL } type : 1;
|
||||||
bool remove : 1;
|
bool remove : 1;
|
||||||
bool name_is_dynamic : 1; // True if name was dynamically allocated (needs delete[])
|
bool name_is_dynamic : 1; // True if name was dynamically allocated (needs delete[])
|
||||||
// 5 bits padding
|
bool is_retry : 1; // True if this is a retry timeout
|
||||||
|
// 4 bits padding
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
SchedulerItem()
|
SchedulerItem()
|
||||||
: component(nullptr), interval(0), next_execution_(0), type(TIMEOUT), remove(false), name_is_dynamic(false) {
|
: component(nullptr),
|
||||||
|
interval(0),
|
||||||
|
next_execution_(0),
|
||||||
|
type(TIMEOUT),
|
||||||
|
remove(false),
|
||||||
|
name_is_dynamic(false),
|
||||||
|
is_retry(false) {
|
||||||
name_.static_name = nullptr;
|
name_.static_name = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +166,10 @@ class Scheduler {
|
|||||||
void set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string, const void *name_ptr,
|
void set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string, const void *name_ptr,
|
||||||
uint32_t delay, std::function<void()> func, bool is_retry = false);
|
uint32_t delay, std::function<void()> func, bool is_retry = false);
|
||||||
|
|
||||||
|
// Common implementation for retry
|
||||||
|
void set_retry_common_(Component *component, bool is_static_string, const void *name_ptr, uint32_t initial_wait_time,
|
||||||
|
uint8_t max_attempts, std::function<RetryResult(uint8_t)> func, float backoff_increase_factor);
|
||||||
|
|
||||||
uint64_t millis_64_(uint32_t now);
|
uint64_t millis_64_(uint32_t now);
|
||||||
// Cleanup logically deleted items from the scheduler
|
// Cleanup logically deleted items from the scheduler
|
||||||
// Returns the number of items remaining after cleanup
|
// Returns the number of items remaining after cleanup
|
||||||
@@ -165,7 +179,7 @@ class Scheduler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Helper to cancel items by name - must be called with lock held
|
// Helper to cancel items by name - must be called with lock held
|
||||||
bool cancel_item_locked_(Component *component, const char *name, SchedulerItem::Type type);
|
bool cancel_item_locked_(Component *component, const char *name, SchedulerItem::Type type, bool match_retry = false);
|
||||||
|
|
||||||
// Helper to extract name as const char* from either static string or std::string
|
// Helper to extract name as const char* from either static string or std::string
|
||||||
inline const char *get_name_cstr_(bool is_static_string, const void *name_ptr) {
|
inline const char *get_name_cstr_(bool is_static_string, const void *name_ptr) {
|
||||||
@@ -177,8 +191,9 @@ class Scheduler {
|
|||||||
|
|
||||||
// Helper function to check if item matches criteria for cancellation
|
// Helper function to check if item matches criteria for cancellation
|
||||||
inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr,
|
inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr,
|
||||||
SchedulerItem::Type type, bool skip_removed = true) const {
|
SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const {
|
||||||
if (item->component != component || item->type != type || (skip_removed && item->remove)) {
|
if (item->component != component || item->type != type || (skip_removed && item->remove) ||
|
||||||
|
(match_retry && !item->is_retry)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const char *item_name = item->get_name();
|
const char *item_name = item->get_name();
|
||||||
@@ -206,10 +221,11 @@ class Scheduler {
|
|||||||
|
|
||||||
// Template helper to check if any item in a container matches our criteria
|
// Template helper to check if any item in a container matches our criteria
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
bool has_cancelled_timeout_in_container_(const Container &container, Component *component,
|
bool has_cancelled_timeout_in_container_(const Container &container, Component *component, const char *name_cstr,
|
||||||
const char *name_cstr) const {
|
bool match_retry) const {
|
||||||
for (const auto &item : container) {
|
for (const auto &item : container) {
|
||||||
if (item->remove && this->matches_item_(item, component, name_cstr, SchedulerItem::TIMEOUT, false)) {
|
if (item->remove && this->matches_item_(item, component, name_cstr, SchedulerItem::TIMEOUT, match_retry,
|
||||||
|
/* skip_removed= */ false)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -225,9 +225,10 @@ class _Schema(vol.Schema):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
schema = schemas[0]
|
schema = schemas[0]
|
||||||
|
extra_schemas = self._extra_schemas.copy()
|
||||||
|
if isinstance(schema, _Schema):
|
||||||
|
extra_schemas.extend(schema._extra_schemas)
|
||||||
if isinstance(schema, vol.Schema):
|
if isinstance(schema, vol.Schema):
|
||||||
schema = schema.schema
|
schema = schema.schema
|
||||||
ret = super().extend(schema, extra=extra)
|
ret = super().extend(schema, extra=extra)
|
||||||
return _Schema(
|
return _Schema(ret.schema, extra=ret.extra, extra_schemas=extra_schemas)
|
||||||
ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas.copy()
|
|
||||||
)
|
|
||||||
|
@@ -78,7 +78,7 @@ lib_deps =
|
|||||||
glmnet/Dsmr@0.7 ; dsmr
|
glmnet/Dsmr@0.7 ; dsmr
|
||||||
rweather/Crypto@0.4.0 ; dsmr
|
rweather/Crypto@0.4.0 ; dsmr
|
||||||
dudanov/MideaUART@1.1.9 ; midea
|
dudanov/MideaUART@1.1.9 ; midea
|
||||||
tonia/HeatpumpIR@1.0.35 ; heatpumpir
|
tonia/HeatpumpIR@1.0.37 ; heatpumpir
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
-DUSE_ARDUINO
|
-DUSE_ARDUINO
|
||||||
@@ -125,7 +125,7 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script
|
|||||||
; This are common settings for the ESP32 (all variants) using Arduino.
|
; This are common settings for the ESP32 (all variants) using Arduino.
|
||||||
[common:esp32-arduino]
|
[common:esp32-arduino]
|
||||||
extends = common:arduino
|
extends = common:arduino
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21-1/platform-espressif32.zip
|
||||||
platform_packages =
|
platform_packages =
|
||||||
pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.2.1/esp32-3.2.1.zip
|
pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.2.1/esp32-3.2.1.zip
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script
|
|||||||
; This are common settings for the ESP32 (all variants) using IDF.
|
; This are common settings for the ESP32 (all variants) using IDF.
|
||||||
[common:esp32-idf]
|
[common:esp32-idf]
|
||||||
extends = common:idf
|
extends = common:idf
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21-1/platform-espressif32.zip
|
||||||
platform_packages =
|
platform_packages =
|
||||||
pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.4.2/esp-idf-v5.4.2.zip
|
pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.4.2/esp-idf-v5.4.2.zip
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
|||||||
esptool==4.9.0
|
esptool==4.9.0
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
esphome-dashboard==20250514.0
|
esphome-dashboard==20250514.0
|
||||||
aioesphomeapi==37.1.2
|
aioesphomeapi==37.1.4
|
||||||
zeroconf==0.147.0
|
zeroconf==0.147.0
|
||||||
puremagic==1.30
|
puremagic==1.30
|
||||||
ruamel.yaml==0.18.14 # dashboard_import
|
ruamel.yaml==0.18.14 # dashboard_import
|
||||||
|
@@ -275,13 +275,13 @@ class TypeInfo(ABC):
|
|||||||
Args:
|
Args:
|
||||||
name: Field name
|
name: Field name
|
||||||
force: Whether this is for a repeated field
|
force: Whether this is for a repeated field
|
||||||
base_method: Base method name (e.g., "add_int32_field")
|
base_method: Base method name (e.g., "add_int32")
|
||||||
value_expr: Optional value expression (defaults to name)
|
value_expr: Optional value expression (defaults to name)
|
||||||
"""
|
"""
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
method = f"{base_method}_repeated" if force else base_method
|
method = f"{base_method}_force" if force else base_method
|
||||||
value = value_expr if value_expr else name
|
value = value_expr if value_expr else name
|
||||||
return f"ProtoSize::{method}(total_size, {field_id_size}, {value});"
|
return f"size.{method}({field_id_size}, {value});"
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
@@ -389,7 +389,7 @@ class DoubleType(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_double_field(total_size, {field_id_size}, {name});"
|
return f"size.add_double({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 8
|
return 8
|
||||||
@@ -413,7 +413,7 @@ class FloatType(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_float_field(total_size, {field_id_size}, {name});"
|
return f"size.add_float({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
@@ -436,7 +436,7 @@ class Int64Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_int64_field")
|
return self._get_simple_size_calculation(name, force, "add_int64")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -456,7 +456,7 @@ class UInt64Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_uint64_field")
|
return self._get_simple_size_calculation(name, force, "add_uint64")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -476,7 +476,7 @@ class Int32Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_int32_field")
|
return self._get_simple_size_calculation(name, force, "add_int32")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -497,7 +497,7 @@ class Fixed64Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_fixed64_field(total_size, {field_id_size}, {name});"
|
return f"size.add_fixed64({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 8
|
return 8
|
||||||
@@ -521,7 +521,7 @@ class Fixed32Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_fixed32_field(total_size, {field_id_size}, {name});"
|
return f"size.add_fixed32({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
@@ -542,7 +542,7 @@ class BoolType(TypeInfo):
|
|||||||
return f"out.append(YESNO({name}));"
|
return f"out.append(YESNO({name}));"
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_bool_field")
|
return self._get_simple_size_calculation(name, force, "add_bool")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 1 # field ID + 1 byte
|
return self.calculate_field_id_size() + 1 # field ID + 1 byte
|
||||||
@@ -561,11 +561,16 @@ class StringType(TypeInfo):
|
|||||||
@property
|
@property
|
||||||
def public_content(self) -> list[str]:
|
def public_content(self) -> list[str]:
|
||||||
content: list[str] = []
|
content: list[str] = []
|
||||||
# Add std::string storage if message needs decoding
|
|
||||||
if self._needs_decode:
|
# Check if no_zero_copy option is set
|
||||||
|
no_zero_copy = get_field_opt(self._field, pb.no_zero_copy, False)
|
||||||
|
|
||||||
|
# Add std::string storage if message needs decoding OR if no_zero_copy is set
|
||||||
|
if self._needs_decode or no_zero_copy:
|
||||||
content.append(f"std::string {self.field_name}{{}};")
|
content.append(f"std::string {self.field_name}{{}};")
|
||||||
|
|
||||||
if self._needs_encode:
|
# Only add StringRef if encoding is needed AND no_zero_copy is not set
|
||||||
|
if self._needs_encode and not no_zero_copy:
|
||||||
content.extend(
|
content.extend(
|
||||||
[
|
[
|
||||||
# Add StringRef field if message needs encoding
|
# Add StringRef field if message needs encoding
|
||||||
@@ -580,13 +585,27 @@ class StringType(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def encode_content(self) -> str:
|
def encode_content(self) -> str:
|
||||||
|
# Check if no_zero_copy option is set
|
||||||
|
no_zero_copy = get_field_opt(self._field, pb.no_zero_copy, False)
|
||||||
|
|
||||||
|
if no_zero_copy:
|
||||||
|
# Use the std::string directly
|
||||||
|
return f"buffer.encode_string({self.number}, this->{self.field_name});"
|
||||||
|
# Use the StringRef
|
||||||
return f"buffer.encode_string({self.number}, this->{self.field_name}_ref_);"
|
return f"buffer.encode_string({self.number}, this->{self.field_name}_ref_);"
|
||||||
|
|
||||||
def dump(self, name):
|
def dump(self, name):
|
||||||
|
# Check if no_zero_copy option is set
|
||||||
|
no_zero_copy = get_field_opt(self._field, pb.no_zero_copy, False)
|
||||||
|
|
||||||
# If name is 'it', this is a repeated field element - always use string
|
# If name is 'it', this is a repeated field element - always use string
|
||||||
if name == "it":
|
if name == "it":
|
||||||
return "append_quoted_string(out, StringRef(it));"
|
return "append_quoted_string(out, StringRef(it));"
|
||||||
|
|
||||||
|
# If no_zero_copy is set, always use std::string
|
||||||
|
if no_zero_copy:
|
||||||
|
return f'out.append("\'").append(this->{self.field_name}).append("\'");'
|
||||||
|
|
||||||
# For SOURCE_CLIENT only, always use std::string
|
# For SOURCE_CLIENT only, always use std::string
|
||||||
if not self._needs_encode:
|
if not self._needs_encode:
|
||||||
return f'out.append("\'").append(this->{self.field_name}).append("\'");'
|
return f'out.append("\'").append(this->{self.field_name}).append("\'");'
|
||||||
@@ -606,6 +625,13 @@ class StringType(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def dump_content(self) -> str:
|
def dump_content(self) -> str:
|
||||||
|
# Check if no_zero_copy option is set
|
||||||
|
no_zero_copy = get_field_opt(self._field, pb.no_zero_copy, False)
|
||||||
|
|
||||||
|
# If no_zero_copy is set, always use std::string
|
||||||
|
if no_zero_copy:
|
||||||
|
return f'dump_field(out, "{self.name}", this->{self.field_name});'
|
||||||
|
|
||||||
# For SOURCE_CLIENT only, use std::string
|
# For SOURCE_CLIENT only, use std::string
|
||||||
if not self._needs_encode:
|
if not self._needs_encode:
|
||||||
return f'dump_field(out, "{self.name}", this->{self.field_name});'
|
return f'dump_field(out, "{self.name}", this->{self.field_name});'
|
||||||
@@ -621,20 +647,29 @@ class StringType(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
# For SOURCE_CLIENT only messages, use the string field directly
|
# Check if no_zero_copy option is set
|
||||||
if not self._needs_encode:
|
no_zero_copy = get_field_opt(self._field, pb.no_zero_copy, False)
|
||||||
return self._get_simple_size_calculation(name, force, "add_string_field")
|
|
||||||
|
# For SOURCE_CLIENT only messages or no_zero_copy, use the string field directly
|
||||||
|
if not self._needs_encode or no_zero_copy:
|
||||||
|
# For no_zero_copy, we need to use .size() on the string
|
||||||
|
if no_zero_copy and name != "it":
|
||||||
|
field_id_size = self.calculate_field_id_size()
|
||||||
|
return (
|
||||||
|
f"size.add_length({field_id_size}, this->{self.field_name}.size());"
|
||||||
|
)
|
||||||
|
return self._get_simple_size_calculation(name, force, "add_length")
|
||||||
|
|
||||||
# Check if this is being called from a repeated field context
|
# Check if this is being called from a repeated field context
|
||||||
# In that case, 'name' will be 'it' and we need to use the repeated version
|
# In that case, 'name' will be 'it' and we need to use the repeated version
|
||||||
if name == "it":
|
if name == "it":
|
||||||
# For repeated fields, we need to use add_string_field_repeated which includes field ID
|
# For repeated fields, we need to use add_length_force which includes field ID
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_string_field_repeated(total_size, {field_id_size}, it);"
|
return f"size.add_length_force({field_id_size}, it.size());"
|
||||||
|
|
||||||
# For messages that need encoding, use the StringRef size
|
# For messages that need encoding, use the StringRef size
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_string_field(total_size, {field_id_size}, this->{self.field_name}_ref_.size());"
|
return f"size.add_length({field_id_size}, this->{self.field_name}_ref_.size());"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
||||||
@@ -768,7 +803,7 @@ class BytesType(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return f"ProtoSize::add_bytes_field(total_size, {self.calculate_field_id_size()}, this->{self.field_name}_len_);"
|
return f"size.add_length({self.calculate_field_id_size()}, this->{self.field_name}_len_);"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
|
||||||
@@ -842,14 +877,10 @@ class FixedArrayBytesType(TypeInfo):
|
|||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
|
|
||||||
if force:
|
if force:
|
||||||
# For repeated fields, always calculate size
|
# For repeated fields, always calculate size (no zero check)
|
||||||
return f"total_size += {field_id_size} + ProtoSize::varint(static_cast<uint32_t>({length_field})) + {length_field};"
|
return f"size.add_length_force({field_id_size}, {length_field});"
|
||||||
# For non-repeated fields, skip if length is 0 (matching encode_string behavior)
|
# For non-repeated fields, add_length already checks for zero
|
||||||
return (
|
return f"size.add_length({field_id_size}, {length_field});"
|
||||||
f"if ({length_field} != 0) {{\n"
|
|
||||||
f" total_size += {field_id_size} + ProtoSize::varint(static_cast<uint32_t>({length_field})) + {length_field};\n"
|
|
||||||
f"}}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
# Estimate based on typical BLE advertisement size
|
# Estimate based on typical BLE advertisement size
|
||||||
@@ -876,7 +907,7 @@ class UInt32Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_uint32_field")
|
return self._get_simple_size_calculation(name, force, "add_uint32")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -912,7 +943,7 @@ class EnumType(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(
|
return self._get_simple_size_calculation(
|
||||||
name, force, "add_enum_field", f"static_cast<uint32_t>({name})"
|
name, force, "add_uint32", f"static_cast<uint32_t>({name})"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
@@ -934,7 +965,7 @@ class SFixed32Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_sfixed32_field(total_size, {field_id_size}, {name});"
|
return f"size.add_sfixed32({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
@@ -958,7 +989,7 @@ class SFixed64Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"ProtoSize::add_sfixed64_field(total_size, {field_id_size}, {name});"
|
return f"size.add_sfixed64({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 8
|
return 8
|
||||||
@@ -981,7 +1012,7 @@ class SInt32Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_sint32_field")
|
return self._get_simple_size_calculation(name, force, "add_sint32")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -1001,7 +1032,7 @@ class SInt64Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_sint64_field")
|
return self._get_simple_size_calculation(name, force, "add_sint64")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -1132,6 +1163,10 @@ class FixedArrayRepeatedType(TypeInfo):
|
|||||||
class RepeatedTypeInfo(TypeInfo):
|
class RepeatedTypeInfo(TypeInfo):
|
||||||
def __init__(self, field: descriptor.FieldDescriptorProto) -> None:
|
def __init__(self, field: descriptor.FieldDescriptorProto) -> None:
|
||||||
super().__init__(field)
|
super().__init__(field)
|
||||||
|
# Check if this is a pointer field by looking for container_pointer option
|
||||||
|
self._container_type = get_field_opt(field, pb.container_pointer, "")
|
||||||
|
self._use_pointer = bool(self._container_type)
|
||||||
|
|
||||||
# For repeated fields, we need to get the base type info
|
# For repeated fields, we need to get the base type info
|
||||||
# but we can't call create_field_type_info as it would cause recursion
|
# but we can't call create_field_type_info as it would cause recursion
|
||||||
# So we extract just the type creation logic
|
# So we extract just the type creation logic
|
||||||
@@ -1147,6 +1182,13 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def cpp_type(self) -> str:
|
def cpp_type(self) -> str:
|
||||||
|
if self._use_pointer and self._container_type:
|
||||||
|
# For pointer fields, use the specified container type
|
||||||
|
# If the container type already includes the element type (e.g., std::set<climate::ClimateMode>)
|
||||||
|
# use it as-is, otherwise append the element type
|
||||||
|
if "<" in self._container_type and ">" in self._container_type:
|
||||||
|
return f"const {self._container_type}*"
|
||||||
|
return f"const {self._container_type}<{self._ti.cpp_type}>*"
|
||||||
return f"std::vector<{self._ti.cpp_type}>"
|
return f"std::vector<{self._ti.cpp_type}>"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -1167,6 +1209,9 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def decode_varint_content(self) -> str:
|
def decode_varint_content(self) -> str:
|
||||||
|
# Pointer fields don't support decoding
|
||||||
|
if self._use_pointer:
|
||||||
|
return None
|
||||||
content = self._ti.decode_varint
|
content = self._ti.decode_varint
|
||||||
if content is None:
|
if content is None:
|
||||||
return None
|
return None
|
||||||
@@ -1176,6 +1221,9 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def decode_length_content(self) -> str:
|
def decode_length_content(self) -> str:
|
||||||
|
# Pointer fields don't support decoding
|
||||||
|
if self._use_pointer:
|
||||||
|
return None
|
||||||
content = self._ti.decode_length
|
content = self._ti.decode_length
|
||||||
if content is None and isinstance(self._ti, MessageType):
|
if content is None and isinstance(self._ti, MessageType):
|
||||||
# Special handling for non-template message decoding
|
# Special handling for non-template message decoding
|
||||||
@@ -1188,6 +1236,9 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def decode_32bit_content(self) -> str:
|
def decode_32bit_content(self) -> str:
|
||||||
|
# Pointer fields don't support decoding
|
||||||
|
if self._use_pointer:
|
||||||
|
return None
|
||||||
content = self._ti.decode_32bit
|
content = self._ti.decode_32bit
|
||||||
if content is None:
|
if content is None:
|
||||||
return None
|
return None
|
||||||
@@ -1197,6 +1248,9 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def decode_64bit_content(self) -> str:
|
def decode_64bit_content(self) -> str:
|
||||||
|
# Pointer fields don't support decoding
|
||||||
|
if self._use_pointer:
|
||||||
|
return None
|
||||||
content = self._ti.decode_64bit
|
content = self._ti.decode_64bit
|
||||||
if content is None:
|
if content is None:
|
||||||
return None
|
return None
|
||||||
@@ -1211,6 +1265,15 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def encode_content(self) -> str:
|
def encode_content(self) -> str:
|
||||||
|
if self._use_pointer:
|
||||||
|
# For pointer fields, just dereference (pointer should never be null in our use case)
|
||||||
|
o = f"for (const auto &it : *this->{self.field_name}) {{\n"
|
||||||
|
if isinstance(self._ti, EnumType):
|
||||||
|
o += f" buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>(it), true);\n"
|
||||||
|
else:
|
||||||
|
o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n"
|
||||||
|
o += "}"
|
||||||
|
return o
|
||||||
o = f"for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n"
|
o = f"for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n"
|
||||||
if isinstance(self._ti, EnumType):
|
if isinstance(self._ti, EnumType):
|
||||||
o += f" buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>(it), true);\n"
|
o += f" buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>(it), true);\n"
|
||||||
@@ -1221,6 +1284,11 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def dump_content(self) -> str:
|
def dump_content(self) -> str:
|
||||||
|
if self._use_pointer:
|
||||||
|
# For pointer fields, dereference and use the existing helper
|
||||||
|
return _generate_array_dump_content(
|
||||||
|
self._ti, f"*this->{self.field_name}", self.name, is_bool=False
|
||||||
|
)
|
||||||
return _generate_array_dump_content(
|
return _generate_array_dump_content(
|
||||||
self._ti, f"this->{self.field_name}", self.name, is_bool=self._ti_is_bool
|
self._ti, f"this->{self.field_name}", self.name, is_bool=self._ti_is_bool
|
||||||
)
|
)
|
||||||
@@ -1231,29 +1299,34 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
# For repeated fields, we always need to pass force=True to the underlying type's calculation
|
# For repeated fields, we always need to pass force=True to the underlying type's calculation
|
||||||
# This is because the encode method always sets force=true for repeated fields
|
# This is because the encode method always sets force=true for repeated fields
|
||||||
|
|
||||||
|
# Handle message types separately as they use a dedicated helper
|
||||||
if isinstance(self._ti, MessageType):
|
if isinstance(self._ti, MessageType):
|
||||||
# For repeated messages, use the dedicated helper that handles iteration internally
|
|
||||||
field_id_size = self._ti.calculate_field_id_size()
|
field_id_size = self._ti.calculate_field_id_size()
|
||||||
return (
|
container = f"*{name}" if self._use_pointer else name
|
||||||
f"ProtoSize::add_repeated_message(total_size, {field_id_size}, {name});"
|
return f"size.add_repeated_message({field_id_size}, {container});"
|
||||||
)
|
|
||||||
|
|
||||||
# For other repeated types, use the underlying type's size calculation with force=True
|
# For non-message types, generate size calculation with iteration
|
||||||
o = f"if (!{name}.empty()) {{\n"
|
container_ref = f"*{name}" if self._use_pointer else name
|
||||||
|
empty_check = f"{name}->empty()" if self._use_pointer else f"{name}.empty()"
|
||||||
|
|
||||||
# Check if this is a fixed-size type by seeing if it has a fixed byte count
|
o = f"if (!{empty_check}) {{\n"
|
||||||
|
|
||||||
|
# Check if this is a fixed-size type
|
||||||
num_bytes = self._ti.get_fixed_size_bytes()
|
num_bytes = self._ti.get_fixed_size_bytes()
|
||||||
if num_bytes is not None:
|
if num_bytes is not None:
|
||||||
# Fixed types have constant size per element, so we can multiply
|
# Fixed types have constant size per element
|
||||||
field_id_size = self._ti.calculate_field_id_size()
|
field_id_size = self._ti.calculate_field_id_size()
|
||||||
# Pre-calculate the total bytes per element
|
|
||||||
bytes_per_element = field_id_size + num_bytes
|
bytes_per_element = field_id_size + num_bytes
|
||||||
o += f" total_size += {name}.size() * {bytes_per_element};\n"
|
size_expr = f"{name}->size()" if self._use_pointer else f"{name}.size()"
|
||||||
|
o += f" size.add_precalculated_size({size_expr} * {bytes_per_element});\n"
|
||||||
else:
|
else:
|
||||||
# Other types need the actual value
|
# Other types need the actual value
|
||||||
o += f" for (const auto {'' if self._ti_is_bool else '&'}it : {name}) {{\n"
|
auto_ref = "" if self._ti_is_bool else "&"
|
||||||
|
o += f" for (const auto {auto_ref}it : {container_ref}) {{\n"
|
||||||
o += f" {self._ti.get_size_calculation('it', True)}\n"
|
o += f" {self._ti.get_size_calculation('it', True)}\n"
|
||||||
o += " }\n"
|
o += " }\n"
|
||||||
|
|
||||||
o += "}"
|
o += "}"
|
||||||
return o
|
return o
|
||||||
|
|
||||||
@@ -1680,11 +1753,11 @@ def build_message_type(
|
|||||||
if needs_encode and encode:
|
if needs_encode and encode:
|
||||||
o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{"
|
o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{"
|
||||||
if len(encode) == 1 and len(encode[0]) + len(o) + 3 < 120:
|
if len(encode) == 1 and len(encode[0]) + len(o) + 3 < 120:
|
||||||
o += f" {encode[0]} "
|
o += f" {encode[0]} }}\n"
|
||||||
else:
|
else:
|
||||||
o += "\n"
|
o += "\n"
|
||||||
o += indent("\n".join(encode)) + "\n"
|
o += indent("\n".join(encode)) + "\n"
|
||||||
o += "}\n"
|
o += "}\n"
|
||||||
cpp += o
|
cpp += o
|
||||||
prot = "void encode(ProtoWriteBuffer buffer) const override;"
|
prot = "void encode(ProtoWriteBuffer buffer) const override;"
|
||||||
public_content.append(prot)
|
public_content.append(prot)
|
||||||
@@ -1692,17 +1765,17 @@ def build_message_type(
|
|||||||
|
|
||||||
# Add calculate_size method only if this message needs encoding and has fields
|
# Add calculate_size method only if this message needs encoding and has fields
|
||||||
if needs_encode and size_calc:
|
if needs_encode and size_calc:
|
||||||
o = f"void {desc.name}::calculate_size(uint32_t &total_size) const {{"
|
o = f"void {desc.name}::calculate_size(ProtoSize &size) const {{"
|
||||||
# For a single field, just inline it for simplicity
|
# For a single field, just inline it for simplicity
|
||||||
if len(size_calc) == 1 and len(size_calc[0]) + len(o) + 3 < 120:
|
if len(size_calc) == 1 and len(size_calc[0]) + len(o) + 3 < 120:
|
||||||
o += f" {size_calc[0]} "
|
o += f" {size_calc[0]} }}\n"
|
||||||
else:
|
else:
|
||||||
# For multiple fields
|
# For multiple fields
|
||||||
o += "\n"
|
o += "\n"
|
||||||
o += indent("\n".join(size_calc)) + "\n"
|
o += indent("\n".join(size_calc)) + "\n"
|
||||||
o += "}\n"
|
o += "}\n"
|
||||||
cpp += o
|
cpp += o
|
||||||
prot = "void calculate_size(uint32_t &total_size) const override;"
|
prot = "void calculate_size(ProtoSize &size) const override;"
|
||||||
public_content.append(prot)
|
public_content.append(prot)
|
||||||
# If no fields to calculate size for or message doesn't need encoding, the default implementation in ProtoMessage will be used
|
# If no fields to calculate size for or message doesn't need encoding, the default implementation in ProtoMessage will be used
|
||||||
|
|
||||||
@@ -1733,8 +1806,13 @@ def build_message_type(
|
|||||||
if base_class:
|
if base_class:
|
||||||
out = f"class {desc.name} : public {base_class} {{\n"
|
out = f"class {desc.name} : public {base_class} {{\n"
|
||||||
else:
|
else:
|
||||||
# Determine inheritance based on whether the message needs decoding
|
# Check if message has any non-deprecated fields
|
||||||
base_class = "ProtoDecodableMessage" if needs_decode else "ProtoMessage"
|
has_fields = any(not field.options.deprecated for field in desc.field)
|
||||||
|
# Determine inheritance based on whether the message needs decoding and has fields
|
||||||
|
if needs_decode and has_fields:
|
||||||
|
base_class = "ProtoDecodableMessage"
|
||||||
|
else:
|
||||||
|
base_class = "ProtoMessage"
|
||||||
out = f"class {desc.name} : public {base_class} {{\n"
|
out = f"class {desc.name} : public {base_class} {{\n"
|
||||||
out += " public:\n"
|
out += " public:\n"
|
||||||
out += indent("\n".join(public_content)) + "\n"
|
out += indent("\n".join(public_content)) + "\n"
|
||||||
@@ -2000,7 +2078,14 @@ def build_service_message_type(
|
|||||||
hout += f"virtual void {func}(const {mt.name} &value){{}};\n"
|
hout += f"virtual void {func}(const {mt.name} &value){{}};\n"
|
||||||
case = ""
|
case = ""
|
||||||
case += f"{mt.name} msg;\n"
|
case += f"{mt.name} msg;\n"
|
||||||
case += "msg.decode(msg_data, msg_size);\n"
|
# Check if this message has any fields (excluding deprecated ones)
|
||||||
|
has_fields = any(not field.options.deprecated for field in mt.field)
|
||||||
|
if has_fields:
|
||||||
|
# Normal case: decode the message
|
||||||
|
case += "msg.decode(msg_data, msg_size);\n"
|
||||||
|
else:
|
||||||
|
# Empty message optimization: skip decode since there are no fields
|
||||||
|
case += "// Empty message: no decode needed\n"
|
||||||
if log:
|
if log:
|
||||||
case += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
|
case += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
|
||||||
case += f'ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n'
|
case += f'ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n'
|
||||||
@@ -2029,6 +2114,7 @@ def main() -> None:
|
|||||||
d = descriptor.FileDescriptorSet.FromString(proto_content)
|
d = descriptor.FileDescriptorSet.FromString(proto_content)
|
||||||
|
|
||||||
file = d.file[0]
|
file = d.file[0]
|
||||||
|
|
||||||
content = FILE_HEADER
|
content = FILE_HEADER
|
||||||
content += """\
|
content += """\
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -2037,7 +2123,10 @@ def main() -> None:
|
|||||||
#include "esphome/core/string_ref.h"
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include "api_pb2_includes.h"
|
||||||
|
"""
|
||||||
|
|
||||||
|
content += """
|
||||||
namespace esphome::api {
|
namespace esphome::api {
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@@ -6,7 +6,7 @@ set -e
|
|||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
if [ ! -n "$VIRTUAL_ENV" ]; then
|
if [ ! -n "$VIRTUAL_ENV" ]; then
|
||||||
if [ -x "$(command -v uv)" ]; then
|
if [ -x "$(command -v uv)" ]; then
|
||||||
uv venv venv
|
uv venv --seed venv
|
||||||
else
|
else
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
fi
|
fi
|
||||||
|
51
tests/component_tests/config_validation/test_config.py
Normal file
51
tests/component_tests/config_validation/test_config.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"""
|
||||||
|
Test schema.extend functionality in esphome.config_validation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_extend() -> None:
|
||||||
|
"""Test that schema.extend correctly merges schemas with extras."""
|
||||||
|
|
||||||
|
def func1(data: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
data["extra_1"] = "value1"
|
||||||
|
return data
|
||||||
|
|
||||||
|
def func2(data: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
data["extra_2"] = "value2"
|
||||||
|
return data
|
||||||
|
|
||||||
|
schema1 = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required("key1"): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
schema1.add_extra(func1)
|
||||||
|
schema2 = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required("key2"): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
schema2.add_extra(func2)
|
||||||
|
extended_schema = schema1.extend(schema2)
|
||||||
|
config = {
|
||||||
|
"key1": "initial_value1",
|
||||||
|
"key2": "initial_value2",
|
||||||
|
}
|
||||||
|
validated = extended_schema(config)
|
||||||
|
assert validated["key1"] == "initial_value1"
|
||||||
|
assert validated["key2"] == "initial_value2"
|
||||||
|
assert validated["extra_1"] == "value1"
|
||||||
|
assert validated["extra_2"] == "value2"
|
||||||
|
|
||||||
|
# Check the opposite order of extension
|
||||||
|
extended_schema = schema2.extend(schema1)
|
||||||
|
|
||||||
|
validated = extended_schema(config)
|
||||||
|
assert validated["key1"] == "initial_value1"
|
||||||
|
assert validated["key2"] == "initial_value2"
|
||||||
|
assert validated["extra_1"] == "value1"
|
||||||
|
assert validated["extra_2"] == "value2"
|
6
tests/components/adc/test.esp32-p4-idf.yaml
Normal file
6
tests/components/adc/test.esp32-p4-idf.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
packages:
|
||||||
|
base: !include common.yaml
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- id: !extend my_sensor
|
||||||
|
pin: GPIO50
|
@@ -738,7 +738,7 @@ lvgl:
|
|||||||
id: bar_id
|
id: bar_id
|
||||||
value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
||||||
start_value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
start_value: !lambda return (int)((float)rand() / RAND_MAX * 100);
|
||||||
mode: symmetrical
|
mode: range
|
||||||
- logger.log:
|
- logger.log:
|
||||||
format: "bar value %f"
|
format: "bar value %f"
|
||||||
args: [x]
|
args: [x]
|
||||||
|
@@ -12,6 +12,8 @@ display:
|
|||||||
#- platform: mipi_dsi
|
#- platform: mipi_dsi
|
||||||
#id: backlight_id
|
#id: backlight_id
|
||||||
|
|
||||||
|
psram:
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
sda: GPIO7
|
sda: GPIO7
|
||||||
scl: GPIO8
|
scl: GPIO8
|
||||||
|
@@ -6,6 +6,12 @@ esphome:
|
|||||||
- output.set_level:
|
- output.set_level:
|
||||||
id: light_output_1
|
id: light_output_1
|
||||||
level: 50%
|
level: 50%
|
||||||
|
- output.set_min_power:
|
||||||
|
id: light_output_1
|
||||||
|
min_power: 20%
|
||||||
|
- output.set_max_power:
|
||||||
|
id: light_output_1
|
||||||
|
max_power: 80%
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ${output_platform}
|
- platform: ${output_platform}
|
||||||
|
5
tests/components/packages/garage-door.yaml
Normal file
5
tests/components/packages/garage-door.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
switch:
|
||||||
|
- name: ${door_name} Garage Door Switch
|
||||||
|
platform: gpio
|
||||||
|
pin: ${door_pin}
|
||||||
|
id: ${door_id}
|
19
tests/components/packages/test-vars.esp32-idf.yaml
Normal file
19
tests/components/packages/test-vars.esp32-idf.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
packages:
|
||||||
|
left_garage_door: !include
|
||||||
|
file: garage-door.yaml
|
||||||
|
vars:
|
||||||
|
door_name: Left
|
||||||
|
door_pin: 1
|
||||||
|
door_id: left_garage_door
|
||||||
|
middle_garage_door: !include
|
||||||
|
file: garage-door.yaml
|
||||||
|
vars:
|
||||||
|
door_name: Middle
|
||||||
|
door_pin: 2
|
||||||
|
door_id: middle_garage_door
|
||||||
|
right_garage_door: !include
|
||||||
|
file: garage-door.yaml
|
||||||
|
vars:
|
||||||
|
door_name: Right
|
||||||
|
door_pin: 3
|
||||||
|
door_id: right_garage_door
|
@@ -1,44 +1,3 @@
|
|||||||
sensor:
|
|
||||||
- platform: template
|
|
||||||
name: "Template Sensor"
|
|
||||||
id: template_sens
|
|
||||||
lambda: |-
|
|
||||||
if (id(some_binary_sensor).state) {
|
|
||||||
return 42.0;
|
|
||||||
} else {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
update_interval: 60s
|
|
||||||
filters:
|
|
||||||
- offset: 10
|
|
||||||
- multiply: 1
|
|
||||||
- offset: !lambda return 10;
|
|
||||||
- multiply: !lambda return 2;
|
|
||||||
- filter_out:
|
|
||||||
- 10
|
|
||||||
- 20
|
|
||||||
- !lambda return 10;
|
|
||||||
- filter_out: 10
|
|
||||||
- filter_out: !lambda return NAN;
|
|
||||||
- timeout:
|
|
||||||
timeout: 10s
|
|
||||||
value: !lambda return 10;
|
|
||||||
- timeout:
|
|
||||||
timeout: 1h
|
|
||||||
value: 20.0
|
|
||||||
- timeout:
|
|
||||||
timeout: 1d
|
|
||||||
- to_ntc_resistance:
|
|
||||||
calibration:
|
|
||||||
- 10.0kOhm -> 25°C
|
|
||||||
- 27.219kOhm -> 0°C
|
|
||||||
- 14.674kOhm -> 15°C
|
|
||||||
- to_ntc_temperature:
|
|
||||||
calibration:
|
|
||||||
- 10.0kOhm -> 25°C
|
|
||||||
- 27.219kOhm -> 0°C
|
|
||||||
- 14.674kOhm -> 15°C
|
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
on_boot:
|
on_boot:
|
||||||
- sensor.template.publish:
|
- sensor.template.publish:
|
||||||
@@ -82,6 +41,123 @@ binary_sensor:
|
|||||||
sensor.in_range:
|
sensor.in_range:
|
||||||
id: template_sens
|
id: template_sens
|
||||||
below: 30.0
|
below: 30.0
|
||||||
|
filters:
|
||||||
|
- invert:
|
||||||
|
- delayed_on: 100ms
|
||||||
|
- delayed_off: 100ms
|
||||||
|
- delayed_on_off: !lambda "if (id(test_switch).state) return 1000; else return 0;"
|
||||||
|
- delayed_on_off:
|
||||||
|
time_on: 10s
|
||||||
|
time_off: !lambda "if (id(test_switch).state) return 1000; else return 0;"
|
||||||
|
- autorepeat:
|
||||||
|
- delay: 1s
|
||||||
|
time_off: 100ms
|
||||||
|
time_on: 900ms
|
||||||
|
- delay: 5s
|
||||||
|
time_off: 100ms
|
||||||
|
time_on: 400ms
|
||||||
|
- lambda: |-
|
||||||
|
if (id(other_binary_sensor).state) {
|
||||||
|
return x;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
- settle: 500ms
|
||||||
|
- timeout: 5s
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: template
|
||||||
|
name: "Template Sensor"
|
||||||
|
id: template_sens
|
||||||
|
lambda: |-
|
||||||
|
if (id(some_binary_sensor).state) {
|
||||||
|
return 42.0;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
update_interval: 60s
|
||||||
|
filters:
|
||||||
|
- calibrate_linear:
|
||||||
|
- 0.0 -> 0.0
|
||||||
|
- 40.0 -> 45.0
|
||||||
|
- 100.0 -> 102.5
|
||||||
|
- calibrate_polynomial:
|
||||||
|
degree: 2
|
||||||
|
datapoints:
|
||||||
|
# Map 0.0 (from sensor) to 0.0 (true value)
|
||||||
|
- 0.0 -> 0.0
|
||||||
|
- 10.0 -> 12.1
|
||||||
|
- 13.0 -> 14.0
|
||||||
|
- clamp:
|
||||||
|
max_value: 10.0
|
||||||
|
min_value: -10.0
|
||||||
|
- debounce: 0.1s
|
||||||
|
- delta: 5.0
|
||||||
|
- exponential_moving_average:
|
||||||
|
alpha: 0.1
|
||||||
|
send_every: 15
|
||||||
|
- filter_out:
|
||||||
|
- 10
|
||||||
|
- 20
|
||||||
|
- !lambda return 10;
|
||||||
|
- filter_out: 10
|
||||||
|
- filter_out: !lambda return NAN;
|
||||||
|
- heartbeat: 5s
|
||||||
|
- lambda: return x * (9.0/5.0) + 32.0;
|
||||||
|
- max:
|
||||||
|
window_size: 10
|
||||||
|
send_every: 2
|
||||||
|
send_first_at: 1
|
||||||
|
- median:
|
||||||
|
window_size: 7
|
||||||
|
send_every: 4
|
||||||
|
send_first_at: 3
|
||||||
|
- min:
|
||||||
|
window_size: 10
|
||||||
|
send_every: 2
|
||||||
|
send_first_at: 1
|
||||||
|
- multiply: 1
|
||||||
|
- multiply: !lambda return 2;
|
||||||
|
- offset: 10
|
||||||
|
- offset: !lambda return 10;
|
||||||
|
- or:
|
||||||
|
- quantile:
|
||||||
|
window_size: 7
|
||||||
|
send_every: 4
|
||||||
|
send_first_at: 3
|
||||||
|
quantile: .9
|
||||||
|
- round: 1
|
||||||
|
- round_to_multiple_of: 0.25
|
||||||
|
- skip_initial: 3
|
||||||
|
- sliding_window_moving_average:
|
||||||
|
window_size: 15
|
||||||
|
send_every: 15
|
||||||
|
- throttle: 1s
|
||||||
|
- throttle_average: 2s
|
||||||
|
- throttle_with_priority: 5s
|
||||||
|
- throttle_with_priority:
|
||||||
|
timeout: 3s
|
||||||
|
value:
|
||||||
|
- 42.0
|
||||||
|
- nan
|
||||||
|
- timeout:
|
||||||
|
timeout: 10s
|
||||||
|
value: !lambda return 10;
|
||||||
|
- timeout:
|
||||||
|
timeout: 1h
|
||||||
|
value: 20.0
|
||||||
|
- timeout:
|
||||||
|
timeout: 1d
|
||||||
|
- to_ntc_resistance:
|
||||||
|
calibration:
|
||||||
|
- 10.0kOhm -> 25°C
|
||||||
|
- 27.219kOhm -> 0°C
|
||||||
|
- 14.674kOhm -> 15°C
|
||||||
|
- to_ntc_temperature:
|
||||||
|
calibration:
|
||||||
|
- 10.0kOhm -> 25°C
|
||||||
|
- 27.219kOhm -> 0°C
|
||||||
|
- 14.674kOhm -> 15°C
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: template
|
- platform: template
|
||||||
@@ -92,6 +168,7 @@ output:
|
|||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: template
|
- platform: template
|
||||||
|
id: test_switch
|
||||||
name: "Template Switch"
|
name: "Template Switch"
|
||||||
lambda: |-
|
lambda: |-
|
||||||
if (id(some_binary_sensor).state) {
|
if (id(some_binary_sensor).state) {
|
||||||
|
311
tests/integration/fixtures/api_homeassistant.yaml
Normal file
311
tests/integration/fixtures/api_homeassistant.yaml
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
esphome:
|
||||||
|
name: test-ha-api
|
||||||
|
friendly_name: Home Assistant API Test
|
||||||
|
|
||||||
|
host:
|
||||||
|
|
||||||
|
api:
|
||||||
|
services:
|
||||||
|
- service: trigger_all_tests
|
||||||
|
then:
|
||||||
|
- logger.log: "=== Starting Home Assistant API Tests ==="
|
||||||
|
- button.press: test_basic_service
|
||||||
|
- button.press: test_templated_service
|
||||||
|
- button.press: test_empty_string_service
|
||||||
|
- button.press: test_multiple_fields_service
|
||||||
|
- button.press: test_complex_lambda_service
|
||||||
|
- button.press: test_all_empty_service
|
||||||
|
- button.press: test_rapid_service_calls
|
||||||
|
- button.press: test_read_ha_states
|
||||||
|
- number.set:
|
||||||
|
id: ha_number
|
||||||
|
value: 42.5
|
||||||
|
- switch.turn_on: ha_switch
|
||||||
|
- switch.turn_off: ha_switch
|
||||||
|
- logger.log: "=== All tests completed ==="
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: DEBUG
|
||||||
|
|
||||||
|
# Time component for templated values
|
||||||
|
time:
|
||||||
|
- platform: homeassistant
|
||||||
|
id: homeassistant_time
|
||||||
|
|
||||||
|
# Global variables for testing
|
||||||
|
globals:
|
||||||
|
- id: test_brightness
|
||||||
|
type: int
|
||||||
|
initial_value: '75'
|
||||||
|
- id: test_string
|
||||||
|
type: std::string
|
||||||
|
initial_value: '"test_value"'
|
||||||
|
|
||||||
|
# Sensors for testing state reading
|
||||||
|
sensor:
|
||||||
|
- platform: template
|
||||||
|
name: "Test Sensor"
|
||||||
|
id: test_sensor
|
||||||
|
lambda: return 42.0;
|
||||||
|
update_interval: 0.1s
|
||||||
|
|
||||||
|
# Home Assistant sensor that reads external state
|
||||||
|
- platform: homeassistant
|
||||||
|
name: "HA Temperature"
|
||||||
|
entity_id: sensor.external_temperature
|
||||||
|
id: ha_temperature
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "HA Temperature state updated: %.1f"
|
||||||
|
args: ['x']
|
||||||
|
|
||||||
|
# Test multiple HA state sensors
|
||||||
|
- platform: homeassistant
|
||||||
|
name: "HA Humidity"
|
||||||
|
entity_id: sensor.external_humidity
|
||||||
|
id: ha_humidity
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "HA Humidity state updated: %.1f"
|
||||||
|
args: ['x']
|
||||||
|
|
||||||
|
# Binary sensor from Home Assistant
|
||||||
|
binary_sensor:
|
||||||
|
- platform: homeassistant
|
||||||
|
name: "HA Motion"
|
||||||
|
entity_id: binary_sensor.external_motion
|
||||||
|
id: ha_motion
|
||||||
|
on_state:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "HA Motion state changed: %s"
|
||||||
|
args: ['x ? "ON" : "OFF"']
|
||||||
|
|
||||||
|
# Text sensor from Home Assistant
|
||||||
|
text_sensor:
|
||||||
|
- platform: homeassistant
|
||||||
|
name: "HA Weather"
|
||||||
|
entity_id: weather.home
|
||||||
|
attribute: condition
|
||||||
|
id: ha_weather
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "HA Weather condition updated: %s"
|
||||||
|
args: ['x.c_str()']
|
||||||
|
|
||||||
|
# Test empty state handling
|
||||||
|
- platform: homeassistant
|
||||||
|
name: "HA Empty State"
|
||||||
|
entity_id: sensor.nonexistent_sensor
|
||||||
|
id: ha_empty_state
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "HA Empty state updated: %s"
|
||||||
|
args: ['x.c_str()']
|
||||||
|
|
||||||
|
# Number component for testing HA number control
|
||||||
|
number:
|
||||||
|
- platform: template
|
||||||
|
name: "HA Controlled Number"
|
||||||
|
id: ha_number
|
||||||
|
min_value: 0
|
||||||
|
max_value: 100
|
||||||
|
step: 1
|
||||||
|
optimistic: true
|
||||||
|
set_action:
|
||||||
|
- logger.log:
|
||||||
|
format: "Setting HA number to: %.1f"
|
||||||
|
args: ['x']
|
||||||
|
- homeassistant.action:
|
||||||
|
action: input_number.set_value
|
||||||
|
data:
|
||||||
|
entity_id: input_number.test_number
|
||||||
|
value: !lambda 'return to_string(x);'
|
||||||
|
|
||||||
|
# Switch component for testing HA switch control
|
||||||
|
switch:
|
||||||
|
- platform: template
|
||||||
|
name: "HA Controlled Switch"
|
||||||
|
id: ha_switch
|
||||||
|
optimistic: true
|
||||||
|
turn_on_action:
|
||||||
|
- logger.log: "Toggling HA switch: switch.test_switch ON"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: switch.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: switch.test_switch
|
||||||
|
turn_off_action:
|
||||||
|
- logger.log: "Toggling HA switch: switch.test_switch OFF"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: switch.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: switch.test_switch
|
||||||
|
|
||||||
|
# Buttons for testing various service call scenarios
|
||||||
|
button:
|
||||||
|
# Test 1: Basic service call with static values
|
||||||
|
- platform: template
|
||||||
|
name: "Test Basic Service"
|
||||||
|
id: test_basic_service
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Sending HomeAssistant service call: light.turn_off"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: light.test_light
|
||||||
|
- logger.log: "Service data: entity_id=light.test_light"
|
||||||
|
|
||||||
|
# Test 2: Service call with templated/lambda values (main bug fix test)
|
||||||
|
- platform: template
|
||||||
|
name: "Test Templated Service"
|
||||||
|
id: test_templated_service
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Testing templated service call"
|
||||||
|
- lambda: |-
|
||||||
|
int brightness_percent = id(test_brightness);
|
||||||
|
std::string computed = to_string(brightness_percent * 255 / 100);
|
||||||
|
ESP_LOGI("test", "Lambda computed value: %s", computed.c_str());
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: light.test_light
|
||||||
|
# This creates a temporary string - the main test case
|
||||||
|
brightness: !lambda 'return to_string(id(test_brightness) * 255 / 100);'
|
||||||
|
data_template:
|
||||||
|
color_name: !lambda 'return id(test_string);'
|
||||||
|
variables:
|
||||||
|
transition: !lambda 'return "2.5";'
|
||||||
|
|
||||||
|
# Test 3: Service call with empty string values
|
||||||
|
- platform: template
|
||||||
|
name: "Test Empty String Service"
|
||||||
|
id: test_empty_string_service
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Testing empty string values"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: notify.test
|
||||||
|
data:
|
||||||
|
message: "Test message"
|
||||||
|
title: ""
|
||||||
|
data_template:
|
||||||
|
target: !lambda 'return "";'
|
||||||
|
variables:
|
||||||
|
sound: !lambda 'return "";'
|
||||||
|
|
||||||
|
- logger.log: "Empty value for key: title"
|
||||||
|
- logger.log: "Empty value for key: target"
|
||||||
|
- logger.log: "Empty value for key: sound"
|
||||||
|
|
||||||
|
# Test 4: Service call with multiple data fields
|
||||||
|
- platform: template
|
||||||
|
name: "Test Multiple Fields Service"
|
||||||
|
id: test_multiple_fields_service
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Testing multiple data fields"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: climate.set_temperature
|
||||||
|
data:
|
||||||
|
entity_id: climate.test_climate
|
||||||
|
temperature: "22"
|
||||||
|
hvac_mode: "heat"
|
||||||
|
data_template:
|
||||||
|
target_temp_high: !lambda 'return "24";'
|
||||||
|
target_temp_low: !lambda 'return "20";'
|
||||||
|
variables:
|
||||||
|
preset_mode: !lambda 'return "comfort";'
|
||||||
|
|
||||||
|
# Test 5: Complex lambda with string operations
|
||||||
|
- platform: template
|
||||||
|
name: "Test Complex Lambda Service"
|
||||||
|
id: test_complex_lambda_service
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Testing complex lambda expressions"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: script.test_script
|
||||||
|
data:
|
||||||
|
entity_id: !lambda |-
|
||||||
|
std::string base = "light.";
|
||||||
|
std::string room = "living_room";
|
||||||
|
return base + room;
|
||||||
|
brightness_pct: !lambda |-
|
||||||
|
float sensor_val = id(test_sensor).state;
|
||||||
|
int pct = (int)(sensor_val * 2.38); // 42 * 2.38 ≈ 100
|
||||||
|
return to_string(pct);
|
||||||
|
data_template:
|
||||||
|
message: !lambda |-
|
||||||
|
char buffer[50];
|
||||||
|
snprintf(buffer, sizeof(buffer), "Sensor: %.1f, Time: %02d:%02d",
|
||||||
|
id(test_sensor).state,
|
||||||
|
id(homeassistant_time).now().hour,
|
||||||
|
id(homeassistant_time).now().minute);
|
||||||
|
return std::string(buffer);
|
||||||
|
|
||||||
|
# Test 6: Service with only empty strings to verify size calculation
|
||||||
|
- platform: template
|
||||||
|
name: "Test All Empty Service"
|
||||||
|
id: test_all_empty_service
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Testing all empty string values"
|
||||||
|
- homeassistant.action:
|
||||||
|
action: test.empty
|
||||||
|
data:
|
||||||
|
field1: ""
|
||||||
|
field2: ""
|
||||||
|
data_template:
|
||||||
|
field3: !lambda 'return "";'
|
||||||
|
variables:
|
||||||
|
field4: !lambda 'return "";'
|
||||||
|
- logger.log: "All empty service call completed"
|
||||||
|
|
||||||
|
# Test 7: Rapid successive service calls
|
||||||
|
- platform: template
|
||||||
|
name: "Test Rapid Service Calls"
|
||||||
|
id: test_rapid_service_calls
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Testing rapid service calls"
|
||||||
|
- repeat:
|
||||||
|
count: 5
|
||||||
|
then:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: counter.increment
|
||||||
|
data:
|
||||||
|
entity_id: counter.test_counter
|
||||||
|
- delay: 10ms
|
||||||
|
- logger.log: "Rapid service calls completed"
|
||||||
|
|
||||||
|
# Test 8: Log current HA states
|
||||||
|
- platform: template
|
||||||
|
name: "Test Read HA States"
|
||||||
|
id: test_read_ha_states
|
||||||
|
on_press:
|
||||||
|
- logger.log: "Reading current HA states"
|
||||||
|
- lambda: |-
|
||||||
|
if (id(ha_temperature).has_state()) {
|
||||||
|
ESP_LOGI("test", "Current HA Temperature: %.1f", id(ha_temperature).state);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI("test", "HA Temperature has no state");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id(ha_humidity).has_state()) {
|
||||||
|
ESP_LOGI("test", "Current HA Humidity: %.1f", id(ha_humidity).state);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI("test", "HA Humidity has no state");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI("test", "Current HA Motion: %s", id(ha_motion).state ? "ON" : "OFF");
|
||||||
|
|
||||||
|
if (id(ha_weather).has_state()) {
|
||||||
|
ESP_LOGI("test", "Current HA Weather: %s", id(ha_weather).state.c_str());
|
||||||
|
} else {
|
||||||
|
ESP_LOGI("test", "HA Weather has no state");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id(ha_empty_state).has_state()) {
|
||||||
|
ESP_LOGI("test", "HA Empty State value: %s", id(ha_empty_state).state.c_str());
|
||||||
|
} else {
|
||||||
|
ESP_LOGI("test", "HA Empty State has no value (expected)");
|
||||||
|
}
|
@@ -210,6 +210,15 @@ sensor:
|
|||||||
name: "Test Sensor 50"
|
name: "Test Sensor 50"
|
||||||
lambda: return 50.0;
|
lambda: return 50.0;
|
||||||
update_interval: 0.1s
|
update_interval: 0.1s
|
||||||
|
# Temperature sensor for the thermostat
|
||||||
|
- platform: template
|
||||||
|
name: "Temperature Sensor"
|
||||||
|
id: temp_sensor
|
||||||
|
lambda: return 22.5;
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
device_class: temperature
|
||||||
|
state_class: measurement
|
||||||
|
update_interval: 5s
|
||||||
|
|
||||||
# Mixed entity types for comprehensive batching test
|
# Mixed entity types for comprehensive batching test
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
@@ -285,6 +294,50 @@ valve:
|
|||||||
stop_action:
|
stop_action:
|
||||||
- logger.log: "Valve stopping"
|
- logger.log: "Valve stopping"
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: template
|
||||||
|
id: heater_output
|
||||||
|
type: binary
|
||||||
|
write_action:
|
||||||
|
- logger.log: "Heater output changed"
|
||||||
|
- platform: template
|
||||||
|
id: cooler_output
|
||||||
|
type: binary
|
||||||
|
write_action:
|
||||||
|
- logger.log: "Cooler output changed"
|
||||||
|
|
||||||
|
climate:
|
||||||
|
- platform: thermostat
|
||||||
|
name: "Test Thermostat"
|
||||||
|
sensor: temp_sensor
|
||||||
|
default_preset: Home
|
||||||
|
on_boot_restore_from: default_preset
|
||||||
|
min_heating_off_time: 1s
|
||||||
|
min_heating_run_time: 1s
|
||||||
|
min_cooling_off_time: 1s
|
||||||
|
min_cooling_run_time: 1s
|
||||||
|
min_idle_time: 1s
|
||||||
|
heat_action:
|
||||||
|
- output.turn_on: heater_output
|
||||||
|
cool_action:
|
||||||
|
- output.turn_on: cooler_output
|
||||||
|
idle_action:
|
||||||
|
- output.turn_off: heater_output
|
||||||
|
- output.turn_off: cooler_output
|
||||||
|
preset:
|
||||||
|
- name: Home
|
||||||
|
default_target_temperature_low: 20
|
||||||
|
default_target_temperature_high: 24
|
||||||
|
mode: heat_cool
|
||||||
|
- name: Away
|
||||||
|
default_target_temperature_low: 16
|
||||||
|
default_target_temperature_high: 26
|
||||||
|
mode: heat_cool
|
||||||
|
- name: Sleep
|
||||||
|
default_target_temperature_low: 18
|
||||||
|
default_target_temperature_high: 22
|
||||||
|
mode: heat_cool
|
||||||
|
|
||||||
alarm_control_panel:
|
alarm_control_panel:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Test Alarm"
|
name: "Test Alarm"
|
||||||
|
@@ -37,6 +37,15 @@ globals:
|
|||||||
- id: multiple_same_name_counter
|
- id: multiple_same_name_counter
|
||||||
type: int
|
type: int
|
||||||
initial_value: '0'
|
initial_value: '0'
|
||||||
|
- id: const_char_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: static_char_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: mixed_cancel_result
|
||||||
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
|
|
||||||
# Using different component types for each test to ensure isolation
|
# Using different component types for each test to ensure isolation
|
||||||
sensor:
|
sensor:
|
||||||
@@ -229,6 +238,56 @@ script:
|
|||||||
return RetryResult::RETRY;
|
return RetryResult::RETRY;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
# Test 8: Const char* overloads
|
||||||
|
- logger.log: "=== Test 8: Const char* overloads ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(simple_retry_sensor);
|
||||||
|
|
||||||
|
// Test 8a: Direct string literal
|
||||||
|
App.scheduler.set_retry(component, "const_char_test", 30, 2,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(const_char_retry_counter)++;
|
||||||
|
ESP_LOGI("test", "Const char retry %d", id(const_char_retry_counter));
|
||||||
|
return RetryResult::DONE;
|
||||||
|
});
|
||||||
|
|
||||||
|
# Test 9: Static const char* variable
|
||||||
|
- logger.log: "=== Test 9: Static const char* ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(backoff_retry_sensor);
|
||||||
|
|
||||||
|
static const char* STATIC_NAME = "static_retry_test";
|
||||||
|
App.scheduler.set_retry(component, STATIC_NAME, 20, 1,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(static_char_retry_counter)++;
|
||||||
|
ESP_LOGI("test", "Static const char retry %d", id(static_char_retry_counter));
|
||||||
|
return RetryResult::DONE;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel with same static const char*
|
||||||
|
App.scheduler.set_timeout(component, "static_cancel", 10, []() {
|
||||||
|
static const char* STATIC_NAME = "static_retry_test";
|
||||||
|
bool result = App.scheduler.cancel_retry(id(backoff_retry_sensor), STATIC_NAME);
|
||||||
|
ESP_LOGI("test", "Static cancel result: %s", result ? "true" : "false");
|
||||||
|
});
|
||||||
|
|
||||||
|
# Test 10: Mix string and const char* cancel
|
||||||
|
- logger.log: "=== Test 10: Mixed string/const char* ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(immediate_done_sensor);
|
||||||
|
|
||||||
|
// Set with std::string
|
||||||
|
std::string str_name = "mixed_retry";
|
||||||
|
App.scheduler.set_retry(component, str_name, 40, 3,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
ESP_LOGI("test", "Mixed retry - should be cancelled");
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel with const char*
|
||||||
|
id(mixed_cancel_result) = App.scheduler.cancel_retry(component, "mixed_retry");
|
||||||
|
ESP_LOGI("test", "Mixed cancel result: %s", id(mixed_cancel_result) ? "true" : "false");
|
||||||
|
|
||||||
# Wait for all tests to complete before reporting
|
# Wait for all tests to complete before reporting
|
||||||
- delay: 500ms
|
- delay: 500ms
|
||||||
|
|
||||||
@@ -242,4 +301,7 @@ script:
|
|||||||
ESP_LOGI("test", "Empty name retry counter: %d (expected 1-2)", id(empty_name_retry_counter));
|
ESP_LOGI("test", "Empty name retry counter: %d (expected 1-2)", id(empty_name_retry_counter));
|
||||||
ESP_LOGI("test", "Component retry counter: %d (expected 2)", id(script_retry_counter));
|
ESP_LOGI("test", "Component retry counter: %d (expected 2)", id(script_retry_counter));
|
||||||
ESP_LOGI("test", "Multiple same name counter: %d (expected 20+)", id(multiple_same_name_counter));
|
ESP_LOGI("test", "Multiple same name counter: %d (expected 20+)", id(multiple_same_name_counter));
|
||||||
|
ESP_LOGI("test", "Const char retry counter: %d (expected 1)", id(const_char_retry_counter));
|
||||||
|
ESP_LOGI("test", "Static char retry counter: %d (expected 1)", id(static_char_retry_counter));
|
||||||
|
ESP_LOGI("test", "Mixed cancel result: %s (expected true)", id(mixed_cancel_result) ? "true" : "false");
|
||||||
ESP_LOGI("test", "All retry tests completed");
|
ESP_LOGI("test", "All retry tests completed");
|
||||||
|
305
tests/integration/test_api_homeassistant.py
Normal file
305
tests/integration/test_api_homeassistant.py
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
"""Integration test for Home Assistant API functionality.
|
||||||
|
|
||||||
|
Tests:
|
||||||
|
- Home Assistant service calls with templated values (main bug fix)
|
||||||
|
- Service calls with empty string values
|
||||||
|
- Home Assistant state reading (sensors, binary sensors, text sensors)
|
||||||
|
- Home Assistant number and switch component control
|
||||||
|
- Complex lambda expressions and string handling
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
|
||||||
|
from aioesphomeapi import HomeassistantServiceCall
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_api_homeassistant(
|
||||||
|
yaml_config: str,
|
||||||
|
run_compiled: RunCompiledFunction,
|
||||||
|
api_client_connected: APIClientConnectedFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Comprehensive test for Home Assistant API functionality."""
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
|
||||||
|
# Create futures for patterns that capture values
|
||||||
|
lambda_computed_future = loop.create_future()
|
||||||
|
ha_temp_state_future = loop.create_future()
|
||||||
|
ha_humidity_state_future = loop.create_future()
|
||||||
|
ha_motion_state_future = loop.create_future()
|
||||||
|
ha_weather_state_future = loop.create_future()
|
||||||
|
|
||||||
|
# State update futures
|
||||||
|
temp_update_future = loop.create_future()
|
||||||
|
humidity_update_future = loop.create_future()
|
||||||
|
motion_update_future = loop.create_future()
|
||||||
|
weather_update_future = loop.create_future()
|
||||||
|
|
||||||
|
# Number future
|
||||||
|
ha_number_future = loop.create_future()
|
||||||
|
|
||||||
|
tests_complete_future = loop.create_future()
|
||||||
|
|
||||||
|
# Patterns to match in logs - only keeping patterns that capture values
|
||||||
|
lambda_computed_pattern = re.compile(r"Lambda computed value: (\d+)")
|
||||||
|
ha_temp_state_pattern = re.compile(r"Current HA Temperature: ([\d.]+)")
|
||||||
|
ha_humidity_state_pattern = re.compile(r"Current HA Humidity: ([\d.]+)")
|
||||||
|
ha_motion_state_pattern = re.compile(r"Current HA Motion: (ON|OFF)")
|
||||||
|
ha_weather_state_pattern = re.compile(r"Current HA Weather: (\w+)")
|
||||||
|
|
||||||
|
# State update patterns
|
||||||
|
temp_update_pattern = re.compile(r"HA Temperature state updated: ([\d.]+)")
|
||||||
|
humidity_update_pattern = re.compile(r"HA Humidity state updated: ([\d.]+)")
|
||||||
|
motion_update_pattern = re.compile(r"HA Motion state changed: (ON|OFF)")
|
||||||
|
weather_update_pattern = re.compile(r"HA Weather condition updated: (\w+)")
|
||||||
|
|
||||||
|
# Number pattern
|
||||||
|
ha_number_pattern = re.compile(r"Setting HA number to: ([\d.]+)")
|
||||||
|
|
||||||
|
tests_complete_pattern = re.compile(r"=== All tests completed ===")
|
||||||
|
|
||||||
|
# Track all log lines for debugging
|
||||||
|
log_lines: list[str] = []
|
||||||
|
|
||||||
|
# Track HomeAssistant service calls
|
||||||
|
ha_service_calls: list[HomeassistantServiceCall] = []
|
||||||
|
|
||||||
|
# Service call futures organized by service name
|
||||||
|
service_call_futures = {
|
||||||
|
"light.turn_off": loop.create_future(), # basic_service_call
|
||||||
|
"light.turn_on": loop.create_future(), # templated_service_call
|
||||||
|
"notify.test": loop.create_future(), # empty_string_service_call
|
||||||
|
"climate.set_temperature": loop.create_future(), # multiple_fields_service_call
|
||||||
|
"script.test_script": loop.create_future(), # complex_lambda_service_call
|
||||||
|
"test.empty": loop.create_future(), # all_empty_service_call
|
||||||
|
"input_number.set_value": loop.create_future(), # ha_number_service_call
|
||||||
|
"switch.turn_on": loop.create_future(), # ha_switch_on_service_call
|
||||||
|
"switch.turn_off": loop.create_future(), # ha_switch_off_service_call
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_service_call(service_call: HomeassistantServiceCall) -> None:
|
||||||
|
"""Capture HomeAssistant service calls."""
|
||||||
|
ha_service_calls.append(service_call)
|
||||||
|
|
||||||
|
# Check if this service call is one we're waiting for
|
||||||
|
if service_call.service in service_call_futures:
|
||||||
|
future = service_call_futures[service_call.service]
|
||||||
|
if not future.done():
|
||||||
|
future.set_result(service_call)
|
||||||
|
|
||||||
|
def check_output(line: str) -> None:
|
||||||
|
"""Check log output for expected messages."""
|
||||||
|
log_lines.append(line)
|
||||||
|
|
||||||
|
# Check for patterns that capture values
|
||||||
|
if not lambda_computed_future.done():
|
||||||
|
match = lambda_computed_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
lambda_computed_future.set_result(match.group(1))
|
||||||
|
elif not ha_temp_state_future.done() and ha_temp_state_pattern.search(line):
|
||||||
|
ha_temp_state_future.set_result(line)
|
||||||
|
elif not ha_humidity_state_future.done() and ha_humidity_state_pattern.search(
|
||||||
|
line
|
||||||
|
):
|
||||||
|
ha_humidity_state_future.set_result(line)
|
||||||
|
elif not ha_motion_state_future.done() and ha_motion_state_pattern.search(line):
|
||||||
|
ha_motion_state_future.set_result(line)
|
||||||
|
elif not ha_weather_state_future.done() and ha_weather_state_pattern.search(
|
||||||
|
line
|
||||||
|
):
|
||||||
|
ha_weather_state_future.set_result(line)
|
||||||
|
|
||||||
|
# Check state update patterns
|
||||||
|
elif not temp_update_future.done() and temp_update_pattern.search(line):
|
||||||
|
temp_update_future.set_result(line)
|
||||||
|
elif not humidity_update_future.done() and humidity_update_pattern.search(line):
|
||||||
|
humidity_update_future.set_result(line)
|
||||||
|
elif not motion_update_future.done() and motion_update_pattern.search(line):
|
||||||
|
motion_update_future.set_result(line)
|
||||||
|
elif not weather_update_future.done() and weather_update_pattern.search(line):
|
||||||
|
weather_update_future.set_result(line)
|
||||||
|
|
||||||
|
# Check number pattern
|
||||||
|
elif not ha_number_future.done() and ha_number_pattern.search(line):
|
||||||
|
match = ha_number_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
ha_number_future.set_result(match.group(1))
|
||||||
|
|
||||||
|
elif not tests_complete_future.done() and tests_complete_pattern.search(line):
|
||||||
|
tests_complete_future.set_result(True)
|
||||||
|
|
||||||
|
# Run with log monitoring
|
||||||
|
async with (
|
||||||
|
run_compiled(yaml_config, line_callback=check_output),
|
||||||
|
api_client_connected() as client,
|
||||||
|
):
|
||||||
|
# Verify device info
|
||||||
|
device_info = await client.device_info()
|
||||||
|
assert device_info is not None
|
||||||
|
assert device_info.name == "test-ha-api"
|
||||||
|
|
||||||
|
# Subscribe to HomeAssistant service calls
|
||||||
|
client.subscribe_service_calls(on_service_call)
|
||||||
|
|
||||||
|
# Send some Home Assistant states for our sensors to read
|
||||||
|
client.send_home_assistant_state("sensor.external_temperature", "", "22.5")
|
||||||
|
client.send_home_assistant_state("sensor.external_humidity", "", "65.0")
|
||||||
|
client.send_home_assistant_state("binary_sensor.external_motion", "", "ON")
|
||||||
|
client.send_home_assistant_state("weather.home", "condition", "sunny")
|
||||||
|
|
||||||
|
# List entities and services
|
||||||
|
_, services = await client.list_entities_services()
|
||||||
|
|
||||||
|
# Find the trigger service
|
||||||
|
trigger_service = next(
|
||||||
|
(s for s in services if s.name == "trigger_all_tests"), None
|
||||||
|
)
|
||||||
|
assert trigger_service is not None, "trigger_all_tests service not found"
|
||||||
|
|
||||||
|
# Execute all tests
|
||||||
|
client.execute_service(trigger_service, {})
|
||||||
|
|
||||||
|
# Wait for all tests to complete with appropriate timeouts
|
||||||
|
try:
|
||||||
|
# Templated service test - the main bug fix
|
||||||
|
computed_value = await asyncio.wait_for(lambda_computed_future, timeout=5.0)
|
||||||
|
# Verify the computed value is reasonable (75 * 255 / 100 = 191.25 -> 191)
|
||||||
|
assert computed_value in ["191", "192"], (
|
||||||
|
f"Unexpected computed value: {computed_value}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check state reads - verify we received the mocked values
|
||||||
|
temp_line = await asyncio.wait_for(ha_temp_state_future, timeout=5.0)
|
||||||
|
assert "Current HA Temperature: 22.5" in temp_line
|
||||||
|
|
||||||
|
humidity_line = await asyncio.wait_for(
|
||||||
|
ha_humidity_state_future, timeout=5.0
|
||||||
|
)
|
||||||
|
assert "Current HA Humidity: 65.0" in humidity_line
|
||||||
|
|
||||||
|
motion_line = await asyncio.wait_for(ha_motion_state_future, timeout=5.0)
|
||||||
|
assert "Current HA Motion: ON" in motion_line
|
||||||
|
|
||||||
|
weather_line = await asyncio.wait_for(ha_weather_state_future, timeout=5.0)
|
||||||
|
assert "Current HA Weather: sunny" in weather_line
|
||||||
|
|
||||||
|
# Number test
|
||||||
|
number_value = await asyncio.wait_for(ha_number_future, timeout=5.0)
|
||||||
|
assert number_value == "42.5", f"Unexpected number value: {number_value}"
|
||||||
|
|
||||||
|
# Wait for completion
|
||||||
|
await asyncio.wait_for(tests_complete_future, timeout=5.0)
|
||||||
|
|
||||||
|
# Now verify the protobuf messages
|
||||||
|
# 1. Basic service call
|
||||||
|
basic_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["light.turn_off"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert basic_call.service == "light.turn_off"
|
||||||
|
assert "entity_id" in basic_call.data, (
|
||||||
|
f"entity_id not found in data: {basic_call.data}"
|
||||||
|
)
|
||||||
|
assert basic_call.data["entity_id"] == "light.test_light", (
|
||||||
|
f"Wrong entity_id: {basic_call.data['entity_id']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Templated service call - verify the temporary string issue is fixed
|
||||||
|
templated_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["light.turn_on"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert templated_call.service == "light.turn_on"
|
||||||
|
# Check the computed brightness value
|
||||||
|
assert "brightness" in templated_call.data
|
||||||
|
assert templated_call.data["brightness"] in ["191", "192"] # 75 * 255 / 100
|
||||||
|
# Check data_template
|
||||||
|
assert "color_name" in templated_call.data_template
|
||||||
|
assert templated_call.data_template["color_name"] == "test_value"
|
||||||
|
# Check variables
|
||||||
|
assert "transition" in templated_call.variables
|
||||||
|
assert templated_call.variables["transition"] == "2.5"
|
||||||
|
|
||||||
|
# 3. Empty string service call
|
||||||
|
empty_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["notify.test"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert empty_call.service == "notify.test"
|
||||||
|
# Verify empty strings are properly handled
|
||||||
|
assert "title" in empty_call.data and empty_call.data["title"] == ""
|
||||||
|
assert (
|
||||||
|
"target" in empty_call.data_template
|
||||||
|
and empty_call.data_template["target"] == ""
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"sound" in empty_call.variables and empty_call.variables["sound"] == ""
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. Multiple fields service call
|
||||||
|
multi_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["climate.set_temperature"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert multi_call.service == "climate.set_temperature"
|
||||||
|
assert multi_call.data["temperature"] == "22"
|
||||||
|
assert multi_call.data["hvac_mode"] == "heat"
|
||||||
|
assert multi_call.data_template["target_temp_high"] == "24"
|
||||||
|
assert multi_call.variables["preset_mode"] == "comfort"
|
||||||
|
|
||||||
|
# 5. Complex lambda service call
|
||||||
|
complex_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["script.test_script"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert complex_call.service == "script.test_script"
|
||||||
|
assert complex_call.data["entity_id"] == "light.living_room"
|
||||||
|
assert complex_call.data["brightness_pct"] == "99" # 42 * 2.38 ≈ 99
|
||||||
|
# Check message includes sensor value
|
||||||
|
assert "message" in complex_call.data_template
|
||||||
|
assert "Sensor: 42.0" in complex_call.data_template["message"]
|
||||||
|
|
||||||
|
# 6. All empty service call
|
||||||
|
all_empty_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["test.empty"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert all_empty_call.service == "test.empty"
|
||||||
|
# All fields should be empty strings
|
||||||
|
assert all(v == "" for v in all_empty_call.data.values())
|
||||||
|
assert all(v == "" for v in all_empty_call.data_template.values())
|
||||||
|
assert all(v == "" for v in all_empty_call.variables.values())
|
||||||
|
|
||||||
|
# 7. HA Number service call
|
||||||
|
number_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["input_number.set_value"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert number_call.service == "input_number.set_value"
|
||||||
|
assert number_call.data["entity_id"] == "input_number.test_number"
|
||||||
|
# The value might be formatted with trailing zeros
|
||||||
|
assert float(number_call.data["value"]) == 42.5
|
||||||
|
|
||||||
|
# 8. HA Switch service calls
|
||||||
|
switch_on_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["switch.turn_on"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert switch_on_call.service == "switch.turn_on"
|
||||||
|
assert switch_on_call.data["entity_id"] == "switch.test_switch"
|
||||||
|
|
||||||
|
switch_off_call = await asyncio.wait_for(
|
||||||
|
service_call_futures["switch.turn_off"], timeout=2.0
|
||||||
|
)
|
||||||
|
assert switch_off_call.service == "switch.turn_off"
|
||||||
|
assert switch_off_call.data["entity_id"] == "switch.test_switch"
|
||||||
|
|
||||||
|
except TimeoutError as e:
|
||||||
|
# Show recent log lines for debugging
|
||||||
|
recent_logs = "\n".join(log_lines[-20:])
|
||||||
|
service_calls_summary = "\n".join(
|
||||||
|
f"- {call.service}" for call in ha_service_calls
|
||||||
|
)
|
||||||
|
pytest.fail(
|
||||||
|
f"Test timed out waiting for expected log pattern or service call. Error: {e}\n\n"
|
||||||
|
f"Recent log lines:\n{recent_logs}\n\n"
|
||||||
|
f"Received service calls:\n{service_calls_summary}"
|
||||||
|
)
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from aioesphomeapi import EntityState, SensorState
|
from aioesphomeapi import ClimateInfo, EntityState, SensorState
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
@@ -70,3 +70,22 @@ async def test_host_mode_many_entities(
|
|||||||
assert len(sensor_states) >= 50, (
|
assert len(sensor_states) >= 50, (
|
||||||
f"Expected at least 50 sensor states, got {len(sensor_states)}"
|
f"Expected at least 50 sensor states, got {len(sensor_states)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get entity info to verify climate entity details
|
||||||
|
entities = await client.list_entities_services()
|
||||||
|
climate_infos = [e for e in entities[0] if isinstance(e, ClimateInfo)]
|
||||||
|
assert len(climate_infos) >= 1, "Expected at least 1 climate entity"
|
||||||
|
|
||||||
|
climate_info = climate_infos[0]
|
||||||
|
# Verify the thermostat has presets
|
||||||
|
assert len(climate_info.supported_presets) > 0, (
|
||||||
|
"Expected climate to have presets"
|
||||||
|
)
|
||||||
|
# The thermostat platform uses standard presets (Home, Away, Sleep)
|
||||||
|
# which should be transmitted properly without string copies
|
||||||
|
|
||||||
|
# Verify specific presets exist
|
||||||
|
preset_names = [p.name for p in climate_info.supported_presets]
|
||||||
|
assert "HOME" in preset_names, f"Expected 'HOME' preset, got {preset_names}"
|
||||||
|
assert "AWAY" in preset_names, f"Expected 'AWAY' preset, got {preset_names}"
|
||||||
|
assert "SLEEP" in preset_names, f"Expected 'SLEEP' preset, got {preset_names}"
|
||||||
|
@@ -23,6 +23,9 @@ async def test_scheduler_retry_test(
|
|||||||
empty_name_retry_done = asyncio.Event()
|
empty_name_retry_done = asyncio.Event()
|
||||||
component_retry_done = asyncio.Event()
|
component_retry_done = asyncio.Event()
|
||||||
multiple_name_done = asyncio.Event()
|
multiple_name_done = asyncio.Event()
|
||||||
|
const_char_done = asyncio.Event()
|
||||||
|
static_char_done = asyncio.Event()
|
||||||
|
mixed_cancel_done = asyncio.Event()
|
||||||
test_complete = asyncio.Event()
|
test_complete = asyncio.Event()
|
||||||
|
|
||||||
# Track retry counts
|
# Track retry counts
|
||||||
@@ -33,16 +36,20 @@ async def test_scheduler_retry_test(
|
|||||||
empty_name_retry_count = 0
|
empty_name_retry_count = 0
|
||||||
component_retry_count = 0
|
component_retry_count = 0
|
||||||
multiple_name_count = 0
|
multiple_name_count = 0
|
||||||
|
const_char_retry_count = 0
|
||||||
|
static_char_retry_count = 0
|
||||||
|
|
||||||
# Track specific test results
|
# Track specific test results
|
||||||
cancel_result = None
|
cancel_result = None
|
||||||
empty_cancel_result = None
|
empty_cancel_result = None
|
||||||
|
mixed_cancel_result = None
|
||||||
backoff_intervals = []
|
backoff_intervals = []
|
||||||
|
|
||||||
def on_log_line(line: str) -> None:
|
def on_log_line(line: str) -> None:
|
||||||
nonlocal simple_retry_count, backoff_retry_count, immediate_done_count
|
nonlocal simple_retry_count, backoff_retry_count, immediate_done_count
|
||||||
nonlocal cancel_retry_count, empty_name_retry_count, component_retry_count
|
nonlocal cancel_retry_count, empty_name_retry_count, component_retry_count
|
||||||
nonlocal multiple_name_count, cancel_result, empty_cancel_result
|
nonlocal multiple_name_count, const_char_retry_count, static_char_retry_count
|
||||||
|
nonlocal cancel_result, empty_cancel_result, mixed_cancel_result
|
||||||
|
|
||||||
# Strip ANSI color codes
|
# Strip ANSI color codes
|
||||||
clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line)
|
clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line)
|
||||||
@@ -106,6 +113,27 @@ async def test_scheduler_retry_test(
|
|||||||
if multiple_name_count >= 20:
|
if multiple_name_count >= 20:
|
||||||
multiple_name_done.set()
|
multiple_name_done.set()
|
||||||
|
|
||||||
|
# Const char retry test
|
||||||
|
elif "Const char retry" in clean_line:
|
||||||
|
if match := re.search(r"Const char retry (\d+)", clean_line):
|
||||||
|
const_char_retry_count = int(match.group(1))
|
||||||
|
const_char_done.set()
|
||||||
|
|
||||||
|
# Static const char retry test
|
||||||
|
elif "Static const char retry" in clean_line:
|
||||||
|
if match := re.search(r"Static const char retry (\d+)", clean_line):
|
||||||
|
static_char_retry_count = int(match.group(1))
|
||||||
|
static_char_done.set()
|
||||||
|
|
||||||
|
elif "Static cancel result:" in clean_line:
|
||||||
|
# This is part of test 9, but we don't track it separately
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Mixed cancel test
|
||||||
|
elif "Mixed cancel result:" in clean_line:
|
||||||
|
mixed_cancel_result = "true" in clean_line
|
||||||
|
mixed_cancel_done.set()
|
||||||
|
|
||||||
# Test completion
|
# Test completion
|
||||||
elif "All retry tests completed" in clean_line:
|
elif "All retry tests completed" in clean_line:
|
||||||
test_complete.set()
|
test_complete.set()
|
||||||
@@ -227,6 +255,40 @@ async def test_scheduler_retry_test(
|
|||||||
f"Expected multiple name count >= 20 (second retry only), got {multiple_name_count}"
|
f"Expected multiple name count >= 20 (second retry only), got {multiple_name_count}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Wait for const char retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(const_char_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Const char retry test did not complete. Count: {const_char_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert const_char_retry_count == 1, (
|
||||||
|
f"Expected 1 const char retry call, got {const_char_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for static char retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(static_char_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Static char retry test did not complete. Count: {static_char_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert static_char_retry_count == 1, (
|
||||||
|
f"Expected 1 static char retry call, got {static_char_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for mixed cancel test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(mixed_cancel_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Mixed cancel test did not complete")
|
||||||
|
|
||||||
|
assert mixed_cancel_result is True, (
|
||||||
|
"Mixed string/const char cancel should have succeeded"
|
||||||
|
)
|
||||||
|
|
||||||
# Wait for test completion
|
# Wait for test completion
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(test_complete.wait(), timeout=1.0)
|
await asyncio.wait_for(test_complete.wait(), timeout=1.0)
|
||||||
|
Reference in New Issue
Block a user