1
0
mirror of https://github.com/esphome/esphome.git synced 2025-06-14 22:36:58 +02:00

Replace API deferred queue with efficient message batching system (#9012)

This commit is contained in:
J. Nick Koston 2025-06-10 18:49:15 -05:00 committed by GitHub
parent 1467b704b8
commit 2ed5611a08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 2832 additions and 1669 deletions

View File

@ -49,6 +49,7 @@ SERVICE_ARG_NATIVE_TYPES = {
"string[]": cg.std_vector.template(cg.std_string),
}
CONF_ENCRYPTION = "encryption"
CONF_BATCH_DELAY = "batch_delay"
def validate_encryption_key(value):
@ -109,6 +110,9 @@ CONFIG_SCHEMA = cv.All(
): ACTIONS_SCHEMA,
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
cv.Optional(CONF_ENCRYPTION): _encryption_schema,
cv.Optional(
CONF_BATCH_DELAY, default="100ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True
),
@ -129,6 +133,7 @@ async def to_code(config):
cg.add(var.set_port(config[CONF_PORT]))
cg.add(var.set_password(config[CONF_PASSWORD]))
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
for conf in config.get(CONF_ACTIONS, []):
template_args = []

View File

@ -3,6 +3,8 @@
#include <cerrno>
#include <cinttypes>
#include <utility>
#include <functional>
#include <limits>
#include "esphome/components/network/util.h"
#include "esphome/core/application.h"
#include "esphome/core/entity_base.h"
@ -29,40 +31,8 @@ namespace api {
static const char *const TAG = "api.connection";
static const int ESP32_CAMERA_STOP_STREAM = 5000;
// helper for allowing only unique entries in the queue
void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t send_message) {
DeferredMessage item(source, send_message);
auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
[&item](const DeferredMessage &test) -> bool { return test == item; });
if (iter != this->deferred_queue_.end()) {
(*iter) = item;
} else {
this->deferred_queue_.push_back(item);
}
}
void DeferredMessageQueue::process_queue() {
while (!deferred_queue_.empty()) {
DeferredMessage &de = deferred_queue_.front();
if ((this->api_connection_->*(de.send_message_))(de.source_)) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
} else {
break;
}
}
}
void DeferredMessageQueue::defer(void *source, send_message_t send_message) {
this->dmq_push_back_with_dedup_(source, send_message);
}
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), deferred_message_queue_(this), initial_state_iterator_(this), list_entities_iterator_(this) {
this->proto_write_buffer_.reserve(64);
: parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
auto noise_ctx = parent->get_noise_ctx();
if (noise_ctx->has_psk()) {
@ -78,6 +48,9 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
#error "No frame helper defined"
#endif
}
uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
void APIConnection::start() {
this->last_traffic_ = App.get_loop_component_start_time();
@ -166,8 +139,10 @@ void APIConnection::loop() {
}
}
if (!this->deferred_message_queue_.empty() && this->helper_->can_write_without_blocking()) {
this->deferred_message_queue_.process_queue();
// Process deferred batch if scheduled
if (this->deferred_batch_.batch_scheduled &&
App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
this->process_batch_();
}
if (!this->list_entities_iterator_.completed())
@ -186,7 +161,7 @@ void APIConnection::loop() {
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
ESP_LOGVV(TAG, "Sending keepalive PING");
this->sent_ping_ = this->send_ping_request(PingRequest());
this->sent_ping_ = this->send_message(PingRequest());
if (!this->sent_ping_) {
this->next_ping_retry_ = now + ping_retry_interval;
this->ping_retries_++;
@ -205,18 +180,7 @@ void APIConnection::loop() {
#ifdef USE_ESP32_CAMERA
if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
// Message will use 8 more bytes than the minimum size, and typical
// MTU is 1500. Sometimes users will see as low as 1460 MTU.
// If its IPv6 the header is 40 bytes, and if its IPv4
// the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
// available for the payload. But we also need to add the size of
// the protobuf overhead, which is 8 bytes.
//
// To be safe we pick 1390 bytes as the maximum size
// to send in one go. This is the maximum size of a single packet
// that can be sent over the network.
// This is to avoid fragmentation of the packet.
uint32_t to_send = std::min((size_t) 1390, this->image_reader_.available());
uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available());
bool done = this->image_reader_.available() == to_send;
uint32_t msg_size = 0;
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
@ -254,7 +218,7 @@ void APIConnection::loop() {
resp.entity_id = it.entity_id;
resp.attribute = it.attribute.value();
resp.once = it.once;
if (this->send_subscribe_home_assistant_state_response(resp)) {
if (this->send_message(resp)) {
state_subs_at_++;
}
}
@ -279,45 +243,74 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
this->remove_ = true;
}
// Encodes a message to the buffer and returns the total number of bytes used,
// including header and footer overhead. Returns 0 if the message doesn't fit.
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
// Calculate size
uint32_t size = 0;
msg.calculate_size(size);
// Calculate total size with padding for buffer allocation
uint16_t total_size =
static_cast<uint16_t>(size) + conn->helper_->frame_header_padding() + conn->helper_->frame_footer_size();
// Check if it fits
if (total_size > remaining_size) {
return 0; // Doesn't fit
}
// Allocate exact buffer space needed (just the payload, not the overhead)
ProtoWriteBuffer buffer =
is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size);
// Encode directly into buffer
msg.encode(buffer);
return total_size;
}
#ifdef USE_BINARY_SENSOR
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
return this->send_state_with_value_(binary_sensor, &APIConnection::try_send_binary_sensor_state_,
&APIConnection::try_send_binary_sensor_state_, state);
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
BinarySensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
this->send_info_(static_cast<EntityBase *>(binary_sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_binary_sensor_info_));
this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info,
ListEntitiesBinarySensorResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor) {
return this->try_send_binary_sensor_state_(binary_sensor, binary_sensor->state);
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
BinarySensorStateResponse resp;
resp.state = binary_sensor->state;
resp.missing_state = !binary_sensor->has_state();
resp.key = binary_sensor->get_object_id_hash();
return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state) {
BinarySensorStateResponse msg;
msg.state = state;
msg.missing_state = !binary_sensor->has_state();
msg.key = binary_sensor->get_object_id_hash();
return this->send_binary_sensor_state_response(msg);
}
bool APIConnection::try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor) {
uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
ListEntitiesBinarySensorResponse msg;
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(binary_sensor), msg,
&APIConnection::send_list_entities_binary_sensor_response);
fill_entity_info_base(binary_sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_COVER
bool APIConnection::send_cover_state(cover::Cover *cover) {
return this->send_state_(static_cast<EntityBase *>(cover),
reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_state_));
return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_cover_info(cover::Cover *cover) {
this->send_info_(static_cast<EntityBase *>(cover),
reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_info_));
this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *cover = static_cast<cover::Cover *>(entity);
CoverStateResponse msg;
auto traits = cover->get_traits();
msg.legacy_state =
@ -327,9 +320,11 @@ bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
msg.tilt = cover->tilt;
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
msg.key = cover->get_object_id_hash();
return this->send_cover_state_response(msg);
return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_cover_info_(cover::Cover *cover) {
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *cover = static_cast<cover::Cover *>(entity);
ListEntitiesCoverResponse msg;
auto traits = cover->get_traits();
msg.assumed_state = traits.get_is_assumed_state();
@ -338,8 +333,8 @@ bool APIConnection::try_send_cover_info_(cover::Cover *cover) {
msg.supports_stop = traits.get_supports_stop();
msg.device_class = cover->get_device_class();
msg.unique_id = get_default_unique_id("cover", cover);
return this->try_send_entity_info_(static_cast<EntityBase *>(cover), msg,
&APIConnection::send_list_entities_cover_response);
fill_entity_info_base(cover, msg);
return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
cover::Cover *cover = App.get_cover_by_key(msg.key);
@ -372,14 +367,14 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
#ifdef USE_FAN
bool APIConnection::send_fan_state(fan::Fan *fan) {
return this->send_state_(static_cast<EntityBase *>(fan),
reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_state_));
return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_fan_info(fan::Fan *fan) {
this->send_info_(static_cast<EntityBase *>(fan),
reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_info_));
this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *fan = static_cast<fan::Fan *>(entity);
FanStateResponse msg;
auto traits = fan->get_traits();
msg.state = fan->state;
@ -393,9 +388,11 @@ bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
if (traits.supports_preset_modes())
msg.preset_mode = fan->preset_mode;
msg.key = fan->get_object_id_hash();
return this->send_fan_state_response(msg);
return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_fan_info_(fan::Fan *fan) {
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *fan = static_cast<fan::Fan *>(entity);
ListEntitiesFanResponse msg;
auto traits = fan->get_traits();
msg.supports_oscillation = traits.supports_oscillation();
@ -405,8 +402,8 @@ bool APIConnection::try_send_fan_info_(fan::Fan *fan) {
for (auto const &preset : traits.supported_preset_modes())
msg.supported_preset_modes.push_back(preset);
msg.unique_id = get_default_unique_id("fan", fan);
return this->try_send_entity_info_(static_cast<EntityBase *>(fan), msg,
&APIConnection::send_list_entities_fan_response);
fill_entity_info_base(fan, msg);
return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
fan::Fan *fan = App.get_fan_by_key(msg.key);
@ -432,14 +429,14 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
#ifdef USE_LIGHT
bool APIConnection::send_light_state(light::LightState *light) {
return this->send_state_(static_cast<EntityBase *>(light),
reinterpret_cast<send_message_t>(&APIConnection::try_send_light_state_));
return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_light_info(light::LightState *light) {
this->send_info_(static_cast<EntityBase *>(light),
reinterpret_cast<send_message_t>(&APIConnection::try_send_light_info_));
this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_light_state_(light::LightState *light) {
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *light = static_cast<light::LightState *>(entity);
LightStateResponse resp;
auto traits = light->get_traits();
auto values = light->remote_values;
@ -458,9 +455,11 @@ bool APIConnection::try_send_light_state_(light::LightState *light) {
if (light->supports_effects())
resp.effect = light->get_effect_name();
resp.key = light->get_object_id_hash();
return this->send_light_state_response(resp);
return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_light_info_(light::LightState *light) {
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *light = static_cast<light::LightState *>(entity);
ListEntitiesLightResponse msg;
auto traits = light->get_traits();
for (auto mode : traits.get_supported_color_modes())
@ -483,8 +482,8 @@ bool APIConnection::try_send_light_info_(light::LightState *light) {
}
}
msg.unique_id = get_default_unique_id("light", light);
return this->try_send_entity_info_(static_cast<EntityBase *>(light), msg,
&APIConnection::send_list_entities_light_response);
fill_entity_info_base(light, msg);
return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::light_command(const LightCommandRequest &msg) {
light::LightState *light = App.get_light_by_key(msg.key);
@ -524,26 +523,26 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
#endif
#ifdef USE_SENSOR
bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
return this->send_state_with_value_(sensor, &APIConnection::try_send_sensor_state_,
&APIConnection::try_send_sensor_state_, state);
bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
this->send_info_(static_cast<EntityBase *>(sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_sensor_info_));
this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor) {
return this->try_send_sensor_state_(sensor, sensor->state);
}
bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor, float state) {
SensorStateResponse resp;
resp.state = state;
resp.missing_state = !sensor->has_state();
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *sensor = static_cast<sensor::Sensor *>(entity);
SensorStateResponse resp;
resp.state = sensor->state;
resp.missing_state = !sensor->has_state();
resp.key = sensor->get_object_id_hash();
return this->send_sensor_state_response(resp);
return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) {
uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *sensor = static_cast<sensor::Sensor *>(entity);
ListEntitiesSensorResponse msg;
msg.unit_of_measurement = sensor->get_unit_of_measurement();
msg.accuracy_decimals = sensor->get_accuracy_decimals();
@ -553,37 +552,37 @@ bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) {
msg.unique_id = sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("sensor", sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(sensor), msg,
&APIConnection::send_list_entities_sensor_response);
fill_entity_info_base(sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_SWITCH
bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
return this->send_state_with_value_(a_switch, &APIConnection::try_send_switch_state_,
&APIConnection::try_send_switch_state_, state);
bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_switch_info(switch_::Switch *a_switch) {
this->send_info_(static_cast<EntityBase *>(a_switch),
reinterpret_cast<send_message_t>(&APIConnection::try_send_switch_info_));
this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch) {
return this->try_send_switch_state_(a_switch, a_switch->state);
}
bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch, bool state) {
SwitchStateResponse resp;
resp.state = state;
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_switch = static_cast<switch_::Switch *>(entity);
SwitchStateResponse resp;
resp.state = a_switch->state;
resp.key = a_switch->get_object_id_hash();
return this->send_switch_state_response(resp);
return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_switch_info_(switch_::Switch *a_switch) {
uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_switch = static_cast<switch_::Switch *>(entity);
ListEntitiesSwitchResponse msg;
msg.assumed_state = a_switch->assumed_state();
msg.device_class = a_switch->get_device_class();
msg.unique_id = get_default_unique_id("switch", a_switch);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_switch), msg,
&APIConnection::send_list_entities_switch_response);
fill_entity_info_base(a_switch, msg);
return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
@ -599,46 +598,44 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
#endif
#ifdef USE_TEXT_SENSOR
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) {
return this->send_state_with_value_(text_sensor, &APIConnection::try_send_text_sensor_state_,
&APIConnection::try_send_text_sensor_state_, std::move(state));
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state,
TextSensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
this->send_info_(static_cast<EntityBase *>(text_sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_text_sensor_info_));
this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info,
ListEntitiesTextSensorResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor) {
return this->try_send_text_sensor_state_(text_sensor, text_sensor->state);
}
bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state) {
TextSensorStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !text_sensor->has_state();
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
TextSensorStateResponse resp;
resp.state = text_sensor->state;
resp.missing_state = !text_sensor->has_state();
resp.key = text_sensor->get_object_id_hash();
return this->send_text_sensor_state_response(resp);
return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor) {
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
ListEntitiesTextSensorResponse msg;
msg.device_class = text_sensor->get_device_class();
msg.unique_id = text_sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(text_sensor), msg,
&APIConnection::send_list_entities_text_sensor_response);
fill_entity_info_base(text_sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_CLIMATE
bool APIConnection::send_climate_state(climate::Climate *climate) {
return this->send_state_(static_cast<EntityBase *>(climate),
reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_state_));
return this->schedule_message_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_climate_info(climate::Climate *climate) {
this->send_info_(static_cast<EntityBase *>(climate),
reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_info_));
}
bool APIConnection::try_send_climate_state_(climate::Climate *climate) {
uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *climate = static_cast<climate::Climate *>(entity);
ClimateStateResponse resp;
resp.key = climate->get_object_id_hash();
auto traits = climate->get_traits();
@ -667,9 +664,14 @@ bool APIConnection::try_send_climate_state_(climate::Climate *climate) {
resp.current_humidity = climate->current_humidity;
if (traits.get_supports_target_humidity())
resp.target_humidity = climate->target_humidity;
return this->send_climate_state_response(resp);
return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_climate_info_(climate::Climate *climate) {
void APIConnection::send_climate_info(climate::Climate *climate) {
this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *climate = static_cast<climate::Climate *>(entity);
ListEntitiesClimateResponse msg;
auto traits = climate->get_traits();
msg.supports_current_temperature = traits.get_supports_current_temperature();
@ -697,8 +699,8 @@ bool APIConnection::try_send_climate_info_(climate::Climate *climate) {
for (auto swing_mode : traits.get_supported_swing_modes())
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
msg.unique_id = get_default_unique_id("climate", climate);
return this->try_send_entity_info_(static_cast<EntityBase *>(climate), msg,
&APIConnection::send_list_entities_climate_response);
fill_entity_info_base(climate, msg);
return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
climate::Climate *climate = App.get_climate_by_key(msg.key);
@ -731,26 +733,26 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
#endif
#ifdef USE_NUMBER
bool APIConnection::send_number_state(number::Number *number, float state) {
return this->send_state_with_value_(number, &APIConnection::try_send_number_state_,
&APIConnection::try_send_number_state_, state);
bool APIConnection::send_number_state(number::Number *number) {
return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_number_info(number::Number *number) {
this->send_info_(static_cast<EntityBase *>(number),
reinterpret_cast<send_message_t>(&APIConnection::try_send_number_info_));
this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_number_state_(number::Number *number) {
return this->try_send_number_state_(number, number->state);
}
bool APIConnection::try_send_number_state_(number::Number *number, float state) {
NumberStateResponse resp;
resp.state = state;
resp.missing_state = !number->has_state();
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *number = static_cast<number::Number *>(entity);
NumberStateResponse resp;
resp.state = number->state;
resp.missing_state = !number->has_state();
resp.key = number->get_object_id_hash();
return this->send_number_state_response(resp);
return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_number_info_(number::Number *number) {
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *number = static_cast<number::Number *>(entity);
ListEntitiesNumberResponse msg;
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
@ -759,8 +761,8 @@ bool APIConnection::try_send_number_info_(number::Number *number) {
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
msg.unique_id = get_default_unique_id("number", number);
return this->try_send_entity_info_(static_cast<EntityBase *>(number), msg,
&APIConnection::send_list_entities_number_response);
fill_entity_info_base(number, msg);
return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::number_command(const NumberCommandRequest &msg) {
number::Number *number = App.get_number_by_key(msg.key);
@ -775,28 +777,29 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
#ifdef USE_DATETIME_DATE
bool APIConnection::send_date_state(datetime::DateEntity *date) {
return this->send_state_(static_cast<EntityBase *>(date),
reinterpret_cast<send_message_t>(&APIConnection::try_send_date_state_));
return this->schedule_message_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_date_info(datetime::DateEntity *date) {
this->send_info_(static_cast<EntityBase *>(date),
reinterpret_cast<send_message_t>(&APIConnection::try_send_date_info_));
}
bool APIConnection::try_send_date_state_(datetime::DateEntity *date) {
uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *date = static_cast<datetime::DateEntity *>(entity);
DateStateResponse resp;
resp.missing_state = !date->has_state();
resp.year = date->year;
resp.month = date->month;
resp.day = date->day;
resp.key = date->get_object_id_hash();
return this->send_date_state_response(resp);
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_date_info_(datetime::DateEntity *date) {
void APIConnection::send_date_info(datetime::DateEntity *date) {
this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *date = static_cast<datetime::DateEntity *>(entity);
ListEntitiesDateResponse msg;
msg.unique_id = get_default_unique_id("date", date);
return this->try_send_entity_info_(static_cast<EntityBase *>(date), msg,
&APIConnection::send_list_entities_date_response);
fill_entity_info_base(date, msg);
return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::date_command(const DateCommandRequest &msg) {
datetime::DateEntity *date = App.get_date_by_key(msg.key);
@ -811,28 +814,29 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
#ifdef USE_DATETIME_TIME
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
return this->send_state_(static_cast<EntityBase *>(time),
reinterpret_cast<send_message_t>(&APIConnection::try_send_time_state_));
return this->schedule_message_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_time_info(datetime::TimeEntity *time) {
this->send_info_(static_cast<EntityBase *>(time),
reinterpret_cast<send_message_t>(&APIConnection::try_send_time_info_));
}
bool APIConnection::try_send_time_state_(datetime::TimeEntity *time) {
uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *time = static_cast<datetime::TimeEntity *>(entity);
TimeStateResponse resp;
resp.missing_state = !time->has_state();
resp.hour = time->hour;
resp.minute = time->minute;
resp.second = time->second;
resp.key = time->get_object_id_hash();
return this->send_time_state_response(resp);
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_time_info_(datetime::TimeEntity *time) {
void APIConnection::send_time_info(datetime::TimeEntity *time) {
this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *time = static_cast<datetime::TimeEntity *>(entity);
ListEntitiesTimeResponse msg;
msg.unique_id = get_default_unique_id("time", time);
return this->try_send_entity_info_(static_cast<EntityBase *>(time), msg,
&APIConnection::send_list_entities_time_response);
fill_entity_info_base(time, msg);
return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::time_command(const TimeCommandRequest &msg) {
datetime::TimeEntity *time = App.get_time_by_key(msg.key);
@ -847,29 +851,31 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
#ifdef USE_DATETIME_DATETIME
bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
return this->send_state_(static_cast<EntityBase *>(datetime),
reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_state_));
return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state,
DateTimeStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
this->send_info_(static_cast<EntityBase *>(datetime),
reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_info_));
}
bool APIConnection::try_send_datetime_state_(datetime::DateTimeEntity *datetime) {
uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
DateTimeStateResponse resp;
resp.missing_state = !datetime->has_state();
if (datetime->has_state()) {
ESPTime state = datetime->state_as_esptime();
resp.epoch_seconds = state.timestamp;
}
resp.key = datetime->get_object_id_hash();
return this->send_date_time_state_response(resp);
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_datetime_info_(datetime::DateTimeEntity *datetime) {
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
ListEntitiesDateTimeResponse msg;
msg.unique_id = get_default_unique_id("datetime", datetime);
return this->try_send_entity_info_(static_cast<EntityBase *>(datetime), msg,
&APIConnection::send_list_entities_date_time_response);
fill_entity_info_base(datetime, msg);
return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key);
@ -883,32 +889,34 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
#endif
#ifdef USE_TEXT
bool APIConnection::send_text_state(text::Text *text, std::string state) {
return this->send_state_with_value_(text, &APIConnection::try_send_text_state_, &APIConnection::try_send_text_state_,
std::move(state));
bool APIConnection::send_text_state(text::Text *text) {
return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_text_info(text::Text *text) {
this->send_info_(static_cast<EntityBase *>(text),
reinterpret_cast<send_message_t>(&APIConnection::try_send_text_info_));
this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_text_state_(text::Text *text) { return this->try_send_text_state_(text, text->state); }
bool APIConnection::try_send_text_state_(text::Text *text, std::string state) {
TextStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !text->has_state();
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text = static_cast<text::Text *>(entity);
TextStateResponse resp;
resp.state = text->state;
resp.missing_state = !text->has_state();
resp.key = text->get_object_id_hash();
return this->send_text_state_response(resp);
return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_text_info_(text::Text *text) {
uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text = static_cast<text::Text *>(entity);
ListEntitiesTextResponse msg;
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
msg.min_length = text->traits.get_min_length();
msg.max_length = text->traits.get_max_length();
msg.pattern = text->traits.get_pattern();
msg.unique_id = get_default_unique_id("text", text);
return this->try_send_entity_info_(static_cast<EntityBase *>(text), msg,
&APIConnection::send_list_entities_text_response);
fill_entity_info_base(text, msg);
return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::text_command(const TextCommandRequest &msg) {
text::Text *text = App.get_text_by_key(msg.key);
@ -922,32 +930,32 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
#endif
#ifdef USE_SELECT
bool APIConnection::send_select_state(select::Select *select, std::string state) {
return this->send_state_with_value_(select, &APIConnection::try_send_select_state_,
&APIConnection::try_send_select_state_, std::move(state));
bool APIConnection::send_select_state(select::Select *select) {
return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_select_info(select::Select *select) {
this->send_info_(static_cast<EntityBase *>(select),
reinterpret_cast<send_message_t>(&APIConnection::try_send_select_info_));
this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_select_state_(select::Select *select) {
return this->try_send_select_state_(select, select->state);
}
bool APIConnection::try_send_select_state_(select::Select *select, std::string state) {
SelectStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !select->has_state();
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *select = static_cast<select::Select *>(entity);
SelectStateResponse resp;
resp.state = select->state;
resp.missing_state = !select->has_state();
resp.key = select->get_object_id_hash();
return this->send_select_state_response(resp);
return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_select_info_(select::Select *select) {
uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *select = static_cast<select::Select *>(entity);
ListEntitiesSelectResponse msg;
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
msg.unique_id = get_default_unique_id("select", select);
return this->try_send_entity_info_(static_cast<EntityBase *>(select), msg,
&APIConnection::send_list_entities_select_response);
fill_entity_info_base(select, msg);
return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
select::Select *select = App.get_select_by_key(msg.key);
@ -962,15 +970,16 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
#ifdef USE_BUTTON
void esphome::api::APIConnection::send_button_info(button::Button *button) {
this->send_info_(static_cast<EntityBase *>(button),
reinterpret_cast<send_message_t>(&APIConnection::try_send_button_info_));
this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE);
}
bool esphome::api::APIConnection::try_send_button_info_(button::Button *button) {
uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *button = static_cast<button::Button *>(entity);
ListEntitiesButtonResponse msg;
msg.device_class = button->get_device_class();
msg.unique_id = get_default_unique_id("button", button);
return this->try_send_entity_info_(static_cast<EntityBase *>(button), msg,
&APIConnection::send_list_entities_button_response);
fill_entity_info_base(button, msg);
return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
@ -982,32 +991,32 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg
#endif
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
return this->send_state_with_value_(a_lock, &APIConnection::try_send_lock_state_,
&APIConnection::try_send_lock_state_, state);
bool APIConnection::send_lock_state(lock::Lock *a_lock) {
return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_lock_info(lock::Lock *a_lock) {
this->send_info_(static_cast<EntityBase *>(a_lock),
reinterpret_cast<send_message_t>(&APIConnection::try_send_lock_info_));
this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_lock_state_(lock::Lock *a_lock) {
return this->try_send_lock_state_(a_lock, a_lock->state);
}
bool APIConnection::try_send_lock_state_(lock::Lock *a_lock, lock::LockState state) {
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(state);
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_lock = static_cast<lock::Lock *>(entity);
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(a_lock->state);
resp.key = a_lock->get_object_id_hash();
return this->send_lock_state_response(resp);
return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_lock_info_(lock::Lock *a_lock) {
uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_lock = static_cast<lock::Lock *>(entity);
ListEntitiesLockResponse msg;
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
msg.unique_id = get_default_unique_id("lock", a_lock);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_lock), msg,
&APIConnection::send_list_entities_lock_response);
fill_entity_info_base(a_lock, msg);
return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
@ -1030,22 +1039,23 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
#ifdef USE_VALVE
bool APIConnection::send_valve_state(valve::Valve *valve) {
return this->send_state_(static_cast<EntityBase *>(valve),
reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_state_));
return this->schedule_message_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_valve_info(valve::Valve *valve) {
this->send_info_(static_cast<EntityBase *>(valve),
reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_info_));
}
bool APIConnection::try_send_valve_state_(valve::Valve *valve) {
uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *valve = static_cast<valve::Valve *>(entity);
ValveStateResponse resp;
resp.position = valve->position;
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
resp.key = valve->get_object_id_hash();
return this->send_valve_state_response(resp);
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_valve_info_(valve::Valve *valve) {
void APIConnection::send_valve_info(valve::Valve *valve) {
this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *valve = static_cast<valve::Valve *>(entity);
ListEntitiesValveResponse msg;
auto traits = valve->get_traits();
msg.device_class = valve->get_device_class();
@ -1053,8 +1063,8 @@ bool APIConnection::try_send_valve_info_(valve::Valve *valve) {
msg.supports_position = traits.get_supports_position();
msg.supports_stop = traits.get_supports_stop();
msg.unique_id = get_default_unique_id("valve", valve);
return this->try_send_entity_info_(static_cast<EntityBase *>(valve), msg,
&APIConnection::send_list_entities_valve_response);
fill_entity_info_base(valve, msg);
return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::valve_command(const ValveCommandRequest &msg) {
valve::Valve *valve = App.get_valve_by_key(msg.key);
@ -1072,14 +1082,12 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) {
#ifdef USE_MEDIA_PLAYER
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
return this->send_state_(static_cast<EntityBase *>(media_player),
reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_state_));
return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state,
MediaPlayerStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
this->send_info_(static_cast<EntityBase *>(media_player),
reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_info_));
}
bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *media_player) {
uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
MediaPlayerStateResponse resp;
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
? media_player::MEDIA_PLAYER_STATE_PLAYING
@ -1087,11 +1095,16 @@ bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *medi
resp.state = static_cast<enums::MediaPlayerState>(report_state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
resp.key = media_player->get_object_id_hash();
return this->send_media_player_state_response(resp);
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media_player) {
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
this->schedule_message_(media_player, &APIConnection::try_send_media_player_info,
ListEntitiesMediaPlayerResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
ListEntitiesMediaPlayerResponse msg;
auto traits = media_player->get_traits();
msg.supports_pause = traits.get_supports_pause();
@ -1105,8 +1118,8 @@ bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media
msg.supported_formats.push_back(media_format);
}
msg.unique_id = get_default_unique_id("media_player", media_player);
return this->try_send_entity_info_(static_cast<EntityBase *>(media_player), msg,
&APIConnection::send_list_entities_media_player_response);
fill_entity_info_base(media_player, msg);
return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
@ -1141,14 +1154,15 @@ void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage>
this->image_reader_.set_image(std::move(image));
}
void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
this->send_info_(static_cast<EntityBase *>(camera),
reinterpret_cast<send_message_t>(&APIConnection::try_send_camera_info_));
this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_camera_info_(esp32_camera::ESP32Camera *camera) {
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity);
ListEntitiesCameraResponse msg;
msg.unique_id = get_default_unique_id("camera", camera);
return this->try_send_entity_info_(static_cast<EntityBase *>(camera), msg,
&APIConnection::send_list_entities_camera_response);
fill_entity_info_base(camera, msg);
return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::camera_image(const CameraImageRequest &msg) {
if (esp32_camera::global_esp32_camera == nullptr)
@ -1191,9 +1205,9 @@ bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertiseme
manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
manufacturer_data.data.clear();
}
return this->send_bluetooth_le_advertisement_response(resp);
return this->send_message(resp);
}
return this->send_bluetooth_le_advertisement_response(msg);
return this->send_message(msg);
}
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
@ -1337,28 +1351,32 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
#ifdef USE_ALARM_CONTROL_PANEL
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
return this->send_state_(static_cast<EntityBase *>(a_alarm_control_panel),
reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_state_));
return this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
AlarmControlPanelStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
this->send_info_(static_cast<EntityBase *>(a_alarm_control_panel),
reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_info_));
}
bool APIConnection::try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
AlarmControlPanelStateResponse resp;
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
resp.key = a_alarm_control_panel->get_object_id_hash();
return this->send_alarm_control_panel_state_response(resp);
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info,
ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
ListEntitiesAlarmControlPanelResponse msg;
msg.supported_features = a_alarm_control_panel->get_supported_features();
msg.requires_code = a_alarm_control_panel->get_requires_code();
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_alarm_control_panel), msg,
&APIConnection::send_list_entities_alarm_control_panel_response);
fill_entity_info_base(a_alarm_control_panel, msg);
return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
@ -1395,45 +1413,40 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
#endif
#ifdef USE_EVENT
void APIConnection::send_event(event::Event *event, std::string event_type) {
this->send_state_with_value_(event, &APIConnection::try_send_event_, &APIConnection::try_send_event_,
std::move(event_type));
void APIConnection::send_event(event::Event *event, const std::string &event_type) {
this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE);
}
void APIConnection::send_event_info(event::Event *event) {
this->send_info_(static_cast<EntityBase *>(event),
reinterpret_cast<send_message_t>(&APIConnection::try_send_event_info_));
this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_event_(event::Event *event) {
return this->try_send_event_(event, *(event->last_event_type));
}
bool APIConnection::try_send_event_(event::Event *event, std::string event_type) {
uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
EventResponse resp;
resp.event_type = std::move(event_type);
resp.event_type = event_type;
resp.key = event->get_object_id_hash();
return this->send_event_response(resp);
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_event_info_(event::Event *event) {
uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *event = static_cast<event::Event *>(entity);
ListEntitiesEventResponse msg;
msg.device_class = event->get_device_class();
for (const auto &event_type : event->get_event_types())
msg.event_types.push_back(event_type);
msg.unique_id = get_default_unique_id("event", event);
return this->try_send_entity_info_(static_cast<EntityBase *>(event), msg,
&APIConnection::send_list_entities_event_response);
fill_entity_info_base(event, msg);
return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_UPDATE
bool APIConnection::send_update_state(update::UpdateEntity *update) {
return this->send_state_(static_cast<EntityBase *>(update),
reinterpret_cast<send_message_t>(&APIConnection::try_send_update_state_));
return this->schedule_message_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_update_info(update::UpdateEntity *update) {
this->send_info_(static_cast<EntityBase *>(update),
reinterpret_cast<send_message_t>(&APIConnection::try_send_update_info_));
}
bool APIConnection::try_send_update_state_(update::UpdateEntity *update) {
uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *update = static_cast<update::UpdateEntity *>(entity);
UpdateStateResponse resp;
resp.missing_state = !update->has_state();
if (update->has_state()) {
@ -1448,16 +1461,20 @@ bool APIConnection::try_send_update_state_(update::UpdateEntity *update) {
resp.release_summary = update->update_info.summary;
resp.release_url = update->update_info.release_url;
}
resp.key = update->get_object_id_hash();
return this->send_update_state_response(resp);
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_update_info_(update::UpdateEntity *update) {
void APIConnection::send_update_info(update::UpdateEntity *update) {
this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *update = static_cast<update::UpdateEntity *>(entity);
ListEntitiesUpdateResponse msg;
msg.device_class = update->get_device_class();
msg.unique_id = get_default_unique_id("update", update);
return this->try_send_entity_info_(static_cast<EntityBase *>(update), msg,
&APIConnection::send_list_entities_update_response);
fill_entity_info_base(update, msg);
return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::update_command(const UpdateCommandRequest &msg) {
update::UpdateEntity *update = App.get_update_by_key(msg.key);
@ -1651,7 +1668,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
}
return false;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
return false;
}
@ -1685,6 +1702,331 @@ void APIConnection::on_fatal_error() {
this->remove_ = true;
}
void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
// Check if we already have a message of this type for this entity
// This provides deduplication per entity/message_type combination
// O(n) but optimized for RAM and not performance.
for (auto &item : items) {
if (item.entity == entity && item.message_type == message_type) {
// Update the existing item with the new creator
item.creator = std::move(creator);
return;
}
}
// No existing item found, add new one
items.emplace_back(entity, std::move(creator), message_type);
}
bool APIConnection::schedule_batch_() {
if (!this->deferred_batch_.batch_scheduled) {
this->deferred_batch_.batch_scheduled = true;
this->deferred_batch_.batch_start_time = App.get_loop_component_start_time();
}
return true;
}
ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_);
this->batch_first_message_ = false;
return result;
}
void APIConnection::process_batch_() {
if (this->deferred_batch_.empty()) {
this->deferred_batch_.batch_scheduled = false;
return;
}
// Try to clear buffer first
if (!this->try_to_clear_buffer(true)) {
// Can't write now, we'll try again later
return;
}
size_t num_items = this->deferred_batch_.items.size();
// Fast path for single message - allocate exact size needed
if (num_items == 1) {
const auto &item = this->deferred_batch_.items[0];
// Let the creator calculate size and encode if it fits
uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true);
if (payload_size > 0 &&
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
this->deferred_batch_.clear();
} else if (payload_size == 0) {
// Message too large
ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
this->deferred_batch_.clear();
}
return;
}
// Pre-allocate storage for packet info
std::vector<PacketInfo> packet_info;
packet_info.reserve(num_items);
// Cache these values to avoid repeated virtual calls
const uint8_t header_padding = this->helper_->frame_header_padding();
const uint8_t footer_size = this->helper_->frame_footer_size();
// Initialize buffer and tracking variables
this->parent_->get_shared_buffer_ref().clear();
// Pre-calculate exact buffer size needed based on message types
uint32_t total_estimated_size = 0;
for (const auto &item : this->deferred_batch_.items) {
total_estimated_size += get_estimated_message_size(item.message_type);
}
// Calculate total overhead for all messages
uint32_t total_overhead = (header_padding + footer_size) * num_items;
// Reserve based on estimated size (much more accurate than 24-byte worst-case)
this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
this->batch_first_message_ = true;
size_t items_processed = 0;
uint32_t remaining_size = MAX_PACKET_SIZE;
// Track where each message's header padding begins in the buffer
// For plaintext: this is where the 6-byte header padding starts
// For noise: this is where the 7-byte header padding starts
// The actual message data follows after the header padding
uint32_t current_offset = 0;
// Process items and encode directly to buffer
for (const auto &item : this->deferred_batch_.items) {
// Try to encode message
// The creator will calculate overhead to determine if the message fits
uint16_t payload_size = item.creator(item.entity, this, remaining_size, false);
if (payload_size == 0) {
// Message won't fit, stop processing
break;
}
// Message was encoded successfully
// payload_size is header_padding + actual payload size + footer_size
uint16_t proto_payload_size = payload_size - header_padding - footer_size;
packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
// Update tracking variables
remaining_size -= payload_size;
// Calculate where the next message's header padding will start
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
items_processed++;
}
if (items_processed == 0) {
this->deferred_batch_.clear();
return;
}
// Add footer space for the last message (for Noise protocol MAC)
if (footer_size > 0) {
auto &shared_buf = this->parent_->get_shared_buffer_ref();
shared_buf.resize(shared_buf.size() + footer_size);
}
// Send all collected packets
APIError err =
this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
}
// Handle remaining items more efficiently
if (items_processed < this->deferred_batch_.items.size()) {
// Remove processed items from the beginning
this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(),
this->deferred_batch_.items.begin() + items_processed);
// Reschedule for remaining items
this->schedule_batch_();
} else {
// All items processed
this->deferred_batch_.clear();
}
}
uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) const {
switch (message_type_) {
case 0: // Function pointer
return data_.ptr(entity, conn, remaining_size, is_single);
#ifdef USE_EVENT
case EventResponse::MESSAGE_TYPE: {
auto *e = static_cast<event::Event *>(entity);
return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
}
#endif
default:
// Should not happen, return 0 to indicate no message
return 0;
}
}
uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
ListEntitiesDoneResponse resp;
return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) {
// Use generated ESTIMATED_SIZE constants from each message type
switch (message_type) {
#ifdef USE_BINARY_SENSOR
case BinarySensorStateResponse::MESSAGE_TYPE:
return BinarySensorStateResponse::ESTIMATED_SIZE;
case ListEntitiesBinarySensorResponse::MESSAGE_TYPE:
return ListEntitiesBinarySensorResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_SENSOR
case SensorStateResponse::MESSAGE_TYPE:
return SensorStateResponse::ESTIMATED_SIZE;
case ListEntitiesSensorResponse::MESSAGE_TYPE:
return ListEntitiesSensorResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_SWITCH
case SwitchStateResponse::MESSAGE_TYPE:
return SwitchStateResponse::ESTIMATED_SIZE;
case ListEntitiesSwitchResponse::MESSAGE_TYPE:
return ListEntitiesSwitchResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_TEXT_SENSOR
case TextSensorStateResponse::MESSAGE_TYPE:
return TextSensorStateResponse::ESTIMATED_SIZE;
case ListEntitiesTextSensorResponse::MESSAGE_TYPE:
return ListEntitiesTextSensorResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_NUMBER
case NumberStateResponse::MESSAGE_TYPE:
return NumberStateResponse::ESTIMATED_SIZE;
case ListEntitiesNumberResponse::MESSAGE_TYPE:
return ListEntitiesNumberResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_TEXT
case TextStateResponse::MESSAGE_TYPE:
return TextStateResponse::ESTIMATED_SIZE;
case ListEntitiesTextResponse::MESSAGE_TYPE:
return ListEntitiesTextResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_SELECT
case SelectStateResponse::MESSAGE_TYPE:
return SelectStateResponse::ESTIMATED_SIZE;
case ListEntitiesSelectResponse::MESSAGE_TYPE:
return ListEntitiesSelectResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_LOCK
case LockStateResponse::MESSAGE_TYPE:
return LockStateResponse::ESTIMATED_SIZE;
case ListEntitiesLockResponse::MESSAGE_TYPE:
return ListEntitiesLockResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_EVENT
case EventResponse::MESSAGE_TYPE:
return EventResponse::ESTIMATED_SIZE;
case ListEntitiesEventResponse::MESSAGE_TYPE:
return ListEntitiesEventResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_COVER
case CoverStateResponse::MESSAGE_TYPE:
return CoverStateResponse::ESTIMATED_SIZE;
case ListEntitiesCoverResponse::MESSAGE_TYPE:
return ListEntitiesCoverResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_FAN
case FanStateResponse::MESSAGE_TYPE:
return FanStateResponse::ESTIMATED_SIZE;
case ListEntitiesFanResponse::MESSAGE_TYPE:
return ListEntitiesFanResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_LIGHT
case LightStateResponse::MESSAGE_TYPE:
return LightStateResponse::ESTIMATED_SIZE;
case ListEntitiesLightResponse::MESSAGE_TYPE:
return ListEntitiesLightResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_CLIMATE
case ClimateStateResponse::MESSAGE_TYPE:
return ClimateStateResponse::ESTIMATED_SIZE;
case ListEntitiesClimateResponse::MESSAGE_TYPE:
return ListEntitiesClimateResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_ESP32_CAMERA
case ListEntitiesCameraResponse::MESSAGE_TYPE:
return ListEntitiesCameraResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_BUTTON
case ListEntitiesButtonResponse::MESSAGE_TYPE:
return ListEntitiesButtonResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_MEDIA_PLAYER
case MediaPlayerStateResponse::MESSAGE_TYPE:
return MediaPlayerStateResponse::ESTIMATED_SIZE;
case ListEntitiesMediaPlayerResponse::MESSAGE_TYPE:
return ListEntitiesMediaPlayerResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
case AlarmControlPanelStateResponse::MESSAGE_TYPE:
return AlarmControlPanelStateResponse::ESTIMATED_SIZE;
case ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE:
return ListEntitiesAlarmControlPanelResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_DATETIME_DATE
case DateStateResponse::MESSAGE_TYPE:
return DateStateResponse::ESTIMATED_SIZE;
case ListEntitiesDateResponse::MESSAGE_TYPE:
return ListEntitiesDateResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_DATETIME_TIME
case TimeStateResponse::MESSAGE_TYPE:
return TimeStateResponse::ESTIMATED_SIZE;
case ListEntitiesTimeResponse::MESSAGE_TYPE:
return ListEntitiesTimeResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_DATETIME_DATETIME
case DateTimeStateResponse::MESSAGE_TYPE:
return DateTimeStateResponse::ESTIMATED_SIZE;
case ListEntitiesDateTimeResponse::MESSAGE_TYPE:
return ListEntitiesDateTimeResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_VALVE
case ValveStateResponse::MESSAGE_TYPE:
return ValveStateResponse::ESTIMATED_SIZE;
case ListEntitiesValveResponse::MESSAGE_TYPE:
return ListEntitiesValveResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_UPDATE
case UpdateStateResponse::MESSAGE_TYPE:
return UpdateStateResponse::ESTIMATED_SIZE;
case ListEntitiesUpdateResponse::MESSAGE_TYPE:
return ListEntitiesUpdateResponse::ESTIMATED_SIZE;
#endif
case ListEntitiesServicesResponse::MESSAGE_TYPE:
return ListEntitiesServicesResponse::ESTIMATED_SIZE;
case ListEntitiesDoneResponse::MESSAGE_TYPE:
return ListEntitiesDoneResponse::ESTIMATED_SIZE;
default:
// Fallback for unknown message types
return 24;
}
}
} // namespace api
} // namespace esphome
#endif

View File

@ -11,6 +11,7 @@
#include "esphome/core/entity_base.h"
#include <vector>
#include <functional>
namespace esphome {
namespace api {
@ -18,49 +19,9 @@ namespace api {
// Keepalive timeout in milliseconds
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
using send_message_t = bool (APIConnection::*)(void *);
/*
This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that
will lazily publish that message. The two pointers allow dedup in the deferred queue if multiple publishes for the
same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a std::vector) is
the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry. Even
100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8
kB.
*/
class DeferredMessageQueue {
struct DeferredMessage {
friend class DeferredMessageQueue;
protected:
void *source_;
send_message_t send_message_;
public:
DeferredMessage(void *source, send_message_t send_message) : source_(source), send_message_(send_message) {}
bool operator==(const DeferredMessage &test) const {
return (source_ == test.source_ && send_message_ == test.send_message_);
}
} __attribute__((packed));
protected:
// vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
// footprint is more important than speed here)
std::vector<DeferredMessage> deferred_queue_;
APIConnection *api_connection_;
// helper for allowing only unique entries in the queue
void dmq_push_back_with_dedup_(void *source, send_message_t send_message);
public:
DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {}
void process_queue();
void defer(void *source, send_message_t send_message);
bool empty() const { return deferred_queue_.empty(); }
};
class APIConnection : public APIServerConnection {
public:
friend class APIServer;
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
virtual ~APIConnection();
@ -68,225 +29,105 @@ class APIConnection : public APIServerConnection {
void loop();
bool send_list_info_done() {
ListEntitiesDoneResponse resp;
return this->send_list_entities_done_response(resp);
return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done,
ListEntitiesDoneResponse::MESSAGE_TYPE);
}
#ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
protected:
bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor);
bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state);
bool try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor);
public:
#endif
#ifdef USE_COVER
bool send_cover_state(cover::Cover *cover);
void send_cover_info(cover::Cover *cover);
void cover_command(const CoverCommandRequest &msg) override;
protected:
bool try_send_cover_state_(cover::Cover *cover);
bool try_send_cover_info_(cover::Cover *cover);
public:
#endif
#ifdef USE_FAN
bool send_fan_state(fan::Fan *fan);
void send_fan_info(fan::Fan *fan);
void fan_command(const FanCommandRequest &msg) override;
protected:
bool try_send_fan_state_(fan::Fan *fan);
bool try_send_fan_info_(fan::Fan *fan);
public:
#endif
#ifdef USE_LIGHT
bool send_light_state(light::LightState *light);
void send_light_info(light::LightState *light);
void light_command(const LightCommandRequest &msg) override;
protected:
bool try_send_light_state_(light::LightState *light);
bool try_send_light_info_(light::LightState *light);
public:
#endif
#ifdef USE_SENSOR
bool send_sensor_state(sensor::Sensor *sensor, float state);
bool send_sensor_state(sensor::Sensor *sensor);
void send_sensor_info(sensor::Sensor *sensor);
protected:
bool try_send_sensor_state_(sensor::Sensor *sensor);
bool try_send_sensor_state_(sensor::Sensor *sensor, float state);
bool try_send_sensor_info_(sensor::Sensor *sensor);
public:
#endif
#ifdef USE_SWITCH
bool send_switch_state(switch_::Switch *a_switch, bool state);
bool send_switch_state(switch_::Switch *a_switch);
void send_switch_info(switch_::Switch *a_switch);
void switch_command(const SwitchCommandRequest &msg) override;
protected:
bool try_send_switch_state_(switch_::Switch *a_switch);
bool try_send_switch_state_(switch_::Switch *a_switch, bool state);
bool try_send_switch_info_(switch_::Switch *a_switch);
public:
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
protected:
bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor);
bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state);
bool try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor);
public:
#endif
#ifdef USE_ESP32_CAMERA
void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
void send_camera_info(esp32_camera::ESP32Camera *camera);
void camera_image(const CameraImageRequest &msg) override;
protected:
bool try_send_camera_info_(esp32_camera::ESP32Camera *camera);
public:
#endif
#ifdef USE_CLIMATE
bool send_climate_state(climate::Climate *climate);
void send_climate_info(climate::Climate *climate);
void climate_command(const ClimateCommandRequest &msg) override;
protected:
bool try_send_climate_state_(climate::Climate *climate);
bool try_send_climate_info_(climate::Climate *climate);
public:
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state);
bool send_number_state(number::Number *number);
void send_number_info(number::Number *number);
void number_command(const NumberCommandRequest &msg) override;
protected:
bool try_send_number_state_(number::Number *number);
bool try_send_number_state_(number::Number *number, float state);
bool try_send_number_info_(number::Number *number);
public:
#endif
#ifdef USE_DATETIME_DATE
bool send_date_state(datetime::DateEntity *date);
void send_date_info(datetime::DateEntity *date);
void date_command(const DateCommandRequest &msg) override;
protected:
bool try_send_date_state_(datetime::DateEntity *date);
bool try_send_date_info_(datetime::DateEntity *date);
public:
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state(datetime::TimeEntity *time);
void send_time_info(datetime::TimeEntity *time);
void time_command(const TimeCommandRequest &msg) override;
protected:
bool try_send_time_state_(datetime::TimeEntity *time);
bool try_send_time_info_(datetime::TimeEntity *time);
public:
#endif
#ifdef USE_DATETIME_DATETIME
bool send_datetime_state(datetime::DateTimeEntity *datetime);
void send_datetime_info(datetime::DateTimeEntity *datetime);
void datetime_command(const DateTimeCommandRequest &msg) override;
protected:
bool try_send_datetime_state_(datetime::DateTimeEntity *datetime);
bool try_send_datetime_info_(datetime::DateTimeEntity *datetime);
public:
#endif
#ifdef USE_TEXT
bool send_text_state(text::Text *text, std::string state);
bool send_text_state(text::Text *text);
void send_text_info(text::Text *text);
void text_command(const TextCommandRequest &msg) override;
protected:
bool try_send_text_state_(text::Text *text);
bool try_send_text_state_(text::Text *text, std::string state);
bool try_send_text_info_(text::Text *text);
public:
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select, std::string state);
bool send_select_state(select::Select *select);
void send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
protected:
bool try_send_select_state_(select::Select *select);
bool try_send_select_state_(select::Select *select, std::string state);
bool try_send_select_info_(select::Select *select);
public:
#endif
#ifdef USE_BUTTON
void send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
protected:
bool try_send_button_info_(button::Button *button);
public:
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_state(lock::Lock *a_lock);
void send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
protected:
bool try_send_lock_state_(lock::Lock *a_lock);
bool try_send_lock_state_(lock::Lock *a_lock, lock::LockState state);
bool try_send_lock_info_(lock::Lock *a_lock);
public:
#endif
#ifdef USE_VALVE
bool send_valve_state(valve::Valve *valve);
void send_valve_info(valve::Valve *valve);
void valve_command(const ValveCommandRequest &msg) override;
protected:
bool try_send_valve_state_(valve::Valve *valve);
bool try_send_valve_info_(valve::Valve *valve);
public:
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player);
void send_media_player_info(media_player::MediaPlayer *media_player);
void media_player_command(const MediaPlayerCommandRequest &msg) override;
protected:
bool try_send_media_player_state_(media_player::MediaPlayer *media_player);
bool try_send_media_player_info_(media_player::MediaPlayer *media_player);
public:
#endif
bool try_send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
if (!this->service_call_subscription_)
return;
this->send_homeassistant_service_response(call);
this->send_message(call);
}
#ifdef USE_BLUETOOTH_PROXY
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
@ -308,7 +149,7 @@ class APIConnection : public APIServerConnection {
#ifdef USE_HOMEASSISTANT_TIME
void send_time_request() {
GetTimeRequest req;
this->send_get_time_request(req);
this->send_message(req);
}
#endif
@ -328,36 +169,17 @@ class APIConnection : public APIServerConnection {
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
protected:
bool try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
bool try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
public:
#endif
#ifdef USE_EVENT
void send_event(event::Event *event, std::string event_type);
void send_event(event::Event *event, const std::string &event_type);
void send_event_info(event::Event *event);
protected:
bool try_send_event_(event::Event *event);
bool try_send_event_(event::Event *event, std::string event_type);
bool try_send_event_info_(event::Event *event);
public:
#endif
#ifdef USE_UPDATE
bool send_update_state(update::UpdateEntity *update);
void send_update_info(update::UpdateEntity *update);
void update_command(const UpdateCommandRequest &msg) override;
protected:
bool try_send_update_state_(update::UpdateEntity *update);
bool try_send_update_info_(update::UpdateEntity *update);
public:
#endif
void on_disconnect_response(const DisconnectResponse &value) override;
@ -407,102 +229,67 @@ class APIConnection : public APIServerConnection {
void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
// FIXME: ensure no recursive writes can happen
this->proto_write_buffer_.clear();
// Get header padding size - used for both reserve and insert
uint8_t header_padding = this->helper_->frame_header_padding();
// Get shared buffer from parent server
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
shared_buf.clear();
// Reserve space for header padding + message + footer
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
this->proto_write_buffer_.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
// Insert header padding bytes so message encoding starts at the correct position
this->proto_write_buffer_.insert(this->proto_write_buffer_.begin(), header_padding, 0);
return {&this->proto_write_buffer_};
shared_buf.insert(shared_buf.begin(), header_padding, 0);
return {&shared_buf};
}
// Prepare buffer for next message in batch
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
// Get reference to shared buffer (it maintains state between batch messages)
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
size_t current_size = shared_buf.size();
if (is_first_message) {
// For first message, initialize buffer with header padding
uint8_t header_padding = this->helper_->frame_header_padding();
shared_buf.clear();
shared_buf.reserve(message_size + header_padding);
shared_buf.resize(header_padding);
// Fill header padding with zeros
std::fill(shared_buf.begin(), shared_buf.end(), 0);
} else {
// For subsequent messages, add footer space for previous message and header for this message
uint8_t footer_size = this->helper_->frame_footer_size();
uint8_t header_padding = this->helper_->frame_header_padding();
// Reserve additional space for everything
shared_buf.reserve(current_size + footer_size + header_padding + message_size);
// Single resize to add both footer and header padding
size_t new_size = current_size + footer_size + header_padding;
shared_buf.resize(new_size);
// Fill the newly added bytes with zeros (footer + header padding)
std::fill(shared_buf.begin() + current_size, shared_buf.end(), 0);
}
return {&shared_buf};
}
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
std::string get_client_combined_info() const { return this->client_combined_info_; }
// Buffer allocator methods for batch processing
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
protected:
friend APIServer;
/**
* Generic send entity state method to reduce code duplication.
* Only attempts to build and send the message if the transmit buffer is available.
*
* This is the base version for entities that use their current state.
*
* @param entity The entity to send state for
* @param try_send_func The function that tries to send the state
* @return True on success or message deferred, false if subscription check failed
*/
bool send_state_(esphome::EntityBase *entity, send_message_t try_send_func) {
if (!this->state_subscription_)
return false;
if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) {
return true;
}
this->deferred_message_queue_.defer(entity, try_send_func);
return true;
}
/**
* Send entity state method that handles explicit state values.
* Only attempts to build and send the message if the transmit buffer is available.
*
* This method accepts a state parameter to be used instead of the entity's current state.
* It attempts to send the state with the provided value first, and if that fails due to buffer constraints,
* it defers the entity for later processing using the entity-only function.
*
* @tparam EntityT The entity type
* @tparam StateT Type of the state parameter
* @tparam Args Additional argument types (if any)
* @param entity The entity to send state for
* @param try_send_entity_func The function that tries to send the state with entity pointer only
* @param try_send_state_func The function that tries to send the state with entity and state parameters
* @param state The state value to send
* @param args Additional arguments to pass to the try_send_state_func
* @return True on success or message deferred, false if subscription check failed
*/
template<typename EntityT, typename StateT, typename... Args>
bool send_state_with_value_(EntityT *entity, bool (APIConnection::*try_send_entity_func)(EntityT *),
bool (APIConnection::*try_send_state_func)(EntityT *, StateT, Args...), StateT state,
Args... args) {
if (!this->state_subscription_)
return false;
if (this->try_to_clear_buffer(true) && (this->*try_send_state_func)(entity, state, args...)) {
return true;
}
this->deferred_message_queue_.defer(entity, reinterpret_cast<send_message_t>(try_send_entity_func));
return true;
}
/**
* Generic send entity info method to reduce code duplication.
* Only attempts to build and send the message if the transmit buffer is available.
*
* @param entity The entity to send info for
* @param try_send_func The function that tries to send the info
*/
void send_info_(esphome::EntityBase *entity, send_message_t try_send_func) {
if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) {
return;
}
this->deferred_message_queue_.defer(entity, try_send_func);
}
/**
* Generic function for generating entity info response messages.
* This is used to reduce duplication in the try_send_*_info functions.
*
* @param entity The entity to generate info for
* @param response The response object
* @param send_response_func Function pointer to send the response
* @return True if the message was sent successfully
*/
template<typename ResponseT>
bool try_send_entity_info_(esphome::EntityBase *entity, ResponseT &response,
bool (APIServerConnectionBase::*send_response_func)(const ResponseT &)) {
// Helper function to fill common entity fields
template<typename ResponseT> static void fill_entity_info_base(esphome::EntityBase *entity, ResponseT &response) {
// Set common fields that are shared by all entity types
response.key = entity->get_object_id_hash();
response.object_id = entity->get_object_id();
@ -514,12 +301,133 @@ class APIConnection : public APIServerConnection {
response.icon = entity->get_icon();
response.disabled_by_default = entity->is_disabled_by_default();
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
// Send the response using the provided send method
return (this->*send_response_func)(response);
}
bool send_(const void *buf, size_t len, bool force);
// Non-template helper to encode any ProtoMessage
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
uint32_t remaining_size, bool is_single);
#ifdef USE_BINARY_SENSOR
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_COVER
static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_FAN
static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_LIGHT
static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_SENSOR
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_SWITCH
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_TEXT_SENSOR
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_CLIMATE
static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_NUMBER
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_DATETIME_DATE
static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_DATETIME_TIME
static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_DATETIME_DATETIME
static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_TEXT
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_SELECT
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_BUTTON
static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_LOCK
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_VALVE
static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_MEDIA_PLAYER
static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_EVENT
static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_UPDATE
static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_ESP32_CAMERA
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
// Method for ListEntitiesDone batching
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
// Helper function to get estimated message size for buffer pre-allocation
static uint16_t get_estimated_message_size(uint16_t message_type);
enum class ConnectionState {
WAITING_FOR_HELLO,
@ -529,9 +437,6 @@ class APIConnection : public APIServerConnection {
bool remove_{false};
// Buffer used to encode proto messages
// Re-use to prevent allocations
std::vector<uint8_t> proto_write_buffer_;
std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_;
@ -552,10 +457,160 @@ class APIConnection : public APIServerConnection {
bool service_call_subscription_{false};
bool next_close_ = false;
APIServer *parent_;
DeferredMessageQueue deferred_message_queue_;
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
int state_subs_at_ = -1;
// Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
// Optimized MessageCreator class using union dispatch
class MessageCreator {
public:
// Constructor for function pointer (message_type = 0)
MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; }
// Constructor for string state capture
MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) {
data_.string_ptr = new std::string(value);
}
// Destructor
~MessageCreator() {
// Clean up string data for string-based message types
if (uses_string_data_()) {
delete data_.string_ptr;
}
}
// Copy constructor
MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) {
if (message_type_ == 0) {
data_.ptr = other.data_.ptr;
} else if (uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
} else {
data_ = other.data_; // For POD types
}
}
// Move constructor
MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) {
other.message_type_ = 0; // Reset other to function pointer type
other.data_.ptr = nullptr;
}
// Assignment operators (needed for batch deduplication)
MessageCreator &operator=(const MessageCreator &other) {
if (this != &other) {
// Clean up current string data if needed
if (uses_string_data_()) {
delete data_.string_ptr;
}
// Copy new data
message_type_ = other.message_type_;
if (other.message_type_ == 0) {
data_.ptr = other.data_.ptr;
} else if (other.uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
} else {
data_ = other.data_;
}
}
return *this;
}
MessageCreator &operator=(MessageCreator &&other) noexcept {
if (this != &other) {
// Clean up current string data if needed
if (uses_string_data_()) {
delete data_.string_ptr;
}
// Move data
message_type_ = other.message_type_;
data_ = other.data_;
// Reset other to safe state
other.message_type_ = 0;
other.data_.ptr = nullptr;
}
return *this;
}
// Call operator
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const;
private:
// Helper to check if this message type uses heap-allocated strings
bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; }
union CreatorData {
MessageCreatorPtr ptr; // 8 bytes
std::string *string_ptr; // 8 bytes
} data_; // 8 bytes
uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture)
};
// Generic batching mechanism for both state updates and entity info
struct DeferredBatch {
struct BatchItem {
EntityBase *entity; // Entity pointer
MessageCreator creator; // Function that creates the message when needed
uint16_t message_type; // Message type for overhead calculation
// Constructor for creating BatchItem
BatchItem(EntityBase *entity, MessageCreator creator, uint16_t message_type)
: entity(entity), creator(std::move(creator)), message_type(message_type) {}
};
std::vector<BatchItem> items;
uint32_t batch_start_time{0};
bool batch_scheduled{false};
DeferredBatch() {
// Pre-allocate capacity for typical batch sizes to avoid reallocation
items.reserve(8);
}
// Add item to the batch
void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type);
void clear() {
items.clear();
batch_scheduled = false;
batch_start_time = 0;
}
bool empty() const { return items.empty(); }
};
DeferredBatch deferred_batch_;
uint32_t get_batch_delay_ms_() const;
// Message will use 8 more bytes than the minimum size, and typical
// MTU is 1500. Sometimes users will see as low as 1460 MTU.
// If its IPv6 the header is 40 bytes, and if its IPv4
// the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
// available for the payload. But we also need to add the size of
// the protobuf overhead, which is 8 bytes.
//
// To be safe we pick 1390 bytes as the maximum size
// to send in one go. This is the maximum size of a single packet
// that can be sent over the network.
// This is to avoid fragmentation of the packet.
static constexpr size_t MAX_PACKET_SIZE = 1390; // MTU
bool schedule_batch_();
void process_batch_();
// State for batch buffer allocation
bool batch_first_message_{false};
// Helper function to schedule a deferred message with known message type
bool schedule_message_(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
this->deferred_batch_.add_item(entity, std::move(creator), message_type);
return this->schedule_batch_();
}
// Overload for function pointers (for info messages and current state reads)
bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) {
return schedule_message_(entity, MessageCreator(function_ptr), message_type);
}
};
} // namespace api

View File

@ -605,9 +605,21 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
return APIError::OK;
}
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
int err;
APIError aerr;
aerr = state_action_();
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
// Resize to include MAC space (required for Noise encryption)
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
// Use write_protobuf_packets with a single packet
std::vector<PacketInfo> packets;
packets.emplace_back(type, 0, payload_len);
return write_protobuf_packets(buffer, packets);
}
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) {
APIError aerr = state_action_();
if (aerr != APIError::OK) {
return aerr;
}
@ -616,56 +628,66 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf
return APIError::WOULD_BLOCK;
}
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
// Message data starts after padding
uint16_t payload_len = raw_buffer->size() - frame_header_padding_;
uint16_t padding = 0;
uint16_t msg_len = 4 + payload_len + padding;
// We need to resize to include MAC space, but we already reserved it in create_buffer
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
// Write the noise header in the padded area
// Buffer layout:
// [0] - 0x01 indicator byte
// [1-2] - Size of encrypted payload (filled after encryption)
// [3-4] - Message type (encrypted)
// [5-6] - Payload length (encrypted)
// [7...] - Actual payload data (encrypted)
uint8_t *buf_start = raw_buffer->data();
buf_start[0] = 0x01; // indicator
// buf_start[1], buf_start[2] to be set later after encryption
const uint8_t msg_offset = 3;
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
// payload data is already in the buffer starting at position 7
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
// The capacity parameter should be msg_len + frame_footer_size_ (MAC length) to allow space for encryption
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
if (packets.empty()) {
return APIError::OK;
}
uint16_t total_len = 3 + mbuf.size;
buf_start[1] = (uint8_t) (mbuf.size >> 8);
buf_start[2] = (uint8_t) mbuf.size;
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());
struct iovec iov;
// Point iov_base to the beginning of the buffer (no unused padding in Noise)
// We send the entire frame: indicator + size + encrypted(type + data_len + payload + MAC)
iov.iov_base = buf_start;
iov.iov_len = total_len;
// We need to encrypt each packet in place
for (const auto &packet : packets) {
uint16_t type = packet.message_type;
uint16_t offset = packet.offset;
uint16_t payload_len = packet.payload_size;
uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload
// write raw to not have two packets sent if NAGLE disabled
return this->write_raw_(&iov, 1);
// The buffer already has padding at offset
uint8_t *buf_start = raw_buffer->data() + offset;
// Write noise header
buf_start[0] = 0x01; // indicator
// buf_start[1], buf_start[2] to be set after encryption
// Write message header (to be encrypted)
const uint8_t msg_offset = 3;
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
// payload data is already in the buffer starting at offset + 7
// Make sure we have space for MAC
// The buffer should already have been sized appropriately
// Encrypt the message in place
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
}
// Fill in the encrypted size
buf_start[1] = (uint8_t) (mbuf.size >> 8);
buf_start[2] = (uint8_t) mbuf.size;
// Add iovec for this encrypted packet
struct iovec iov;
iov.iov_base = buf_start;
iov.iov_len = 3 + mbuf.size; // indicator + size + encrypted data
this->reusable_iovs_.push_back(iov);
}
// Send all encrypted packets in one writev call
return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
}
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
uint8_t header[3];
header[0] = 0x01; // indicator
@ -1004,65 +1026,86 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
return APIError::OK;
}
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
// Use write_protobuf_packets with a single packet
std::vector<PacketInfo> packets;
packets.emplace_back(type, 0, payload_len);
return write_protobuf_packets(buffer, packets);
}
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer,
const std::vector<PacketInfo> &packets) {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
// Message data starts after padding (frame_header_padding_ = 6)
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
// Calculate varint sizes for header components
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
if (total_header_len > frame_header_padding_) {
// Header is too large to fit in the padding
return APIError::BAD_ARG;
if (packets.empty()) {
return APIError::OK;
}
// Calculate where to start writing the header
// The header starts at the latest possible position to minimize unused padding
//
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
// [0-2] - Unused padding
// [3] - 0x00 indicator byte
// [4] - Payload size varint (1 byte, for sizes 0-127)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
// [0-1] - Unused padding
// [2] - 0x00 indicator byte
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
// [0] - 0x00 indicator byte
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
// [4-5] - Message type varint (2 bytes, for types 128-32767)
// [6...] - Actual payload data
uint8_t *buf_start = raw_buffer->data();
uint8_t header_offset = frame_header_padding_ - total_header_len;
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());
// Write the plaintext header
buf_start[header_offset] = 0x00; // indicator
for (const auto &packet : packets) {
uint16_t type = packet.message_type;
uint16_t offset = packet.offset;
uint16_t payload_len = packet.payload_size;
// Encode size varint directly into buffer
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
// Calculate varint sizes for header layout
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
// Encode type varint directly into buffer
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
// Calculate where to start writing the header
// The header starts at the latest possible position to minimize unused padding
//
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
// [0-2] - Unused padding
// [3] - 0x00 indicator byte
// [4] - Payload size varint (1 byte, for sizes 0-127)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
// [0-1] - Unused padding
// [2] - 0x00 indicator byte
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
// [0] - 0x00 indicator byte
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
// [4-5] - Message type varint (2 bytes, for types 128-32767)
// [6...] - Actual payload data
//
// The message starts at offset + frame_header_padding_
// So we write the header starting at offset + frame_header_padding_ - total_header_len
uint8_t *buf_start = raw_buffer->data() + offset;
uint32_t header_offset = frame_header_padding_ - total_header_len;
struct iovec iov;
// Point iov_base to the beginning of our header (skip unused padding)
// This ensures we only send the actual header and payload, not the empty padding bytes
iov.iov_base = buf_start + header_offset;
iov.iov_len = total_header_len + payload_len;
// Write the plaintext header
buf_start[header_offset] = 0x00; // indicator
return write_raw_(&iov, 1);
// Encode size varint directly into buffer
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
// Encode type varint directly into buffer
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
// Add iovec for this packet (header + payload)
struct iovec iov;
iov.iov_base = buf_start + header_offset;
iov.iov_len = total_header_len + payload_len;
this->reusable_iovs_.push_back(iov);
}
// Send all packets in one writev call
return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
}
#endif // USE_API_PLAINTEXT

View File

@ -27,6 +27,17 @@ struct ReadPacketBuffer {
uint16_t data_len;
};
// Packed packet info structure to minimize memory usage
struct PacketInfo {
uint16_t message_type; // 2 bytes
uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes)
uint16_t payload_size; // 2 bytes (up to 65535 bytes)
uint16_t padding; // 2 byte (for alignment)
PacketInfo(uint16_t type, uint16_t off, uint16_t size)
: message_type(type), offset(off), payload_size(size), padding(0) {}
};
enum class APIError : int {
OK = 0,
WOULD_BLOCK = 1001,
@ -87,6 +98,10 @@ class APIFrameHelper {
// Give this helper a name for logging
void set_log_info(std::string info) { info_ = std::move(info); }
virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0;
// Write multiple protobuf packets in a single operation
// packets contains (message_type, offset, length) for each message in the buffer
// The buffer contains all messages with appropriate padding before each
virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) = 0;
// Get the frame header padding required by this protocol
virtual uint8_t frame_header_padding() = 0;
// Get the frame footer size required by this protocol
@ -157,6 +172,9 @@ class APIFrameHelper {
uint8_t frame_header_padding_{0};
uint8_t frame_footer_size_{0};
// Reusable IOV array for write_protobuf_packets to avoid repeated allocations
std::vector<struct iovec> reusable_iovs_;
// Receive buffer for reading frame data
std::vector<uint8_t> rx_buf_;
uint16_t rx_buf_len_ = 0;
@ -182,6 +200,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
// Get the frame header padding required by this protocol
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol
@ -226,6 +245,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol
uint8_t frame_footer_size() override { return frame_footer_size_; }

View File

@ -255,6 +255,11 @@ enum UpdateCommand : uint32_t {
class HelloRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 1;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "hello_request"; }
#endif
std::string client_info{};
uint32_t api_version_major{0};
uint32_t api_version_minor{0};
@ -270,6 +275,11 @@ class HelloRequest : public ProtoMessage {
};
class HelloResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 2;
static constexpr uint16_t ESTIMATED_SIZE = 26;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "hello_response"; }
#endif
uint32_t api_version_major{0};
uint32_t api_version_minor{0};
std::string server_info{};
@ -286,6 +296,11 @@ class HelloResponse : public ProtoMessage {
};
class ConnectRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 3;
static constexpr uint16_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "connect_request"; }
#endif
std::string password{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -298,6 +313,11 @@ class ConnectRequest : public ProtoMessage {
};
class ConnectResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 4;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "connect_response"; }
#endif
bool invalid_password{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -310,6 +330,11 @@ class ConnectResponse : public ProtoMessage {
};
class DisconnectRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 5;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "disconnect_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -320,6 +345,11 @@ class DisconnectRequest : public ProtoMessage {
};
class DisconnectResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 6;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "disconnect_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -330,6 +360,11 @@ class DisconnectResponse : public ProtoMessage {
};
class PingRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 7;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "ping_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -340,6 +375,11 @@ class PingRequest : public ProtoMessage {
};
class PingResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 8;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "ping_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -350,6 +390,11 @@ class PingResponse : public ProtoMessage {
};
class DeviceInfoRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 9;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "device_info_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -360,6 +405,11 @@ class DeviceInfoRequest : public ProtoMessage {
};
class DeviceInfoResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 10;
static constexpr uint16_t ESTIMATED_SIZE = 129;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "device_info_response"; }
#endif
bool uses_password{false};
std::string name{};
std::string mac_address{};
@ -391,6 +441,11 @@ class DeviceInfoResponse : public ProtoMessage {
};
class ListEntitiesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 11;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -401,6 +456,11 @@ class ListEntitiesRequest : public ProtoMessage {
};
class ListEntitiesDoneResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 19;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_done_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -411,6 +471,11 @@ class ListEntitiesDoneResponse : public ProtoMessage {
};
class SubscribeStatesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 20;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_states_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -421,6 +486,11 @@ class SubscribeStatesRequest : public ProtoMessage {
};
class ListEntitiesBinarySensorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 12;
static constexpr uint16_t ESTIMATED_SIZE = 56;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -443,6 +513,11 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
};
class BinarySensorStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 21;
static constexpr uint16_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "binary_sensor_state_response"; }
#endif
uint32_t key{0};
bool state{false};
bool missing_state{false};
@ -458,6 +533,11 @@ class BinarySensorStateResponse : public ProtoMessage {
};
class ListEntitiesCoverResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 13;
static constexpr uint16_t ESTIMATED_SIZE = 62;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_cover_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -483,6 +563,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
};
class CoverStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 22;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "cover_state_response"; }
#endif
uint32_t key{0};
enums::LegacyCoverState legacy_state{};
float position{0.0f};
@ -500,6 +585,11 @@ class CoverStateResponse : public ProtoMessage {
};
class CoverCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 30;
static constexpr uint16_t ESTIMATED_SIZE = 25;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "cover_command_request"; }
#endif
uint32_t key{0};
bool has_legacy_command{false};
enums::LegacyCoverCommand legacy_command{};
@ -520,6 +610,11 @@ class CoverCommandRequest : public ProtoMessage {
};
class ListEntitiesFanResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 14;
static constexpr uint16_t ESTIMATED_SIZE = 73;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_fan_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -545,6 +640,11 @@ class ListEntitiesFanResponse : public ProtoMessage {
};
class FanStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 23;
static constexpr uint16_t ESTIMATED_SIZE = 26;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "fan_state_response"; }
#endif
uint32_t key{0};
bool state{false};
bool oscillating{false};
@ -565,6 +665,11 @@ class FanStateResponse : public ProtoMessage {
};
class FanCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 31;
static constexpr uint16_t ESTIMATED_SIZE = 38;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "fan_command_request"; }
#endif
uint32_t key{0};
bool has_state{false};
bool state{false};
@ -591,6 +696,11 @@ class FanCommandRequest : public ProtoMessage {
};
class ListEntitiesLightResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 15;
static constexpr uint16_t ESTIMATED_SIZE = 85;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_light_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -619,6 +729,11 @@ class ListEntitiesLightResponse : public ProtoMessage {
};
class LightStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 24;
static constexpr uint16_t ESTIMATED_SIZE = 63;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "light_state_response"; }
#endif
uint32_t key{0};
bool state{false};
float brightness{0.0f};
@ -645,6 +760,11 @@ class LightStateResponse : public ProtoMessage {
};
class LightCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 32;
static constexpr uint16_t ESTIMATED_SIZE = 107;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "light_command_request"; }
#endif
uint32_t key{0};
bool has_state{false};
bool state{false};
@ -685,6 +805,11 @@ class LightCommandRequest : public ProtoMessage {
};
class ListEntitiesSensorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 16;
static constexpr uint16_t ESTIMATED_SIZE = 73;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_sensor_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -711,6 +836,11 @@ class ListEntitiesSensorResponse : public ProtoMessage {
};
class SensorStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 25;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "sensor_state_response"; }
#endif
uint32_t key{0};
float state{0.0f};
bool missing_state{false};
@ -726,6 +856,11 @@ class SensorStateResponse : public ProtoMessage {
};
class ListEntitiesSwitchResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 17;
static constexpr uint16_t ESTIMATED_SIZE = 56;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_switch_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -748,6 +883,11 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
};
class SwitchStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 26;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "switch_state_response"; }
#endif
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -762,6 +902,11 @@ class SwitchStateResponse : public ProtoMessage {
};
class SwitchCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 33;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "switch_command_request"; }
#endif
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -776,6 +921,11 @@ class SwitchCommandRequest : public ProtoMessage {
};
class ListEntitiesTextSensorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 18;
static constexpr uint16_t ESTIMATED_SIZE = 54;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_text_sensor_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -797,6 +947,11 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
};
class TextSensorStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 27;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "text_sensor_state_response"; }
#endif
uint32_t key{0};
std::string state{};
bool missing_state{false};
@ -813,6 +968,11 @@ class TextSensorStateResponse : public ProtoMessage {
};
class SubscribeLogsRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 28;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_logs_request"; }
#endif
enums::LogLevel level{};
bool dump_config{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -826,6 +986,11 @@ class SubscribeLogsRequest : public ProtoMessage {
};
class SubscribeLogsResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 29;
static constexpr uint16_t ESTIMATED_SIZE = 13;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_logs_response"; }
#endif
enums::LogLevel level{};
std::string message{};
bool send_failed{false};
@ -841,6 +1006,11 @@ class SubscribeLogsResponse : public ProtoMessage {
};
class NoiseEncryptionSetKeyRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 124;
static constexpr uint16_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "noise_encryption_set_key_request"; }
#endif
std::string key{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -853,6 +1023,11 @@ class NoiseEncryptionSetKeyRequest : public ProtoMessage {
};
class NoiseEncryptionSetKeyResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 125;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "noise_encryption_set_key_response"; }
#endif
bool success{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -865,6 +1040,11 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage {
};
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 34;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -888,6 +1068,11 @@ class HomeassistantServiceMap : public ProtoMessage {
};
class HomeassistantServiceResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 35;
static constexpr uint16_t ESTIMATED_SIZE = 113;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "homeassistant_service_response"; }
#endif
std::string service{};
std::vector<HomeassistantServiceMap> data{};
std::vector<HomeassistantServiceMap> data_template{};
@ -905,6 +1090,11 @@ class HomeassistantServiceResponse : public ProtoMessage {
};
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 38;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -915,6 +1105,11 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
};
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 39;
static constexpr uint16_t ESTIMATED_SIZE = 20;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_home_assistant_state_response"; }
#endif
std::string entity_id{};
std::string attribute{};
bool once{false};
@ -930,6 +1125,11 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
};
class HomeAssistantStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 40;
static constexpr uint16_t ESTIMATED_SIZE = 27;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "home_assistant_state_response"; }
#endif
std::string entity_id{};
std::string state{};
std::string attribute{};
@ -944,6 +1144,11 @@ class HomeAssistantStateResponse : public ProtoMessage {
};
class GetTimeRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 36;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "get_time_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -954,6 +1159,11 @@ class GetTimeRequest : public ProtoMessage {
};
class GetTimeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 37;
static constexpr uint16_t ESTIMATED_SIZE = 5;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "get_time_response"; }
#endif
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -980,6 +1190,11 @@ class ListEntitiesServicesArgument : public ProtoMessage {
};
class ListEntitiesServicesResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 41;
static constexpr uint16_t ESTIMATED_SIZE = 48;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_services_response"; }
#endif
std::string name{};
uint32_t key{0};
std::vector<ListEntitiesServicesArgument> args{};
@ -1017,6 +1232,11 @@ class ExecuteServiceArgument : public ProtoMessage {
};
class ExecuteServiceRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 42;
static constexpr uint16_t ESTIMATED_SIZE = 39;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "execute_service_request"; }
#endif
uint32_t key{0};
std::vector<ExecuteServiceArgument> args{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1031,6 +1251,11 @@ class ExecuteServiceRequest : public ProtoMessage {
};
class ListEntitiesCameraResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 43;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_camera_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1051,6 +1276,11 @@ class ListEntitiesCameraResponse : public ProtoMessage {
};
class CameraImageResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 44;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "camera_image_response"; }
#endif
uint32_t key{0};
std::string data{};
bool done{false};
@ -1067,6 +1297,11 @@ class CameraImageResponse : public ProtoMessage {
};
class CameraImageRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 45;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "camera_image_request"; }
#endif
bool single{false};
bool stream{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -1080,6 +1315,11 @@ class CameraImageRequest : public ProtoMessage {
};
class ListEntitiesClimateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 46;
static constexpr uint16_t ESTIMATED_SIZE = 151;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_climate_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1118,6 +1358,11 @@ class ListEntitiesClimateResponse : public ProtoMessage {
};
class ClimateStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 47;
static constexpr uint16_t ESTIMATED_SIZE = 65;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "climate_state_response"; }
#endif
uint32_t key{0};
enums::ClimateMode mode{};
float current_temperature{0.0f};
@ -1146,6 +1391,11 @@ class ClimateStateResponse : public ProtoMessage {
};
class ClimateCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 48;
static constexpr uint16_t ESTIMATED_SIZE = 83;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "climate_command_request"; }
#endif
uint32_t key{0};
bool has_mode{false};
enums::ClimateMode mode{};
@ -1182,6 +1432,11 @@ class ClimateCommandRequest : public ProtoMessage {
};
class ListEntitiesNumberResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 49;
static constexpr uint16_t ESTIMATED_SIZE = 80;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_number_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1208,6 +1463,11 @@ class ListEntitiesNumberResponse : public ProtoMessage {
};
class NumberStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 50;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "number_state_response"; }
#endif
uint32_t key{0};
float state{0.0f};
bool missing_state{false};
@ -1223,6 +1483,11 @@ class NumberStateResponse : public ProtoMessage {
};
class NumberCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 51;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "number_command_request"; }
#endif
uint32_t key{0};
float state{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
@ -1236,6 +1501,11 @@ class NumberCommandRequest : public ProtoMessage {
};
class ListEntitiesSelectResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 52;
static constexpr uint16_t ESTIMATED_SIZE = 63;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_select_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1257,6 +1527,11 @@ class ListEntitiesSelectResponse : public ProtoMessage {
};
class SelectStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 53;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "select_state_response"; }
#endif
uint32_t key{0};
std::string state{};
bool missing_state{false};
@ -1273,6 +1548,11 @@ class SelectStateResponse : public ProtoMessage {
};
class SelectCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 54;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "select_command_request"; }
#endif
uint32_t key{0};
std::string state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1287,6 +1567,11 @@ class SelectCommandRequest : public ProtoMessage {
};
class ListEntitiesSirenResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 55;
static constexpr uint16_t ESTIMATED_SIZE = 67;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_siren_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1310,6 +1595,11 @@ class ListEntitiesSirenResponse : public ProtoMessage {
};
class SirenStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 56;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "siren_state_response"; }
#endif
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -1324,6 +1614,11 @@ class SirenStateResponse : public ProtoMessage {
};
class SirenCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 57;
static constexpr uint16_t ESTIMATED_SIZE = 33;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "siren_command_request"; }
#endif
uint32_t key{0};
bool has_state{false};
bool state{false};
@ -1346,6 +1641,11 @@ class SirenCommandRequest : public ProtoMessage {
};
class ListEntitiesLockResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 58;
static constexpr uint16_t ESTIMATED_SIZE = 60;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_lock_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1370,6 +1670,11 @@ class ListEntitiesLockResponse : public ProtoMessage {
};
class LockStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 59;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "lock_state_response"; }
#endif
uint32_t key{0};
enums::LockState state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1384,6 +1689,11 @@ class LockStateResponse : public ProtoMessage {
};
class LockCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 60;
static constexpr uint16_t ESTIMATED_SIZE = 18;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "lock_command_request"; }
#endif
uint32_t key{0};
enums::LockCommand command{};
bool has_code{false};
@ -1401,6 +1711,11 @@ class LockCommandRequest : public ProtoMessage {
};
class ListEntitiesButtonResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 61;
static constexpr uint16_t ESTIMATED_SIZE = 54;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_button_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1422,6 +1737,11 @@ class ListEntitiesButtonResponse : public ProtoMessage {
};
class ButtonCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 62;
static constexpr uint16_t ESTIMATED_SIZE = 5;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "button_command_request"; }
#endif
uint32_t key{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1451,6 +1771,11 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
};
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 63;
static constexpr uint16_t ESTIMATED_SIZE = 81;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_media_player_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1473,6 +1798,11 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage {
};
class MediaPlayerStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 64;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "media_player_state_response"; }
#endif
uint32_t key{0};
enums::MediaPlayerState state{};
float volume{0.0f};
@ -1489,6 +1819,11 @@ class MediaPlayerStateResponse : public ProtoMessage {
};
class MediaPlayerCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 65;
static constexpr uint16_t ESTIMATED_SIZE = 31;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "media_player_command_request"; }
#endif
uint32_t key{0};
bool has_command{false};
enums::MediaPlayerCommand command{};
@ -1511,6 +1846,11 @@ class MediaPlayerCommandRequest : public ProtoMessage {
};
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 66;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_bluetooth_le_advertisements_request"; }
#endif
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1538,6 +1878,11 @@ class BluetoothServiceData : public ProtoMessage {
};
class BluetoothLEAdvertisementResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 67;
static constexpr uint16_t ESTIMATED_SIZE = 107;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_le_advertisement_response"; }
#endif
uint64_t address{0};
std::string name{};
int32_t rssi{0};
@ -1573,6 +1918,11 @@ class BluetoothLERawAdvertisement : public ProtoMessage {
};
class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 93;
static constexpr uint16_t ESTIMATED_SIZE = 34;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_le_raw_advertisements_response"; }
#endif
std::vector<BluetoothLERawAdvertisement> advertisements{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1585,6 +1935,11 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
};
class BluetoothDeviceRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 68;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_request"; }
#endif
uint64_t address{0};
enums::BluetoothDeviceRequestType request_type{};
bool has_address_type{false};
@ -1600,6 +1955,11 @@ class BluetoothDeviceRequest : public ProtoMessage {
};
class BluetoothDeviceConnectionResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 69;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_connection_response"; }
#endif
uint64_t address{0};
bool connected{false};
uint32_t mtu{0};
@ -1615,6 +1975,11 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage {
};
class BluetoothGATTGetServicesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 70;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_get_services_request"; }
#endif
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1671,6 +2036,11 @@ class BluetoothGATTService : public ProtoMessage {
};
class BluetoothGATTGetServicesResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 71;
static constexpr uint16_t ESTIMATED_SIZE = 38;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_get_services_response"; }
#endif
uint64_t address{0};
std::vector<BluetoothGATTService> services{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1685,6 +2055,11 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage {
};
class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 72;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_get_services_done_response"; }
#endif
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1697,6 +2072,11 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
};
class BluetoothGATTReadRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 73;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_read_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1710,6 +2090,11 @@ class BluetoothGATTReadRequest : public ProtoMessage {
};
class BluetoothGATTReadResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 74;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_read_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
@ -1725,6 +2110,11 @@ class BluetoothGATTReadResponse : public ProtoMessage {
};
class BluetoothGATTWriteRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 75;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_write_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
bool response{false};
@ -1741,6 +2131,11 @@ class BluetoothGATTWriteRequest : public ProtoMessage {
};
class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 76;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_read_descriptor_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1754,6 +2149,11 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
};
class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 77;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_write_descriptor_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
@ -1769,6 +2169,11 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
};
class BluetoothGATTNotifyRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 78;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_notify_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
bool enable{false};
@ -1783,6 +2188,11 @@ class BluetoothGATTNotifyRequest : public ProtoMessage {
};
class BluetoothGATTNotifyDataResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 79;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_notify_data_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
@ -1798,6 +2208,11 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage {
};
class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 80;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1808,6 +2223,11 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
};
class BluetoothConnectionsFreeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 81;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_connections_free_response"; }
#endif
uint32_t free{0};
uint32_t limit{0};
std::vector<uint64_t> allocated{};
@ -1822,6 +2242,11 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
};
class BluetoothGATTErrorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 82;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_error_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
int32_t error{0};
@ -1836,6 +2261,11 @@ class BluetoothGATTErrorResponse : public ProtoMessage {
};
class BluetoothGATTWriteResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 83;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_write_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1849,6 +2279,11 @@ class BluetoothGATTWriteResponse : public ProtoMessage {
};
class BluetoothGATTNotifyResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 84;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_notify_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1862,6 +2297,11 @@ class BluetoothGATTNotifyResponse : public ProtoMessage {
};
class BluetoothDevicePairingResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 85;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_pairing_response"; }
#endif
uint64_t address{0};
bool paired{false};
int32_t error{0};
@ -1876,6 +2316,11 @@ class BluetoothDevicePairingResponse : public ProtoMessage {
};
class BluetoothDeviceUnpairingResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 86;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_unpairing_response"; }
#endif
uint64_t address{0};
bool success{false};
int32_t error{0};
@ -1890,6 +2335,11 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
};
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 87;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1900,6 +2350,11 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
};
class BluetoothDeviceClearCacheResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 88;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_clear_cache_response"; }
#endif
uint64_t address{0};
bool success{false};
int32_t error{0};
@ -1914,6 +2369,11 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
};
class BluetoothScannerStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 126;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_scanner_state_response"; }
#endif
enums::BluetoothScannerState state{};
enums::BluetoothScannerMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1927,6 +2387,11 @@ class BluetoothScannerStateResponse : public ProtoMessage {
};
class BluetoothScannerSetModeRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 127;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_scanner_set_mode_request"; }
#endif
enums::BluetoothScannerMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1939,6 +2404,11 @@ class BluetoothScannerSetModeRequest : public ProtoMessage {
};
class SubscribeVoiceAssistantRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 89;
static constexpr uint16_t ESTIMATED_SIZE = 6;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_voice_assistant_request"; }
#endif
bool subscribe{false};
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1967,6 +2437,11 @@ class VoiceAssistantAudioSettings : public ProtoMessage {
};
class VoiceAssistantRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 90;
static constexpr uint16_t ESTIMATED_SIZE = 41;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_request"; }
#endif
bool start{false};
std::string conversation_id{};
uint32_t flags{0};
@ -1984,6 +2459,11 @@ class VoiceAssistantRequest : public ProtoMessage {
};
class VoiceAssistantResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 91;
static constexpr uint16_t ESTIMATED_SIZE = 6;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_response"; }
#endif
uint32_t port{0};
bool error{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -2010,6 +2490,11 @@ class VoiceAssistantEventData : public ProtoMessage {
};
class VoiceAssistantEventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 92;
static constexpr uint16_t ESTIMATED_SIZE = 36;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_event_response"; }
#endif
enums::VoiceAssistantEvent event_type{};
std::vector<VoiceAssistantEventData> data{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2024,6 +2509,11 @@ class VoiceAssistantEventResponse : public ProtoMessage {
};
class VoiceAssistantAudio : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 106;
static constexpr uint16_t ESTIMATED_SIZE = 11;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_audio"; }
#endif
std::string data{};
bool end{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -2038,6 +2528,11 @@ class VoiceAssistantAudio : public ProtoMessage {
};
class VoiceAssistantTimerEventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 115;
static constexpr uint16_t ESTIMATED_SIZE = 30;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_timer_event_response"; }
#endif
enums::VoiceAssistantTimerEvent event_type{};
std::string timer_id{};
std::string name{};
@ -2056,6 +2551,11 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage {
};
class VoiceAssistantAnnounceRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 119;
static constexpr uint16_t ESTIMATED_SIZE = 29;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_announce_request"; }
#endif
std::string media_id{};
std::string text{};
std::string preannounce_media_id{};
@ -2072,6 +2572,11 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage {
};
class VoiceAssistantAnnounceFinished : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 120;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_announce_finished"; }
#endif
bool success{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2098,6 +2603,11 @@ class VoiceAssistantWakeWord : public ProtoMessage {
};
class VoiceAssistantConfigurationRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 121;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_configuration_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -2108,6 +2618,11 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage {
};
class VoiceAssistantConfigurationResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 122;
static constexpr uint16_t ESTIMATED_SIZE = 56;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_configuration_response"; }
#endif
std::vector<VoiceAssistantWakeWord> available_wake_words{};
std::vector<std::string> active_wake_words{};
uint32_t max_active_wake_words{0};
@ -2123,6 +2638,11 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage {
};
class VoiceAssistantSetConfiguration : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 123;
static constexpr uint16_t ESTIMATED_SIZE = 18;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_set_configuration"; }
#endif
std::vector<std::string> active_wake_words{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2135,6 +2655,11 @@ class VoiceAssistantSetConfiguration : public ProtoMessage {
};
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 94;
static constexpr uint16_t ESTIMATED_SIZE = 53;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2158,6 +2683,11 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
};
class AlarmControlPanelStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 95;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "alarm_control_panel_state_response"; }
#endif
uint32_t key{0};
enums::AlarmControlPanelState state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2172,6 +2702,11 @@ class AlarmControlPanelStateResponse : public ProtoMessage {
};
class AlarmControlPanelCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 96;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "alarm_control_panel_command_request"; }
#endif
uint32_t key{0};
enums::AlarmControlPanelStateCommand command{};
std::string code{};
@ -2188,6 +2723,11 @@ class AlarmControlPanelCommandRequest : public ProtoMessage {
};
class ListEntitiesTextResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 97;
static constexpr uint16_t ESTIMATED_SIZE = 64;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_text_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2212,6 +2752,11 @@ class ListEntitiesTextResponse : public ProtoMessage {
};
class TextStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 98;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "text_state_response"; }
#endif
uint32_t key{0};
std::string state{};
bool missing_state{false};
@ -2228,6 +2773,11 @@ class TextStateResponse : public ProtoMessage {
};
class TextCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 99;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "text_command_request"; }
#endif
uint32_t key{0};
std::string state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2242,6 +2792,11 @@ class TextCommandRequest : public ProtoMessage {
};
class ListEntitiesDateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 100;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_date_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2262,6 +2817,11 @@ class ListEntitiesDateResponse : public ProtoMessage {
};
class DateStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 101;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
uint32_t year{0};
@ -2279,6 +2839,11 @@ class DateStateResponse : public ProtoMessage {
};
class DateCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 102;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_command_request"; }
#endif
uint32_t key{0};
uint32_t year{0};
uint32_t month{0};
@ -2295,6 +2860,11 @@ class DateCommandRequest : public ProtoMessage {
};
class ListEntitiesTimeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 103;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_time_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2315,6 +2885,11 @@ class ListEntitiesTimeResponse : public ProtoMessage {
};
class TimeStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 104;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "time_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
uint32_t hour{0};
@ -2332,6 +2907,11 @@ class TimeStateResponse : public ProtoMessage {
};
class TimeCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 105;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "time_command_request"; }
#endif
uint32_t key{0};
uint32_t hour{0};
uint32_t minute{0};
@ -2348,6 +2928,11 @@ class TimeCommandRequest : public ProtoMessage {
};
class ListEntitiesEventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 107;
static constexpr uint16_t ESTIMATED_SIZE = 72;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_event_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2370,6 +2955,11 @@ class ListEntitiesEventResponse : public ProtoMessage {
};
class EventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 108;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "event_response"; }
#endif
uint32_t key{0};
std::string event_type{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2384,6 +2974,11 @@ class EventResponse : public ProtoMessage {
};
class ListEntitiesValveResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 109;
static constexpr uint16_t ESTIMATED_SIZE = 60;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_valve_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2408,6 +3003,11 @@ class ListEntitiesValveResponse : public ProtoMessage {
};
class ValveStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 110;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "valve_state_response"; }
#endif
uint32_t key{0};
float position{0.0f};
enums::ValveOperation current_operation{};
@ -2423,6 +3023,11 @@ class ValveStateResponse : public ProtoMessage {
};
class ValveCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 111;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "valve_command_request"; }
#endif
uint32_t key{0};
bool has_position{false};
float position{0.0f};
@ -2439,6 +3044,11 @@ class ValveCommandRequest : public ProtoMessage {
};
class ListEntitiesDateTimeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 112;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_date_time_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2459,6 +3069,11 @@ class ListEntitiesDateTimeResponse : public ProtoMessage {
};
class DateTimeStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 113;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_time_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
uint32_t epoch_seconds{0};
@ -2474,6 +3089,11 @@ class DateTimeStateResponse : public ProtoMessage {
};
class DateTimeCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 114;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_time_command_request"; }
#endif
uint32_t key{0};
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -2487,6 +3107,11 @@ class DateTimeCommandRequest : public ProtoMessage {
};
class ListEntitiesUpdateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 116;
static constexpr uint16_t ESTIMATED_SIZE = 54;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_update_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2508,6 +3133,11 @@ class ListEntitiesUpdateResponse : public ProtoMessage {
};
class UpdateStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 117;
static constexpr uint16_t ESTIMATED_SIZE = 61;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "update_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
bool in_progress{false};
@ -2531,6 +3161,11 @@ class UpdateStateResponse : public ProtoMessage {
};
class UpdateCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 118;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "update_command_request"; }
#endif
uint32_t key{0};
enums::UpdateCommand command{};
void encode(ProtoWriteBuffer buffer) const override;

View File

@ -8,688 +8,12 @@ namespace api {
static const char *const TAG = "api.service";
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HelloResponse>(msg, 2);
}
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ConnectResponse>(msg, 4);
}
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectRequest>(msg, 5);
}
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectResponse>(msg, 6);
}
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
#endif
return this->send_message_<PingRequest>(msg, 7);
}
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
#endif
return this->send_message_<PingResponse>(msg, 8);
}
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DeviceInfoResponse>(msg, 10);
}
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
}
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
}
#endif
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BinarySensorStateResponse>(msg, 21);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CoverStateResponse>(msg, 22);
}
#endif
#ifdef USE_COVER
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
}
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<FanStateResponse>(msg, 23);
}
#endif
#ifdef USE_FAN
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
}
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LightStateResponse>(msg, 24);
}
#endif
#ifdef USE_LIGHT
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
}
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SensorStateResponse>(msg, 25);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SwitchStateResponse>(msg, 26);
}
#endif
#ifdef USE_SWITCH
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
}
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TextSensorStateResponse>(msg, 27);
}
#endif
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
return this->send_message_<SubscribeLogsResponse>(msg, 29);
}
#ifdef USE_API_NOISE
#endif
#ifdef USE_API_NOISE
bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NoiseEncryptionSetKeyResponse>(msg, 125);
}
#endif
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
}
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
const SubscribeHomeAssistantStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
}
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeRequest>(msg, 36);
}
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeResponse>(msg, 37);
}
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
}
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
}
#endif
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CameraImageResponse>(msg, 44);
}
#endif
#ifdef USE_ESP32_CAMERA
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
}
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ClimateStateResponse>(msg, 47);
}
#endif
#ifdef USE_CLIMATE
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
}
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NumberStateResponse>(msg, 50);
}
#endif
#ifdef USE_NUMBER
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
}
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SelectStateResponse>(msg, 53);
}
#endif
#ifdef USE_SELECT
#endif
#ifdef USE_SIREN
bool APIServerConnectionBase::send_list_entities_siren_response(const ListEntitiesSirenResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_siren_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSirenResponse>(msg, 55);
}
#endif
#ifdef USE_SIREN
bool APIServerConnectionBase::send_siren_state_response(const SirenStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_siren_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SirenStateResponse>(msg, 56);
}
#endif
#ifdef USE_SIREN
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLockResponse>(msg, 58);
void APIServerConnectionBase::log_send_message_(const char *name, const std::string &dump) {
ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());
}
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LockStateResponse>(msg, 59);
}
#endif
#ifdef USE_LOCK
#endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
}
#endif
#ifdef USE_BUTTON
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesMediaPlayerResponse>(msg, 63);
}
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<MediaPlayerStateResponse>(msg, 64);
}
#endif
#ifdef USE_MEDIA_PLAYER
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLEAdvertisementResponse>(msg, 67);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response(
const BluetoothLERawAdvertisementsResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLERawAdvertisementsResponse>(msg, 93);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceConnectionResponse>(msg, 69);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTGetServicesResponse>(msg, 71);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response(
const BluetoothGATTGetServicesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTGetServicesDoneResponse>(msg, 72);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTReadResponse>(msg, 74);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTNotifyDataResponse>(msg, 79);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothConnectionsFreeResponse>(msg, 81);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTErrorResponse>(msg, 82);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTWriteResponse>(msg, 83);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTNotifyResponse>(msg, 84);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_pairing_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDevicePairingResponse>(msg, 85);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_unpairing_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothScannerStateResponse>(msg, 126);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantRequest>(msg, 90);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantAudio>(msg, 106);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_announce_finished: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantAnnounceFinished>(msg, 120);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_configuration_response(
const VoiceAssistantConfigurationResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_configuration_response: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantConfigurationResponse>(msg, 122);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
const ListEntitiesAlarmControlPanelResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesAlarmControlPanelResponse>(msg, 94);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<AlarmControlPanelStateResponse>(msg, 95);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
#endif
#ifdef USE_TEXT
bool APIServerConnectionBase::send_list_entities_text_response(const ListEntitiesTextResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_text_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTextResponse>(msg, 97);
}
#endif
#ifdef USE_TEXT
bool APIServerConnectionBase::send_text_state_response(const TextStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_text_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TextStateResponse>(msg, 98);
}
#endif
#ifdef USE_TEXT
#endif
#ifdef USE_DATETIME_DATE
bool APIServerConnectionBase::send_list_entities_date_response(const ListEntitiesDateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_date_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDateResponse>(msg, 100);
}
#endif
#ifdef USE_DATETIME_DATE
bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_date_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DateStateResponse>(msg, 101);
}
#endif
#ifdef USE_DATETIME_DATE
#endif
#ifdef USE_DATETIME_TIME
bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTimeResponse>(msg, 103);
}
#endif
#ifdef USE_DATETIME_TIME
bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TimeStateResponse>(msg, 104);
}
#endif
#ifdef USE_DATETIME_TIME
#endif
#ifdef USE_EVENT
bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesEventResponse>(msg, 107);
}
#endif
#ifdef USE_EVENT
bool APIServerConnectionBase::send_event_response(const EventResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str());
#endif
return this->send_message_<EventResponse>(msg, 108);
}
#endif
#ifdef USE_VALVE
bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesValveResponse>(msg, 109);
}
#endif
#ifdef USE_VALVE
bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ValveStateResponse>(msg, 110);
}
#endif
#ifdef USE_VALVE
#endif
#ifdef USE_DATETIME_DATETIME
bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDateTimeResponse>(msg, 112);
}
#endif
#ifdef USE_DATETIME_DATETIME
bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DateTimeStateResponse>(msg, 113);
}
#endif
#ifdef USE_DATETIME_DATETIME
#endif
#ifdef USE_UPDATE
bool APIServerConnectionBase::send_list_entities_update_response(const ListEntitiesUpdateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_update_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesUpdateResponse>(msg, 116);
}
#endif
#ifdef USE_UPDATE
bool APIServerConnectionBase::send_update_state_response(const UpdateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_update_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<UpdateStateResponse>(msg, 117);
}
#endif
#ifdef USE_UPDATE
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@ -1273,25 +597,25 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
HelloResponse ret = this->hello(msg);
if (!this->send_hello_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_connect_request(const ConnectRequest &msg) {
ConnectResponse ret = this->connect(msg);
if (!this->send_connect_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
DisconnectResponse ret = this->disconnect(msg);
if (!this->send_disconnect_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_ping_request(const PingRequest &msg) {
PingResponse ret = this->ping(msg);
if (!this->send_ping_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1301,7 +625,7 @@ void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
return;
}
DeviceInfoResponse ret = this->device_info(msg);
if (!this->send_device_info_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1367,7 +691,7 @@ void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
return;
}
GetTimeResponse ret = this->get_time(msg);
if (!this->send_get_time_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1393,7 +717,7 @@ void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncrypt
return;
}
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_noise_encryption_set_key_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1749,7 +1073,7 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
return;
}
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_bluetooth_connections_free_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1805,7 +1129,7 @@ void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAs
return;
}
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
if (!this->send_voice_assistant_configuration_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}

View File

@ -10,162 +10,94 @@ namespace api {
class APIServerConnectionBase : public ProtoService {
public:
#ifdef HAS_PROTO_MESSAGE_DUMP
protected:
void log_send_message_(const char *name, const std::string &dump);
public:
#endif
template<typename T> bool send_message(const T &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_send_message_(T::message_name(), msg.dump());
#endif
return this->send_message_(msg, T::MESSAGE_TYPE);
}
virtual void on_hello_request(const HelloRequest &value){};
bool send_hello_response(const HelloResponse &msg);
virtual void on_connect_request(const ConnectRequest &value){};
bool send_connect_response(const ConnectResponse &msg);
bool send_disconnect_request(const DisconnectRequest &msg);
virtual void on_disconnect_request(const DisconnectRequest &value){};
bool send_disconnect_response(const DisconnectResponse &msg);
virtual void on_disconnect_response(const DisconnectResponse &value){};
bool send_ping_request(const PingRequest &msg);
virtual void on_ping_request(const PingRequest &value){};
bool send_ping_response(const PingResponse &msg);
virtual void on_ping_response(const PingResponse &value){};
virtual void on_device_info_request(const DeviceInfoRequest &value){};
bool send_device_info_response(const DeviceInfoResponse &msg);
virtual void on_list_entities_request(const ListEntitiesRequest &value){};
bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg);
virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
#ifdef USE_BINARY_SENSOR
bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg);
#endif
#ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg);
#endif
#ifdef USE_COVER
bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg);
#endif
#ifdef USE_COVER
bool send_cover_state_response(const CoverStateResponse &msg);
#endif
#ifdef USE_COVER
virtual void on_cover_command_request(const CoverCommandRequest &value){};
#endif
#ifdef USE_FAN
bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg);
#endif
#ifdef USE_FAN
bool send_fan_state_response(const FanStateResponse &msg);
#endif
#ifdef USE_FAN
virtual void on_fan_command_request(const FanCommandRequest &value){};
#endif
#ifdef USE_LIGHT
bool send_list_entities_light_response(const ListEntitiesLightResponse &msg);
#endif
#ifdef USE_LIGHT
bool send_light_state_response(const LightStateResponse &msg);
#endif
#ifdef USE_LIGHT
virtual void on_light_command_request(const LightCommandRequest &value){};
#endif
#ifdef USE_SENSOR
bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg);
#endif
#ifdef USE_SENSOR
bool send_sensor_state_response(const SensorStateResponse &msg);
#endif
#ifdef USE_SWITCH
bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg);
#endif
#ifdef USE_SWITCH
bool send_switch_state_response(const SwitchStateResponse &msg);
#endif
#ifdef USE_SWITCH
virtual void on_switch_command_request(const SwitchCommandRequest &value){};
#endif
#ifdef USE_TEXT_SENSOR
bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg);
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state_response(const TextSensorStateResponse &msg);
#endif
virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
#ifdef USE_API_NOISE
virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
#endif
#ifdef USE_API_NOISE
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg);
#endif
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg);
virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){};
bool send_get_time_request(const GetTimeRequest &msg);
virtual void on_get_time_request(const GetTimeRequest &value){};
bool send_get_time_response(const GetTimeResponse &msg);
virtual void on_get_time_response(const GetTimeResponse &value){};
bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg);
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
#ifdef USE_ESP32_CAMERA
bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg);
#endif
#ifdef USE_ESP32_CAMERA
bool send_camera_image_response(const CameraImageResponse &msg);
#endif
#ifdef USE_ESP32_CAMERA
virtual void on_camera_image_request(const CameraImageRequest &value){};
#endif
#ifdef USE_CLIMATE
bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg);
#endif
#ifdef USE_CLIMATE
bool send_climate_state_response(const ClimateStateResponse &msg);
#endif
#ifdef USE_CLIMATE
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
#endif
#ifdef USE_NUMBER
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
#endif
#ifdef USE_NUMBER
bool send_number_state_response(const NumberStateResponse &msg);
#endif
#ifdef USE_NUMBER
virtual void on_number_command_request(const NumberCommandRequest &value){};
#endif
#ifdef USE_SELECT
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
#endif
#ifdef USE_SELECT
bool send_select_state_response(const SelectStateResponse &msg);
#endif
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_SIREN
bool send_list_entities_siren_response(const ListEntitiesSirenResponse &msg);
#endif
#ifdef USE_SIREN
bool send_siren_state_response(const SirenStateResponse &msg);
#endif
#ifdef USE_SIREN
virtual void on_siren_command_request(const SirenCommandRequest &value){};
#endif
#ifdef USE_LOCK
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
#endif
#ifdef USE_LOCK
bool send_lock_state_response(const LockStateResponse &msg);
#endif
#ifdef USE_LOCK
virtual void on_lock_command_request(const LockCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
#ifdef USE_MEDIA_PLAYER
bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state_response(const MediaPlayerStateResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
#endif
@ -173,33 +105,19 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){};
#endif
@ -212,49 +130,23 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
#endif
@ -262,7 +154,6 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
@ -271,84 +162,39 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){};
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){};
#endif
#ifdef USE_TEXT
bool send_list_entities_text_response(const ListEntitiesTextResponse &msg);
#endif
#ifdef USE_TEXT
bool send_text_state_response(const TextStateResponse &msg);
#endif
#ifdef USE_TEXT
virtual void on_text_command_request(const TextCommandRequest &value){};
#endif
#ifdef USE_DATETIME_DATE
bool send_list_entities_date_response(const ListEntitiesDateResponse &msg);
#endif
#ifdef USE_DATETIME_DATE
bool send_date_state_response(const DateStateResponse &msg);
#endif
#ifdef USE_DATETIME_DATE
virtual void on_date_command_request(const DateCommandRequest &value){};
#endif
#ifdef USE_DATETIME_TIME
bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg);
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state_response(const TimeStateResponse &msg);
#endif
#ifdef USE_DATETIME_TIME
virtual void on_time_command_request(const TimeCommandRequest &value){};
#endif
#ifdef USE_EVENT
bool send_list_entities_event_response(const ListEntitiesEventResponse &msg);
#endif
#ifdef USE_EVENT
bool send_event_response(const EventResponse &msg);
#endif
#ifdef USE_VALVE
bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg);
#endif
#ifdef USE_VALVE
bool send_valve_state_response(const ValveStateResponse &msg);
#endif
#ifdef USE_VALVE
virtual void on_valve_command_request(const ValveCommandRequest &value){};
#endif
#ifdef USE_DATETIME_DATETIME
bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg);
#endif
#ifdef USE_DATETIME_DATETIME
bool send_date_time_state_response(const DateTimeStateResponse &msg);
#endif
#ifdef USE_DATETIME_DATETIME
virtual void on_date_time_command_request(const DateTimeCommandRequest &value){};
#endif
#ifdef USE_UPDATE
bool send_list_entities_update_response(const ListEntitiesUpdateResponse &msg);
#endif
#ifdef USE_UPDATE
bool send_update_state_response(const UpdateStateResponse &msg);
#endif
#ifdef USE_UPDATE
virtual void on_update_command_request(const UpdateCommandRequest &value){};
#endif

View File

@ -24,7 +24,11 @@ static const char *const TAG = "api";
// APIServer
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
APIServer::APIServer() { global_api_server = this; }
APIServer::APIServer() {
global_api_server = this;
// Pre-allocate shared write buffer
shared_write_buffer_.reserve(64);
}
void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
@ -227,7 +231,7 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_binary_sensor_state(obj, state);
c->send_binary_sensor_state(obj);
}
#endif
@ -263,7 +267,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_sensor_state(obj, state);
c->send_sensor_state(obj);
}
#endif
@ -272,7 +276,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_switch_state(obj, state);
c->send_switch_state(obj);
}
#endif
@ -281,7 +285,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_text_sensor_state(obj, state);
c->send_text_sensor_state(obj);
}
#endif
@ -299,7 +303,7 @@ void APIServer::on_number_update(number::Number *obj, float state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_number_state(obj, state);
c->send_number_state(obj);
}
#endif
@ -335,7 +339,7 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_text_state(obj, state);
c->send_text_state(obj);
}
#endif
@ -344,7 +348,7 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state,
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_select_state(obj, state);
c->send_select_state(obj);
}
#endif
@ -353,7 +357,7 @@ void APIServer::on_lock_update(lock::Lock *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_lock_state(obj, obj->state);
c->send_lock_state(obj);
}
#endif
@ -404,6 +408,8 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; }
void APIServer::set_password(const std::string &password) { this->password_ = password; }
void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
for (auto &client : this->clients_) {
client->send_homeassistant_service_call(call);
@ -462,7 +468,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
this->set_noise_psk(psk);
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
c->send_message(DisconnectRequest());
}
});
}
@ -492,7 +498,7 @@ void APIServer::on_shutdown() {
// Send disconnect requests to all connected clients
for (auto &c : this->clients_) {
if (!c->send_disconnect_request(DisconnectRequest())) {
if (!c->send_message(DisconnectRequest())) {
// If we can't send the disconnect request, mark for immediate closure
c->next_close_ = true;
}

View File

@ -40,6 +40,11 @@ class APIServer : public Component, public Controller {
void set_port(uint16_t port);
void set_password(const std::string &password);
void set_reboot_timeout(uint32_t reboot_timeout);
void set_batch_delay(uint32_t batch_delay);
uint32_t get_batch_delay() const { return batch_delay_; }
// Get reference to shared buffer for API connections
std::vector<uint8_t> &get_shared_buffer_ref() { return shared_write_buffer_; }
#ifdef USE_API_NOISE
bool save_noise_psk(psk_t psk, bool make_active = true);
@ -141,9 +146,11 @@ class APIServer : public Component, public Controller {
std::unique_ptr<socket::Socket> socket_ = nullptr;
uint16_t port_{6053};
uint32_t reboot_timeout_{300000};
uint32_t batch_delay_{100};
uint32_t last_connected_{0};
std::vector<std::unique_ptr<APIConnection>> clients_;
std::string password_;
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_;
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();

View File

@ -73,7 +73,7 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
auto resp = service->encode_list_service_response();
return this->client_->send_list_entities_services_response(resp);
return this->client_->send_message(resp);
}
#ifdef USE_ESP32_CAMERA

View File

@ -360,11 +360,11 @@ class ProtoService {
* @return A ProtoWriteBuffer object with the reserved size.
*/
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
// Optimized method that pre-allocates buffer based on message size
template<class C> bool send_message_(const C &msg, uint32_t message_type) {
bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
uint32_t msg_size = 0;
msg.calculate_size(msg_size);

View File

@ -8,7 +8,7 @@ namespace api {
#ifdef USE_BINARY_SENSOR
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
return this->client_->send_binary_sensor_state(binary_sensor);
}
#endif
#ifdef USE_COVER
@ -21,27 +21,21 @@ bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fa
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
#endif
#ifdef USE_SENSOR
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
return this->client_->send_sensor_state(sensor, sensor->state);
}
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); }
#endif
#ifdef USE_SWITCH
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
return this->client_->send_switch_state(a_switch, a_switch->state);
}
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); }
#endif
#ifdef USE_TEXT_SENSOR
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
return this->client_->send_text_sensor_state(text_sensor);
}
#endif
#ifdef USE_CLIMATE
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
#endif
#ifdef USE_NUMBER
bool InitialStateIterator::on_number(number::Number *number) {
return this->client_->send_number_state(number, number->state);
}
bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); }
#endif
#ifdef USE_DATETIME_DATE
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
@ -55,15 +49,13 @@ bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) {
}
#endif
#ifdef USE_TEXT
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); }
#endif
#ifdef USE_SELECT
bool InitialStateIterator::on_select(select::Select *select) {
return this->client_->send_select_state(select, select->state);
}
bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); }
#endif
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); }
#endif
#ifdef USE_VALVE
bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); }

View File

@ -75,7 +75,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
resp.data.reserve(param->read.value_len);
// Use bulk insert instead of individual push_backs
resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len);
this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_WRITE_CHAR_EVT:
@ -89,7 +89,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTWriteResponse resp;
resp.address = this->address_;
resp.handle = param->write.handle;
this->proxy_->get_api_connection()->send_bluetooth_gatt_write_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
@ -103,7 +103,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_;
resp.handle = param->unreg_for_notify.handle;
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
@ -116,7 +116,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_;
resp.handle = param->reg_for_notify.handle;
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_NOTIFY_EVT: {
@ -128,7 +128,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
resp.data.reserve(param->notify.value_len);
// Use bulk insert instead of individual push_backs
resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len);
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
default:

View File

@ -39,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
this->api_connection_->send_bluetooth_scanner_state_response(resp);
this->api_connection_->send_message(resp);
}
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
@ -103,7 +103,7 @@ void BluetoothProxy::flush_pending_advertisements() {
api::BluetoothLERawAdvertisementsResponse resp;
resp.advertisements.swap(batch_buffer);
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
this->api_connection_->send_message(resp);
}
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
@ -141,7 +141,7 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
manufacturer_data.data.assign(data.data.begin(), data.data.end());
}
this->api_connection_->send_bluetooth_le_advertisement(resp);
this->api_connection_->send_message(resp);
}
void BluetoothProxy::dump_config() {
@ -302,7 +302,7 @@ void BluetoothProxy::loop() {
service_resp.characteristics.push_back(std::move(characteristic_resp));
}
resp.services.push_back(std::move(service_resp));
this->api_connection_->send_bluetooth_gatt_get_services_response(resp);
this->api_connection_->send_message(resp);
}
}
}
@ -455,7 +455,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
call.success = ret == ESP_OK;
call.error = ret;
this->api_connection_->send_bluetooth_device_clear_cache_response(call);
this->api_connection_->send_message(call);
break;
}
@ -579,7 +579,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui
call.connected = connected;
call.mtu = mtu;
call.error = error;
this->api_connection_->send_bluetooth_device_connection_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_connections_free() {
if (this->api_connection_ == nullptr)
@ -592,7 +592,7 @@ void BluetoothProxy::send_connections_free() {
call.allocated.push_back(connection->address_);
}
}
this->api_connection_->send_bluetooth_connections_free_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_gatt_services_done(uint64_t address) {
@ -600,7 +600,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) {
return;
api::BluetoothGATTGetServicesDoneResponse call;
call.address = address;
this->api_connection_->send_bluetooth_gatt_get_services_done_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
@ -610,7 +610,7 @@ void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_
call.address = address;
call.handle = handle;
call.error = error;
this->api_connection_->send_bluetooth_gatt_error_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
@ -619,7 +619,7 @@ void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_
call.paired = paired;
call.error = error;
this->api_connection_->send_bluetooth_device_pairing_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
@ -628,7 +628,7 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e
call.success = success;
call.error = error;
this->api_connection_->send_bluetooth_device_unpairing_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {

View File

@ -223,7 +223,7 @@ void VoiceAssistant::loop() {
msg.wake_word_phrase = this->wake_word_;
this->wake_word_ = "";
if (this->api_client_ == nullptr || !this->api_client_->send_voice_assistant_request(msg)) {
if (this->api_client_ == nullptr || !this->api_client_->send_message(msg)) {
ESP_LOGW(TAG, "Could not request start");
this->error_trigger_->trigger("not-connected", "Could not request start");
this->continuous_ = false;
@ -245,7 +245,7 @@ void VoiceAssistant::loop() {
if (this->audio_mode_ == AUDIO_MODE_API) {
api::VoiceAssistantAudio msg;
msg.data.assign((char *) this->send_buffer_, read_bytes);
this->api_client_->send_voice_assistant_audio(msg);
this->api_client_->send_message(msg);
} else {
if (!this->udp_socket_running_) {
if (!this->start_udp_socket_()) {
@ -331,7 +331,7 @@ void VoiceAssistant::loop() {
api::VoiceAssistantAnnounceFinished msg;
msg.success = true;
this->api_client_->send_voice_assistant_announce_finished(msg);
this->api_client_->send_message(msg);
break;
}
}
@ -580,7 +580,7 @@ void VoiceAssistant::signal_stop_() {
ESP_LOGD(TAG, "Signaling stop");
api::VoiceAssistantRequest msg;
msg.start = false;
this->api_client_->send_voice_assistant_request(msg);
this->api_client_->send_message(msg);
}
void VoiceAssistant::start_playback_timeout_() {
@ -590,7 +590,7 @@ void VoiceAssistant::start_playback_timeout_() {
api::VoiceAssistantAnnounceFinished msg;
msg.success = true;
this->api_client_->send_voice_assistant_announce_finished(msg);
this->api_client_->send_message(msg);
});
}

View File

@ -258,6 +258,14 @@ class TypeInfo(ABC):
force: Whether to force encoding the field even if it has a default value
"""
@abstractmethod
def get_estimated_size(self) -> int:
"""Get estimated size in bytes for this field with typical values.
Returns:
Estimated size in bytes including field ID and typical data
"""
TYPE_INFO: dict[int, TypeInfo] = {}
@ -291,6 +299,9 @@ class DoubleType(TypeInfo):
o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0.0, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 8 # field ID + 8 bytes for double
@register_type(2)
class FloatType(TypeInfo):
@ -310,6 +321,9 @@ class FloatType(TypeInfo):
o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0.0f, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 4 # field ID + 4 bytes for float
@register_type(3)
class Int64Type(TypeInfo):
@ -329,6 +343,9 @@ class Int64Type(TypeInfo):
o = f"ProtoSize::add_int64_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
@register_type(4)
class UInt64Type(TypeInfo):
@ -348,6 +365,9 @@ class UInt64Type(TypeInfo):
o = f"ProtoSize::add_uint64_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
@register_type(5)
class Int32Type(TypeInfo):
@ -367,6 +387,9 @@ class Int32Type(TypeInfo):
o = f"ProtoSize::add_int32_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
@register_type(6)
class Fixed64Type(TypeInfo):
@ -386,6 +409,9 @@ class Fixed64Type(TypeInfo):
o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed
@register_type(7)
class Fixed32Type(TypeInfo):
@ -405,6 +431,9 @@ class Fixed32Type(TypeInfo):
o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed
@register_type(8)
class BoolType(TypeInfo):
@ -423,6 +452,9 @@ class BoolType(TypeInfo):
o = f"ProtoSize::add_bool_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 1 # field ID + 1 byte
@register_type(9)
class StringType(TypeInfo):
@ -443,6 +475,9 @@ class StringType(TypeInfo):
o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
@register_type(11)
class MessageType(TypeInfo):
@ -478,6 +513,11 @@ class MessageType(TypeInfo):
o = f"ProtoSize::add_message_object(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return (
self.calculate_field_id_size() + 16
) # field ID + 16 bytes estimated submessage
@register_type(12)
class BytesType(TypeInfo):
@ -498,6 +538,9 @@ class BytesType(TypeInfo):
o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
@register_type(13)
class UInt32Type(TypeInfo):
@ -517,6 +560,9 @@ class UInt32Type(TypeInfo):
o = f"ProtoSize::add_uint32_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
@register_type(14)
class EnumType(TypeInfo):
@ -544,6 +590,9 @@ class EnumType(TypeInfo):
o = f"ProtoSize::add_enum_field(total_size, {field_id_size}, static_cast<uint32_t>({name}), {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 1 # field ID + 1 byte typical enum
@register_type(15)
class SFixed32Type(TypeInfo):
@ -563,6 +612,9 @@ class SFixed32Type(TypeInfo):
o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed
@register_type(16)
class SFixed64Type(TypeInfo):
@ -582,6 +634,9 @@ class SFixed64Type(TypeInfo):
o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed
@register_type(17)
class SInt32Type(TypeInfo):
@ -601,6 +656,9 @@ class SInt32Type(TypeInfo):
o = f"ProtoSize::add_sint32_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
@register_type(18)
class SInt64Type(TypeInfo):
@ -620,6 +678,9 @@ class SInt64Type(TypeInfo):
o = f"ProtoSize::add_sint64_field(total_size, {field_id_size}, {name}, {force_str(force)});"
return o
def get_estimated_size(self) -> int:
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
class RepeatedTypeInfo(TypeInfo):
def __init__(self, field: descriptor.FieldDescriptorProto) -> None:
@ -738,6 +799,15 @@ class RepeatedTypeInfo(TypeInfo):
o += "}"
return o
def get_estimated_size(self) -> int:
# For repeated fields, estimate underlying type size * 2 (assume 2 items typically)
underlying_size = (
self._ti.get_estimated_size()
if hasattr(self._ti, "get_estimated_size")
else 8
)
return underlying_size * 2
def build_enum_type(desc) -> tuple[str, str]:
"""Builds the enum type."""
@ -762,6 +832,22 @@ def build_enum_type(desc) -> tuple[str, str]:
return out, cpp
def calculate_message_estimated_size(desc: descriptor.DescriptorProto) -> int:
"""Calculate estimated size for a complete message based on typical values."""
total_size = 0
for field in desc.field:
if field.label == 3: # repeated
ti = RepeatedTypeInfo(field)
else:
ti = TYPE_INFO[field.type](field)
# Add estimated size for this field
total_size += ti.get_estimated_size()
return total_size
def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]:
public_content: list[str] = []
protected_content: list[str] = []
@ -773,6 +859,28 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]:
dump: list[str] = []
size_calc: list[str] = []
# Get message ID if it's a service message
message_id: int | None = get_opt(desc, pb.id)
# Add MESSAGE_TYPE method if this is a service message
if message_id is not None:
# Add static constexpr for message type
public_content.append(f"static constexpr uint16_t MESSAGE_TYPE = {message_id};")
# Add estimated size constant
estimated_size = calculate_message_estimated_size(desc)
public_content.append(
f"static constexpr uint16_t ESTIMATED_SIZE = {estimated_size};"
)
# Add message_name method for debugging
public_content.append("#ifdef HAS_PROTO_MESSAGE_DUMP")
snake_name = camel_to_snake(desc.name)
public_content.append(
f'static constexpr const char *message_name() {{ return "{snake_name}"; }}'
)
public_content.append("#endif")
for field in desc.field:
if field.label == 3:
ti = RepeatedTypeInfo(field)
@ -941,24 +1049,18 @@ def build_service_message_type(
hout = ""
cout = ""
# Store ifdef for later use
if ifdef is not None:
ifdefs[str(mt.name)] = ifdef
hout += f"#ifdef {ifdef}\n"
cout += f"#ifdef {ifdef}\n"
if source in (SOURCE_BOTH, SOURCE_SERVER):
# Generate send
func = f"send_{snake}"
hout += f"bool {func}(const {mt.name} &msg);\n"
cout += f"bool APIServerConnectionBase::{func}(const {mt.name} &msg) {{\n"
if log:
cout += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
cout += f' ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n'
cout += "#endif\n"
# cout += f' this->set_nodelay({str(nodelay).lower()});\n'
cout += f" return this->send_message_<{mt.name}>(msg, {id_});\n"
cout += "}\n"
# Don't generate individual send methods anymore
# The generic send_message method will be used instead
pass
if source in (SOURCE_BOTH, SOURCE_CLIENT):
# Only add ifdef when we're actually generating content
if ifdef is not None:
hout += f"#ifdef {ifdef}\n"
# Generate receive
func = f"on_{snake}"
hout += f"virtual void {func}(const {mt.name} &value){{}};\n"
@ -977,9 +1079,9 @@ def build_service_message_type(
case += "break;"
RECEIVE_CASES[id_] = case
if ifdef is not None:
hout += "#endif\n"
cout += "#endif\n"
# Only close ifdef if we opened it
if ifdef is not None:
hout += "#endif\n"
return hout, cout
@ -1083,6 +1185,29 @@ def main() -> None:
hpp += f"class {class_name} : public ProtoService {{\n"
hpp += " public:\n"
# Add logging helper method declaration
hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
hpp += " protected:\n"
hpp += " void log_send_message_(const char *name, const std::string &dump);\n"
hpp += " public:\n"
hpp += "#endif\n\n"
# Add generic send_message method
hpp += " template<typename T>\n"
hpp += " bool send_message(const T &msg) {\n"
hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
hpp += " this->log_send_message_(T::message_name(), msg.dump());\n"
hpp += "#endif\n"
hpp += " return this->send_message_(msg, T::MESSAGE_TYPE);\n"
hpp += " }\n\n"
# Add logging helper method implementation to cpp
cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
cpp += f"void {class_name}::log_send_message_(const char *name, const std::string &dump) {{\n"
cpp += ' ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());\n'
cpp += "}\n"
cpp += "#endif\n\n"
for mt in file.message_type:
obj = build_service_message_type(mt)
if obj is None:
@ -1155,8 +1280,7 @@ def main() -> None:
body += f"this->{func}(msg);\n"
else:
body += f"{ret} ret = this->{func}(msg);\n"
ret_snake = camel_to_snake(ret)
body += f"if (!this->send_{ret_snake}(ret)) {{\n"
body += "if (!this->send_message(ret)) {\n"
body += " this->on_fatal_error();\n"
body += "}\n"
cpp += indent(body) + "\n" + "}\n"

View File

@ -0,0 +1,55 @@
esphome:
name: host-batch-delay-test
host:
api:
batch_delay: 0ms
logger:
# Add multiple sensors to test batching
sensor:
- platform: template
name: "Test Sensor 1"
id: test_sensor1
lambda: |-
return 1.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 2"
id: test_sensor2
lambda: |-
return 2.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 3"
id: test_sensor3
lambda: |-
return 3.0;
update_interval: 0.1s
binary_sensor:
- platform: template
name: "Test Binary Sensor 1"
id: test_binary_sensor1
lambda: |-
return millis() % 1000 < 500;
- platform: template
name: "Test Binary Sensor 2"
id: test_binary_sensor2
lambda: |-
return millis() % 2000 < 1000;
switch:
- platform: template
name: "Test Switch 1"
id: test_switch1
turn_on_action:
- logger.log: "Switch 1 turned on"
turn_off_action:
- logger.log: "Switch 1 turned off"
- platform: template
name: "Test Switch 2"
id: test_switch2
turn_on_action:
- logger.log: "Switch 2 turned on"
turn_off_action:
- logger.log: "Switch 2 turned off"

View File

@ -0,0 +1,322 @@
esphome:
name: host-mode-many-entities
friendly_name: "Host Mode Many Entities Test"
logger:
host:
api:
sensor:
# 50 test sensors with predictable values for batching test
- platform: template
name: "Test Sensor 1"
lambda: return 1.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 2"
lambda: return 2.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 3"
lambda: return 3.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 4"
lambda: return 4.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 5"
lambda: return 5.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 6"
lambda: return 6.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 7"
lambda: return 7.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 8"
lambda: return 8.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 9"
lambda: return 9.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 10"
lambda: return 10.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 11"
lambda: return 11.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 12"
lambda: return 12.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 13"
lambda: return 13.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 14"
lambda: return 14.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 15"
lambda: return 15.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 16"
lambda: return 16.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 17"
lambda: return 17.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 18"
lambda: return 18.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 19"
lambda: return 19.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 20"
lambda: return 20.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 21"
lambda: return 21.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 22"
lambda: return 22.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 23"
lambda: return 23.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 24"
lambda: return 24.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 25"
lambda: return 25.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 26"
lambda: return 26.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 27"
lambda: return 27.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 28"
lambda: return 28.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 29"
lambda: return 29.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 30"
lambda: return 30.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 31"
lambda: return 31.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 32"
lambda: return 32.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 33"
lambda: return 33.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 34"
lambda: return 34.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 35"
lambda: return 35.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 36"
lambda: return 36.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 37"
lambda: return 37.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 38"
lambda: return 38.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 39"
lambda: return 39.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 40"
lambda: return 40.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 41"
lambda: return 41.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 42"
lambda: return 42.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 43"
lambda: return 43.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 44"
lambda: return 44.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 45"
lambda: return 45.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 46"
lambda: return 46.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 47"
lambda: return 47.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 48"
lambda: return 48.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 49"
lambda: return 49.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 50"
lambda: return 50.0;
update_interval: 0.1s
# Mixed entity types for comprehensive batching test
binary_sensor:
- platform: template
name: "Test Binary Sensor 1"
lambda: return millis() % 1000 < 500;
- platform: template
name: "Test Binary Sensor 2"
lambda: return millis() % 2000 < 1000;
switch:
- platform: template
name: "Test Switch 1"
lambda: return true;
turn_on_action:
- logger.log: "Switch 1 ON"
turn_off_action:
- logger.log: "Switch 1 OFF"
- platform: template
name: "Test Switch 2"
lambda: return false;
turn_on_action:
- logger.log: "Switch 2 ON"
turn_off_action:
- logger.log: "Switch 2 OFF"
text_sensor:
- platform: template
name: "Test Text Sensor 1"
lambda: return std::string("Test Value 1");
- platform: template
name: "Test Text Sensor 2"
lambda: return std::string("Test Value 2");
- platform: version
name: "ESPHome Version"
number:
- platform: template
name: "Test Number"
min_value: 0
max_value: 100
step: 1
lambda: return 50.0;
set_action:
- logger.log: "Number set"
select:
- platform: template
name: "Test Select"
options:
- "Option 1"
- "Option 2"
initial_option: "Option 1"
optimistic: true
set_action:
- logger.log: "Select changed"
text:
- platform: template
name: "Test Text"
mode: text
initial_value: "Hello"
set_action:
- logger.log: "Text changed"
valve:
- platform: template
name: "Test Valve"
open_action:
- logger.log: "Valve opening"
close_action:
- logger.log: "Valve closing"
stop_action:
- logger.log: "Valve stopping"
alarm_control_panel:
- platform: template
name: "Test Alarm"
codes:
- "1234"
arming_away_time: 0s
arming_home_time: 0s
pending_time: 0s
trigger_time: 300s
restore_mode: ALWAYS_DISARMED
on_disarmed:
- logger.log: "Alarm disarmed"
on_arming:
- logger.log: "Alarm arming"
on_armed_away:
- logger.log: "Alarm armed away"
on_armed_home:
- logger.log: "Alarm armed home"
on_pending:
- logger.log: "Alarm pending"
on_triggered:
- logger.log: "Alarm triggered"
event:
- platform: template
name: "Test Event"
event_types:
- first_event
- second_event
button:
- platform: template
name: "Test Button"
on_press:
- logger.log: "Button pressed"

View File

@ -0,0 +1,136 @@
esphome:
name: host-mode-many-entities-multi
friendly_name: "Host Mode Many Entities Multiple Connections Test"
logger:
host:
api:
sensor:
# 20 test sensors for faster testing with multiple connections
- platform: template
name: "Test Sensor 1"
lambda: return 1.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 2"
lambda: return 2.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 3"
lambda: return 3.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 4"
lambda: return 4.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 5"
lambda: return 5.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 6"
lambda: return 6.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 7"
lambda: return 7.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 8"
lambda: return 8.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 9"
lambda: return 9.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 10"
lambda: return 10.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 11"
lambda: return 11.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 12"
lambda: return 12.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 13"
lambda: return 13.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 14"
lambda: return 14.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 15"
lambda: return 15.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 16"
lambda: return 16.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 17"
lambda: return 17.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 18"
lambda: return 18.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 19"
lambda: return 19.0;
update_interval: 0.1s
- platform: template
name: "Test Sensor 20"
lambda: return 20.0;
update_interval: 0.1s
# Mixed entity types for comprehensive batching test
binary_sensor:
- platform: template
name: "Test Binary Sensor 1"
lambda: return millis() % 1000 < 500;
- platform: template
name: "Test Binary Sensor 2"
lambda: return millis() % 2000 < 1000;
text_sensor:
- platform: template
name: "Test Text Sensor 1"
lambda: return std::string("Test Value 1");
- platform: template
name: "Test Text Sensor 2"
lambda: return std::string("Test Value 2");
- platform: version
name: "ESPHome Version"
switch:
- platform: template
name: "Test Switch 1"
lambda: return true;
turn_on_action:
- logger.log: "Switch 1 ON"
turn_off_action:
- logger.log: "Switch 1 OFF"
button:
- platform: template
name: "Test Button"
on_press:
- logger.log: "Button pressed"
number:
- platform: template
name: "Test Number"
min_value: 0
max_value: 100
step: 1
lambda: return 50.0;
set_action:
- logger.log: "Number set"

View File

@ -5,3 +5,46 @@ api:
encryption:
key: N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU=
logger:
# Test sensors to verify batching works with noise encryption
sensor:
- platform: template
name: "Noise Test Sensor 1"
lambda: return 1.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 2"
lambda: return 2.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 3"
lambda: return 3.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 4"
lambda: return 4.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 5"
lambda: return 5.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 6"
lambda: return 6.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 7"
lambda: return 7.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 8"
lambda: return 8.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 9"
lambda: return 9.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 10"
lambda: return 10.0;
update_interval: 2s

View File

@ -0,0 +1,80 @@
"""Integration test for API batch_delay setting."""
from __future__ import annotations
import asyncio
import time
from aioesphomeapi import EntityState
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_host_mode_batch_delay(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test API with batch_delay set to 0ms - messages should be sent immediately without batching."""
# Write, compile and run the ESPHome device, then connect to API
loop = asyncio.get_running_loop()
async with run_compiled(yaml_config), api_client_connected() as client:
# Verify we can get device info
device_info = await client.device_info()
assert device_info is not None
assert device_info.name == "host-batch-delay-test"
# Subscribe to state changes
states: dict[int, EntityState] = {}
state_timestamps: dict[int, float] = {}
entity_count_future: asyncio.Future[int] = loop.create_future()
def on_state(state: EntityState) -> None:
"""Track when states are received."""
states[state.key] = state
state_timestamps[state.key] = time.monotonic()
# When we have received all expected entities, resolve the future
if len(states) >= 7 and not entity_count_future.done():
entity_count_future.set_result(len(states))
client.subscribe_states(on_state)
# Wait for states from all entities with timeout
try:
entity_count = await asyncio.wait_for(entity_count_future, timeout=5.0)
except asyncio.TimeoutError:
pytest.fail(
f"Did not receive states from at least 7 entities within 5 seconds. "
f"Received {len(states)} states"
)
# Verify we received all states
assert entity_count >= 7, f"Expected at least 7 entities, got {entity_count}"
assert len(states) >= 7 # 3 sensors + 2 binary sensors + 2 switches
# When batch_delay is 0ms, states are sent immediately without batching
# This means each state arrives in its own packet, which may actually be slower
# than batching due to network overhead
if state_timestamps:
first_timestamp = min(state_timestamps.values())
last_timestamp = max(state_timestamps.values())
time_spread = last_timestamp - first_timestamp
# With batch_delay=0ms, states arrive individually which may take longer
# We just verify they all arrive within a reasonable time
assert time_spread < 1.0, f"States took {time_spread:.3f}s to arrive"
# Also test list_entities - with batch_delay=0ms each entity is sent separately
start_time = time.monotonic()
entity_info, services = await client.list_entities_services()
end_time = time.monotonic()
list_time = end_time - start_time
# Verify we got all expected entities
assert len(entity_info) >= 7 # 3 sensors + 2 binary sensors + 2 switches
# With batch_delay=0ms, listing sends each entity separately which may be slower
assert list_time < 1.0, f"list_entities took {list_time:.3f}s"

View File

@ -0,0 +1,57 @@
"""Integration test for many entities to test API batching."""
from __future__ import annotations
import asyncio
from aioesphomeapi import EntityState
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_host_mode_many_entities(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test API batching with many entities of different types."""
# Write, compile and run the ESPHome device, then connect to API
loop = asyncio.get_running_loop()
async with run_compiled(yaml_config), api_client_connected() as client:
# Subscribe to state changes
states: dict[int, EntityState] = {}
entity_count_future: asyncio.Future[int] = loop.create_future()
def on_state(state: EntityState) -> None:
states[state.key] = state
# When we have received states from a good number of entities, resolve the future
if len(states) >= 50 and not entity_count_future.done():
entity_count_future.set_result(len(states))
client.subscribe_states(on_state)
# Wait for states from at least 50 entities with timeout
try:
entity_count = await asyncio.wait_for(entity_count_future, timeout=10.0)
except asyncio.TimeoutError:
pytest.fail(
f"Did not receive states from at least 50 entities within 10 seconds. "
f"Received {len(states)} states: {list(states.keys())}"
)
# Verify we received a good number of entity states
assert entity_count >= 50, f"Expected at least 50 entities, got {entity_count}"
assert len(states) >= 50, f"Expected at least 50 states, got {len(states)}"
# Verify we have different entity types by checking some expected values
sensor_states = [
s
for s in states.values()
if hasattr(s, "state") and isinstance(s.state, float)
]
assert len(sensor_states) >= 50, (
f"Expected at least 50 sensor states, got {len(sensor_states)}"
)

View File

@ -0,0 +1,71 @@
"""Integration test for shared buffer optimization with multiple API connections."""
from __future__ import annotations
import asyncio
from aioesphomeapi import EntityState
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_host_mode_many_entities_multiple_connections(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test shared buffer optimization with multiple API connections."""
# Write, compile and run the ESPHome device
loop = asyncio.get_running_loop()
async with (
run_compiled(yaml_config),
api_client_connected() as client1,
api_client_connected() as client2,
):
# Subscribe both clients to state changes
states1: dict[int, EntityState] = {}
states2: dict[int, EntityState] = {}
client1_ready = loop.create_future()
client2_ready = loop.create_future()
def on_state1(state: EntityState) -> None:
states1[state.key] = state
if len(states1) >= 20 and not client1_ready.done():
client1_ready.set_result(len(states1))
def on_state2(state: EntityState) -> None:
states2[state.key] = state
if len(states2) >= 20 and not client2_ready.done():
client2_ready.set_result(len(states2))
client1.subscribe_states(on_state1)
client2.subscribe_states(on_state2)
# Wait for both clients to receive states
try:
count1, count2 = await asyncio.gather(
asyncio.wait_for(client1_ready, timeout=10.0),
asyncio.wait_for(client2_ready, timeout=10.0),
)
except asyncio.TimeoutError:
pytest.fail(
f"One or both clients did not receive enough states within 10 seconds. "
f"Client1: {len(states1)}, Client2: {len(states2)}"
)
# Verify both clients received states successfully
assert count1 >= 20, (
f"Client 1 should have received at least 20 states, got {count1}"
)
assert count2 >= 20, (
f"Client 2 should have received at least 20 states, got {count2}"
)
# Verify both clients received the same entity keys (same device state)
common_keys = set(states1.keys()) & set(states2.keys())
assert len(common_keys) >= 20, (
f"Expected at least 20 common entity keys, got {len(common_keys)}"
)