ESPHome 2025.6.0
Loading...
Searching...
No Matches
api_connection.cpp
Go to the documentation of this file.
1#include "api_connection.h"
2#ifdef USE_API
3#include <cerrno>
4#include <cinttypes>
5#include <utility>
6#include <functional>
7#include <limits>
11#include "esphome/core/hal.h"
12#include "esphome/core/log.h"
14
15#ifdef USE_DEEP_SLEEP
17#endif
18#ifdef USE_HOMEASSISTANT_TIME
20#endif
21#ifdef USE_BLUETOOTH_PROXY
23#endif
24#ifdef USE_VOICE_ASSISTANT
26#endif
27
28namespace esphome {
29namespace api {
30
31static const char *const TAG = "api.connection";
32static const int ESP32_CAMERA_STOP_STREAM = 5000;
33
34APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
35 : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
36#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
37 auto noise_ctx = parent->get_noise_ctx();
38 if (noise_ctx->has_psk()) {
39 this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
40 } else {
41 this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
42 }
43#elif defined(USE_API_PLAINTEXT)
44 this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
45#elif defined(USE_API_NOISE)
46 this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
47#else
48#error "No frame helper defined"
49#endif
50}
51
52uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
53
54void APIConnection::start() {
55 this->last_traffic_ = App.get_loop_component_start_time();
56
57 // Set next_ping_retry_ to prevent immediate ping
58 // This ensures the first ping happens after the keepalive period
59 this->next_ping_retry_ = this->last_traffic_ + KEEPALIVE_TIMEOUT_MS;
60
61 APIError err = this->helper_->init();
62 if (err != APIError::OK) {
63 on_fatal_error();
64 ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
65 errno);
66 return;
67 }
68 this->client_info_ = helper_->getpeername();
69 this->client_peername_ = this->client_info_;
70 this->helper_->set_log_info(this->client_info_);
71}
72
73APIConnection::~APIConnection() {
74#ifdef USE_BLUETOOTH_PROXY
75 if (bluetooth_proxy::global_bluetooth_proxy->get_api_connection() == this) {
77 }
78#endif
79#ifdef USE_VOICE_ASSISTANT
80 if (voice_assistant::global_voice_assistant->get_api_connection() == this) {
82 }
83#endif
84}
85
86void APIConnection::loop() {
87 if (this->remove_)
88 return;
89
90 if (!network::is_connected()) {
91 // when network is disconnected force disconnect immediately
92 // don't wait for timeout
93 this->on_fatal_error();
94 ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str());
95 return;
96 }
97 if (this->next_close_) {
98 // requested a disconnect
99 this->helper_->close();
100 this->remove_ = true;
101 return;
102 }
103
104 APIError err = this->helper_->loop();
105 if (err != APIError::OK) {
106 on_fatal_error();
107 ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
108 api_error_to_str(err), errno);
109 return;
110 }
111
112 // Check if socket has data ready before attempting to read
113 if (this->helper_->is_socket_ready()) {
114 ReadPacketBuffer buffer;
115 err = this->helper_->read_packet(&buffer);
116 if (err == APIError::WOULD_BLOCK) {
117 // pass
118 } else if (err != APIError::OK) {
119 on_fatal_error();
120 if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
121 ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
122 } else if (err == APIError::CONNECTION_CLOSED) {
123 ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
124 } else {
125 ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
126 errno);
127 }
128 return;
129 } else {
130 this->last_traffic_ = App.get_loop_component_start_time();
131 // read a packet
132 if (buffer.data_len > 0) {
133 this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
134 } else {
135 this->read_message(0, buffer.type, nullptr);
136 }
137 if (this->remove_)
138 return;
139 }
140 }
141
142 // Process deferred batch if scheduled
143 if (this->deferred_batch_.batch_scheduled &&
144 App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
145 this->process_batch_();
146 }
147
148 if (!this->list_entities_iterator_.completed())
149 this->list_entities_iterator_.advance();
150 if (!this->initial_state_iterator_.completed() && this->list_entities_iterator_.completed())
151 this->initial_state_iterator_.advance();
152
153 static uint8_t max_ping_retries = 60;
154 static uint16_t ping_retry_interval = 1000;
155 const uint32_t now = App.get_loop_component_start_time();
156 if (this->sent_ping_) {
157 // Disconnect if not responded within 2.5*keepalive
158 if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
159 on_fatal_error();
160 ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str());
161 }
162 } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
163 ESP_LOGVV(TAG, "Sending keepalive PING");
164 this->sent_ping_ = this->send_message(PingRequest());
165 if (!this->sent_ping_) {
166 this->next_ping_retry_ = now + ping_retry_interval;
167 this->ping_retries_++;
168 std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
169 this->client_combined_info_.c_str(), this->ping_retries_);
170 if (this->ping_retries_ >= max_ping_retries) {
171 on_fatal_error();
172 ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
173 } else if (this->ping_retries_ >= 10) {
174 ESP_LOGW(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
175 } else {
176 ESP_LOGD(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
177 }
178 }
179 }
180
181#ifdef USE_ESP32_CAMERA
182 if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
183 uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available());
184 bool done = this->image_reader_.available() == to_send;
185 uint32_t msg_size = 0;
186 ProtoSize::add_fixed_field<4>(msg_size, 1, true);
187 // partial message size calculated manually since its a special case
188 // 1 for the data field, varint for the data size, and the data itself
189 msg_size += 1 + ProtoSize::varint(to_send) + to_send;
190 ProtoSize::add_bool_field(msg_size, 1, done);
191
192 auto buffer = this->create_buffer(msg_size);
193 // fixed32 key = 1;
194 buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
195 // bytes data = 2;
196 buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
197 // bool done = 3;
198 buffer.encode_bool(3, done);
199
200 bool success = this->send_buffer(buffer, 44);
201
202 if (success) {
203 this->image_reader_.consume_data(to_send);
204 }
205 if (success && done) {
206 this->image_reader_.return_image();
207 }
208 }
209#endif
210
211 if (state_subs_at_ != -1) {
212 const auto &subs = this->parent_->get_state_subs();
213 if (state_subs_at_ >= (int) subs.size()) {
214 state_subs_at_ = -1;
215 } else {
216 auto &it = subs[state_subs_at_];
218 resp.entity_id = it.entity_id;
219 resp.attribute = it.attribute.value();
220 resp.once = it.once;
221 if (this->send_message(resp)) {
222 state_subs_at_++;
223 }
224 }
225 }
226}
227
228std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
229 return App.get_name() + component_type + entity->get_object_id();
230}
231
232DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
233 // remote initiated disconnect_client
234 // don't close yet, we still need to send the disconnect response
235 // close will happen on next loop
236 ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str());
237 this->next_close_ = true;
239 return resp;
240}
241void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
242 this->helper_->close();
243 this->remove_ = true;
244}
245
246// Encodes a message to the buffer and returns the total number of bytes used,
247// including header and footer overhead. Returns 0 if the message doesn't fit.
248uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
249 uint32_t remaining_size, bool is_single) {
250 // Calculate size
251 uint32_t calculated_size = 0;
252 msg.calculate_size(calculated_size);
253
254 // Cache frame sizes to avoid repeated virtual calls
255 const uint8_t header_padding = conn->helper_->frame_header_padding();
256 const uint8_t footer_size = conn->helper_->frame_footer_size();
257
258 // Calculate total size with padding for buffer allocation
259 size_t total_calculated_size = calculated_size + header_padding + footer_size;
260
261 // Check if it fits
262 if (total_calculated_size > remaining_size) {
263 return 0; // Doesn't fit
264 }
265
266 // Allocate buffer space - pass payload size, allocation functions add header/footer space
267 ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
268 : conn->allocate_batch_message_buffer(calculated_size);
269
270 // Get buffer size after allocation (which includes header padding)
271 std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
272 size_t size_before_encode = shared_buf.size();
273
274 // Encode directly into buffer
275 msg.encode(buffer);
276
277 // Calculate actual encoded size (not including header that was already added)
278 size_t actual_payload_size = shared_buf.size() - size_before_encode;
279
280 // Return actual total size (header + actual payload + footer)
281 size_t actual_total_size = header_padding + actual_payload_size + footer_size;
282
283 // Verify that calculate_size() returned the correct value
284 assert(calculated_size == actual_payload_size);
285 return static_cast<uint16_t>(actual_total_size);
286}
287
288#ifdef USE_BINARY_SENSOR
289bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
290 return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
291 BinarySensorStateResponse::MESSAGE_TYPE);
292}
293void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
294 this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info,
295 ListEntitiesBinarySensorResponse::MESSAGE_TYPE);
296}
297
298uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
299 bool is_single) {
300 auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
302 resp.state = binary_sensor->state;
303 resp.missing_state = !binary_sensor->has_state();
304 fill_entity_state_base(binary_sensor, resp);
305 return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
306}
307
308uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
309 bool is_single) {
310 auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
312 msg.device_class = binary_sensor->get_device_class();
313 msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
314 msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
315 fill_entity_info_base(binary_sensor, msg);
316 return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
317}
318#endif
319
320#ifdef USE_COVER
321bool APIConnection::send_cover_state(cover::Cover *cover) {
322 return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE);
323}
324void APIConnection::send_cover_info(cover::Cover *cover) {
325 this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE);
326}
327uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
328 bool is_single) {
329 auto *cover = static_cast<cover::Cover *>(entity);
331 auto traits = cover->get_traits();
332 msg.legacy_state =
333 (cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
334 msg.position = cover->position;
335 if (traits.get_supports_tilt())
336 msg.tilt = cover->tilt;
337 msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
338 fill_entity_state_base(cover, msg);
339 return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
340}
341uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
342 bool is_single) {
343 auto *cover = static_cast<cover::Cover *>(entity);
345 auto traits = cover->get_traits();
346 msg.assumed_state = traits.get_is_assumed_state();
347 msg.supports_position = traits.get_supports_position();
348 msg.supports_tilt = traits.get_supports_tilt();
349 msg.supports_stop = traits.get_supports_stop();
350 msg.device_class = cover->get_device_class();
351 msg.unique_id = get_default_unique_id("cover", cover);
352 fill_entity_info_base(cover, msg);
353 return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
354}
355void APIConnection::cover_command(const CoverCommandRequest &msg) {
356 cover::Cover *cover = App.get_cover_by_key(msg.key);
357 if (cover == nullptr)
358 return;
359
360 auto call = cover->make_call();
361 if (msg.has_legacy_command) {
362 switch (msg.legacy_command) {
363 case enums::LEGACY_COVER_COMMAND_OPEN:
364 call.set_command_open();
365 break;
366 case enums::LEGACY_COVER_COMMAND_CLOSE:
367 call.set_command_close();
368 break;
369 case enums::LEGACY_COVER_COMMAND_STOP:
370 call.set_command_stop();
371 break;
372 }
373 }
374 if (msg.has_position)
375 call.set_position(msg.position);
376 if (msg.has_tilt)
377 call.set_tilt(msg.tilt);
378 if (msg.stop)
379 call.set_command_stop();
380 call.perform();
381}
382#endif
383
384#ifdef USE_FAN
385bool APIConnection::send_fan_state(fan::Fan *fan) {
386 return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE);
387}
388void APIConnection::send_fan_info(fan::Fan *fan) {
389 this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE);
390}
391uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
392 bool is_single) {
393 auto *fan = static_cast<fan::Fan *>(entity);
395 auto traits = fan->get_traits();
396 msg.state = fan->state;
397 if (traits.supports_oscillation())
398 msg.oscillating = fan->oscillating;
399 if (traits.supports_speed()) {
400 msg.speed_level = fan->speed;
401 }
402 if (traits.supports_direction())
403 msg.direction = static_cast<enums::FanDirection>(fan->direction);
404 if (traits.supports_preset_modes())
405 msg.preset_mode = fan->preset_mode;
406 fill_entity_state_base(fan, msg);
407 return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
408}
409uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
410 bool is_single) {
411 auto *fan = static_cast<fan::Fan *>(entity);
413 auto traits = fan->get_traits();
414 msg.supports_oscillation = traits.supports_oscillation();
415 msg.supports_speed = traits.supports_speed();
416 msg.supports_direction = traits.supports_direction();
417 msg.supported_speed_count = traits.supported_speed_count();
418 for (auto const &preset : traits.supported_preset_modes())
419 msg.supported_preset_modes.push_back(preset);
420 msg.unique_id = get_default_unique_id("fan", fan);
421 fill_entity_info_base(fan, msg);
422 return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
423}
424void APIConnection::fan_command(const FanCommandRequest &msg) {
425 fan::Fan *fan = App.get_fan_by_key(msg.key);
426 if (fan == nullptr)
427 return;
428
429 auto call = fan->make_call();
430 if (msg.has_state)
431 call.set_state(msg.state);
432 if (msg.has_oscillating)
434 if (msg.has_speed_level) {
435 // Prefer level
436 call.set_speed(msg.speed_level);
437 }
438 if (msg.has_direction)
439 call.set_direction(static_cast<fan::FanDirection>(msg.direction));
440 if (msg.has_preset_mode)
442 call.perform();
443}
444#endif
445
446#ifdef USE_LIGHT
447bool APIConnection::send_light_state(light::LightState *light) {
448 return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE);
449}
450void APIConnection::send_light_info(light::LightState *light) {
451 this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE);
452}
453uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
454 bool is_single) {
455 auto *light = static_cast<light::LightState *>(entity);
457 auto traits = light->get_traits();
458 auto values = light->remote_values;
459 auto color_mode = values.get_color_mode();
460 resp.state = values.is_on();
461 resp.color_mode = static_cast<enums::ColorMode>(color_mode);
462 resp.brightness = values.get_brightness();
463 resp.color_brightness = values.get_color_brightness();
464 resp.red = values.get_red();
465 resp.green = values.get_green();
466 resp.blue = values.get_blue();
467 resp.white = values.get_white();
468 resp.color_temperature = values.get_color_temperature();
469 resp.cold_white = values.get_cold_white();
470 resp.warm_white = values.get_warm_white();
471 if (light->supports_effects())
472 resp.effect = light->get_effect_name();
473 fill_entity_state_base(light, resp);
474 return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
475}
476uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
477 bool is_single) {
478 auto *light = static_cast<light::LightState *>(entity);
480 auto traits = light->get_traits();
481 for (auto mode : traits.get_supported_color_modes())
482 msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
483 msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
484 msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
486 msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
487 traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
489 traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
491 msg.min_mireds = traits.get_min_mireds();
492 msg.max_mireds = traits.get_max_mireds();
493 }
494 if (light->supports_effects()) {
495 msg.effects.emplace_back("None");
496 for (auto *effect : light->get_effects()) {
497 msg.effects.push_back(effect->get_name());
498 }
499 }
500 msg.unique_id = get_default_unique_id("light", light);
501 fill_entity_info_base(light, msg);
502 return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
503}
504void APIConnection::light_command(const LightCommandRequest &msg) {
506 if (light == nullptr)
507 return;
508
509 auto call = light->make_call();
510 if (msg.has_state)
511 call.set_state(msg.state);
512 if (msg.has_brightness)
513 call.set_brightness(msg.brightness);
514 if (msg.has_color_mode)
515 call.set_color_mode(static_cast<light::ColorMode>(msg.color_mode));
516 if (msg.has_color_brightness)
518 if (msg.has_rgb) {
519 call.set_red(msg.red);
520 call.set_green(msg.green);
521 call.set_blue(msg.blue);
522 }
523 if (msg.has_white)
524 call.set_white(msg.white);
525 if (msg.has_color_temperature)
527 if (msg.has_cold_white)
528 call.set_cold_white(msg.cold_white);
529 if (msg.has_warm_white)
530 call.set_warm_white(msg.warm_white);
531 if (msg.has_transition_length)
533 if (msg.has_flash_length)
535 if (msg.has_effect)
536 call.set_effect(msg.effect);
537 call.perform();
538}
539#endif
540
541#ifdef USE_SENSOR
542bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
543 return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
544}
545void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
546 this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE);
547}
548
549uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
550 bool is_single) {
551 auto *sensor = static_cast<sensor::Sensor *>(entity);
553 resp.state = sensor->state;
554 resp.missing_state = !sensor->has_state();
555 fill_entity_state_base(sensor, resp);
556 return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
557}
558
559uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
560 bool is_single) {
561 auto *sensor = static_cast<sensor::Sensor *>(entity);
563 msg.unit_of_measurement = sensor->get_unit_of_measurement();
564 msg.accuracy_decimals = sensor->get_accuracy_decimals();
565 msg.force_update = sensor->get_force_update();
566 msg.device_class = sensor->get_device_class();
567 msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
568 msg.unique_id = sensor->unique_id();
569 if (msg.unique_id.empty())
570 msg.unique_id = get_default_unique_id("sensor", sensor);
571 fill_entity_info_base(sensor, msg);
572 return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
573}
574#endif
575
576#ifdef USE_SWITCH
577bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
578 return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
579}
580void APIConnection::send_switch_info(switch_::Switch *a_switch) {
581 this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE);
582}
583
584uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
585 bool is_single) {
586 auto *a_switch = static_cast<switch_::Switch *>(entity);
588 resp.state = a_switch->state;
589 fill_entity_state_base(a_switch, resp);
590 return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
591}
592
593uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
594 bool is_single) {
595 auto *a_switch = static_cast<switch_::Switch *>(entity);
597 msg.assumed_state = a_switch->assumed_state();
598 msg.device_class = a_switch->get_device_class();
599 msg.unique_id = get_default_unique_id("switch", a_switch);
600 fill_entity_info_base(a_switch, msg);
601 return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
602}
603void APIConnection::switch_command(const SwitchCommandRequest &msg) {
604 switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
605 if (a_switch == nullptr)
606 return;
607
608 if (msg.state) {
609 a_switch->turn_on();
610 } else {
611 a_switch->turn_off();
612 }
613}
614#endif
615
616#ifdef USE_TEXT_SENSOR
617bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
618 return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state,
619 TextSensorStateResponse::MESSAGE_TYPE);
620}
621void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
622 this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info,
623 ListEntitiesTextSensorResponse::MESSAGE_TYPE);
624}
625
626uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
627 bool is_single) {
628 auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
630 resp.state = text_sensor->state;
631 resp.missing_state = !text_sensor->has_state();
632 fill_entity_state_base(text_sensor, resp);
633 return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
634}
635uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
636 bool is_single) {
637 auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
639 msg.device_class = text_sensor->get_device_class();
640 msg.unique_id = text_sensor->unique_id();
641 if (msg.unique_id.empty())
642 msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
643 fill_entity_info_base(text_sensor, msg);
644 return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
645}
646#endif
647
648#ifdef USE_CLIMATE
649bool APIConnection::send_climate_state(climate::Climate *climate) {
650 return this->schedule_message_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE);
651}
652uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
653 bool is_single) {
654 auto *climate = static_cast<climate::Climate *>(entity);
656 fill_entity_state_base(climate, resp);
657 auto traits = climate->get_traits();
658 resp.mode = static_cast<enums::ClimateMode>(climate->mode);
659 resp.action = static_cast<enums::ClimateAction>(climate->action);
660 if (traits.get_supports_current_temperature())
661 resp.current_temperature = climate->current_temperature;
662 if (traits.get_supports_two_point_target_temperature()) {
663 resp.target_temperature_low = climate->target_temperature_low;
664 resp.target_temperature_high = climate->target_temperature_high;
665 } else {
666 resp.target_temperature = climate->target_temperature;
667 }
668 if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
669 resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
670 if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
671 resp.custom_fan_mode = climate->custom_fan_mode.value();
672 if (traits.get_supports_presets() && climate->preset.has_value()) {
673 resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
674 }
675 if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
676 resp.custom_preset = climate->custom_preset.value();
677 if (traits.get_supports_swing_modes())
678 resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
679 if (traits.get_supports_current_humidity())
680 resp.current_humidity = climate->current_humidity;
681 if (traits.get_supports_target_humidity())
682 resp.target_humidity = climate->target_humidity;
683 return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
684}
685void APIConnection::send_climate_info(climate::Climate *climate) {
686 this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE);
687}
688uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
689 bool is_single) {
690 auto *climate = static_cast<climate::Climate *>(entity);
692 auto traits = climate->get_traits();
693 msg.supports_current_temperature = traits.get_supports_current_temperature();
694 msg.supports_current_humidity = traits.get_supports_current_humidity();
695 msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
696 msg.supports_target_humidity = traits.get_supports_target_humidity();
697 for (auto mode : traits.get_supported_modes())
698 msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
699 msg.visual_min_temperature = traits.get_visual_min_temperature();
700 msg.visual_max_temperature = traits.get_visual_max_temperature();
701 msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
702 msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
703 msg.visual_min_humidity = traits.get_visual_min_humidity();
704 msg.visual_max_humidity = traits.get_visual_max_humidity();
705 msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
706 msg.supports_action = traits.get_supports_action();
707 for (auto fan_mode : traits.get_supported_fan_modes())
708 msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
709 for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
711 for (auto preset : traits.get_supported_presets())
712 msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
713 for (auto const &custom_preset : traits.get_supported_custom_presets())
715 for (auto swing_mode : traits.get_supported_swing_modes())
716 msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
717 msg.unique_id = get_default_unique_id("climate", climate);
718 fill_entity_info_base(climate, msg);
719 return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
720}
721void APIConnection::climate_command(const ClimateCommandRequest &msg) {
723 if (climate == nullptr)
724 return;
725
726 auto call = climate->make_call();
727 if (msg.has_mode)
728 call.set_mode(static_cast<climate::ClimateMode>(msg.mode));
735 if (msg.has_target_humidity)
737 if (msg.has_fan_mode)
738 call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
739 if (msg.has_custom_fan_mode)
741 if (msg.has_preset)
742 call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
743 if (msg.has_custom_preset)
744 call.set_preset(msg.custom_preset);
745 if (msg.has_swing_mode)
747 call.perform();
748}
749#endif
750
751#ifdef USE_NUMBER
752bool APIConnection::send_number_state(number::Number *number) {
753 return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
754}
755void APIConnection::send_number_info(number::Number *number) {
756 this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE);
757}
758
759uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
760 bool is_single) {
761 auto *number = static_cast<number::Number *>(entity);
763 resp.state = number->state;
764 resp.missing_state = !number->has_state();
765 fill_entity_state_base(number, resp);
766 return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
767}
768
769uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
770 bool is_single) {
771 auto *number = static_cast<number::Number *>(entity);
773 msg.unit_of_measurement = number->traits.get_unit_of_measurement();
774 msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
775 msg.device_class = number->traits.get_device_class();
776 msg.min_value = number->traits.get_min_value();
777 msg.max_value = number->traits.get_max_value();
778 msg.step = number->traits.get_step();
779 msg.unique_id = get_default_unique_id("number", number);
780 fill_entity_info_base(number, msg);
781 return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
782}
783void APIConnection::number_command(const NumberCommandRequest &msg) {
785 if (number == nullptr)
786 return;
787
788 auto call = number->make_call();
789 call.set_value(msg.state);
790 call.perform();
791}
792#endif
793
794#ifdef USE_DATETIME_DATE
795bool APIConnection::send_date_state(datetime::DateEntity *date) {
796 return this->schedule_message_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE);
797}
798uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
799 bool is_single) {
800 auto *date = static_cast<datetime::DateEntity *>(entity);
802 resp.missing_state = !date->has_state();
803 resp.year = date->year;
804 resp.month = date->month;
805 resp.day = date->day;
806 fill_entity_state_base(date, resp);
807 return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
808}
809void APIConnection::send_date_info(datetime::DateEntity *date) {
810 this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE);
811}
812uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
813 bool is_single) {
814 auto *date = static_cast<datetime::DateEntity *>(entity);
816 msg.unique_id = get_default_unique_id("date", date);
817 fill_entity_info_base(date, msg);
818 return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
819}
820void APIConnection::date_command(const DateCommandRequest &msg) {
822 if (date == nullptr)
823 return;
824
825 auto call = date->make_call();
826 call.set_date(msg.year, msg.month, msg.day);
827 call.perform();
828}
829#endif
830
831#ifdef USE_DATETIME_TIME
832bool APIConnection::send_time_state(datetime::TimeEntity *time) {
833 return this->schedule_message_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE);
834}
835uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
836 bool is_single) {
837 auto *time = static_cast<datetime::TimeEntity *>(entity);
839 resp.missing_state = !time->has_state();
840 resp.hour = time->hour;
841 resp.minute = time->minute;
842 resp.second = time->second;
843 fill_entity_state_base(time, resp);
844 return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
845}
846void APIConnection::send_time_info(datetime::TimeEntity *time) {
847 this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE);
848}
849uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
850 bool is_single) {
851 auto *time = static_cast<datetime::TimeEntity *>(entity);
853 msg.unique_id = get_default_unique_id("time", time);
854 fill_entity_info_base(time, msg);
855 return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
856}
857void APIConnection::time_command(const TimeCommandRequest &msg) {
859 if (time == nullptr)
860 return;
861
862 auto call = time->make_call();
863 call.set_time(msg.hour, msg.minute, msg.second);
864 call.perform();
865}
866#endif
867
868#ifdef USE_DATETIME_DATETIME
869bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
870 return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state,
871 DateTimeStateResponse::MESSAGE_TYPE);
872}
873uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
874 bool is_single) {
875 auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
877 resp.missing_state = !datetime->has_state();
878 if (datetime->has_state()) {
879 ESPTime state = datetime->state_as_esptime();
880 resp.epoch_seconds = state.timestamp;
881 }
882 fill_entity_state_base(datetime, resp);
883 return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
884}
885void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
886 this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE);
887}
888uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
889 bool is_single) {
890 auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
892 msg.unique_id = get_default_unique_id("datetime", datetime);
893 fill_entity_info_base(datetime, msg);
894 return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
895}
896void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
898 if (datetime == nullptr)
899 return;
900
901 auto call = datetime->make_call();
902 call.set_datetime(msg.epoch_seconds);
903 call.perform();
904}
905#endif
906
907#ifdef USE_TEXT
908bool APIConnection::send_text_state(text::Text *text) {
909 return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
910}
911void APIConnection::send_text_info(text::Text *text) {
912 this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE);
913}
914
915uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
916 bool is_single) {
917 auto *text = static_cast<text::Text *>(entity);
919 resp.state = text->state;
920 resp.missing_state = !text->has_state();
921 fill_entity_state_base(text, resp);
922 return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
923}
924
925uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
926 bool is_single) {
927 auto *text = static_cast<text::Text *>(entity);
929 msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
930 msg.min_length = text->traits.get_min_length();
931 msg.max_length = text->traits.get_max_length();
932 msg.pattern = text->traits.get_pattern();
933 msg.unique_id = get_default_unique_id("text", text);
934 fill_entity_info_base(text, msg);
935 return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
936}
937void APIConnection::text_command(const TextCommandRequest &msg) {
938 text::Text *text = App.get_text_by_key(msg.key);
939 if (text == nullptr)
940 return;
941
942 auto call = text->make_call();
943 call.set_value(msg.state);
944 call.perform();
945}
946#endif
947
948#ifdef USE_SELECT
949bool APIConnection::send_select_state(select::Select *select) {
950 return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
951}
952void APIConnection::send_select_info(select::Select *select) {
953 this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE);
954}
955
956uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
957 bool is_single) {
958 auto *select = static_cast<select::Select *>(entity);
960 resp.state = select->state;
961 resp.missing_state = !select->has_state();
962 fill_entity_state_base(select, resp);
963 return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
964}
965
966uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
967 bool is_single) {
968 auto *select = static_cast<select::Select *>(entity);
970 for (const auto &option : select->traits.get_options())
971 msg.options.push_back(option);
972 msg.unique_id = get_default_unique_id("select", select);
973 fill_entity_info_base(select, msg);
974 return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
975}
976void APIConnection::select_command(const SelectCommandRequest &msg) {
978 if (select == nullptr)
979 return;
980
981 auto call = select->make_call();
982 call.set_option(msg.state);
983 call.perform();
984}
985#endif
986
987#ifdef USE_BUTTON
989 this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE);
990}
991uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
992 bool is_single) {
993 auto *button = static_cast<button::Button *>(entity);
995 msg.device_class = button->get_device_class();
996 msg.unique_id = get_default_unique_id("button", button);
997 fill_entity_info_base(button, msg);
998 return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
999}
1001 button::Button *button = App.get_button_by_key(msg.key);
1002 if (button == nullptr)
1003 return;
1004
1005 button->press();
1006}
1007#endif
1008
1009#ifdef USE_LOCK
1010bool APIConnection::send_lock_state(lock::Lock *a_lock) {
1011 return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
1012}
1013void APIConnection::send_lock_info(lock::Lock *a_lock) {
1014 this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE);
1015}
1016
1017uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1018 bool is_single) {
1019 auto *a_lock = static_cast<lock::Lock *>(entity);
1020 LockStateResponse resp;
1021 resp.state = static_cast<enums::LockState>(a_lock->state);
1022 fill_entity_state_base(a_lock, resp);
1023 return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1024}
1025
1026uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1027 bool is_single) {
1028 auto *a_lock = static_cast<lock::Lock *>(entity);
1030 msg.assumed_state = a_lock->traits.get_assumed_state();
1031 msg.supports_open = a_lock->traits.get_supports_open();
1032 msg.requires_code = a_lock->traits.get_requires_code();
1033 msg.unique_id = get_default_unique_id("lock", a_lock);
1034 fill_entity_info_base(a_lock, msg);
1035 return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1036}
1037void APIConnection::lock_command(const LockCommandRequest &msg) {
1038 lock::Lock *a_lock = App.get_lock_by_key(msg.key);
1039 if (a_lock == nullptr)
1040 return;
1041
1042 switch (msg.command) {
1043 case enums::LOCK_UNLOCK:
1044 a_lock->unlock();
1045 break;
1046 case enums::LOCK_LOCK:
1047 a_lock->lock();
1048 break;
1049 case enums::LOCK_OPEN:
1050 a_lock->open();
1051 break;
1052 }
1053}
1054#endif
1055
1056#ifdef USE_VALVE
1057bool APIConnection::send_valve_state(valve::Valve *valve) {
1058 return this->schedule_message_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE);
1059}
1060uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1061 bool is_single) {
1062 auto *valve = static_cast<valve::Valve *>(entity);
1063 ValveStateResponse resp;
1064 resp.position = valve->position;
1065 resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
1066 fill_entity_state_base(valve, resp);
1067 return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1068}
1069void APIConnection::send_valve_info(valve::Valve *valve) {
1070 this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE);
1071}
1072uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1073 bool is_single) {
1074 auto *valve = static_cast<valve::Valve *>(entity);
1076 auto traits = valve->get_traits();
1077 msg.device_class = valve->get_device_class();
1078 msg.assumed_state = traits.get_is_assumed_state();
1079 msg.supports_position = traits.get_supports_position();
1080 msg.supports_stop = traits.get_supports_stop();
1081 msg.unique_id = get_default_unique_id("valve", valve);
1082 fill_entity_info_base(valve, msg);
1083 return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1084}
1085void APIConnection::valve_command(const ValveCommandRequest &msg) {
1086 valve::Valve *valve = App.get_valve_by_key(msg.key);
1087 if (valve == nullptr)
1088 return;
1089
1090 auto call = valve->make_call();
1091 if (msg.has_position)
1092 call.set_position(msg.position);
1093 if (msg.stop)
1094 call.set_command_stop();
1095 call.perform();
1096}
1097#endif
1098
1099#ifdef USE_MEDIA_PLAYER
1100bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
1101 return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state,
1102 MediaPlayerStateResponse::MESSAGE_TYPE);
1103}
1104uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1105 bool is_single) {
1106 auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
1110 : media_player->state;
1111 resp.state = static_cast<enums::MediaPlayerState>(report_state);
1112 resp.volume = media_player->volume;
1113 resp.muted = media_player->is_muted();
1114 fill_entity_state_base(media_player, resp);
1115 return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1116}
1117void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
1118 this->schedule_message_(media_player, &APIConnection::try_send_media_player_info,
1119 ListEntitiesMediaPlayerResponse::MESSAGE_TYPE);
1120}
1121uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1122 bool is_single) {
1123 auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
1125 auto traits = media_player->get_traits();
1126 msg.supports_pause = traits.get_supports_pause();
1127 for (auto &supported_format : traits.get_supported_formats()) {
1128 MediaPlayerSupportedFormat media_format;
1129 media_format.format = supported_format.format;
1130 media_format.sample_rate = supported_format.sample_rate;
1131 media_format.num_channels = supported_format.num_channels;
1132 media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
1133 media_format.sample_bytes = supported_format.sample_bytes;
1134 msg.supported_formats.push_back(media_format);
1135 }
1136 msg.unique_id = get_default_unique_id("media_player", media_player);
1137 fill_entity_info_base(media_player, msg);
1138 return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1139}
1140void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
1142 if (media_player == nullptr)
1143 return;
1144
1145 auto call = media_player->make_call();
1146 if (msg.has_command) {
1147 call.set_command(static_cast<media_player::MediaPlayerCommand>(msg.command));
1148 }
1149 if (msg.has_volume) {
1150 call.set_volume(msg.volume);
1151 }
1152 if (msg.has_media_url) {
1153 call.set_media_url(msg.media_url);
1154 }
1155 if (msg.has_announcement) {
1156 call.set_announcement(msg.announcement);
1157 }
1158 call.perform();
1159}
1160#endif
1161
1162#ifdef USE_ESP32_CAMERA
1163void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
1164 if (!this->state_subscription_)
1165 return;
1166 if (this->image_reader_.available())
1167 return;
1168 if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
1169 image->was_requested_by(esphome::esp32_camera::IDLE))
1170 this->image_reader_.set_image(std::move(image));
1171}
1172void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
1173 this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE);
1174}
1175uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1176 bool is_single) {
1177 auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity);
1179 msg.unique_id = get_default_unique_id("camera", camera);
1180 fill_entity_info_base(camera, msg);
1181 return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1182}
1183void APIConnection::camera_image(const CameraImageRequest &msg) {
1184 if (esp32_camera::global_esp32_camera == nullptr)
1185 return;
1186
1187 if (msg.single)
1189 if (msg.stream) {
1191
1192 App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
1194 });
1195 }
1196}
1197#endif
1198
1199#ifdef USE_HOMEASSISTANT_TIME
1200void APIConnection::on_get_time_response(const GetTimeResponse &value) {
1203}
1204#endif
1205
1206#ifdef USE_BLUETOOTH_PROXY
1207void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
1209}
1210void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
1212}
1213bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
1214 if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
1216 for (auto &service : resp.service_data) {
1217 service.legacy_data.assign(service.data.begin(), service.data.end());
1218 service.data.clear();
1219 }
1220 for (auto &manufacturer_data : resp.manufacturer_data) {
1221 manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
1222 manufacturer_data.data.clear();
1223 }
1224 return this->send_message(resp);
1225 }
1226 return this->send_message(msg);
1227}
1228void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
1230}
1231void APIConnection::bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) {
1233}
1234void APIConnection::bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) {
1236}
1237void APIConnection::bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) {
1239}
1240void APIConnection::bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) {
1242}
1243void APIConnection::bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) {
1245}
1246
1247void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) {
1249}
1250
1258
1259void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
1261 msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1262}
1263#endif
1264
1265#ifdef USE_VOICE_ASSISTANT
1266void APIConnection::subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) {
1269 }
1270}
1271void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
1273 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1274 return;
1275 }
1276
1277 if (msg.error) {
1279 return;
1280 }
1281 if (msg.port == 0) {
1282 // Use API Audio
1284 } else {
1285 struct sockaddr_storage storage;
1286 socklen_t len = sizeof(storage);
1287 this->helper_->getpeername((struct sockaddr *) &storage, &len);
1289 }
1290 }
1291};
1292void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
1294 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1295 return;
1296 }
1297
1299 }
1300}
1301void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
1303 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1304 return;
1305 }
1306
1308 }
1309};
1310void APIConnection::on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) {
1312 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1313 return;
1314 }
1315
1317 }
1318};
1319
1320void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) {
1322 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1323 return;
1324 }
1325
1327 }
1328}
1329
1330VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration(
1334 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1335 return resp;
1336 }
1337
1339 for (auto &wake_word : config.available_wake_words) {
1340 VoiceAssistantWakeWord resp_wake_word;
1341 resp_wake_word.id = wake_word.id;
1342 resp_wake_word.wake_word = wake_word.wake_word;
1343 for (const auto &lang : wake_word.trained_languages) {
1344 resp_wake_word.trained_languages.push_back(lang);
1345 }
1346 resp.available_wake_words.push_back(std::move(resp_wake_word));
1347 }
1348 for (auto &wake_word_id : config.active_wake_words) {
1349 resp.active_wake_words.push_back(wake_word_id);
1350 }
1351 resp.max_active_wake_words = config.max_active_wake_words;
1352 }
1353 return resp;
1354}
1355
1356void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
1358 if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1359 return;
1360 }
1361
1363 }
1364}
1365
1366#endif
1367
1368#ifdef USE_ALARM_CONTROL_PANEL
1369bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1370 return this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
1371 AlarmControlPanelStateResponse::MESSAGE_TYPE);
1372}
1373uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
1374 uint32_t remaining_size, bool is_single) {
1375 auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
1377 resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
1378 fill_entity_state_base(a_alarm_control_panel, resp);
1379 return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1380}
1381void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1382 this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info,
1383 ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE);
1384}
1385uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
1386 uint32_t remaining_size, bool is_single) {
1387 auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
1389 msg.supported_features = a_alarm_control_panel->get_supported_features();
1390 msg.requires_code = a_alarm_control_panel->get_requires_code();
1391 msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
1392 msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
1393 fill_entity_info_base(a_alarm_control_panel, msg);
1394 return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size,
1395 is_single);
1396}
1397void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
1399 if (a_alarm_control_panel == nullptr)
1400 return;
1401
1402 auto call = a_alarm_control_panel->make_call();
1403 switch (msg.command) {
1404 case enums::ALARM_CONTROL_PANEL_DISARM:
1405 call.disarm();
1406 break;
1407 case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
1408 call.arm_away();
1409 break;
1410 case enums::ALARM_CONTROL_PANEL_ARM_HOME:
1411 call.arm_home();
1412 break;
1413 case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
1414 call.arm_night();
1415 break;
1416 case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
1417 call.arm_vacation();
1418 break;
1419 case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
1420 call.arm_custom_bypass();
1421 break;
1422 case enums::ALARM_CONTROL_PANEL_TRIGGER:
1423 call.pending();
1424 break;
1425 }
1426 call.set_code(msg.code);
1427 call.perform();
1428}
1429#endif
1430
1431#ifdef USE_EVENT
1432void APIConnection::send_event(event::Event *event, const std::string &event_type) {
1433 this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE);
1434}
1435void APIConnection::send_event_info(event::Event *event) {
1436 this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
1437}
1438uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
1439 uint32_t remaining_size, bool is_single) {
1440 EventResponse resp;
1441 resp.event_type = event_type;
1442 fill_entity_state_base(event, resp);
1443 return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1444}
1445
1446uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1447 bool is_single) {
1448 auto *event = static_cast<event::Event *>(entity);
1450 msg.device_class = event->get_device_class();
1451 for (const auto &event_type : event->get_event_types())
1452 msg.event_types.push_back(event_type);
1453 msg.unique_id = get_default_unique_id("event", event);
1454 fill_entity_info_base(event, msg);
1455 return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1456}
1457#endif
1458
1459#ifdef USE_UPDATE
1460bool APIConnection::send_update_state(update::UpdateEntity *update) {
1461 return this->schedule_message_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE);
1462}
1463uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1464 bool is_single) {
1465 auto *update = static_cast<update::UpdateEntity *>(entity);
1467 resp.missing_state = !update->has_state();
1468 if (update->has_state()) {
1470 if (update->update_info.has_progress) {
1471 resp.has_progress = true;
1472 resp.progress = update->update_info.progress;
1473 }
1474 resp.current_version = update->update_info.current_version;
1475 resp.latest_version = update->update_info.latest_version;
1476 resp.title = update->update_info.title;
1477 resp.release_summary = update->update_info.summary;
1478 resp.release_url = update->update_info.release_url;
1479 }
1480 fill_entity_state_base(update, resp);
1481 return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1482}
1483void APIConnection::send_update_info(update::UpdateEntity *update) {
1484 this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE);
1485}
1486uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1487 bool is_single) {
1488 auto *update = static_cast<update::UpdateEntity *>(entity);
1490 msg.device_class = update->get_device_class();
1491 msg.unique_id = get_default_unique_id("update", update);
1492 fill_entity_info_base(update, msg);
1493 return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1494}
1495void APIConnection::update_command(const UpdateCommandRequest &msg) {
1497 if (update == nullptr)
1498 return;
1499
1500 switch (msg.command) {
1501 case enums::UPDATE_COMMAND_UPDATE:
1502 update->perform();
1503 break;
1504 case enums::UPDATE_COMMAND_CHECK:
1505 update->check();
1506 break;
1507 case enums::UPDATE_COMMAND_NONE:
1508 ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled; confirm command is correct");
1509 break;
1510 default:
1511 ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command);
1512 break;
1513 }
1514}
1515#endif
1516
1517bool APIConnection::try_send_log_message(int level, const char *tag, const char *line) {
1518 if (this->log_subscription_ < level)
1519 return false;
1520
1521 // Pre-calculate message size to avoid reallocations
1522 const size_t line_length = strlen(line);
1523 uint32_t msg_size = 0;
1524
1525 // Add size for level field (field ID 1, varint type)
1526 // 1 byte for field tag + size of the level varint
1527 msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(level));
1528
1529 // Add size for string field (field ID 3, string type)
1530 // 1 byte for field tag + size of length varint + string length
1531 msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(line_length)) + line_length;
1532
1533 // Create a pre-sized buffer
1534 auto buffer = this->create_buffer(msg_size);
1535
1536 // Encode the message (SubscribeLogsResponse)
1537 buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
1538 buffer.encode_string(3, line, line_length); // string message = 3
1539
1540 // SubscribeLogsResponse - 29
1541 return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
1542}
1543
1544HelloResponse APIConnection::hello(const HelloRequest &msg) {
1545 this->client_info_ = msg.client_info;
1546 this->client_peername_ = this->helper_->getpeername();
1547 this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")";
1548 this->helper_->set_log_info(this->client_combined_info_);
1549 this->client_api_version_major_ = msg.api_version_major;
1550 this->client_api_version_minor_ = msg.api_version_minor;
1551 ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
1552 this->client_peername_.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
1553
1554 HelloResponse resp;
1555 resp.api_version_major = 1;
1556 resp.api_version_minor = 10;
1557 resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
1558 resp.name = App.get_name();
1559
1560 this->connection_state_ = ConnectionState::CONNECTED;
1561 return resp;
1562}
1563ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
1564 bool correct = this->parent_->check_password(msg.password);
1565
1566 ConnectResponse resp;
1567 // bool invalid_password = 1;
1568 resp.invalid_password = !correct;
1569 if (correct) {
1570 ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str());
1571 this->connection_state_ = ConnectionState::AUTHENTICATED;
1572 this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
1573#ifdef USE_HOMEASSISTANT_TIME
1575 this->send_time_request();
1576 }
1577#endif
1578 }
1579 return resp;
1580}
1581DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1582 DeviceInfoResponse resp{};
1583 resp.uses_password = this->parent_->uses_password();
1584 resp.name = App.get_name();
1585 resp.friendly_name = App.get_friendly_name();
1586 resp.suggested_area = App.get_area();
1587 resp.mac_address = get_mac_address_pretty();
1588 resp.esphome_version = ESPHOME_VERSION;
1589 resp.compilation_time = App.get_compilation_time();
1590#if defined(USE_ESP8266) || defined(USE_ESP32)
1591 resp.manufacturer = "Espressif";
1592#elif defined(USE_RP2040)
1593 resp.manufacturer = "Raspberry Pi";
1594#elif defined(USE_BK72XX)
1595 resp.manufacturer = "Beken";
1596#elif defined(USE_RTL87XX)
1597 resp.manufacturer = "Realtek";
1598#elif defined(USE_HOST)
1599 resp.manufacturer = "Host";
1600#endif
1601 resp.model = ESPHOME_BOARD;
1602#ifdef USE_DEEP_SLEEP
1603 resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
1604#endif
1605#ifdef ESPHOME_PROJECT_NAME
1606 resp.project_name = ESPHOME_PROJECT_NAME;
1607 resp.project_version = ESPHOME_PROJECT_VERSION;
1608#endif
1609#ifdef USE_WEBSERVER
1610 resp.webserver_port = USE_WEBSERVER_PORT;
1611#endif
1612#ifdef USE_BLUETOOTH_PROXY
1613 resp.legacy_bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->get_legacy_version();
1614 resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
1616#endif
1617#ifdef USE_VOICE_ASSISTANT
1618 resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
1619 resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
1620#endif
1621#ifdef USE_API_NOISE
1622 resp.api_encryption_supported = true;
1623#endif
1624 return resp;
1625}
1626void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
1627 for (auto &it : this->parent_->get_state_subs()) {
1628 if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
1629 it.callback(msg.state);
1630 }
1631 }
1632}
1633void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
1634 bool found = false;
1635 for (auto *service : this->parent_->get_user_services()) {
1636 if (service->execute_service(msg)) {
1637 found = true;
1638 }
1639 }
1640 if (!found) {
1641 ESP_LOGV(TAG, "Could not find service");
1642 }
1643}
1644#ifdef USE_API_NOISE
1645NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) {
1646 psk_t psk{};
1648 if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
1649 ESP_LOGW(TAG, "Invalid encryption key length");
1650 resp.success = false;
1651 return resp;
1652 }
1653
1654 if (!this->parent_->save_noise_psk(psk, true)) {
1655 ESP_LOGW(TAG, "Failed to save encryption key");
1656 resp.success = false;
1657 return resp;
1658 }
1659
1660 resp.success = true;
1661 return resp;
1662}
1663#endif
1664void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
1665 state_subs_at_ = 0;
1666}
1667bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1668 if (this->remove_)
1669 return false;
1670 if (this->helper_->can_write_without_blocking())
1671 return true;
1672 delay(0);
1673 APIError err = this->helper_->loop();
1674 if (err != APIError::OK) {
1675 on_fatal_error();
1676 ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
1677 api_error_to_str(err), errno);
1678 return false;
1679 }
1680 if (this->helper_->can_write_without_blocking())
1681 return true;
1682 if (log_out_of_space) {
1683 ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
1684 }
1685 return false;
1686}
1687bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
1688 if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse
1689 return false;
1690 }
1691
1692 APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
1693 if (err == APIError::WOULD_BLOCK)
1694 return false;
1695 if (err != APIError::OK) {
1696 on_fatal_error();
1697 if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1698 ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
1699 } else {
1700 ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
1701 errno);
1702 }
1703 return false;
1704 }
1705 // Do not set last_traffic_ on send
1706 return true;
1707}
1708void APIConnection::on_unauthenticated_access() {
1709 this->on_fatal_error();
1710 ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str());
1711}
1712void APIConnection::on_no_setup_connection() {
1713 this->on_fatal_error();
1714 ESP_LOGD(TAG, "%s requested access without full connection", this->client_combined_info_.c_str());
1715}
1716void APIConnection::on_fatal_error() {
1717 this->helper_->close();
1718 this->remove_ = true;
1719}
1720
1721void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
1722 // Check if we already have a message of this type for this entity
1723 // This provides deduplication per entity/message_type combination
1724 // O(n) but optimized for RAM and not performance.
1725 for (auto &item : items) {
1726 if (item.entity == entity && item.message_type == message_type) {
1727 // Update the existing item with the new creator
1728 item.creator = std::move(creator);
1729 return;
1730 }
1731 }
1732
1733 // No existing item found, add new one
1734 items.emplace_back(entity, std::move(creator), message_type);
1735}
1736
1737bool APIConnection::schedule_batch_() {
1738 if (!this->deferred_batch_.batch_scheduled) {
1739 this->deferred_batch_.batch_scheduled = true;
1740 this->deferred_batch_.batch_start_time = App.get_loop_component_start_time();
1741 }
1742 return true;
1743}
1744
1745ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
1746
1747ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
1748 ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_);
1749 this->batch_first_message_ = false;
1750 return result;
1751}
1752
1753void APIConnection::process_batch_() {
1754 if (this->deferred_batch_.empty()) {
1755 this->deferred_batch_.batch_scheduled = false;
1756 return;
1757 }
1758
1759 // Try to clear buffer first
1760 if (!this->try_to_clear_buffer(true)) {
1761 // Can't write now, we'll try again later
1762 return;
1763 }
1764
1765 size_t num_items = this->deferred_batch_.items.size();
1766
1767 // Fast path for single message - allocate exact size needed
1768 if (num_items == 1) {
1769 const auto &item = this->deferred_batch_.items[0];
1770
1771 // Let the creator calculate size and encode if it fits
1772 uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true);
1773
1774 if (payload_size > 0 &&
1775 this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
1776 this->deferred_batch_.clear();
1777 } else if (payload_size == 0) {
1778 // Message too large
1779 ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
1780 this->deferred_batch_.clear();
1781 }
1782 return;
1783 }
1784
1785 // Pre-allocate storage for packet info
1786 std::vector<PacketInfo> packet_info;
1787 packet_info.reserve(num_items);
1788
1789 // Cache these values to avoid repeated virtual calls
1790 const uint8_t header_padding = this->helper_->frame_header_padding();
1791 const uint8_t footer_size = this->helper_->frame_footer_size();
1792
1793 // Initialize buffer and tracking variables
1794 this->parent_->get_shared_buffer_ref().clear();
1795
1796 // Pre-calculate exact buffer size needed based on message types
1797 uint32_t total_estimated_size = 0;
1798 for (const auto &item : this->deferred_batch_.items) {
1799 total_estimated_size += get_estimated_message_size(item.message_type);
1800 }
1801
1802 // Calculate total overhead for all messages
1803 uint32_t total_overhead = (header_padding + footer_size) * num_items;
1804
1805 // Reserve based on estimated size (much more accurate than 24-byte worst-case)
1806 this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
1807 this->batch_first_message_ = true;
1808
1809 size_t items_processed = 0;
1810 uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
1811
1812 // Track where each message's header padding begins in the buffer
1813 // For plaintext: this is where the 6-byte header padding starts
1814 // For noise: this is where the 7-byte header padding starts
1815 // The actual message data follows after the header padding
1816 uint32_t current_offset = 0;
1817
1818 // Process items and encode directly to buffer
1819 for (const auto &item : this->deferred_batch_.items) {
1820 // Try to encode message
1821 // The creator will calculate overhead to determine if the message fits
1822 uint16_t payload_size = item.creator(item.entity, this, remaining_size, false);
1823
1824 if (payload_size == 0) {
1825 // Message won't fit, stop processing
1826 break;
1827 }
1828
1829 // Message was encoded successfully
1830 // payload_size is header_padding + actual payload size + footer_size
1831 uint16_t proto_payload_size = payload_size - header_padding - footer_size;
1832 packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
1833
1834 // Update tracking variables
1835 items_processed++;
1836 // After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation
1837 if (items_processed == 1) {
1838 remaining_size = MAX_PACKET_SIZE;
1839 }
1840 remaining_size -= payload_size;
1841 // Calculate where the next message's header padding will start
1842 // Current buffer size + footer space (that prepare_message_buffer will add for this message)
1843 current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
1844 }
1845
1846 if (items_processed == 0) {
1847 this->deferred_batch_.clear();
1848 return;
1849 }
1850
1851 // Add footer space for the last message (for Noise protocol MAC)
1852 if (footer_size > 0) {
1853 auto &shared_buf = this->parent_->get_shared_buffer_ref();
1854 shared_buf.resize(shared_buf.size() + footer_size);
1855 }
1856
1857 // Send all collected packets
1858 APIError err =
1859 this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
1860 if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1861 on_fatal_error();
1862 if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1863 ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str());
1864 } else {
1865 ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
1866 errno);
1867 }
1868 }
1869
1870 // Handle remaining items more efficiently
1871 if (items_processed < this->deferred_batch_.items.size()) {
1872 // Remove processed items from the beginning
1873 this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(),
1874 this->deferred_batch_.items.begin() + items_processed);
1875
1876 // Reschedule for remaining items
1877 this->schedule_batch_();
1878 } else {
1879 // All items processed
1880 this->deferred_batch_.clear();
1881 }
1882}
1883
1884uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1885 bool is_single) const {
1886 switch (message_type_) {
1887 case 0: // Function pointer
1888 return data_.ptr(entity, conn, remaining_size, is_single);
1889
1890#ifdef USE_EVENT
1891 case EventResponse::MESSAGE_TYPE: {
1892 auto *e = static_cast<event::Event *>(entity);
1893 return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
1894 }
1895#endif
1896
1897 default:
1898 // Should not happen, return 0 to indicate no message
1899 return 0;
1900 }
1901}
1902
1903uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1904 bool is_single) {
1906 return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1907}
1908
1909uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1910 bool is_single) {
1912 return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1913}
1914
1915uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) {
1916 // Use generated ESTIMATED_SIZE constants from each message type
1917 switch (message_type) {
1918#ifdef USE_BINARY_SENSOR
1919 case BinarySensorStateResponse::MESSAGE_TYPE:
1920 return BinarySensorStateResponse::ESTIMATED_SIZE;
1921 case ListEntitiesBinarySensorResponse::MESSAGE_TYPE:
1922 return ListEntitiesBinarySensorResponse::ESTIMATED_SIZE;
1923#endif
1924#ifdef USE_SENSOR
1925 case SensorStateResponse::MESSAGE_TYPE:
1926 return SensorStateResponse::ESTIMATED_SIZE;
1927 case ListEntitiesSensorResponse::MESSAGE_TYPE:
1928 return ListEntitiesSensorResponse::ESTIMATED_SIZE;
1929#endif
1930#ifdef USE_SWITCH
1931 case SwitchStateResponse::MESSAGE_TYPE:
1932 return SwitchStateResponse::ESTIMATED_SIZE;
1933 case ListEntitiesSwitchResponse::MESSAGE_TYPE:
1934 return ListEntitiesSwitchResponse::ESTIMATED_SIZE;
1935#endif
1936#ifdef USE_TEXT_SENSOR
1937 case TextSensorStateResponse::MESSAGE_TYPE:
1938 return TextSensorStateResponse::ESTIMATED_SIZE;
1939 case ListEntitiesTextSensorResponse::MESSAGE_TYPE:
1940 return ListEntitiesTextSensorResponse::ESTIMATED_SIZE;
1941#endif
1942#ifdef USE_NUMBER
1943 case NumberStateResponse::MESSAGE_TYPE:
1944 return NumberStateResponse::ESTIMATED_SIZE;
1945 case ListEntitiesNumberResponse::MESSAGE_TYPE:
1946 return ListEntitiesNumberResponse::ESTIMATED_SIZE;
1947#endif
1948#ifdef USE_TEXT
1949 case TextStateResponse::MESSAGE_TYPE:
1950 return TextStateResponse::ESTIMATED_SIZE;
1951 case ListEntitiesTextResponse::MESSAGE_TYPE:
1952 return ListEntitiesTextResponse::ESTIMATED_SIZE;
1953#endif
1954#ifdef USE_SELECT
1955 case SelectStateResponse::MESSAGE_TYPE:
1956 return SelectStateResponse::ESTIMATED_SIZE;
1957 case ListEntitiesSelectResponse::MESSAGE_TYPE:
1958 return ListEntitiesSelectResponse::ESTIMATED_SIZE;
1959#endif
1960#ifdef USE_LOCK
1961 case LockStateResponse::MESSAGE_TYPE:
1962 return LockStateResponse::ESTIMATED_SIZE;
1963 case ListEntitiesLockResponse::MESSAGE_TYPE:
1964 return ListEntitiesLockResponse::ESTIMATED_SIZE;
1965#endif
1966#ifdef USE_EVENT
1967 case EventResponse::MESSAGE_TYPE:
1968 return EventResponse::ESTIMATED_SIZE;
1969 case ListEntitiesEventResponse::MESSAGE_TYPE:
1970 return ListEntitiesEventResponse::ESTIMATED_SIZE;
1971#endif
1972#ifdef USE_COVER
1973 case CoverStateResponse::MESSAGE_TYPE:
1974 return CoverStateResponse::ESTIMATED_SIZE;
1975 case ListEntitiesCoverResponse::MESSAGE_TYPE:
1976 return ListEntitiesCoverResponse::ESTIMATED_SIZE;
1977#endif
1978#ifdef USE_FAN
1979 case FanStateResponse::MESSAGE_TYPE:
1980 return FanStateResponse::ESTIMATED_SIZE;
1981 case ListEntitiesFanResponse::MESSAGE_TYPE:
1982 return ListEntitiesFanResponse::ESTIMATED_SIZE;
1983#endif
1984#ifdef USE_LIGHT
1985 case LightStateResponse::MESSAGE_TYPE:
1986 return LightStateResponse::ESTIMATED_SIZE;
1987 case ListEntitiesLightResponse::MESSAGE_TYPE:
1988 return ListEntitiesLightResponse::ESTIMATED_SIZE;
1989#endif
1990#ifdef USE_CLIMATE
1991 case ClimateStateResponse::MESSAGE_TYPE:
1992 return ClimateStateResponse::ESTIMATED_SIZE;
1993 case ListEntitiesClimateResponse::MESSAGE_TYPE:
1994 return ListEntitiesClimateResponse::ESTIMATED_SIZE;
1995#endif
1996#ifdef USE_ESP32_CAMERA
1997 case ListEntitiesCameraResponse::MESSAGE_TYPE:
1998 return ListEntitiesCameraResponse::ESTIMATED_SIZE;
1999#endif
2000#ifdef USE_BUTTON
2001 case ListEntitiesButtonResponse::MESSAGE_TYPE:
2002 return ListEntitiesButtonResponse::ESTIMATED_SIZE;
2003#endif
2004#ifdef USE_MEDIA_PLAYER
2005 case MediaPlayerStateResponse::MESSAGE_TYPE:
2006 return MediaPlayerStateResponse::ESTIMATED_SIZE;
2007 case ListEntitiesMediaPlayerResponse::MESSAGE_TYPE:
2008 return ListEntitiesMediaPlayerResponse::ESTIMATED_SIZE;
2009#endif
2010#ifdef USE_ALARM_CONTROL_PANEL
2011 case AlarmControlPanelStateResponse::MESSAGE_TYPE:
2012 return AlarmControlPanelStateResponse::ESTIMATED_SIZE;
2013 case ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE:
2014 return ListEntitiesAlarmControlPanelResponse::ESTIMATED_SIZE;
2015#endif
2016#ifdef USE_DATETIME_DATE
2017 case DateStateResponse::MESSAGE_TYPE:
2018 return DateStateResponse::ESTIMATED_SIZE;
2019 case ListEntitiesDateResponse::MESSAGE_TYPE:
2020 return ListEntitiesDateResponse::ESTIMATED_SIZE;
2021#endif
2022#ifdef USE_DATETIME_TIME
2023 case TimeStateResponse::MESSAGE_TYPE:
2024 return TimeStateResponse::ESTIMATED_SIZE;
2025 case ListEntitiesTimeResponse::MESSAGE_TYPE:
2026 return ListEntitiesTimeResponse::ESTIMATED_SIZE;
2027#endif
2028#ifdef USE_DATETIME_DATETIME
2029 case DateTimeStateResponse::MESSAGE_TYPE:
2030 return DateTimeStateResponse::ESTIMATED_SIZE;
2031 case ListEntitiesDateTimeResponse::MESSAGE_TYPE:
2032 return ListEntitiesDateTimeResponse::ESTIMATED_SIZE;
2033#endif
2034#ifdef USE_VALVE
2035 case ValveStateResponse::MESSAGE_TYPE:
2036 return ValveStateResponse::ESTIMATED_SIZE;
2037 case ListEntitiesValveResponse::MESSAGE_TYPE:
2038 return ListEntitiesValveResponse::ESTIMATED_SIZE;
2039#endif
2040#ifdef USE_UPDATE
2041 case UpdateStateResponse::MESSAGE_TYPE:
2042 return UpdateStateResponse::ESTIMATED_SIZE;
2043 case ListEntitiesUpdateResponse::MESSAGE_TYPE:
2044 return ListEntitiesUpdateResponse::ESTIMATED_SIZE;
2045#endif
2046 case ListEntitiesServicesResponse::MESSAGE_TYPE:
2047 return ListEntitiesServicesResponse::ESTIMATED_SIZE;
2048 case ListEntitiesDoneResponse::MESSAGE_TYPE:
2049 return ListEntitiesDoneResponse::ESTIMATED_SIZE;
2050 case DisconnectRequest::MESSAGE_TYPE:
2051 return DisconnectRequest::ESTIMATED_SIZE;
2052 default:
2053 // Fallback for unknown message types
2054 return 24;
2055 }
2056}
2057
2058} // namespace api
2059} // namespace esphome
2060#endif
BedjetMode mode
BedJet operating mode.
datetime::DateEntity * get_date_by_key(uint32_t key, bool include_internal=false)
alarm_control_panel::AlarmControlPanel * get_alarm_control_panel_by_key(uint32_t key, bool include_internal=false)
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
text::Text * get_text_by_key(uint32_t key, bool include_internal=false)
button::Button * get_button_by_key(uint32_t key, bool include_internal=false)
std::string get_area() const
Get the area of this Application set by pre_setup().
fan::Fan * get_fan_by_key(uint32_t key, bool include_internal=false)
std::string get_compilation_time() const
light::LightState * get_light_by_key(uint32_t key, bool include_internal=false)
media_player::MediaPlayer * get_media_player_by_key(uint32_t key, bool include_internal=false)
climate::Climate * get_climate_by_key(uint32_t key, bool include_internal=false)
const std::string & get_name() const
Get the name of this Application set by pre_setup().
lock::Lock * get_lock_by_key(uint32_t key, bool include_internal=false)
switch_::Switch * get_switch_by_key(uint32_t key, bool include_internal=false)
number::Number * get_number_by_key(uint32_t key, bool include_internal=false)
datetime::TimeEntity * get_time_by_key(uint32_t key, bool include_internal=false)
update::UpdateEntity * get_update_by_key(uint32_t key, bool include_internal=false)
datetime::DateTimeEntity * get_datetime_by_key(uint32_t key, bool include_internal=false)
valve::Valve * get_valve_by_key(uint32_t key, bool include_internal=false)
select::Select * get_select_by_key(uint32_t key, bool include_internal=false)
cover::Cover * get_cover_by_key(uint32_t key, bool include_internal=false)
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
std::string get_object_id() const
void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function< void()> func)
Definition scheduler.cpp:25
AlarmControlPanelCall & set_code(const std::string &code)
AlarmControlPanelCall make_call()
Make a AlarmControlPanelCall.
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 send_button_info(button::Button *button)
void button_command(const ButtonCommandRequest &msg) override
std::shared_ptr< APINoiseContext > get_noise_ctx()
Definition api_server.h:52
std::vector< uint8_t > & get_shared_buffer_ref()
Definition api_server.h:47
enums::AlarmControlPanelStateCommand command
Definition api_pb2.h:2607
enums::AlarmControlPanelState state
Definition api_pb2.h:2588
std::vector< BluetoothServiceData > service_data
Definition api_pb2.h:1794
std::vector< BluetoothServiceData > manufacturer_data
Definition api_pb2.h:1795
enums::BluetoothScannerMode mode
Definition api_pb2.h:2299
enums::ClimateSwingMode swing_mode
Definition api_pb2.h:1364
enums::ClimateFanMode fan_mode
Definition api_pb2.h:1362
enums::ClimatePreset preset
Definition api_pb2.h:1368
enums::ClimateFanMode fan_mode
Definition api_pb2.h:1325
enums::ClimateSwingMode swing_mode
Definition api_pb2.h:1326
enums::ClimateAction action
Definition api_pb2.h:1324
enums::ClimatePreset preset
Definition api_pb2.h:1328
enums::LegacyCoverCommand legacy_command
Definition api_pb2.h:601
enums::LegacyCoverState legacy_state
Definition api_pb2.h:578
enums::CoverOperation current_operation
Definition api_pb2.h:581
enums::FanDirection direction
Definition api_pb2.h:679
enums::FanDirection direction
Definition api_pb2.h:650
enums::ColorMode color_mode
Definition api_pb2.h:730
std::vector< enums::ClimatePreset > supported_presets
Definition api_pb2.h:1293
std::vector< enums::ClimateSwingMode > supported_swing_modes
Definition api_pb2.h:1291
std::vector< enums::ClimateFanMode > supported_fan_modes
Definition api_pb2.h:1290
std::vector< enums::ClimateMode > supported_modes
Definition api_pb2.h:1284
std::vector< std::string > supported_custom_presets
Definition api_pb2.h:1294
std::vector< std::string > supported_custom_fan_modes
Definition api_pb2.h:1292
std::vector< std::string > event_types
Definition api_pb2.h:2809
std::vector< std::string > supported_preset_modes
Definition api_pb2.h:628
std::vector< std::string > effects
Definition api_pb2.h:709
std::vector< enums::ColorMode > supported_color_modes
Definition api_pb2.h:702
std::vector< MediaPlayerSupportedFormat > supported_formats
Definition api_pb2.h:1692
std::vector< std::string > options
Definition api_pb2.h:1452
enums::SensorStateClass state_class
Definition api_pb2.h:807
enums::LockCommand command
Definition api_pb2.h:1617
enums::MediaPlayerCommand command
Definition api_pb2.h:1733
enums::MediaPlayerState state
Definition api_pb2.h:1711
enums::MediaPlayerFormatPurpose purpose
Definition api_pb2.h:1672
virtual void calculate_size(uint32_t &total_size) const =0
virtual void encode(ProtoWriteBuffer buffer) const =0
static uint32_t varint(uint32_t value)
ProtoSize class for Protocol Buffer serialization size calculation.
enums::UpdateCommand command
Definition api_pb2.h:3010
enums::ValveOperation current_operation
Definition api_pb2.h:2869
std::vector< VoiceAssistantWakeWord > available_wake_words
Definition api_pb2.h:2530
std::vector< std::string > active_wake_words
Definition api_pb2.h:2531
std::vector< std::string > active_wake_words
Definition api_pb2.h:2550
std::vector< std::string > trained_languages
Definition api_pb2.h:2498
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 subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags)
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)
Base class for all buttons.
Definition button.h:29
void press()
Press this button.
Definition button.cpp:9
ClimateCall & set_target_temperature(float target_temperature)
Set the target temperature of the climate device.
Definition climate.cpp:256
ClimateCall & set_swing_mode(ClimateSwingMode swing_mode)
Set the swing mode of the climate device.
Definition climate.cpp:237
ClimateCall & set_target_temperature_low(float target_temperature_low)
Set the low point target temperature of the climate device.
Definition climate.cpp:260
ClimateCall & set_preset(ClimatePreset preset)
Set the preset of the climate device.
Definition climate.cpp:199
ClimateCall & set_fan_mode(ClimateFanMode fan_mode)
Set the fan mode of the climate device.
Definition climate.cpp:157
ClimateCall & set_target_humidity(float target_humidity)
Set the target humidity of the climate device.
Definition climate.cpp:268
ClimateCall & set_target_temperature_high(float target_temperature_high)
Set the high point target temperature of the climate device.
Definition climate.cpp:264
ClimateCall & set_mode(ClimateMode mode)
Set the mode of the climate device.
Definition climate.cpp:133
ClimateDevice - This is the base class for all climate integrations.
Definition climate.h:168
ClimateCall make_call()
Make a climate device control call, this is used to control the climate device, see the ClimateCall d...
Definition climate.cpp:479
void perform()
Perform the cover call.
Definition cover.cpp:75
CoverCall & set_position(float position)
Set the call to a certain target position.
Definition cover.cpp:67
CoverCall & set_command_stop()
Set the command to stop the cover.
Definition cover.cpp:59
CoverCall & set_tilt(float tilt)
Set the call to a certain target tilt.
Definition cover.cpp:71
Base class for all cover devices.
Definition cover.h:111
CoverCall make_call()
Construct a new cover call used to control the cover.
Definition cover.cpp:149
DateCall & set_date(uint16_t year, uint8_t month, uint8_t day)
DateTimeCall & set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
TimeCall & set_time(uint8_t hour, uint8_t minute, uint8_t second)
void request_image(CameraRequester requester)
void stop_stream(CameraRequester requester)
void start_stream(CameraRequester requester)
FanCall & set_oscillating(bool oscillating)
Definition fan.h:50
FanCall & set_direction(FanDirection direction)
Definition fan.h:66
FanCall & set_state(bool binary_state)
Definition fan.h:41
FanCall & set_preset_mode(const std::string &preset_mode)
Definition fan.h:75
FanCall make_call()
Definition fan.cpp:123
LightCall & set_color_temperature(optional< float > color_temperature)
Set the color temperature of the light in mireds for CWWW or RGBWW lights.
LightCall & set_color_brightness(optional< float > brightness)
Set the color brightness of the light from 0.0 (no color) to 1.0 (fully on)
LightCall & set_effect(optional< std::string > effect)
Set the effect of the light by its name.
LightCall & set_white(optional< float > white)
Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights.
LightCall & set_warm_white(optional< float > warm_white)
Set the warm white value of the light from 0.0 to 1.0.
LightCall & set_flash_length(optional< uint32_t > flash_length)
Start and set the flash length of this call in milliseconds.
LightCall & set_cold_white(optional< float > cold_white)
Set the cold white value of the light from 0.0 to 1.0.
LightCall & set_brightness(optional< float > brightness)
Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on)
LightCall & set_state(optional< bool > state)
Set the binary ON/OFF state of the light.
LightCall & set_color_mode(optional< ColorMode > color_mode)
Set the color mode of the light.
LightCall & set_transition_length(optional< uint32_t > transition_length)
Set the transition length of this call in milliseconds.
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition light_state.h:63
Base class for all locks.
Definition lock.h:103
void lock()
Turn this lock on.
Definition lock.cpp:30
void unlock()
Turn this lock off.
Definition lock.cpp:35
void open()
Open (unlatch) this lock.
Definition lock.cpp:40
NumberCall & set_value(float value)
Base-class for all numbers.
Definition number.h:39
NumberCall make_call()
Definition number.h:45
SelectCall & set_option(const std::string &option)
Base-class for all selects.
Definition select.h:31
SelectCall make_call()
Instantiate a SelectCall object to modify this select component's state.
Definition select.h:39
Base-class for all sensors.
Definition sensor.h:62
Base class for all switches.
Definition switch.h:39
void turn_on()
Turn this switch on.
Definition switch.cpp:11
void turn_off()
Turn this switch off.
Definition switch.cpp:15
TextCall & set_value(const std::string &value)
Definition text_call.cpp:10
Base-class for all text inputs.
Definition text.h:24
TextCall make_call()
Instantiate a TextCall object to modify this text component's state.
Definition text.h:32
ValveCall & set_position(float position)
Set the call to a certain target position.
Definition valve.cpp:67
ValveCall & set_command_stop()
Set the command to stop the valve.
Definition valve.cpp:59
void perform()
Perform the valve call.
Definition valve.cpp:71
Base class for all valve devices.
Definition valve.h:105
ValveCall make_call()
Construct a new valve call used to control the valve.
Definition valve.cpp:127
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)
void on_set_configuration(const std::vector< std::string > &active_wake_words)
ClimateSwingMode swing_mode
Definition climate.h:11
uint8_t custom_preset
Definition climate.h:9
ClimateFanMode fan_mode
Definition climate.h:3
ClimatePreset preset
Definition climate.h:8
uint8_t custom_fan_mode
Definition climate.h:4
bool state
Definition fan.h:0
uint32_t socklen_t
Definition headers.h:97
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity)
std::array< uint8_t, 32 > psk_t
const char * api_error_to_str(APIError err)
BluetoothProxy * global_bluetooth_proxy
ClimatePreset
Enum for all preset modes.
@ CLIMATE_PRESET_AWAY
Device is in away preset.
ClimateSwingMode
Enum for all modes a climate swing can be in.
ClimateMode
Enum for all modes a climate device can be in.
const float COVER_OPEN
Definition cover.cpp:9
ESP32Camera * global_esp32_camera
FanDirection
Simple enum to represent the direction of a fan.
Definition fan.h:20
HomeassistantTime * global_homeassistant_time
ColorMode
Color modes are a combination of color capabilities that can be used at the same time.
Definition color_mode.h:49
@ BRIGHTNESS
Master brightness of the light can be controlled.
@ RGB
Color can be controlled using RGB format (includes a brightness control for the color).
@ COLOR_TEMPERATURE
Color temperature can be controlled.
@ WHITE
Brightness of white channel can be controlled separately from other channels.
@ COLD_WARM_WHITE
Brightness of cold and warm white output can be controlled.
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition util.cpp:19
VoiceAssistant * global_voice_assistant
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:302
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition helpers.cpp:732
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:323
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29
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)
Definition helpers.cpp:507
A more user-friendly version of struct tm from time.h.
Definition time.h:15
std::vector< uint8_t > container
uint32_t payload_size()