7#if (ESP_IDF_VERSION_MAJOR >= 5 && ESP_IDF_VERSION_MINOR >= 1)
8#include <esp_eap_client.h>
14#if defined(USE_ESP32) || defined(USE_ESP_IDF)
18#include <user_interface.h>
32#ifdef USE_CAPTIVE_PORTAL
43static const char *
const TAG =
"wifi";
48 ESP_LOGCONFIG(TAG,
"Setting up WiFi...");
61 ESP_LOGCONFIG(TAG,
"Starting WiFi...");
74 ESP_LOGD(TAG,
"Loaded saved wifi settings: %s", save.ssid);
78 sta.set_password(save.password);
85 ESP_LOGV(TAG,
"Setting Output Power Option failed!");
89 ESP_LOGV(TAG,
"Setting Power Save Option failed!");
100 }
else if (this->
has_ap()) {
103 ESP_LOGV(TAG,
"Setting Output Power Option failed!");
105#ifdef USE_CAPTIVE_PORTAL
125 const uint32_t now =
millis();
163 ESP_LOGW(TAG,
"WiFi Connection lost... Reconnecting...");
182 ESP_LOGI(TAG,
"Starting fallback AP!");
184#ifdef USE_CAPTIVE_PORTAL
204 ESP_LOGE(TAG,
"Can't connect to WiFi, rebooting...");
216#ifdef USE_WIFI_11KV_SUPPORT
253 if (name.length() > 32) {
255 name.erase(name.begin() + 25, name.end() - 7);
257 name = name.substr(0, 32);
263 ESP_LOGCONFIG(TAG,
"Setting up AP...");
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());
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());
275 ESP_LOGCONFIG(TAG,
" IP Address: %s", this->
wifi_soft_ap_ip().str().c_str());
300 snprintf(save.ssid,
sizeof(save.ssid),
"%s", ssid.c_str());
301 snprintf(save.password,
sizeof(save.password),
"%s", password.c_str());
308 sta.set_password(password);
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());
319 ESP_LOGV(TAG,
" BSSID: %02X:%02X:%02X:%02X:%02X:%02X", b[0], b[1], b[2], b[3], b[4], b[5]);
321 ESP_LOGV(TAG,
" BSSID: Not Set");
324#ifdef USE_WIFI_WPA2_EAP
325 if (ap.
get_eap().has_value()) {
326 ESP_LOGV(TAG,
" WPA2 Enterprise authentication configured:");
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());
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());
341 bool ca_cert_present = eap_config.
ca_cert !=
nullptr && strlen(eap_config.
ca_cert);
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");
349 ESP_LOGV(TAG,
" Password: " LOG_SECRET(
"'%s'"), ap.
get_password().c_str());
350#ifdef USE_WIFI_WPA2_EAP
356 ESP_LOGV(TAG,
" Channel: Not Set");
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());
363 ESP_LOGV(TAG,
" Using DHCP IP");
365 ESP_LOGV(TAG,
" Hidden: %s", YESNO(ap.
get_hidden()));
369 ESP_LOGE(TAG,
"wifi_sta_connect_ failed!");
392 return LOG_STR(
"\033[0;32m"
398 }
else if (rssi >= -65) {
399 return LOG_STR(
"\033[0;33m"
406 }
else if (rssi >= -85) {
407 return LOG_STR(
"\033[0;33m"
415 return LOG_STR(
"\033[0;31m"
430 ESP_LOGCONFIG(TAG,
" WiFi is disabled!");
433 ESP_LOGCONFIG(TAG,
" SSID: " LOG_SECRET(
"'%s'"),
wifi_ssid().c_str());
436 ESP_LOGCONFIG(TAG,
" IP Address: %s", ip.str().c_str());
439 ESP_LOGCONFIG(TAG,
" BSSID: " LOG_SECRET(
"%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3],
441 ESP_LOGCONFIG(TAG,
" Hostname: '%s'",
App.
get_name().c_str());
443 ESP_LOGCONFIG(TAG,
" Signal strength: %d dB %s", rssi, LOG_STR_ARG(
get_signal_bars(rssi)));
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");
462 ESP_LOGD(TAG,
"Enabling WIFI...");
472 ESP_LOGD(TAG,
"Disabling WIFI...");
482 ESP_LOGD(TAG,
"Starting scan...");
490 ESP_LOGE(TAG,
"Scan timeout!");
497 ESP_LOGD(TAG,
"Found networks:");
499 ESP_LOGD(TAG,
" No network found!");
505 for (
auto &ap : this->
sta_) {
506 if (res.matches(ap)) {
507 res.set_matches(
true);
517 std::stable_sort(this->scan_result_.begin(), this->scan_result_.end(),
520 if (a.get_matches() && !b.get_matches())
522 if (!a.get_matches() && b.get_matches())
525 if (a.get_matches() && b.get_matches()) {
527 if (a.get_priority() != b.get_priority())
528 return a.get_priority() > b.get_priority();
534 for (
auto &res : this->scan_result_) {
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]);
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());
545 ESP_LOGD(TAG,
"- " LOG_SECRET(
"'%s'")
" " LOG_SECRET(
"(%s) ")
"%s", res.get_ssid().c_str(), bssid_s,
550 if (!this->scan_result_[0].get_matches()) {
551 ESP_LOGW(TAG,
"No matching network found!");
552 this->retry_connect();
556 WiFiAP connect_params;
557 WiFiScanResult scan_res = this->scan_result_[0];
558 for (
auto &config : this->sta_) {
560 if (!scan_res.matches(config)) {
564 if (config.get_hidden()) {
566 connect_params.set_hidden(
true);
567 connect_params.set_ssid(config.get_ssid());
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());
580 connect_params.set_manual_ip(config.get_manual_ip());
582#ifdef USE_WIFI_WPA2_EAP
584 connect_params.set_eap(config.get_eap());
588 connect_params.set_password(config.get_password());
595 this->selected_ap_ = connect_params;
596 this->start_connecting(connect_params,
false);
599void WiFiComponent::dump_config() {
600 ESP_LOGCONFIG(TAG,
"WiFi:");
601 this->print_connect_params_();
604void WiFiComponent::check_connecting_finished() {
605 auto status = this->wifi_sta_connect_status_();
607 if (
status == WiFiSTAConnectStatus::CONNECTED) {
608 if (wifi_ssid().empty()) {
609 ESP_LOGW(TAG,
"Incomplete connection.");
610 this->retry_connect();
615 this->retry_hidden_ =
false;
617 ESP_LOGI(TAG,
"WiFi Connected!");
618 this->print_connect_params_();
620 if (this->has_ap()) {
621#ifdef USE_CAPTIVE_PORTAL
622 if (this->is_captive_portal_active_()) {
626 ESP_LOGD(TAG,
"Disabling AP...");
627 this->wifi_mode_({},
false);
630 if (this->is_esp32_improv_active_()) {
636 this->num_retried_ = 0;
638 if (this->fast_connect_) {
639 this->save_fast_connect_settings_();
646 if (now - this->action_started_ > 30000) {
647 ESP_LOGW(TAG,
"Timeout while connecting to WiFi.");
648 this->retry_connect();
652 if (this->error_from_callback_) {
653 ESP_LOGW(TAG,
"Error while connecting to network.");
654 this->retry_connect();
658 if (
status == WiFiSTAConnectStatus::CONNECTING) {
662 if (
status == WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND) {
663 ESP_LOGW(TAG,
"WiFi network can not be found anymore.");
664 this->retry_connect();
668 if (
status == WiFiSTAConnectStatus::ERROR_CONNECT_FAILED) {
669 ESP_LOGW(TAG,
"Connecting to WiFi network failed. Are the credentials wrong?");
670 this->retry_connect();
674 ESP_LOGW(TAG,
"WiFi Unknown connection status %d", (
int)
status);
675 this->retry_connect();
678void WiFiComponent::retry_connect() {
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);
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) {
690 ESP_LOGW(TAG,
"Restarting WiFi adapter...");
691 this->wifi_mode_(
false, {});
693 this->num_retried_ = 0;
694 this->retry_hidden_ =
false;
697 ESP_LOGD(TAG,
"Retrying with hidden networks...");
698 this->retry_hidden_ =
true;
699 this->num_retried_++;
702 this->num_retried_++;
704 this->error_from_callback_ =
false;
708 this->start_connecting(this->selected_ap_,
true);
713 this->action_started_ =
millis();
716bool WiFiComponent::can_proceed() {
720 return this->is_connected();
722void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
723bool WiFiComponent::is_connected() {
725 this->wifi_sta_connect_status_() == WiFiSTAConnectStatus::CONNECTED && !this->error_from_callback_;
727void WiFiComponent::set_power_save_mode(
WiFiPowerSaveMode power_save) { this->power_save_ = power_save; }
729void WiFiComponent::set_passive_scan(
bool passive) { this->passive_scan_ = passive; }
731std::string WiFiComponent::format_mac_addr(
const uint8_t *mac) {
733 sprintf(buf,
"%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
736bool WiFiComponent::is_captive_portal_active_() {
737#ifdef USE_CAPTIVE_PORTAL
743bool WiFiComponent::is_esp32_improv_active_() {
751void WiFiComponent::load_fast_connect_settings_() {
754 if (this->fast_connect_pref_.load(&fast_connect_save)) {
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);
760 ESP_LOGD(TAG,
"Loaded saved fast_connect wifi settings");
764void WiFiComponent::save_fast_connect_settings_() {
766 uint8_t channel = get_wifi_channel();
768 if (bssid != this->selected_ap_.get_bssid() || channel != this->selected_ap_.get_channel()) {
771 memcpy(fast_connect_save.bssid, bssid.data(), 6);
772 fast_connect_save.channel = channel;
774 this->fast_connect_pref_.save(&fast_connect_save);
776 ESP_LOGD(TAG,
"Saved fast_connect wifi settings");
780void WiFiAP::set_ssid(
const std::string &ssid) { this->ssid_ = ssid; }
781void WiFiAP::set_bssid(
bssid_t bssid) { this->bssid_ = bssid; }
783void WiFiAP::set_password(
const std::string &password) { this->password_ = password; }
784#ifdef USE_WIFI_WPA2_EAP
789void WiFiAP::set_hidden(
bool hidden) { this->hidden_ = hidden; }
790const std::string &WiFiAP::get_ssid()
const {
return this->ssid_; }
792const std::string &WiFiAP::get_password()
const {
return this->password_; }
793#ifdef USE_WIFI_WPA2_EAP
798bool WiFiAP::get_hidden()
const {
return this->hidden_; }
800WiFiScanResult::WiFiScanResult(
const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi,
bool with_auth,
803 ssid_(std::move(ssid)),
806 with_auth_(with_auth),
807 is_hidden_(is_hidden) {}
814 }
else if (!config.
get_ssid().empty()) {
816 if (config.
get_ssid() != this->ssid_)
825#ifdef USE_WIFI_WPA2_EAP
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().
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
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.
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
const std::string & get_password() const
const optional< ManualIP > & get_manual_ip() const
This component is responsible for managing the ESP WiFi interface.
bool error_from_callback_
void add_sta(const WiFiAP &ap)
bool wifi_apply_power_save_()
void set_ap(const WiFiAP &ap)
Setup an Access Point that should be created if no connection to a station can be made.
ESPPreferenceObject pref_
void set_sta(const WiFiAP &ap)
bool has_sta_priority(const bssid_t &bssid)
std::string get_use_address() const
void check_connecting_finished()
void save_wifi_sta(const std::string &ssid, const std::string &password)
void load_fast_connect_settings_()
WiFiComponentState state_
void print_connect_params_()
void loop() override
Reconnect WiFi if required.
void check_scanning_finished()
float get_sta_priority(const bssid_t bssid)
network::IPAddress get_dns_address(int num)
WiFiComponent()
Construct a WiFiComponent.
std::vector< WiFiScanResult > scan_result_
bool wifi_start_ap_(const WiFiAP &ap)
int32_t get_wifi_channel()
void start_connecting(const WiFiAP &ap, bool two)
network::IPAddress wifi_subnet_mask_()
network::IPAddress wifi_soft_ap_ip()
network::IPAddress wifi_gateway_ip_()
Trigger * connect_trigger_
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
bool wifi_apply_hostname_()
bool wifi_sta_pre_setup_()
network::IPAddresses get_ip_addresses()
Trigger * disconnect_trigger_
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_sta_connect_(const WiFiAP &ap)
bool wifi_mode_(optional< bool > sta, optional< bool > ap)
network::IPAddresses wifi_sta_ip_addresses()
bool wifi_scan_start_(bool passive)
bool handled_connected_state_
void setup() override
Setup WiFi interface.
const std::string & get_ssid() const
uint8_t get_channel() const
bool get_with_auth() const
const bssid_t & get_bssid() const
void set_matches(bool matches)
bool matches(const WiFiAP &config)
bool get_is_hidden() const
bool operator==(const WiFiScanResult &rhs) const
CaptivePortal * global_captive_portal
ESP32ImprovComponent * global_improv_component
std::array< IPAddress, 5 > IPAddresses
std::array< uint8_t, 6 > bssid_t
const LogString * get_signal_bars(int8_t rssi)
@ 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.
WiFiComponent * global_wifi_component
Providing packet encoding functions for exchanging data with a remote host.
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
ESPPreferences * global_preferences
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
void IRAM_ATTR HOT yield()
void IRAM_ATTR HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
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.