ESPHome 2025.5.0
Loading...
Searching...
No Matches
api_server.cpp
Go to the documentation of this file.
1#include "api_server.h"
2#ifdef USE_API
3#include <cerrno>
4#include "api_connection.h"
8#include "esphome/core/hal.h"
9#include "esphome/core/log.h"
10#include "esphome/core/util.h"
12
13#ifdef USE_LOGGER
15#endif
16
17#include <algorithm>
18
19namespace esphome {
20namespace api {
21
22static const char *const TAG = "api";
23
24// APIServer
25APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
26
28
30 ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
31 this->setup_controller();
32
33#ifdef USE_API_NOISE
34 uint32_t hash = 88491486UL;
35
37
38 SavedNoisePsk noise_pref_saved{};
39 if (this->noise_pref_.load(&noise_pref_saved)) {
40 ESP_LOGD(TAG, "Loaded saved Noise PSK");
41
42 this->set_noise_psk(noise_pref_saved.psk);
43 }
44#endif
45
46 this->socket_ = socket::socket_ip(SOCK_STREAM, 0);
47 if (this->socket_ == nullptr) {
48 ESP_LOGW(TAG, "Could not create socket");
49 this->mark_failed();
50 return;
51 }
52 int enable = 1;
53 int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
54 if (err != 0) {
55 ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
56 // we can still continue
57 }
58 err = this->socket_->setblocking(false);
59 if (err != 0) {
60 ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
61 this->mark_failed();
62 return;
63 }
64
65 struct sockaddr_storage server;
66
67 socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
68 if (sl == 0) {
69 ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
70 this->mark_failed();
71 return;
72 }
73
74 err = this->socket_->bind((struct sockaddr *) &server, sl);
75 if (err != 0) {
76 ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
77 this->mark_failed();
78 return;
79 }
80
81 err = this->socket_->listen(4);
82 if (err != 0) {
83 ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
84 this->mark_failed();
85 return;
86 }
87
88#ifdef USE_LOGGER
89 if (logger::global_logger != nullptr) {
90 logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
91 for (auto &c : this->clients_) {
92 if (!c->remove_)
93 c->try_send_log_message(level, tag, message);
94 }
95 });
96 }
97#endif
98
99 this->last_connected_ = millis();
100
101#ifdef USE_ESP32_CAMERA
104 [this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
105 for (auto &c : this->clients_) {
106 if (!c->remove_)
107 c->set_camera_state(image);
108 }
109 });
110 }
111#endif
112}
113
115 // Accept new clients
116 while (true) {
117 struct sockaddr_storage source_addr;
118 socklen_t addr_len = sizeof(source_addr);
119 auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len);
120 if (!sock)
121 break;
122 ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
123
124 auto *conn = new APIConnection(std::move(sock), this);
125 this->clients_.emplace_back(conn);
126 conn->start();
127 }
128
129 // Process clients and remove disconnected ones in a single pass
130 if (!this->clients_.empty()) {
131 size_t client_index = 0;
132 while (client_index < this->clients_.size()) {
133 auto &client = this->clients_[client_index];
134
135 if (client->remove_) {
136 // Handle disconnection
137 this->client_disconnected_trigger_->trigger(client->client_info_, client->client_peername_);
138 ESP_LOGV(TAG, "Removing connection to %s", client->client_info_.c_str());
139
140 // Swap with the last element and pop (avoids expensive vector shifts)
141 if (client_index < this->clients_.size() - 1) {
142 std::swap(this->clients_[client_index], this->clients_.back());
143 }
144 this->clients_.pop_back();
145 // Don't increment client_index since we need to process the swapped element
146 } else {
147 // Process active client
148 client->loop();
149 client_index++; // Move to next client
150 }
151 }
152 }
153
154 if (this->reboot_timeout_ != 0) {
155 const uint32_t now = millis();
156 if (!this->is_connected()) {
157 if (now - this->last_connected_ > this->reboot_timeout_) {
158 ESP_LOGE(TAG, "No client connected to API. Rebooting...");
159 App.reboot();
160 }
161 this->status_set_warning();
162 } else {
163 this->last_connected_ = now;
164 this->status_clear_warning();
165 }
166 }
167}
168
170 ESP_LOGCONFIG(TAG, "API Server:");
171 ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
172#ifdef USE_API_NOISE
173 ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
174 if (!this->noise_ctx_->has_psk()) {
175 ESP_LOGCONFIG(TAG, " Supports noise encryption: YES");
176 }
177#else
178 ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
179#endif
180}
181
182bool APIServer::uses_password() const { return !this->password_.empty(); }
183
184bool APIServer::check_password(const std::string &password) const {
185 // depend only on input password length
186 const char *a = this->password_.c_str();
187 uint32_t len_a = this->password_.length();
188 const char *b = password.c_str();
189 uint32_t len_b = password.length();
190
191 // disable optimization with volatile
192 volatile uint32_t length = len_b;
193 volatile const char *left = nullptr;
194 volatile const char *right = b;
195 uint8_t result = 0;
196
197 if (len_a == length) {
198 left = *((volatile const char **) &a);
199 result = 0;
200 }
201 if (len_a != length) {
202 left = b;
203 result = 1;
204 }
205
206 for (size_t i = 0; i < length; i++) {
207 result |= *left++ ^ *right++; // NOLINT
208 }
209
210 return result == 0;
211}
212
214
215#ifdef USE_BINARY_SENSOR
217 if (obj->is_internal())
218 return;
219 for (auto &c : this->clients_)
220 c->send_binary_sensor_state(obj, state);
221}
222#endif
223
224#ifdef USE_COVER
226 if (obj->is_internal())
227 return;
228 for (auto &c : this->clients_)
229 c->send_cover_state(obj);
230}
231#endif
232
233#ifdef USE_FAN
235 if (obj->is_internal())
236 return;
237 for (auto &c : this->clients_)
238 c->send_fan_state(obj);
239}
240#endif
241
242#ifdef USE_LIGHT
244 if (obj->is_internal())
245 return;
246 for (auto &c : this->clients_)
247 c->send_light_state(obj);
248}
249#endif
250
251#ifdef USE_SENSOR
253 if (obj->is_internal())
254 return;
255 for (auto &c : this->clients_)
256 c->send_sensor_state(obj, state);
257}
258#endif
259
260#ifdef USE_SWITCH
262 if (obj->is_internal())
263 return;
264 for (auto &c : this->clients_)
265 c->send_switch_state(obj, state);
266}
267#endif
268
269#ifdef USE_TEXT_SENSOR
271 if (obj->is_internal())
272 return;
273 for (auto &c : this->clients_)
274 c->send_text_sensor_state(obj, state);
275}
276#endif
277
278#ifdef USE_CLIMATE
280 if (obj->is_internal())
281 return;
282 for (auto &c : this->clients_)
283 c->send_climate_state(obj);
284}
285#endif
286
287#ifdef USE_NUMBER
289 if (obj->is_internal())
290 return;
291 for (auto &c : this->clients_)
292 c->send_number_state(obj, state);
293}
294#endif
295
296#ifdef USE_DATETIME_DATE
298 if (obj->is_internal())
299 return;
300 for (auto &c : this->clients_)
301 c->send_date_state(obj);
302}
303#endif
304
305#ifdef USE_DATETIME_TIME
307 if (obj->is_internal())
308 return;
309 for (auto &c : this->clients_)
310 c->send_time_state(obj);
311}
312#endif
313
314#ifdef USE_DATETIME_DATETIME
316 if (obj->is_internal())
317 return;
318 for (auto &c : this->clients_)
319 c->send_datetime_state(obj);
320}
321#endif
322
323#ifdef USE_TEXT
324void APIServer::on_text_update(text::Text *obj, const std::string &state) {
325 if (obj->is_internal())
326 return;
327 for (auto &c : this->clients_)
328 c->send_text_state(obj, state);
329}
330#endif
331
332#ifdef USE_SELECT
333void APIServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
334 if (obj->is_internal())
335 return;
336 for (auto &c : this->clients_)
337 c->send_select_state(obj, state);
338}
339#endif
340
341#ifdef USE_LOCK
343 if (obj->is_internal())
344 return;
345 for (auto &c : this->clients_)
346 c->send_lock_state(obj, obj->state);
347}
348#endif
349
350#ifdef USE_VALVE
352 if (obj->is_internal())
353 return;
354 for (auto &c : this->clients_)
355 c->send_valve_state(obj);
356}
357#endif
358
359#ifdef USE_MEDIA_PLAYER
361 if (obj->is_internal())
362 return;
363 for (auto &c : this->clients_)
364 c->send_media_player_state(obj);
365}
366#endif
367
368#ifdef USE_EVENT
369void APIServer::on_event(event::Event *obj, const std::string &event_type) {
370 for (auto &c : this->clients_)
371 c->send_event(obj, event_type);
372}
373#endif
374
375#ifdef USE_UPDATE
377 for (auto &c : this->clients_)
378 c->send_update_state(obj);
379}
380#endif
381
382#ifdef USE_ALARM_CONTROL_PANEL
384 if (obj->is_internal())
385 return;
386 for (auto &c : this->clients_)
387 c->send_alarm_control_panel_state(obj);
388}
389#endif
390
392
393void APIServer::set_port(uint16_t port) { this->port_ = port; }
394
395void APIServer::set_password(const std::string &password) { this->password_ = password; }
396
398 for (auto &client : this->clients_) {
399 client->send_homeassistant_service_call(call);
400 }
401}
402
404 std::function<void(std::string)> f) {
406 .entity_id = std::move(entity_id),
407 .attribute = std::move(attribute),
408 .callback = std::move(f),
409 .once = false,
410 });
411}
412
413void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
414 std::function<void(std::string)> f) {
416 .entity_id = std::move(entity_id),
417 .attribute = std::move(attribute),
418 .callback = std::move(f),
419 .once = true,
420 });
421};
422
423const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
424 return this->state_subs_;
425}
426
427uint16_t APIServer::get_port() const { return this->port_; }
428
429void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
430
431#ifdef USE_API_NOISE
432bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
433 auto &old_psk = this->noise_ctx_->get_psk();
434 if (std::equal(old_psk.begin(), old_psk.end(), psk.begin())) {
435 ESP_LOGW(TAG, "New PSK matches old");
436 return true;
437 }
438
439 SavedNoisePsk new_saved_psk{psk};
440 if (!this->noise_pref_.save(&new_saved_psk)) {
441 ESP_LOGW(TAG, "Failed to save Noise PSK");
442 return false;
443 }
444 // ensure it's written immediately
445 if (!global_preferences->sync()) {
446 ESP_LOGW(TAG, "Failed to sync preferences");
447 return false;
448 }
449 ESP_LOGD(TAG, "Noise PSK saved");
450 if (make_active) {
451 this->set_timeout(100, [this, psk]() {
452 ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
453 this->set_noise_psk(psk);
454 for (auto &c : this->clients_) {
455 c->send_disconnect_request(DisconnectRequest());
456 }
457 });
458 }
459 return true;
460}
461#endif
462
463#ifdef USE_HOMEASSISTANT_TIME
465 for (auto &client : this->clients_) {
466 if (!client->remove_ && client->is_authenticated())
467 client->send_time_request();
468 }
469}
470#endif
471
472bool APIServer::is_connected() const { return !this->clients_.empty(); }
473
475 for (auto &c : this->clients_) {
476 c->send_disconnect_request(DisconnectRequest());
477 }
478 delay(10);
479}
480
481} // namespace api
482} // namespace esphome
483#endif
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.cpp:72
void setup_controller(bool include_internal=false)
Definition controller.cpp:7
bool save(const T *src)
Definition preferences.h:21
virtual bool sync()=0
Commit pending writes to flash.
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
bool is_internal() const
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition automation.h:96
void on_valve_update(valve::Valve *obj) override
std::vector< std::unique_ptr< APIConnection > > clients_
Definition api_server.h:143
void on_select_update(select::Select *obj, const std::string &state, size_t index) override
void send_homeassistant_service_call(const HomeassistantServiceResponse &call)
void set_password(const std::string &password)
void on_time_update(datetime::TimeEntity *obj) override
void on_cover_update(cover::Cover *obj) override
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override
void on_sensor_update(sensor::Sensor *obj, float state) override
void on_light_update(light::LightState *obj) override
void on_media_player_update(media_player::MediaPlayer *obj) override
void set_port(uint16_t port)
void dump_config() override
void handle_disconnect(APIConnection *conn)
void set_reboot_timeout(uint32_t reboot_timeout)
bool save_noise_psk(psk_t psk, bool make_active=true)
void on_lock_update(lock::Lock *obj) override
void setup() override
void on_date_update(datetime::DateEntity *obj) override
void on_update(update::UpdateEntity *obj) override
bool check_password(const std::string &password) const
void get_home_assistant_state(std::string entity_id, optional< std::string > attribute, std::function< void(std::string)> f)
const std::vector< HomeAssistantStateSubscription > & get_state_subs() const
std::shared_ptr< APINoiseContext > noise_ctx_
Definition api_server.h:151
void on_number_update(number::Number *obj, float state) override
void on_text_update(text::Text *obj, const std::string &state) override
Trigger< std::string, std::string > * client_disconnected_trigger_
Definition api_server.h:148
void subscribe_home_assistant_state(std::string entity_id, optional< std::string > attribute, std::function< void(std::string)> f)
void on_climate_update(climate::Climate *obj) override
ESPPreferenceObject noise_pref_
Definition api_server.h:152
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override
void on_fan_update(fan::Fan *obj) override
std::vector< HomeAssistantStateSubscription > state_subs_
Definition api_server.h:145
void on_switch_update(switch_::Switch *obj, bool state) override
uint16_t get_port() const
void set_noise_psk(psk_t psk)
Definition api_server.h:45
void on_datetime_update(datetime::DateTimeEntity *obj) override
void on_event(event::Event *obj, const std::string &event_type) override
float get_setup_priority() const override
std::unique_ptr< socket::Socket > socket_
Definition api_server.h:139
void on_shutdown() override
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override
Base class for all binary_sensor-type classes.
ClimateDevice - This is the base class for all climate integrations.
Definition climate.h:168
Base class for all cover devices.
Definition cover.h:111
void add_image_callback(std::function< void(std::shared_ptr< CameraImage >)> &&callback)
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
LockState state
The current reported state of the lock.
Definition lock.h:122
void add_on_log_callback(std::function< void(int, const char *, const char *)> &&callback)
Register a callback that will be called for every log message sent.
Definition logger.cpp:217
Base-class for all numbers.
Definition number.h:39
Base-class for all selects.
Definition select.h:31
Base-class for all sensors.
Definition sensor.h:57
Base class for all switches.
Definition switch.h:39
Base-class for all text inputs.
Definition text.h:24
Base class for all valve devices.
Definition valve.h:105
bool state
Definition fan.h:0
uint32_t socklen_t
Definition headers.h:97
APIServer * global_api_server
std::array< uint8_t, 32 > psk_t
ESP32Camera * global_esp32_camera
Logger * global_logger
Definition logger.cpp:251
std::string get_use_address()
Get the active network hostname.
Definition util.cpp:52
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition component.cpp:26
std::unique_ptr< Socket > socket_ip(int type, int protocol)
Create a socket in the newest available IP domain (IPv6 or IPv4) of the given type and protocol.
Definition socket.cpp:13
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
Definition socket.cpp:51
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
ESPPreferences * global_preferences
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
Application App
Global storage of Application pointer - only one Application can exist.
uint16_t length
Definition tt21100.cpp:0