mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	[api] Implement zero-copy string optimization for outgoing protobuf messages (#9790)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -248,8 +248,10 @@ void APIConnection::loop() { | ||||
|     if (state_subs_at_ < static_cast<int>(subs.size())) { | ||||
|       auto &it = subs[state_subs_at_]; | ||||
|       SubscribeHomeAssistantStateResponse resp; | ||||
|       resp.entity_id = it.entity_id; | ||||
|       resp.attribute = it.attribute.value(); | ||||
|       resp.set_entity_id(StringRef(it.entity_id)); | ||||
|       // attribute.value() returns temporary - must store it | ||||
|       std::string attribute_value = it.attribute.value(); | ||||
|       resp.set_attribute(StringRef(attribute_value)); | ||||
|       resp.once = it.once; | ||||
|       if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) { | ||||
|         state_subs_at_++; | ||||
| @@ -260,14 +262,14 @@ void APIConnection::loop() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) { | ||||
| bool APIConnection::send_disconnect_response(const DisconnectRequest &msg) { | ||||
|   // remote initiated disconnect_client | ||||
|   // don't close yet, we still need to send the disconnect response | ||||
|   // close will happen on next loop | ||||
|   ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str()); | ||||
|   this->flags_.next_close = true; | ||||
|   DisconnectResponse resp; | ||||
|   return resp; | ||||
|   return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE); | ||||
| } | ||||
| void APIConnection::on_disconnect_response(const DisconnectResponse &value) { | ||||
|   this->helper_->close(); | ||||
| @@ -344,7 +346,7 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne | ||||
|                                                     bool is_single) { | ||||
|   auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity); | ||||
|   ListEntitiesBinarySensorResponse msg; | ||||
|   msg.device_class = binary_sensor->get_device_class(); | ||||
|   msg.set_device_class(binary_sensor->get_device_class_ref()); | ||||
|   msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); | ||||
|   return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, | ||||
|                                      remaining_size, is_single); | ||||
| @@ -376,7 +378,7 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c | ||||
|   msg.supports_position = traits.get_supports_position(); | ||||
|   msg.supports_tilt = traits.get_supports_tilt(); | ||||
|   msg.supports_stop = traits.get_supports_stop(); | ||||
|   msg.device_class = cover->get_device_class(); | ||||
|   msg.set_device_class(cover->get_device_class_ref()); | ||||
|   return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                      is_single); | ||||
| } | ||||
| @@ -411,7 +413,7 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co | ||||
|   if (traits.supports_direction()) | ||||
|     msg.direction = static_cast<enums::FanDirection>(fan->direction); | ||||
|   if (traits.supports_preset_modes()) | ||||
|     msg.preset_mode = fan->preset_mode; | ||||
|     msg.set_preset_mode(StringRef(fan->preset_mode)); | ||||
|   return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
| uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, | ||||
| @@ -468,8 +470,11 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection * | ||||
|   resp.color_temperature = values.get_color_temperature(); | ||||
|   resp.cold_white = values.get_cold_white(); | ||||
|   resp.warm_white = values.get_warm_white(); | ||||
|   if (light->supports_effects()) | ||||
|     resp.effect = light->get_effect_name(); | ||||
|   if (light->supports_effects()) { | ||||
|     // get_effect_name() returns temporary std::string - must store it | ||||
|     std::string effect_name = light->get_effect_name(); | ||||
|     resp.set_effect(StringRef(effect_name)); | ||||
|   } | ||||
|   return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
| uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, | ||||
| @@ -545,10 +550,10 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection * | ||||
|                                              bool is_single) { | ||||
|   auto *sensor = static_cast<sensor::Sensor *>(entity); | ||||
|   ListEntitiesSensorResponse msg; | ||||
|   msg.unit_of_measurement = sensor->get_unit_of_measurement(); | ||||
|   msg.set_unit_of_measurement(sensor->get_unit_of_measurement_ref()); | ||||
|   msg.accuracy_decimals = sensor->get_accuracy_decimals(); | ||||
|   msg.force_update = sensor->get_force_update(); | ||||
|   msg.device_class = sensor->get_device_class(); | ||||
|   msg.set_device_class(sensor->get_device_class_ref()); | ||||
|   msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class()); | ||||
|   return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                      is_single); | ||||
| @@ -575,7 +580,7 @@ uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection * | ||||
|   auto *a_switch = static_cast<switch_::Switch *>(entity); | ||||
|   ListEntitiesSwitchResponse msg; | ||||
|   msg.assumed_state = a_switch->assumed_state(); | ||||
|   msg.device_class = a_switch->get_device_class(); | ||||
|   msg.set_device_class(a_switch->get_device_class_ref()); | ||||
|   return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                      is_single); | ||||
| } | ||||
| @@ -600,7 +605,7 @@ uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnec | ||||
|                                                    bool is_single) { | ||||
|   auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity); | ||||
|   TextSensorStateResponse resp; | ||||
|   resp.state = text_sensor->state; | ||||
|   resp.set_state(StringRef(text_sensor->state)); | ||||
|   resp.missing_state = !text_sensor->has_state(); | ||||
|   return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                       is_single); | ||||
| @@ -609,7 +614,7 @@ uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnect | ||||
|                                                   bool is_single) { | ||||
|   auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity); | ||||
|   ListEntitiesTextSensorResponse msg; | ||||
|   msg.device_class = text_sensor->get_device_class(); | ||||
|   msg.set_device_class(text_sensor->get_device_class_ref()); | ||||
|   return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, | ||||
|                                      remaining_size, is_single); | ||||
| } | ||||
| @@ -637,13 +642,19 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection | ||||
|   } | ||||
|   if (traits.get_supports_fan_modes() && climate->fan_mode.has_value()) | ||||
|     resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value()); | ||||
|   if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) | ||||
|     resp.custom_fan_mode = climate->custom_fan_mode.value(); | ||||
|   if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) { | ||||
|     // custom_fan_mode.value() returns temporary - must store it | ||||
|     std::string custom_fan_mode = climate->custom_fan_mode.value(); | ||||
|     resp.set_custom_fan_mode(StringRef(custom_fan_mode)); | ||||
|   } | ||||
|   if (traits.get_supports_presets() && climate->preset.has_value()) { | ||||
|     resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value()); | ||||
|   } | ||||
|   if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) | ||||
|     resp.custom_preset = climate->custom_preset.value(); | ||||
|   if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) { | ||||
|     // custom_preset.value() returns temporary - must store it | ||||
|     std::string custom_preset = climate->custom_preset.value(); | ||||
|     resp.set_custom_preset(StringRef(custom_preset)); | ||||
|   } | ||||
|   if (traits.get_supports_swing_modes()) | ||||
|     resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode); | ||||
|   if (traits.get_supports_current_humidity()) | ||||
| @@ -729,9 +740,9 @@ uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection * | ||||
|                                              bool is_single) { | ||||
|   auto *number = static_cast<number::Number *>(entity); | ||||
|   ListEntitiesNumberResponse msg; | ||||
|   msg.unit_of_measurement = number->traits.get_unit_of_measurement(); | ||||
|   msg.set_unit_of_measurement(number->traits.get_unit_of_measurement_ref()); | ||||
|   msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode()); | ||||
|   msg.device_class = number->traits.get_device_class(); | ||||
|   msg.set_device_class(number->traits.get_device_class_ref()); | ||||
|   msg.min_value = number->traits.get_min_value(); | ||||
|   msg.max_value = number->traits.get_max_value(); | ||||
|   msg.step = number->traits.get_step(); | ||||
| @@ -844,7 +855,7 @@ uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *c | ||||
|                                             bool is_single) { | ||||
|   auto *text = static_cast<text::Text *>(entity); | ||||
|   TextStateResponse resp; | ||||
|   resp.state = text->state; | ||||
|   resp.set_state(StringRef(text->state)); | ||||
|   resp.missing_state = !text->has_state(); | ||||
|   return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
| @@ -856,7 +867,7 @@ uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *co | ||||
|   msg.mode = static_cast<enums::TextMode>(text->traits.get_mode()); | ||||
|   msg.min_length = text->traits.get_min_length(); | ||||
|   msg.max_length = text->traits.get_max_length(); | ||||
|   msg.pattern = text->traits.get_pattern(); | ||||
|   msg.set_pattern(text->traits.get_pattern_ref()); | ||||
|   return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                      is_single); | ||||
| } | ||||
| @@ -877,7 +888,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection | ||||
|                                               bool is_single) { | ||||
|   auto *select = static_cast<select::Select *>(entity); | ||||
|   SelectStateResponse resp; | ||||
|   resp.state = select->state; | ||||
|   resp.set_state(StringRef(select->state)); | ||||
|   resp.missing_state = !select->has_state(); | ||||
|   return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
| @@ -903,7 +914,7 @@ uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection * | ||||
|                                              bool is_single) { | ||||
|   auto *button = static_cast<button::Button *>(entity); | ||||
|   ListEntitiesButtonResponse msg; | ||||
|   msg.device_class = button->get_device_class(); | ||||
|   msg.set_device_class(button->get_device_class_ref()); | ||||
|   return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                      is_single); | ||||
| } | ||||
| @@ -972,7 +983,7 @@ uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *c | ||||
|   auto *valve = static_cast<valve::Valve *>(entity); | ||||
|   ListEntitiesValveResponse msg; | ||||
|   auto traits = valve->get_traits(); | ||||
|   msg.device_class = valve->get_device_class(); | ||||
|   msg.set_device_class(valve->get_device_class_ref()); | ||||
|   msg.assumed_state = traits.get_is_assumed_state(); | ||||
|   msg.supports_position = traits.get_supports_position(); | ||||
|   msg.supports_stop = traits.get_supports_stop(); | ||||
| @@ -1014,13 +1025,13 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec | ||||
|   auto traits = media_player->get_traits(); | ||||
|   msg.supports_pause = traits.get_supports_pause(); | ||||
|   for (auto &supported_format : traits.get_supported_formats()) { | ||||
|     MediaPlayerSupportedFormat media_format; | ||||
|     media_format.format = supported_format.format; | ||||
|     msg.supported_formats.emplace_back(); | ||||
|     auto &media_format = msg.supported_formats.back(); | ||||
|     media_format.set_format(StringRef(supported_format.format)); | ||||
|     media_format.sample_rate = supported_format.sample_rate; | ||||
|     media_format.num_channels = supported_format.num_channels; | ||||
|     media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose); | ||||
|     media_format.sample_bytes = supported_format.sample_bytes; | ||||
|     msg.supported_formats.push_back(media_format); | ||||
|   } | ||||
|   return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, | ||||
|                                      remaining_size, is_single); | ||||
| @@ -1083,6 +1094,12 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool APIConnection::send_get_time_response(const GetTimeRequest &msg) { | ||||
|   GetTimeResponse resp; | ||||
|   resp.epoch_seconds = ::time(nullptr); | ||||
|   return this->send_message(resp, GetTimeResponse::MESSAGE_TYPE); | ||||
| } | ||||
|  | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
| void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) { | ||||
|   bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags); | ||||
| @@ -1113,12 +1130,12 @@ void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) | ||||
|   bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg); | ||||
| } | ||||
|  | ||||
| BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_free( | ||||
| bool APIConnection::send_subscribe_bluetooth_connections_free_response( | ||||
|     const SubscribeBluetoothConnectionsFreeRequest &msg) { | ||||
|   BluetoothConnectionsFreeResponse resp; | ||||
|   resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free(); | ||||
|   resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit(); | ||||
|   return resp; | ||||
|   return this->send_message(resp, BluetoothConnectionsFreeResponse::MESSAGE_TYPE); | ||||
| } | ||||
|  | ||||
| void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) { | ||||
| @@ -1179,28 +1196,27 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno | ||||
|   } | ||||
| } | ||||
|  | ||||
| VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration( | ||||
|     const VoiceAssistantConfigurationRequest &msg) { | ||||
| bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) { | ||||
|   VoiceAssistantConfigurationResponse resp; | ||||
|   if (!this->check_voice_assistant_api_connection_()) { | ||||
|     return resp; | ||||
|     return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE); | ||||
|   } | ||||
|  | ||||
|   auto &config = voice_assistant::global_voice_assistant->get_configuration(); | ||||
|   for (auto &wake_word : config.available_wake_words) { | ||||
|     VoiceAssistantWakeWord resp_wake_word; | ||||
|     resp_wake_word.id = wake_word.id; | ||||
|     resp_wake_word.wake_word = wake_word.wake_word; | ||||
|     resp.available_wake_words.emplace_back(); | ||||
|     auto &resp_wake_word = resp.available_wake_words.back(); | ||||
|     resp_wake_word.set_id(StringRef(wake_word.id)); | ||||
|     resp_wake_word.set_wake_word(StringRef(wake_word.wake_word)); | ||||
|     for (const auto &lang : wake_word.trained_languages) { | ||||
|       resp_wake_word.trained_languages.push_back(lang); | ||||
|     } | ||||
|     resp.available_wake_words.push_back(std::move(resp_wake_word)); | ||||
|   } | ||||
|   for (auto &wake_word_id : config.active_wake_words) { | ||||
|     resp.active_wake_words.push_back(wake_word_id); | ||||
|   } | ||||
|   resp.max_active_wake_words = config.max_active_wake_words; | ||||
|   return resp; | ||||
|   return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE); | ||||
| } | ||||
|  | ||||
| void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) { | ||||
| @@ -1273,7 +1289,7 @@ void APIConnection::send_event(event::Event *event, const std::string &event_typ | ||||
| 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 = event_type; | ||||
|   resp.set_event_type(StringRef(event_type)); | ||||
|   return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
|  | ||||
| @@ -1281,7 +1297,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c | ||||
|                                             bool is_single) { | ||||
|   auto *event = static_cast<event::Event *>(entity); | ||||
|   ListEntitiesEventResponse msg; | ||||
|   msg.device_class = event->get_device_class(); | ||||
|   msg.set_device_class(event->get_device_class_ref()); | ||||
|   for (const auto &event_type : event->get_event_types()) | ||||
|     msg.event_types.push_back(event_type); | ||||
|   return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
| @@ -1305,11 +1321,11 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection | ||||
|       resp.has_progress = true; | ||||
|       resp.progress = update->update_info.progress; | ||||
|     } | ||||
|     resp.current_version = update->update_info.current_version; | ||||
|     resp.latest_version = update->update_info.latest_version; | ||||
|     resp.title = update->update_info.title; | ||||
|     resp.release_summary = update->update_info.summary; | ||||
|     resp.release_url = update->update_info.release_url; | ||||
|     resp.set_current_version(StringRef(update->update_info.current_version)); | ||||
|     resp.set_latest_version(StringRef(update->update_info.latest_version)); | ||||
|     resp.set_title(StringRef(update->update_info.title)); | ||||
|     resp.set_release_summary(StringRef(update->update_info.summary)); | ||||
|     resp.set_release_url(StringRef(update->update_info.release_url)); | ||||
|   } | ||||
|   return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
| @@ -1317,7 +1333,7 @@ uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection * | ||||
|                                              bool is_single) { | ||||
|   auto *update = static_cast<update::UpdateEntity *>(entity); | ||||
|   ListEntitiesUpdateResponse msg; | ||||
|   msg.device_class = update->get_device_class(); | ||||
|   msg.set_device_class(update->get_device_class_ref()); | ||||
|   return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, | ||||
|                                      is_single); | ||||
| } | ||||
| @@ -1366,7 +1382,7 @@ void APIConnection::complete_authentication_() { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| HelloResponse APIConnection::hello(const HelloRequest &msg) { | ||||
| bool APIConnection::send_hello_response(const HelloRequest &msg) { | ||||
|   this->client_info_.name = msg.client_info; | ||||
|   this->client_info_.peername = this->helper_->getpeername(); | ||||
|   this->client_api_version_major_ = msg.api_version_major; | ||||
| @@ -1377,8 +1393,10 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) { | ||||
|   HelloResponse resp; | ||||
|   resp.api_version_major = 1; | ||||
|   resp.api_version_minor = 10; | ||||
|   resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; | ||||
|   resp.name = App.get_name(); | ||||
|   // Temporary string for concatenation - will be valid during send_message call | ||||
|   std::string server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; | ||||
|   resp.set_server_info(StringRef(server_info)); | ||||
|   resp.set_name(StringRef(App.get_name())); | ||||
|  | ||||
| #ifdef USE_API_PASSWORD | ||||
|   // Password required - wait for authentication | ||||
| @@ -1388,9 +1406,9 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) { | ||||
|   this->complete_authentication_(); | ||||
| #endif | ||||
|  | ||||
|   return resp; | ||||
|   return this->send_message(resp, HelloResponse::MESSAGE_TYPE); | ||||
| } | ||||
| ConnectResponse APIConnection::connect(const ConnectRequest &msg) { | ||||
| bool APIConnection::send_connect_response(const ConnectRequest &msg) { | ||||
|   bool correct = true; | ||||
| #ifdef USE_API_PASSWORD | ||||
|   correct = this->parent_->check_password(msg.password); | ||||
| @@ -1402,48 +1420,71 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) { | ||||
|   if (correct) { | ||||
|     this->complete_authentication_(); | ||||
|   } | ||||
|   return resp; | ||||
|   return this->send_message(resp, ConnectResponse::MESSAGE_TYPE); | ||||
| } | ||||
| DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { | ||||
|  | ||||
| bool APIConnection::send_ping_response(const PingRequest &msg) { | ||||
|   PingResponse resp; | ||||
|   return this->send_message(resp, PingResponse::MESSAGE_TYPE); | ||||
| } | ||||
|  | ||||
| bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) { | ||||
|   DeviceInfoResponse resp{}; | ||||
| #ifdef USE_API_PASSWORD | ||||
|   resp.uses_password = true; | ||||
| #endif | ||||
|   resp.name = App.get_name(); | ||||
|   resp.friendly_name = App.get_friendly_name(); | ||||
|   resp.set_name(StringRef(App.get_name())); | ||||
|   resp.set_friendly_name(StringRef(App.get_friendly_name())); | ||||
| #ifdef USE_AREAS | ||||
|   resp.suggested_area = App.get_area(); | ||||
|   resp.set_suggested_area(StringRef(App.get_area())); | ||||
| #endif | ||||
|   resp.mac_address = get_mac_address_pretty(); | ||||
|   resp.esphome_version = ESPHOME_VERSION; | ||||
|   resp.compilation_time = App.get_compilation_time(); | ||||
|   // mac_address must store temporary string - will be valid during send_message call | ||||
|   std::string mac_address = get_mac_address_pretty(); | ||||
|   resp.set_mac_address(StringRef(mac_address)); | ||||
|  | ||||
|   // Compile-time StringRef constants | ||||
|   static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION); | ||||
|   resp.set_esphome_version(ESPHOME_VERSION_REF); | ||||
|  | ||||
|   // get_compilation_time() returns temporary std::string - must store it | ||||
|   std::string compilation_time = App.get_compilation_time(); | ||||
|   resp.set_compilation_time(StringRef(compilation_time)); | ||||
|  | ||||
|   // Compile-time StringRef constants for manufacturers | ||||
| #if defined(USE_ESP8266) || defined(USE_ESP32) | ||||
|   resp.manufacturer = "Espressif"; | ||||
|   static constexpr auto MANUFACTURER = StringRef::from_lit("Espressif"); | ||||
| #elif defined(USE_RP2040) | ||||
|   resp.manufacturer = "Raspberry Pi"; | ||||
|   static constexpr auto MANUFACTURER = StringRef::from_lit("Raspberry Pi"); | ||||
| #elif defined(USE_BK72XX) | ||||
|   resp.manufacturer = "Beken"; | ||||
|   static constexpr auto MANUFACTURER = StringRef::from_lit("Beken"); | ||||
| #elif defined(USE_LN882X) | ||||
|   resp.manufacturer = "Lightning"; | ||||
|   static constexpr auto MANUFACTURER = StringRef::from_lit("Lightning"); | ||||
| #elif defined(USE_RTL87XX) | ||||
|   resp.manufacturer = "Realtek"; | ||||
|   static constexpr auto MANUFACTURER = StringRef::from_lit("Realtek"); | ||||
| #elif defined(USE_HOST) | ||||
|   resp.manufacturer = "Host"; | ||||
|   static constexpr auto MANUFACTURER = StringRef::from_lit("Host"); | ||||
| #endif | ||||
|   resp.model = ESPHOME_BOARD; | ||||
|   resp.set_manufacturer(MANUFACTURER); | ||||
|  | ||||
|   static constexpr auto MODEL = StringRef::from_lit(ESPHOME_BOARD); | ||||
|   resp.set_model(MODEL); | ||||
| #ifdef USE_DEEP_SLEEP | ||||
|   resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; | ||||
| #endif | ||||
| #ifdef ESPHOME_PROJECT_NAME | ||||
|   resp.project_name = ESPHOME_PROJECT_NAME; | ||||
|   resp.project_version = ESPHOME_PROJECT_VERSION; | ||||
|   static constexpr auto PROJECT_NAME = StringRef::from_lit(ESPHOME_PROJECT_NAME); | ||||
|   static constexpr auto PROJECT_VERSION = StringRef::from_lit(ESPHOME_PROJECT_VERSION); | ||||
|   resp.set_project_name(PROJECT_NAME); | ||||
|   resp.set_project_version(PROJECT_VERSION); | ||||
| #endif | ||||
| #ifdef USE_WEBSERVER | ||||
|   resp.webserver_port = USE_WEBSERVER_PORT; | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
|   resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags(); | ||||
|   resp.bluetooth_mac_address = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty(); | ||||
|   // bt_mac must store temporary string - will be valid during send_message call | ||||
|   std::string bluetooth_mac = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty(); | ||||
|   resp.set_bluetooth_mac_address(StringRef(bluetooth_mac)); | ||||
| #endif | ||||
| #ifdef USE_VOICE_ASSISTANT | ||||
|   resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags(); | ||||
| @@ -1453,23 +1494,25 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { | ||||
| #endif | ||||
| #ifdef USE_DEVICES | ||||
|   for (auto const &device : App.get_devices()) { | ||||
|     DeviceInfo device_info; | ||||
|     resp.devices.emplace_back(); | ||||
|     auto &device_info = resp.devices.back(); | ||||
|     device_info.device_id = device->get_device_id(); | ||||
|     device_info.name = device->get_name(); | ||||
|     device_info.set_name(StringRef(device->get_name())); | ||||
|     device_info.area_id = device->get_area_id(); | ||||
|     resp.devices.push_back(device_info); | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
|   for (auto const &area : App.get_areas()) { | ||||
|     AreaInfo area_info; | ||||
|     resp.areas.emplace_back(); | ||||
|     auto &area_info = resp.areas.back(); | ||||
|     area_info.area_id = area->get_area_id(); | ||||
|     area_info.name = area->get_name(); | ||||
|     resp.areas.push_back(area_info); | ||||
|     area_info.set_name(StringRef(area->get_name())); | ||||
|   } | ||||
| #endif | ||||
|   return resp; | ||||
|  | ||||
|   return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE); | ||||
| } | ||||
|  | ||||
| void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) { | ||||
|   for (auto &it : this->parent_->get_state_subs()) { | ||||
|     if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) { | ||||
| @@ -1491,23 +1534,21 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) { | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_API_NOISE | ||||
| NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) { | ||||
| bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) { | ||||
|   psk_t psk{}; | ||||
|   NoiseEncryptionSetKeyResponse resp; | ||||
|   if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) { | ||||
|     ESP_LOGW(TAG, "Invalid encryption key length"); | ||||
|     resp.success = false; | ||||
|     return resp; | ||||
|     return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE); | ||||
|   } | ||||
|  | ||||
|   if (!this->parent_->save_noise_psk(psk, true)) { | ||||
|     ESP_LOGW(TAG, "Failed to save encryption key"); | ||||
|     resp.success = false; | ||||
|     return resp; | ||||
|     return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE); | ||||
|   } | ||||
|  | ||||
|   resp.success = true; | ||||
|   return resp; | ||||
|   return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE); | ||||
| } | ||||
| #endif | ||||
| void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) { | ||||
|   | ||||
| @@ -148,8 +148,7 @@ class APIConnection : public APIServerConnection { | ||||
|   void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override; | ||||
|   void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override; | ||||
|   void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override; | ||||
|   BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( | ||||
|       const SubscribeBluetoothConnectionsFreeRequest &msg) override; | ||||
|   bool send_subscribe_bluetooth_connections_free_response(const SubscribeBluetoothConnectionsFreeRequest &msg) override; | ||||
|   void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override; | ||||
|  | ||||
| #endif | ||||
| @@ -167,8 +166,7 @@ class APIConnection : public APIServerConnection { | ||||
|   void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override; | ||||
|   void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override; | ||||
|   void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override; | ||||
|   VoiceAssistantConfigurationResponse voice_assistant_get_configuration( | ||||
|       const VoiceAssistantConfigurationRequest &msg) override; | ||||
|   bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) override; | ||||
|   void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override; | ||||
| #endif | ||||
|  | ||||
| @@ -195,11 +193,11 @@ class APIConnection : public APIServerConnection { | ||||
| #ifdef USE_HOMEASSISTANT_TIME | ||||
|   void on_get_time_response(const GetTimeResponse &value) override; | ||||
| #endif | ||||
|   HelloResponse hello(const HelloRequest &msg) override; | ||||
|   ConnectResponse connect(const ConnectRequest &msg) override; | ||||
|   DisconnectResponse disconnect(const DisconnectRequest &msg) override; | ||||
|   PingResponse ping(const PingRequest &msg) override { return {}; } | ||||
|   DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override; | ||||
|   bool send_hello_response(const HelloRequest &msg) override; | ||||
|   bool send_connect_response(const ConnectRequest &msg) override; | ||||
|   bool send_disconnect_response(const DisconnectRequest &msg) override; | ||||
|   bool send_ping_response(const PingRequest &msg) override; | ||||
|   bool send_device_info_response(const DeviceInfoRequest &msg) override; | ||||
|   void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); } | ||||
|   void subscribe_states(const SubscribeStatesRequest &msg) override { | ||||
|     this->flags_.state_subscription = true; | ||||
| @@ -214,15 +212,12 @@ class APIConnection : public APIServerConnection { | ||||
|     this->flags_.service_call_subscription = true; | ||||
|   } | ||||
|   void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override; | ||||
|   GetTimeResponse get_time(const GetTimeRequest &msg) override { | ||||
|     // TODO | ||||
|     return {}; | ||||
|   } | ||||
|   bool send_get_time_response(const GetTimeRequest &msg) override; | ||||
| #ifdef USE_API_SERVICES | ||||
|   void execute_service(const ExecuteServiceRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_API_NOISE | ||||
|   NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override; | ||||
|   bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) override; | ||||
| #endif | ||||
|  | ||||
|   bool is_authenticated() override { | ||||
| @@ -313,14 +308,17 @@ class APIConnection : public APIServerConnection { | ||||
|                                               APIConnection *conn, uint32_t remaining_size, bool is_single) { | ||||
|     // Set common fields that are shared by all entity types | ||||
|     msg.key = entity->get_object_id_hash(); | ||||
|     msg.object_id = entity->get_object_id(); | ||||
|     // IMPORTANT: get_object_id() may return a temporary std::string | ||||
|     std::string object_id = entity->get_object_id(); | ||||
|     msg.set_object_id(StringRef(object_id)); | ||||
|  | ||||
|     if (entity->has_own_name()) | ||||
|       msg.name = entity->get_name(); | ||||
|     if (entity->has_own_name()) { | ||||
|       msg.set_name(entity->get_name()); | ||||
|     } | ||||
|  | ||||
|       // Set common EntityBase properties | ||||
|     // Set common EntityBase properties | ||||
| #ifdef USE_ENTITY_ICON | ||||
|     msg.icon = entity->get_icon(); | ||||
|     msg.set_icon(entity->get_icon_ref()); | ||||
| #endif | ||||
|     msg.disabled_by_default = entity->is_disabled_by_default(); | ||||
|     msg.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category()); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -3,6 +3,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/string_ref.h" | ||||
|  | ||||
| #include "proto.h" | ||||
|  | ||||
| @@ -269,12 +270,15 @@ enum UpdateCommand : uint32_t { | ||||
| class InfoResponseProtoMessage : public ProtoMessage { | ||||
|  public: | ||||
|   ~InfoResponseProtoMessage() override = default; | ||||
|   std::string object_id{}; | ||||
|   StringRef object_id_ref_{}; | ||||
|   void set_object_id(const StringRef &ref) { this->object_id_ref_ = ref; } | ||||
|   uint32_t key{0}; | ||||
|   std::string name{}; | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   bool disabled_by_default{false}; | ||||
| #ifdef USE_ENTITY_ICON | ||||
|   std::string icon{}; | ||||
|   StringRef icon_ref_{}; | ||||
|   void set_icon(const StringRef &ref) { this->icon_ref_ = ref; } | ||||
| #endif | ||||
|   enums::EntityCategory entity_category{}; | ||||
| #ifdef USE_DEVICES | ||||
| @@ -332,8 +336,10 @@ class HelloResponse : public ProtoMessage { | ||||
| #endif | ||||
|   uint32_t api_version_major{0}; | ||||
|   uint32_t api_version_minor{0}; | ||||
|   std::string server_info{}; | ||||
|   std::string name{}; | ||||
|   StringRef server_info_ref_{}; | ||||
|   void set_server_info(const StringRef &ref) { this->server_info_ref_ = ref; } | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -442,7 +448,8 @@ class DeviceInfoRequest : public ProtoDecodableMessage { | ||||
| class AreaInfo : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t area_id{0}; | ||||
|   std::string name{}; | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -456,7 +463,8 @@ class AreaInfo : public ProtoMessage { | ||||
| class DeviceInfo : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t device_id{0}; | ||||
|   std::string name{}; | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   uint32_t area_id{0}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -477,19 +485,26 @@ class DeviceInfoResponse : public ProtoMessage { | ||||
| #ifdef USE_API_PASSWORD | ||||
|   bool uses_password{false}; | ||||
| #endif | ||||
|   std::string name{}; | ||||
|   std::string mac_address{}; | ||||
|   std::string esphome_version{}; | ||||
|   std::string compilation_time{}; | ||||
|   std::string model{}; | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   StringRef mac_address_ref_{}; | ||||
|   void set_mac_address(const StringRef &ref) { this->mac_address_ref_ = ref; } | ||||
|   StringRef esphome_version_ref_{}; | ||||
|   void set_esphome_version(const StringRef &ref) { this->esphome_version_ref_ = ref; } | ||||
|   StringRef compilation_time_ref_{}; | ||||
|   void set_compilation_time(const StringRef &ref) { this->compilation_time_ref_ = ref; } | ||||
|   StringRef model_ref_{}; | ||||
|   void set_model(const StringRef &ref) { this->model_ref_ = ref; } | ||||
| #ifdef USE_DEEP_SLEEP | ||||
|   bool has_deep_sleep{false}; | ||||
| #endif | ||||
| #ifdef ESPHOME_PROJECT_NAME | ||||
|   std::string project_name{}; | ||||
|   StringRef project_name_ref_{}; | ||||
|   void set_project_name(const StringRef &ref) { this->project_name_ref_ = ref; } | ||||
| #endif | ||||
| #ifdef ESPHOME_PROJECT_NAME | ||||
|   std::string project_version{}; | ||||
|   StringRef project_version_ref_{}; | ||||
|   void set_project_version(const StringRef &ref) { this->project_version_ref_ = ref; } | ||||
| #endif | ||||
| #ifdef USE_WEBSERVER | ||||
|   uint32_t webserver_port{0}; | ||||
| @@ -497,16 +512,20 @@ class DeviceInfoResponse : public ProtoMessage { | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
|   uint32_t bluetooth_proxy_feature_flags{0}; | ||||
| #endif | ||||
|   std::string manufacturer{}; | ||||
|   std::string friendly_name{}; | ||||
|   StringRef manufacturer_ref_{}; | ||||
|   void set_manufacturer(const StringRef &ref) { this->manufacturer_ref_ = ref; } | ||||
|   StringRef friendly_name_ref_{}; | ||||
|   void set_friendly_name(const StringRef &ref) { this->friendly_name_ref_ = ref; } | ||||
| #ifdef USE_VOICE_ASSISTANT | ||||
|   uint32_t voice_assistant_feature_flags{0}; | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
|   std::string suggested_area{}; | ||||
|   StringRef suggested_area_ref_{}; | ||||
|   void set_suggested_area(const StringRef &ref) { this->suggested_area_ref_ = ref; } | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
|   std::string bluetooth_mac_address{}; | ||||
|   StringRef bluetooth_mac_address_ref_{}; | ||||
|   void set_bluetooth_mac_address(const StringRef &ref) { this->bluetooth_mac_address_ref_ = ref; } | ||||
| #endif | ||||
| #ifdef USE_API_NOISE | ||||
|   bool api_encryption_supported{false}; | ||||
| @@ -575,7 +594,8 @@ class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_binary_sensor_response"; } | ||||
| #endif | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   bool is_status_binary_sensor{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -614,7 +634,8 @@ class ListEntitiesCoverResponse : public InfoResponseProtoMessage { | ||||
|   bool assumed_state{false}; | ||||
|   bool supports_position{false}; | ||||
|   bool supports_tilt{false}; | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   bool supports_stop{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -695,7 +716,8 @@ class FanStateResponse : public StateResponseProtoMessage { | ||||
|   bool oscillating{false}; | ||||
|   enums::FanDirection direction{}; | ||||
|   int32_t speed_level{0}; | ||||
|   std::string preset_mode{}; | ||||
|   StringRef preset_mode_ref_{}; | ||||
|   void set_preset_mode(const StringRef &ref) { this->preset_mode_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -769,7 +791,8 @@ class LightStateResponse : public StateResponseProtoMessage { | ||||
|   float color_temperature{0.0f}; | ||||
|   float cold_white{0.0f}; | ||||
|   float warm_white{0.0f}; | ||||
|   std::string effect{}; | ||||
|   StringRef effect_ref_{}; | ||||
|   void set_effect(const StringRef &ref) { this->effect_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -829,10 +852,12 @@ class ListEntitiesSensorResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_sensor_response"; } | ||||
| #endif | ||||
|   std::string unit_of_measurement{}; | ||||
|   StringRef unit_of_measurement_ref_{}; | ||||
|   void set_unit_of_measurement(const StringRef &ref) { this->unit_of_measurement_ref_ = ref; } | ||||
|   int32_t accuracy_decimals{0}; | ||||
|   bool force_update{false}; | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   enums::SensorStateClass state_class{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -869,7 +894,8 @@ class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { | ||||
|   const char *message_name() const override { return "list_entities_switch_response"; } | ||||
| #endif | ||||
|   bool assumed_state{false}; | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -919,7 +945,8 @@ class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_text_sensor_response"; } | ||||
| #endif | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -935,7 +962,8 @@ class TextSensorStateResponse : public StateResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "text_sensor_state_response"; } | ||||
| #endif | ||||
|   std::string state{}; | ||||
|   StringRef state_ref_{}; | ||||
|   void set_state(const StringRef &ref) { this->state_ref_ = ref; } | ||||
|   bool missing_state{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -1032,8 +1060,10 @@ class SubscribeHomeassistantServicesRequest : public ProtoDecodableMessage { | ||||
| }; | ||||
| class HomeassistantServiceMap : public ProtoMessage { | ||||
|  public: | ||||
|   std::string key{}; | ||||
|   std::string value{}; | ||||
|   StringRef key_ref_{}; | ||||
|   void set_key(const StringRef &ref) { this->key_ref_ = ref; } | ||||
|   StringRef value_ref_{}; | ||||
|   void set_value(const StringRef &ref) { this->value_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -1049,7 +1079,8 @@ class HomeassistantServiceResponse : public ProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "homeassistant_service_response"; } | ||||
| #endif | ||||
|   std::string service{}; | ||||
|   StringRef service_ref_{}; | ||||
|   void set_service(const StringRef &ref) { this->service_ref_ = ref; } | ||||
|   std::vector<HomeassistantServiceMap> data{}; | ||||
|   std::vector<HomeassistantServiceMap> data_template{}; | ||||
|   std::vector<HomeassistantServiceMap> variables{}; | ||||
| @@ -1082,8 +1113,10 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "subscribe_home_assistant_state_response"; } | ||||
| #endif | ||||
|   std::string entity_id{}; | ||||
|   std::string attribute{}; | ||||
|   StringRef entity_id_ref_{}; | ||||
|   void set_entity_id(const StringRef &ref) { this->entity_id_ref_ = ref; } | ||||
|   StringRef attribute_ref_{}; | ||||
|   void set_attribute(const StringRef &ref) { this->attribute_ref_ = ref; } | ||||
|   bool once{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -1143,7 +1176,8 @@ class GetTimeResponse : public ProtoDecodableMessage { | ||||
| #ifdef USE_API_SERVICES | ||||
| class ListEntitiesServicesArgument : public ProtoMessage { | ||||
|  public: | ||||
|   std::string name{}; | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   enums::ServiceArgType type{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -1160,7 +1194,8 @@ class ListEntitiesServicesResponse : public ProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_services_response"; } | ||||
| #endif | ||||
|   std::string name{}; | ||||
|   StringRef name_ref_{}; | ||||
|   void set_name(const StringRef &ref) { this->name_ref_ = ref; } | ||||
|   uint32_t key{0}; | ||||
|   std::vector<ListEntitiesServicesArgument> args{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| @@ -1312,9 +1347,11 @@ class ClimateStateResponse : public StateResponseProtoMessage { | ||||
|   enums::ClimateAction action{}; | ||||
|   enums::ClimateFanMode fan_mode{}; | ||||
|   enums::ClimateSwingMode swing_mode{}; | ||||
|   std::string custom_fan_mode{}; | ||||
|   StringRef custom_fan_mode_ref_{}; | ||||
|   void set_custom_fan_mode(const StringRef &ref) { this->custom_fan_mode_ref_ = ref; } | ||||
|   enums::ClimatePreset preset{}; | ||||
|   std::string custom_preset{}; | ||||
|   StringRef custom_preset_ref_{}; | ||||
|   void set_custom_preset(const StringRef &ref) { this->custom_preset_ref_ = ref; } | ||||
|   float current_humidity{0.0f}; | ||||
|   float target_humidity{0.0f}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| @@ -1373,9 +1410,11 @@ class ListEntitiesNumberResponse : public InfoResponseProtoMessage { | ||||
|   float min_value{0.0f}; | ||||
|   float max_value{0.0f}; | ||||
|   float step{0.0f}; | ||||
|   std::string unit_of_measurement{}; | ||||
|   StringRef unit_of_measurement_ref_{}; | ||||
|   void set_unit_of_measurement(const StringRef &ref) { this->unit_of_measurement_ref_ = ref; } | ||||
|   enums::NumberMode mode{}; | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -1442,7 +1481,8 @@ class SelectStateResponse : public StateResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "select_state_response"; } | ||||
| #endif | ||||
|   std::string state{}; | ||||
|   StringRef state_ref_{}; | ||||
|   void set_state(const StringRef &ref) { this->state_ref_ = ref; } | ||||
|   bool missing_state{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -1541,7 +1581,8 @@ class ListEntitiesLockResponse : public InfoResponseProtoMessage { | ||||
|   bool assumed_state{false}; | ||||
|   bool supports_open{false}; | ||||
|   bool requires_code{false}; | ||||
|   std::string code_format{}; | ||||
|   StringRef code_format_ref_{}; | ||||
|   void set_code_format(const StringRef &ref) { this->code_format_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -1594,7 +1635,8 @@ class ListEntitiesButtonResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_button_response"; } | ||||
| #endif | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -1622,7 +1664,8 @@ class ButtonCommandRequest : public CommandProtoMessage { | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| class MediaPlayerSupportedFormat : public ProtoMessage { | ||||
|  public: | ||||
|   std::string format{}; | ||||
|   StringRef format_ref_{}; | ||||
|   void set_format(const StringRef &ref) { this->format_ref_ = ref; } | ||||
|   uint32_t sample_rate{0}; | ||||
|   uint32_t num_channels{0}; | ||||
|   enums::MediaPlayerFormatPurpose purpose{}; | ||||
| @@ -2219,10 +2262,12 @@ class VoiceAssistantRequest : public ProtoMessage { | ||||
|   const char *message_name() const override { return "voice_assistant_request"; } | ||||
| #endif | ||||
|   bool start{false}; | ||||
|   std::string conversation_id{}; | ||||
|   StringRef conversation_id_ref_{}; | ||||
|   void set_conversation_id(const StringRef &ref) { this->conversation_id_ref_ = ref; } | ||||
|   uint32_t flags{0}; | ||||
|   VoiceAssistantAudioSettings audio_settings{}; | ||||
|   std::string wake_word_phrase{}; | ||||
|   StringRef wake_word_phrase_ref_{}; | ||||
|   void set_wake_word_phrase(const StringRef &ref) { this->wake_word_phrase_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -2358,8 +2403,10 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage { | ||||
| }; | ||||
| class VoiceAssistantWakeWord : public ProtoMessage { | ||||
|  public: | ||||
|   std::string id{}; | ||||
|   std::string wake_word{}; | ||||
|   StringRef id_ref_{}; | ||||
|   void set_id(const StringRef &ref) { this->id_ref_ = ref; } | ||||
|   StringRef wake_word_ref_{}; | ||||
|   void set_wake_word(const StringRef &ref) { this->wake_word_ref_ = ref; } | ||||
|   std::vector<std::string> trained_languages{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -2480,7 +2527,8 @@ class ListEntitiesTextResponse : public InfoResponseProtoMessage { | ||||
| #endif | ||||
|   uint32_t min_length{0}; | ||||
|   uint32_t max_length{0}; | ||||
|   std::string pattern{}; | ||||
|   StringRef pattern_ref_{}; | ||||
|   void set_pattern(const StringRef &ref) { this->pattern_ref_ = ref; } | ||||
|   enums::TextMode mode{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -2497,7 +2545,8 @@ class TextStateResponse : public StateResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "text_state_response"; } | ||||
| #endif | ||||
|   std::string state{}; | ||||
|   StringRef state_ref_{}; | ||||
|   void set_state(const StringRef &ref) { this->state_ref_ = ref; } | ||||
|   bool missing_state{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -2641,7 +2690,8 @@ class ListEntitiesEventResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_event_response"; } | ||||
| #endif | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   std::vector<std::string> event_types{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| @@ -2658,7 +2708,8 @@ class EventResponse : public StateResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "event_response"; } | ||||
| #endif | ||||
|   std::string event_type{}; | ||||
|   StringRef event_type_ref_{}; | ||||
|   void set_event_type(const StringRef &ref) { this->event_type_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -2676,7 +2727,8 @@ class ListEntitiesValveResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_valve_response"; } | ||||
| #endif | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   bool assumed_state{false}; | ||||
|   bool supports_position{false}; | ||||
|   bool supports_stop{false}; | ||||
| @@ -2782,7 +2834,8 @@ class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   const char *message_name() const override { return "list_entities_update_response"; } | ||||
| #endif | ||||
|   std::string device_class{}; | ||||
|   StringRef device_class_ref_{}; | ||||
|   void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -2802,11 +2855,16 @@ class UpdateStateResponse : public StateResponseProtoMessage { | ||||
|   bool in_progress{false}; | ||||
|   bool has_progress{false}; | ||||
|   float progress{0.0f}; | ||||
|   std::string current_version{}; | ||||
|   std::string latest_version{}; | ||||
|   std::string title{}; | ||||
|   std::string release_summary{}; | ||||
|   std::string release_url{}; | ||||
|   StringRef current_version_ref_{}; | ||||
|   void set_current_version(const StringRef &ref) { this->current_version_ref_ = ref; } | ||||
|   StringRef latest_version_ref_{}; | ||||
|   void set_latest_version(const StringRef &ref) { this->latest_version_ref_ = ref; } | ||||
|   StringRef title_ref_{}; | ||||
|   void set_title(const StringRef &ref) { this->title_ref_ = ref; } | ||||
|   StringRef release_summary_ref_{}; | ||||
|   void set_release_summary(const StringRef &ref) { this->release_summary_ref_ = ref; } | ||||
|   StringRef release_url_ref_{}; | ||||
|   void set_release_url(const StringRef &ref) { this->release_url_ref_ = ref; } | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -597,33 +597,28 @@ void 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_message(ret, HelloResponse::MESSAGE_TYPE)) { | ||||
|   if (!this->send_hello_response(msg)) { | ||||
|     this->on_fatal_error(); | ||||
|   } | ||||
| } | ||||
| void APIServerConnection::on_connect_request(const ConnectRequest &msg) { | ||||
|   ConnectResponse ret = this->connect(msg); | ||||
|   if (!this->send_message(ret, ConnectResponse::MESSAGE_TYPE)) { | ||||
|   if (!this->send_connect_response(msg)) { | ||||
|     this->on_fatal_error(); | ||||
|   } | ||||
| } | ||||
| void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) { | ||||
|   DisconnectResponse ret = this->disconnect(msg); | ||||
|   if (!this->send_message(ret, DisconnectResponse::MESSAGE_TYPE)) { | ||||
|   if (!this->send_disconnect_response(msg)) { | ||||
|     this->on_fatal_error(); | ||||
|   } | ||||
| } | ||||
| void APIServerConnection::on_ping_request(const PingRequest &msg) { | ||||
|   PingResponse ret = this->ping(msg); | ||||
|   if (!this->send_message(ret, PingResponse::MESSAGE_TYPE)) { | ||||
|   if (!this->send_ping_response(msg)) { | ||||
|     this->on_fatal_error(); | ||||
|   } | ||||
| } | ||||
| void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) { | ||||
|   if (this->check_connection_setup_()) { | ||||
|     DeviceInfoResponse ret = this->device_info(msg); | ||||
|     if (!this->send_message(ret, DeviceInfoResponse::MESSAGE_TYPE)) { | ||||
|     if (!this->send_device_info_response(msg)) { | ||||
|       this->on_fatal_error(); | ||||
|     } | ||||
|   } | ||||
| @@ -656,8 +651,7 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc | ||||
| } | ||||
| void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { | ||||
|   if (this->check_connection_setup_()) { | ||||
|     GetTimeResponse ret = this->get_time(msg); | ||||
|     if (!this->send_message(ret, GetTimeResponse::MESSAGE_TYPE)) { | ||||
|     if (!this->send_get_time_response(msg)) { | ||||
|       this->on_fatal_error(); | ||||
|     } | ||||
|   } | ||||
| @@ -672,8 +666,7 @@ void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest | ||||
| #ifdef USE_API_NOISE | ||||
| void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) { | ||||
|   if (this->check_authenticated_()) { | ||||
|     NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg); | ||||
|     if (!this->send_message(ret, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE)) { | ||||
|     if (!this->send_noise_encryption_set_key_response(msg)) { | ||||
|       this->on_fatal_error(); | ||||
|     } | ||||
|   } | ||||
| @@ -866,8 +859,7 @@ void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNo | ||||
| void APIServerConnection::on_subscribe_bluetooth_connections_free_request( | ||||
|     const SubscribeBluetoothConnectionsFreeRequest &msg) { | ||||
|   if (this->check_authenticated_()) { | ||||
|     BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg); | ||||
|     if (!this->send_message(ret, BluetoothConnectionsFreeResponse::MESSAGE_TYPE)) { | ||||
|     if (!this->send_subscribe_bluetooth_connections_free_response(msg)) { | ||||
|       this->on_fatal_error(); | ||||
|     } | ||||
|   } | ||||
| @@ -898,8 +890,7 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo | ||||
| #ifdef USE_VOICE_ASSISTANT | ||||
| void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) { | ||||
|   if (this->check_authenticated_()) { | ||||
|     VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg); | ||||
|     if (!this->send_message(ret, VoiceAssistantConfigurationResponse::MESSAGE_TYPE)) { | ||||
|     if (!this->send_voice_assistant_get_configuration_response(msg)) { | ||||
|       this->on_fatal_error(); | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -207,22 +207,22 @@ class APIServerConnectionBase : public ProtoService { | ||||
|  | ||||
| class APIServerConnection : public APIServerConnectionBase { | ||||
|  public: | ||||
|   virtual HelloResponse hello(const HelloRequest &msg) = 0; | ||||
|   virtual ConnectResponse connect(const ConnectRequest &msg) = 0; | ||||
|   virtual DisconnectResponse disconnect(const DisconnectRequest &msg) = 0; | ||||
|   virtual PingResponse ping(const PingRequest &msg) = 0; | ||||
|   virtual DeviceInfoResponse device_info(const DeviceInfoRequest &msg) = 0; | ||||
|   virtual bool send_hello_response(const HelloRequest &msg) = 0; | ||||
|   virtual bool send_connect_response(const ConnectRequest &msg) = 0; | ||||
|   virtual bool send_disconnect_response(const DisconnectRequest &msg) = 0; | ||||
|   virtual bool send_ping_response(const PingRequest &msg) = 0; | ||||
|   virtual bool send_device_info_response(const DeviceInfoRequest &msg) = 0; | ||||
|   virtual void list_entities(const ListEntitiesRequest &msg) = 0; | ||||
|   virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0; | ||||
|   virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0; | ||||
|   virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0; | ||||
|   virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; | ||||
|   virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; | ||||
|   virtual bool send_get_time_response(const GetTimeRequest &msg) = 0; | ||||
| #ifdef USE_API_SERVICES | ||||
|   virtual void execute_service(const ExecuteServiceRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_API_NOISE | ||||
|   virtual NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) = 0; | ||||
|   virtual bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_BUTTON | ||||
|   virtual void button_command(const ButtonCommandRequest &msg) = 0; | ||||
| @@ -303,7 +303,7 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
|   virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
|   virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( | ||||
|   virtual bool send_subscribe_bluetooth_connections_free_response( | ||||
|       const SubscribeBluetoothConnectionsFreeRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
| @@ -316,8 +316,7 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
|   virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_VOICE_ASSISTANT | ||||
|   virtual VoiceAssistantConfigurationResponse voice_assistant_get_configuration( | ||||
|       const VoiceAssistantConfigurationRequest &msg) = 0; | ||||
|   virtual bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_VOICE_ASSISTANT | ||||
|   virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0; | ||||
|   | ||||
| @@ -148,7 +148,7 @@ class CustomAPIDevice { | ||||
|    */ | ||||
|   void call_homeassistant_service(const std::string &service_name) { | ||||
|     HomeassistantServiceResponse resp; | ||||
|     resp.service = service_name; | ||||
|     resp.set_service(StringRef(service_name)); | ||||
|     global_api_server->send_homeassistant_service_call(resp); | ||||
|   } | ||||
|  | ||||
| @@ -168,12 +168,12 @@ class CustomAPIDevice { | ||||
|    */ | ||||
|   void call_homeassistant_service(const std::string &service_name, const std::map<std::string, std::string> &data) { | ||||
|     HomeassistantServiceResponse resp; | ||||
|     resp.service = service_name; | ||||
|     resp.set_service(StringRef(service_name)); | ||||
|     for (auto &it : data) { | ||||
|       HomeassistantServiceMap kv; | ||||
|       kv.key = it.first; | ||||
|       kv.value = it.second; | ||||
|       resp.data.push_back(kv); | ||||
|       resp.data.emplace_back(); | ||||
|       auto &kv = resp.data.back(); | ||||
|       kv.set_key(StringRef(it.first)); | ||||
|       kv.set_value(StringRef(it.second)); | ||||
|     } | ||||
|     global_api_server->send_homeassistant_service_call(resp); | ||||
|   } | ||||
| @@ -190,7 +190,7 @@ class CustomAPIDevice { | ||||
|    */ | ||||
|   void fire_homeassistant_event(const std::string &event_name) { | ||||
|     HomeassistantServiceResponse resp; | ||||
|     resp.service = event_name; | ||||
|     resp.set_service(StringRef(event_name)); | ||||
|     resp.is_event = true; | ||||
|     global_api_server->send_homeassistant_service_call(resp); | ||||
|   } | ||||
| @@ -210,13 +210,13 @@ class CustomAPIDevice { | ||||
|    */ | ||||
|   void fire_homeassistant_event(const std::string &service_name, const std::map<std::string, std::string> &data) { | ||||
|     HomeassistantServiceResponse resp; | ||||
|     resp.service = service_name; | ||||
|     resp.set_service(StringRef(service_name)); | ||||
|     resp.is_event = true; | ||||
|     for (auto &it : data) { | ||||
|       HomeassistantServiceMap kv; | ||||
|       kv.key = it.first; | ||||
|       kv.value = it.second; | ||||
|       resp.data.push_back(kv); | ||||
|       resp.data.emplace_back(); | ||||
|       auto &kv = resp.data.back(); | ||||
|       kv.set_key(StringRef(it.first)); | ||||
|       kv.set_value(StringRef(it.second)); | ||||
|     } | ||||
|     global_api_server->send_homeassistant_service_call(resp); | ||||
|   } | ||||
|   | ||||
| @@ -59,25 +59,29 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     HomeassistantServiceResponse resp; | ||||
|     resp.service = this->service_.value(x...); | ||||
|     std::string service_value = this->service_.value(x...); | ||||
|     resp.set_service(StringRef(service_value)); | ||||
|     resp.is_event = this->is_event_; | ||||
|     for (auto &it : this->data_) { | ||||
|       HomeassistantServiceMap kv; | ||||
|       kv.key = it.key; | ||||
|       kv.value = it.value.value(x...); | ||||
|       resp.data.push_back(kv); | ||||
|       resp.data.emplace_back(); | ||||
|       auto &kv = resp.data.back(); | ||||
|       kv.set_key(StringRef(it.key)); | ||||
|       std::string value = it.value.value(x...); | ||||
|       kv.set_value(StringRef(value)); | ||||
|     } | ||||
|     for (auto &it : this->data_template_) { | ||||
|       HomeassistantServiceMap kv; | ||||
|       kv.key = it.key; | ||||
|       kv.value = it.value.value(x...); | ||||
|       resp.data_template.push_back(kv); | ||||
|       resp.data_template.emplace_back(); | ||||
|       auto &kv = resp.data_template.back(); | ||||
|       kv.set_key(StringRef(it.key)); | ||||
|       std::string value = it.value.value(x...); | ||||
|       kv.set_value(StringRef(value)); | ||||
|     } | ||||
|     for (auto &it : this->variables_) { | ||||
|       HomeassistantServiceMap kv; | ||||
|       kv.key = it.key; | ||||
|       kv.value = it.value.value(x...); | ||||
|       resp.variables.push_back(kv); | ||||
|       resp.variables.emplace_back(); | ||||
|       auto &kv = resp.variables.back(); | ||||
|       kv.set_key(StringRef(it.key)); | ||||
|       std::string value = it.value.value(x...); | ||||
|       kv.set_value(StringRef(value)); | ||||
|     } | ||||
|     this->parent_->send_homeassistant_service_call(resp); | ||||
|   } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/string_ref.h" | ||||
|  | ||||
| #include <cassert> | ||||
| #include <cstring> | ||||
| @@ -15,6 +16,37 @@ | ||||
| namespace esphome { | ||||
| namespace api { | ||||
|  | ||||
| /* | ||||
|  * StringRef Ownership Model for API Protocol Messages | ||||
|  * =================================================== | ||||
|  * | ||||
|  * StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages. | ||||
|  * It holds a pointer and length to existing string data without copying. | ||||
|  * | ||||
|  * CRITICAL: The referenced string data MUST remain valid until message encoding completes. | ||||
|  * | ||||
|  * Safe StringRef Patterns: | ||||
|  * 1. String literals: StringRef("literal") - Always safe (static storage duration) | ||||
|  * 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding | ||||
|  * 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe | ||||
|  * 4. Local variables: Safe ONLY if encoding happens before function returns: | ||||
|  *    std::string temp = compute_value(); | ||||
|  *    msg.set_field(StringRef(temp)); | ||||
|  *    return this->send_message(msg);  // temp is valid during encoding | ||||
|  * | ||||
|  * Unsafe Patterns (WILL cause crashes/corruption): | ||||
|  * 1. Temporaries: msg.set_field(StringRef(obj.get_string())) // get_string() returns by value | ||||
|  * 2. Optional values: msg.set_field(StringRef(optional.value())) // value() returns a copy | ||||
|  * 3. Concatenation: msg.set_field(StringRef(str1 + str2)) // Result is temporary | ||||
|  * | ||||
|  * For unsafe patterns, store in a local variable first: | ||||
|  *    std::string temp = optional.value();  // or get_string() or str1 + str2 | ||||
|  *    msg.set_field(StringRef(temp)); | ||||
|  * | ||||
|  * The send_*_response pattern ensures proper lifetime management by encoding | ||||
|  * within the same function scope where temporaries are created. | ||||
|  */ | ||||
|  | ||||
| /// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit | ||||
| class ProtoVarInt { | ||||
|  public: | ||||
| @@ -218,6 +250,9 @@ class ProtoWriteBuffer { | ||||
|   void encode_string(uint32_t field_id, const std::string &value, bool force = false) { | ||||
|     this->encode_string(field_id, value.data(), value.size(), force); | ||||
|   } | ||||
|   void encode_string(uint32_t field_id, const StringRef &ref, bool force = false) { | ||||
|     this->encode_string(field_id, ref.c_str(), ref.size(), force); | ||||
|   } | ||||
|   void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) { | ||||
|     this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force); | ||||
|   } | ||||
| @@ -669,17 +704,16 @@ class ProtoSize { | ||||
|   // sint64 type is not supported by ESPHome API to reduce overhead on embedded systems | ||||
|  | ||||
|   /** | ||||
|    * @brief Calculates and adds the size of a string/bytes field to the total message size | ||||
|    * @brief Calculates and adds the size of a string field using length | ||||
|    */ | ||||
|   static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { | ||||
|   static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, size_t len) { | ||||
|     // Skip calculation if string is empty | ||||
|     if (str.empty()) { | ||||
|     if (len == 0) { | ||||
|       return;  // No need to update total_size | ||||
|     } | ||||
|  | ||||
|     // Calculate and directly add to total_size | ||||
|     const uint32_t str_size = static_cast<uint32_t>(str.size()); | ||||
|     total_size += field_id_size + varint(str_size) + str_size; | ||||
|     // Field ID + length varint + string bytes | ||||
|     total_size += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   | ||||
| @@ -33,14 +33,14 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor { | ||||
|  | ||||
|   ListEntitiesServicesResponse encode_list_service_response() override { | ||||
|     ListEntitiesServicesResponse msg; | ||||
|     msg.name = this->name_; | ||||
|     msg.set_name(StringRef(this->name_)); | ||||
|     msg.key = this->key_; | ||||
|     std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...}; | ||||
|     for (int i = 0; i < sizeof...(Ts); i++) { | ||||
|       ListEntitiesServicesArgument arg; | ||||
|       msg.args.emplace_back(); | ||||
|       auto &arg = msg.args.back(); | ||||
|       arg.type = arg_types[i]; | ||||
|       arg.name = this->arg_names_[i]; | ||||
|       msg.args.push_back(arg); | ||||
|       arg.set_name(StringRef(this->arg_names_[i])); | ||||
|     } | ||||
|     return msg; | ||||
|   } | ||||
|   | ||||
| @@ -83,18 +83,24 @@ void HomeassistantNumber::control(float value) { | ||||
|  | ||||
|   this->publish_state(value); | ||||
|  | ||||
|   static constexpr auto SERVICE_NAME = StringRef::from_lit("number.set_value"); | ||||
|   static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id"); | ||||
|   static constexpr auto VALUE_KEY = StringRef::from_lit("value"); | ||||
|  | ||||
|   api::HomeassistantServiceResponse resp; | ||||
|   resp.service = "number.set_value"; | ||||
|   resp.set_service(SERVICE_NAME); | ||||
|  | ||||
|   api::HomeassistantServiceMap entity_id; | ||||
|   entity_id.key = "entity_id"; | ||||
|   entity_id.value = this->entity_id_; | ||||
|   resp.data.push_back(entity_id); | ||||
|   resp.data.emplace_back(); | ||||
|   auto &entity_id = resp.data.back(); | ||||
|   entity_id.set_key(ENTITY_ID_KEY); | ||||
|   entity_id.set_value(StringRef(this->entity_id_)); | ||||
|  | ||||
|   api::HomeassistantServiceMap entity_value; | ||||
|   entity_value.key = "value"; | ||||
|   entity_value.value = to_string(value); | ||||
|   resp.data.push_back(entity_value); | ||||
|   resp.data.emplace_back(); | ||||
|   auto &entity_value = resp.data.back(); | ||||
|   entity_value.set_key(VALUE_KEY); | ||||
|   // to_string() returns a temporary - must store it to avoid dangling reference | ||||
|   std::string value_str = to_string(value); | ||||
|   entity_value.set_value(StringRef(value_str)); | ||||
|  | ||||
|   api::global_api_server->send_homeassistant_service_call(resp); | ||||
| } | ||||
|   | ||||
| @@ -40,17 +40,21 @@ void HomeassistantSwitch::write_state(bool state) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   static constexpr auto SERVICE_ON = StringRef::from_lit("homeassistant.turn_on"); | ||||
|   static constexpr auto SERVICE_OFF = StringRef::from_lit("homeassistant.turn_off"); | ||||
|   static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id"); | ||||
|  | ||||
|   api::HomeassistantServiceResponse resp; | ||||
|   if (state) { | ||||
|     resp.service = "homeassistant.turn_on"; | ||||
|     resp.set_service(SERVICE_ON); | ||||
|   } else { | ||||
|     resp.service = "homeassistant.turn_off"; | ||||
|     resp.set_service(SERVICE_OFF); | ||||
|   } | ||||
|  | ||||
|   api::HomeassistantServiceMap entity_id_kv; | ||||
|   entity_id_kv.key = "entity_id"; | ||||
|   entity_id_kv.value = this->entity_id_; | ||||
|   resp.data.push_back(entity_id_kv); | ||||
|   resp.data.emplace_back(); | ||||
|   auto &entity_id_kv = resp.data.back(); | ||||
|   entity_id_kv.set_key(ENTITY_ID_KEY); | ||||
|   entity_id_kv.set_value(StringRef(this->entity_id_)); | ||||
|  | ||||
|   api::global_api_server->send_homeassistant_service_call(resp); | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| #include <utility> | ||||
|  | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/string_ref.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace text { | ||||
| @@ -23,6 +24,7 @@ class TextTraits { | ||||
|   // Set/get the pattern. | ||||
|   void set_pattern(std::string pattern) { this->pattern_ = std::move(pattern); } | ||||
|   std::string get_pattern() const { return this->pattern_; } | ||||
|   StringRef get_pattern_ref() const { return StringRef(this->pattern_); } | ||||
|  | ||||
|   // Set/get the frontend mode. | ||||
|   void set_mode(TextMode mode) { this->mode_ = mode; } | ||||
|   | ||||
| @@ -238,10 +238,10 @@ void VoiceAssistant::loop() { | ||||
|  | ||||
|       api::VoiceAssistantRequest msg; | ||||
|       msg.start = true; | ||||
|       msg.conversation_id = this->conversation_id_; | ||||
|       msg.set_conversation_id(StringRef(this->conversation_id_)); | ||||
|       msg.flags = flags; | ||||
|       msg.audio_settings = audio_settings; | ||||
|       msg.wake_word_phrase = this->wake_word_; | ||||
|       msg.set_wake_word_phrase(StringRef(this->wake_word_)); | ||||
|       this->wake_word_ = ""; | ||||
|  | ||||
|       // Reset media player state tracking | ||||
|   | ||||
| @@ -54,6 +54,14 @@ class EntityBase { | ||||
|   // Get/set this entity's icon | ||||
|   std::string get_icon() const; | ||||
|   void set_icon(const char *icon); | ||||
|   StringRef get_icon_ref() const { | ||||
|     static constexpr auto EMPTY_STRING = StringRef::from_lit(""); | ||||
| #ifdef USE_ENTITY_ICON | ||||
|     return this->icon_c_str_ == nullptr ? EMPTY_STRING : StringRef(this->icon_c_str_); | ||||
| #else | ||||
|     return EMPTY_STRING; | ||||
| #endif | ||||
|   } | ||||
|  | ||||
| #ifdef USE_DEVICES | ||||
|   // Get/set this entity's device id | ||||
| @@ -105,6 +113,11 @@ class EntityBase_DeviceClass {  // NOLINT(readability-identifier-naming) | ||||
|   std::string get_device_class(); | ||||
|   /// Manually set the device class. | ||||
|   void set_device_class(const char *device_class); | ||||
|   /// Get the device class as StringRef | ||||
|   StringRef get_device_class_ref() const { | ||||
|     static constexpr auto EMPTY_STRING = StringRef::from_lit(""); | ||||
|     return this->device_class_ == nullptr ? EMPTY_STRING : StringRef(this->device_class_); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   const char *device_class_{nullptr};  ///< Device class override | ||||
| @@ -116,6 +129,11 @@ class EntityBase_UnitOfMeasurement {  // NOLINT(readability-identifier-naming) | ||||
|   std::string get_unit_of_measurement(); | ||||
|   /// Manually set the unit of measurement. | ||||
|   void set_unit_of_measurement(const char *unit_of_measurement); | ||||
|   /// Get the unit of measurement as StringRef | ||||
|   StringRef get_unit_of_measurement_ref() const { | ||||
|     static constexpr auto EMPTY_STRING = StringRef::from_lit(""); | ||||
|     return this->unit_of_measurement_ == nullptr ? EMPTY_STRING : StringRef(this->unit_of_measurement_); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   const char *unit_of_measurement_{nullptr};  ///< Unit of measurement override | ||||
|   | ||||
		Reference in New Issue
	
	Block a user