ESPHome  2024.10.2
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"
28 #include "esphome/core/helpers.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 
40 namespace esphome {
41 namespace wifi {
42 
43 static const char *const TAG = "wifi";
44 
46 
48  ESP_LOGCONFIG(TAG, "Setting up WiFi...");
49  this->wifi_pre_setup_();
50  if (this->enable_on_boot_) {
51  this->start();
52  } else {
53 #ifdef USE_ESP32
54  esp_netif_init();
55 #endif
57  }
58 }
59 
61  ESP_LOGCONFIG(TAG, "Starting WiFi...");
62  ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str());
63  this->last_connected_ = millis();
64 
65  uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL;
66 
68  if (this->fast_connect_) {
70  }
71 
72  SavedWifiSettings save{};
73  if (this->pref_.load(&save)) {
74  ESP_LOGD(TAG, "Loaded saved wifi settings: %s", save.ssid);
75 
76  WiFiAP sta{};
77  sta.set_ssid(save.ssid);
78  sta.set_password(save.password);
79  this->set_sta(sta);
80  }
81 
82  if (this->has_sta()) {
83  this->wifi_sta_pre_setup_();
84  if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) {
85  ESP_LOGV(TAG, "Setting Output Power Option failed!");
86  }
87 
88  if (!this->wifi_apply_power_save_()) {
89  ESP_LOGV(TAG, "Setting Power Save Option failed!");
90  }
91 
92  if (this->fast_connect_) {
93  this->selected_ap_ = this->sta_[0];
95  this->start_connecting(this->selected_ap_, false);
96  } else {
97  this->start_scanning();
98  }
99 #ifdef USE_WIFI_AP
100  } else if (this->has_ap()) {
101  this->setup_ap_config_();
102  if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) {
103  ESP_LOGV(TAG, "Setting Output Power Option failed!");
104  }
105 #ifdef USE_CAPTIVE_PORTAL
106  if (captive_portal::global_captive_portal != nullptr) {
107  this->wifi_sta_pre_setup_();
108  this->start_scanning();
110  }
111 #endif
112 #endif // USE_WIFI_AP
113  }
114 #ifdef USE_IMPROV
115  if (!this->has_sta() && esp32_improv::global_improv_component != nullptr) {
116  if (this->wifi_mode_(true, {}))
118  }
119 #endif
120  this->wifi_apply_hostname_();
121 }
122 
124  this->wifi_loop_();
125  const uint32_t now = millis();
126 
127  if (this->has_sta()) {
128  if (this->is_connected() != this->handled_connected_state_) {
129  if (this->handled_connected_state_) {
130  this->disconnect_trigger_->trigger();
131  } else {
132  this->connect_trigger_->trigger();
133  }
134  this->handled_connected_state_ = this->is_connected();
135  }
136 
137  switch (this->state_) {
139  this->status_set_warning("waiting to reconnect");
140  if (millis() - this->action_started_ > 5000) {
141  if (this->fast_connect_ || this->retry_hidden_) {
142  this->start_connecting(this->sta_[0], false);
143  } else {
144  this->start_scanning();
145  }
146  }
147  break;
148  }
150  this->status_set_warning("scanning for networks");
151  this->check_scanning_finished();
152  break;
153  }
156  this->status_set_warning("associating to network");
158  break;
159  }
160 
162  if (!this->is_connected()) {
163  ESP_LOGW(TAG, "WiFi Connection lost... Reconnecting...");
165  this->retry_connect();
166  } else {
167  this->status_clear_warning();
168  this->last_connected_ = now;
169  }
170  break;
171  }
174  break;
176  return;
177  }
178 
179 #ifdef USE_WIFI_AP
180  if (this->has_ap() && !this->ap_setup_) {
181  if (this->ap_timeout_ != 0 && (now - this->last_connected_ > this->ap_timeout_)) {
182  ESP_LOGI(TAG, "Starting fallback AP!");
183  this->setup_ap_config_();
184 #ifdef USE_CAPTIVE_PORTAL
187 #endif
188  }
189  }
190 #endif // USE_WIFI_AP
191 
192 #ifdef USE_IMPROV
194  if (now - this->last_connected_ > esp32_improv::global_improv_component->get_wifi_timeout()) {
195  if (this->wifi_mode_(true, {}))
197  }
198  }
199 
200 #endif
201 
202  if (!this->has_ap() && this->reboot_timeout_ != 0) {
203  if (now - this->last_connected_ > this->reboot_timeout_) {
204  ESP_LOGE(TAG, "Can't connect to WiFi, rebooting...");
205  App.reboot();
206  }
207  }
208  }
209 }
210 
212 
213 bool WiFiComponent::has_ap() const { return this->has_ap_; }
214 bool WiFiComponent::has_sta() const { return !this->sta_.empty(); }
215 void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; }
216 #ifdef USE_WIFI_11KV_SUPPORT
217 void WiFiComponent::set_btm(bool btm) { this->btm_ = btm; }
218 void WiFiComponent::set_rrm(bool rrm) { this->rrm_ = rrm; }
219 #endif
221  if (this->has_sta())
222  return this->wifi_sta_ip_addresses();
223 
224 #ifdef USE_WIFI_AP
225  if (this->has_ap())
226  return {this->wifi_soft_ap_ip()};
227 #endif // USE_WIFI_AP
228 
229  return {};
230 }
232  if (this->has_sta())
233  return this->wifi_dns_ip_(num);
234  return {};
235 }
236 std::string WiFiComponent::get_use_address() const {
237  if (this->use_address_.empty()) {
238  return App.get_name() + ".local";
239  }
240  return this->use_address_;
241 }
242 void WiFiComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
243 
244 #ifdef USE_WIFI_AP
246  this->wifi_mode_({}, true);
247 
248  if (this->ap_setup_)
249  return;
250 
251  if (this->ap_.get_ssid().empty()) {
252  std::string name = App.get_name();
253  if (name.length() > 32) {
255  name.erase(name.begin() + 25, name.end() - 7); // Remove characters between 25 and the mac address
256  } else {
257  name = name.substr(0, 32);
258  }
259  }
260  this->ap_.set_ssid(name);
261  }
262 
263  ESP_LOGCONFIG(TAG, "Setting up AP...");
264 
265  ESP_LOGCONFIG(TAG, " AP SSID: '%s'", this->ap_.get_ssid().c_str());
266  ESP_LOGCONFIG(TAG, " AP Password: '%s'", this->ap_.get_password().c_str());
267  if (this->ap_.get_manual_ip().has_value()) {
268  auto manual = *this->ap_.get_manual_ip();
269  ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.str().c_str());
270  ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.str().c_str());
271  ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.str().c_str());
272  }
273 
274  this->ap_setup_ = this->wifi_start_ap_(this->ap_);
275  ESP_LOGCONFIG(TAG, " IP Address: %s", this->wifi_soft_ap_ip().str().c_str());
276 
277  if (!this->has_sta()) {
279  }
280 }
281 
282 void WiFiComponent::set_ap(const WiFiAP &ap) {
283  this->ap_ = ap;
284  this->has_ap_ = true;
285 }
286 #endif // USE_WIFI_AP
287 
289  return 10.0f; // before other loop components
290 }
291 
292 void WiFiComponent::add_sta(const WiFiAP &ap) { this->sta_.push_back(ap); }
294  this->clear_sta();
295  this->add_sta(ap);
296 }
297 void WiFiComponent::clear_sta() { this->sta_.clear(); }
298 void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) {
299  SavedWifiSettings save{};
300  strncpy(save.ssid, ssid.c_str(), sizeof(save.ssid));
301  strncpy(save.password, password.c_str(), sizeof(save.password));
302  this->pref_.save(&save);
303  // ensure it's written immediately
305 
306  WiFiAP sta{};
307  sta.set_ssid(ssid);
308  sta.set_password(password);
309  this->set_sta(sta);
310 }
311 
312 void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
313  ESP_LOGI(TAG, "WiFi Connecting to '%s'...", ap.get_ssid().c_str());
314 #ifdef ESPHOME_LOG_HAS_VERBOSE
315  ESP_LOGV(TAG, "Connection Params:");
316  ESP_LOGV(TAG, " SSID: '%s'", ap.get_ssid().c_str());
317  if (ap.get_bssid().has_value()) {
318  bssid_t b = *ap.get_bssid();
319  ESP_LOGV(TAG, " BSSID: %02X:%02X:%02X:%02X:%02X:%02X", b[0], b[1], b[2], b[3], b[4], b[5]);
320  } else {
321  ESP_LOGV(TAG, " BSSID: Not Set");
322  }
323 
324 #ifdef USE_WIFI_WPA2_EAP
325  if (ap.get_eap().has_value()) {
326  ESP_LOGV(TAG, " WPA2 Enterprise authentication configured:");
327  EAPAuth eap_config = ap.get_eap().value();
328  ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
329  ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
330  ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
331 #ifdef USE_ESP_IDF
332 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
333  std::map<esp_eap_ttls_phase2_types, std::string> phase2types = {{ESP_EAP_TTLS_PHASE2_PAP, "pap"},
334  {ESP_EAP_TTLS_PHASE2_CHAP, "chap"},
335  {ESP_EAP_TTLS_PHASE2_MSCHAP, "mschap"},
336  {ESP_EAP_TTLS_PHASE2_MSCHAPV2, "mschapv2"},
337  {ESP_EAP_TTLS_PHASE2_EAP, "eap"}};
338  ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), phase2types[eap_config.ttls_phase_2].c_str());
339 #endif
340 #endif
341  bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert);
342  bool client_cert_present = eap_config.client_cert != nullptr && strlen(eap_config.client_cert);
343  bool client_key_present = eap_config.client_key != nullptr && strlen(eap_config.client_key);
344  ESP_LOGV(TAG, " CA Cert: %s", ca_cert_present ? "present" : "not present");
345  ESP_LOGV(TAG, " Client Cert: %s", client_cert_present ? "present" : "not present");
346  ESP_LOGV(TAG, " Client Key: %s", client_key_present ? "present" : "not present");
347  } else {
348 #endif
349  ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password().c_str());
350 #ifdef USE_WIFI_WPA2_EAP
351  }
352 #endif
353  if (ap.get_channel().has_value()) {
354  ESP_LOGV(TAG, " Channel: %u", *ap.get_channel());
355  } else {
356  ESP_LOGV(TAG, " Channel: Not Set");
357  }
358  if (ap.get_manual_ip().has_value()) {
359  ManualIP m = *ap.get_manual_ip();
360  ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.str().c_str(),
361  m.gateway.str().c_str(), m.subnet.str().c_str(), m.dns1.str().c_str(), m.dns2.str().c_str());
362  } else {
363  ESP_LOGV(TAG, " Using DHCP IP");
364  }
365  ESP_LOGV(TAG, " Hidden: %s", YESNO(ap.get_hidden()));
366 #endif
367 
368  if (!this->wifi_sta_connect_(ap)) {
369  ESP_LOGE(TAG, "wifi_sta_connect_ failed!");
370  this->retry_connect();
371  return;
372  }
373 
374  if (!two) {
376  } else {
378  }
379  this->action_started_ = millis();
380 }
381 
382 const LogString *get_signal_bars(int8_t rssi) {
383  // LOWER ONE QUARTER BLOCK
384  // Unicode: U+2582, UTF-8: E2 96 82
385  // LOWER HALF BLOCK
386  // Unicode: U+2584, UTF-8: E2 96 84
387  // LOWER THREE QUARTERS BLOCK
388  // Unicode: U+2586, UTF-8: E2 96 86
389  // FULL BLOCK
390  // Unicode: U+2588, UTF-8: E2 96 88
391  if (rssi >= -50) {
392  return LOG_STR("\033[0;32m" // green
393  "\xe2\x96\x82"
394  "\xe2\x96\x84"
395  "\xe2\x96\x86"
396  "\xe2\x96\x88"
397  "\033[0m");
398  } else if (rssi >= -65) {
399  return LOG_STR("\033[0;33m" // yellow
400  "\xe2\x96\x82"
401  "\xe2\x96\x84"
402  "\xe2\x96\x86"
403  "\033[0;37m"
404  "\xe2\x96\x88"
405  "\033[0m");
406  } else if (rssi >= -85) {
407  return LOG_STR("\033[0;33m" // yellow
408  "\xe2\x96\x82"
409  "\xe2\x96\x84"
410  "\033[0;37m"
411  "\xe2\x96\x86"
412  "\xe2\x96\x88"
413  "\033[0m");
414  } else {
415  return LOG_STR("\033[0;31m" // red
416  "\xe2\x96\x82"
417  "\033[0;37m"
418  "\xe2\x96\x84"
419  "\xe2\x96\x86"
420  "\xe2\x96\x88"
421  "\033[0m");
422  }
423 }
424 
426  bssid_t bssid = wifi_bssid();
427 
428  ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str());
429  if (this->is_disabled()) {
430  ESP_LOGCONFIG(TAG, " WiFi is disabled!");
431  return;
432  }
433  ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str());
434  for (auto &ip : wifi_sta_ip_addresses()) {
435  if (ip.is_set()) {
436  ESP_LOGCONFIG(TAG, " IP Address: %s", ip.str().c_str());
437  }
438  }
439  ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3],
440  bssid[4], bssid[5]);
441  ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str());
442  int8_t rssi = wifi_rssi();
443  ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, LOG_STR_ARG(get_signal_bars(rssi)));
444  if (this->selected_ap_.get_bssid().has_value()) {
445  ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid()));
446  }
447  ESP_LOGCONFIG(TAG, " Channel: %" PRId32, wifi_channel_());
448  ESP_LOGCONFIG(TAG, " Subnet: %s", wifi_subnet_mask_().str().c_str());
449  ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str());
450  ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str());
451  ESP_LOGCONFIG(TAG, " DNS2: %s", wifi_dns_ip_(1).str().c_str());
452 #ifdef USE_WIFI_11KV_SUPPORT
453  ESP_LOGCONFIG(TAG, " BTM: %s", this->btm_ ? "enabled" : "disabled");
454  ESP_LOGCONFIG(TAG, " RRM: %s", this->rrm_ ? "enabled" : "disabled");
455 #endif
456 }
457 
460  return;
461 
462  ESP_LOGD(TAG, "Enabling WIFI...");
463  this->error_from_callback_ = false;
465  this->start();
466 }
467 
470  return;
471 
472  ESP_LOGD(TAG, "Disabling WIFI...");
474  this->wifi_disconnect_();
475  this->wifi_mode_(false, false);
476 }
477 
479 
481  this->action_started_ = millis();
482  ESP_LOGD(TAG, "Starting scan...");
483  this->wifi_scan_start_(this->passive_scan_);
485 }
486 
488  if (!this->scan_done_) {
489  if (millis() - this->action_started_ > 30000) {
490  ESP_LOGE(TAG, "Scan timeout!");
491  this->retry_connect();
492  }
493  return;
494  }
495  this->scan_done_ = false;
496 
497  ESP_LOGD(TAG, "Found networks:");
498  if (this->scan_result_.empty()) {
499  ESP_LOGD(TAG, " No network found!");
500  this->retry_connect();
501  return;
502  }
503 
504  for (auto &res : this->scan_result_) {
505  for (auto &ap : this->sta_) {
506  if (res.matches(ap)) {
507  res.set_matches(true);
508  if (!this->has_sta_priority(res.get_bssid())) {
509  this->set_sta_priority(res.get_bssid(), ap.get_priority());
510  }
511  res.set_priority(this->get_sta_priority(res.get_bssid()));
512  break;
513  }
514  }
515  }
516 
517  std::stable_sort(this->scan_result_.begin(), this->scan_result_.end(),
518  [](const WiFiScanResult &a, const WiFiScanResult &b) {
519  // return true if a is better than b
520  if (a.get_matches() && !b.get_matches())
521  return true;
522  if (!a.get_matches() && b.get_matches())
523  return false;
524 
525  if (a.get_matches() && b.get_matches()) {
526  // if both match, check priority
527  if (a.get_priority() != b.get_priority())
528  return a.get_priority() > b.get_priority();
529  }
530 
531  return a.get_rssi() > b.get_rssi();
532  });
533 
534  for (auto &res : this->scan_result_) {
535  char bssid_s[18];
536  auto bssid = res.get_bssid();
537  sprintf(bssid_s, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
538 
539  if (res.get_matches()) {
540  ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(),
541  res.get_is_hidden() ? "(HIDDEN) " : "", bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi())));
542  ESP_LOGD(TAG, " Channel: %u", res.get_channel());
543  ESP_LOGD(TAG, " RSSI: %d dB", res.get_rssi());
544  } else {
545  ESP_LOGD(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s,
546  LOG_STR_ARG(get_signal_bars(res.get_rssi())));
547  }
548  }
549 
550  if (!this->scan_result_[0].get_matches()) {
551  ESP_LOGW(TAG, "No matching network found!");
552  this->retry_connect();
553  return;
554  }
555 
556  WiFiAP connect_params;
557  WiFiScanResult scan_res = this->scan_result_[0];
558  for (auto &config : this->sta_) {
559  // search for matching STA config, at least one will match (from checks before)
560  if (!scan_res.matches(config)) {
561  continue;
562  }
563 
564  if (config.get_hidden()) {
565  // selected network is hidden, we use the data from the config
566  connect_params.set_hidden(true);
567  connect_params.set_ssid(config.get_ssid());
568  // don't set BSSID and channel, there might be multiple hidden networks
569  // but we can't know which one is the correct one. Rely on probe-req with just SSID.
570  } else {
571  // selected network is visible, we use the data from the scan
572  // limit the connect params to only connect to exactly this network
573  // (network selection is done during scan phase).
574  connect_params.set_hidden(false);
575  connect_params.set_ssid(scan_res.get_ssid());
576  connect_params.set_channel(scan_res.get_channel());
577  connect_params.set_bssid(scan_res.get_bssid());
578  }
579  // copy manual IP (if set)
580  connect_params.set_manual_ip(config.get_manual_ip());
581 
582 #ifdef USE_WIFI_WPA2_EAP
583  // copy EAP parameters (if set)
584  connect_params.set_eap(config.get_eap());
585 #endif
586 
587  // copy password (if set)
588  connect_params.set_password(config.get_password());
589 
590  break;
591  }
592 
593  yield();
594 
595  this->selected_ap_ = connect_params;
596  this->start_connecting(connect_params, false);
597 }
598 
600  ESP_LOGCONFIG(TAG, "WiFi:");
601  this->print_connect_params_();
602 }
603 
605  auto status = this->wifi_sta_connect_status_();
606 
608  if (wifi_ssid().empty()) {
609  ESP_LOGW(TAG, "Incomplete connection.");
610  this->retry_connect();
611  return;
612  }
613 
614  // We won't retry hidden networks unless a reconnect fails more than three times again
615  this->retry_hidden_ = false;
616 
617  ESP_LOGI(TAG, "WiFi Connected!");
618  this->print_connect_params_();
619 
620  if (this->has_ap()) {
621 #ifdef USE_CAPTIVE_PORTAL
622  if (this->is_captive_portal_active_()) {
624  }
625 #endif
626  ESP_LOGD(TAG, "Disabling AP...");
627  this->wifi_mode_({}, false);
628  }
629 #ifdef USE_IMPROV
630  if (this->is_esp32_improv_active_()) {
632  }
633 #endif
634 
636  this->num_retried_ = 0;
637 
638  if (this->fast_connect_) {
640  }
641 
642  return;
643  }
644 
645  uint32_t now = millis();
646  if (now - this->action_started_ > 30000) {
647  ESP_LOGW(TAG, "Timeout while connecting to WiFi.");
648  this->retry_connect();
649  return;
650  }
651 
652  if (this->error_from_callback_) {
653  ESP_LOGW(TAG, "Error while connecting to network.");
654  this->retry_connect();
655  return;
656  }
657 
659  return;
660  }
661 
663  ESP_LOGW(TAG, "WiFi network can not be found anymore.");
664  this->retry_connect();
665  return;
666  }
667 
669  ESP_LOGW(TAG, "Connecting to WiFi network failed. Are the credentials wrong?");
670  this->retry_connect();
671  return;
672  }
673 
674  ESP_LOGW(TAG, "WiFi Unknown connection status %d", (int) status);
675  this->retry_connect();
676 }
677 
679  if (this->selected_ap_.get_bssid()) {
680  auto bssid = *this->selected_ap_.get_bssid();
681  float priority = this->get_sta_priority(bssid);
682  this->set_sta_priority(bssid, priority - 1.0f);
683  }
684 
685  delay(10);
686  if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_() &&
687  (this->num_retried_ > 3 || this->error_from_callback_)) {
688  if (this->num_retried_ > 5) {
689  // If retry failed for more than 5 times, let's restart STA
690  ESP_LOGW(TAG, "Restarting WiFi adapter...");
691  this->wifi_mode_(false, {});
692  delay(100); // NOLINT
693  this->num_retried_ = 0;
694  this->retry_hidden_ = false;
695  } else {
696  // Try hidden networks after 3 failed retries
697  ESP_LOGD(TAG, "Retrying with hidden networks...");
698  this->retry_hidden_ = true;
699  this->num_retried_++;
700  }
701  } else {
702  this->num_retried_++;
703  }
704  this->error_from_callback_ = false;
706  yield();
708  this->start_connecting(this->selected_ap_, true);
709  return;
710  }
711 
713  this->action_started_ = millis();
714 }
715 
717  if (!this->has_sta() || this->state_ == WIFI_COMPONENT_STATE_DISABLED || this->ap_setup_) {
718  return true;
719  }
720  return this->is_connected();
721 }
722 void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
724  return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED &&
726 }
727 void WiFiComponent::set_power_save_mode(WiFiPowerSaveMode power_save) { this->power_save_ = power_save; }
728 
729 void WiFiComponent::set_passive_scan(bool passive) { this->passive_scan_ = passive; }
730 
731 std::string WiFiComponent::format_mac_addr(const uint8_t *mac) {
732  char buf[20];
733  sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
734  return buf;
735 }
737 #ifdef USE_CAPTIVE_PORTAL
739 #else
740  return false;
741 #endif
742 }
744 #ifdef USE_IMPROV
746 #else
747  return false;
748 #endif
749 }
750 
752  SavedWifiFastConnectSettings fast_connect_save{};
753 
754  if (this->fast_connect_pref_.load(&fast_connect_save)) {
755  bssid_t bssid{};
756  std::copy(fast_connect_save.bssid, fast_connect_save.bssid + 6, bssid.begin());
757  this->selected_ap_.set_bssid(bssid);
758  this->selected_ap_.set_channel(fast_connect_save.channel);
759 
760  ESP_LOGD(TAG, "Loaded saved fast_connect wifi settings");
761  }
762 }
763 
765  bssid_t bssid = wifi_bssid();
766  uint8_t channel = wifi_channel_();
767 
768  if (bssid != this->selected_ap_.get_bssid() || channel != this->selected_ap_.get_channel()) {
769  SavedWifiFastConnectSettings fast_connect_save{};
770 
771  memcpy(fast_connect_save.bssid, bssid.data(), 6);
772  fast_connect_save.channel = channel;
773 
774  this->fast_connect_pref_.save(&fast_connect_save);
775 
776  ESP_LOGD(TAG, "Saved fast_connect wifi settings");
777  }
778 }
779 
780 void WiFiAP::set_ssid(const std::string &ssid) { this->ssid_ = ssid; }
781 void WiFiAP::set_bssid(bssid_t bssid) { this->bssid_ = bssid; }
782 void WiFiAP::set_bssid(optional<bssid_t> bssid) { this->bssid_ = bssid; }
783 void WiFiAP::set_password(const std::string &password) { this->password_ = password; }
784 #ifdef USE_WIFI_WPA2_EAP
785 void WiFiAP::set_eap(optional<EAPAuth> eap_auth) { this->eap_ = std::move(eap_auth); }
786 #endif
787 void WiFiAP::set_channel(optional<uint8_t> channel) { this->channel_ = channel; }
788 void WiFiAP::set_manual_ip(optional<ManualIP> manual_ip) { this->manual_ip_ = manual_ip; }
789 void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; }
790 const std::string &WiFiAP::get_ssid() const { return this->ssid_; }
791 const optional<bssid_t> &WiFiAP::get_bssid() const { return this->bssid_; }
792 const std::string &WiFiAP::get_password() const { return this->password_; }
793 #ifdef USE_WIFI_WPA2_EAP
794 const optional<EAPAuth> &WiFiAP::get_eap() const { return this->eap_; }
795 #endif
796 const optional<uint8_t> &WiFiAP::get_channel() const { return this->channel_; }
797 const optional<ManualIP> &WiFiAP::get_manual_ip() const { return this->manual_ip_; }
798 bool WiFiAP::get_hidden() const { return this->hidden_; }
799 
800 WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth,
801  bool is_hidden)
802  : bssid_(bssid),
803  ssid_(std::move(ssid)),
804  channel_(channel),
805  rssi_(rssi),
806  with_auth_(with_auth),
807  is_hidden_(is_hidden) {}
808 bool WiFiScanResult::matches(const WiFiAP &config) {
809  if (config.get_hidden()) {
810  // User configured a hidden network, only match actually hidden networks
811  // don't match SSID
812  if (!this->is_hidden_)
813  return false;
814  } else if (!config.get_ssid().empty()) {
815  // check if SSID matches
816  if (config.get_ssid() != this->ssid_)
817  return false;
818  } else {
819  // network is configured without SSID - match other settings
820  }
821  // If BSSID configured, only match for correct BSSIDs
822  if (config.get_bssid().has_value() && *config.get_bssid() != this->bssid_)
823  return false;
824 
825 #ifdef USE_WIFI_WPA2_EAP
826  // BSSID requires auth but no PSK or EAP credentials given
827  if (this->with_auth_ && (config.get_password().empty() && !config.get_eap().has_value()))
828  return false;
829 
830  // BSSID does not require auth, but PSK or EAP credentials given
831  if (!this->with_auth_ && (!config.get_password().empty() || config.get_eap().has_value()))
832  return false;
833 #else
834  // If PSK given, only match for networks with auth (and vice versa)
835  if (config.get_password().empty() == this->with_auth_)
836  return false;
837 #endif
838 
839  // If channel configured, only match networks on that channel.
840  if (config.get_channel().has_value() && *config.get_channel() != this->channel_) {
841  return false;
842  }
843  return true;
844 }
845 bool WiFiScanResult::get_matches() const { return this->matches_; }
847 const bssid_t &WiFiScanResult::get_bssid() const { return this->bssid_; }
848 const std::string &WiFiScanResult::get_ssid() const { return this->ssid_; }
849 uint8_t WiFiScanResult::get_channel() const { return this->channel_; }
850 int8_t WiFiScanResult::get_rssi() const { return this->rssi_; }
851 bool WiFiScanResult::get_with_auth() const { return this->with_auth_; }
852 bool WiFiScanResult::get_is_hidden() const { return this->is_hidden_; }
853 
854 bool WiFiScanResult::operator==(const WiFiScanResult &rhs) const { return this->bssid_ == rhs.bssid_; }
855 
856 WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
857 
858 } // namespace wifi
859 } // namespace esphome
860 #endif
Nothing has been initialized yet.
const char * name
Definition: stm32flash.h:78
This component is responsible for managing the ESP WiFi interface.
const std::string & get_ssid() const
std::array< uint8_t, 6 > bssid_t
const optional< EAPAuth > & get_eap() const
static std::string format_mac_addr(const uint8_t mac[6])
network::IPAddresses get_ip_addresses()
const std::string & get_password() const
WiFiPowerSaveMode power_save_
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
void save_wifi_sta(const std::string &ssid, const std::string &password)
network::IPAddress wifi_dns_ip_(int num)
void set_sta_priority(const bssid_t bssid, float priority)
bool wifi_mode_(optional< bool > sta, optional< bool > ap)
const optional< bssid_t > & get_bssid() const
std::string str() const
Definition: ip_address.h:122
bool wifi_apply_output_power_(float output_power)
void add_sta(const WiFiAP &ap)
WiFi is in STA(+AP) mode and currently connecting to an AP a second time.
STL namespace.
WiFi is in STA(+AP) mode and successfully connected.
bool has_value() const
Definition: optional.h:87
network::IPAddress static_ip
void set_ap(const WiFiAP &ap)
Setup an Access Point that should be created if no connection to a station can be made...
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
const bssid_t & get_bssid() const
void set_channel(optional< uint8_t > channel)
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition: automation.h:95
bool save(const T *src)
Definition: preferences.h:21
network::IPAddress gateway
void start_connecting(const WiFiAP &ap, bool two)
WiFiComponent()
Construct a WiFiComponent.
CaptivePortal * global_captive_portal
void set_passive_scan(bool passive)
std::vector< WiFiScanResult > scan_result_
WiFi is in STA-only mode and currently scanning for APs.
Struct for setting static IPs in WiFiComponent.
void set_power_save_mode(WiFiPowerSaveMode power_save)
network::IPAddress dns1
The first DNS server. 0.0.0.0 for default.
WiFi is in STA(+AP) mode and currently connecting to an AP.
bool has_sta_priority(const bssid_t &bssid)
const optional< ManualIP > & get_manual_ip() const
ESPPreferences * global_preferences
const optional< uint8_t > & get_channel() const
void status_clear_warning()
Definition: component.cpp:166
WiFi is in cooldown mode because something went wrong, scanning will begin after a short period of ti...
esp_eap_ttls_phase2_types ttls_phase_2
void set_ssid(const std::string &ssid)
Application App
Global storage of Application pointer - only one Application can exist.
bool matches(const WiFiAP &config)
WiFiComponent * global_wifi_component
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:202
network::IPAddress get_dns_address(int num)
void set_reboot_timeout(uint32_t reboot_timeout)
std::array< IPAddress, 5 > IPAddresses
Definition: ip_address.h:141
bool is_name_add_mac_suffix_enabled() const
Definition: application.h:213
ESP32ImprovComponent * global_improv_component
void loop() override
Reconnect WiFi if required.
uint8_t priority
bool operator==(const WiFiScanResult &rhs) const
uint8_t status
Definition: bl0942.h:74
ESPPreferenceObject pref_
network::IPAddress dns2
The second DNS server. 0.0.0.0 for default.
void IRAM_ATTR HOT yield()
Definition: core.cpp:24
void set_fast_connect(bool fast_connect)
void set_manual_ip(optional< ManualIP > manual_ip)
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
Definition: helpers.cpp:182
std::vector< WiFiAP > sta_
optional< float > output_power_
network::IPAddress subnet
void set_sta(const WiFiAP &ap)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void set_bssid(bssid_t bssid)
const std::string & get_ssid() const
void set_eap(optional< EAPAuth > eap_auth)
void set_hidden(bool hidden)
ESPPreferenceObject fast_connect_pref_
float get_loop_priority() const override
void set_use_address(const std::string &use_address)
uint8_t m
Definition: bl0906.h:208
WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden)
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition: helpers.cpp:697
virtual bool sync()=0
Commit pending writes to flash.
std::string get_use_address() const
std::string get_compilation_time() const
Definition: application.h:215
float get_sta_priority(const bssid_t bssid)
void set_password(const std::string &password)
void setup() override
Setup WiFi interface.
float get_setup_priority() const override
WIFI setup_priority.
WiFi is in AP-only mode and internal AP is already enabled.
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26
const LogString * get_signal_bars(int8_t rssi)