ESPHome 2025.12.0-dev
Loading...
Searching...
No Matches
wifi_component.h
Go to the documentation of this file.
1#pragma once
2
4#ifdef USE_WIFI
9
10#include <string>
11#include <vector>
12
13#ifdef USE_ESP32_FRAMEWORK_ARDUINO
14#include <WiFi.h>
15#include <WiFiType.h>
16#include <esp_wifi.h>
17#endif
18
19#ifdef USE_LIBRETINY
20#include <WiFi.h>
21#endif
22
23#if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP)
24#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1)
25#include <esp_eap_client.h>
26#else
27#include <esp_wpa2.h>
28#endif
29#endif
30
31#ifdef USE_ESP8266
32#include <ESP8266WiFi.h>
33#include <ESP8266WiFiType.h>
34
35#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0)
36extern "C" {
37#include <user_interface.h>
38};
39#endif
40#endif
41
42#ifdef USE_RP2040
43extern "C" {
44#include "cyw43.h"
45#include "cyw43_country.h"
46#include "pico/cyw43_arch.h"
47}
48
49#include <WiFi.h>
50#endif
51
52namespace esphome {
53namespace wifi {
54
56static constexpr int8_t WIFI_RSSI_DISCONNECTED = -127;
57
59 char ssid[33];
60 char password[65];
61} PACKED; // NOLINT
62
64 uint8_t bssid[6];
65 uint8_t channel;
66 int8_t ap_index;
67} PACKED; // NOLINT
68
85
93
95enum class WiFiRetryPhase : uint8_t {
98#ifdef USE_WIFI_FAST_CONNECT
101#endif
110};
111
120
121#ifdef USE_WIFI_WPA2_EAP
122struct EAPAuth {
123 std::string identity; // required for all auth types
124 std::string username;
125 std::string password;
126 const char *ca_cert; // optionally verify authentication server
127 // used for EAP-TLS
128 const char *client_cert;
129 const char *client_key;
130// used for EAP-TTLS
131#ifdef USE_ESP32
132 esp_eap_ttls_phase2_types ttls_phase_2;
133#endif
134};
135#endif // USE_WIFI_WPA2_EAP
136
137using bssid_t = std::array<uint8_t, 6>;
138
139// Use std::vector for RP2040 since scan count is unknown (callback-based)
140// Use FixedVector for other platforms where count is queried first
141#ifdef USE_RP2040
142template<typename T> using wifi_scan_vector_t = std::vector<T>;
143#else
144template<typename T> using wifi_scan_vector_t = FixedVector<T>;
145#endif
146
147class WiFiAP {
148 public:
149 void set_ssid(const std::string &ssid);
150 void set_bssid(bssid_t bssid);
151 void set_bssid(optional<bssid_t> bssid);
152 void set_password(const std::string &password);
153#ifdef USE_WIFI_WPA2_EAP
154 void set_eap(optional<EAPAuth> eap_auth);
155#endif // USE_WIFI_WPA2_EAP
156 void set_channel(optional<uint8_t> channel);
158#ifdef USE_WIFI_MANUAL_IP
159 void set_manual_ip(optional<ManualIP> manual_ip);
160#endif
161 void set_hidden(bool hidden);
162 const std::string &get_ssid() const;
163 const optional<bssid_t> &get_bssid() const;
164 const std::string &get_password() const;
165#ifdef USE_WIFI_WPA2_EAP
166 const optional<EAPAuth> &get_eap() const;
167#endif // USE_WIFI_WPA2_EAP
168 const optional<uint8_t> &get_channel() const;
169 int8_t get_priority() const { return priority_; }
170#ifdef USE_WIFI_MANUAL_IP
171 const optional<ManualIP> &get_manual_ip() const;
172#endif
173 bool get_hidden() const;
174
175 protected:
176 std::string ssid_;
177 std::string password_;
179#ifdef USE_WIFI_WPA2_EAP
181#endif // USE_WIFI_WPA2_EAP
182#ifdef USE_WIFI_MANUAL_IP
184#endif
186 int8_t priority_{0};
187 bool hidden_{false};
188};
189
191 public:
192 WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden);
193
194 bool matches(const WiFiAP &config) const;
195
196 bool get_matches() const;
197 void set_matches(bool matches);
198 const bssid_t &get_bssid() const;
199 const std::string &get_ssid() const;
200 uint8_t get_channel() const;
201 int8_t get_rssi() const;
202 bool get_with_auth() const;
203 bool get_is_hidden() const;
204 int8_t get_priority() const { return priority_; }
206
207 bool operator==(const WiFiScanResult &rhs) const;
208
209 protected:
211 uint8_t channel_;
212 int8_t rssi_;
213 std::string ssid_;
214 int8_t priority_{0};
215 bool matches_{false};
218};
219
224
230
236
237#ifdef USE_ESP32
238struct IDFWiFiEvent;
239#endif
240
242class WiFiComponent : public Component {
243 public:
246
247 void set_sta(const WiFiAP &ap);
248 // Returns a copy of the currently selected AP configuration
249 WiFiAP get_sta() const;
250 void init_sta(size_t count);
251 void add_sta(const WiFiAP &ap);
252 void clear_sta() {
253 this->sta_.clear();
254 this->selected_sta_index_ = -1;
255 }
256
257#ifdef USE_WIFI_AP
265 void set_ap(const WiFiAP &ap);
266 WiFiAP get_ap() { return this->ap_; }
267 void set_ap_timeout(uint32_t ap_timeout) { ap_timeout_ = ap_timeout; }
268#endif // USE_WIFI_AP
269
270 void enable();
271 void disable();
272 bool is_disabled();
273 void start_scanning();
275 void start_connecting(const WiFiAP &ap);
276 // Backward compatibility overload - ignores 'two' parameter
277 void start_connecting(const WiFiAP &ap, bool /* two */) { this->start_connecting(ap); }
278
280
281 void retry_connect();
282
283 void set_reboot_timeout(uint32_t reboot_timeout);
284
285 bool is_connected();
286
287 void set_power_save_mode(WiFiPowerSaveMode power_save);
288 void set_min_auth_mode(WifiMinAuthMode min_auth_mode) { min_auth_mode_ = min_auth_mode; }
289 void set_output_power(float output_power) { output_power_ = output_power; }
290
291 void set_passive_scan(bool passive);
292
293 void save_wifi_sta(const std::string &ssid, const std::string &password);
294 // ========== INTERNAL METHODS ==========
295 // (In most use cases you won't need these)
297 void setup() override;
298 void start();
299 void dump_config() override;
300 void restart_adapter();
302 float get_setup_priority() const override;
303 float get_loop_priority() const override;
304
306 void loop() override;
307
308 bool has_sta() const;
309 bool has_ap() const;
310
311#ifdef USE_WIFI_11KV_SUPPORT
312 void set_btm(bool btm);
313 void set_rrm(bool rrm);
314#endif
315
318 const char *get_use_address() const;
319 void set_use_address(const char *use_address);
320
322
324
325 bool has_sta_priority(const bssid_t &bssid) {
326 for (auto &it : this->sta_priorities_) {
327 if (it.bssid == bssid)
328 return true;
329 }
330 return false;
331 }
332 int8_t get_sta_priority(const bssid_t bssid) {
333 for (auto &it : this->sta_priorities_) {
334 if (it.bssid == bssid)
335 return it.priority;
336 }
337 return 0;
338 }
339 void set_sta_priority(const bssid_t bssid, int8_t priority) {
340 for (auto &it : this->sta_priorities_) {
341 if (it.bssid == bssid) {
342 it.priority = priority;
343 return;
344 }
345 }
346 this->sta_priorities_.push_back(WiFiSTAPriority{
347 .bssid = bssid,
348 .priority = priority,
349 });
350 }
351
353 std::string wifi_ssid();
355
356 int8_t wifi_rssi();
357
358 void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
359 void set_keep_scan_results(bool keep_scan_results) { this->keep_scan_results_ = keep_scan_results; }
360
363
364 int32_t get_wifi_channel();
365
366 protected:
367#ifdef USE_WIFI_AP
368 void setup_ap_config_();
369#endif // USE_WIFI_AP
370
373
378 bool transition_to_phase_(WiFiRetryPhase new_phase);
381 bool needs_scan_results_() const;
387 int8_t find_first_non_hidden_index_() const;
390 bool ssid_was_seen_in_scan_(const std::string &ssid) const;
394 int8_t find_next_hidden_sta_(int8_t start_index);
404 const WiFiAP *get_selected_sta_() const {
405 if (this->selected_sta_index_ >= 0 && static_cast<size_t>(this->selected_sta_index_) < this->sta_.size()) {
406 return &this->sta_[this->selected_sta_index_];
407 }
408 return nullptr;
409 }
410
412 if (this->selected_sta_index_ < 0 || static_cast<size_t>(this->selected_sta_index_) >= this->sta_.size()) {
413 this->selected_sta_index_ = this->sta_.empty() ? -1 : 0;
414 }
415 }
416
417 bool all_networks_hidden_() const {
418 if (this->sta_.empty())
419 return false;
420 for (const auto &ap : this->sta_) {
421 if (!ap.get_hidden())
422 return false;
423 }
424 return true;
425 }
426
427 void wifi_loop_();
429 bool wifi_sta_pre_setup_();
430 bool wifi_apply_output_power_(float output_power);
432 bool wifi_sta_ip_config_(const optional<ManualIP> &manual_ip);
434 bool wifi_sta_connect_(const WiFiAP &ap);
435 void wifi_pre_setup_();
437 bool wifi_scan_start_(bool passive);
438
439#ifdef USE_WIFI_AP
440 bool wifi_ap_ip_config_(const optional<ManualIP> &manual_ip);
441 bool wifi_start_ap_(const WiFiAP &ap);
442#endif // USE_WIFI_AP
443
444 bool wifi_disconnect_();
445
449
452
453#ifdef USE_WIFI_FAST_CONNECT
456#endif
457
458#ifdef USE_ESP8266
459 static void wifi_event_callback(System_Event_t *event);
460 void wifi_scan_done_callback_(void *arg, STATUS status);
461 static void s_wifi_scan_done_callback(void *arg, STATUS status);
462#endif
463
464#ifdef USE_ESP32_FRAMEWORK_ARDUINO
465 void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info);
467#endif
468#ifdef USE_ESP32
469 void wifi_process_event_(IDFWiFiEvent *data);
470#endif
471
472#ifdef USE_RP2040
473 static int s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result);
474 void wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result);
475#endif
476
477#ifdef USE_LIBRETINY
478 void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info);
480#endif
481
483 std::vector<WiFiSTAPriority> sta_priorities_;
485#ifdef USE_WIFI_AP
487#endif
490#ifdef USE_WIFI_FAST_CONNECT
492#endif
493
494 // Group all 32-bit integers together
496 uint32_t last_connected_{0};
497 uint32_t reboot_timeout_{};
498#ifdef USE_WIFI_AP
499 uint32_t ap_timeout_{};
500#endif
501
502 // Group all 8-bit values together
507 uint8_t num_retried_{0};
508 // Index into sta_ array for the currently selected AP configuration (-1 = none selected)
509 // Used to access password, manual_ip, priority, EAP settings, and hidden flag
510 // int8_t limits to 127 APs (enforced in __init__.py via MAX_WIFI_NETWORKS)
512
513#if USE_NETWORK_IPV6
515#endif /* USE_NETWORK_IPV6 */
516
517 // Group all boolean values together
518 bool has_ap_{false};
521 bool scan_done_{false};
522 bool ap_setup_{false};
523 bool passive_scan_{false};
525#ifdef USE_WIFI_11KV_SUPPORT
526 bool btm_{false};
527 bool rrm_{false};
528#endif
530 bool got_ipv4_address_{false};
532
533 // Pointers at the end (naturally aligned)
536
537 private:
538 // Stores a pointer to a string literal (static storage duration).
539 // ONLY set from Python-generated code with string literals - never dynamic strings.
540 const char *use_address_{""};
541};
542
543extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
544
545template<typename... Ts> class WiFiConnectedCondition : public Condition<Ts...> {
546 public:
547 bool check(const Ts &...x) override { return global_wifi_component->is_connected(); }
548};
549
550template<typename... Ts> class WiFiEnabledCondition : public Condition<Ts...> {
551 public:
552 bool check(const Ts &...x) override { return !global_wifi_component->is_disabled(); }
553};
554
555template<typename... Ts> class WiFiEnableAction : public Action<Ts...> {
556 public:
557 void play(const Ts &...x) override { global_wifi_component->enable(); }
558};
559
560template<typename... Ts> class WiFiDisableAction : public Action<Ts...> {
561 public:
562 void play(const Ts &...x) override { global_wifi_component->disable(); }
563};
564
565template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, public Component {
566 public:
567 TEMPLATABLE_VALUE(std::string, ssid)
568 TEMPLATABLE_VALUE(std::string, password)
569 TEMPLATABLE_VALUE(bool, save)
570 TEMPLATABLE_VALUE(uint32_t, connection_timeout)
571
572 void play(const Ts &...x) override {
573 auto ssid = this->ssid_.value(x...);
574 auto password = this->password_.value(x...);
575 // Avoid multiple calls
576 if (this->connecting_)
577 return;
578 // If already connected to the same AP, do nothing
579 if (global_wifi_component->wifi_ssid() == ssid) {
580 // Callback to notify the user that the connection was successful
581 this->connect_trigger_->trigger();
582 return;
583 }
584 // Create a new WiFiAP object with the new SSID and password
585 this->new_sta_.set_ssid(ssid);
586 this->new_sta_.set_password(password);
587 // Save the current STA
588 this->old_sta_ = global_wifi_component->get_sta();
589 // Disable WiFi
591 // Set the state to connecting
592 this->connecting_ = true;
593 // Store the new STA so once the WiFi is enabled, it will connect to it
594 // This is necessary because the WiFiComponent will raise an error and fallback to the saved STA
595 // if trying to connect to a new STA while already connected to another one
596 if (this->save_.value(x...)) {
597 global_wifi_component->save_wifi_sta(new_sta_.get_ssid(), new_sta_.get_password());
598 } else {
600 }
601 // Enable WiFi
603 // Set timeout for the connection
604 this->set_timeout("wifi-connect-timeout", this->connection_timeout_.value(x...), [this, x...]() {
605 // If the timeout is reached, stop connecting and revert to the old AP
606 global_wifi_component->disable();
607 global_wifi_component->save_wifi_sta(old_sta_.get_ssid(), old_sta_.get_password());
608 global_wifi_component->enable();
609 // Start a timeout for the fallback if the connection to the old AP fails
610 this->set_timeout("wifi-fallback-timeout", this->connection_timeout_.value(x...), [this]() {
611 this->connecting_ = false;
612 this->error_trigger_->trigger();
613 });
614 });
615 }
616
617 Trigger<> *get_connect_trigger() const { return this->connect_trigger_; }
618 Trigger<> *get_error_trigger() const { return this->error_trigger_; }
619
620 void loop() override {
621 if (!this->connecting_)
622 return;
624 // The WiFi is connected, stop the timeout and reset the connecting flag
625 this->cancel_timeout("wifi-connect-timeout");
626 this->cancel_timeout("wifi-fallback-timeout");
627 this->connecting_ = false;
628 if (global_wifi_component->wifi_ssid() == this->new_sta_.get_ssid()) {
629 // Callback to notify the user that the connection was successful
630 this->connect_trigger_->trigger();
631 } else {
632 // Callback to notify the user that the connection failed
633 this->error_trigger_->trigger();
634 }
635 }
636 }
637
638 protected:
639 bool connecting_{false};
642 Trigger<> *connect_trigger_{new Trigger<>()};
643 Trigger<> *error_trigger_{new Trigger<>()};
644};
645
646} // namespace wifi
647} // namespace esphome
648#endif
virtual void play(const Ts &...x)=0
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Base class for all automation conditions.
Definition automation.h:148
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:184
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:169
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
int8_t get_priority() const
void set_hidden(bool hidden)
void set_priority(int8_t priority)
This component is responsible for managing the ESP WiFi interface.
Trigger * get_connect_trigger() const
void add_sta(const WiFiAP &ap)
bool load_fast_connect_settings_(WiFiAP &params)
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)
const WiFiAP * get_selected_sta_() const
int8_t get_sta_priority(const bssid_t bssid)
void log_and_adjust_priority_for_failed_connect_()
Log failed connection and decrease BSSID priority to avoid repeated attempts.
void save_wifi_sta(const std::string &ssid, const std::string &password)
wifi_scan_vector_t< WiFiScanResult > scan_result_
void set_sta_priority(const bssid_t bssid, int8_t priority)
void loop() override
Reconnect WiFi if required.
void start_connecting(const WiFiAP &ap)
void set_enable_on_boot(bool enable_on_boot)
void advance_to_next_target_or_increment_retry_()
Advance to next target (AP/SSID) within current phase, or increment retry counter Called when staying...
bool wifi_sta_ip_config_(const optional< ManualIP > &manual_ip)
static int s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result)
network::IPAddress get_dns_address(int num)
static void wifi_event_callback(System_Event_t *event)
WiFiComponent()
Construct a WiFiComponent.
void wifi_process_event_(IDFWiFiEvent *data)
std::vector< WiFiSTAPriority > sta_priorities_
void set_min_auth_mode(WifiMinAuthMode min_auth_mode)
void start_connecting(const WiFiAP &ap, bool)
void set_passive_scan(bool passive)
void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info)
static void s_wifi_scan_done_callback(void *arg, STATUS status)
void set_power_save_mode(WiFiPowerSaveMode power_save)
int8_t find_next_hidden_sta_(int8_t start_index)
Find next SSID that wasn't in scan results (might be hidden) Returns index of next potentially hidden...
ESPPreferenceObject fast_connect_pref_
void clear_priorities_if_all_min_()
Clear BSSID priority tracking if all priorities are at minimum (saves memory)
void wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result)
WiFiRetryPhase determine_next_phase_()
Determine next retry phase based on current state and failure conditions.
network::IPAddress wifi_dns_ip_(int num)
float get_loop_priority() const override
network::IPAddresses get_ip_addresses()
float get_setup_priority() const override
WIFI setup_priority.
void set_output_power(float output_power)
FixedVector< WiFiAP > sta_
int8_t find_first_non_hidden_index_() const
Find the index of the first non-hidden network Returns where EXPLICIT_HIDDEN phase would have stopped...
bool ssid_was_seen_in_scan_(const std::string &ssid) const
Check if an SSID was seen in the most recent scan results Used to skip hidden mode for SSIDs we know ...
bool wifi_ap_ip_config_(const optional< ManualIP > &manual_ip)
bool needs_scan_results_() const
Check if we need valid scan results for the current phase but don't have any Returns true if the phas...
bool transition_to_phase_(WiFiRetryPhase new_phase)
Transition to a new retry phase with logging Returns true if a scan was started (caller should wait),...
Trigger * get_disconnect_trigger() const
optional< float > output_power_
void set_ap_timeout(uint32_t ap_timeout)
bool wifi_apply_output_power_(float output_power)
const char * get_use_address() const
WiFiSTAConnectStatus wifi_sta_connect_status_()
bool went_through_explicit_hidden_phase_() const
Check if we went through EXPLICIT_HIDDEN phase (first network is marked hidden) Used in RETRY_HIDDEN ...
bool wifi_mode_(optional< bool > sta, optional< bool > ap)
void set_reboot_timeout(uint32_t reboot_timeout)
network::IPAddresses wifi_sta_ip_addresses()
void set_keep_scan_results(bool keep_scan_results)
void start_initial_connection_()
Start initial connection - either scan or connect directly to hidden networks.
void setup() override
Setup WiFi interface.
void set_use_address(const char *use_address)
const wifi_scan_vector_t< WiFiScanResult > & get_scan_result() const
TEMPLATABLE_VALUE(std::string, ssid) TEMPLATABLE_VALUE(std WiFiAP new_sta_
bool check(const Ts &...x) override
void play(const Ts &...x) override
void play(const Ts &...x) override
bool check(const Ts &...x) override
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) const
void set_priority(int8_t priority)
bool operator==(const WiFiScanResult &rhs) const
uint8_t priority
void loop()
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:144
std::array< uint8_t, 6 > bssid_t
std::vector< T > wifi_scan_vector_t
struct esphome::wifi::SavedWifiSettings PACKED
WiFiRetryPhase
Tracks the current retry strategy/phase for WiFi connection attempts.
@ RETRY_HIDDEN
Retry networks not found in scan (might be hidden)
@ RESTARTING_ADAPTER
Restarting WiFi adapter to clear stuck state.
@ INITIAL_CONNECT
Initial connection attempt (varies based on fast_connect setting)
@ EXPLICIT_HIDDEN
Explicitly hidden networks (user marked as hidden, try before scanning)
@ FAST_CONNECT_CYCLING_APS
Fast connect mode: cycling through configured APs (config-only, no scan)
@ SCAN_CONNECTING
Scan-based: connecting to best AP from scan results.
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_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
esp_eap_ttls_phase2_types ttls_phase_2
Struct for setting static IPs in WiFiComponent.
network::IPAddress static_ip
network::IPAddress dns1
The first DNS server. 0.0.0.0 for default.
network::IPAddress gateway
network::IPAddress dns2
The second DNS server. 0.0.0.0 for default.
network::IPAddress subnet
uint16_t x
Definition tt21100.cpp:5