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