6#ifdef USE_API_PLAINTEXT
9#ifdef USE_API_USER_DEFINED_ACTIONS
31#ifdef USE_HOMEASSISTANT_TIME
34#ifdef USE_BLUETOOTH_PROXY
40#ifdef USE_VOICE_ASSISTANT
46#ifdef USE_WATER_HEATER
59static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
60static constexpr uint8_t MAX_PING_RETRIES = 60;
61static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
62static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
67static constexpr uint32_t HANDSHAKE_TIMEOUT_MS = 15000;
71static const char *
const TAG =
"api.connection";
73static const int CAMERA_STOP_STREAM = 5000;
79#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
80 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
81 if ((entity_var) == nullptr) \
83 auto call = (entity_var)->make_call();
87#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
88 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
89 if ((entity_var) == nullptr) \
94#define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
95 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
96 if ((entity_var) == nullptr) \
98 auto call = (entity_var)->make_call();
102#define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
103 entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
104 if ((entity_var) == nullptr) \
109#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
111 if (noise_ctx.has_psk()) {
112 this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
116#elif defined(USE_API_PLAINTEXT)
117 this->helper_ = std::unique_ptr<APIFrameHelper>{
new APIPlaintextFrameHelper(std::move(sock))};
118#elif defined(USE_API_NOISE)
119 this->helper_ = std::unique_ptr<APIFrameHelper>{
new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
121#error "No frame helper defined"
130uint32_t APIConnection::get_batch_delay_ms_()
const {
return this->parent_->get_batch_delay(); }
132void APIConnection::start() {
135 APIError err = this->helper_->init();
136 if (err != APIError::OK) {
137 this->fatal_error_with_log_(LOG_STR(
"Helper init failed"), err);
141 char peername[socket::SOCKADDR_STR_LEN];
142 this->helper_->set_client_name(this->helper_->get_peername_to(peername), strlen(peername));
145APIConnection::~APIConnection() {
146 this->destroy_active_iterator_();
147#ifdef USE_BLUETOOTH_PROXY
152#ifdef USE_VOICE_ASSISTANT
159void APIConnection::destroy_active_iterator_() {
160 switch (this->active_iterator_) {
161 case ActiveIterator::LIST_ENTITIES:
162 this->iterator_storage_.list_entities.~ListEntitiesIterator();
164 case ActiveIterator::INITIAL_STATE:
165 this->iterator_storage_.initial_state.~InitialStateIterator();
167 case ActiveIterator::NONE:
170 this->active_iterator_ = ActiveIterator::NONE;
174 this->destroy_active_iterator_();
175 this->active_iterator_ =
type;
176 if (
type == ActiveIterator::LIST_ENTITIES) {
178 this->iterator_storage_.list_entities.
begin();
181 this->iterator_storage_.initial_state.
begin();
185void APIConnection::loop() {
186 if (this->flags_.next_close) {
189 this->flags_.remove =
true;
193 APIError err = this->helper_->loop();
194 if (err != APIError::OK) {
195 this->fatal_error_with_log_(LOG_STR(
"Socket operation failed"), err);
201 if (this->helper_->is_socket_ready()) {
203 for (uint8_t message_count = 0; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
205 err = this->helper_->read_packet(&buffer);
206 if (err == APIError::WOULD_BLOCK) {
209 }
else if (err != APIError::OK) {
210 this->fatal_error_with_log_(LOG_STR(
"Reading failed"), err);
216 if (this->is_authenticated()) {
217 this->last_traffic_ = now;
221 if (this->flags_.remove)
228 if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
229 this->process_batch_();
232 if (this->active_iterator_ != ActiveIterator::NONE) {
233 this->process_active_iterator_();
239 if (!this->is_authenticated() && now - this->last_traffic_ > HANDSHAKE_TIMEOUT_MS) {
240 this->on_fatal_error();
241 this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR(
"handshake timeout; disconnecting"));
248 if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
249 this->check_keepalive_(now);
252#ifdef USE_API_HOMEASSISTANT_STATES
253 if (state_subs_at_ >= 0) {
254 this->process_state_subscriptions_();
261 this->try_send_camera_image_();
265void APIConnection::check_keepalive_(uint32_t now) {
267 if (this->flags_.sent_ping) {
269 if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
271 this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR(
"is unresponsive; disconnecting"));
273 }
else if (!this->flags_.remove) {
275 ESP_LOGVV(TAG,
"Sending keepalive PING");
277 this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
278 if (!this->flags_.sent_ping) {
281 ESP_LOGW(TAG,
"Buffer full, ping queued");
282 this->schedule_message_front_(
nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
283 this->flags_.sent_ping =
true;
288void APIConnection::process_active_iterator_() {
290 if (this->active_iterator_ == ActiveIterator::LIST_ENTITIES) {
291 if (this->iterator_storage_.list_entities.completed()) {
292 this->destroy_active_iterator_();
293 if (this->flags_.state_subscription) {
294 this->begin_iterator_(ActiveIterator::INITIAL_STATE);
297 this->process_iterator_batch_(this->iterator_storage_.list_entities);
300 if (this->iterator_storage_.initial_state.completed()) {
301 this->destroy_active_iterator_();
303 if (!this->deferred_batch_.empty()) {
304 this->process_batch_();
307 this->flags_.should_try_send_immediately =
true;
309 this->deferred_batch_.release_buffer();
310 this->helper_->release_buffers();
312 this->process_iterator_batch_(this->iterator_storage_.initial_state);
318 size_t initial_size = this->deferred_batch_.size();
319 size_t max_batch = this->get_max_batch_size_();
320 while (!iterator.
completed() && (this->deferred_batch_.size() - initial_size) < max_batch) {
326 if (this->deferred_batch_.size() >= max_batch) {
327 this->process_batch_();
331bool APIConnection::send_disconnect_response_() {
335 this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR(
"disconnected"));
336 this->flags_.next_close =
true;
338 return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
340void APIConnection::on_disconnect_response() {
343 this->flags_.remove =
true;
349 uint32_t remaining_size) {
350#ifdef HAS_PROTO_MESSAGE_DUMP
363 const uint8_t header_padding = conn->
helper_->frame_header_padding();
364 const uint8_t footer_size = conn->
helper_->frame_footer_size();
367 size_t total_calculated_size = calculated_size + header_padding + footer_size;
370 if (total_calculated_size > remaining_size) {
383 size_t current_size = shared_buf.size();
384 shared_buf.reserve(current_size + total_calculated_size);
385 shared_buf.resize(current_size + footer_size + header_padding);
389 size_t write_start = shared_buf.size();
390 shared_buf.resize(write_start + calculated_size);
395 return static_cast<uint16_t
>(header_padding + calculated_size + footer_size);
398#ifdef USE_BINARY_SENSOR
400 return this->send_message_smart_(binary_sensor, BinarySensorStateResponse::MESSAGE_TYPE,
401 BinarySensorStateResponse::ESTIMATED_SIZE);
407 resp.
state = binary_sensor->state;
409 return fill_and_encode_entity_state(binary_sensor, resp, BinarySensorStateResponse::MESSAGE_TYPE, conn,
416 msg.
device_class = binary_sensor->get_device_class_ref();
418 return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
425 return this->send_message_smart_(cover, CoverStateResponse::MESSAGE_TYPE, CoverStateResponse::ESTIMATED_SIZE);
430 auto traits = cover->get_traits();
432 if (traits.get_supports_tilt())
433 msg.
tilt = cover->tilt;
435 return fill_and_encode_entity_state(cover, msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size);
440 auto traits = cover->get_traits();
446 return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size);
453 call.set_tilt(msg.
tilt);
455 call.set_command_stop();
462 return this->send_message_smart_(fan, FanStateResponse::MESSAGE_TYPE, FanStateResponse::ESTIMATED_SIZE);
465 auto *fan =
static_cast<fan::Fan *
>(entity);
467 auto traits = fan->get_traits();
468 msg.
state = fan->state;
469 if (traits.supports_oscillation())
471 if (traits.supports_speed()) {
474 if (traits.supports_direction())
476 if (traits.supports_preset_modes() && fan->has_preset_mode())
478 return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size);
481 auto *fan =
static_cast<fan::Fan *
>(entity);
483 auto traits = fan->get_traits();
489 return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size);
492 ENTITY_COMMAND_MAKE_CALL(
fan::Fan, fan, fan)
494 call.set_state(msg.
state);
511 return this->send_message_smart_(light, LightStateResponse::MESSAGE_TYPE, LightStateResponse::ESTIMATED_SIZE);
516 auto values = light->remote_values;
517 auto color_mode = values.get_color_mode();
518 resp.
state = values.is_on();
522 resp.
red = values.get_red();
523 resp.
green = values.get_green();
524 resp.
blue = values.get_blue();
525 resp.
white = values.get_white();
529 if (light->supports_effects()) {
530 resp.
effect = light->get_effect_name();
532 return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size);
537 auto traits = light->get_traits();
538 auto supported_modes = traits.get_supported_color_modes();
547 if (light->supports_effects()) {
548 auto &light_effects = light->get_effects();
549 effects_list.
init(light_effects.size() + 1);
551 for (
auto *effect : light_effects) {
553 effects_list.
push_back(effect->get_name().c_str());
557 return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size);
562 call.set_state(msg.
state);
570 call.set_red(msg.
red);
571 call.set_green(msg.
green);
572 call.set_blue(msg.
blue);
575 call.set_white(msg.
white);
594 return this->send_message_smart_(sensor, SensorStateResponse::MESSAGE_TYPE, SensorStateResponse::ESTIMATED_SIZE);
600 resp.
state = sensor->state;
602 return fill_and_encode_entity_state(sensor, resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size);
613 return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size);
619 return this->send_message_smart_(a_switch, SwitchStateResponse::MESSAGE_TYPE, SwitchStateResponse::ESTIMATED_SIZE);
625 resp.
state = a_switch->state;
626 return fill_and_encode_entity_state(a_switch, resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size);
634 return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size);
642 a_switch->turn_off();
647#ifdef USE_TEXT_SENSOR
649 return this->send_message_smart_(text_sensor, TextSensorStateResponse::MESSAGE_TYPE,
650 TextSensorStateResponse::ESTIMATED_SIZE);
658 return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size);
664 return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
671 return this->send_message_smart_(climate, ClimateStateResponse::MESSAGE_TYPE, ClimateStateResponse::ESTIMATED_SIZE);
676 auto traits = climate->get_traits();
688 if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
690 if (!traits.get_supported_custom_fan_modes().empty() && climate->has_custom_fan_mode()) {
693 if (traits.get_supports_presets() && climate->preset.has_value()) {
696 if (!traits.get_supported_custom_presets().empty() && climate->has_custom_preset()) {
699 if (traits.get_supports_swing_modes())
705 return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size);
710 auto traits = climate->get_traits();
732 return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size);
762 return this->send_message_smart_(number, NumberStateResponse::MESSAGE_TYPE, NumberStateResponse::ESTIMATED_SIZE);
768 resp.
state = number->state;
770 return fill_and_encode_entity_state(number, resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size);
778 msg.
device_class = number->traits.get_device_class_ref();
779 msg.
min_value = number->traits.get_min_value();
780 msg.
max_value = number->traits.get_max_value();
781 msg.
step = number->traits.get_step();
782 return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size);
786 call.set_value(msg.
state);
791#ifdef USE_DATETIME_DATE
793 return this->send_message_smart_(date, DateStateResponse::MESSAGE_TYPE, DateStateResponse::ESTIMATED_SIZE);
799 resp.
year = date->year;
800 resp.
month = date->month;
801 resp.
day = date->day;
802 return fill_and_encode_entity_state(date, resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size);
807 return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size);
816#ifdef USE_DATETIME_TIME
818 return this->send_message_smart_(time, TimeStateResponse::MESSAGE_TYPE, TimeStateResponse::ESTIMATED_SIZE);
824 resp.
hour = time->hour;
825 resp.
minute = time->minute;
826 resp.
second = time->second;
827 return fill_and_encode_entity_state(time, resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size);
832 return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size);
841#ifdef USE_DATETIME_DATETIME
843 return this->send_message_smart_(datetime, DateTimeStateResponse::MESSAGE_TYPE,
844 DateTimeStateResponse::ESTIMATED_SIZE);
850 if (datetime->has_state()) {
854 return fill_and_encode_entity_state(datetime, resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size);
859 return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size);
870 return this->send_message_smart_(text, TextStateResponse::MESSAGE_TYPE, TextStateResponse::ESTIMATED_SIZE);
874 auto *text =
static_cast<text::Text *
>(entity);
878 return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size);
882 auto *text =
static_cast<text::Text *
>(entity);
885 msg.
min_length = text->traits.get_min_length();
886 msg.
max_length = text->traits.get_max_length();
887 msg.
pattern = text->traits.get_pattern_ref();
888 return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size);
891 ENTITY_COMMAND_MAKE_CALL(
text::Text, text, text)
899 return this->send_message_smart_(select, SelectStateResponse::MESSAGE_TYPE, SelectStateResponse::ESTIMATED_SIZE);
905 resp.
state = select->current_option();
907 return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size);
913 msg.
options = &select->traits.get_options();
914 return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size);
928 return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size);
938 return this->send_message_smart_(a_lock, LockStateResponse::MESSAGE_TYPE, LockStateResponse::ESTIMATED_SIZE);
942 auto *a_lock =
static_cast<lock::Lock *
>(entity);
945 return fill_and_encode_entity_state(a_lock, resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size);
949 auto *a_lock =
static_cast<lock::Lock *
>(entity);
954 return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size);
960 case enums::LOCK_UNLOCK:
963 case enums::LOCK_LOCK:
966 case enums::LOCK_OPEN:
975 return this->send_message_smart_(valve, ValveStateResponse::MESSAGE_TYPE, ValveStateResponse::ESTIMATED_SIZE);
982 return fill_and_encode_entity_state(valve, resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size);
987 auto traits = valve->get_traits();
992 return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size);
999 call.set_command_stop();
1004#ifdef USE_MEDIA_PLAYER
1006 return this->send_message_smart_(media_player, MediaPlayerStateResponse::MESSAGE_TYPE,
1007 MediaPlayerStateResponse::ESTIMATED_SIZE);
1014 : media_player->state;
1016 resp.
volume = media_player->volume;
1017 resp.
muted = media_player->is_muted();
1018 return fill_and_encode_entity_state(media_player, resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size);
1023 auto traits = media_player->get_traits();
1026 for (
auto &supported_format : traits.get_supported_formats()) {
1029 media_format.format =
StringRef(supported_format.format);
1030 media_format.sample_rate = supported_format.sample_rate;
1031 media_format.num_channels = supported_format.num_channels;
1033 media_format.sample_bytes = supported_format.sample_bytes;
1035 return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
1044 call.set_volume(msg.
volume);
1057void APIConnection::try_send_camera_image_() {
1058 if (!this->image_reader_)
1062 while (this->image_reader_->available()) {
1063 if (!this->helper_->can_write_without_blocking())
1066 uint32_t to_send = std::min((
size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
1067 bool done = this->image_reader_->available() == to_send;
1071 msg.
set_data(this->image_reader_->peek_data_buffer(), to_send);
1077 if (!this->send_message_impl(msg, CameraImageResponse::MESSAGE_TYPE)) {
1080 this->image_reader_->consume_data(to_send);
1082 this->image_reader_->return_image();
1087void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
1088 if (!this->flags_.state_subscription)
1090 if (!this->image_reader_)
1092 if (this->image_reader_->available())
1095 this->image_reader_->set_image(std::move(image));
1097 this->try_send_camera_image_();
1103 return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size);
1114 App.scheduler.set_timeout(this->parent_,
"api_camera_stop_stream", CAMERA_STOP_STREAM,
1120#ifdef USE_HOMEASSISTANT_TIME
1124#ifdef USE_TIME_TIMEZONE
1130 if (pt.std_offset_seconds != 0 || pt.dst_start.type != enums::DST_RULE_TYPE_NONE) {
1135 tz.
dst_start.
day =
static_cast<uint16_t
>(pt.dst_start.day);
1138 tz.
dst_start.
week =
static_cast<uint8_t
>(pt.dst_start.week);
1141 tz.
dst_end.
day =
static_cast<uint16_t
>(pt.dst_end.day);
1143 tz.
dst_end.
month =
static_cast<uint8_t
>(pt.dst_end.month);
1144 tz.
dst_end.
week =
static_cast<uint8_t
>(pt.dst_end.week);
1156#ifdef USE_BLUETOOTH_PROXY
1157void APIConnection::on_subscribe_bluetooth_le_advertisements_request(
1161void APIConnection::on_unsubscribe_bluetooth_le_advertisements_request() {
1187bool APIConnection::send_subscribe_bluetooth_connections_free_response_() {
1191void APIConnection::on_subscribe_bluetooth_connections_free_request() {
1192 if (!this->send_subscribe_bluetooth_connections_free_response_()) {
1193 this->on_fatal_error();
1199 msg.
mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1203#ifdef USE_VOICE_ASSISTANT
1204bool APIConnection::check_voice_assistant_api_connection_()
const {
1215 if (!this->check_voice_assistant_api_connection_()) {
1223 if (msg.
port == 0) {
1229 this->helper_->getpeername((
struct sockaddr *) &storage, &
len);
1234 if (this->check_voice_assistant_api_connection_()) {
1239 if (this->check_voice_assistant_api_connection_()) {
1244 if (this->check_voice_assistant_api_connection_()) {
1250 if (this->check_voice_assistant_api_connection_()) {
1257 if (!this->check_voice_assistant_api_connection_()) {
1258 return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
1262 for (
auto &wake_word : config.available_wake_words) {
1265 resp_wake_word.id =
StringRef(wake_word.id);
1266 resp_wake_word.wake_word =
StringRef(wake_word.wake_word);
1267 for (
const auto &lang : wake_word.trained_languages) {
1268 resp_wake_word.trained_languages.push_back(lang);
1274 if (wake_word.model_type !=
"micro") {
1281 resp_wake_word.id =
StringRef(wake_word.id);
1282 resp_wake_word.wake_word =
StringRef(wake_word.wake_word);
1283 for (
const auto &lang : wake_word.trained_languages) {
1284 resp_wake_word.trained_languages.push_back(lang);
1290 return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
1293 if (!this->send_voice_assistant_get_configuration_response_(msg)) {
1294 this->on_fatal_error();
1299 if (this->check_voice_assistant_api_connection_()) {
1305#ifdef USE_ZWAVE_PROXY
1315#ifdef USE_ALARM_CONTROL_PANEL
1317 return this->send_message_smart_(a_alarm_control_panel, AlarmControlPanelStateResponse::MESSAGE_TYPE,
1318 AlarmControlPanelStateResponse::ESTIMATED_SIZE);
1321 uint32_t remaining_size) {
1325 return fill_and_encode_entity_state(a_alarm_control_panel, resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn,
1329 uint32_t remaining_size) {
1333 msg.
requires_code = a_alarm_control_panel->get_requires_code();
1335 return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
1336 conn, remaining_size);
1341 case enums::ALARM_CONTROL_PANEL_DISARM:
1344 case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
1347 case enums::ALARM_CONTROL_PANEL_ARM_HOME:
1350 case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
1353 case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
1354 call.arm_vacation();
1356 case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
1357 call.arm_custom_bypass();
1359 case enums::ALARM_CONTROL_PANEL_TRIGGER:
1368#ifdef USE_WATER_HEATER
1370 return this->send_message_smart_(water_heater, WaterHeaterStateResponse::MESSAGE_TYPE,
1371 WaterHeaterStateResponse::ESTIMATED_SIZE);
1381 resp.
state = wh->get_state();
1383 return fill_and_encode_entity_state(wh, resp, WaterHeaterStateResponse::MESSAGE_TYPE, conn, remaining_size);
1388 auto traits = wh->get_traits();
1394 return fill_and_encode_entity_info(wh, msg, ListEntitiesWaterHeaterResponse::MESSAGE_TYPE, conn, remaining_size);
1399 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_MODE)
1401 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE)
1403 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_LOW)
1405 if (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_TARGET_TEMPERATURE_HIGH)
1407 if ((msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_AWAY_STATE) ||
1408 (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_STATE)) {
1411 if ((msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_ON_STATE) ||
1412 (msg.
has_fields & enums::WATER_HEATER_COMMAND_HAS_STATE)) {
1423 this->send_message_smart_(event, EventResponse::MESSAGE_TYPE, EventResponse::ESTIMATED_SIZE,
1427 uint32_t remaining_size) {
1430 return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size);
1438 return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size);
1456 this->send_message(msg, InfraredRFReceiveEvent::MESSAGE_TYPE);
1465 return fill_and_encode_entity_info(infrared, msg, ListEntitiesInfraredResponse::MESSAGE_TYPE, conn, remaining_size);
1471 return this->send_message_smart_(update, UpdateStateResponse::MESSAGE_TYPE, UpdateStateResponse::ESTIMATED_SIZE);
1477 if (update->has_state()) {
1479 if (update->update_info.has_progress) {
1481 resp.
progress = update->update_info.progress;
1489 return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size);
1495 return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size);
1501 case enums::UPDATE_COMMAND_UPDATE:
1504 case enums::UPDATE_COMMAND_CHECK:
1507 case enums::UPDATE_COMMAND_NONE:
1508 ESP_LOGE(TAG,
"UPDATE_COMMAND_NONE not handled; confirm command is correct");
1511 ESP_LOGW(TAG,
"Unknown update command: %" PRIu32, msg.
command);
1517bool APIConnection::try_send_log_message(
int level,
const char *tag,
const char *line,
size_t message_len) {
1520 msg.
set_message(
reinterpret_cast<const uint8_t *
>(line), message_len);
1521 return this->send_message_impl(msg, SubscribeLogsResponse::MESSAGE_TYPE);
1524void APIConnection::complete_authentication_() {
1526 if (this->flags_.connection_state ==
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED)) {
1530 this->flags_.connection_state =
static_cast<uint8_t
>(ConnectionState::AUTHENTICATED);
1533 this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR(
"connected"));
1534#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1536 char peername[socket::SOCKADDR_STR_LEN];
1537 this->parent_->get_client_connected_trigger()->trigger(std::string(this->helper_->get_client_name()),
1538 std::string(this->helper_->get_peername_to(peername)));
1541#ifdef USE_HOMEASSISTANT_TIME
1543 this->send_time_request();
1546#ifdef USE_ZWAVE_PROXY
1558 char peername[socket::SOCKADDR_STR_LEN];
1559 ESP_LOGV(TAG,
"Hello from client: '%s' | %s | API Version %" PRIu16
".%" PRIu16, this->helper_->get_client_name(),
1560 this->helper_->get_peername_to(peername), this->client_api_version_major_, this->client_api_version_minor_);
1563 if (!this->client_supports_api_version(1, 14)) {
1564 ESP_LOGW(TAG,
"'%s' using outdated API %" PRIu16
".%" PRIu16
", update to 1.14+", this->helper_->get_client_name(),
1565 this->client_api_version_major_, this->client_api_version_minor_);
1576 this->complete_authentication_();
1578 return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
1581bool APIConnection::send_ping_response_() {
1583 return this->send_message(resp, PingResponse::MESSAGE_TYPE);
1586bool APIConnection::send_device_info_response_() {
1594 char mac_address[18];
1598 resp.mac_address =
StringRef(mac_address);
1600 resp.esphome_version = ESPHOME_VERSION_REF;
1605 resp.compilation_time =
StringRef(build_time_str);
1608#if defined(USE_ESP8266) || defined(USE_ESP32)
1609#define ESPHOME_MANUFACTURER "Espressif"
1610#elif defined(USE_RP2040)
1611#define ESPHOME_MANUFACTURER "Raspberry Pi"
1612#elif defined(USE_BK72XX)
1613#define ESPHOME_MANUFACTURER "Beken"
1614#elif defined(USE_LN882X)
1615#define ESPHOME_MANUFACTURER "Lightning"
1616#elif defined(USE_NRF52)
1617#define ESPHOME_MANUFACTURER "Nordic Semiconductor"
1618#elif defined(USE_RTL87XX)
1619#define ESPHOME_MANUFACTURER "Realtek"
1620#elif defined(USE_HOST)
1621#define ESPHOME_MANUFACTURER "Host"
1626 static const char MANUFACTURER_PROGMEM[]
PROGMEM = ESPHOME_MANUFACTURER;
1627 char manufacturer_buf[
sizeof(MANUFACTURER_PROGMEM)];
1628 memcpy_P(manufacturer_buf, MANUFACTURER_PROGMEM,
sizeof(MANUFACTURER_PROGMEM));
1629 resp.manufacturer =
StringRef(manufacturer_buf,
sizeof(MANUFACTURER_PROGMEM) - 1);
1632 resp.manufacturer = MANUFACTURER;
1634#undef ESPHOME_MANUFACTURER
1637 static const char MODEL_PROGMEM[]
PROGMEM = ESPHOME_BOARD;
1638 char model_buf[
sizeof(MODEL_PROGMEM)];
1639 memcpy_P(model_buf, MODEL_PROGMEM,
sizeof(MODEL_PROGMEM));
1640 resp.model =
StringRef(model_buf,
sizeof(MODEL_PROGMEM) - 1);
1645#ifdef USE_DEEP_SLEEP
1648#ifdef ESPHOME_PROJECT_NAME
1650 static const char PROJECT_NAME_PROGMEM[]
PROGMEM = ESPHOME_PROJECT_NAME;
1651 static const char PROJECT_VERSION_PROGMEM[]
PROGMEM = ESPHOME_PROJECT_VERSION;
1652 char project_name_buf[
sizeof(PROJECT_NAME_PROGMEM)];
1653 char project_version_buf[
sizeof(PROJECT_VERSION_PROGMEM)];
1654 memcpy_P(project_name_buf, PROJECT_NAME_PROGMEM,
sizeof(PROJECT_NAME_PROGMEM));
1655 memcpy_P(project_version_buf, PROJECT_VERSION_PROGMEM,
sizeof(PROJECT_VERSION_PROGMEM));
1656 resp.project_name =
StringRef(project_name_buf,
sizeof(PROJECT_NAME_PROGMEM) - 1);
1657 resp.project_version =
StringRef(project_version_buf,
sizeof(PROJECT_VERSION_PROGMEM) - 1);
1661 resp.project_name = PROJECT_NAME;
1662 resp.project_version = PROJECT_VERSION;
1666 resp.webserver_port = USE_WEBSERVER_PORT;
1668#ifdef USE_BLUETOOTH_PROXY
1671 char bluetooth_mac[18];
1673 resp.bluetooth_mac_address =
StringRef(bluetooth_mac);
1675#ifdef USE_VOICE_ASSISTANT
1678#ifdef USE_ZWAVE_PROXY
1683 resp.api_encryption_supported =
true;
1686 size_t device_index = 0;
1688 if (device_index >= ESPHOME_DEVICE_COUNT)
1690 auto &device_info = resp.devices[device_index++];
1691 device_info.device_id = device->get_device_id();
1692 device_info.name =
StringRef(device->get_name());
1693 device_info.area_id = device->get_area_id();
1697 size_t area_index = 0;
1699 if (area_index >= ESPHOME_AREA_COUNT)
1701 auto &area_info = resp.areas[area_index++];
1702 area_info.area_id = area->get_area_id();
1703 area_info.name =
StringRef(area->get_name());
1707 return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE);
1710 if (!this->send_hello_response_(msg)) {
1711 this->on_fatal_error();
1714void APIConnection::on_disconnect_request() {
1715 if (!this->send_disconnect_response_()) {
1716 this->on_fatal_error();
1719void APIConnection::on_ping_request() {
1720 if (!this->send_ping_response_()) {
1721 this->on_fatal_error();
1724void APIConnection::on_device_info_request() {
1725 if (!this->send_device_info_response_()) {
1726 this->on_fatal_error();
1730#ifdef USE_API_HOMEASSISTANT_STATES
1746 for (
auto &it : this->parent_->get_state_subs()) {
1757 it.callback(msg.
state);
1761#ifdef USE_API_USER_DEFINED_ACTIONS
1769 if (!arg.string_.empty()) {
1770 const_cast<char *
>(arg.string_.c_str())[arg.string_.size()] =
'\0';
1774#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
1777 uint32_t action_call_id = 0;
1779 action_call_id = this->parent_->register_active_action_call(msg.
call_id,
this);
1782 for (
auto *service : this->parent_->get_user_services()) {
1783 if (service->execute_service(msg, action_call_id)) {
1788 for (
auto *service : this->parent_->get_user_services()) {
1789 if (service->execute_service(msg)) {
1795 ESP_LOGV(TAG,
"Could not find service");
1801#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
1802void APIConnection::send_execute_service_response(uint32_t call_id,
bool success,
StringRef error_message) {
1807 this->send_message(resp, ExecuteServiceResponse::MESSAGE_TYPE);
1809#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
1810void APIConnection::send_execute_service_response(uint32_t call_id,
bool success,
StringRef error_message,
1811 const uint8_t *response_data,
size_t response_data_len) {
1818 this->send_message(resp, ExecuteServiceResponse::MESSAGE_TYPE);
1824#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
1826#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
1844 if (this->parent_->clear_noise_psk(
true)) {
1847 ESP_LOGW(TAG,
"Failed to clear encryption key");
1850 ESP_LOGW(TAG,
"Invalid encryption key length");
1851 }
else if (!this->parent_->save_noise_psk(psk,
true)) {
1852 ESP_LOGW(TAG,
"Failed to save encryption key");
1857 return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
1860 if (!this->send_noise_encryption_set_key_response_(msg)) {
1861 this->on_fatal_error();
1865#ifdef USE_API_HOMEASSISTANT_STATES
1866void APIConnection::on_subscribe_home_assistant_states_request() { state_subs_at_ = 0; }
1868bool APIConnection::try_to_clear_buffer(
bool log_out_of_space) {
1869 if (this->flags_.remove)
1871 if (this->helper_->can_write_without_blocking())
1874 APIError err = this->helper_->loop();
1875 if (err != APIError::OK) {
1876 this->fatal_error_with_log_(LOG_STR(
"Socket operation failed"), err);
1879 if (this->helper_->can_write_without_blocking())
1881 if (log_out_of_space) {
1882 ESP_LOGV(TAG,
"Cannot send message because of TCP buffer space");
1886bool APIConnection::send_message_impl(
const ProtoMessage &msg, uint8_t message_type) {
1888 std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
1889 this->prepare_first_message_buffer(shared_buf,
payload_size);
1890 size_t write_start = shared_buf.size();
1897 const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);
1899 if (!this->try_to_clear_buffer(!is_log_message)) {
1904 this->helper_->set_nodelay_for_message(is_log_message);
1906 APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
1907 if (err == APIError::WOULD_BLOCK)
1909 if (err != APIError::OK) {
1910 this->fatal_error_with_log_(LOG_STR(
"Packet write failed"), err);
1916void APIConnection::on_no_setup_connection() {
1917 this->on_fatal_error();
1918 this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR(
"no connection setup"));
1920void APIConnection::on_fatal_error() {
1923 this->flags_.remove =
true;
1926void __attribute__((flatten)) APIConnection::DeferredBatch::push_item(
const BatchItem &item) { items.push_back(item); }
1928void APIConnection::DeferredBatch::add_item(
EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
1929 uint8_t aux_data_index) {
1935 if (message_type != EventResponse::MESSAGE_TYPE)
1938 for (
const auto &item : items) {
1939 if (item.entity == entity && item.message_type == message_type)
1944 this->push_item({entity, message_type, estimated_size, aux_data_index});
1947void APIConnection::DeferredBatch::add_item_front(
EntityBase *entity, uint8_t message_type, uint8_t estimated_size) {
1952 this->push_item({entity, message_type, estimated_size, AUX_DATA_UNUSED});
1953 if (items.size() > 1) {
1955 std::swap(items.front(), items.back());
1959bool APIConnection::send_message_smart_(
EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
1960 uint8_t aux_data_index) {
1961 if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
1962 auto &shared_buf = this->parent_->get_shared_buffer_ref();
1963 this->prepare_first_message_buffer(shared_buf, estimated_size);
1965 if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE,
true) &&
1967#ifdef HAS_PROTO_MESSAGE_DUMP
1968 this->log_batch_item_(item);
1973 return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
1976bool APIConnection::schedule_batch_() {
1977 if (!this->flags_.batch_scheduled) {
1978 this->flags_.batch_scheduled =
true;
1984void APIConnection::process_batch_() {
1985 if (this->deferred_batch_.empty()) {
1986 this->flags_.batch_scheduled =
false;
1991 if (!this->try_to_clear_buffer(
true)) {
1997 auto &shared_buf = this->parent_->get_shared_buffer_ref();
1998 size_t num_items = this->deferred_batch_.size();
2001 const uint8_t header_padding = this->helper_->frame_header_padding();
2002 const uint8_t footer_size = this->helper_->frame_footer_size();
2005 uint32_t total_estimated_size = num_items * (header_padding + footer_size);
2006 for (
size_t i = 0; i < num_items; i++) {
2007 total_estimated_size += this->deferred_batch_[i].estimated_size;
2010 if (total_estimated_size > MAX_BATCH_PACKET_SIZE) {
2011 total_estimated_size = MAX_BATCH_PACKET_SIZE;
2014 this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size);
2017 if (num_items == 1) {
2018 const auto &item = this->deferred_batch_[0];
2020 uint16_t
payload_size = this->dispatch_message_(item, std::numeric_limits<uint16_t>::max(),
true);
2023#ifdef HAS_PROTO_MESSAGE_DUMP
2025 this->log_batch_item_(item);
2027 this->clear_batch_();
2030 ESP_LOGW(TAG,
"Message too large to send: type=%u", item.message_type);
2031 this->clear_batch_();
2037 this->process_batch_multi_(shared_buf, num_items, header_padding, footer_size);
2042void APIConnection::process_batch_multi_(std::vector<uint8_t> &shared_buf,
size_t num_items, uint8_t header_padding,
2043 uint8_t footer_size) {
2045 static_assert(std::is_trivially_destructible<MessageInfo>::value,
2046 "MessageInfo must remain trivially destructible with this placement-new approach");
2048 const size_t messages_to_process = std::min(num_items, MAX_MESSAGES_PER_BATCH);
2049 const uint8_t frame_overhead = header_padding + footer_size;
2054 size_t items_processed = 0;
2055 uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
2060 uint32_t current_offset = 0;
2063 for (
size_t i = 0; i < messages_to_process; i++) {
2064 const auto &item = this->deferred_batch_[i];
2067 uint16_t
payload_size = this->dispatch_message_(item, remaining_size, i == 0);
2076 uint16_t proto_payload_size =
payload_size - frame_overhead;
2081 new (&message_info[items_processed++])
MessageInfo(item.message_type, current_offset, proto_payload_size);
2083 if (items_processed == 1) {
2084 remaining_size = MAX_BATCH_PACKET_SIZE;
2089 current_offset = shared_buf.size() + footer_size;
2092 if (items_processed > 0) {
2094 if (footer_size > 0) {
2095 shared_buf.resize(shared_buf.size() + footer_size);
2100 std::span<const MessageInfo>(message_info, items_processed));
2101 if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
2102 this->fatal_error_with_log_(LOG_STR(
"Batch write failed"), err);
2105#ifdef HAS_PROTO_MESSAGE_DUMP
2108 for (
size_t i = 0; i < items_processed; i++) {
2109 const auto &item = this->deferred_batch_[i];
2110 this->log_batch_item_(item);
2115 if (items_processed < this->deferred_batch_.size()) {
2116 this->deferred_batch_.remove_front(items_processed);
2117 this->schedule_batch_();
2123 this->clear_batch_();
2130 this->flags_.batch_first_message = batch_first;
2133 if (item.
message_type == EventResponse::MESSAGE_TYPE) {
2139 this, remaining_size);
2147#define CASE_STATE_INFO(entity_name, StateResp, InfoResp) \
2148 case StateResp::MESSAGE_TYPE: \
2149 func = &try_send_##entity_name##_state; \
2151 case InfoResp::MESSAGE_TYPE: \
2152 func = &try_send_##entity_name##_info; \
2154#define CASE_INFO_ONLY(entity_name, InfoResp) \
2155 case InfoResp::MESSAGE_TYPE: \
2156 func = &try_send_##entity_name##_info; \
2160#ifdef USE_BINARY_SENSOR
2181#ifdef USE_TEXT_SENSOR
2190#ifdef USE_DATETIME_DATE
2193#ifdef USE_DATETIME_TIME
2196#ifdef USE_DATETIME_DATETIME
2211#ifdef USE_MEDIA_PLAYER
2214#ifdef USE_ALARM_CONTROL_PANEL
2217#ifdef USE_WATER_HEATER
2233 case ListEntitiesDoneResponse::MESSAGE_TYPE:
2234 func = &try_send_list_info_done;
2236 case DisconnectRequest::MESSAGE_TYPE:
2237 func = &try_send_disconnect_request;
2239 case PingRequest::MESSAGE_TYPE:
2240 func = &try_send_ping_request;
2246#undef CASE_STATE_INFO
2247#undef CASE_INFO_ONLY
2249 return func(item.
entity,
this, remaining_size);
2254 return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size);
2259 return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size);
2264 return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size);
2267#ifdef USE_API_HOMEASSISTANT_STATES
2268void APIConnection::process_state_subscriptions_() {
2269 const auto &subs = this->parent_->get_state_subs();
2270 if (this->state_subs_at_ >=
static_cast<int>(subs.size())) {
2271 this->state_subs_at_ = -1;
2275 const auto &it = subs[this->state_subs_at_];
2282 resp.
once = it.once;
2283 if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
2284 this->state_subs_at_++;
2289void APIConnection::log_client_(
int level,
const LogString *
message) {
2290 char peername[socket::SOCKADDR_STR_LEN];
2291 esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT(
"%s (%s): %s"), this->helper_->get_client_name(),
2292 this->helper_->get_peername_to(peername), LOG_STR_ARG(
message));
2296 char peername[socket::SOCKADDR_STR_LEN];
2297 ESP_LOGW(TAG,
"%s (%s): %s %s errno=%d", this->helper_->get_client_name(), this->helper_->get_peername_to(peername),
const std::string & get_friendly_name() const
Get the friendly 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)
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()
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.
void begin(bool include_internal=false)
ESPDEPRECATED("object_id mangles names and all object_id methods are planned for removal " "(see https://github.com/esphome/backlog/issues/76). " "Now is the time to stop using object_id. If still needed, use get_object_id_to() " "which will remain available longer. get_object_id() will be removed in 2026.7.0", "2025.12.0") std uint32_t get_object_id_hash()
uint32_t get_device_id() const
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
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 constexpr StringRef from_lit(const CharT(&s)[N])
static StringRef from_maybe_nullptr(const char *s)
struct esphome::api::APIConnection::APIFlags flags_
std::unique_ptr< APIFrameHelper > helper_
APIConnection(std::unique_ptr< socket::Socket > socket, APIServer *parent)
uint16_t(*)(EntityBase *, APIConnection *, uint32_t remaining_size) MessageCreatorPtr
void on_button_command_request(const ButtonCommandRequest &msg) override
void log_send_message_(const char *name, const char *dump)
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
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
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
ParsedTimezone parsed_timezone
uint32_t api_version_major
uint32_t api_version_minor
uint32_t api_version_minor
uint32_t api_version_major
const uint8_t * response_data
uint16_t response_data_len
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
const std::vector< const char * > * supported_custom_presets
const climate::ClimateSwingModeMask * supported_swing_modes
float visual_max_humidity
const std::vector< const char * > * supported_custom_fan_modes
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
float visual_min_temperature
float visual_current_temperature_step
const FixedVector< const char * > * event_types
const std::vector< const char * > * supported_preset_modes
int32_t supported_speed_count
bool supports_oscillation
const FixedVector< const char * > * effects
const light::ColorModeMask * supported_color_modes
StringRef unit_of_measurement
const FixedVector< const char * > * options
int32_t accuracy_decimals
StringRef unit_of_measurement
enums::SensorStateClass state_class
float target_temperature_step
const water_heater::WaterHeaterModeMask * supported_modes
uint32_t supported_features
enums::LockCommand command
uint32_t calculated_size() const
virtual const char * message_name() const
virtual void encode(ProtoWriteBuffer &buffer) const
virtual const char * dump_to(DumpBuffer &out) const =0
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< VoiceAssistantExternalWakeWord > external_wake_words
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
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 get_bluetooth_mac_address_pretty(std::span< char, 18 > output)
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.
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.
void set_timezone(const char *tz)
Set the time zone from a POSIX TZ string.
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)
void zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type)
void send_frame(const uint8_t *data, size_t length)
uint32_t get_feature_flags() const
void api_connection_authenticated(api::APIConnection *conn)
struct @65::@66 __attribute__
const LogString * api_error_to_logstr(APIError err)
std::array< uint8_t, 32 > psk_t
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 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,...)
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.
size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len)
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.
uint8_t batch_first_message
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)
const uint8_t ESPHOME_WEBSERVER_INDEX_HTML[] PROGMEM