From 2ed5611a0835eea019f5bdbdf63d39dc4e9ffe65 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 10 Jun 2025 18:49:15 -0500 Subject: [PATCH] Replace API deferred queue with efficient message batching system (#9012) --- esphome/components/api/__init__.py | 5 + esphome/components/api/api_connection.cpp | 990 ++++++++++++------ esphome/components/api/api_connection.h | 625 ++++++----- esphome/components/api/api_frame_helper.cpp | 235 +++-- esphome/components/api/api_frame_helper.h | 20 + esphome/components/api/api_pb2.h | 635 +++++++++++ esphome/components/api/api_pb2_service.cpp | 700 +------------ esphome/components/api/api_pb2_service.h | 260 +---- esphome/components/api/api_server.cpp | 28 +- esphome/components/api/api_server.h | 7 + esphome/components/api/list_entities.cpp | 2 +- esphome/components/api/proto.h | 4 +- esphome/components/api/subscribe_state.cpp | 24 +- .../bluetooth_proxy/bluetooth_connection.cpp | 10 +- .../bluetooth_proxy/bluetooth_proxy.cpp | 22 +- .../voice_assistant/voice_assistant.cpp | 10 +- script/api_protobuf/api_protobuf.py | 160 ++- .../fixtures/host_mode_batch_delay.yaml | 55 + .../fixtures/host_mode_many_entities.yaml | 322 ++++++ ...de_many_entities_multiple_connections.yaml | 136 +++ .../fixtures/host_mode_noise_encryption.yaml | 43 + .../integration/test_host_mode_batch_delay.py | 80 ++ .../test_host_mode_many_entities.py | 57 + ...mode_many_entities_multiple_connections.py | 71 ++ 24 files changed, 2832 insertions(+), 1669 deletions(-) create mode 100644 tests/integration/fixtures/host_mode_batch_delay.yaml create mode 100644 tests/integration/fixtures/host_mode_many_entities.yaml create mode 100644 tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml create mode 100644 tests/integration/test_host_mode_batch_delay.py create mode 100644 tests/integration/test_host_mode_many_entities.py create mode 100644 tests/integration/test_host_mode_many_entities_multiple_connections.py diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 4b63c76fba..bd131ef8de 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -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 = [] diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index e6875a5d6d..9ef00546c8 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #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 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 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(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(binary_sensor), - reinterpret_cast(&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(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(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(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(cover), - reinterpret_cast(&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(cover), - reinterpret_cast(&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(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(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(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(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(fan), - reinterpret_cast(&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(fan), - reinterpret_cast(&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(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(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(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(light), - reinterpret_cast(&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(light), - reinterpret_cast(&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(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(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(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(sensor), - reinterpret_cast(&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(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(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(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(a_switch), - reinterpret_cast(&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(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(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(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(text_sensor), - reinterpret_cast(&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(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(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(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(climate), - reinterpret_cast(&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(climate), - reinterpret_cast(&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(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(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(swing_mode)); msg.unique_id = get_default_unique_id("climate", climate); - return this->try_send_entity_info_(static_cast(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(number), - reinterpret_cast(&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(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(entity); ListEntitiesNumberResponse msg; msg.unit_of_measurement = number->traits.get_unit_of_measurement(); msg.mode = static_cast(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(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(date), - reinterpret_cast(&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(date), - reinterpret_cast(&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(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(entity); ListEntitiesDateResponse msg; msg.unique_id = get_default_unique_id("date", date); - return this->try_send_entity_info_(static_cast(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(time), - reinterpret_cast(&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(time), - reinterpret_cast(&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(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(entity); ListEntitiesTimeResponse msg; msg.unique_id = get_default_unique_id("time", time); - return this->try_send_entity_info_(static_cast(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(datetime), - reinterpret_cast(&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(datetime), - reinterpret_cast(&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(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(entity); ListEntitiesDateTimeResponse msg; msg.unique_id = get_default_unique_id("datetime", datetime); - return this->try_send_entity_info_(static_cast(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(text), - reinterpret_cast(&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(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(entity); ListEntitiesTextResponse msg; msg.mode = static_cast(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(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(select), - reinterpret_cast(&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(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(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(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(button), - reinterpret_cast(&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(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(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(a_lock), - reinterpret_cast(&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(state); +uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_lock = static_cast(entity); + LockStateResponse resp; + resp.state = static_cast(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(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(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(valve), - reinterpret_cast(&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(valve), - reinterpret_cast(&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(entity); ValveStateResponse resp; resp.position = valve->position; resp.current_operation = static_cast(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(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(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(media_player), - reinterpret_cast(&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(media_player), - reinterpret_cast(&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(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(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(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(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 this->image_reader_.set_image(std::move(image)); } void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { - this->send_info_(static_cast(camera), - reinterpret_cast(&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(entity); ListEntitiesCameraResponse msg; msg.unique_id = get_default_unique_id("camera", camera); - return this->try_send_entity_info_(static_cast(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(a_alarm_control_panel), - reinterpret_cast(&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(a_alarm_control_panel), - reinterpret_cast(&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(entity); AlarmControlPanelStateResponse resp; resp.state = static_cast(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(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(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(event), - reinterpret_cast(&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(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(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(update), - reinterpret_cast(&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(update), - reinterpret_cast(&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(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(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(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::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 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(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 diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index f965a9e795..6e85aba298 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -11,6 +11,7 @@ #include "esphome/core/entity_base.h" #include +#include 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 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, 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 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 &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 &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 - 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(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 - 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 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(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 proto_write_buffer_; std::unique_ptr 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 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 diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 54b80a0852..f5467992cf 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -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 *raw_buffer = buffer.get_buffer(); + uint16_t payload_len = static_cast(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 packets; + packets.emplace_back(type, 0, payload_len); + + return write_protobuf_packets(buffer, packets); +} + +APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &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 *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 *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 *raw_buffer = buffer.get_buffer(); + uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); + + // Use write_protobuf_packets with a single packet + std::vector packets; + packets.emplace_back(type, 0, payload_len); + + return write_protobuf_packets(buffer, packets); +} + +APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, + const std::vector &packets) { if (state_ != State::DATA) { return APIError::BAD_STATE; } - std::vector *raw_buffer = buffer.get_buffer(); - // Message data starts after padding (frame_header_padding_ = 6) - uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); - - // Calculate varint sizes for header components - uint8_t size_varint_len = api::ProtoSize::varint(static_cast(payload_len)); - uint8_t type_varint_len = api::ProtoSize::varint(static_cast(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 *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(payload_len)); + uint8_t type_varint_len = api::ProtoSize::varint(static_cast(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 diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 0799ae0f85..dc71a7ca17 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -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 &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 reusable_iovs_; + // Receive buffer for reading frame data std::vector 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 &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 &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_; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 1869fc5ba1..8b3f7a7b2a 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -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 data{}; std::vector 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 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 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 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 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 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 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 available_wake_words{}; std::vector 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 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; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 5a701aeafa..dacb23c12b 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(msg, 27); -} -#endif -bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) { - return this->send_message_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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_(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(); } } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 8ee5c0fcf1..b2be314aaf 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -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 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 diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index d2ab4398bb..378aa63981 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -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; } diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index a8c4a0f009..71e470d4f8 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -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 &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_ = nullptr; uint16_t port_{6053}; uint32_t reboot_timeout_{300000}; + uint32_t batch_delay_{100}; uint32_t last_connected_{0}; std::vector> clients_; std::string password_; + std::vector shared_write_buffer_; // Shared proto write buffer for all connections std::vector state_subs_; std::vector user_services_; Trigger *client_connected_trigger_ = new Trigger(); diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 574aa2525b..ceee3f00b8 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -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 diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index fae722f750..b3440dbfe0 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -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 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); diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 4b1d5ebc0d..4180435fcc 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -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); } diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 3c5c2bd438..44d434802c 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -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: diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 32c2126c1e..7aeb818306 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -39,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta resp.state = static_cast(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) { diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 5b715f4821..a692a7556e 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -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); }); } diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 63c1efa1ee..d634be98c4 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -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({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\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" diff --git a/tests/integration/fixtures/host_mode_batch_delay.yaml b/tests/integration/fixtures/host_mode_batch_delay.yaml new file mode 100644 index 0000000000..8abe1da6fb --- /dev/null +++ b/tests/integration/fixtures/host_mode_batch_delay.yaml @@ -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" diff --git a/tests/integration/fixtures/host_mode_many_entities.yaml b/tests/integration/fixtures/host_mode_many_entities.yaml new file mode 100644 index 0000000000..3d1aa36196 --- /dev/null +++ b/tests/integration/fixtures/host_mode_many_entities.yaml @@ -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" diff --git a/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml b/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml new file mode 100644 index 0000000000..296cb2455f --- /dev/null +++ b/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml @@ -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" diff --git a/tests/integration/fixtures/host_mode_noise_encryption.yaml b/tests/integration/fixtures/host_mode_noise_encryption.yaml index 83605e28a3..da817fdc3b 100644 --- a/tests/integration/fixtures/host_mode_noise_encryption.yaml +++ b/tests/integration/fixtures/host_mode_noise_encryption.yaml @@ -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 diff --git a/tests/integration/test_host_mode_batch_delay.py b/tests/integration/test_host_mode_batch_delay.py new file mode 100644 index 0000000000..5165b90e47 --- /dev/null +++ b/tests/integration/test_host_mode_batch_delay.py @@ -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" diff --git a/tests/integration/test_host_mode_many_entities.py b/tests/integration/test_host_mode_many_entities.py new file mode 100644 index 0000000000..d5622e6fa4 --- /dev/null +++ b/tests/integration/test_host_mode_many_entities.py @@ -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)}" + ) diff --git a/tests/integration/test_host_mode_many_entities_multiple_connections.py b/tests/integration/test_host_mode_many_entities_multiple_connections.py new file mode 100644 index 0000000000..a4e5f8a45c --- /dev/null +++ b/tests/integration/test_host_mode_many_entities_multiple_connections.py @@ -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)}" + )