From ff6191cfd4c5128f073f7e339fa749c08c96e00b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Oct 2025 12:55:03 -1000 Subject: [PATCH] [esp32_improv] Fix state not transitioning to PROVISIONED when WiFi configured via captive portal --- .../esp32_improv/esp32_improv_component.cpp | 53 ++++++++++++------- .../esp32_improv/esp32_improv_component.h | 1 + 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index f773083890..ed709da89c 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -40,6 +40,9 @@ void ESP32ImprovComponent::setup() { #endif global_ble_server->on_disconnect([this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); }); + // Listen for WiFi connections to detect when provisioning happens via captive portal or other means + wifi::global_wifi_component->get_connect_trigger()->add_callback([this]() { this->on_wifi_connected_(); }); + // Start with loop disabled - will be enabled by start() when needed this->disable_loop(); } @@ -161,25 +164,7 @@ void ESP32ImprovComponent::loop() { case improv::STATE_PROVISIONING: { this->set_status_indicator_state_((now % 200) < 100); if (wifi::global_wifi_component->is_connected()) { - wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), - this->connecting_sta_.get_password()); - this->connecting_sta_ = {}; - this->cancel_timeout("wifi-connect-timeout"); - this->set_state_(improv::STATE_PROVISIONED); - - std::vector urls = {ESPHOME_MY_LINK}; -#ifdef USE_WEBSERVER - for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { - if (ip.is_ip4()) { - std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); - urls.push_back(webserver_url); - break; - } - } -#endif - std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); - this->send_response_(data); - this->stop(); + this->on_wifi_connected_(); } break; } @@ -392,6 +377,36 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() { wifi::global_wifi_component->clear_sta(); } +void ESP32ImprovComponent::on_wifi_connected_() { + // Handle WiFi connection, whether from Improv provisioning or external (e.g., captive portal) + if (this->state_ == improv::STATE_PROVISIONING) { + // WiFi provisioned via Improv - save credentials and send response + wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), this->connecting_sta_.get_password()); + this->connecting_sta_ = {}; + this->cancel_timeout("wifi-connect-timeout"); + + std::vector urls = {ESPHOME_MY_LINK}; +#ifdef USE_WEBSERVER + for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { + if (ip.is_ip4()) { + std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); + urls.push_back(webserver_url); + break; + } + } +#endif + std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); + this->send_response_(data); + } else if (this->is_active() && this->state_ != improv::STATE_PROVISIONED) { + // WiFi provisioned externally (e.g., captive portal) - just transition to provisioned + ESP_LOGD(TAG, "WiFi provisioned externally, transitioning to provisioned state"); + } + + // Common actions for both cases + this->set_state_(improv::STATE_PROVISIONED); + this->stop(); +} + void ESP32ImprovComponent::advertise_service_data_() { uint8_t service_data[IMPROV_SERVICE_DATA_SIZE] = {}; service_data[0] = IMPROV_PROTOCOL_ID_1; // PR diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index eb07e09dce..39c3483b2a 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -111,6 +111,7 @@ class ESP32ImprovComponent : public Component { void send_response_(std::vector &response); void process_incoming_data_(); void on_wifi_connect_timeout_(); + void on_wifi_connected_(); bool check_identify_(); void advertise_service_data_(); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG