diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 26195694e7..b74237ad2e 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -265,8 +265,6 @@ def _validate(config): networks = config.get(CONF_NETWORKS, []) if not networks: raise cv.Invalid("At least one network required for fast_connect!") - if len(networks) != 1: - raise cv.Invalid("Fast connect can only be used with one network!") if CONF_USE_ADDRESS not in config: use_address = CORE.name + config[CONF_DOMAIN] diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 2731dc1f3f..98f75894f4 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -91,8 +91,11 @@ void WiFiComponent::start() { } if (this->fast_connect_) { - this->selected_ap_ = this->sta_[0]; - this->load_fast_connect_settings_(); + this->trying_loaded_ap_ = this->load_fast_connect_settings_(); + if (!this->trying_loaded_ap_) { + this->ap_index_ = 0; + this->selected_ap_ = this->sta_[this->ap_index_]; + } this->start_connecting(this->selected_ap_, false); } else { this->start_scanning(); @@ -121,6 +124,14 @@ void WiFiComponent::start() { this->wifi_apply_hostname_(); } +void WiFiComponent::restart_adapter() { + ESP_LOGW(TAG, "Restarting adapter"); + this->wifi_mode_(false, {}); + delay(100); // NOLINT + this->num_retried_ = 0; + this->retry_hidden_ = false; +} + void WiFiComponent::loop() { this->wifi_loop_(); const uint32_t now = App.get_loop_component_start_time(); @@ -140,7 +151,7 @@ void WiFiComponent::loop() { this->status_set_warning("waiting to reconnect"); if (millis() - this->action_started_ > 5000) { if (this->fast_connect_ || this->retry_hidden_) { - this->start_connecting(this->sta_[0], false); + this->start_connecting(this->selected_ap_, false); } else { this->start_scanning(); } @@ -703,18 +714,30 @@ void WiFiComponent::retry_connect() { delay(10); if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_() && (this->num_retried_ > 3 || this->error_from_callback_)) { - if (this->num_retried_ > 5) { - // If retry failed for more than 5 times, let's restart STA - ESP_LOGW(TAG, "Restarting adapter"); - this->wifi_mode_(false, {}); - delay(100); // NOLINT + if (this->fast_connect_) { + if (this->trying_loaded_ap_) { + this->trying_loaded_ap_ = false; + this->ap_index_ = 0; // Retry from the first configured AP + } else if (this->ap_index_ >= this->sta_.size() - 1) { + ESP_LOGW(TAG, "No more APs to try"); + this->ap_index_ = 0; + this->restart_adapter(); + } else { + // Try next AP + this->ap_index_++; + } this->num_retried_ = 0; - this->retry_hidden_ = false; + this->selected_ap_ = this->sta_[this->ap_index_]; } else { - // Try hidden networks after 3 failed retries - ESP_LOGD(TAG, "Retrying with hidden networks"); - this->retry_hidden_ = true; - this->num_retried_++; + if (this->num_retried_ > 5) { + // If retry failed for more than 5 times, let's restart STA + this->restart_adapter(); + } else { + // Try hidden networks after 3 failed retries + ESP_LOGD(TAG, "Retrying with hidden networks"); + this->retry_hidden_ = true; + this->num_retried_++; + } } } else { this->num_retried_++; @@ -761,17 +784,22 @@ bool WiFiComponent::is_esp32_improv_active_() { #endif } -void WiFiComponent::load_fast_connect_settings_() { +bool WiFiComponent::load_fast_connect_settings_() { SavedWifiFastConnectSettings fast_connect_save{}; if (this->fast_connect_pref_.load(&fast_connect_save)) { bssid_t bssid{}; std::copy(fast_connect_save.bssid, fast_connect_save.bssid + 6, bssid.begin()); + this->ap_index_ = fast_connect_save.ap_index; + this->selected_ap_ = this->sta_[this->ap_index_]; this->selected_ap_.set_bssid(bssid); this->selected_ap_.set_channel(fast_connect_save.channel); ESP_LOGD(TAG, "Loaded fast_connect settings"); + return true; } + + return false; } void WiFiComponent::save_fast_connect_settings_() { @@ -783,6 +811,7 @@ void WiFiComponent::save_fast_connect_settings_() { memcpy(fast_connect_save.bssid, bssid.data(), 6); fast_connect_save.channel = channel; + fast_connect_save.ap_index = this->ap_index_; this->fast_connect_pref_.save(&fast_connect_save); diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 64797a5801..bbe1bbb874 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -60,6 +60,7 @@ struct SavedWifiSettings { struct SavedWifiFastConnectSettings { uint8_t bssid[6]; uint8_t channel; + int8_t ap_index; } PACKED; // NOLINT enum WiFiComponentState : uint8_t { @@ -256,6 +257,7 @@ class WiFiComponent : public Component { void setup() override; void start(); void dump_config() override; + void restart_adapter(); /// WIFI setup_priority. float get_setup_priority() const override; float get_loop_priority() const override; @@ -353,7 +355,7 @@ class WiFiComponent : public Component { bool is_captive_portal_active_(); bool is_esp32_improv_active_(); - void load_fast_connect_settings_(); + bool load_fast_connect_settings_(); void save_fast_connect_settings_(); #ifdef USE_ESP8266 @@ -400,12 +402,14 @@ class WiFiComponent : public Component { WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE}; uint8_t num_retried_{0}; + uint8_t ap_index_{0}; #if USE_NETWORK_IPV6 uint8_t num_ipv6_addresses_{0}; #endif /* USE_NETWORK_IPV6 */ // Group all boolean values together bool fast_connect_{false}; + bool trying_loaded_ap_{false}; bool retry_hidden_{false}; bool has_ap_{false}; bool handled_connected_state_{false};