diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 8a60da9f44f..14129a1f6e2 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1464,13 +1464,22 @@ void WiFiComponent::check_connecting_finished(uint32_t now) { this->release_scan_results_(); -#if defined(USE_WIFI_CONNECT_STATE_LISTENERS) && !defined(USE_ESP8266) +#ifdef USE_WIFI_CONNECT_STATE_LISTENERS // Notify listeners now that state machine has reached STA_CONNECTED // This ensures wifi.connected condition returns true in listener automations - // On ESP8266, this is handled by process_pending_callbacks_() instead. this->notify_connect_state_listeners_(); #endif +#if defined(USE_ESP8266) && defined(USE_WIFI_IP_STATE_LISTENERS) && defined(USE_WIFI_MANUAL_IP) + // On ESP8266, GOT_IP event may not fire for static IP configurations, + // so notify IP state listeners here as a fallback. + if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_manual_ip().has_value()) { + for (auto *listener : this->ip_state_listeners_) { + listener->on_ip_state(this->wifi_sta_ip_addresses(), this->get_dns_address(0), this->get_dns_address(1)); + } + } +#endif + return; } @@ -2194,7 +2203,7 @@ void WiFiComponent::release_scan_results_() { } } -#if defined(USE_WIFI_CONNECT_STATE_LISTENERS) && !defined(USE_ESP8266) +#ifdef USE_WIFI_CONNECT_STATE_LISTENERS void WiFiComponent::notify_connect_state_listeners_() { if (!this->pending_.connect_state) return; @@ -2207,7 +2216,7 @@ void WiFiComponent::notify_connect_state_listeners_() { listener->on_wifi_connect_state(StringRef(ssid, strlen(ssid)), bssid); } } -#endif // USE_WIFI_CONNECT_STATE_LISTENERS && !USE_ESP8266 +#endif // USE_WIFI_CONNECT_STATE_LISTENERS void WiFiComponent::check_roaming_(uint32_t now) { // Guard: not for hidden networks (may not appear in scan) diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index e6499e9c58e..fd705ab86ea 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -641,9 +641,8 @@ class WiFiComponent : public Component { /// Free scan results memory unless a component needs them void release_scan_results_(); -#if defined(USE_WIFI_CONNECT_STATE_LISTENERS) && !defined(USE_ESP8266) +#ifdef USE_WIFI_CONNECT_STATE_LISTENERS /// Notify connect state listeners (called after state machine reaches STA_CONNECTED) - /// On ESP8266, this is handled by process_pending_callbacks_() instead. void notify_connect_state_listeners_(); #endif @@ -729,18 +728,18 @@ class WiFiComponent : public Component { // Pending listener callbacks deferred from platform callbacks to main loop. struct { +#ifdef USE_WIFI_CONNECT_STATE_LISTENERS + // Deferred until state machine reaches STA_CONNECTED so wifi.connected + // condition returns true in listener automations. + bool connect_state : 1; +#endif #ifdef USE_ESP8266 // ESP8266 callbacks run in SDK system context with ~2KB stack where // calling arbitrary listener callbacks is unsafe. These flags defer // listener notifications to wifi_loop_() which runs with full stack. - bool connect : 1; // STA connected, notify listeners bool disconnect : 1; // STA disconnected, notify listeners bool got_ip : 1; // Got IP, notify listeners bool scan_complete : 1; // Scan complete, notify listeners -#elif defined(USE_WIFI_CONNECT_STATE_LISTENERS) - // Non-ESP8266 platforms: deferred until state machine reaches STA_CONNECTED - // so wifi.connected condition returns true in listener automations. - bool connect_state : 1; #endif } pending_{}; diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 222212c8e27..935f039efbd 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -515,8 +515,11 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { it.channel); #endif s_sta_connected = true; - // Defer listener callbacks to main loop - system context has limited stack - global_wifi_component->pending_.connect = true; +#ifdef USE_WIFI_CONNECT_STATE_LISTENERS + // Defer listener notification until state machine reaches STA_CONNECTED + // This ensures wifi.connected condition returns true in listener automations + global_wifi_component->pending_.connect_state = true; +#endif break; } case EVENT_STAMODE_DISCONNECTED: { @@ -959,24 +962,9 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddr void WiFiComponent::wifi_loop_() { this->process_pending_callbacks_(); } void WiFiComponent::process_pending_callbacks_() { - // Notify listeners for connect event (logging already done in callback) - if (this->pending_.connect) { - this->pending_.connect = false; -#ifdef USE_WIFI_CONNECT_STATE_LISTENERS - bssid_t bssid = this->wifi_bssid(); - char ssid_buf[SSID_BUFFER_SIZE]; - for (auto *listener : this->connect_state_listeners_) { - listener->on_wifi_connect_state(StringRef(this->wifi_ssid_to(ssid_buf)), bssid); - } -#endif -#if defined(USE_WIFI_IP_STATE_LISTENERS) && defined(USE_WIFI_MANUAL_IP) - if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_manual_ip().has_value()) { - for (auto *listener : this->ip_state_listeners_) { - listener->on_ip_state(this->wifi_sta_ip_addresses(), this->get_dns_address(0), this->get_dns_address(1)); - } - } -#endif - } + // Process callbacks deferred from ESP8266 SDK system context (~2KB stack) + // to main loop context (full stack). Connect state listeners are handled + // by notify_connect_state_listeners_() in the shared state machine code. // Notify listeners for disconnect event (logging already done in callback) if (this->pending_.disconnect) {