From 020c4af21803e48887a38823335c40dcb6b99e21 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Jun 2025 03:37:08 +0100 Subject: [PATCH] adj --- esphome/components/api/api_connection.cpp | 448 ++++++++++++---------- esphome/components/api/api_connection.h | 170 +++++--- 2 files changed, 356 insertions(+), 262 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 9dfef87a08..3deecdda2e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -254,22 +254,24 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) { #ifdef USE_BINARY_SENSOR bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) { - return this->schedule_message_( - binary_sensor, - [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *bs = static_cast(entity); - BinarySensorStateResponse msg; // Stack allocated! - msg.state = state; - msg.missing_state = !bs->has_state(); - msg.key = bs->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_(binary_sensor, + [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *bs = static_cast(entity); + BinarySensorStateResponse msg; // Stack allocated! + msg.state = state; + msg.missing_state = !bs->has_state(); + msg.key = bs->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) { - this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info_); + this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info_, + ListEntitiesBinarySensorResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_binary_sensor_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *binary_sensor = static_cast(entity); ListEntitiesBinarySensorResponse msg; // Stack allocated! msg.device_class = binary_sensor->get_device_class(); @@ -279,7 +281,7 @@ APIConnection::MessageInfo APIConnection::try_send_binary_sensor_info_(EntityBas // Fill common entity fields fill_entity_info_base_(binary_sensor, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } #endif @@ -288,10 +290,11 @@ bool APIConnection::send_cover_state(cover::Cover *cover) { return this->schedule_message_(cover, &APIConnection::try_send_cover_state_); } void APIConnection::send_cover_info(cover::Cover *cover) { - this->schedule_message_(cover, &APIConnection::try_send_cover_info_); + this->schedule_message_(cover, &APIConnection::try_send_cover_info_, ListEntitiesCoverResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_cover_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *cover = static_cast(entity); CoverStateResponse msg; // Stack allocated! auto traits = cover->get_traits(); @@ -302,10 +305,11 @@ APIConnection::MessageInfo APIConnection::try_send_cover_state_(EntityBase *enti msg.tilt = cover->tilt; msg.current_operation = static_cast(cover->current_operation); msg.key = cover->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } APIConnection::MessageInfo APIConnection::try_send_cover_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *cover = static_cast(entity); ListEntitiesCoverResponse msg; // Stack allocated! auto traits = cover->get_traits(); @@ -316,7 +320,7 @@ APIConnection::MessageInfo APIConnection::try_send_cover_info_(EntityBase *entit msg.device_class = cover->get_device_class(); msg.unique_id = get_default_unique_id("cover", cover); fill_entity_info_base_(cover, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::cover_command(const CoverCommandRequest &msg) { cover::Cover *cover = App.get_cover_by_key(msg.key); @@ -351,9 +355,12 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { bool APIConnection::send_fan_state(fan::Fan *fan) { return this->schedule_message_(fan, &APIConnection::try_send_fan_state_); } -void APIConnection::send_fan_info(fan::Fan *fan) { this->schedule_message_(fan, &APIConnection::try_send_fan_info_); } +void APIConnection::send_fan_info(fan::Fan *fan) { + this->schedule_message_(fan, &APIConnection::try_send_fan_info_, ListEntitiesFanResponse::message_type); +} APIConnection::MessageInfo APIConnection::try_send_fan_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *fan = static_cast(entity); FanStateResponse msg; // Stack allocated! auto traits = fan->get_traits(); @@ -368,10 +375,11 @@ APIConnection::MessageInfo APIConnection::try_send_fan_state_(EntityBase *entity if (traits.supports_preset_modes()) msg.preset_mode = fan->preset_mode; msg.key = fan->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } APIConnection::MessageInfo APIConnection::try_send_fan_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *fan = static_cast(entity); ListEntitiesFanResponse msg; // Stack allocated! auto traits = fan->get_traits(); @@ -383,7 +391,7 @@ APIConnection::MessageInfo APIConnection::try_send_fan_info_(EntityBase *entity, msg.supported_preset_modes.push_back(preset); msg.unique_id = get_default_unique_id("fan", fan); fill_entity_info_base_(fan, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::fan_command(const FanCommandRequest &msg) { fan::Fan *fan = App.get_fan_by_key(msg.key); @@ -412,10 +420,11 @@ bool APIConnection::send_light_state(light::LightState *light) { return this->schedule_message_(light, &APIConnection::try_send_light_state_); } void APIConnection::send_light_info(light::LightState *light) { - this->schedule_message_(light, &APIConnection::try_send_light_info_); + this->schedule_message_(light, &APIConnection::try_send_light_info_, ListEntitiesLightResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_light_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *light = static_cast(entity); LightStateResponse msg; // Stack allocated! auto traits = light->get_traits(); @@ -435,10 +444,11 @@ APIConnection::MessageInfo APIConnection::try_send_light_state_(EntityBase *enti if (light->supports_effects()) msg.effect = light->get_effect_name(); msg.key = light->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } APIConnection::MessageInfo APIConnection::try_send_light_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *light = static_cast(entity); ListEntitiesLightResponse msg; // Stack allocated! auto traits = light->get_traits(); @@ -463,7 +473,7 @@ APIConnection::MessageInfo APIConnection::try_send_light_info_(EntityBase *entit } msg.unique_id = get_default_unique_id("light", light); fill_entity_info_base_(light, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::light_command(const LightCommandRequest &msg) { light::LightState *light = App.get_light_by_key(msg.key); @@ -504,22 +514,23 @@ void APIConnection::light_command(const LightCommandRequest &msg) { #ifdef USE_SENSOR bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) { - return this->schedule_message_( - sensor, - [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *s = static_cast(entity); - SensorStateResponse msg; // Stack allocated! - msg.state = state; - msg.missing_state = !s->has_state(); - msg.key = s->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_(sensor, + [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *s = static_cast(entity); + SensorStateResponse msg; // Stack allocated! + msg.state = state; + msg.missing_state = !s->has_state(); + msg.key = s->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_sensor_info(sensor::Sensor *sensor) { - this->schedule_message_(sensor, &APIConnection::try_send_sensor_info_); + this->schedule_message_(sensor, &APIConnection::try_send_sensor_info_, ListEntitiesSensorResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_sensor_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *sensor = static_cast(entity); ListEntitiesSensorResponse msg; // Stack allocated! msg.unit_of_measurement = sensor->get_unit_of_measurement(); @@ -531,34 +542,35 @@ APIConnection::MessageInfo APIConnection::try_send_sensor_info_(EntityBase *enti if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("sensor", sensor); fill_entity_info_base_(sensor, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } #endif #ifdef USE_SWITCH bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) { - return this->schedule_message_( - a_switch, - [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *sw = static_cast(entity); - SwitchStateResponse msg; // Stack allocated! - msg.state = state; - msg.key = sw->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_(a_switch, + [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *sw = static_cast(entity); + SwitchStateResponse msg; // Stack allocated! + msg.state = state; + msg.key = sw->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_switch_info(switch_::Switch *a_switch) { - this->schedule_message_(a_switch, &APIConnection::try_send_switch_info_); + this->schedule_message_(a_switch, &APIConnection::try_send_switch_info_, ListEntitiesSwitchResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_switch_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *a_switch = static_cast(entity); ListEntitiesSwitchResponse msg; // Stack allocated! 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); fill_entity_info_base_(a_switch, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::switch_command(const SwitchCommandRequest &msg) { switch_::Switch *a_switch = App.get_switch_by_key(msg.key); @@ -575,22 +587,25 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) { #ifdef USE_TEXT_SENSOR bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) { - return this->schedule_message_(text_sensor, - [state = std::move(state)](EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *ts = static_cast(entity); - TextSensorStateResponse msg; // Stack allocated! - msg.state = state; - msg.missing_state = !ts->has_state(); - msg.key = ts->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_( + text_sensor, + [state = std::move(state)](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *ts = static_cast(entity); + TextSensorStateResponse msg; // Stack allocated! + msg.state = state; + msg.missing_state = !ts->has_state(); + msg.key = ts->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) { - this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info_); + this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info_, + ListEntitiesTextSensorResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_text_sensor_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *text_sensor = static_cast(entity); ListEntitiesTextSensorResponse msg; // Stack allocated! msg.device_class = text_sensor->get_device_class(); @@ -601,7 +616,7 @@ APIConnection::MessageInfo APIConnection::try_send_text_sensor_info_(EntityBase // Fill common entity fields fill_entity_info_base_(text_sensor, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } #endif @@ -610,7 +625,8 @@ bool APIConnection::send_climate_state(climate::Climate *climate) { return this->schedule_message_(climate, &APIConnection::try_send_climate_state_); } APIConnection::MessageInfo APIConnection::try_send_climate_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *climate = static_cast(entity); ClimateStateResponse msg; // Stack allocated! msg.key = climate->get_object_id_hash(); @@ -640,13 +656,14 @@ APIConnection::MessageInfo APIConnection::try_send_climate_state_(EntityBase *en msg.current_humidity = climate->current_humidity; if (traits.get_supports_target_humidity()) msg.target_humidity = climate->target_humidity; - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_climate_info(climate::Climate *climate) { - this->schedule_message_(climate, &APIConnection::try_send_climate_info_); + this->schedule_message_(climate, &APIConnection::try_send_climate_info_, ListEntitiesClimateResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_climate_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *climate = static_cast(entity); ListEntitiesClimateResponse msg; // Stack allocated! auto traits = climate->get_traits(); @@ -679,7 +696,7 @@ APIConnection::MessageInfo APIConnection::try_send_climate_info_(EntityBase *ent // Fill common entity fields fill_entity_info_base_(climate, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::climate_command(const ClimateCommandRequest &msg) { climate::Climate *climate = App.get_climate_by_key(msg.key); @@ -713,22 +730,23 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) { #ifdef USE_NUMBER bool APIConnection::send_number_state(number::Number *number, float state) { - return this->schedule_message_( - number, - [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *n = static_cast(entity); - NumberStateResponse msg; // Stack allocated! - msg.state = state; - msg.missing_state = !n->has_state(); - msg.key = n->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_(number, + [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *n = static_cast(entity); + NumberStateResponse msg; // Stack allocated! + msg.state = state; + msg.missing_state = !n->has_state(); + msg.key = n->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_number_info(number::Number *number) { - this->schedule_message_(number, &APIConnection::try_send_number_info_); + this->schedule_message_(number, &APIConnection::try_send_number_info_, ListEntitiesNumberResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_number_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *number = static_cast(entity); ListEntitiesNumberResponse msg; // Stack allocated! msg.unit_of_measurement = number->traits.get_unit_of_measurement(); @@ -742,7 +760,7 @@ APIConnection::MessageInfo APIConnection::try_send_number_info_(EntityBase *enti // Fill common entity fields fill_entity_info_base_(number, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::number_command(const NumberCommandRequest &msg) { number::Number *number = App.get_number_by_key(msg.key); @@ -760,7 +778,8 @@ bool APIConnection::send_date_state(datetime::DateEntity *date) { return this->schedule_message_(date, &APIConnection::try_send_date_state_); } APIConnection::MessageInfo APIConnection::try_send_date_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *date = static_cast(entity); DateStateResponse msg; // Stack allocated! msg.missing_state = !date->has_state(); @@ -768,13 +787,14 @@ APIConnection::MessageInfo APIConnection::try_send_date_state_(EntityBase *entit msg.month = date->month; msg.day = date->day; msg.key = date->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_date_info(datetime::DateEntity *date) { - this->schedule_message_(date, &APIConnection::try_send_date_info_); + this->schedule_message_(date, &APIConnection::try_send_date_info_, ListEntitiesDateResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_date_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *date = static_cast(entity); ListEntitiesDateResponse msg; // Stack allocated! msg.unique_id = get_default_unique_id("date", date); @@ -782,7 +802,7 @@ APIConnection::MessageInfo APIConnection::try_send_date_info_(EntityBase *entity // Fill common entity fields fill_entity_info_base_(date, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::date_command(const DateCommandRequest &msg) { datetime::DateEntity *date = App.get_date_by_key(msg.key); @@ -800,7 +820,8 @@ bool APIConnection::send_time_state(datetime::TimeEntity *time) { return this->schedule_message_(time, &APIConnection::try_send_time_state_); } APIConnection::MessageInfo APIConnection::try_send_time_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *time = static_cast(entity); TimeStateResponse msg; // Stack allocated! msg.missing_state = !time->has_state(); @@ -808,13 +829,14 @@ APIConnection::MessageInfo APIConnection::try_send_time_state_(EntityBase *entit msg.minute = time->minute; msg.second = time->second; msg.key = time->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_time_info(datetime::TimeEntity *time) { - this->schedule_message_(time, &APIConnection::try_send_time_info_); + this->schedule_message_(time, &APIConnection::try_send_time_info_, ListEntitiesTimeResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_time_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *time = static_cast(entity); ListEntitiesTimeResponse msg; // Stack allocated! msg.unique_id = get_default_unique_id("time", time); @@ -822,7 +844,7 @@ APIConnection::MessageInfo APIConnection::try_send_time_info_(EntityBase *entity // Fill common entity fields fill_entity_info_base_(time, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::time_command(const TimeCommandRequest &msg) { datetime::TimeEntity *time = App.get_time_by_key(msg.key); @@ -840,7 +862,8 @@ bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) { return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state_); } APIConnection::MessageInfo APIConnection::try_send_datetime_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *datetime = static_cast(entity); DateTimeStateResponse msg; // Stack allocated! msg.missing_state = !datetime->has_state(); @@ -849,13 +872,15 @@ APIConnection::MessageInfo APIConnection::try_send_datetime_state_(EntityBase *e msg.epoch_seconds = state.timestamp; } msg.key = datetime->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) { - this->schedule_message_(datetime, &APIConnection::try_send_datetime_info_); + this->schedule_message_(datetime, &APIConnection::try_send_datetime_info_, + ListEntitiesDateTimeResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_datetime_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *datetime = static_cast(entity); ListEntitiesDateTimeResponse msg; // Stack allocated! msg.unique_id = get_default_unique_id("datetime", datetime); @@ -863,7 +888,7 @@ APIConnection::MessageInfo APIConnection::try_send_datetime_info_(EntityBase *en // Fill common entity fields fill_entity_info_base_(datetime, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key); @@ -878,22 +903,24 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { #ifdef USE_TEXT bool APIConnection::send_text_state(text::Text *text, std::string state) { - return this->schedule_message_(text, - [state = std::move(state)](EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *t = static_cast(entity); - TextStateResponse msg; // Stack allocated! - msg.state = state; - msg.missing_state = !t->has_state(); - msg.key = t->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_( + text, + [state = std::move(state)](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *t = static_cast(entity); + TextStateResponse msg; // Stack allocated! + msg.state = state; + msg.missing_state = !t->has_state(); + msg.key = t->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_text_info(text::Text *text) { - this->schedule_message_(text, &APIConnection::try_send_text_info_); + this->schedule_message_(text, &APIConnection::try_send_text_info_, ListEntitiesTextResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_text_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *text = static_cast(entity); ListEntitiesTextResponse msg; // Stack allocated! msg.mode = static_cast(text->traits.get_mode()); @@ -905,7 +932,7 @@ APIConnection::MessageInfo APIConnection::try_send_text_info_(EntityBase *entity // Fill common entity fields fill_entity_info_base_(text, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::text_command(const TextCommandRequest &msg) { text::Text *text = App.get_text_by_key(msg.key); @@ -920,22 +947,24 @@ void APIConnection::text_command(const TextCommandRequest &msg) { #ifdef USE_SELECT bool APIConnection::send_select_state(select::Select *select, std::string state) { - return this->schedule_message_(select, - [state = std::move(state)](EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *s = static_cast(entity); - SelectStateResponse msg; // Stack allocated! - msg.state = state; - msg.missing_state = !s->has_state(); - msg.key = s->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_( + select, + [state = std::move(state)](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *s = static_cast(entity); + SelectStateResponse msg; // Stack allocated! + msg.state = state; + msg.missing_state = !s->has_state(); + msg.key = s->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_select_info(select::Select *select) { - this->schedule_message_(select, &APIConnection::try_send_select_info_); + this->schedule_message_(select, &APIConnection::try_send_select_info_, ListEntitiesSelectResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_select_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *select = static_cast(entity); ListEntitiesSelectResponse msg; // Stack allocated! for (const auto &option : select->traits.get_options()) @@ -945,7 +974,7 @@ APIConnection::MessageInfo APIConnection::try_send_select_info_(EntityBase *enti // Fill common entity fields fill_entity_info_base_(select, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::select_command(const SelectCommandRequest &msg) { select::Select *select = App.get_select_by_key(msg.key); @@ -960,10 +989,11 @@ void APIConnection::select_command(const SelectCommandRequest &msg) { #ifdef USE_BUTTON void esphome::api::APIConnection::send_button_info(button::Button *button) { - this->schedule_message_(button, &APIConnection::try_send_button_info_); + this->schedule_message_(button, &APIConnection::try_send_button_info_, ListEntitiesButtonResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_button_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *button = static_cast(entity); ListEntitiesButtonResponse msg; // Stack allocated! msg.device_class = button->get_device_class(); @@ -972,7 +1002,7 @@ APIConnection::MessageInfo APIConnection::try_send_button_info_(EntityBase *enti // Fill common entity fields fill_entity_info_base_(button, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) { button::Button *button = App.get_button_by_key(msg.key); @@ -985,21 +1015,22 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg #ifdef USE_LOCK bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) { - return this->schedule_message_( - a_lock, - [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *l = static_cast(entity); - LockStateResponse msg; // Stack allocated! - msg.state = static_cast(state); - msg.key = l->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + return this->schedule_message_(a_lock, + [state](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *l = static_cast(entity); + LockStateResponse msg; // Stack allocated! + msg.state = static_cast(state); + msg.key = l->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_lock_info(lock::Lock *a_lock) { - this->schedule_message_(a_lock, &APIConnection::try_send_lock_info_); + this->schedule_message_(a_lock, &APIConnection::try_send_lock_info_, ListEntitiesLockResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_lock_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *a_lock = static_cast(entity); ListEntitiesLockResponse msg; // Stack allocated! msg.assumed_state = a_lock->traits.get_assumed_state(); @@ -1010,7 +1041,7 @@ APIConnection::MessageInfo APIConnection::try_send_lock_info_(EntityBase *entity // Fill common entity fields fill_entity_info_base_(a_lock, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::lock_command(const LockCommandRequest &msg) { lock::Lock *a_lock = App.get_lock_by_key(msg.key); @@ -1036,19 +1067,21 @@ bool APIConnection::send_valve_state(valve::Valve *valve) { return this->schedule_message_(valve, &APIConnection::try_send_valve_state_); } APIConnection::MessageInfo APIConnection::try_send_valve_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *valve = static_cast(entity); ValveStateResponse msg; // Stack allocated! msg.position = valve->position; msg.current_operation = static_cast(valve->current_operation); msg.key = valve->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_valve_info(valve::Valve *valve) { - this->schedule_message_(valve, &APIConnection::try_send_valve_info_); + this->schedule_message_(valve, &APIConnection::try_send_valve_info_, ListEntitiesValveResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_valve_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *valve = static_cast(entity); ListEntitiesValveResponse msg; // Stack allocated! auto traits = valve->get_traits(); @@ -1061,7 +1094,7 @@ APIConnection::MessageInfo APIConnection::try_send_valve_info_(EntityBase *entit // Fill common entity fields fill_entity_info_base_(valve, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::valve_command(const ValveCommandRequest &msg) { valve::Valve *valve = App.get_valve_by_key(msg.key); @@ -1082,7 +1115,8 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state_); } APIConnection::MessageInfo APIConnection::try_send_media_player_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *media_player = static_cast(entity); MediaPlayerStateResponse msg; // Stack allocated! media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING @@ -1092,13 +1126,15 @@ APIConnection::MessageInfo APIConnection::try_send_media_player_state_(EntityBas msg.volume = media_player->volume; msg.muted = media_player->is_muted(); msg.key = media_player->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) { - this->schedule_message_(media_player, &APIConnection::try_send_media_player_info_); + this->schedule_message_(media_player, &APIConnection::try_send_media_player_info_, + ListEntitiesMediaPlayerResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_media_player_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *media_player = static_cast(entity); ListEntitiesMediaPlayerResponse msg; // Stack allocated! auto traits = media_player->get_traits(); @@ -1117,7 +1153,7 @@ APIConnection::MessageInfo APIConnection::try_send_media_player_info_(EntityBase // Fill common entity fields fill_entity_info_base_(media_player, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key); @@ -1152,10 +1188,11 @@ 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->schedule_message_(camera, &APIConnection::try_send_camera_info_); + this->schedule_message_(camera, &APIConnection::try_send_camera_info_, ListEntitiesCameraResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_camera_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *camera = static_cast(entity); ListEntitiesCameraResponse msg; // Stack allocated! msg.unique_id = get_default_unique_id("camera", camera); @@ -1163,7 +1200,7 @@ APIConnection::MessageInfo APIConnection::try_send_camera_info_(EntityBase *enti // Fill common entity fields fill_entity_info_base_(camera, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::camera_image(const CameraImageRequest &msg) { if (esp32_camera::global_esp32_camera == nullptr) @@ -1361,10 +1398,11 @@ APIConnection::MessageInfo APIConnection::try_send_alarm_control_panel_state_(En AlarmControlPanelStateResponse msg; // Stack allocated! msg.state = static_cast(a_alarm_control_panel->get_state()); msg.key = a_alarm_control_panel->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } 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_); + this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info_, + ListEntitiesAlarmControlPanelResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_alarm_control_panel_info_(EntityBase *entity, BufferAllocator allocator, @@ -1379,7 +1417,7 @@ APIConnection::MessageInfo APIConnection::try_send_alarm_control_panel_info_(Ent // Fill common entity fields fill_entity_info_base_(a_alarm_control_panel, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } 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); @@ -1417,21 +1455,23 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe #ifdef USE_EVENT void APIConnection::send_event(event::Event *event, std::string event_type) { - this->schedule_message_(event, - [event_type = std::move(event_type)](EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) -> APIConnection::MessageInfo { - auto *e = static_cast(entity); - EventResponse msg; // Stack allocated! - msg.event_type = event_type; - msg.key = e->get_object_id_hash(); - return APIConnection::encode_message_to_buffer(msg, allocator, remaining_size); - }); + this->schedule_message_( + event, + [event_type = std::move(event_type)](EntityBase *entity, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) -> APIConnection::MessageInfo { + auto *e = static_cast(entity); + EventResponse msg; // Stack allocated! + msg.event_type = event_type; + msg.key = e->get_object_id_hash(); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); + }); } void APIConnection::send_event_info(event::Event *event) { - this->schedule_message_(event, &APIConnection::try_send_event_info_); + this->schedule_message_(event, &APIConnection::try_send_event_info_, ListEntitiesEventResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_event_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *event = static_cast(entity); ListEntitiesEventResponse msg; // Stack allocated! msg.device_class = event->get_device_class(); @@ -1442,7 +1482,7 @@ APIConnection::MessageInfo APIConnection::try_send_event_info_(EntityBase *entit // Fill common entity fields fill_entity_info_base_(event, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } #endif @@ -1451,7 +1491,8 @@ bool APIConnection::send_update_state(update::UpdateEntity *update) { return this->schedule_message_(update, &APIConnection::try_send_update_state_); } APIConnection::MessageInfo APIConnection::try_send_update_state_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *update = static_cast(entity); UpdateStateResponse msg; // Stack allocated! msg.missing_state = !update->has_state(); @@ -1468,13 +1509,14 @@ APIConnection::MessageInfo APIConnection::try_send_update_state_(EntityBase *ent msg.release_url = update->update_info.release_url; } msg.key = update->get_object_id_hash(); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::send_update_info(update::UpdateEntity *update) { - this->schedule_message_(update, &APIConnection::try_send_update_info_); + this->schedule_message_(update, &APIConnection::try_send_update_info_, ListEntitiesUpdateResponse::message_type); } APIConnection::MessageInfo APIConnection::try_send_update_info_(EntityBase *entity, BufferAllocator allocator, - uint32_t remaining_size) { + uint32_t remaining_size, + OverheadCalculator overhead_calc) { auto *update = static_cast(entity); ListEntitiesUpdateResponse msg; // Stack allocated! msg.device_class = update->get_device_class(); @@ -1483,7 +1525,7 @@ APIConnection::MessageInfo APIConnection::try_send_update_info_(EntityBase *enti // Fill common entity fields fill_entity_info_base_(update, msg); - return encode_message_to_buffer(msg, allocator, remaining_size); + return encode_message_to_buffer(msg, allocator, remaining_size, overhead_calc); } void APIConnection::update_command(const UpdateCommandRequest &msg) { update::UpdateEntity *update = App.get_update_by_key(msg.key); @@ -1733,6 +1775,8 @@ ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint32_t size) { } void APIConnection::process_batch_() { + using EncodedMessage = APIConnection::EncodedMessage; + if (this->deferred_batch_.empty()) { this->deferred_batch_.batch_scheduled = false; return; @@ -1753,14 +1797,18 @@ void APIConnection::process_batch_() { // Use bound method for single message allocation BufferAllocator allocator = std::bind(&APIConnection::allocate_single_message_buffer, this, std::placeholders::_1); - // Let the creator calculate size and encode if it fits - MessageInfo info = item.creator(item.entity, allocator, std::numeric_limits::max()); + // Bind the overhead calculator + OverheadCalculator overhead_calc = std::bind(&APIFrameHelper::calculate_packet_overhead, this->helper_.get(), + std::placeholders::_1, std::placeholders::_2); - if (info.encoded && this->send_buffer(ProtoWriteBuffer{&this->proto_write_buffer_}, info.type)) { + // Let the creator calculate size and encode if it fits + EncodedMessage msg = item.creator(item.entity, allocator, std::numeric_limits::max(), overhead_calc); + + if (msg && this->send_buffer(ProtoWriteBuffer{&this->proto_write_buffer_}, item.message_type)) { this->deferred_batch_.clear(); - } else if (!info.encoded) { + } else if (!msg) { // Message too large - ESP_LOGW(TAG, "Message too large to send: type=%u size=%u", info.type, info.size); + ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type); this->deferred_batch_.clear(); } return; @@ -1782,40 +1830,40 @@ void APIConnection::process_batch_() { // Use bound method for batch allocation BufferAllocator allocator = std::bind(&APIConnection::allocate_batch_message_buffer, this, std::placeholders::_1); + // Bind the overhead calculator + OverheadCalculator overhead_calc = std::bind(&APIFrameHelper::calculate_packet_overhead, this->helper_.get(), + std::placeholders::_1, std::placeholders::_2); + // Process items and encode directly to buffer for (size_t i = 0; i < this->deferred_batch_.items.size(); i++) { const auto &item = this->deferred_batch_.items[i]; - // Update current offset for this message - if (i > 0) { - // Account for header/footer padding in remaining size - uint32_t padding = this->helper_->frame_header_padding() + this->helper_->frame_footer_size(); - if (padding >= remaining_size) { - break; // No more space - } - remaining_size -= padding; - current_offset = this->proto_write_buffer_.size() - this->helper_->frame_header_padding(); + // Calculate where this message will start in the buffer + // The allocator will add footer (for previous message) + header padding before the message + if (i == 0) { + // First message starts after initial header padding + current_offset = this->helper_->frame_header_padding(); + } else { + // Subsequent messages start after previous content + footer + header padding + current_offset = + this->proto_write_buffer_.size() + this->helper_->frame_footer_size() + this->helper_->frame_header_padding(); } - // Try to encode message with allocator - MessageInfo info = item.creator(item.entity, allocator, remaining_size); + // Try to encode message with allocator and overhead calculator + // The creator will use the overhead calculator to determine if the message fits + // Pass the raw remaining size - the creator will calculate the overhead + EncodedMessage msg = item.creator(item.entity, allocator, remaining_size, overhead_calc); - if (!info.encoded) { - // Message won't fit (allocator was not called), stop processing + if (!msg) { + // Message won't fit, stop processing break; } // Message was encoded successfully - packet_info.push_back(std::make_tuple(info.type, current_offset, info.size)); + packet_info.push_back(std::make_tuple(item.message_type, current_offset, msg.payload_size)); - // Calculate overhead and update remaining size - uint16_t overhead = this->helper_->calculate_packet_overhead(info.type, info.size); - uint32_t total_packet_size = info.size + overhead; - if (total_packet_size >= remaining_size) { - items_processed = i + 1; - break; // Buffer full - } - remaining_size -= total_packet_size; + // Update remaining size based on what was actually used + remaining_size -= msg.total_size; items_processed++; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 06098c52c0..2ed8f86719 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -327,16 +327,21 @@ class APIConnection : public APIServerConnection { ProtoWriteBuffer allocate_single_message_buffer(uint32_t size); ProtoWriteBuffer allocate_batch_message_buffer(uint32_t size); - // Info about an encoded message - struct MessageInfo { - uint16_t type; - uint16_t size; - bool encoded; - }; - // Function to allocate buffer space for a message using BufferAllocator = std::function; + // Function to calculate packet overhead for a given message type and size + using OverheadCalculator = std::function; + + // Result of message encoding + struct EncodedMessage { + uint16_t payload_size; // Size of the message payload only + uint16_t total_size; // Total size including overhead + + // Returns true if message was encoded (both sizes > 0) + operator bool() const { return total_size > 0; } + }; + protected: // Helper function to fill common entity fields template static void fill_entity_info_base_(esphome::EntityBase *entity, ResponseT &response) { @@ -355,115 +360,154 @@ class APIConnection : public APIServerConnection { // Helper function to encode a message to buffer template - static MessageInfo encode_message_to_buffer(MessageT &msg, BufferAllocator allocator, uint32_t remaining_size) { + static EncodedMessage encode_message_to_buffer(MessageT &msg, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc) { // Calculate size uint32_t size = 0; msg.calculate_size(size); + // Calculate overhead for this message + uint16_t overhead = overhead_calc(MessageT::message_type, static_cast(size)); + uint32_t total_size = size + overhead; + // Check if it fits - if (size > remaining_size) { - return {MessageT::message_type, static_cast(size), false}; + if (total_size > remaining_size) { + return {0, 0}; // Doesn't fit } - // Allocate exact buffer space needed + // Allocate exact buffer space needed (just the payload, not the overhead) ProtoWriteBuffer buffer = allocator(size); // Encode directly into buffer msg.encode(buffer); - return {MessageT::message_type, static_cast(size), true}; + return {static_cast(size), static_cast(total_size)}; } #ifdef USE_BINARY_SENSOR - static MessageInfo try_send_binary_sensor_state_(EntityBase *binary_sensor, BufferAllocator allocator, - uint32_t remaining_size); - static MessageInfo try_send_binary_sensor_info_(EntityBase *binary_sensor, BufferAllocator allocator, - uint32_t remaining_size); + static EncodedMessage try_send_binary_sensor_state_(EntityBase *binary_sensor, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); + static EncodedMessage try_send_binary_sensor_info_(EntityBase *binary_sensor, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); #endif #ifdef USE_COVER - static MessageInfo try_send_cover_state_(EntityBase *cover, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_cover_info_(EntityBase *cover, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_cover_state_(EntityBase *cover, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_cover_info_(EntityBase *cover, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_FAN - static MessageInfo try_send_fan_state_(EntityBase *fan, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_fan_info_(EntityBase *fan, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_fan_state_(EntityBase *fan, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_fan_info_(EntityBase *fan, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_LIGHT - static MessageInfo try_send_light_state_(EntityBase *light, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_light_info_(EntityBase *light, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_light_state_(EntityBase *light, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_light_info_(EntityBase *light, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_SENSOR - static MessageInfo try_send_sensor_state_(EntityBase *sensor, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_sensor_info_(EntityBase *sensor, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_sensor_state_(EntityBase *sensor, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_sensor_info_(EntityBase *sensor, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_SWITCH - static MessageInfo try_send_switch_state_(EntityBase *a_switch, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_switch_info_(EntityBase *a_switch, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_switch_state_(EntityBase *a_switch, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_switch_info_(EntityBase *a_switch, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_TEXT_SENSOR - static MessageInfo try_send_text_sensor_state_(EntityBase *text_sensor, BufferAllocator allocator, - uint32_t remaining_size); - static MessageInfo try_send_text_sensor_info_(EntityBase *text_sensor, BufferAllocator allocator, - uint32_t remaining_size); + static EncodedMessage try_send_text_sensor_state_(EntityBase *text_sensor, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); + static EncodedMessage try_send_text_sensor_info_(EntityBase *text_sensor, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); #endif #ifdef USE_CLIMATE - static MessageInfo try_send_climate_state_(EntityBase *climate, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_climate_info_(EntityBase *climate, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_climate_state_(EntityBase *climate, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_climate_info_(EntityBase *climate, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_NUMBER - static MessageInfo try_send_number_state_(EntityBase *number, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_number_info_(EntityBase *number, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_number_state_(EntityBase *number, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_number_info_(EntityBase *number, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_DATETIME_DATE - static MessageInfo try_send_date_state_(EntityBase *date, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_date_info_(EntityBase *date, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_date_state_(EntityBase *date, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_date_info_(EntityBase *date, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_DATETIME_TIME - static MessageInfo try_send_time_state_(EntityBase *time, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_time_info_(EntityBase *time, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_time_state_(EntityBase *time, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_time_info_(EntityBase *time, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_DATETIME_DATETIME - static MessageInfo try_send_datetime_state_(EntityBase *datetime, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_datetime_info_(EntityBase *datetime, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_datetime_state_(EntityBase *datetime, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); + static EncodedMessage try_send_datetime_info_(EntityBase *datetime, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); #endif #ifdef USE_TEXT - static MessageInfo try_send_text_state_(EntityBase *text, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_text_info_(EntityBase *text, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_text_state_(EntityBase *text, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_text_info_(EntityBase *text, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_SELECT - static MessageInfo try_send_select_state_(EntityBase *select, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_select_info_(EntityBase *select, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_select_state_(EntityBase *select, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_select_info_(EntityBase *select, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_BUTTON - static MessageInfo try_send_button_info_(EntityBase *button, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_button_info_(EntityBase *button, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_LOCK - static MessageInfo try_send_lock_state_(EntityBase *a_lock, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_lock_info_(EntityBase *a_lock, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_lock_state_(EntityBase *a_lock, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_lock_info_(EntityBase *a_lock, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_VALVE - static MessageInfo try_send_valve_state_(EntityBase *valve, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_valve_info_(EntityBase *valve, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_valve_state_(EntityBase *valve, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_valve_info_(EntityBase *valve, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_MEDIA_PLAYER - static MessageInfo try_send_media_player_state_(EntityBase *media_player, BufferAllocator allocator, - uint32_t remaining_size); - static MessageInfo try_send_media_player_info_(EntityBase *media_player, BufferAllocator allocator, - uint32_t remaining_size); + static EncodedMessage try_send_media_player_state_(EntityBase *media_player, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); + static EncodedMessage try_send_media_player_info_(EntityBase *media_player, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); #endif #ifdef USE_ALARM_CONTROL_PANEL - static MessageInfo try_send_alarm_control_panel_state_(EntityBase *a_alarm_control_panel, BufferAllocator allocator, - uint32_t remaining_size); - static MessageInfo try_send_alarm_control_panel_info_(EntityBase *a_alarm_control_panel, BufferAllocator allocator, - uint32_t remaining_size); + static EncodedMessage try_send_alarm_control_panel_state_(EntityBase *a_alarm_control_panel, + BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_alarm_control_panel_info_(EntityBase *a_alarm_control_panel, BufferAllocator allocator, + uint32_t remaining_size, OverheadCalculator overhead_calc); #endif #ifdef USE_EVENT - static MessageInfo try_send_event_info_(EntityBase *event, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_event_info_(EntityBase *event, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_UPDATE - static MessageInfo try_send_update_state_(EntityBase *update, BufferAllocator allocator, uint32_t remaining_size); - static MessageInfo try_send_update_info_(EntityBase *update, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_update_state_(EntityBase *update, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); + static EncodedMessage try_send_update_info_(EntityBase *update, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif #ifdef USE_ESP32_CAMERA - static MessageInfo try_send_camera_info_(EntityBase *camera, BufferAllocator allocator, uint32_t remaining_size); + static EncodedMessage try_send_camera_info_(EntityBase *camera, BufferAllocator allocator, uint32_t remaining_size, + OverheadCalculator overhead_calc); #endif enum class ConnectionState { @@ -502,7 +546,9 @@ class APIConnection : public APIServerConnection { int state_subs_at_ = -1; // Function type that encodes a message directly to buffer - using MessageCreator = std::function; + // Returns EncodedMessage with sizes if successful, {0, 0} if it doesn't fit + using MessageCreator = + std::function; // Generic batching mechanism for both state updates and entity info struct DeferredBatch {