mirror of
https://github.com/esphome/esphome.git
synced 2026-02-01 09:17:34 -07:00
Compare commits
1 Commits
task_prio
...
wifi_callb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c27835c03 |
@@ -643,7 +643,7 @@ void WiFiComponent::restart_adapter() {
|
||||
// through start_connecting() first. Without this clear, stale errors would
|
||||
// trigger spurious "failed (callback)" logs. The canonical clear location
|
||||
// is in start_connecting(); this is the only exception to that pattern.
|
||||
this->error_from_callback_ = false;
|
||||
this->error_from_callback_ = 0;
|
||||
}
|
||||
|
||||
void WiFiComponent::loop() {
|
||||
@@ -1063,7 +1063,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) {
|
||||
// This is the canonical location for clearing the flag since all connection
|
||||
// attempts go through start_connecting(). The only other clear is in
|
||||
// restart_adapter() which enters COOLDOWN without calling start_connecting().
|
||||
this->error_from_callback_ = false;
|
||||
this->error_from_callback_ = 0;
|
||||
|
||||
if (!this->wifi_sta_connect_(ap)) {
|
||||
ESP_LOGE(TAG, "wifi_sta_connect_ failed");
|
||||
@@ -1468,7 +1468,11 @@ void WiFiComponent::check_connecting_finished(uint32_t now) {
|
||||
}
|
||||
|
||||
if (this->error_from_callback_) {
|
||||
// ESP8266: logging done in callback, listeners deferred via pending_.disconnect
|
||||
// Other platforms: just log generic failure message
|
||||
#ifndef USE_ESP8266
|
||||
ESP_LOGW(TAG, "Connecting to network failed (callback)");
|
||||
#endif
|
||||
this->retry_connect();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ static constexpr int8_t WIFI_RSSI_DISCONNECTED = -127;
|
||||
/// Buffer size for SSID (IEEE 802.11 max 32 bytes + null terminator)
|
||||
static constexpr size_t SSID_BUFFER_SIZE = 33;
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
/// Special disconnect reason for authmode downgrade (CVE-2020-12638 mitigation)
|
||||
/// Not a real WiFi reason code - used internally for deferred logging
|
||||
static constexpr uint8_t WIFI_DISCONNECT_REASON_AUTHMODE_DOWNGRADE = 254;
|
||||
#endif
|
||||
|
||||
struct SavedWifiSettings {
|
||||
char ssid[33];
|
||||
char password[65];
|
||||
@@ -590,6 +596,9 @@ class WiFiComponent : public Component {
|
||||
void connect_soon_();
|
||||
|
||||
void wifi_loop_();
|
||||
#ifdef USE_ESP8266
|
||||
void process_pending_callbacks_();
|
||||
#endif
|
||||
bool wifi_mode_(optional<bool> sta, optional<bool> ap);
|
||||
bool wifi_sta_pre_setup_();
|
||||
bool wifi_apply_output_power_(float output_power);
|
||||
@@ -704,10 +713,26 @@ class WiFiComponent : public Component {
|
||||
uint8_t num_ipv6_addresses_{0};
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
// 0 = no error, non-zero = disconnect reason code from callback
|
||||
// This serves as both the error flag and stores the reason for deferred logging
|
||||
uint8_t error_from_callback_{0};
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
// Pending listener callbacks from system context (ESP8266 only)
|
||||
// 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.
|
||||
struct {
|
||||
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
|
||||
} pending_{};
|
||||
#endif
|
||||
|
||||
// Group all boolean values together
|
||||
bool has_ap_{false};
|
||||
bool handled_connected_state_{false};
|
||||
bool error_from_callback_{false};
|
||||
bool scan_done_{false};
|
||||
bool ap_setup_{false};
|
||||
bool ap_started_{false};
|
||||
|
||||
@@ -511,21 +511,8 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
|
||||
it.channel);
|
||||
#endif
|
||||
s_sta_connected = true;
|
||||
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
|
||||
for (auto *listener : global_wifi_component->connect_state_listeners_) {
|
||||
listener->on_wifi_connect_state(StringRef(it.ssid, it.ssid_len), it.bssid);
|
||||
}
|
||||
#endif
|
||||
// For static IP configurations, GOT_IP event may not fire, so notify IP listeners here
|
||||
#if defined(USE_WIFI_IP_STATE_LISTENERS) && defined(USE_WIFI_MANUAL_IP)
|
||||
if (const WiFiAP *config = global_wifi_component->get_selected_sta_();
|
||||
config && config->get_manual_ip().has_value()) {
|
||||
for (auto *listener : global_wifi_component->ip_state_listeners_) {
|
||||
listener->on_ip_state(global_wifi_component->wifi_sta_ip_addresses(),
|
||||
global_wifi_component->get_dns_address(0), global_wifi_component->get_dns_address(1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Defer listener callbacks to main loop - system context has limited stack
|
||||
global_wifi_component->pending_.connect = true;
|
||||
break;
|
||||
}
|
||||
case EVENT_STAMODE_DISCONNECTED: {
|
||||
@@ -543,17 +530,9 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
|
||||
}
|
||||
s_sta_connected = false;
|
||||
s_sta_connecting = false;
|
||||
// IMPORTANT: Set error flag BEFORE notifying listeners.
|
||||
// This ensures is_connected() returns false during listener callbacks,
|
||||
// which is critical for proper reconnection logic (e.g., roaming).
|
||||
global_wifi_component->error_from_callback_ = true;
|
||||
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
|
||||
// Notify listeners AFTER setting error flag so they see correct state
|
||||
static constexpr uint8_t EMPTY_BSSID[6] = {};
|
||||
for (auto *listener : global_wifi_component->connect_state_listeners_) {
|
||||
listener->on_wifi_connect_state(StringRef(), EMPTY_BSSID);
|
||||
}
|
||||
#endif
|
||||
// Store reason as error flag; defer listener callbacks to main loop
|
||||
global_wifi_component->error_from_callback_ = it.reason;
|
||||
global_wifi_component->pending_.disconnect = true;
|
||||
break;
|
||||
}
|
||||
case EVENT_STAMODE_AUTHMODE_CHANGE: {
|
||||
@@ -564,10 +543,8 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
|
||||
// https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors
|
||||
if (it.old_mode != AUTH_OPEN && it.new_mode == AUTH_OPEN) {
|
||||
ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting");
|
||||
// we can't call retry_connect() from this context, so disconnect immediately
|
||||
// and notify main thread with error_from_callback_
|
||||
wifi_station_disconnect();
|
||||
global_wifi_component->error_from_callback_ = true;
|
||||
global_wifi_component->error_from_callback_ = WIFI_DISCONNECT_REASON_AUTHMODE_DOWNGRADE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -578,12 +555,8 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) {
|
||||
ESP_LOGV(TAG, "static_ip=%s gateway=%s netmask=%s", network::IPAddress(&it.ip).str_to(ip_buf),
|
||||
network::IPAddress(&it.gw).str_to(gw_buf), network::IPAddress(&it.mask).str_to(mask_buf));
|
||||
s_sta_got_ip = true;
|
||||
#ifdef USE_WIFI_IP_STATE_LISTENERS
|
||||
for (auto *listener : global_wifi_component->ip_state_listeners_) {
|
||||
listener->on_ip_state(global_wifi_component->wifi_sta_ip_addresses(), global_wifi_component->get_dns_address(0),
|
||||
global_wifi_component->get_dns_address(1));
|
||||
}
|
||||
#endif
|
||||
// Defer listener callbacks to main loop - system context has limited stack
|
||||
global_wifi_component->pending_.got_ip = true;
|
||||
break;
|
||||
}
|
||||
case EVENT_STAMODE_DHCP_TIMEOUT: {
|
||||
@@ -793,11 +766,7 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) {
|
||||
ESP_LOGV(TAG, "Scan complete: %zu found, %zu stored%s", total, this->scan_result_.size(),
|
||||
needs_full ? "" : " (filtered)");
|
||||
this->scan_done_ = true;
|
||||
#ifdef USE_WIFI_SCAN_RESULTS_LISTENERS
|
||||
for (auto *listener : global_wifi_component->scan_results_listeners_) {
|
||||
listener->on_wifi_scan_results(global_wifi_component->scan_result_);
|
||||
}
|
||||
#endif
|
||||
this->pending_.scan_complete = true; // Defer listener callbacks to main loop
|
||||
}
|
||||
|
||||
#ifdef USE_WIFI_AP
|
||||
@@ -983,7 +952,59 @@ network::IPAddress WiFiComponent::wifi_gateway_ip_() {
|
||||
return network::IPAddress(&ip.gw);
|
||||
}
|
||||
network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddress(dns_getserver(num)); }
|
||||
void WiFiComponent::wifi_loop_() {}
|
||||
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
|
||||
}
|
||||
|
||||
// Notify listeners for disconnect event (logging already done in callback)
|
||||
if (this->pending_.disconnect) {
|
||||
this->pending_.disconnect = false;
|
||||
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
|
||||
static constexpr uint8_t EMPTY_BSSID[6] = {};
|
||||
for (auto *listener : this->connect_state_listeners_) {
|
||||
listener->on_wifi_connect_state(StringRef(), EMPTY_BSSID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Notify listeners for got IP event (logging already done in callback)
|
||||
if (this->pending_.got_ip) {
|
||||
this->pending_.got_ip = false;
|
||||
#ifdef USE_WIFI_IP_STATE_LISTENERS
|
||||
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
|
||||
}
|
||||
|
||||
// Notify listeners for scan complete (logging already done in callback)
|
||||
if (this->pending_.scan_complete) {
|
||||
this->pending_.scan_complete = false;
|
||||
#ifdef USE_WIFI_SCAN_RESULTS_LISTENERS
|
||||
for (auto *listener : this->scan_results_listeners_) {
|
||||
listener->on_wifi_scan_results(this->scan_result_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::wifi
|
||||
#endif
|
||||
|
||||
@@ -774,7 +774,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
}
|
||||
s_sta_connected = false;
|
||||
s_sta_connecting = false;
|
||||
error_from_callback_ = true;
|
||||
error_from_callback_ = 1;
|
||||
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
|
||||
static constexpr uint8_t EMPTY_BSSID[6] = {};
|
||||
for (auto *listener : this->connect_state_listeners_) {
|
||||
|
||||
@@ -494,7 +494,7 @@ void WiFiComponent::wifi_process_event_(LTWiFiEvent *event) {
|
||||
s_ignored_disconnect_count, get_disconnect_reason_str(it.reason));
|
||||
s_sta_state = LTWiFiSTAState::ERROR_FAILED;
|
||||
WiFi.disconnect();
|
||||
this->error_from_callback_ = true;
|
||||
this->error_from_callback_ = 1;
|
||||
// Don't break - fall through to notify listeners
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Ignoring disconnect event with empty ssid while connecting (reason=%s, count=%u)",
|
||||
@@ -520,7 +520,7 @@ void WiFiComponent::wifi_process_event_(LTWiFiEvent *event) {
|
||||
reason == WIFI_REASON_NO_AP_FOUND || reason == WIFI_REASON_ASSOC_FAIL ||
|
||||
reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
|
||||
WiFi.disconnect();
|
||||
this->error_from_callback_ = true;
|
||||
this->error_from_callback_ = 1;
|
||||
}
|
||||
|
||||
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
|
||||
@@ -539,7 +539,7 @@ void WiFiComponent::wifi_process_event_(LTWiFiEvent *event) {
|
||||
if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) {
|
||||
ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting");
|
||||
WiFi.disconnect();
|
||||
this->error_from_callback_ = true;
|
||||
this->error_from_callback_ = 1;
|
||||
s_sta_state = LTWiFiSTAState::ERROR_FAILED;
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user