6#ifdef USE_API_PLAINTEXT
24#ifdef USE_HOMEASSISTANT_TIME
27#ifdef USE_BLUETOOTH_PROXY
30#ifdef USE_VOICE_ASSISTANT
40static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
41static constexpr uint8_t MAX_PING_RETRIES = 60;
42static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
43static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
45static const char *
const TAG =
"api.connection";
47static const int CAMERA_STOP_STREAM = 5000;
53#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
54 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
55 if ((entity_var) == nullptr) \
57 auto call = (entity_var)->make_call();
61#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
62 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
63 if ((entity_var) == nullptr) \
68#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
69 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
70 if ((entity_var) == nullptr) \
72 auto call = (entity_var)->make_call();
76#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
77 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
78 if ((entity_var) == nullptr) \
83 : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
84#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
86 if (noise_ctx->has_psk()) {
88 std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx, &this->client_info_)};
90 this->helper_ = std::unique_ptr<APIFrameHelper>{
new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
92#elif defined(USE_API_PLAINTEXT)
93 this->helper_ = std::unique_ptr<APIFrameHelper>{
new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
94#elif defined(USE_API_NOISE)
95 this->helper_ = std::unique_ptr<APIFrameHelper>{
96 new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx(), &this->client_info_)};
98#error "No frame helper defined"
107uint32_t APIConnection::get_batch_delay_ms_()
const {
return this->parent_->get_batch_delay(); }
109void APIConnection::start() {
112 APIError err = this->helper_->init();
113 if (err != APIError::OK) {
115 this->log_warning_(
"Helper init failed", err);
118 this->client_info_.peername = helper_->getpeername();
119 this->client_info_.name = this->client_info_.peername;
122APIConnection::~APIConnection() {
123#ifdef USE_BLUETOOTH_PROXY
128#ifdef USE_VOICE_ASSISTANT
135void APIConnection::loop() {
136 if (this->flags_.next_close) {
138 this->helper_->close();
139 this->flags_.remove =
true;
143 APIError err = this->helper_->loop();
144 if (err != APIError::OK) {
146 this->log_socket_operation_failed_(err);
152 if (this->helper_->is_socket_ready()) {
154 for (uint8_t message_count = 0; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
156 err = this->helper_->read_packet(&buffer);
157 if (err == APIError::WOULD_BLOCK) {
160 }
else if (err != APIError::OK) {
162 this->log_warning_(
"Reading failed", err);
165 this->last_traffic_ = now;
170 this->read_message(0, buffer.
type,
nullptr);
172 if (this->flags_.remove)
179 if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
180 this->process_batch_();
183 if (!this->list_entities_iterator_.completed()) {
184 this->process_iterator_batch_(this->list_entities_iterator_);
185 }
else if (!this->initial_state_iterator_.completed()) {
186 this->process_iterator_batch_(this->initial_state_iterator_);
189 if (this->initial_state_iterator_.completed()) {
191 if (!this->deferred_batch_.empty()) {
192 this->process_batch_();
195 this->flags_.should_try_send_immediately =
true;
199 if (this->flags_.sent_ping) {
201 if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
203 ESP_LOGW(TAG,
"%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
205 }
else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
207 ESP_LOGVV(TAG,
"Sending keepalive PING");
209 this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
210 if (!this->flags_.sent_ping) {
213 ESP_LOGW(TAG,
"Buffer full, ping queued");
214 this->schedule_message_front_(
nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE,
215 PingRequest::ESTIMATED_SIZE);
216 this->flags_.sent_ping =
true;
221 if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
222 uint32_t to_send = std::min((
size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
223 bool done = this->image_reader_->available() == to_send;
227 msg.
set_data(this->image_reader_->peek_data_buffer(), to_send);
233 if (this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) {
234 this->image_reader_->consume_data(to_send);
236 this->image_reader_->return_image();
242#ifdef USE_API_HOMEASSISTANT_STATES
243 if (state_subs_at_ >= 0) {
244 this->process_state_subscriptions_();
253 ESP_LOGD(TAG,
"%s disconnected", this->get_client_combined_info().c_str());
254 this->flags_.next_close =
true;
256 return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
259 this->helper_->close();
260 this->flags_.remove =
true;
266 uint32_t remaining_size,
bool is_single) {
267#ifdef HAS_PROTO_MESSAGE_DUMP
278 uint32_t calculated_size = size_calc.
get_size();
281 const uint8_t header_padding = conn->
helper_->frame_header_padding();
282 const uint8_t footer_size = conn->
helper_->frame_footer_size();
285 size_t total_calculated_size = calculated_size + header_padding + footer_size;
288 if (total_calculated_size > remaining_size) {
298 size_t size_before_encode = shared_buf.size();
304 size_t actual_payload_size = shared_buf.size() - size_before_encode;
307 size_t actual_total_size = header_padding + actual_payload_size + footer_size;
310 assert(calculated_size == actual_payload_size);
311 return static_cast<uint16_t
>(actual_total_size);
314#ifdef USE_BINARY_SENSOR
316 return this->send_message_smart_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
317 BinarySensorStateResponse::MESSAGE_TYPE, BinarySensorStateResponse::ESTIMATED_SIZE);
324 resp.
state = binary_sensor->state;
326 return fill_and_encode_entity_state(binary_sensor, resp, BinarySensorStateResponse::MESSAGE_TYPE, conn,
327 remaining_size, is_single);
336 return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
337 remaining_size, is_single);
343 return this->send_message_smart_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE,
344 CoverStateResponse::ESTIMATED_SIZE);
350 auto traits = cover->get_traits();
352 if (traits.get_supports_tilt())
353 msg.
tilt = cover->tilt;
355 return fill_and_encode_entity_state(cover, msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
361 auto traits = cover->get_traits();
367 return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size,
375 call.set_tilt(msg.
tilt);
377 call.set_command_stop();
384 return this->send_message_smart_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE,
385 FanStateResponse::ESTIMATED_SIZE);
389 auto *fan =
static_cast<fan::Fan *
>(entity);
391 auto traits = fan->get_traits();
392 msg.
state = fan->state;
393 if (traits.supports_oscillation())
395 if (traits.supports_speed()) {
398 if (traits.supports_direction())
400 if (traits.supports_preset_modes())
402 return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
406 auto *fan =
static_cast<fan::Fan *
>(entity);
408 auto traits = fan->get_traits();
414 return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
417 ENTITY_COMMAND_MAKE_CALL(
fan::Fan, fan, fan)
419 call.set_state(msg.
state);
436 return this->send_message_smart_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE,
437 LightStateResponse::ESTIMATED_SIZE);
443 auto traits = light->get_traits();
444 auto values = light->remote_values;
445 auto color_mode = values.get_color_mode();
446 resp.
state = values.is_on();
450 resp.
red = values.get_red();
451 resp.
green = values.get_green();
452 resp.
blue = values.get_blue();
453 resp.
white = values.get_white();
457 if (light->supports_effects()) {
459 std::string effect_name = light->get_effect_name();
462 return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
468 auto traits = light->get_traits();
475 if (light->supports_effects()) {
476 msg.
effects.emplace_back(
"None");
477 for (
auto *effect : light->get_effects()) {
478 msg.
effects.push_back(effect->get_name());
481 return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size,
487 call.set_state(msg.
state);
495 call.set_red(msg.
red);
496 call.set_green(msg.
green);
497 call.set_blue(msg.
blue);
500 call.set_white(msg.
white);
512 call.set_effect(msg.
effect);
519 return this->send_message_smart_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE,
520 SensorStateResponse::ESTIMATED_SIZE);
527 resp.
state = sensor->state;
529 return fill_and_encode_entity_state(sensor, resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
541 return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size,
548 return this->send_message_smart_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE,
549 SwitchStateResponse::ESTIMATED_SIZE);
556 resp.
state = a_switch->state;
557 return fill_and_encode_entity_state(a_switch, resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size,
567 return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size,
576 a_switch->turn_off();
581#ifdef USE_TEXT_SENSOR
583 return this->send_message_smart_(text_sensor, &APIConnection::try_send_text_sensor_state,
584 TextSensorStateResponse::MESSAGE_TYPE, TextSensorStateResponse::ESTIMATED_SIZE);
593 return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size,
601 return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
602 remaining_size, is_single);
608 return this->send_message_smart_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE,
609 ClimateStateResponse::ESTIMATED_SIZE);
615 auto traits = climate->get_traits();
618 if (traits.get_supports_current_temperature())
620 if (traits.get_supports_two_point_target_temperature()) {
626 if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
628 if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) {
631 if (traits.get_supports_presets() && climate->preset.has_value()) {
634 if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) {
637 if (traits.get_supports_swing_modes())
639 if (traits.get_supports_current_humidity())
641 if (traits.get_supports_target_humidity())
643 return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size,
650 auto traits = climate->get_traits();
668 return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size,
699 return this->send_message_smart_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE,
700 NumberStateResponse::ESTIMATED_SIZE);
707 resp.
state = number->state;
709 return fill_and_encode_entity_state(number, resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
719 msg.
min_value = number->traits.get_min_value();
720 msg.
max_value = number->traits.get_max_value();
721 msg.
step = number->traits.get_step();
722 return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size,
727 call.set_value(msg.
state);
732#ifdef USE_DATETIME_DATE
734 return this->send_message_smart_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE,
735 DateStateResponse::ESTIMATED_SIZE);
742 resp.
year = date->year;
743 resp.
month = date->month;
744 resp.
day = date->day;
745 return fill_and_encode_entity_state(date, resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
751 return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size,
761#ifdef USE_DATETIME_TIME
763 return this->send_message_smart_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE,
764 TimeStateResponse::ESTIMATED_SIZE);
771 resp.
hour = time->hour;
772 resp.
minute = time->minute;
773 resp.
second = time->second;
774 return fill_and_encode_entity_state(time, resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
780 return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size,
790#ifdef USE_DATETIME_DATETIME
792 return this->send_message_smart_(datetime, &APIConnection::try_send_datetime_state,
793 DateTimeStateResponse::MESSAGE_TYPE, DateTimeStateResponse::ESTIMATED_SIZE);
800 if (datetime->has_state()) {
804 return fill_and_encode_entity_state(datetime, resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size,
811 return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size,
823 return this->send_message_smart_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE,
824 TextStateResponse::ESTIMATED_SIZE);
829 auto *text =
static_cast<text::Text *
>(entity);
833 return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
838 auto *text =
static_cast<text::Text *
>(entity);
841 msg.
min_length = text->traits.get_min_length();
842 msg.
max_length = text->traits.get_max_length();
844 return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size,
848 ENTITY_COMMAND_MAKE_CALL(
text::Text, text, text)
849 call.set_value(msg.
state);
856 return this->send_message_smart_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE,
857 SelectStateResponse::ESTIMATED_SIZE);
866 return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
873 msg.
options = &select->traits.get_options();
874 return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size,
879 call.set_option(msg.
state);
890 return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size,
901 return this->send_message_smart_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE,
902 LockStateResponse::ESTIMATED_SIZE);
907 auto *a_lock =
static_cast<lock::Lock *
>(entity);
910 return fill_and_encode_entity_state(a_lock, resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
915 auto *a_lock =
static_cast<lock::Lock *
>(entity);
920 return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size,
927 case enums::LOCK_UNLOCK:
930 case enums::LOCK_LOCK:
933 case enums::LOCK_OPEN:
942 return this->send_message_smart_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE,
943 ValveStateResponse::ESTIMATED_SIZE);
951 return fill_and_encode_entity_state(valve, resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
957 auto traits = valve->get_traits();
962 return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size,
970 call.set_command_stop();
975#ifdef USE_MEDIA_PLAYER
977 return this->send_message_smart_(media_player, &APIConnection::try_send_media_player_state,
978 MediaPlayerStateResponse::MESSAGE_TYPE, MediaPlayerStateResponse::ESTIMATED_SIZE);
986 : media_player->state;
988 resp.
volume = media_player->volume;
989 resp.
muted = media_player->is_muted();
990 return fill_and_encode_entity_state(media_player, resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size,
997 auto traits = media_player->get_traits();
1000 for (
auto &supported_format : traits.get_supported_formats()) {
1003 media_format.set_format(
StringRef(supported_format.format));
1004 media_format.sample_rate = supported_format.sample_rate;
1005 media_format.num_channels = supported_format.num_channels;
1007 media_format.sample_bytes = supported_format.sample_bytes;
1009 return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
1010 remaining_size, is_single);
1018 call.set_volume(msg.
volume);
1031void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
1032 if (!this->flags_.state_subscription)
1034 if (!this->image_reader_)
1036 if (this->image_reader_->available())
1039 this->image_reader_->set_image(std::move(image));
1045 return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size,
1057 App.scheduler.set_timeout(this->parent_,
"api_camera_stop_stream", CAMERA_STOP_STREAM,
1063#ifdef USE_HOMEASSISTANT_TIME
1073 return this->send_message(resp, GetTimeResponse::MESSAGE_TYPE);
1076#ifdef USE_BLUETOOTH_PROXY
1106bool APIConnection::send_subscribe_bluetooth_connections_free_response(
1114 msg.
mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1118#ifdef USE_VOICE_ASSISTANT
1119bool APIConnection::check_voice_assistant_api_connection_()
const {
1130 if (!this->check_voice_assistant_api_connection_()) {
1138 if (msg.
port == 0) {
1144 this->helper_->getpeername((
struct sockaddr *) &storage, &
len);
1149 if (this->check_voice_assistant_api_connection_()) {
1154 if (this->check_voice_assistant_api_connection_()) {
1159 if (this->check_voice_assistant_api_connection_()) {
1165 if (this->check_voice_assistant_api_connection_()) {
1172 if (!this->check_voice_assistant_api_connection_()) {
1173 return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
1177 for (
auto &wake_word : config.available_wake_words) {
1180 resp_wake_word.set_id(
StringRef(wake_word.id));
1181 resp_wake_word.set_wake_word(
StringRef(wake_word.wake_word));
1182 for (
const auto &lang : wake_word.trained_languages) {
1183 resp_wake_word.trained_languages.push_back(lang);
1188 return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
1192 if (this->check_voice_assistant_api_connection_()) {
1199#ifdef USE_ALARM_CONTROL_PANEL
1201 return this->send_message_smart_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
1202 AlarmControlPanelStateResponse::MESSAGE_TYPE,
1203 AlarmControlPanelStateResponse::ESTIMATED_SIZE);
1206 uint32_t remaining_size,
bool is_single) {
1210 return fill_and_encode_entity_state(a_alarm_control_panel, resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn,
1211 remaining_size, is_single);
1214 uint32_t remaining_size,
bool is_single) {
1218 msg.
requires_code = a_alarm_control_panel->get_requires_code();
1220 return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
1221 conn, remaining_size, is_single);
1226 case enums::ALARM_CONTROL_PANEL_DISARM:
1229 case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
1232 case enums::ALARM_CONTROL_PANEL_ARM_HOME:
1235 case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
1238 case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
1239 call.arm_vacation();
1241 case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
1242 call.arm_custom_bypass();
1244 case enums::ALARM_CONTROL_PANEL_TRIGGER:
1248 call.set_code(msg.
code);
1254void APIConnection::send_event(
event::Event *event,
const std::string &event_type) {
1255 this->schedule_message_(event,
MessageCreator(event_type), EventResponse::MESSAGE_TYPE,
1256 EventResponse::ESTIMATED_SIZE);
1259 uint32_t remaining_size,
bool is_single) {
1262 return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1270 for (
const auto &event_type : event->get_event_types())
1272 return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
1279 return this->send_message_smart_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE,
1280 UpdateStateResponse::ESTIMATED_SIZE);
1287 if (update->has_state()) {
1289 if (update->update_info.has_progress) {
1291 resp.
progress = update->update_info.progress;
1299 return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1306 return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size,
1313 case enums::UPDATE_COMMAND_UPDATE:
1316 case enums::UPDATE_COMMAND_CHECK:
1319 case enums::UPDATE_COMMAND_NONE:
1320 ESP_LOGE(TAG,
"UPDATE_COMMAND_NONE not handled; confirm command is correct");
1323 ESP_LOGW(TAG,
"Unknown update command: %" PRIu32, msg.
command);
1329bool APIConnection::try_send_log_message(
int level,
const char *tag,
const char *line,
size_t message_len) {
1332 msg.
set_message(
reinterpret_cast<const uint8_t *
>(line), message_len);
1333 return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE);
1336void APIConnection::complete_authentication_() {
1338 if (this->flags_.connection_state ==
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED)) {
1342 this->flags_.connection_state =
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED);
1343 ESP_LOGD(TAG,
"%s connected", this->get_client_combined_info().c_str());
1344#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1345 this->parent_->get_client_connected_trigger()->trigger(this->client_info_.name, this->client_info_.peername);
1347#ifdef USE_HOMEASSISTANT_TIME
1349 this->send_time_request();
1356 this->client_info_.peername = this->helper_->getpeername();
1359 ESP_LOGV(TAG,
"Hello from client: '%s' | %s | API Version %" PRIu32
".%" PRIu32, this->client_info_.name.c_str(),
1360 this->client_info_.peername.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
1366 std::string server_info =
App.
get_name() +
" (esphome v" ESPHOME_VERSION
")";
1370#ifdef USE_API_PASSWORD
1372 this->flags_.connection_state =
static_cast<uint8_t
>(ConnectionState::CONNECTED);
1375 this->complete_authentication_();
1378 return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
1381 bool correct =
true;
1382#ifdef USE_API_PASSWORD
1383 correct = this->parent_->check_password(msg.
password);
1390 this->complete_authentication_();
1392 return this->send_message(resp, ConnectResponse::MESSAGE_TYPE);
1397 return this->send_message(resp, PingResponse::MESSAGE_TYPE);
1402#ifdef USE_API_PASSWORD
1412 resp.set_mac_address(
StringRef(mac_address));
1416 resp.set_esphome_version(ESPHOME_VERSION_REF);
1420 resp.set_compilation_time(
StringRef(compilation_time));
1423#if defined(USE_ESP8266) || defined(USE_ESP32)
1425#elif defined(USE_RP2040)
1427#elif defined(USE_BK72XX)
1429#elif defined(USE_LN882X)
1431#elif defined(USE_RTL87XX)
1433#elif defined(USE_HOST)
1436 resp.set_manufacturer(MANUFACTURER);
1439 resp.set_model(MODEL);
1440#ifdef USE_DEEP_SLEEP
1443#ifdef ESPHOME_PROJECT_NAME
1446 resp.set_project_name(PROJECT_NAME);
1447 resp.set_project_version(PROJECT_VERSION);
1450 resp.webserver_port = USE_WEBSERVER_PORT;
1452#ifdef USE_BLUETOOTH_PROXY
1456 resp.set_bluetooth_mac_address(
StringRef(bluetooth_mac));
1458#ifdef USE_VOICE_ASSISTANT
1462 resp.api_encryption_supported =
true;
1465 size_t device_index = 0;
1467 if (device_index >= ESPHOME_DEVICE_COUNT)
1469 auto &device_info = resp.devices[device_index++];
1470 device_info.device_id = device->get_device_id();
1471 device_info.set_name(
StringRef(device->get_name()));
1472 device_info.area_id = device->get_area_id();
1476 size_t area_index = 0;
1478 if (area_index >= ESPHOME_AREA_COUNT)
1480 auto &area_info = resp.areas[area_index++];
1481 area_info.area_id = area->get_area_id();
1482 area_info.set_name(
StringRef(area->get_name()));
1486 return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE);
1489#ifdef USE_API_HOMEASSISTANT_STATES
1491 for (
auto &it : this->parent_->get_state_subs()) {
1493 it.callback(msg.
state);
1498#ifdef USE_API_SERVICES
1501 for (
auto *service : this->parent_->get_user_services()) {
1502 if (service->execute_service(msg)) {
1507 ESP_LOGV(TAG,
"Could not find service");
1518 ESP_LOGW(TAG,
"Invalid encryption key length");
1519 }
else if (!this->parent_->save_noise_psk(psk,
true)) {
1520 ESP_LOGW(TAG,
"Failed to save encryption key");
1525 return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
1528#ifdef USE_API_HOMEASSISTANT_STATES
1533bool APIConnection::try_to_clear_buffer(
bool log_out_of_space) {
1534 if (this->flags_.remove)
1536 if (this->helper_->can_write_without_blocking())
1539 APIError err = this->helper_->loop();
1540 if (err != APIError::OK) {
1542 this->log_socket_operation_failed_(err);
1545 if (this->helper_->can_write_without_blocking())
1547 if (log_out_of_space) {
1548 ESP_LOGV(TAG,
"Cannot send message because of TCP buffer space");
1553 if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) {
1557 APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
1558 if (err == APIError::WOULD_BLOCK)
1560 if (err != APIError::OK) {
1562 this->log_warning_(
"Packet write failed", err);
1568#ifdef USE_API_PASSWORD
1569void APIConnection::on_unauthenticated_access() {
1570 this->on_fatal_error();
1571 ESP_LOGD(TAG,
"%s access without authentication", this->get_client_combined_info().c_str());
1574void APIConnection::on_no_setup_connection() {
1575 this->on_fatal_error();
1576 ESP_LOGD(TAG,
"%s access without full connection", this->get_client_combined_info().c_str());
1578void APIConnection::on_fatal_error() {
1579 this->helper_->close();
1580 this->flags_.remove =
true;
1584 uint8_t estimated_size) {
1588 for (
auto &item : items) {
1589 if (item.entity == entity && item.message_type == message_type) {
1591 item.creator.cleanup(message_type);
1593 item.creator = std::move(creator);
1599 items.emplace_back(entity, std::move(creator), message_type, estimated_size);
1603 uint8_t estimated_size) {
1608 items.emplace_back(entity, std::move(creator), message_type, estimated_size);
1609 if (items.size() > 1) {
1611 std::swap(items.front(), items.back());
1615bool APIConnection::schedule_batch_() {
1616 if (!this->flags_.batch_scheduled) {
1617 this->flags_.batch_scheduled =
true;
1623ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) {
return this->create_buffer(size); }
1626 ProtoWriteBuffer result = this->prepare_message_buffer(size, this->flags_.batch_first_message);
1627 this->flags_.batch_first_message =
false;
1631void APIConnection::process_batch_() {
1633 static_assert(std::is_trivially_destructible<PacketInfo>::value,
1634 "PacketInfo must remain trivially destructible with this placement-new approach");
1636 if (this->deferred_batch_.empty()) {
1637 this->flags_.batch_scheduled =
false;
1642 if (!this->try_to_clear_buffer(
true)) {
1648 auto &shared_buf = this->parent_->get_shared_buffer_ref();
1649 size_t num_items = this->deferred_batch_.size();
1652 if (num_items == 1) {
1653 const auto &item = this->deferred_batch_[0];
1657 item.creator(item.entity,
this, std::numeric_limits<uint16_t>::max(),
true, item.message_type);
1660#ifdef HAS_PROTO_MESSAGE_DUMP
1663 this->log_batch_item_(item);
1665 this->clear_batch_();
1668 ESP_LOGW(TAG,
"Message too large to send: type=%u", item.message_type);
1669 this->clear_batch_();
1674 size_t packets_to_process = std::min(num_items, MAX_PACKETS_PER_BATCH);
1679 size_t packet_count = 0;
1682 const uint8_t header_padding = this->helper_->frame_header_padding();
1683 const uint8_t footer_size = this->helper_->frame_footer_size();
1689 uint32_t total_estimated_size = num_items * (header_padding + footer_size);
1690 for (
size_t i = 0; i < this->deferred_batch_.size(); i++) {
1691 const auto &item = this->deferred_batch_[i];
1692 total_estimated_size += item.estimated_size;
1697 shared_buf.reserve(total_estimated_size);
1698 this->flags_.batch_first_message =
true;
1700 size_t items_processed = 0;
1701 uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
1707 uint32_t current_offset = 0;
1710 for (
size_t i = 0; i < packets_to_process; i++) {
1711 const auto &item = this->deferred_batch_[i];
1714 uint16_t
payload_size = item.creator(item.entity,
this, remaining_size,
false, item.message_type);
1723 uint16_t proto_payload_size =
payload_size - header_padding - footer_size;
1728 new (&packet_info[packet_count++])
PacketInfo(item.message_type, current_offset, proto_payload_size);
1733 if (items_processed == 1) {
1734 remaining_size = MAX_BATCH_PACKET_SIZE;
1739 current_offset = shared_buf.size() + footer_size;
1742 if (items_processed == 0) {
1743 this->deferred_batch_.clear();
1748 if (footer_size > 0) {
1749 shared_buf.resize(shared_buf.size() + footer_size);
1754 std::span<const PacketInfo>(packet_info, packet_count));
1755 if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1757 this->log_warning_(
"Batch write failed", err);
1760#ifdef HAS_PROTO_MESSAGE_DUMP
1763 for (
size_t i = 0; i < items_processed; i++) {
1764 const auto &item = this->deferred_batch_[i];
1765 this->log_batch_item_(item);
1770 if (items_processed < this->deferred_batch_.size()) {
1772 this->deferred_batch_.remove_front(items_processed);
1774 this->schedule_batch_();
1777 this->clear_batch_();
1782 bool is_single, uint8_t message_type)
const {
1785 if (message_type == EventResponse::MESSAGE_TYPE) {
1787 return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
1792 return data_.function_ptr(entity, conn, remaining_size, is_single);
1798 return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1804 return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1810 return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1813#ifdef USE_API_HOMEASSISTANT_STATES
1814void APIConnection::process_state_subscriptions_() {
1815 const auto &subs = this->parent_->get_state_subs();
1816 if (this->state_subs_at_ >=
static_cast<int>(subs.size())) {
1817 this->state_subs_at_ = -1;
1821 const auto &it = subs[this->state_subs_at_];
1828 resp.
once = it.once;
1829 if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
1830 this->state_subs_at_++;
1835void APIConnection::log_warning_(
const char *message,
APIError err) {
1836 ESP_LOGW(TAG,
"%s: %s %s errno=%d", this->get_client_combined_info().c_str(), message,
api_error_to_str(err), errno);
1839void APIConnection::log_socket_operation_failed_(
APIError err) { this->log_warning_(
"Socket operation failed", err); }
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
std::string get_compilation_time() const
const char * get_area() const
Get the area of this Application set by pre_setup().
const auto & get_devices()
const std::string & get_name() const
Get the name of this Application set by pre_setup().
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.
uint32_t get_object_id_hash()
uint32_t get_device_id() const
StringRef is a reference to a string owned by something else.
static constexpr StringRef from_lit(const CharT(&s)[N])
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
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
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
void set_custom_fan_mode(const StringRef &ref)
void set_custom_preset(const StringRef &ref)
enums::ClimateAction action
enums::ClimatePreset preset
float current_temperature
float target_temperature_high
enums::CoverOperation current_operation
void set_event_type(const StringRef &ref)
enums::FanDirection direction
enums::FanDirection direction
void set_preset_mode(const StringRef &ref)
uint32_t api_version_major
uint32_t api_version_minor
uint32_t api_version_minor
void set_name(const StringRef &ref)
void set_server_info(const StringRef &ref)
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
void set_effect(const StringRef &ref)
enums::ColorMode color_mode
bool requires_code_to_arm
uint32_t supported_features
bool is_status_binary_sensor
void set_device_class(const StringRef &ref)
float visual_max_humidity
const std::set< std::string > * supported_custom_presets
bool supports_current_temperature
bool supports_current_humidity
bool supports_target_humidity
float visual_min_humidity
float visual_max_temperature
const std::set< climate::ClimateSwingMode > * supported_swing_modes
float visual_target_temperature_step
bool supports_two_point_target_temperature
const std::set< std::string > * supported_custom_fan_modes
float visual_min_temperature
const std::set< climate::ClimateFanMode > * supported_fan_modes
float visual_current_temperature_step
const std::set< climate::ClimatePreset > * supported_presets
const std::set< climate::ClimateMode > * supported_modes
void set_device_class(const StringRef &ref)
std::vector< std::string > event_types
void set_device_class(const StringRef &ref)
const std::set< std::string > * supported_preset_modes
int32_t supported_speed_count
bool supports_oscillation
const std::set< light::ColorMode > * supported_color_modes
std::vector< std::string > effects
void set_unit_of_measurement(const StringRef &ref)
void set_device_class(const StringRef &ref)
const std::vector< std::string > * options
int32_t accuracy_decimals
void set_unit_of_measurement(const StringRef &ref)
void set_device_class(const StringRef &ref)
enums::SensorStateClass state_class
void set_device_class(const StringRef &ref)
void set_pattern(const StringRef &ref)
void set_device_class(const StringRef &ref)
void set_device_class(const StringRef &ref)
void set_device_class(const StringRef &ref)
enums::LockCommand command
virtual void encode(ProtoWriteBuffer buffer) const
virtual const char * message_name() const
virtual void calculate_size(ProtoSize &size) const
uint32_t get_size() const
void set_state(const StringRef &ref)
void set_entity_id(const StringRef &ref)
void set_attribute(const StringRef &ref)
void set_message(const uint8_t *data, size_t len)
void set_state(const StringRef &ref)
void set_state(const StringRef &ref)
enums::UpdateCommand command
void set_current_version(const StringRef &ref)
void set_latest_version(const StringRef &ref)
void set_release_summary(const StringRef &ref)
void set_title(const StringRef &ref)
void set_release_url(const StringRef &ref)
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
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 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_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()
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)
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.
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.
@ COLOR_TEMPERATURE
Color temperature can be controlled.
@ COLD_WARM_WHITE
Brightness of cold and warm white output can be controlled.
@ UPDATE_STATE_INSTALLING
VoiceAssistant * global_voice_assistant
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