18#ifdef USE_HOMEASSISTANT_TIME
21#ifdef USE_BLUETOOTH_PROXY
24#ifdef USE_VOICE_ASSISTANT
35static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
36static constexpr uint8_t MAX_PING_RETRIES = 60;
37static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
38static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
40static const char *
const TAG =
"api.connection";
42static const int CAMERA_STOP_STREAM = 5000;
46#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
47 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
48 if ((entity_var) == nullptr) \
50 auto call = (entity_var)->make_call();
53#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
54 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
55 if ((entity_var) == nullptr) \
59 : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
60#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
62 if (noise_ctx->has_psk()) {
63 this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
67#elif defined(USE_API_PLAINTEXT)
68 this->helper_ = std::unique_ptr<APIFrameHelper>{
new APIPlaintextFrameHelper(std::move(sock))};
69#elif defined(USE_API_NOISE)
70 this->helper_ = std::unique_ptr<APIFrameHelper>{
new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
72#error "No frame helper defined"
81uint32_t APIConnection::get_batch_delay_ms_()
const {
return this->parent_->get_batch_delay(); }
83void APIConnection::start() {
86 APIError err = this->helper_->init();
87 if (err != APIError::OK) {
89 ESP_LOGW(TAG,
"%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(),
93 this->client_info_ = helper_->getpeername();
94 this->client_peername_ = this->client_info_;
95 this->helper_->set_log_info(this->client_info_);
98APIConnection::~APIConnection() {
99#ifdef USE_BLUETOOTH_PROXY
104#ifdef USE_VOICE_ASSISTANT
111void APIConnection::loop() {
112 if (this->flags_.next_close) {
114 this->helper_->close();
115 this->flags_.remove =
true;
119 APIError err = this->helper_->loop();
120 if (err != APIError::OK) {
122 ESP_LOGW(TAG,
"%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
129 if (this->helper_->is_socket_ready()) {
131 for (uint8_t message_count = 0; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
133 err = this->helper_->read_packet(&buffer);
134 if (err == APIError::WOULD_BLOCK) {
137 }
else if (err != APIError::OK) {
139 if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
140 ESP_LOGW(TAG,
"%s: Connection reset", this->get_client_combined_info().c_str());
141 }
else if (err == APIError::CONNECTION_CLOSED) {
142 ESP_LOGW(TAG,
"%s: Connection closed", this->get_client_combined_info().c_str());
144 ESP_LOGW(TAG,
"%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(),
149 this->last_traffic_ = now;
154 this->read_message(0, buffer.
type,
nullptr);
156 if (this->flags_.remove)
163 if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
164 this->process_batch_();
167 if (!this->list_entities_iterator_.completed()) {
168 this->process_iterator_batch_(this->list_entities_iterator_);
169 }
else if (!this->initial_state_iterator_.completed()) {
170 this->process_iterator_batch_(this->initial_state_iterator_);
173 if (this->initial_state_iterator_.completed()) {
175 if (!this->deferred_batch_.empty()) {
176 this->process_batch_();
179 this->flags_.should_try_send_immediately =
true;
183 if (this->flags_.sent_ping) {
185 if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
187 ESP_LOGW(TAG,
"%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
189 }
else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
190 ESP_LOGVV(TAG,
"Sending keepalive PING");
191 this->flags_.sent_ping = this->send_message(
PingRequest());
192 if (!this->flags_.sent_ping) {
195 ESP_LOGW(TAG,
"Buffer full, ping queued");
196 this->schedule_message_front_(
nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE,
197 PingRequest::ESTIMATED_SIZE);
198 this->flags_.sent_ping =
true;
203 if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
204 uint32_t to_send = std::min((
size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
205 bool done = this->image_reader_->available() == to_send;
206 uint32_t msg_size = 0;
207 ProtoSize::add_fixed_field<4>(msg_size, 1,
true);
210 msg_size += 1 + ProtoSize::varint(to_send) + to_send;
211 ProtoSize::add_bool_field(msg_size, 1, done);
213 auto buffer = this->create_buffer(msg_size);
217 buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send);
219 buffer.encode_bool(3, done);
221 bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
224 this->image_reader_->consume_data(to_send);
226 this->image_reader_->return_image();
232 if (state_subs_at_ >= 0) {
233 const auto &subs = this->parent_->get_state_subs();
234 if (state_subs_at_ <
static_cast<int>(subs.size())) {
235 auto &it = subs[state_subs_at_];
240 if (this->send_message(resp)) {
257 ESP_LOGD(TAG,
"%s disconnected", this->get_client_combined_info().c_str());
258 this->flags_.next_close =
true;
263 this->helper_->close();
264 this->flags_.remove =
true;
270 uint32_t remaining_size,
bool is_single) {
271#ifdef HAS_PROTO_MESSAGE_DUMP
280 uint32_t calculated_size = 0;
284 const uint8_t header_padding = conn->
helper_->frame_header_padding();
285 const uint8_t footer_size = conn->
helper_->frame_footer_size();
288 size_t total_calculated_size = calculated_size + header_padding + footer_size;
291 if (total_calculated_size > remaining_size) {
301 size_t size_before_encode = shared_buf.size();
307 size_t actual_payload_size = shared_buf.size() - size_before_encode;
310 size_t actual_total_size = header_padding + actual_payload_size + footer_size;
313 assert(calculated_size == actual_payload_size);
314 return static_cast<uint16_t
>(actual_total_size);
317#ifdef USE_BINARY_SENSOR
319 return this->send_message_smart_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
320 BinarySensorStateResponse::MESSAGE_TYPE, BinarySensorStateResponse::ESTIMATED_SIZE);
327 resp.
state = binary_sensor->state;
329 fill_entity_state_base(binary_sensor, resp);
330 return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
340 fill_entity_info_base(binary_sensor, msg);
341 return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
347 return this->send_message_smart_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE,
348 CoverStateResponse::ESTIMATED_SIZE);
354 auto traits = cover->get_traits();
356 (cover->position ==
cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
358 if (traits.get_supports_tilt())
359 msg.
tilt = cover->tilt;
361 fill_entity_state_base(cover, msg);
362 return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
368 auto traits = cover->get_traits();
375 fill_entity_info_base(cover, msg);
376 return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
382 case enums::LEGACY_COVER_COMMAND_OPEN:
383 call.set_command_open();
385 case enums::LEGACY_COVER_COMMAND_CLOSE:
386 call.set_command_close();
388 case enums::LEGACY_COVER_COMMAND_STOP:
389 call.set_command_stop();
396 call.set_tilt(msg.
tilt);
398 call.set_command_stop();
405 return this->send_message_smart_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE,
406 FanStateResponse::ESTIMATED_SIZE);
410 auto *fan =
static_cast<fan::Fan *
>(entity);
412 auto traits = fan->get_traits();
413 msg.
state = fan->state;
414 if (traits.supports_oscillation())
416 if (traits.supports_speed()) {
419 if (traits.supports_direction())
421 if (traits.supports_preset_modes())
423 fill_entity_state_base(fan, msg);
424 return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
428 auto *fan =
static_cast<fan::Fan *
>(entity);
430 auto traits = fan->get_traits();
435 for (
auto const &
preset : traits.supported_preset_modes())
438 fill_entity_info_base(fan, msg);
439 return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
442 ENTITY_COMMAND_MAKE_CALL(
fan::Fan, fan, fan)
444 call.set_state(msg.
state);
461 return this->send_message_smart_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE,
462 LightStateResponse::ESTIMATED_SIZE);
468 auto traits = light->get_traits();
469 auto values = light->remote_values;
470 auto color_mode = values.get_color_mode();
471 resp.
state = values.is_on();
475 resp.
red = values.get_red();
476 resp.
green = values.get_green();
477 resp.
blue = values.get_blue();
478 resp.
white = values.get_white();
482 if (light->supports_effects())
483 resp.
effect = light->get_effect_name();
484 fill_entity_state_base(light, resp);
485 return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
491 auto traits = light->get_traits();
492 for (
auto mode : traits.get_supported_color_modes())
505 if (light->supports_effects()) {
506 msg.
effects.emplace_back(
"None");
507 for (
auto *effect : light->get_effects()) {
508 msg.
effects.push_back(effect->get_name());
512 fill_entity_info_base(light, msg);
513 return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
518 call.set_state(msg.
state);
526 call.set_red(msg.
red);
527 call.set_green(msg.
green);
528 call.set_blue(msg.
blue);
531 call.set_white(msg.
white);
543 call.set_effect(msg.
effect);
550 return this->send_message_smart_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE,
551 SensorStateResponse::ESTIMATED_SIZE);
558 resp.
state = sensor->state;
560 fill_entity_state_base(sensor, resp);
561 return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
576 fill_entity_info_base(sensor, msg);
577 return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
583 return this->send_message_smart_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE,
584 SwitchStateResponse::ESTIMATED_SIZE);
591 resp.
state = a_switch->state;
592 fill_entity_state_base(a_switch, resp);
593 return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
603 fill_entity_info_base(a_switch, msg);
604 return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
612 a_switch->turn_off();
617#ifdef USE_TEXT_SENSOR
619 return this->send_message_smart_(text_sensor, &APIConnection::try_send_text_sensor_state,
620 TextSensorStateResponse::MESSAGE_TYPE, TextSensorStateResponse::ESTIMATED_SIZE);
627 resp.
state = text_sensor->state;
629 fill_entity_state_base(text_sensor, resp);
630 return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
637 msg.
unique_id = text_sensor->unique_id();
640 fill_entity_info_base(text_sensor, msg);
641 return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
647 return this->send_message_smart_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE,
648 ClimateStateResponse::ESTIMATED_SIZE);
654 fill_entity_state_base(climate, resp);
655 auto traits = climate->get_traits();
658 if (traits.get_supports_current_temperature())
660 if (traits.get_supports_two_point_target_temperature()) {
666 if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
668 if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
670 if (traits.get_supports_presets() && climate->preset.has_value()) {
673 if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
675 if (traits.get_supports_swing_modes())
677 if (traits.get_supports_current_humidity())
679 if (traits.get_supports_target_humidity())
681 return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
687 auto traits = climate->get_traits();
692 for (
auto mode : traits.get_supported_modes())
702 for (
auto fan_mode : traits.get_supported_fan_modes())
704 for (
auto const &
custom_fan_mode : traits.get_supported_custom_fan_modes())
706 for (
auto preset : traits.get_supported_presets())
708 for (
auto const &
custom_preset : traits.get_supported_custom_presets())
710 for (
auto swing_mode : traits.get_supported_swing_modes())
713 fill_entity_info_base(climate, msg);
714 return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
744 return this->send_message_smart_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE,
745 NumberStateResponse::ESTIMATED_SIZE);
752 resp.
state = number->state;
754 fill_entity_state_base(number, resp);
755 return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
765 msg.
min_value = number->traits.get_min_value();
766 msg.
max_value = number->traits.get_max_value();
767 msg.
step = number->traits.get_step();
769 fill_entity_info_base(number, msg);
770 return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
774 call.set_value(msg.
state);
779#ifdef USE_DATETIME_DATE
781 return this->send_message_smart_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE,
782 DateStateResponse::ESTIMATED_SIZE);
789 resp.
year = date->year;
790 resp.
month = date->month;
791 resp.
day = date->day;
792 fill_entity_state_base(date, resp);
793 return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
800 fill_entity_info_base(date, msg);
801 return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
810#ifdef USE_DATETIME_TIME
812 return this->send_message_smart_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE,
813 TimeStateResponse::ESTIMATED_SIZE);
820 resp.
hour = time->hour;
821 resp.
minute = time->minute;
822 resp.
second = time->second;
823 fill_entity_state_base(time, resp);
824 return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
831 fill_entity_info_base(time, msg);
832 return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
841#ifdef USE_DATETIME_DATETIME
843 return this->send_message_smart_(datetime, &APIConnection::try_send_datetime_state,
844 DateTimeStateResponse::MESSAGE_TYPE, DateTimeStateResponse::ESTIMATED_SIZE);
851 if (datetime->has_state()) {
855 fill_entity_state_base(datetime, resp);
856 return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
863 fill_entity_info_base(datetime, msg);
864 return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
875 return this->send_message_smart_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE,
876 TextStateResponse::ESTIMATED_SIZE);
881 auto *text =
static_cast<text::Text *
>(entity);
883 resp.
state = text->state;
885 fill_entity_state_base(text, resp);
886 return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
891 auto *text =
static_cast<text::Text *
>(entity);
894 msg.
min_length = text->traits.get_min_length();
895 msg.
max_length = text->traits.get_max_length();
896 msg.
pattern = text->traits.get_pattern();
898 fill_entity_info_base(text, msg);
899 return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
902 ENTITY_COMMAND_MAKE_CALL(
text::Text, text, text)
903 call.set_value(msg.
state);
910 return this->send_message_smart_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE,
911 SelectStateResponse::ESTIMATED_SIZE);
918 resp.
state = select->state;
920 fill_entity_state_base(select, resp);
921 return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
928 for (
const auto &option : select->traits.get_options())
931 fill_entity_info_base(select, msg);
932 return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
936 call.set_option(msg.
state);
948 fill_entity_info_base(button, msg);
949 return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
959 return this->send_message_smart_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE,
960 LockStateResponse::ESTIMATED_SIZE);
965 auto *a_lock =
static_cast<lock::Lock *
>(entity);
968 fill_entity_state_base(a_lock, resp);
969 return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
974 auto *a_lock =
static_cast<lock::Lock *
>(entity);
980 fill_entity_info_base(a_lock, msg);
981 return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
987 case enums::LOCK_UNLOCK:
990 case enums::LOCK_LOCK:
993 case enums::LOCK_OPEN:
1002 return this->send_message_smart_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE,
1003 ValveStateResponse::ESTIMATED_SIZE);
1011 fill_entity_state_base(valve, resp);
1012 return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1018 auto traits = valve->get_traits();
1024 fill_entity_info_base(valve, msg);
1025 return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1032 call.set_command_stop();
1037#ifdef USE_MEDIA_PLAYER
1039 return this->send_message_smart_(media_player, &APIConnection::try_send_media_player_state,
1040 MediaPlayerStateResponse::MESSAGE_TYPE, MediaPlayerStateResponse::ESTIMATED_SIZE);
1048 : media_player->state;
1050 resp.
volume = media_player->volume;
1051 resp.
muted = media_player->is_muted();
1052 fill_entity_state_base(media_player, resp);
1053 return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1059 auto traits = media_player->get_traits();
1061 for (
auto &supported_format : traits.get_supported_formats()) {
1063 media_format.
format = supported_format.format;
1064 media_format.
sample_rate = supported_format.sample_rate;
1065 media_format.
num_channels = supported_format.num_channels;
1067 media_format.
sample_bytes = supported_format.sample_bytes;
1071 fill_entity_info_base(media_player, msg);
1072 return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1080 call.set_volume(msg.
volume);
1093void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
1094 if (!this->flags_.state_subscription)
1096 if (!this->image_reader_)
1098 if (this->image_reader_->available())
1101 this->image_reader_->set_image(std::move(image));
1108 fill_entity_info_base(camera, msg);
1109 return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1120 App.scheduler.set_timeout(this->parent_,
"api_camera_stop_stream", CAMERA_STOP_STREAM,
1126#ifdef USE_HOMEASSISTANT_TIME
1133#ifdef USE_BLUETOOTH_PROXY
1141 if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
1144 service.legacy_data.assign(service.data.begin(), service.data.end());
1145 service.data.clear();
1148 manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
1149 manufacturer_data.data.clear();
1151 return this->send_message(resp);
1153 return this->send_message(msg);
1188 msg.
mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1192#ifdef USE_VOICE_ASSISTANT
1193bool APIConnection::check_voice_assistant_api_connection_()
const {
1204 if (!this->check_voice_assistant_api_connection_()) {
1212 if (msg.
port == 0) {
1218 this->helper_->getpeername((
struct sockaddr *) &storage, &
len);
1223 if (this->check_voice_assistant_api_connection_()) {
1228 if (this->check_voice_assistant_api_connection_()) {
1233 if (this->check_voice_assistant_api_connection_()) {
1239 if (this->check_voice_assistant_api_connection_()) {
1247 if (!this->check_voice_assistant_api_connection_()) {
1252 for (
auto &wake_word : config.available_wake_words) {
1254 resp_wake_word.
id = wake_word.id;
1255 resp_wake_word.
wake_word = wake_word.wake_word;
1256 for (
const auto &lang : wake_word.trained_languages) {
1261 for (
auto &wake_word_id : config.active_wake_words) {
1269 if (this->check_voice_assistant_api_connection_()) {
1276#ifdef USE_ALARM_CONTROL_PANEL
1278 return this->send_message_smart_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
1279 AlarmControlPanelStateResponse::MESSAGE_TYPE,
1280 AlarmControlPanelStateResponse::ESTIMATED_SIZE);
1283 uint32_t remaining_size,
bool is_single) {
1287 fill_entity_state_base(a_alarm_control_panel, resp);
1288 return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1291 uint32_t remaining_size,
bool is_single) {
1295 msg.
requires_code = a_alarm_control_panel->get_requires_code();
1298 fill_entity_info_base(a_alarm_control_panel, msg);
1299 return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size,
1305 case enums::ALARM_CONTROL_PANEL_DISARM:
1308 case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
1311 case enums::ALARM_CONTROL_PANEL_ARM_HOME:
1314 case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
1317 case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
1318 call.arm_vacation();
1320 case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
1321 call.arm_custom_bypass();
1323 case enums::ALARM_CONTROL_PANEL_TRIGGER:
1327 call.set_code(msg.
code);
1333void APIConnection::send_event(
event::Event *event,
const std::string &event_type) {
1334 this->schedule_message_(event,
MessageCreator(event_type), EventResponse::MESSAGE_TYPE,
1335 EventResponse::ESTIMATED_SIZE);
1338 uint32_t remaining_size,
bool is_single) {
1341 fill_entity_state_base(event, resp);
1342 return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1350 for (
const auto &event_type : event->get_event_types())
1353 fill_entity_info_base(event, msg);
1354 return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1360 return this->send_message_smart_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE,
1361 UpdateStateResponse::ESTIMATED_SIZE);
1368 if (update->has_state()) {
1370 if (update->update_info.has_progress) {
1372 resp.
progress = update->update_info.progress;
1376 resp.
title = update->update_info.title;
1378 resp.
release_url = update->update_info.release_url;
1380 fill_entity_state_base(update, resp);
1381 return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1389 fill_entity_info_base(update, msg);
1390 return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1396 case enums::UPDATE_COMMAND_UPDATE:
1399 case enums::UPDATE_COMMAND_CHECK:
1402 case enums::UPDATE_COMMAND_NONE:
1403 ESP_LOGE(TAG,
"UPDATE_COMMAND_NONE not handled; confirm command is correct");
1406 ESP_LOGW(TAG,
"Unknown update command: %" PRIu32, msg.
command);
1412bool APIConnection::try_send_log_message(
int level,
const char *tag,
const char *line,
size_t message_len) {
1413 if (this->flags_.log_subscription < level)
1417 uint32_t msg_size = 0;
1428 auto buffer = this->create_buffer(msg_size);
1431 buffer.encode_uint32(1,
static_cast<uint32_t
>(level));
1432 buffer.encode_string(3, line, message_len);
1435 return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
1440 this->client_peername_ = this->helper_->getpeername();
1441 this->helper_->set_log_info(this->get_client_combined_info());
1444 ESP_LOGV(TAG,
"Hello from client: '%s' | %s | API Version %" PRIu32
".%" PRIu32, this->client_info_.c_str(),
1445 this->client_peername_.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
1453 this->flags_.connection_state =
static_cast<uint8_t
>(ConnectionState::CONNECTED);
1457 bool correct =
true;
1458#ifdef USE_API_PASSWORD
1459 correct = this->parent_->check_password(msg.
password);
1466 ESP_LOGD(TAG,
"%s connected", this->get_client_combined_info().c_str());
1467 this->flags_.connection_state =
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED);
1468#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1469 this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
1471#ifdef USE_HOMEASSISTANT_TIME
1473 this->send_time_request();
1481#ifdef USE_API_PASSWORD
1484 resp.uses_password =
false;
1490 resp.esphome_version = ESPHOME_VERSION;
1492#if defined(USE_ESP8266) || defined(USE_ESP32)
1493 resp.manufacturer =
"Espressif";
1494#elif defined(USE_RP2040)
1495 resp.manufacturer =
"Raspberry Pi";
1496#elif defined(USE_BK72XX)
1497 resp.manufacturer =
"Beken";
1498#elif defined(USE_LN882X)
1499 resp.manufacturer =
"Lightning";
1500#elif defined(USE_RTL87XX)
1501 resp.manufacturer =
"Realtek";
1502#elif defined(USE_HOST)
1503 resp.manufacturer =
"Host";
1505 resp.model = ESPHOME_BOARD;
1506#ifdef USE_DEEP_SLEEP
1509#ifdef ESPHOME_PROJECT_NAME
1510 resp.project_name = ESPHOME_PROJECT_NAME;
1511 resp.project_version = ESPHOME_PROJECT_VERSION;
1514 resp.webserver_port = USE_WEBSERVER_PORT;
1516#ifdef USE_BLUETOOTH_PROXY
1521#ifdef USE_VOICE_ASSISTANT
1526 resp.api_encryption_supported =
true;
1531 device_info.
device_id = device->get_device_id();
1532 device_info.
name = device->get_name();
1533 device_info.
area_id = device->get_area_id();
1534 resp.devices.push_back(device_info);
1540 area_info.
area_id = area->get_area_id();
1541 area_info.
name = area->get_name();
1542 resp.areas.push_back(area_info);
1548 for (
auto &it : this->parent_->get_state_subs()) {
1550 it.callback(msg.
state);
1554#ifdef USE_API_SERVICES
1557 for (
auto *service : this->parent_->get_user_services()) {
1558 if (service->execute_service(msg)) {
1563 ESP_LOGV(TAG,
"Could not find service");
1572 ESP_LOGW(TAG,
"Invalid encryption key length");
1577 if (!this->parent_->save_noise_psk(psk,
true)) {
1578 ESP_LOGW(TAG,
"Failed to save encryption key");
1590bool APIConnection::try_to_clear_buffer(
bool log_out_of_space) {
1591 if (this->flags_.remove)
1593 if (this->helper_->can_write_without_blocking())
1596 APIError err = this->helper_->loop();
1597 if (err != APIError::OK) {
1599 ESP_LOGW(TAG,
"%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
1603 if (this->helper_->can_write_without_blocking())
1605 if (log_out_of_space) {
1606 ESP_LOGV(TAG,
"Cannot send message because of TCP buffer space");
1611 if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) {
1615 APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
1616 if (err == APIError::WOULD_BLOCK)
1618 if (err != APIError::OK) {
1620 if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1621 ESP_LOGW(TAG,
"%s: Connection reset", this->get_client_combined_info().c_str());
1623 ESP_LOGW(TAG,
"%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
1631void APIConnection::on_unauthenticated_access() {
1632 this->on_fatal_error();
1633 ESP_LOGD(TAG,
"%s requested access without authentication", this->get_client_combined_info().c_str());
1635void APIConnection::on_no_setup_connection() {
1636 this->on_fatal_error();
1637 ESP_LOGD(TAG,
"%s requested access without full connection", this->get_client_combined_info().c_str());
1639void APIConnection::on_fatal_error() {
1640 this->helper_->close();
1641 this->flags_.remove =
true;
1645 uint8_t estimated_size) {
1649 for (
auto &item : items) {
1650 if (item.entity == entity && item.message_type == message_type) {
1652 item.creator.cleanup(message_type);
1654 item.creator = std::move(creator);
1660 items.emplace_back(entity, std::move(creator), message_type, estimated_size);
1664 uint8_t estimated_size) {
1666 items.insert(items.begin(),
BatchItem(entity, std::move(creator), message_type, estimated_size));
1669bool APIConnection::schedule_batch_() {
1670 if (!this->flags_.batch_scheduled) {
1671 this->flags_.batch_scheduled =
true;
1677ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) {
return this->create_buffer(size); }
1680 ProtoWriteBuffer result = this->prepare_message_buffer(size, this->flags_.batch_first_message);
1681 this->flags_.batch_first_message =
false;
1685void APIConnection::process_batch_() {
1686 if (this->deferred_batch_.empty()) {
1687 this->flags_.batch_scheduled =
false;
1692 if (!this->try_to_clear_buffer(
true)) {
1697 size_t num_items = this->deferred_batch_.size();
1700 if (num_items == 1) {
1701 const auto &item = this->deferred_batch_[0];
1705 item.creator(item.entity,
this, std::numeric_limits<uint16_t>::max(),
true, item.message_type);
1708 this->send_buffer(
ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
1709#ifdef HAS_PROTO_MESSAGE_DUMP
1712 this->log_batch_item_(item);
1714 this->clear_batch_();
1717 ESP_LOGW(TAG,
"Message too large to send: type=%u", item.message_type);
1718 this->clear_batch_();
1724 std::vector<PacketInfo> packet_info;
1725 packet_info.reserve(num_items);
1728 const uint8_t header_padding = this->helper_->frame_header_padding();
1729 const uint8_t footer_size = this->helper_->frame_footer_size();
1732 this->parent_->get_shared_buffer_ref().clear();
1735 uint32_t total_estimated_size = 0;
1736 for (
size_t i = 0; i < this->deferred_batch_.size(); i++) {
1737 const auto &item = this->deferred_batch_[i];
1738 total_estimated_size += item.estimated_size;
1742 uint32_t total_overhead = (header_padding + footer_size) * num_items;
1745 this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
1746 this->flags_.batch_first_message =
true;
1748 size_t items_processed = 0;
1749 uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
1755 uint32_t current_offset = 0;
1758 for (
size_t i = 0; i < this->deferred_batch_.size(); i++) {
1759 const auto &item = this->deferred_batch_[i];
1762 uint16_t
payload_size = item.creator(item.entity,
this, remaining_size,
false, item.message_type);
1771 uint16_t proto_payload_size =
payload_size - header_padding - footer_size;
1772 packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
1777 if (items_processed == 1) {
1778 remaining_size = MAX_BATCH_PACKET_SIZE;
1783 current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
1786 if (items_processed == 0) {
1787 this->deferred_batch_.clear();
1792 if (footer_size > 0) {
1793 auto &shared_buf = this->parent_->get_shared_buffer_ref();
1794 shared_buf.resize(shared_buf.size() + footer_size);
1799 this->helper_->write_protobuf_packets(
ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
1800 if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1802 if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1803 ESP_LOGW(TAG,
"%s: Connection reset during batch write", this->get_client_combined_info().c_str());
1805 ESP_LOGW(TAG,
"%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
1810#ifdef HAS_PROTO_MESSAGE_DUMP
1813 for (
size_t i = 0; i < items_processed; i++) {
1814 const auto &item = this->deferred_batch_[i];
1815 this->log_batch_item_(item);
1820 if (items_processed < this->deferred_batch_.size()) {
1822 this->deferred_batch_.remove_front(items_processed);
1824 this->schedule_batch_();
1827 this->clear_batch_();
1832 bool is_single, uint8_t message_type)
const {
1835 if (message_type == EventResponse::MESSAGE_TYPE) {
1837 return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
1842 return data_.function_ptr(entity, conn, remaining_size, is_single);
1848 return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1854 return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1860 return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
BedjetMode mode
BedJet operating mode.
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
const std::vector< Area * > & get_areas()
std::string get_compilation_time() const
const char * get_area() const
Get the area of this Application set by pre_setup().
const std::string & get_name() const
Get the name of this Application set by pre_setup().
const std::vector< Device * > & get_devices()
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
std::string get_object_id() const
struct esphome::api::APIConnection::APIFlags flags_
std::unique_ptr< APIFrameHelper > helper_
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size)
APIConnection(std::unique_ptr< socket::Socket > socket, APIServer *parent)
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size)
void button_command(const ButtonCommandRequest &msg) override
void log_send_message_(const char *name, const std::string &dump)
std::shared_ptr< APINoiseContext > get_noise_ctx()
std::vector< uint8_t > & get_shared_buffer_ref()
enums::AlarmControlPanelStateCommand command
enums::AlarmControlPanelState state
std::vector< BluetoothServiceData > service_data
std::vector< BluetoothServiceData > manufacturer_data
enums::BluetoothScannerMode mode
bool has_target_temperature_high
float target_temperature_low
bool has_target_temperature_low
float target_temperature_high
enums::ClimateSwingMode swing_mode
enums::ClimateFanMode fan_mode
bool has_target_temperature
std::string custom_fan_mode
enums::ClimatePreset preset
std::string custom_preset
enums::ClimateFanMode fan_mode
float target_temperature_low
enums::ClimateSwingMode swing_mode
std::string custom_fan_mode
enums::ClimateAction action
enums::ClimatePreset preset
std::string custom_preset
float current_temperature
float target_temperature_high
enums::LegacyCoverCommand legacy_command
enums::LegacyCoverState legacy_state
enums::CoverOperation current_operation
enums::FanDirection direction
enums::FanDirection direction
uint32_t api_version_major
uint32_t api_version_minor
uint32_t api_version_minor
uint32_t api_version_major
bool has_color_temperature
enums::ColorMode color_mode
bool has_transition_length
uint32_t transition_length
bool has_color_brightness
enums::ColorMode color_mode
bool requires_code_to_arm
uint32_t supported_features
bool is_status_binary_sensor
std::vector< enums::ClimatePreset > supported_presets
std::vector< enums::ClimateSwingMode > supported_swing_modes
float visual_max_humidity
std::vector< enums::ClimateFanMode > supported_fan_modes
bool supports_current_temperature
bool supports_current_humidity
std::vector< enums::ClimateMode > supported_modes
bool supports_target_humidity
float visual_min_humidity
float visual_max_temperature
float visual_target_temperature_step
bool supports_two_point_target_temperature
float visual_min_temperature
float visual_current_temperature_step
bool legacy_supports_away
std::vector< std::string > supported_custom_presets
std::vector< std::string > supported_custom_fan_modes
std::vector< std::string > event_types
std::vector< std::string > supported_preset_modes
int32_t supported_speed_count
bool supports_oscillation
bool legacy_supports_color_temperature
bool legacy_supports_white_value
bool legacy_supports_brightness
std::vector< std::string > effects
std::vector< enums::ColorMode > supported_color_modes
std::string unit_of_measurement
std::vector< std::string > options
int32_t accuracy_decimals
std::string unit_of_measurement
enums::SensorStateClass state_class
enums::LockCommand command
virtual void encode(ProtoWriteBuffer buffer) const
virtual const char * message_name() const
virtual void calculate_size(uint32_t &total_size) const
static uint32_t varint(uint32_t value)
ProtoSize class for Protocol Buffer serialization size calculation.
enums::UpdateCommand command
std::string latest_version
std::string current_version
std::string release_summary
enums::ValveOperation current_operation
std::vector< VoiceAssistantWakeWord > available_wake_words
uint32_t max_active_wake_words
std::vector< std::string > active_wake_words
std::vector< std::string > active_wake_words
std::vector< std::string > trained_languages
Base class for all binary_sensor-type classes.
void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg)
int get_bluetooth_connections_limit()
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg)
void bluetooth_device_request(const api::BluetoothDeviceRequest &msg)
void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg)
void bluetooth_scanner_set_mode(bool active)
void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags)
uint32_t get_legacy_version() const
uint32_t get_feature_flags() const
void unsubscribe_api_connection(api::APIConnection *api_connection)
int get_bluetooth_connections_free()
void bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg)
void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg)
void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg)
std::string get_bluetooth_mac_address_pretty()
Abstract camera base class.
virtual CameraImageReader * create_image_reader()=0
Returns a new camera image reader that keeps track of the JPEG data in the camera image.
virtual void start_stream(CameraRequester requester)=0
virtual void stop_stream(CameraRequester requester)=0
virtual void request_image(CameraRequester requester)=0
static Camera * instance()
The singleton instance of the camera implementation.
ClimateDevice - This is the base class for all climate integrations.
Base class for all cover devices.
void set_epoch_time(uint32_t epoch)
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Base class for all locks.
Base-class for all numbers.
Base-class for all selects.
Base-class for all sensors.
Base class for all switches.
Base-class for all text inputs.
Base class for all valve devices.
const Configuration & get_configuration()
uint32_t get_legacy_version() const
void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg)
void on_audio(const api::VoiceAssistantAudio &msg)
void client_subscription(api::APIConnection *client, bool subscribe)
void on_event(const api::VoiceAssistantEventResponse &msg)
void on_announce(const api::VoiceAssistantAnnounceRequest &msg)
api::APIConnection * get_api_connection() const
uint32_t get_feature_flags() const
void on_set_configuration(const std::vector< std::string > &active_wake_words)
ClimateSwingMode swing_mode
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity)
std::array< uint8_t, 32 > psk_t
const char * api_error_to_str(APIError err)
BluetoothProxy * global_bluetooth_proxy
ClimatePreset
Enum for all preset modes.
@ CLIMATE_PRESET_AWAY
Device is in away preset.
ClimateSwingMode
Enum for all modes a climate swing can be in.
ClimateMode
Enum for all modes a climate device can be in.
bool global_has_deep_sleep
FanDirection
Simple enum to represent the direction of a fan.
HomeassistantTime * global_homeassistant_time
ColorMode
Color modes are a combination of color capabilities that can be used at the same time.
@ BRIGHTNESS
Master brightness of the light can be controlled.
@ RGB
Color can be controlled using RGB format (includes a brightness control for the color).
@ COLOR_TEMPERATURE
Color temperature can be controlled.
@ WHITE
Brightness of white channel can be controlled separately from other channels.
@ COLD_WARM_WHITE
Brightness of cold and warm white output can be controlled.
@ UPDATE_STATE_INSTALLING
VoiceAssistant * global_voice_assistant
Providing packet encoding functions for exchanging data with a remote host.
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
void IRAM_ATTR HOT delay(uint32_t ms)
Application App
Global storage of Application pointer - only one Application can exist.
size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len)
A more user-friendly version of struct tm from time.h.
std::vector< uint8_t > container