diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 0fde3e47af..f5f1e1893e 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -21,22 +21,40 @@ namespace api { static const char *const TAG = "api"; // APIServer +APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +APIServer::APIServer() { global_api_server = this; } + void APIServer::setup() { ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); this->setup_controller(); - socket_ = socket::socket_ip(SOCK_STREAM, 0); - if (socket_ == nullptr) { - ESP_LOGW(TAG, "Could not create socket."); + +#ifdef USE_API_NOISE + uint32_t hash = 88491486UL; + + this->noise_pref_ = global_preferences->make_preference(hash, true); + + SavedNoisePsk noise_pref_saved{}; + if (this->noise_pref_.load(&noise_pref_saved)) { + ESP_LOGD(TAG, "Loaded saved Noise PSK"); + + this->set_noise_psk(noise_pref_saved.psk); + } +#endif + + this->socket_ = socket::socket_ip(SOCK_STREAM, 0); + if (this->socket_ == nullptr) { + ESP_LOGW(TAG, "Could not create socket"); this->mark_failed(); return; } int enable = 1; - int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); if (err != 0) { ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err); // we can still continue } - err = socket_->setblocking(false); + err = this->socket_->setblocking(false); if (err != 0) { ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err); this->mark_failed(); @@ -52,14 +70,14 @@ void APIServer::setup() { return; } - err = socket_->bind((struct sockaddr *) &server, sl); + err = this->socket_->bind((struct sockaddr *) &server, sl); if (err != 0) { ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); this->mark_failed(); return; } - err = socket_->listen(4); + err = this->socket_->listen(4); if (err != 0) { ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno); this->mark_failed(); @@ -91,18 +109,19 @@ void APIServer::setup() { } #endif } + void APIServer::loop() { // Accept new clients while (true) { struct sockaddr_storage source_addr; socklen_t addr_len = sizeof(source_addr); - auto sock = socket_->accept((struct sockaddr *) &source_addr, &addr_len); + auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len); if (!sock) break; ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str()); auto *conn = new APIConnection(std::move(sock), this); - clients_.emplace_back(conn); + this->clients_.emplace_back(conn); conn->start(); } @@ -135,16 +154,19 @@ void APIServer::loop() { } } } + void APIServer::dump_config() { ESP_LOGCONFIG(TAG, "API Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, " Address: %s: %u", network::get_use_address().c_str(), this->port_); #ifdef USE_API_NOISE ESP_LOGCONFIG(TAG, " Using noise encryption: YES"); #else ESP_LOGCONFIG(TAG, " Using noise encryption: NO"); #endif } + bool APIServer::uses_password() const { return !this->password_.empty(); } + bool APIServer::check_password(const std::string &password) const { // depend only on input password length const char *a = this->password_.c_str(); @@ -173,7 +195,9 @@ bool APIServer::check_password(const std::string &password) const { return result == 0; } + void APIServer::handle_disconnect(APIConnection *conn) {} + #ifdef USE_BINARY_SENSOR void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) { if (obj->is_internal()) @@ -341,57 +365,6 @@ void APIServer::on_update(update::UpdateEntity *obj) { } #endif -float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } -void APIServer::set_port(uint16_t port) { this->port_ = port; } -APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -void APIServer::set_password(const std::string &password) { this->password_ = password; } -void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { - for (auto &client : this->clients_) { - client->send_homeassistant_service_call(call); - } -} - -APIServer::APIServer() { global_api_server = this; } -void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, - std::function f) { - this->state_subs_.push_back(HomeAssistantStateSubscription{ - .entity_id = std::move(entity_id), - .attribute = std::move(attribute), - .callback = std::move(f), - .once = false, - }); -} -void APIServer::get_home_assistant_state(std::string entity_id, optional attribute, - std::function f) { - this->state_subs_.push_back(HomeAssistantStateSubscription{ - .entity_id = std::move(entity_id), - .attribute = std::move(attribute), - .callback = std::move(f), - .once = true, - }); -}; -const std::vector &APIServer::get_state_subs() const { - return this->state_subs_; -} -uint16_t APIServer::get_port() const { return this->port_; } -void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } -#ifdef USE_HOMEASSISTANT_TIME -void APIServer::request_time() { - for (auto &client : this->clients_) { - if (!client->remove_ && client->is_authenticated()) - client->send_time_request(); - } -} -#endif -bool APIServer::is_connected() const { return !this->clients_.empty(); } -void APIServer::on_shutdown() { - for (auto &c : this->clients_) { - c->send_disconnect_request(DisconnectRequest()); - } - delay(10); -} - #ifdef USE_ALARM_CONTROL_PANEL void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) { if (obj->is_internal()) @@ -401,5 +374,85 @@ void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP } #endif +float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } + +void APIServer::set_port(uint16_t port) { this->port_ = port; } + +void APIServer::set_password(const std::string &password) { this->password_ = password; } + +void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { + for (auto &client : this->clients_) { + client->send_homeassistant_service_call(call); + } +} + +void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, + std::function f) { + this->state_subs_.push_back(HomeAssistantStateSubscription{ + .entity_id = std::move(entity_id), + .attribute = std::move(attribute), + .callback = std::move(f), + .once = false, + }); +} + +void APIServer::get_home_assistant_state(std::string entity_id, optional attribute, + std::function f) { + this->state_subs_.push_back(HomeAssistantStateSubscription{ + .entity_id = std::move(entity_id), + .attribute = std::move(attribute), + .callback = std::move(f), + .once = true, + }); +}; + +const std::vector &APIServer::get_state_subs() const { + return this->state_subs_; +} + +uint16_t APIServer::get_port() const { return this->port_; } + +void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } + +#ifdef USE_API_NOISE +void APIServer::save_noise_psk(psk_t psk, bool make_active) { + if (psk == this->noise_ctx_->get_psk()) { + ESP_LOGW(TAG, "New PSK matches old; doing nothing"); + return; + } + + SavedNoisePsk new_saved_psk{psk}; + this->noise_pref_.save(&new_saved_psk); + // ensure it's written immediately + global_preferences->sync(); + ESP_LOGD(TAG, "Noise PSK updated"); + if (make_active) { + ESP_LOGW(TAG, "Disconnecting all clients to reset connections"); + this->set_noise_psk(psk); + for (auto &c : this->clients_) { + c->send_disconnect_request(DisconnectRequest()); + } + } +} +#endif + +#ifdef USE_HOMEASSISTANT_TIME +void APIServer::request_time() { + for (auto &client : this->clients_) { + if (!client->remove_ && client->is_authenticated()) + client->send_time_request(); + } +} +#endif + +bool APIServer::is_connected() const { return !this->clients_.empty(); } + +void APIServer::on_shutdown() { + for (auto &c : this->clients_) { + c->send_disconnect_request(DisconnectRequest()); + } + delay(10); +} + } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 899eaede49..338d63e994 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -18,6 +18,12 @@ namespace esphome { namespace api { +#ifdef USE_API_NOISE +struct SavedNoisePsk { + psk_t psk; +} PACKED; // NOLINT +#endif + class APIServer : public Component, public Controller { public: APIServer(); @@ -34,6 +40,7 @@ class APIServer : public Component, public Controller { void set_reboot_timeout(uint32_t reboot_timeout); #ifdef USE_API_NOISE + void save_noise_psk(psk_t psk, bool make_active = true); void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); } std::shared_ptr get_noise_ctx() { return noise_ctx_; } #endif // USE_API_NOISE @@ -141,6 +148,7 @@ class APIServer : public Component, public Controller { #ifdef USE_API_NOISE std::shared_ptr noise_ctx_ = std::make_shared(); + ESPPreferenceObject noise_pref_; #endif // USE_API_NOISE };