ESPHome 2025.8.2
Loading...
Searching...
No Matches
wifi_component.cpp
Go to the documentation of this file.
1#include "wifi_component.h"
2#ifdef USE_WIFI
3#include <cinttypes>
4#include <map>
5
6#ifdef USE_ESP_IDF
7#if (ESP_IDF_VERSION_MAJOR >= 5 && ESP_IDF_VERSION_MINOR >= 1)
8#include <esp_eap_client.h>
9#else
10#include <esp_wpa2.h>
11#endif
12#endif
13
14#if defined(USE_ESP32) || defined(USE_ESP_IDF)
15#include <esp_wifi.h>
16#endif
17#ifdef USE_ESP8266
18#include <user_interface.h>
19#endif
20
21#include <algorithm>
22#include <utility>
23#include "lwip/dns.h"
24#include "lwip/err.h"
25
27#include "esphome/core/hal.h"
29#include "esphome/core/log.h"
30#include "esphome/core/util.h"
31
32#ifdef USE_CAPTIVE_PORTAL
34#endif
35
36#ifdef USE_IMPROV
38#endif
39
40namespace esphome {
41namespace wifi {
42
43static const char *const TAG = "wifi";
44
46
48 this->wifi_pre_setup_();
49 if (this->enable_on_boot_) {
50 this->start();
51 } else {
52#ifdef USE_ESP32
53 esp_netif_init();
54#endif
56 }
57}
58
60 ESP_LOGCONFIG(TAG,
61 "Starting\n"
62 " Local MAC: %s",
63 get_mac_address_pretty().c_str());
64 this->last_connected_ = millis();
65
66 uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL;
67
69 if (this->fast_connect_) {
71 }
72
73 SavedWifiSettings save{};
74 if (this->pref_.load(&save)) {
75 ESP_LOGD(TAG, "Loaded settings: %s", save.ssid);
76
77 WiFiAP sta{};
78 sta.set_ssid(save.ssid);
79 sta.set_password(save.password);
80 this->set_sta(sta);
81 }
82
83 if (this->has_sta()) {
84 this->wifi_sta_pre_setup_();
85 if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) {
86 ESP_LOGV(TAG, "Setting Output Power Option failed");
87 }
88
89 if (!this->wifi_apply_power_save_()) {
90 ESP_LOGV(TAG, "Setting Power Save Option failed");
91 }
92
93 if (this->fast_connect_) {
95 if (!this->trying_loaded_ap_) {
96 this->ap_index_ = 0;
97 this->selected_ap_ = this->sta_[this->ap_index_];
98 }
99 this->start_connecting(this->selected_ap_, false);
100 } else {
101 this->start_scanning();
102 }
103#ifdef USE_WIFI_AP
104 } else if (this->has_ap()) {
105 this->setup_ap_config_();
106 if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) {
107 ESP_LOGV(TAG, "Setting Output Power Option failed");
108 }
109#ifdef USE_CAPTIVE_PORTAL
111 this->wifi_sta_pre_setup_();
112 this->start_scanning();
114 }
115#endif
116#endif // USE_WIFI_AP
117 }
118#ifdef USE_IMPROV
119 if (!this->has_sta() && esp32_improv::global_improv_component != nullptr) {
120 if (this->wifi_mode_(true, {}))
122 }
123#endif
124 this->wifi_apply_hostname_();
125}
126
128 ESP_LOGW(TAG, "Restarting adapter");
129 this->wifi_mode_(false, {});
130 delay(100); // NOLINT
131 this->num_retried_ = 0;
132 this->retry_hidden_ = false;
133}
134
136 this->wifi_loop_();
137 const uint32_t now = App.get_loop_component_start_time();
138
139 if (this->has_sta()) {
140 if (this->is_connected() != this->handled_connected_state_) {
141 if (this->handled_connected_state_) {
143 } else {
144 this->connect_trigger_->trigger();
145 }
147 }
148
149 switch (this->state_) {
151 this->status_set_warning("waiting to reconnect");
152 if (millis() - this->action_started_ > 5000) {
153 if (this->fast_connect_ || this->retry_hidden_) {
154 if (!this->selected_ap_.get_bssid().has_value())
155 this->selected_ap_ = this->sta_[0];
156 this->start_connecting(this->selected_ap_, false);
157 } else {
158 this->start_scanning();
159 }
160 }
161 break;
162 }
164 this->status_set_warning("scanning for networks");
166 break;
167 }
170 this->status_set_warning("associating to network");
172 break;
173 }
174
176 if (!this->is_connected()) {
177 ESP_LOGW(TAG, "Connection lost; reconnecting");
179 this->retry_connect();
180 } else {
181 this->status_clear_warning();
182 this->last_connected_ = now;
183 }
184 break;
185 }
188 break;
190 return;
191 }
192
193#ifdef USE_WIFI_AP
194 if (this->has_ap() && !this->ap_setup_) {
195 if (this->ap_timeout_ != 0 && (now - this->last_connected_ > this->ap_timeout_)) {
196 ESP_LOGI(TAG, "Starting fallback AP");
197 this->setup_ap_config_();
198#ifdef USE_CAPTIVE_PORTAL
201#endif
202 }
203 }
204#endif // USE_WIFI_AP
205
206#ifdef USE_IMPROV
208 if (now - this->last_connected_ > esp32_improv::global_improv_component->get_wifi_timeout()) {
209 if (this->wifi_mode_(true, {}))
211 }
212 }
213
214#endif
215
216 if (!this->has_ap() && this->reboot_timeout_ != 0) {
217 if (now - this->last_connected_ > this->reboot_timeout_) {
218 ESP_LOGE(TAG, "Can't connect; rebooting");
219 App.reboot();
220 }
221 }
222 }
223}
224
226
227bool WiFiComponent::has_ap() const { return this->has_ap_; }
228bool WiFiComponent::has_sta() const { return !this->sta_.empty(); }
229void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; }
230#ifdef USE_WIFI_11KV_SUPPORT
231void WiFiComponent::set_btm(bool btm) { this->btm_ = btm; }
232void WiFiComponent::set_rrm(bool rrm) { this->rrm_ = rrm; }
233#endif
235 if (this->has_sta())
236 return this->wifi_sta_ip_addresses();
237
238#ifdef USE_WIFI_AP
239 if (this->has_ap())
240 return {this->wifi_soft_ap_ip()};
241#endif // USE_WIFI_AP
242
243 return {};
244}
246 if (this->has_sta())
247 return this->wifi_dns_ip_(num);
248 return {};
249}
251 if (this->use_address_.empty()) {
252 return App.get_name() + ".local";
253 }
254 return this->use_address_;
255}
256void WiFiComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
257
258#ifdef USE_WIFI_AP
260 this->wifi_mode_({}, true);
261
262 if (this->ap_setup_)
263 return;
264
265 if (this->ap_.get_ssid().empty()) {
266 std::string name = App.get_name();
267 if (name.length() > 32) {
269 name.erase(name.begin() + 25, name.end() - 7); // Remove characters between 25 and the mac address
270 } else {
271 name = name.substr(0, 32);
272 }
273 }
274 this->ap_.set_ssid(name);
275 }
276 ESP_LOGCONFIG(TAG,
277 "Setting up AP:\n"
278 " AP SSID: '%s'\n"
279 " AP Password: '%s'",
280 this->ap_.get_ssid().c_str(), this->ap_.get_password().c_str());
281 if (this->ap_.get_manual_ip().has_value()) {
282 auto manual = *this->ap_.get_manual_ip();
283 ESP_LOGCONFIG(TAG,
284 " AP Static IP: '%s'\n"
285 " AP Gateway: '%s'\n"
286 " AP Subnet: '%s'",
287 manual.static_ip.str().c_str(), manual.gateway.str().c_str(), manual.subnet.str().c_str());
288 }
289
290 this->ap_setup_ = this->wifi_start_ap_(this->ap_);
291 ESP_LOGCONFIG(TAG, " IP Address: %s", this->wifi_soft_ap_ip().str().c_str());
292
293 if (!this->has_sta()) {
295 }
296}
297
299 this->ap_ = ap;
300 this->has_ap_ = true;
301}
302#endif // USE_WIFI_AP
303
305 return 10.0f; // before other loop components
306}
307
308void WiFiComponent::add_sta(const WiFiAP &ap) { this->sta_.push_back(ap); }
310 this->clear_sta();
311 this->add_sta(ap);
312}
313void WiFiComponent::clear_sta() { this->sta_.clear(); }
314void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) {
315 SavedWifiSettings save{};
316 snprintf(save.ssid, sizeof(save.ssid), "%s", ssid.c_str());
317 snprintf(save.password, sizeof(save.password), "%s", password.c_str());
318 this->pref_.save(&save);
319 // ensure it's written immediately
321
322 WiFiAP sta{};
323 sta.set_ssid(ssid);
324 sta.set_password(password);
325 this->set_sta(sta);
326}
327
328void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
329 ESP_LOGI(TAG, "Connecting to '%s'", ap.get_ssid().c_str());
330#ifdef ESPHOME_LOG_HAS_VERBOSE
331 ESP_LOGV(TAG, "Connection Params:");
332 ESP_LOGV(TAG, " SSID: '%s'", ap.get_ssid().c_str());
333 if (ap.get_bssid().has_value()) {
334 bssid_t b = *ap.get_bssid();
335 ESP_LOGV(TAG, " BSSID: %02X:%02X:%02X:%02X:%02X:%02X", b[0], b[1], b[2], b[3], b[4], b[5]);
336 } else {
337 ESP_LOGV(TAG, " BSSID: Not Set");
338 }
339
340#ifdef USE_WIFI_WPA2_EAP
341 if (ap.get_eap().has_value()) {
342 ESP_LOGV(TAG, " WPA2 Enterprise authentication configured:");
343 EAPAuth eap_config = ap.get_eap().value();
344 ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
345 ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
346 ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
347#ifdef USE_ESP_IDF
348#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
349 std::map<esp_eap_ttls_phase2_types, std::string> phase2types = {{ESP_EAP_TTLS_PHASE2_PAP, "pap"},
350 {ESP_EAP_TTLS_PHASE2_CHAP, "chap"},
351 {ESP_EAP_TTLS_PHASE2_MSCHAP, "mschap"},
352 {ESP_EAP_TTLS_PHASE2_MSCHAPV2, "mschapv2"},
353 {ESP_EAP_TTLS_PHASE2_EAP, "eap"}};
354 ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), phase2types[eap_config.ttls_phase_2].c_str());
355#endif
356#endif
357 bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert);
358 bool client_cert_present = eap_config.client_cert != nullptr && strlen(eap_config.client_cert);
359 bool client_key_present = eap_config.client_key != nullptr && strlen(eap_config.client_key);
360 ESP_LOGV(TAG, " CA Cert: %s", ca_cert_present ? "present" : "not present");
361 ESP_LOGV(TAG, " Client Cert: %s", client_cert_present ? "present" : "not present");
362 ESP_LOGV(TAG, " Client Key: %s", client_key_present ? "present" : "not present");
363 } else {
364#endif
365 ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password().c_str());
366#ifdef USE_WIFI_WPA2_EAP
367 }
368#endif
369 if (ap.get_channel().has_value()) {
370 ESP_LOGV(TAG, " Channel: %u", *ap.get_channel());
371 } else {
372 ESP_LOGV(TAG, " Channel not set");
373 }
374 if (ap.get_manual_ip().has_value()) {
375 ManualIP m = *ap.get_manual_ip();
376 ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.str().c_str(),
377 m.gateway.str().c_str(), m.subnet.str().c_str(), m.dns1.str().c_str(), m.dns2.str().c_str());
378 } else {
379 ESP_LOGV(TAG, " Using DHCP IP");
380 }
381 ESP_LOGV(TAG, " Hidden: %s", YESNO(ap.get_hidden()));
382#endif
383
384 if (!this->wifi_sta_connect_(ap)) {
385 ESP_LOGE(TAG, "wifi_sta_connect_ failed");
386 this->retry_connect();
387 return;
388 }
389
390 if (!two) {
392 } else {
394 }
395 this->action_started_ = millis();
396}
397
398const LogString *get_signal_bars(int8_t rssi) {
399 // LOWER ONE QUARTER BLOCK
400 // Unicode: U+2582, UTF-8: E2 96 82
401 // LOWER HALF BLOCK
402 // Unicode: U+2584, UTF-8: E2 96 84
403 // LOWER THREE QUARTERS BLOCK
404 // Unicode: U+2586, UTF-8: E2 96 86
405 // FULL BLOCK
406 // Unicode: U+2588, UTF-8: E2 96 88
407 if (rssi >= -50) {
408 return LOG_STR("\033[0;32m" // green
409 "\xe2\x96\x82"
410 "\xe2\x96\x84"
411 "\xe2\x96\x86"
412 "\xe2\x96\x88"
413 "\033[0m");
414 } else if (rssi >= -65) {
415 return LOG_STR("\033[0;33m" // yellow
416 "\xe2\x96\x82"
417 "\xe2\x96\x84"
418 "\xe2\x96\x86"
419 "\033[0;37m"
420 "\xe2\x96\x88"
421 "\033[0m");
422 } else if (rssi >= -85) {
423 return LOG_STR("\033[0;33m" // yellow
424 "\xe2\x96\x82"
425 "\xe2\x96\x84"
426 "\033[0;37m"
427 "\xe2\x96\x86"
428 "\xe2\x96\x88"
429 "\033[0m");
430 } else {
431 return LOG_STR("\033[0;31m" // red
432 "\xe2\x96\x82"
433 "\033[0;37m"
434 "\xe2\x96\x84"
435 "\xe2\x96\x86"
436 "\xe2\x96\x88"
437 "\033[0m");
438 }
439}
440
442 bssid_t bssid = wifi_bssid();
443
444 ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str());
445 if (this->is_disabled()) {
446 ESP_LOGCONFIG(TAG, " Disabled");
447 return;
448 }
449 ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str());
450 for (auto &ip : wifi_sta_ip_addresses()) {
451 if (ip.is_set()) {
452 ESP_LOGCONFIG(TAG, " IP Address: %s", ip.str().c_str());
453 }
454 }
455 int8_t rssi = wifi_rssi();
456 ESP_LOGCONFIG(TAG,
457 " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X") "\n"
458 " Hostname: '%s'\n"
459 " Signal strength: %d dB %s",
460 bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], App.get_name().c_str(), rssi,
461 LOG_STR_ARG(get_signal_bars(rssi)));
462#ifdef ESPHOME_LOG_HAS_VERBOSE
463 if (this->selected_ap_.get_bssid().has_value()) {
464 ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid()));
465 }
466#endif
467 ESP_LOGCONFIG(TAG,
468 " Channel: %" PRId32 "\n"
469 " Subnet: %s\n"
470 " Gateway: %s\n"
471 " DNS1: %s\n"
472 " DNS2: %s",
473 get_wifi_channel(), wifi_subnet_mask_().str().c_str(), wifi_gateway_ip_().str().c_str(),
474 wifi_dns_ip_(0).str().c_str(), wifi_dns_ip_(1).str().c_str());
475#ifdef USE_WIFI_11KV_SUPPORT
476 ESP_LOGCONFIG(TAG,
477 " BTM: %s\n"
478 " RRM: %s",
479 this->btm_ ? "enabled" : "disabled", this->rrm_ ? "enabled" : "disabled");
480#endif
481}
482
485 return;
486
487 ESP_LOGD(TAG, "Enabling");
488 this->error_from_callback_ = false;
490 this->start();
491}
492
495 return;
496
497 ESP_LOGD(TAG, "Disabling");
499 this->wifi_disconnect_();
500 this->wifi_mode_(false, false);
501}
502
504
506 this->action_started_ = millis();
507 ESP_LOGD(TAG, "Starting scan");
508 this->wifi_scan_start_(this->passive_scan_);
510}
511
512// Helper function for WiFi scan result comparison
513// Returns true if 'a' should be placed before 'b' in the sorted order
514[[nodiscard]] inline static bool wifi_scan_result_is_better(const WiFiScanResult &a, const WiFiScanResult &b) {
515 // Matching networks always come before non-matching
516 if (a.get_matches() && !b.get_matches())
517 return true;
518 if (!a.get_matches() && b.get_matches())
519 return false;
520
521 if (a.get_matches() && b.get_matches()) {
522 // For APs with the same SSID, always prefer stronger signal
523 // This helps with mesh networks and multiple APs
524 if (a.get_ssid() == b.get_ssid()) {
525 return a.get_rssi() > b.get_rssi();
526 }
527
528 // For different SSIDs, check priority first
529 if (a.get_priority() != b.get_priority())
530 return a.get_priority() > b.get_priority();
531 // If priorities are equal, prefer stronger signal
532 return a.get_rssi() > b.get_rssi();
533 }
534
535 // Both don't match - sort by signal strength
536 return a.get_rssi() > b.get_rssi();
537}
538
539// Helper function for insertion sort of WiFi scan results
540// Using insertion sort instead of std::stable_sort saves flash memory
541// by avoiding template instantiations (std::rotate, std::stable_sort, lambdas)
542// IMPORTANT: This sort is stable (preserves relative order of equal elements)
543static void insertion_sort_scan_results(std::vector<WiFiScanResult> &results) {
544 const size_t size = results.size();
545 for (size_t i = 1; i < size; i++) {
546 // Make a copy to avoid issues with move semantics during comparison
547 WiFiScanResult key = results[i];
548 int32_t j = i - 1;
549
550 // Move elements that are worse than key to the right
551 // For stability, we only move if key is strictly better than results[j]
552 while (j >= 0 && wifi_scan_result_is_better(key, results[j])) {
553 results[j + 1] = results[j];
554 j--;
555 }
556 results[j + 1] = key;
557 }
558}
559
561 if (!this->scan_done_) {
562 if (millis() - this->action_started_ > 30000) {
563 ESP_LOGE(TAG, "Scan timeout");
564 this->retry_connect();
565 }
566 return;
567 }
568 this->scan_done_ = false;
569
570 if (this->scan_result_.empty()) {
571 ESP_LOGW(TAG, "No networks found");
572 this->retry_connect();
573 return;
574 }
575
576 ESP_LOGD(TAG, "Found networks:");
577 for (auto &res : this->scan_result_) {
578 for (auto &ap : this->sta_) {
579 if (res.matches(ap)) {
580 res.set_matches(true);
581 if (!this->has_sta_priority(res.get_bssid())) {
582 this->set_sta_priority(res.get_bssid(), ap.get_priority());
583 }
584 res.set_priority(this->get_sta_priority(res.get_bssid()));
585 break;
586 }
587 }
588 }
589
590 // Sort scan results using insertion sort for better memory efficiency
591 insertion_sort_scan_results(this->scan_result_);
592
593 for (auto &res : this->scan_result_) {
594 char bssid_s[18];
595 auto bssid = res.get_bssid();
596 sprintf(bssid_s, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
597
598 if (res.get_matches()) {
599 ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(),
600 res.get_is_hidden() ? "(HIDDEN) " : "", bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi())));
601 ESP_LOGD(TAG,
602 " Channel: %u\n"
603 " RSSI: %d dB",
604 res.get_channel(), res.get_rssi());
605 } else {
606 ESP_LOGD(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s,
607 LOG_STR_ARG(get_signal_bars(res.get_rssi())));
608 }
609 }
610
611 if (!this->scan_result_[0].get_matches()) {
612 ESP_LOGW(TAG, "No matching network found");
613 this->retry_connect();
614 return;
615 }
616
617 WiFiAP connect_params;
618 WiFiScanResult scan_res = this->scan_result_[0];
619 for (auto &config : this->sta_) {
620 // search for matching STA config, at least one will match (from checks before)
621 if (!scan_res.matches(config)) {
622 continue;
623 }
624
625 if (config.get_hidden()) {
626 // selected network is hidden, we use the data from the config
627 connect_params.set_hidden(true);
628 connect_params.set_ssid(config.get_ssid());
629 // don't set BSSID and channel, there might be multiple hidden networks
630 // but we can't know which one is the correct one. Rely on probe-req with just SSID.
631 } else {
632 // selected network is visible, we use the data from the scan
633 // limit the connect params to only connect to exactly this network
634 // (network selection is done during scan phase).
635 connect_params.set_hidden(false);
636 connect_params.set_ssid(scan_res.get_ssid());
637 connect_params.set_channel(scan_res.get_channel());
638 connect_params.set_bssid(scan_res.get_bssid());
639 }
640 // copy manual IP (if set)
641 connect_params.set_manual_ip(config.get_manual_ip());
642
643#ifdef USE_WIFI_WPA2_EAP
644 // copy EAP parameters (if set)
645 connect_params.set_eap(config.get_eap());
646#endif
647
648 // copy password (if set)
649 connect_params.set_password(config.get_password());
650
651 break;
652 }
653
654 yield();
655
656 this->selected_ap_ = connect_params;
657 this->start_connecting(connect_params, false);
658}
659
661 ESP_LOGCONFIG(TAG, "WiFi:");
662 this->print_connect_params_();
663}
664
666 auto status = this->wifi_sta_connect_status_();
667
669 if (wifi_ssid().empty()) {
670 ESP_LOGW(TAG, "Connection incomplete");
671 this->retry_connect();
672 return;
673 }
674
675 ESP_LOGI(TAG, "Connected");
676 // We won't retry hidden networks unless a reconnect fails more than three times again
677 if (this->retry_hidden_ && !this->selected_ap_.get_hidden())
678 ESP_LOGW(TAG, "Network '%s' should be marked as hidden", this->selected_ap_.get_ssid().c_str());
679 this->retry_hidden_ = false;
680
681 this->print_connect_params_();
682
683 if (this->has_ap()) {
684#ifdef USE_CAPTIVE_PORTAL
685 if (this->is_captive_portal_active_()) {
687 }
688#endif
689 ESP_LOGD(TAG, "Disabling AP");
690 this->wifi_mode_({}, false);
691 }
692#ifdef USE_IMPROV
693 if (this->is_esp32_improv_active_()) {
695 }
696#endif
697
699 this->num_retried_ = 0;
700
701 if (this->fast_connect_) {
703 }
704
705 return;
706 }
707
708 uint32_t now = millis();
709 if (now - this->action_started_ > 30000) {
710 ESP_LOGW(TAG, "Connection timeout");
711 this->retry_connect();
712 return;
713 }
714
715 if (this->error_from_callback_) {
716 ESP_LOGW(TAG, "Connecting to network failed");
717 this->retry_connect();
718 return;
719 }
720
722 return;
723 }
724
726 ESP_LOGW(TAG, "Network no longer found");
727 this->retry_connect();
728 return;
729 }
730
732 ESP_LOGW(TAG, "Connecting to network failed");
733 this->retry_connect();
734 return;
735 }
736
737 ESP_LOGW(TAG, "Unknown connection status %d", (int) status);
738 this->retry_connect();
739}
740
742 if (this->selected_ap_.get_bssid()) {
743 auto bssid = *this->selected_ap_.get_bssid();
744 float priority = this->get_sta_priority(bssid);
745 this->set_sta_priority(bssid, priority - 1.0f);
746 }
747
748 delay(10);
749 if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_() &&
750 (this->num_retried_ > 3 || this->error_from_callback_)) {
751 if (this->fast_connect_) {
752 if (this->trying_loaded_ap_) {
753 this->trying_loaded_ap_ = false;
754 this->ap_index_ = 0; // Retry from the first configured AP
755 } else if (this->ap_index_ >= this->sta_.size() - 1) {
756 ESP_LOGW(TAG, "No more APs to try");
757 this->ap_index_ = 0;
758 this->restart_adapter();
759 } else {
760 // Try next AP
761 this->ap_index_++;
762 }
763 this->num_retried_ = 0;
764 this->selected_ap_ = this->sta_[this->ap_index_];
765 } else {
766 if (this->num_retried_ > 5) {
767 // If retry failed for more than 5 times, let's restart STA
768 this->restart_adapter();
769 } else {
770 // Try hidden networks after 3 failed retries
771 ESP_LOGD(TAG, "Retrying with hidden networks");
772 this->retry_hidden_ = true;
773 this->num_retried_++;
774 }
775 }
776 } else {
777 this->num_retried_++;
778 }
779 this->error_from_callback_ = false;
781 yield();
783 this->start_connecting(this->selected_ap_, true);
784 return;
785 }
786
788 this->action_started_ = millis();
789}
790
792 if (!this->has_sta() || this->state_ == WIFI_COMPONENT_STATE_DISABLED || this->ap_setup_) {
793 return true;
794 }
795 return this->is_connected();
796}
797void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
803
804void WiFiComponent::set_passive_scan(bool passive) { this->passive_scan_ = passive; }
805
807#ifdef USE_CAPTIVE_PORTAL
809#else
810 return false;
811#endif
812}
814#ifdef USE_IMPROV
816#else
817 return false;
818#endif
819}
820
822 SavedWifiFastConnectSettings fast_connect_save{};
823
824 if (this->fast_connect_pref_.load(&fast_connect_save)) {
825 bssid_t bssid{};
826 std::copy(fast_connect_save.bssid, fast_connect_save.bssid + 6, bssid.begin());
827 this->ap_index_ = fast_connect_save.ap_index;
828 this->selected_ap_ = this->sta_[this->ap_index_];
829 this->selected_ap_.set_bssid(bssid);
830 this->selected_ap_.set_channel(fast_connect_save.channel);
831
832 ESP_LOGD(TAG, "Loaded fast_connect settings");
833 return true;
834 }
835
836 return false;
837}
838
840 bssid_t bssid = wifi_bssid();
841 uint8_t channel = get_wifi_channel();
842
843 if (bssid != this->selected_ap_.get_bssid() || channel != this->selected_ap_.get_channel()) {
844 SavedWifiFastConnectSettings fast_connect_save{};
845
846 memcpy(fast_connect_save.bssid, bssid.data(), 6);
847 fast_connect_save.channel = channel;
848 fast_connect_save.ap_index = this->ap_index_;
849
850 this->fast_connect_pref_.save(&fast_connect_save);
851
852 ESP_LOGD(TAG, "Saved fast_connect settings");
853 }
854}
855
856void WiFiAP::set_ssid(const std::string &ssid) { this->ssid_ = ssid; }
857void WiFiAP::set_bssid(bssid_t bssid) { this->bssid_ = bssid; }
858void WiFiAP::set_bssid(optional<bssid_t> bssid) { this->bssid_ = bssid; }
859void WiFiAP::set_password(const std::string &password) { this->password_ = password; }
860#ifdef USE_WIFI_WPA2_EAP
861void WiFiAP::set_eap(optional<EAPAuth> eap_auth) { this->eap_ = std::move(eap_auth); }
862#endif
863void WiFiAP::set_channel(optional<uint8_t> channel) { this->channel_ = channel; }
864void WiFiAP::set_manual_ip(optional<ManualIP> manual_ip) { this->manual_ip_ = manual_ip; }
865void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; }
866const std::string &WiFiAP::get_ssid() const { return this->ssid_; }
867const optional<bssid_t> &WiFiAP::get_bssid() const { return this->bssid_; }
868const std::string &WiFiAP::get_password() const { return this->password_; }
869#ifdef USE_WIFI_WPA2_EAP
870const optional<EAPAuth> &WiFiAP::get_eap() const { return this->eap_; }
871#endif
872const optional<uint8_t> &WiFiAP::get_channel() const { return this->channel_; }
874bool WiFiAP::get_hidden() const { return this->hidden_; }
875
876WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth,
877 bool is_hidden)
878 : bssid_(bssid),
879 ssid_(std::move(ssid)),
880 channel_(channel),
881 rssi_(rssi),
882 with_auth_(with_auth),
883 is_hidden_(is_hidden) {}
884bool WiFiScanResult::matches(const WiFiAP &config) {
885 if (config.get_hidden()) {
886 // User configured a hidden network, only match actually hidden networks
887 // don't match SSID
888 if (!this->is_hidden_)
889 return false;
890 } else if (!config.get_ssid().empty()) {
891 // check if SSID matches
892 if (config.get_ssid() != this->ssid_)
893 return false;
894 } else {
895 // network is configured without SSID - match other settings
896 }
897 // If BSSID configured, only match for correct BSSIDs
898 if (config.get_bssid().has_value() && *config.get_bssid() != this->bssid_)
899 return false;
900
901#ifdef USE_WIFI_WPA2_EAP
902 // BSSID requires auth but no PSK or EAP credentials given
903 if (this->with_auth_ && (config.get_password().empty() && !config.get_eap().has_value()))
904 return false;
905
906 // BSSID does not require auth, but PSK or EAP credentials given
907 if (!this->with_auth_ && (!config.get_password().empty() || config.get_eap().has_value()))
908 return false;
909#else
910 // If PSK given, only match for networks with auth (and vice versa)
911 if (config.get_password().empty() == this->with_auth_)
912 return false;
913#endif
914
915 // If channel configured, only match networks on that channel.
916 if (config.get_channel().has_value() && *config.get_channel() != this->channel_) {
917 return false;
918 }
919 return true;
920}
921bool WiFiScanResult::get_matches() const { return this->matches_; }
922void WiFiScanResult::set_matches(bool matches) { this->matches_ = matches; }
923const bssid_t &WiFiScanResult::get_bssid() const { return this->bssid_; }
924const std::string &WiFiScanResult::get_ssid() const { return this->ssid_; }
925uint8_t WiFiScanResult::get_channel() const { return this->channel_; }
926int8_t WiFiScanResult::get_rssi() const { return this->rssi_; }
927bool WiFiScanResult::get_with_auth() const { return this->with_auth_; }
928bool WiFiScanResult::get_is_hidden() const { return this->is_hidden_; }
929
930bool WiFiScanResult::operator==(const WiFiScanResult &rhs) const { return this->bssid_ == rhs.bssid_; }
931
932WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
933
934} // namespace wifi
935} // namespace esphome
936#endif
uint8_t m
Definition bl0906.h:1
uint8_t status
Definition bl0942.h:8
std::string get_compilation_time() const
bool is_name_add_mac_suffix_enabled() const
const std::string & get_name() const
Get the name of this Application set by pre_setup().
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void status_set_warning(const char *message=nullptr)
void status_clear_warning()
bool save(const T *src)
Definition preferences.h:21
virtual bool sync()=0
Commit pending writes to flash.
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition automation.h:145
bool has_value() const
Definition optional.h:92
const optional< bssid_t > & get_bssid() const
const std::string & get_ssid() const
void set_ssid(const std::string &ssid)
const optional< uint8_t > & get_channel() const
const optional< EAPAuth > & get_eap() const
void set_channel(optional< uint8_t > channel)
const std::string & get_password() const
void set_bssid(bssid_t bssid)
optional< uint8_t > channel_
optional< EAPAuth > eap_
optional< bssid_t > bssid_
optional< ManualIP > manual_ip_
void set_eap(optional< EAPAuth > eap_auth)
void set_password(const std::string &password)
void set_manual_ip(optional< ManualIP > manual_ip)
const optional< ManualIP > & get_manual_ip() const
void set_hidden(bool hidden)
This component is responsible for managing the ESP WiFi interface.
void add_sta(const WiFiAP &ap)
void set_ap(const WiFiAP &ap)
Setup an Access Point that should be created if no connection to a station can be made.
void set_sta(const WiFiAP &ap)
bool has_sta_priority(const bssid_t &bssid)
std::string get_use_address() const
void save_wifi_sta(const std::string &ssid, const std::string &password)
void loop() override
Reconnect WiFi if required.
float get_sta_priority(const bssid_t bssid)
network::IPAddress get_dns_address(int num)
WiFiComponent()
Construct a WiFiComponent.
std::vector< WiFiScanResult > scan_result_
void start_connecting(const WiFiAP &ap, bool two)
void set_passive_scan(bool passive)
void set_power_save_mode(WiFiPowerSaveMode power_save)
void set_use_address(const std::string &use_address)
ESPPreferenceObject fast_connect_pref_
float get_loop_priority() const override
network::IPAddresses get_ip_addresses()
std::vector< WiFiAP > sta_
float get_setup_priority() const override
WIFI setup_priority.
optional< float > output_power_
void set_sta_priority(const bssid_t bssid, float priority)
void set_fast_connect(bool fast_connect)
bool wifi_mode_(optional< bool > sta, optional< bool > ap)
void set_reboot_timeout(uint32_t reboot_timeout)
void setup() override
Setup WiFi interface.
const std::string & get_ssid() const
const bssid_t & get_bssid() const
WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden)
bool matches(const WiFiAP &config)
bool operator==(const WiFiScanResult &rhs) const
uint8_t priority
CaptivePortal * global_captive_portal
ESP32ImprovComponent * global_improv_component
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:144
std::array< uint8_t, 6 > bssid_t
const LogString * get_signal_bars(int8_t rssi)
WiFiComponent * global_wifi_component
@ WIFI_COMPONENT_STATE_DISABLED
WiFi is disabled.
@ WIFI_COMPONENT_STATE_AP
WiFi is in AP-only mode and internal AP is already enabled.
@ WIFI_COMPONENT_STATE_STA_CONNECTING
WiFi is in STA(+AP) mode and currently connecting to an AP.
@ WIFI_COMPONENT_STATE_OFF
Nothing has been initialized yet.
@ WIFI_COMPONENT_STATE_STA_SCANNING
WiFi is in STA-only mode and currently scanning for APs.
@ WIFI_COMPONENT_STATE_STA_CONNECTING_2
WiFi is in STA(+AP) mode and currently connecting to an AP a second time.
@ WIFI_COMPONENT_STATE_COOLDOWN
WiFi is in cooldown mode because something went wrong, scanning will begin after a short period of ti...
@ WIFI_COMPONENT_STATE_STA_CONNECTED
WiFi is in STA(+AP) mode and successfully connected.
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:134
ESPPreferences * global_preferences
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition helpers.cpp:584
void IRAM_ATTR HOT yield()
Definition core.cpp:27
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
esp_eap_ttls_phase2_types ttls_phase_2
Struct for setting static IPs in WiFiComponent.