2#if defined(USE_LOGGER) && defined(USE_CRASH_HANDLER)
10#ifdef USE_API_PLAINTEXT
13#ifdef USE_API_USER_DEFINED_ACTIONS
34#ifdef USE_PROVISIONING
41#ifdef USE_HOMEASSISTANT_TIME
44#ifdef USE_BLUETOOTH_PROXY
50#ifdef USE_VOICE_ASSISTANT
56#if defined(USE_SENDSPIN) && defined(USE_ESP_IDF)
59#ifdef USE_WATER_HEATER
65#ifdef USE_RADIO_FREQUENCY
75static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 10;
76static constexpr uint8_t MAX_PING_RETRIES = 60;
77static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
78static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
87static constexpr uint32_t HANDSHAKE_TIMEOUT_MS = 60000;
90static_assert(MAC_ADDRESS_PRETTY_BUFFER_SIZE - 1 == 17,
91 "Update max_data_length for mac_address/bluetooth_mac_address in api.proto");
93static_assert(
sizeof(ESPHOME_VERSION) - 1 <= 32,
"Update max_data_length for esphome_version in api.proto");
94static_assert(ESPHOME_DEVICE_NAME_MAX_LEN <= 31,
"Update max_data_length for name in api.proto");
95static_assert(ESPHOME_FRIENDLY_NAME_MAX_LEN <= 120,
"Update max_data_length for friendly_name in api.proto");
99#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
101 esp_log_printf_(ESPHOME_LOG_LEVEL_WARN, tag, line, ESPHOME_LOG_FORMAT(
"%s dropped, TCP buffer full"),
106static const int CAMERA_STOP_STREAM = 5000;
112#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
113 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
114 if ((entity_var) == nullptr) \
116 auto call = (entity_var)->make_call();
120#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
121 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
122 if ((entity_var) == nullptr) \
127#define ENTITY_COMMAND_LOOKUP(entity_type, entity_var, getter_name) \
128 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id)
133#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
134 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
135 if ((entity_var) == nullptr) \
137 auto call = (entity_var)->make_call();
141#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
142 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
143 if ((entity_var) == nullptr) \
148#define ENTITY_COMMAND_LOOKUP(entity_type, entity_var, getter_name) \
149 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key)
154#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
156 if (noise_ctx.has_psk()) {
157 this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
161#elif defined(USE_API_PLAINTEXT)
162 this->helper_ = std::unique_ptr<APIPlaintextFrameHelper>{
new APIPlaintextFrameHelper(std::move(sock))};
163#elif defined(USE_API_NOISE)
165 std::unique_ptr<APINoiseFrameHelper>{
new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
167#error "No frame helper defined"
171void APIConnection::start() {
174 APIError err = this->helper_->init();
175 if (err != APIError::OK) {
176 this->fatal_error_with_log_(LOG_STR(
"Helper init failed"), err);
180 char peername[socket::SOCKADDR_STR_LEN];
181 this->helper_->set_client_name(this->helper_->get_peername_to(peername), strlen(peername));
184APIConnection::~APIConnection() {
185 this->destroy_active_iterator_();
186#ifdef USE_BLUETOOTH_PROXY
191#ifdef USE_VOICE_ASSISTANT
196#ifdef USE_ZWAVE_PROXY
201#ifdef USE_SERIAL_PROXY
203 if (proxy->get_api_connection() ==
this) {
204 proxy->serial_proxy_request(
this, enums::SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE);
210 for (
auto *infrared :
App.get_infrareds()) {
211 infrared->on_api_connection_closed(
this);
214#ifdef USE_RADIO_FREQUENCY
215 for (
auto *radio_frequency :
App.get_radio_frequencies()) {
216 radio_frequency->on_api_connection_closed(
this);
221#if defined(USE_API_NOISE) && defined(USE_API_PLAINTEXT)
222void APIConnection::upgrade_helper_to_noise_() {
232 auto *noise =
new APINoiseFrameHelper(plaintext->release_socket_for_switch(), this->parent_->get_noise_ctx());
234 const char *name = plaintext->get_client_name();
235 noise->set_client_name(name, strlen(name));
236 this->helper_.reset(noise);
237 APIError err = noise->init_from_handoff(header, header_len);
238 if (err != APIError::OK) {
239 this->fatal_error_with_log_(LOG_STR(
"Noise handoff failed"), err);
244void APIConnection::destroy_active_iterator_() {
245 switch (this->active_iterator_) {
246 case ActiveIterator::LIST_ENTITIES:
247 this->iterator_storage_.list_entities.~ListEntitiesIterator();
249 case ActiveIterator::INITIAL_STATE:
250 this->iterator_storage_.initial_state.~InitialStateIterator();
252 case ActiveIterator::NONE:
255 this->active_iterator_ = ActiveIterator::NONE;
259 this->destroy_active_iterator_();
260 this->active_iterator_ =
type;
261 if (
type == ActiveIterator::LIST_ENTITIES) {
263 this->iterator_storage_.list_entities.
begin();
266 this->iterator_storage_.initial_state.
begin();
270void APIConnection::loop() {
271 if (this->flags_.next_close) {
274 this->flags_.remove =
true;
278 APIError err = this->helper_->loop();
279 if (err != APIError::OK) {
280 this->fatal_error_with_log_(LOG_STR(
"Socket operation failed"), err);
291 if (this->helper_->is_socket_ready() || this->flags_.may_have_remaining_data) {
292 this->flags_.may_have_remaining_data =
false;
294 uint8_t message_count = 0;
295 for (; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
297 err = this->helper_->read_packet(&
buffer);
298 if (err == APIError::WOULD_BLOCK) {
301 }
else if (err != APIError::OK) {
302#if defined(USE_API_NOISE) && defined(USE_API_PLAINTEXT)
306 if (err == APIError::PROTOCOL_SWITCH_TO_NOISE) {
307 this->upgrade_helper_to_noise_();
311 this->fatal_error_with_log_(LOG_STR(
"Reading failed"), err);
317 if (this->is_authenticated()) {
318 this->last_traffic_ = now;
322 if (this->flags_.remove)
328 if (message_count == MAX_MESSAGES_PER_LOOP) {
329 this->flags_.may_have_remaining_data =
true;
334 if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
335 this->process_batch_();
338 if (this->active_iterator_ != ActiveIterator::NONE) {
339 this->process_active_iterator_();
345 if (!this->is_authenticated() && now - this->last_traffic_ > HANDSHAKE_TIMEOUT_MS) {
346 this->on_fatal_error();
347 this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR(
"handshake timeout; disconnecting"));
354 if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
355 this->check_keepalive_(now);
358#ifdef USE_API_HOMEASSISTANT_STATES
359 if (state_subs_at_ >= 0) {
360 this->process_state_subscriptions_();
367 this->try_send_camera_image_();
371void APIConnection::check_keepalive_(
uint32_t now) {
373 if (this->flags_.sent_ping) {
375 if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
377 this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR(
"is unresponsive; disconnecting"));
379 }
else if (!this->flags_.remove) {
381 ESP_LOGVV(TAG,
"Sending keepalive PING");
383 this->flags_.sent_ping = this->send_message(req);
384 if (this->flags_.sent_ping) {
386 this->helper_->release_overflow_buffer();
390 ESP_LOGW(TAG,
"Buffer full, ping queued");
391 this->schedule_message_front_(
nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
392 this->flags_.sent_ping =
true;
397void APIConnection::process_active_iterator_() {
399 if (this->active_iterator_ == ActiveIterator::LIST_ENTITIES) {
400 if (this->iterator_storage_.list_entities.completed()) {
401 this->destroy_active_iterator_();
402 if (this->flags_.state_subscription) {
403 this->begin_iterator_(ActiveIterator::INITIAL_STATE);
405 this->finalize_iterator_sync_();
408 this->process_iterator_batch_(this->iterator_storage_.list_entities);
411 if (this->iterator_storage_.initial_state.completed()) {
412 this->destroy_active_iterator_();
413 this->finalize_iterator_sync_();
415 this->process_iterator_batch_(this->iterator_storage_.initial_state);
420void APIConnection::finalize_iterator_sync_() {
424 if (!this->deferred_batch_.empty()) {
425 this->process_batch_();
428 this->flags_.should_try_send_immediately =
true;
430 this->deferred_batch_.release_buffer();
431 this->helper_->release_buffers();
437 size_t batch_size = this->deferred_batch_.size();
438 if (batch_size < MAX_INITIAL_BATCH_SIZE)
439 iterator.
try_advance(MAX_INITIAL_BATCH_SIZE - batch_size);
443 if (this->deferred_batch_.size() >= MAX_INITIAL_BATCH_SIZE) {
444 this->process_batch_();
448bool APIConnection::send_disconnect_response_() {
452 this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR(
"disconnected"));
453 this->flags_.next_close =
true;
455 return this->send_message(resp);
458 this->flags_.log_subscription = msg.
level;
461#if defined(USE_LOGGER) && defined(USE_CRASH_HANDLER)
466void APIConnection::on_disconnect_response() {
469 this->flags_.remove =
true;
479 return encode_to_buffer(size_fn(&msg), encode_fn, &msg, conn, remaining_size);
493#ifdef USE_ENTITY_ICON
501 return encode_to_buffer_slow(size_fn(&msg), encode_fn, &msg, conn, remaining_size);
510 return fill_and_encode_entity_info(entity, msg, size_fn, encode_fn, conn, remaining_size);
513#ifdef USE_BINARY_SENSOR
515 return this->send_message_smart_(binary_sensor, BinarySensorStateResponse::MESSAGE_TYPE,
516 BinarySensorStateResponse::ESTIMATED_SIZE);
522 resp.
state = binary_sensor->state;
524 return fill_and_encode_entity_state(binary_sensor, resp, conn, remaining_size);
531 return fill_and_encode_entity_info_with_device_class(binary_sensor, msg, msg.
device_class, conn, remaining_size);
537 return this->send_message_smart_(cover, CoverStateResponse::MESSAGE_TYPE, CoverStateResponse::ESTIMATED_SIZE);
542 auto traits = cover->get_traits();
544 if (traits.get_supports_tilt())
545 msg.
tilt = cover->tilt;
547 return fill_and_encode_entity_state(cover, msg, conn, remaining_size);
552 auto traits = cover->get_traits();
559 return fill_and_encode_entity_info_with_device_class(cover, msg, msg.
device_class, conn, remaining_size);
566 call.set_tilt(msg.
tilt);
568 call.set_command_stop();
570 call.set_command_stop_tilt();
572 call.set_command_toggle();
579 return this->send_message_smart_(fan, FanStateResponse::MESSAGE_TYPE, FanStateResponse::ESTIMATED_SIZE);
582 auto *fan =
static_cast<fan::Fan *
>(entity);
584 auto traits = fan->get_traits();
585 msg.
state = fan->state;
586 if (traits.supports_oscillation())
588 if (traits.supports_speed()) {
591 if (traits.supports_direction())
593 if (traits.supports_preset_modes() && fan->has_preset_mode())
595 return fill_and_encode_entity_state(fan, msg, conn, remaining_size);
598 auto *fan =
static_cast<fan::Fan *
>(entity);
600 auto traits = fan->get_traits();
606 return fill_and_encode_entity_info(fan, msg, conn, remaining_size);
609 ENTITY_COMMAND_MAKE_CALL(
fan::Fan, fan, fan)
611 call.set_state(msg.
state);
628 return this->send_message_smart_(light, LightStateResponse::MESSAGE_TYPE, LightStateResponse::ESTIMATED_SIZE);
633 auto values = light->get_reported_values();
634 auto color_mode = values.get_color_mode();
635 resp.
state = values.is_on();
639 resp.
red = values.get_red();
640 resp.
green = values.get_green();
641 resp.
blue = values.get_blue();
642 resp.
white = values.get_white();
646 if (light->supports_effects()) {
647 resp.
effect = light->get_effect_name();
649 return fill_and_encode_entity_state(light, resp, conn, remaining_size);
654 auto traits = light->get_traits();
655 auto supported_modes = traits.get_supported_color_modes();
665 return fill_and_encode_entity_info(light, msg, conn, remaining_size);
670 call.set_state(msg.
state);
678 call.set_red(msg.
red);
679 call.set_green(msg.
green);
680 call.set_blue(msg.
blue);
683 call.set_white(msg.
white);
702 return this->send_message_smart_(sensor, SensorStateResponse::MESSAGE_TYPE, SensorStateResponse::ESTIMATED_SIZE);
708 resp.
state = sensor->state;
710 return fill_and_encode_entity_state(sensor, resp, conn, remaining_size);
720 return fill_and_encode_entity_info_with_device_class(sensor, msg, msg.
device_class, conn, remaining_size);
726 return this->send_message_smart_(a_switch, SwitchStateResponse::MESSAGE_TYPE, SwitchStateResponse::ESTIMATED_SIZE);
732 resp.
state = a_switch->state;
734 return fill_and_encode_entity_state(a_switch, resp, conn, remaining_size);
741 return fill_and_encode_entity_info_with_device_class(a_switch, msg, msg.
device_class, conn, remaining_size);
745 a_switch->control(msg.
state);
749#ifdef USE_TEXT_SENSOR
751 return this->send_message_smart_(text_sensor, TextSensorStateResponse::MESSAGE_TYPE,
752 TextSensorStateResponse::ESTIMATED_SIZE);
760 return fill_and_encode_entity_state(text_sensor, resp, conn, remaining_size);
765 return fill_and_encode_entity_info_with_device_class(text_sensor, msg, msg.
device_class, conn, remaining_size);
771 return this->send_message_smart_(climate, ClimateStateResponse::MESSAGE_TYPE, ClimateStateResponse::ESTIMATED_SIZE);
776 auto traits = climate->get_traits();
789 if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
791 if (!traits.get_supported_custom_fan_modes().empty() && climate->has_custom_fan_mode()) {
794 if (traits.get_supports_presets() && climate->preset.has_value()) {
797 if (!traits.get_supported_custom_presets().empty() && climate->has_custom_preset()) {
800 if (traits.get_supports_swing_modes())
806 return fill_and_encode_entity_state(climate, resp, conn, remaining_size);
811 auto traits = climate->get_traits();
834 return fill_and_encode_entity_info(climate, msg, conn, remaining_size);
864 return this->send_message_smart_(number, NumberStateResponse::MESSAGE_TYPE, NumberStateResponse::ESTIMATED_SIZE);
870 resp.
state = number->state;
872 return fill_and_encode_entity_state(number, resp, conn, remaining_size);
880 msg.
min_value = number->traits.get_min_value();
881 msg.
max_value = number->traits.get_max_value();
882 msg.
step = number->traits.get_step();
883 return fill_and_encode_entity_info_with_device_class(number, msg, msg.
device_class, conn, remaining_size);
887 call.set_value(msg.
state);
892#ifdef USE_DATETIME_DATE
894 return this->send_message_smart_(date, DateStateResponse::MESSAGE_TYPE, DateStateResponse::ESTIMATED_SIZE);
900 resp.
year = date->year;
901 resp.
month = date->month;
902 resp.
day = date->day;
903 return fill_and_encode_entity_state(date, resp, conn, remaining_size);
908 return fill_and_encode_entity_info(date, msg, conn, remaining_size);
917#ifdef USE_DATETIME_TIME
919 return this->send_message_smart_(time, TimeStateResponse::MESSAGE_TYPE, TimeStateResponse::ESTIMATED_SIZE);
925 resp.
hour = time->hour;
926 resp.
minute = time->minute;
927 resp.
second = time->second;
928 return fill_and_encode_entity_state(time, resp, conn, remaining_size);
933 return fill_and_encode_entity_info(time, msg, conn, remaining_size);
942#ifdef USE_DATETIME_DATETIME
944 return this->send_message_smart_(datetime, DateTimeStateResponse::MESSAGE_TYPE,
945 DateTimeStateResponse::ESTIMATED_SIZE);
951 if (datetime->has_state()) {
955 return fill_and_encode_entity_state(datetime, resp, conn, remaining_size);
960 return fill_and_encode_entity_info(datetime, msg, conn, remaining_size);
971 return this->send_message_smart_(text, TextStateResponse::MESSAGE_TYPE, TextStateResponse::ESTIMATED_SIZE);
975 auto *text =
static_cast<text::Text *
>(entity);
979 return fill_and_encode_entity_state(text, resp, conn, remaining_size);
983 auto *text =
static_cast<text::Text *
>(entity);
986 msg.
min_length = text->traits.get_min_length();
987 msg.
max_length = text->traits.get_max_length();
988 msg.
pattern = text->traits.get_pattern_ref();
989 return fill_and_encode_entity_info(text, msg, conn, remaining_size);
992 ENTITY_COMMAND_MAKE_CALL(
text::Text, text, text)
1000 return this->send_message_smart_(select, SelectStateResponse::MESSAGE_TYPE, SelectStateResponse::ESTIMATED_SIZE);
1006 resp.
state = select->current_option();
1008 return fill_and_encode_entity_state(select, resp, conn, remaining_size);
1014 const auto &opts = select->traits.get_options();
1015 const std::span<const char *const>
options(opts.data(), opts.size());
1017 return fill_and_encode_entity_info(select, msg, conn, remaining_size);
1030 return fill_and_encode_entity_info_with_device_class(button, msg, msg.
device_class, conn, remaining_size);
1040 return this->send_message_smart_(a_lock, LockStateResponse::MESSAGE_TYPE, LockStateResponse::ESTIMATED_SIZE);
1044 auto *a_lock =
static_cast<lock::Lock *
>(entity);
1047 return fill_and_encode_entity_state(a_lock, resp, conn, remaining_size);
1051 auto *a_lock =
static_cast<lock::Lock *
>(entity);
1056 return fill_and_encode_entity_info(a_lock, msg, conn, remaining_size);
1062 case enums::LOCK_UNLOCK:
1065 case enums::LOCK_LOCK:
1068 case enums::LOCK_OPEN:
1077 return this->send_message_smart_(valve, ValveStateResponse::MESSAGE_TYPE, ValveStateResponse::ESTIMATED_SIZE);
1084 return fill_and_encode_entity_state(valve, resp, conn, remaining_size);
1089 auto traits = valve->get_traits();
1093 return fill_and_encode_entity_info_with_device_class(valve, msg, msg.
device_class, conn, remaining_size);
1100 call.set_command_stop();
1105#ifdef USE_MEDIA_PLAYER
1107 return this->send_message_smart_(media_player, MediaPlayerStateResponse::MESSAGE_TYPE,
1108 MediaPlayerStateResponse::ESTIMATED_SIZE);
1115 : media_player->state;
1117 resp.
volume = media_player->volume;
1118 resp.
muted = media_player->is_muted();
1119 return fill_and_encode_entity_state(media_player, resp, conn, remaining_size);
1124 auto traits = media_player->get_traits();
1126 for (
auto &supported_format : traits.get_supported_formats()) {
1129 media_format.format =
StringRef(supported_format.format);
1130 media_format.sample_rate = supported_format.sample_rate;
1131 media_format.num_channels = supported_format.num_channels;
1133 media_format.sample_bytes = supported_format.sample_bytes;
1134 media_format.bitrate = supported_format.bitrate;
1136 return fill_and_encode_entity_info(media_player, msg, conn, remaining_size);
1144 call.set_volume(msg.
volume);
1157void APIConnection::try_send_camera_image_() {
1158 if (!this->image_reader_)
1163 while (this->image_reader_->available()) {
1164 if (!this->helper_->can_write_without_blocking())
1167 uint32_t to_send = std::min((
size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
1168 bool done = this->image_reader_->available() == to_send;
1171 msg.
key = cam->get_object_id_hash();
1172 msg.
set_data(this->image_reader_->peek_data_buffer(), to_send);
1178 if (!this->send_message(msg)) {
1181 this->image_reader_->consume_data(to_send);
1183 this->image_reader_->return_image();
1188void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
1189 if (!this->flags_.state_subscription)
1191 if (this->image_reader_ && this->image_reader_->available())
1195 if (!this->image_reader_) {
1201 this->image_reader_->set_image(std::move(image));
1203 this->try_send_camera_image_();
1208 return fill_and_encode_entity_info(camera, msg, conn, remaining_size);
1219 App.
scheduler.set_timeout(this->parent_,
"api_camera_stop_stream", CAMERA_STOP_STREAM,
1225#ifdef USE_HOMEASSISTANT_TIME
1229#if defined(USE_HOMEASSISTANT_TIMEZONE) && defined(USE_TIME_TIMEZONE)
1234 if (value.has_parsed_timezone) {
1235 const auto &pt = value.parsed_timezone;
1240 tz.
dst_start.
day =
static_cast<uint16_t
>(pt.dst_start.day);
1243 tz.
dst_start.
week =
static_cast<uint8_t
>(pt.dst_start.week);
1246 tz.
dst_end.
day =
static_cast<uint16_t
>(pt.dst_end.day);
1248 tz.
dst_end.
month =
static_cast<uint8_t
>(pt.dst_end.month);
1249 tz.
dst_end.
week =
static_cast<uint8_t
>(pt.dst_end.week);
1258#ifdef USE_BLUETOOTH_PROXY
1259void APIConnection::on_subscribe_bluetooth_le_advertisements_request(
1263void APIConnection::on_unsubscribe_bluetooth_le_advertisements_request() {
1266#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
1290bool APIConnection::send_subscribe_bluetooth_connections_free_response_() {
1294void APIConnection::on_subscribe_bluetooth_connections_free_request() {
1295 if (!this->send_subscribe_bluetooth_connections_free_response_()) {
1296 this->on_fatal_error();
1307 msg.
mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1311#ifdef USE_VOICE_ASSISTANT
1312bool APIConnection::check_voice_assistant_api_connection_()
const {
1323 if (!this->check_voice_assistant_api_connection_()) {
1331 if (msg.
port == 0) {
1337 this->helper_->getpeername((
struct sockaddr *) &storage, &
len);
1342 if (this->check_voice_assistant_api_connection_()) {
1347 if (this->check_voice_assistant_api_connection_()) {
1352 if (this->check_voice_assistant_api_connection_()) {
1358 if (this->check_voice_assistant_api_connection_()) {
1363bool APIConnection::send_voice_assistant_get_configuration_response_(
1366 if (!this->check_voice_assistant_api_connection_()) {
1368 const std::vector<std::string> empty_wake_words;
1370 return this->send_message(resp);
1374 for (
auto &wake_word : config.available_wake_words) {
1377 resp_wake_word.id =
StringRef(wake_word.id);
1378 resp_wake_word.wake_word =
StringRef(wake_word.wake_word);
1379 for (
const auto &lang : wake_word.trained_languages) {
1380 resp_wake_word.trained_languages.push_back(lang);
1386 return this->send_message(resp);
1389 if (!this->send_voice_assistant_get_configuration_response_(msg)) {
1390 this->on_fatal_error();
1395 if (this->check_voice_assistant_api_connection_()) {
1401#ifdef USE_ZWAVE_PROXY
1410 if (!this->send_message(resp)) {
1411 API_LOG_MSG_DROPPED(TAG,
"Z-Wave proxy response");
1416#if defined(USE_SENDSPIN) && defined(USE_ESP_IDF)
1417bool APIConnection::send_sendspin_pairing_token_response_() {
1422 if (!this->parent_->get_noise_ctx().has_psk()) {
1423 resp.
status = enums::SENDSPIN_PAIRING_TOKEN_STATUS_ENCRYPTION_REQUIRED;
1424 return this->send_message(resp);
1427 if (token.has_value()) {
1428 resp.
status = enums::SENDSPIN_PAIRING_TOKEN_STATUS_OK;
1431 resp.
status = enums::SENDSPIN_PAIRING_TOKEN_STATUS_FAILED;
1433 resp.
status = enums::SENDSPIN_PAIRING_TOKEN_STATUS_DISABLED;
1435 resp.
status = enums::SENDSPIN_PAIRING_TOKEN_STATUS_NOT_READY;
1438 resp.
status = enums::SENDSPIN_PAIRING_TOKEN_STATUS_ENCRYPTION_REQUIRED;
1440 return this->send_message(resp);
1443void APIConnection::on_sendspin_pairing_token_request() {
1444 if (!this->send_sendspin_pairing_token_response_()) {
1445 this->on_fatal_error();
1450#ifdef USE_ALARM_CONTROL_PANEL
1452 return this->send_message_smart_(a_alarm_control_panel, AlarmControlPanelStateResponse::MESSAGE_TYPE,
1453 AlarmControlPanelStateResponse::ESTIMATED_SIZE);
1460 return fill_and_encode_entity_state(a_alarm_control_panel, resp, conn, remaining_size);
1467 msg.
requires_code = a_alarm_control_panel->get_requires_code();
1469 return fill_and_encode_entity_info(a_alarm_control_panel, msg, conn, remaining_size);
1474 case enums::ALARM_CONTROL_PANEL_DISARM:
1477 case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
1480 case enums::ALARM_CONTROL_PANEL_ARM_HOME:
1483 case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
1486 case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
1487 call.arm_vacation();
1489 case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
1490 call.arm_custom_bypass();
1492 case enums::ALARM_CONTROL_PANEL_TRIGGER:
1501#ifdef USE_WATER_HEATER
1503 return this->send_message_smart_(water_heater, WaterHeaterStateResponse::MESSAGE_TYPE,
1504 WaterHeaterStateResponse::ESTIMATED_SIZE);
1515 resp.
state = wh->get_state();
1517 return fill_and_encode_entity_state(wh, resp, conn, remaining_size);
1522 auto traits = wh->get_traits();
1529 return fill_and_encode_entity_info(wh, msg, conn, remaining_size);
1534 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_MODE)
1536 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE)
1538 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_LOW)
1540 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH)
1542 if ((msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_AWAY_STATE) ||
1543 (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_STATE)) {
1546 if ((msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_ON_STATE) ||
1547 (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_STATE)) {
1558 this->send_message_smart_(event, EventResponse::MESSAGE_TYPE, EventResponse::ESTIMATED_SIZE,
1565 return fill_and_encode_entity_state(event, resp, conn, remaining_size);
1572 return fill_and_encode_entity_info_with_device_class(event, msg, msg.
device_class, conn, remaining_size);
1579 const bool want_reply = this->client_supports_api_version(1, 18);
1584 if (infrared !=
nullptr) {
1585 auto call = infrared->make_call();
1589 call.set_api_connection(want_reply ?
this :
nullptr);
1594#ifdef USE_RADIO_FREQUENCY
1596 if (radio_frequency !=
nullptr) {
1597 auto call = radio_frequency->make_call();
1602 call.set_api_connection(want_reply ?
this :
nullptr);
1607 ESP_LOGW(TAG,
"IR/RF transmit for unknown key %" PRIu32, msg.
key);
1615 if (!this->send_infrared_rf_transmit_complete(device_id, msg.
key,
false)) {
1616 API_LOG_MSG_DROPPED(TAG,
"IR/RF reply");
1628 resp.success = success;
1629 return this->send_message(resp);
1635 if (!this->send_message(msg)) {
1638 ESP_LOGV(TAG,
"IR/RF event dropped, TCP buffer full");
1644#ifdef USE_SERIAL_PROXY
1648 return enums::SERIAL_PROXY_STATUS_OK;
1650 return enums::SERIAL_PROXY_STATUS_ASSUMED_SUCCESS;
1652 return enums::SERIAL_PROXY_STATUS_PORT_IN_USE;
1654 return enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT;
1656 return enums::SERIAL_PROXY_STATUS_TIMEOUT;
1658 return enums::SERIAL_PROXY_STATUS_NOT_SUPPORTED;
1660 return enums::SERIAL_PROXY_STATUS_ERROR;
1662 return enums::SERIAL_PROXY_STATUS_ERROR;
1665static void send_serial_proxy_ack(APIConnection *conn,
uint32_t instance, enums::SerialProxyRequestType
type,
1666 enums::SerialProxyStatus
status) {
1667 SerialProxyRequestResponse resp{};
1668 resp.instance = instance;
1671 if (!conn->send_message(resp)) {
1672 API_LOG_MSG_DROPPED(TAG,
"Serial proxy response");
1678 if (msg.
instance >= proxies.size()) {
1679 ESP_LOGW(TAG,
"Serial proxy instance %" PRIu32
" out of range (max %" PRIu32
")", msg.
instance,
1680 static_cast<uint32_t>(proxies.size()));
1681 send_serial_proxy_ack(
this, msg.
instance, enums::SERIAL_PROXY_REQUEST_TYPE_CONFIGURE,
1682 enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT);
1687 send_serial_proxy_ack(
this, msg.
instance, enums::SERIAL_PROXY_REQUEST_TYPE_CONFIGURE,
1688 serial_proxy_result_to_status(result));
1693 if (msg.
instance >= proxies.size()) {
1694 ESP_LOGW(TAG,
"Serial proxy instance %" PRIu32
" out of range", msg.
instance);
1702 if (msg.
instance >= proxies.size()) {
1703 ESP_LOGW(TAG,
"Serial proxy instance %" PRIu32
" out of range", msg.
instance);
1704 send_serial_proxy_ack(
this, msg.
instance, enums::SERIAL_PROXY_REQUEST_TYPE_SET_MODEM_PINS,
1705 enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT);
1709 send_serial_proxy_ack(
this, msg.
instance, enums::SERIAL_PROXY_REQUEST_TYPE_SET_MODEM_PINS,
1710 serial_proxy_result_to_status(result));
1717 if (msg.
instance >= proxies.size()) {
1718 ESP_LOGW(TAG,
"Serial proxy instance %" PRIu32
" out of range", msg.
instance);
1721 if (!this->client_supports_api_version(1, 16)) {
1724 resp.status = enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT;
1726 resp.line_states = proxies[msg.
instance]->get_modem_pins();
1728 if (!this->send_message(resp)) {
1729 API_LOG_MSG_DROPPED(TAG,
"Serial proxy response");
1733void APIConnection::on_subscribe_serial_proxy_identity_request() {
1734#ifdef USE_SERIAL_PROXY_USB_IDENTITY
1736 this->flags_.serial_proxy_identity_subscription =
true;
1739 proxy->send_identity(
this);
1744 if (!this->send_message(msg)) {
1745 API_LOG_MSG_DROPPED(TAG,
"Serial proxy identity");
1751 if (msg.
instance >= proxies.size()) {
1752 ESP_LOGW(TAG,
"Serial proxy instance %" PRIu32
" out of range", msg.
instance);
1753 send_serial_proxy_ack(
this, msg.
instance, msg.
type, enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT);
1756 auto *proxy = proxies[msg.
instance];
1759 case enums::SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE:
1760 case enums::SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE:
1761 status = serial_proxy_result_to_status(proxy->serial_proxy_request(
this, msg.
type));
1763 case enums::SERIAL_PROXY_REQUEST_TYPE_FLUSH:
1764 status = serial_proxy_result_to_status(proxy->flush_port(
this));
1766 case enums::SERIAL_PROXY_REQUEST_TYPE_CONFIGURE:
1767 case enums::SERIAL_PROXY_REQUEST_TYPE_SET_MODEM_PINS:
1768 case enums::SERIAL_PROXY_REQUEST_TYPE_SET_MODE:
1770 ESP_LOGW(TAG,
"Response-only serial proxy request type: %" PRIu32,
static_cast<uint32_t>(msg.
type));
1771 status = enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT;
1774 ESP_LOGW(TAG,
"Unknown serial proxy request type: %" PRIu32,
static_cast<uint32_t>(msg.
type));
1775 status = enums::SERIAL_PROXY_STATUS_NOT_SUPPORTED;
1783 if (msg.
instance >= proxies.size()) {
1784 ESP_LOGW(TAG,
"Serial proxy instance %" PRIu32
" out of range", msg.
instance);
1785 send_serial_proxy_ack(
this, msg.
instance, enums::SERIAL_PROXY_REQUEST_TYPE_SET_MODE,
1786 enums::SERIAL_PROXY_STATUS_INVALID_ARGUMENT);
1790 send_serial_proxy_ack(
this, msg.
instance, enums::SERIAL_PROXY_REQUEST_TYPE_SET_MODE,
1791 serial_proxy_result_to_status(result));
1795 if (!this->send_message(msg)) {
1796 ESP_LOGV(TAG,
"Serial proxy data dropped, TCP buffer full");
1807 return fill_and_encode_entity_info(infrared, msg, conn, remaining_size);
1811#ifdef USE_RADIO_FREQUENCY
1817 msg.
frequency_min = rf->get_traits().get_frequency_min_hz();
1818 msg.
frequency_max = rf->get_traits().get_frequency_max_hz();
1820 return fill_and_encode_entity_info(rf, msg, conn, remaining_size);
1826 return this->send_message_smart_(update, UpdateStateResponse::MESSAGE_TYPE, UpdateStateResponse::ESTIMATED_SIZE);
1832 if (update->has_state()) {
1834 if (update->update_info.has_progress) {
1836 resp.
progress = update->update_info.progress;
1844 return fill_and_encode_entity_state(update, resp, conn, remaining_size);
1849 return fill_and_encode_entity_info_with_device_class(update, msg, msg.
device_class, conn, remaining_size);
1855 case enums::UPDATE_COMMAND_UPDATE:
1858 case enums::UPDATE_COMMAND_CHECK:
1861 case enums::UPDATE_COMMAND_NONE:
1862 ESP_LOGE(TAG,
"UPDATE_COMMAND_NONE not handled; confirm command is correct");
1865 ESP_LOGW(TAG,
"Unknown update command: %" PRIu32, msg.
command);
1871bool APIConnection::try_send_log_message(
int level,
const char *tag,
const char *line,
size_t message_len) {
1874 msg.
set_message(
reinterpret_cast<const uint8_t *
>(line), message_len);
1875 return this->send_message(msg);
1878void APIConnection::complete_authentication_() {
1880 if (this->flags_.connection_state ==
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED)) {
1884 this->flags_.connection_state =
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED);
1887 this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR(
"connected"));
1888#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1890 char peername[socket::SOCKADDR_STR_LEN];
1891 this->parent_->get_client_connected_trigger()->trigger(std::string(this->helper_->get_client_name()),
1892 std::string(this->helper_->get_peername_to(peername)));
1895#ifdef USE_HOMEASSISTANT_TIME
1897 this->send_time_request();
1900#ifdef USE_ZWAVE_PROXY
1908#define PROGMEM_STRING_REF(s) ProgmemStringRef(ESPHOME_PSTR(s), sizeof(s) - 1)
1910static ProgmemStringRef esphome_version_ref() {
return PROGMEM_STRING_REF(ESPHOME_VERSION); }
1915 this->client_api_version_major_ =
1916 static_cast<uint8_t
>(std::min<uint32_t>(msg.
api_version_major, std::numeric_limits<uint8_t>::max()));
1917 this->client_api_version_minor_ =
1918 static_cast<uint8_t
>(std::min<uint32_t>(msg.
api_version_minor, std::numeric_limits<uint8_t>::max()));
1919 char peername[socket::SOCKADDR_STR_LEN];
1920 ESP_LOGV(TAG,
"Hello from client: '%s' | %s | API Version %u.%u", this->helper_->get_client_name(),
1921 this->helper_->get_peername_to(peername), this->client_api_version_major_, this->client_api_version_minor_);
1930#ifdef USE_PROVISIONING
1935 this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR(
"Provisioning closed; rejecting connection"));
1936 if (!this->send_message(resp)) {
1937 API_LOG_MSG_DROPPED(TAG,
"Hello response");
1940 req.
reason = enums::DISCONNECT_REASON_PROVISIONING_CLOSED;
1941 return this->send_message(req);
1946 this->complete_authentication_();
1948#ifdef USE_API_OUTGOING_CONNECTION
1952 if (this->parent_->get_noise_ctx().has_psk()) {
1953 this->flags_.outgoing_connection_target =
true;
1954 this->parent_->on_outgoing_target_client(
this);
1956 this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR(
"Dial-back target refused; no key active"));
1961 return this->send_message(resp);
1964bool APIConnection::send_ping_response_() {
1966 return this->send_message(resp);
1969bool APIConnection::send_device_info_response_() {
1976 char mac_address[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
1977 uint8_t mac[MAC_ADDRESS_SIZE];
1989#if defined(USE_ESP8266) || defined(USE_ESP32)
1990#define ESPHOME_MANUFACTURER "Espressif"
1991#elif defined(USE_RP2)
1992#define ESPHOME_MANUFACTURER "Raspberry Pi"
1993#elif defined(USE_BK72XX)
1994#define ESPHOME_MANUFACTURER "Beken"
1995#elif defined(USE_LN882X)
1996#define ESPHOME_MANUFACTURER "Lightning"
1997#elif defined(USE_NRF52)
1998#define ESPHOME_MANUFACTURER "Nordic Semiconductor"
1999#elif defined(USE_RTL87XX)
2000#define ESPHOME_MANUFACTURER "Realtek"
2001#elif defined(USE_HOST)
2002#define ESPHOME_MANUFACTURER "Host"
2005 resp.
manufacturer = PROGMEM_STRING_REF(ESPHOME_MANUFACTURER);
2006 static_assert(
sizeof(ESPHOME_MANUFACTURER) - 1 <= 20,
"Update max_data_length for manufacturer in api.proto");
2007#undef ESPHOME_MANUFACTURER
2009 resp.
model = PROGMEM_STRING_REF(ESPHOME_BOARD);
2010#ifdef USE_DEEP_SLEEP
2013#ifdef ESPHOME_PROJECT_NAME
2014 resp.
project_name = PROGMEM_STRING_REF(ESPHOME_PROJECT_NAME);
2017#undef PROGMEM_STRING_REF
2021#ifdef USE_BLUETOOTH_PROXY
2023 char bluetooth_mac[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
2027#ifdef USE_VOICE_ASSISTANT
2030#ifdef USE_ZWAVE_PROXY
2034#ifdef USE_SERIAL_PROXY
2035 size_t serial_proxy_index = 0;
2037 if (serial_proxy_index >= SERIAL_PROXY_COUNT)
2040 info.name =
StringRef(proxy->get_name());
2041 info.port_type = proxy->get_port_type();
2042 info.configured_line_states = proxy->get_configured_modem_pins();
2047#ifndef USE_API_NOISE_PSK_FROM_YAML
2053#ifdef USE_API_OUTGOING_CONNECTION
2058 size_t device_index = 0;
2060 if (device_index >= ESPHOME_DEVICE_COUNT)
2062 auto &device_info = resp.
devices[device_index++];
2063 device_info.device_id = device->get_device_id();
2064 device_info.name =
StringRef(device->get_name());
2065 device_info.area_id = device->get_area_id();
2070 size_t area_index = 0;
2072 if (area_index >= ESPHOME_AREA_COUNT)
2074 auto &area_info = resp.
areas[area_index++];
2075 area_info.area_id = area->get_area_id();
2076 area_info.name =
StringRef(area->get_name());
2080 return this->send_message(resp);
2082bool APIConnection::send_device_capabilities_response_() {
2086#ifdef USE_BLUETOOTH_PROXY
2088 char bluetooth_mac[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
2092#ifdef USE_VOICE_ASSISTANT
2095#ifdef USE_ZWAVE_PROXY
2099#ifdef USE_SERIAL_PROXY
2100 size_t serial_proxy_index = 0;
2102 if (serial_proxy_index >= SERIAL_PROXY_COUNT)
2105 info.name =
StringRef(proxy->get_name());
2106 info.port_type = proxy->get_port_type();
2107 info.configured_line_states = proxy->get_configured_modem_pins();
2110#ifdef USE_API_WIZARD
2113#if defined(USE_SENDSPIN) && defined(USE_ESP_IDF)
2117 return this->send_message(resp);
2120 if (!this->send_hello_response_(msg)) {
2121 this->on_fatal_error();
2126 if (!this->send_disconnect_response_()) {
2127 this->on_fatal_error();
2130void APIConnection::on_ping_request() {
2131 if (!this->send_ping_response_()) {
2132 this->on_fatal_error();
2135void APIConnection::on_device_info_request() {
2136 if (!this->send_device_info_response_()) {
2137 this->on_fatal_error();
2140void APIConnection::on_device_capabilities_request() {
2141 if (!this->send_device_capabilities_response_()) {
2142 this->on_fatal_error();
2146#ifdef USE_API_HOMEASSISTANT_STATES
2162 for (
auto &it : this->parent_->get_state_subs()) {
2173 it.callback(msg.
state);
2177#ifdef USE_API_USER_DEFINED_ACTIONS
2185 if (!arg.string_.empty()) {
2186 const_cast<char *
>(arg.string_.c_str())[arg.string_.size()] =
'\0';
2190#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
2195 action_call_id = this->parent_->register_active_action_call(msg.
call_id,
this);
2198 for (
auto *service : this->parent_->get_user_services()) {
2199 if (service->execute_service(msg, action_call_id)) {
2204 for (
auto *service : this->parent_->get_user_services()) {
2205 if (service->execute_service(msg)) {
2211 ESP_LOGV(TAG,
"Could not find service");
2217#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
2218void APIConnection::send_execute_service_response(
uint32_t call_id,
bool success,
StringRef error_message) {
2223 if (!this->send_message(resp)) {
2224 API_LOG_MSG_DROPPED(TAG,
"Action response");
2227#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
2228void APIConnection::send_execute_service_response(
uint32_t call_id,
bool success,
StringRef error_message,
2229 const uint8_t *response_data,
size_t response_data_len) {
2236 if (!this->send_message(resp)) {
2237 API_LOG_MSG_DROPPED(TAG,
"Action response");
2244#ifdef USE_API_HOMEASSISTANT_SERVICES
2246 if (!this->flags_.service_call_subscription)
2248 if (!this->send_message(call)) {
2249 API_LOG_MSG_DROPPED(TAG,
"Action request");
2255#ifdef USE_HOMEASSISTANT_TIME
2256void APIConnection::send_time_request() {
2258 if (!this->send_message(req)) {
2259 API_LOG_MSG_DROPPED(TAG,
"Time request");
2264#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
2266#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
2281#ifdef USE_API_NOISE_PSK_FROM_YAML
2283 ESP_LOGW(TAG,
"Key set in YAML");
2285#ifdef USE_PROVISIONING
2289 ESP_LOGW(TAG,
"Provisioning closed; rejecting key set");
2290 return this->send_message(resp);
2296 if (this->parent_->clear_noise_psk(
true)) {
2299 ESP_LOGW(TAG,
"Failed to clear encryption key");
2302 ESP_LOGW(TAG,
"Invalid encryption key length");
2306 ESP_LOGW(TAG,
"Rejecting all-zero encryption key");
2307 }
else if (!this->parent_->save_noise_psk(psk,
true)) {
2308 ESP_LOGW(TAG,
"Failed to save encryption key");
2311#ifdef USE_API_PLAINTEXT
2312 if (this->helper_->frame_footer_size() == 0) {
2315 ESP_LOGW(TAG,
"Key received over plaintext; deprecated, will be removed in 2027.2.0");
2321 return this->send_message(resp);
2324 if (!this->send_noise_encryption_set_key_response_(msg)) {
2325 this->on_fatal_error();
2329#ifdef USE_API_HOMEASSISTANT_STATES
2330void APIConnection::on_subscribe_home_assistant_states_request() {
2331#ifdef USE_API_WIZARD_LINKED_INPUTS
2333 this->flags_.home_assistant_states =
true;
2338bool APIConnection::try_to_clear_buffer_slow_(
bool log_out_of_space) {
2340 APIError err = this->helper_->loop();
2341 if (err != APIError::OK) {
2342 this->fatal_error_with_log_(LOG_STR(
"Socket operation failed"), err);
2345 if (this->helper_->can_write_without_blocking())
2347 if (log_out_of_space) {
2351 ESP_LOGVV(TAG,
"Cannot send message because of TCP buffer space");
2357#ifdef HAS_PROTO_MESSAGE_DUMP
2359 if (message_type != SubscribeLogsResponse::MESSAGE_TYPE
2361 && message_type != CameraImageResponse::MESSAGE_TYPE
2364 auto *proto_msg =
static_cast<const ProtoMessage *
>(msg);
2366 this->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf));
2369 if (!this->prepare_first_message_buffer(
payload_size)) [[unlikely]] {
2370 this->fatal_out_of_memory_();
2373 auto &shared_buf = this->parent_->get_shared_buffer_ref();
2374 size_t write_start = shared_buf.size();
2375#ifdef ESPHOME_DEBUG_API
2376 assert(shared_buf.capacity() >= write_start +
payload_size);
2380 uint8_t *
end = encode_fn(msg, shared_buf.data() + write_start PROTO_ENCODE_DEBUG_INIT(&shared_buf));
2381#ifdef ESPHOME_DEBUG_API
2393 return encode_to_buffer(calculated_size, encode_fn, msg, conn, remaining_size);
2396 const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);
2398 if (!this->try_to_clear_buffer(!is_log_message)) {
2403 this->helper_->set_nodelay_for_message(is_log_message);
2405 APIError err = this->helper_->write_protobuf_packet(message_type,
buffer);
2406 if (err == APIError::WOULD_BLOCK)
2408 if (err != APIError::OK) {
2409 this->fatal_error_with_log_(LOG_STR(
"Packet write failed"), err);
2415void APIConnection::on_no_setup_connection() {
2416 this->on_fatal_error();
2417 this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR(
"no connection setup"));
2419void APIConnection::fatal_out_of_memory_() {
2420 this->fatal_error_with_log_(LOG_STR(
"Out of memory"), APIError::OUT_OF_MEMORY);
2422void APIConnection::on_fatal_error() {
2425 this->flags_.remove =
true;
2428bool APIConnection::schedule_message_front_(
EntityBase *entity, uint16_t message_type, uint8_t estimated_size) {
2429 this->deferred_batch_.add_item_front(entity, message_type, estimated_size);
2430 return this->schedule_batch_();
2433bool APIConnection::send_message_smart_(
EntityBase *entity, uint16_t message_type, uint8_t estimated_size,
2434 uint8_t aux_data_index) {
2435 if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
2439 if (!this->prepare_first_message_buffer(estimated_size)) [[unlikely]] {
2440 this->fatal_out_of_memory_();
2444 if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE,
true) &&
2445 this->send_buffer(
ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
2446#ifdef HAS_PROTO_MESSAGE_DUMP
2447 this->log_batch_item_(item);
2453 if (this->flags_.remove) [[unlikely]]
2456 return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
2459bool APIConnection::schedule_batch_() {
2460 if (!this->flags_.batch_scheduled) {
2461 this->flags_.batch_scheduled =
true;
2467void APIConnection::process_batch_() {
2468 if (this->deferred_batch_.empty()) {
2469 this->flags_.batch_scheduled =
false;
2478 this->helper_->set_nodelay_for_message(
false);
2481 if (!this->try_to_clear_buffer(
true)) {
2487 auto &shared_buf = this->parent_->get_shared_buffer_ref();
2488 size_t num_items = this->deferred_batch_.size();
2491 const uint8_t header_padding = this->helper_->frame_header_padding();
2492 const uint8_t footer_size = this->helper_->frame_footer_size();
2495 uint32_t total_estimated_size = num_items * (header_padding + footer_size);
2496 for (
size_t i = 0; i < num_items; i++) {
2497 total_estimated_size += this->deferred_batch_[i].estimated_size;
2500 if (total_estimated_size > MAX_BATCH_PACKET_SIZE) {
2501 total_estimated_size = MAX_BATCH_PACKET_SIZE;
2504 if (!this->prepare_first_message_buffer(header_padding, total_estimated_size)) [[unlikely]] {
2505 this->fatal_out_of_memory_();
2506 this->clear_batch_();
2511 if (num_items == 1) {
2512 const auto &item = this->deferred_batch_[0];
2514 uint16_t
payload_size = this->dispatch_message_(item, std::numeric_limits<uint16_t>::max(),
true);
2517#ifdef HAS_PROTO_MESSAGE_DUMP
2519 this->log_batch_item_(item);
2521 this->clear_batch_();
2525 if (!this->flags_.remove) {
2526 ESP_LOGW(TAG,
"Message too large to send: type=%u", item.message_type);
2528 this->clear_batch_();
2534 this->process_batch_multi_(shared_buf, num_items, header_padding, footer_size);
2539void APIConnection::process_batch_multi_(
APIBuffer &shared_buf,
size_t num_items, uint8_t header_padding,
2540 uint8_t footer_size) {
2542 static_assert(std::is_trivially_destructible<MessageInfo>::value,
2543 "MessageInfo must remain trivially destructible with this placement-new approach");
2545 const size_t messages_to_process = std::min(num_items, MAX_MESSAGES_PER_BATCH);
2550 size_t items_processed = 0;
2551 uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
2558 for (
size_t i = 0; i < messages_to_process; i++) {
2559 const auto &item = this->deferred_batch_[i];
2562 uint16_t
payload_size = this->dispatch_message_(item, remaining_size, i == 0);
2571 uint16_t proto_payload_size =
payload_size - this->batch_header_size_ - footer_size;
2576 new (&message_info[items_processed++])
2577 MessageInfo(item.message_type, current_offset, proto_payload_size, this->batch_header_size_);
2579 if (items_processed == 1) {
2580 remaining_size = MAX_BATCH_PACKET_SIZE;
2585 current_offset = shared_buf.
size() + footer_size;
2588 if (items_processed > 0) {
2590 if (footer_size > 0 && !shared_buf.
resize(shared_buf.
size() + footer_size)) [[unlikely]] {
2591 this->fatal_out_of_memory_();
2592 this->clear_batch_();
2598 std::span<const MessageInfo>(message_info, items_processed));
2599 if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
2600 this->fatal_error_with_log_(LOG_STR(
"Batch write failed"), err);
2603#ifdef HAS_PROTO_MESSAGE_DUMP
2606 for (
size_t i = 0; i < items_processed; i++) {
2607 const auto &item = this->deferred_batch_[i];
2608 this->log_batch_item_(item);
2613 if (items_processed < this->deferred_batch_.size()) {
2614 this->deferred_batch_.remove_front(items_processed);
2615 this->schedule_batch_();
2621 this->clear_batch_();
2628 this->flags_.batch_first_message = batch_first;
2632 if (item.
message_type == EventResponse::MESSAGE_TYPE) {
2638 this, remaining_size);
2646#define CASE_STATE_INFO(entity_name, StateResp, InfoResp) \
2647 case StateResp::MESSAGE_TYPE: \
2648 func = &try_send_##entity_name##_state; \
2650 case InfoResp::MESSAGE_TYPE: \
2651 func = &try_send_##entity_name##_info; \
2653#define CASE_INFO_ONLY(entity_name, InfoResp) \
2654 case InfoResp::MESSAGE_TYPE: \
2655 func = &try_send_##entity_name##_info; \
2659#ifdef USE_BINARY_SENSOR
2680#ifdef USE_TEXT_SENSOR
2689#ifdef USE_DATETIME_DATE
2692#ifdef USE_DATETIME_TIME
2695#ifdef USE_DATETIME_DATETIME
2710#ifdef USE_MEDIA_PLAYER
2713#ifdef USE_ALARM_CONTROL_PANEL
2716#ifdef USE_WATER_HEATER
2725#ifdef USE_RADIO_FREQUENCY
2735 case ListEntitiesDoneResponse::MESSAGE_TYPE:
2736 func = &try_send_list_info_done;
2738 case DisconnectRequest::MESSAGE_TYPE:
2739 func = &try_send_disconnect_request;
2741 case PingRequest::MESSAGE_TYPE:
2742 func = &try_send_ping_request;
2748#undef CASE_STATE_INFO
2749#undef CASE_INFO_ONLY
2751 return func(item.
entity,
this, remaining_size);
2756 return encode_message_to_buffer(resp, conn, remaining_size);
2761 return encode_message_to_buffer(req, conn, remaining_size);
2766 return encode_message_to_buffer(req, conn, remaining_size);
2769#ifdef USE_API_HOMEASSISTANT_STATES
2770void APIConnection::process_state_subscriptions_() {
2771 const auto &subs = this->parent_->get_state_subs();
2772 if (this->state_subs_at_ >=
static_cast<int>(subs.size())) {
2773 this->state_subs_at_ = -1;
2777 const auto &it = subs[this->state_subs_at_];
2778#ifdef USE_API_WIZARD_LINKED_INPUTS
2780 if (it.entity_id[0] ==
'\0') {
2781 this->state_subs_at_++;
2791 resp.
once = it.once;
2792 if (this->send_message(resp)) {
2793 this->state_subs_at_++;
2798void APIConnection::log_client_(
int level,
const LogString *
message) {
2799 char peername[socket::SOCKADDR_STR_LEN];
2800 esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT(
"%s (%s): %s"), this->helper_->get_client_name(),
2801 this->helper_->get_peername_to(peername), LOG_STR_ARG(
message));
2805 char peername[socket::SOCKADDR_STR_LEN];
2806 ESP_LOGW(TAG,
"%s (%s): %s %s errno=%d", this->helper_->get_client_name(), this->helper_->get_peername_to(peername),
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
static constexpr size_t BUILD_TIME_STR_SIZE
Size of buffer required for build time string (including null terminator)
const StringRef & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
void get_build_time_string(std::span< char, BUILD_TIME_STR_SIZE > buffer)
Copy the build time string into the provided buffer Buffer must be BUILD_TIME_STR_SIZE bytes (compile...
const char * get_area() const
Get the area of this Application set by pre_setup().
const auto & get_devices()
auto & get_serial_proxies() const
void schedule_dump_config()
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.
void begin(bool include_internal=false)
ESPHOME_ALWAYS_INLINE void try_advance(size_t max_steps)
Run up to max_steps iteration steps; stops early when iteration completes or a callback refuses (that...
const ProgmemStringRef & get_name_progmem() const
The name as stored, for consumers that read flash themselves (e.g. API progmem fields).
ProgmemStringRef get_device_class_progmem() const
The device class as stored (in flash on ESP8266), for consumers that read flash themselves.
bool has_own_name() const
uint32_t get_object_id_hash() const
ProgmemStringRef get_icon_progmem() const
The icon as stored (in flash on ESP8266), for consumers that read flash themselves.
uint32_t get_device_id() const
bool is_disabled_by_default() const
EntityCategory get_entity_category() const
A string that may live in flash (on ESP8266): pointer and length, with no byte-reading operations.
StringRef is a reference to a string owned by something else.
constexpr const char * c_str() const
constexpr bool empty() const
constexpr size_type size() const
static StringRef from_maybe_nullptr(const char *s)
Byte buffer that skips zero-initialization on resize().
bool resize(size_t n) ESPHOME_ALWAYS_INLINE
Returns false if allocation fails; the buffer is left unchanged. No zero-fill.
void on_button_command_request(const ButtonCommandRequest &msg)
ProtoEncodeFn MessageEncodeFn
APIConnection(std::unique_ptr< socket::Socket > socket, APIServer *parent)
uint16_t(*)(EntityBase *, APIConnection *, uint32_t remaining_size) MessageCreatorPtr
uint32_t(*)(const void *) CalculateSizeFn
uint8_t get_consumed_header(uint8_t out[3]) const
noise::NoiseContext & get_noise_ctx()
enums::AlarmControlPanelStateCommand command
enums::AlarmControlPanelState state
enums::BluetoothScannerMode mode
void set_data(const uint8_t *data, size_t len)
bool has_target_temperature_high
float target_temperature_low
bool has_target_temperature_low
StringRef custom_fan_mode
float target_temperature_high
enums::ClimateSwingMode swing_mode
enums::ClimateFanMode fan_mode
bool has_target_temperature
enums::ClimatePreset preset
enums::ClimateFanMode fan_mode
float target_temperature_low
enums::ClimateSwingMode swing_mode
enums::ClimateAction action
enums::ClimatePreset preset
StringRef custom_fan_mode
float current_temperature
float target_temperature_high
enums::CoverOperation current_operation
std::array< SerialProxyInfo, SERIAL_PROXY_COUNT > serial_proxies
VoiceAssistantCapabilities voice_assistant
WizardCapabilities wizard
ZWaveProxyCapabilities zwave_proxy
SendspinCapabilities sendspin
BluetoothProxyCapabilities bluetooth_proxy
bool api_outgoing_connection_supported
ProgmemStringRef project_name
ProgmemStringRef esphome_version
bool api_encryption_provisionable
uint32_t zwave_proxy_feature_flags
ProgmemStringRef project_version
std::array< AreaInfo, ESPHOME_AREA_COUNT > areas
StringRef bluetooth_mac_address
uint32_t bluetooth_proxy_feature_flags
ProgmemStringRef manufacturer
StringRef compilation_time
uint32_t voice_assistant_feature_flags
bool api_encryption_supported
std::array< SerialProxyInfo, SERIAL_PROXY_COUNT > serial_proxies
std::array< DeviceInfo, ESPHOME_DEVICE_COUNT > devices
enums::DisconnectReason reason
Fixed-size buffer for message dumps - avoids heap allocation.
uint16_t response_data_len
const uint8_t * response_data
enums::FanDirection direction
enums::FanDirection direction
uint32_t api_version_major
uint32_t api_version_minor
bool outgoing_connection_target
uint32_t api_version_minor
ProgmemStringRef server_info
uint32_t api_version_major
const uint8_t * response_data
uint16_t response_data_len
enums::EntityCategory entity_category
const uint8_t * timings_data_
uint32_t carrier_frequency
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
ProgmemStringRef device_class
const climate::ClimateSwingModeMask * supported_swing_modes
enums::TemperatureUnit temperature_unit
float visual_max_humidity
bool supports_current_temperature
bool supports_current_humidity
bool supports_target_humidity
float visual_min_humidity
float visual_max_temperature
float visual_target_temperature_step
bool supports_two_point_target_temperature
const climate::ClimatePresetMask * supported_presets
const climate::ClimateFanModeMask * supported_fan_modes
const climate::ClimateModeMask * supported_modes
const ConstVector< const char *, true > * supported_custom_fan_modes
float visual_min_temperature
const ConstVector< const char *, true > * supported_custom_presets
float visual_current_temperature_step
ProgmemStringRef device_class
ProgmemStringRef device_class
const ConstVector< const char *, true > * event_types
const ConstVector< const char *, true > * supported_preset_modes
int32_t supported_speed_count
bool supports_oscillation
uint32_t receiver_frequency
const light::LightEffectNames * effects
const light::ColorModeMask * supported_color_modes
ProgmemStringRef device_class
StringRef unit_of_measurement
uint32_t supported_modulations
const std::span< const char *const > * options
ProgmemStringRef device_class
int32_t accuracy_decimals
StringRef unit_of_measurement
enums::SensorStateClass state_class
ProgmemStringRef device_class
ProgmemStringRef device_class
ProgmemStringRef device_class
ProgmemStringRef device_class
enums::TemperatureUnit temperature_unit
float target_temperature_step
const water_heater::WaterHeaterModeMask * supported_modes
uint32_t supported_features
enums::LockCommand command
enums::SendspinPairingTokenStatus status
enums::SerialProxyRequestType type
enums::SerialProxyMode mode
void set_message(const uint8_t *data, size_t len)
enums::UpdateCommand command
StringRef current_version
StringRef release_summary
enums::ValveOperation current_operation
std::vector< VoiceAssistantWakeWord > available_wake_words
uint32_t max_active_wake_words
const std::vector< std::string > * active_wake_words
std::vector< std::string > active_wake_words
float target_temperature_low
enums::WaterHeaterMode mode
float target_temperature_high
float current_temperature
float target_temperature_low
float target_temperature_high
enums::WaterHeaterMode mode
enums::ZWaveProxyRequestType type
enums::ZWaveProxyRequestType type
Base class for all binary_sensor-type classes.
void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg)
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg)
void get_bluetooth_mac_address_pretty(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > output)
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_feature_flags() const
void send_connections_free()
void unsubscribe_api_connection(api::APIConnection *api_connection)
void bluetooth_set_connection_params(const api::BluetoothSetConnectionParamsRequest &msg)
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)
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.
uint8_t get_last_event_type_index() const
Return index of last triggered event type, or max uint8_t if no event triggered yet.
void set_epoch_time(uint32_t epoch)
Infrared - Base class for infrared remote control implementations.
Effect names as the native API lists them: "None" first, then each effect; empty without effects.
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Base class for all locks.
static bool is_all_zeros(const psk_t &psk)
Base-class for all numbers.
RadioFrequency - Base class for radio frequency implementations.
Base-class for all selects.
uint32_t get_feature_flags() const
Returns the SendspinFeature flags reported to API clients.
std::optional< std::string > get_pairing_token() const
Returns the pairing token a Sendspin server uses to pair with this device.
StringRef get_client_id() const
Returns the Sendspin client_id: base64url of the client's static public key.
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()
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)
uint32_t get_feature_flags() const
void send_frame(api::APIConnection *api_connection, const uint8_t *data, size_t length)
void api_connection_authenticated(api::APIConnection *conn)
api::enums::ZWaveProxyStatus zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type)
const LogString * message
ESPHOME_LOG_TAG(TAG, "api.connection")
const LogString * api_error_to_logstr(APIError err)
void proto_check_encode_end(const uint8_t *end, const uint8_t *expected)
Aborts unless an encode body ended exactly where calculate_size() promised.
void log_dropped_message(const char *tag, int line, const LogString *what)
BluetoothProxy * global_bluetooth_proxy
@ CLIMATE_SUPPORTS_CURRENT_HUMIDITY
@ CLIMATE_SUPPORTS_TARGET_HUMIDITY
@ CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
@ CLIMATE_SUPPORTS_CURRENT_TEMPERATURE
@ CLIMATE_SUPPORTS_ACTION
@ CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE
ClimatePreset
Enum for all preset modes NOTE: If adding values, update ClimatePresetMask in climate_traits....
ClimateSwingMode
Enum for all modes a climate swing can be in NOTE: If adding values, update ClimateSwingModeMask in c...
ClimateMode
Enum for all modes a climate device can be in.
ClimateFanMode
NOTE: If adding values, update ClimateFanModeMask in climate_traits.h to use the new last value.
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.
@ COLOR_TEMPERATURE
Color temperature can be controlled.
@ COLD_WARM_WHITE
Brightness of cold and warm white output can be controlled.
void log_crash_to_client()
Log the previous crash for a client that just subscribed to the logs.
std::array< uint8_t, 32 > psk_t
ProvisioningManager * global_provisioning_manager
RadioFrequencyModulation
Modulation types supported by radio frequency implementations.
SendspinHub * global_sendspin_hub
SerialProxyResult
Result of a client-initiated operation; mapped to api::enums::SerialProxyStatus by the API layer.
@ SERIAL_PROXY_RESULT_TIMEOUT
Timed out before TX completed.
@ SERIAL_PROXY_RESULT_ERROR
Driver or hardware error.
@ SERIAL_PROXY_RESULT_NOT_SUPPORTED
Requested feature is not available on this instance.
@ SERIAL_PROXY_RESULT_PORT_IN_USE
Denied: another live client holds the port.
@ SERIAL_PROXY_RESULT_OK
Operation completed or request accepted.
@ SERIAL_PROXY_RESULT_INVALID_ARGUMENT
A parameter value is out of range.
@ SERIAL_PROXY_RESULT_ASSUMED_SUCCESS
Platform cannot confirm TX drain; success assumed.
void set_global_tz(const ParsedTimezone &tz)
Set the global timezone used by epoch_to_local_tm() when called without a timezone.
DSTRuleType
Type of DST transition rule.
@ UPDATE_STATE_INSTALLING
VoiceAssistant * global_voice_assistant
@ WATER_HEATER_STATE_ON
Water heater is on (not in standby)
@ WATER_HEATER_STATE_AWAY
Away/vacation mode is currently active.
ZWaveProxy * global_zwave_proxy
void HOT esp_log_printf_(int level, const char *tag, int line, const char *format,...)
std::vector< uint8_t > base64_decode(const std::string &encoded_string)
Decode a base64 string to a byte vector.
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
void HOT delay(uint32_t ms)
Application App
Global storage of Application pointer - only one Application can exist.
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)
A more user-friendly version of struct tm from time.h.
uint16_t day
Day of year (for JULIAN_NO_LEAP and DAY_OF_YEAR)
DSTRuleType type
Type of rule.
uint8_t week
Week 1-5, 5 = last (for MONTH_WEEK_DAY)
int32_t time_seconds
Seconds after midnight (default 7200 = 2:00 AM)
uint8_t day_of_week
Day 0-6, 0 = Sunday (for MONTH_WEEK_DAY)
uint8_t month
Month 1-12 (for MONTH_WEEK_DAY)
Parsed POSIX timezone information (packed for 32-bit: 32 bytes)
DSTRule dst_end
When DST ends.
DSTRule dst_start
When DST starts.
int32_t dst_offset_seconds
DST offset from UTC in seconds.
int32_t std_offset_seconds
Standard time offset from UTC in seconds (positive = west)