ESPHome  2024.12.2
wifi_component.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "esphome/core/defines.h"
4 #ifdef USE_WIFI
8 #include "esphome/core/helpers.h"
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_ESP_IDF) && 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)
36 extern "C" {
37 #include <user_interface.h>
38 };
39 #endif
40 #endif
41 
42 #ifdef USE_RP2040
43 extern "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 
52 namespace esphome {
53 namespace wifi {
54 
56  char ssid[33];
57  char password[65];
58 } PACKED; // NOLINT
59 
61  uint8_t bssid[6];
62  uint8_t channel;
63 } PACKED; // NOLINT
64 
86 };
87 
88 enum class WiFiSTAConnectStatus : int {
89  IDLE,
90  CONNECTING,
91  CONNECTED,
94 };
95 
97 struct ManualIP {
103 };
104 
105 #ifdef USE_WIFI_WPA2_EAP
106 struct EAPAuth {
107  std::string identity; // required for all auth types
108  std::string username;
109  std::string password;
110  const char *ca_cert; // optionally verify authentication server
111  // used for EAP-TLS
112  const char *client_cert;
113  const char *client_key;
114 // used for EAP-TTLS
115 #ifdef USE_ESP_IDF
116  esp_eap_ttls_phase2_types ttls_phase_2;
117 #endif
118 };
119 #endif // USE_WIFI_WPA2_EAP
120 
121 using bssid_t = std::array<uint8_t, 6>;
122 
123 class WiFiAP {
124  public:
125  void set_ssid(const std::string &ssid);
126  void set_bssid(bssid_t bssid);
127  void set_bssid(optional<bssid_t> bssid);
128  void set_password(const std::string &password);
129 #ifdef USE_WIFI_WPA2_EAP
130  void set_eap(optional<EAPAuth> eap_auth);
131 #endif // USE_WIFI_WPA2_EAP
132  void set_channel(optional<uint8_t> channel);
133  void set_priority(float priority) { priority_ = priority; }
134  void set_manual_ip(optional<ManualIP> manual_ip);
135  void set_hidden(bool hidden);
136  const std::string &get_ssid() const;
137  const optional<bssid_t> &get_bssid() const;
138  const std::string &get_password() const;
139 #ifdef USE_WIFI_WPA2_EAP
140  const optional<EAPAuth> &get_eap() const;
141 #endif // USE_WIFI_WPA2_EAP
142  const optional<uint8_t> &get_channel() const;
143  float get_priority() const { return priority_; }
144  const optional<ManualIP> &get_manual_ip() const;
145  bool get_hidden() const;
146 
147  protected:
148  std::string ssid_;
150  std::string password_;
151 #ifdef USE_WIFI_WPA2_EAP
153 #endif // USE_WIFI_WPA2_EAP
155  float priority_{0};
157  bool hidden_{false};
158 };
159 
161  public:
162  WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden);
163 
164  bool matches(const WiFiAP &config);
165 
166  bool get_matches() const;
167  void set_matches(bool matches);
168  const bssid_t &get_bssid() const;
169  const std::string &get_ssid() const;
170  uint8_t get_channel() const;
171  int8_t get_rssi() const;
172  bool get_with_auth() const;
173  bool get_is_hidden() const;
174  float get_priority() const { return priority_; }
175  void set_priority(float priority) { priority_ = priority; }
176 
177  bool operator==(const WiFiScanResult &rhs) const;
178 
179  protected:
180  bool matches_{false};
182  std::string ssid_;
183  uint8_t channel_;
184  int8_t rssi_;
187  float priority_{0.0f};
188 };
189 
192  float priority;
193 };
194 
199 };
200 
201 #ifdef USE_ESP_IDF
202 struct IDFWiFiEvent;
203 #endif
204 
206 class WiFiComponent : public Component {
207  public:
209  WiFiComponent();
210 
211  void set_sta(const WiFiAP &ap);
212  WiFiAP get_sta() { return this->selected_ap_; }
213  void add_sta(const WiFiAP &ap);
214  void clear_sta();
215 
216 #ifdef USE_WIFI_AP
217 
224  void set_ap(const WiFiAP &ap);
225  WiFiAP get_ap() { return this->ap_; }
226 #endif // USE_WIFI_AP
227 
228  void enable();
229  void disable();
230  bool is_disabled();
231  void start_scanning();
232  void check_scanning_finished();
233  void start_connecting(const WiFiAP &ap, bool two);
234  void set_fast_connect(bool fast_connect);
235  void set_ap_timeout(uint32_t ap_timeout) { ap_timeout_ = ap_timeout; }
236 
237  void check_connecting_finished();
238 
239  void retry_connect();
240 
241  bool can_proceed() override;
242 
243  void set_reboot_timeout(uint32_t reboot_timeout);
244 
245  bool is_connected();
246 
247  void set_power_save_mode(WiFiPowerSaveMode power_save);
248  void set_output_power(float output_power) { output_power_ = output_power; }
249 
250  void set_passive_scan(bool passive);
251 
252  void save_wifi_sta(const std::string &ssid, const std::string &password);
253  // ========== INTERNAL METHODS ==========
254  // (In most use cases you won't need these)
256  void setup() override;
257  void start();
258  void dump_config() override;
260  float get_setup_priority() const override;
261  float get_loop_priority() const override;
262 
264  void loop() override;
265 
266  bool has_sta() const;
267  bool has_ap() const;
268 
269 #ifdef USE_WIFI_11KV_SUPPORT
270  void set_btm(bool btm);
271  void set_rrm(bool rrm);
272 #endif
273 
274  network::IPAddress get_dns_address(int num);
276  std::string get_use_address() const;
277  void set_use_address(const std::string &use_address);
278 
279  const std::vector<WiFiScanResult> &get_scan_result() const { return scan_result_; }
280 
281  network::IPAddress wifi_soft_ap_ip();
282 
283  bool has_sta_priority(const bssid_t &bssid) {
284  for (auto &it : this->sta_priorities_) {
285  if (it.bssid == bssid)
286  return true;
287  }
288  return false;
289  }
290  float get_sta_priority(const bssid_t bssid) {
291  for (auto &it : this->sta_priorities_) {
292  if (it.bssid == bssid)
293  return it.priority;
294  }
295  return 0.0f;
296  }
297  void set_sta_priority(const bssid_t bssid, float priority) {
298  for (auto &it : this->sta_priorities_) {
299  if (it.bssid == bssid) {
300  it.priority = priority;
301  return;
302  }
303  }
304  this->sta_priorities_.push_back(WiFiSTAPriority{
305  .bssid = bssid,
306  .priority = priority,
307  });
308  }
309 
310  network::IPAddresses wifi_sta_ip_addresses();
311  std::string wifi_ssid();
312  bssid_t wifi_bssid();
313 
314  int8_t wifi_rssi();
315 
316  void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
317 
318  Trigger<> *get_connect_trigger() const { return this->connect_trigger_; };
319  Trigger<> *get_disconnect_trigger() const { return this->disconnect_trigger_; };
320 
321  int32_t get_wifi_channel();
322 
323  protected:
324  static std::string format_mac_addr(const uint8_t mac[6]);
325 
326 #ifdef USE_WIFI_AP
327  void setup_ap_config_();
328 #endif // USE_WIFI_AP
329 
330  void print_connect_params_();
331 
332  void wifi_loop_();
333  bool wifi_mode_(optional<bool> sta, optional<bool> ap);
334  bool wifi_sta_pre_setup_();
335  bool wifi_apply_output_power_(float output_power);
336  bool wifi_apply_power_save_();
337  bool wifi_sta_ip_config_(optional<ManualIP> manual_ip);
338  bool wifi_apply_hostname_();
339  bool wifi_sta_connect_(const WiFiAP &ap);
340  void wifi_pre_setup_();
341  WiFiSTAConnectStatus wifi_sta_connect_status_();
342  bool wifi_scan_start_(bool passive);
343 
344 #ifdef USE_WIFI_AP
345  bool wifi_ap_ip_config_(optional<ManualIP> manual_ip);
346  bool wifi_start_ap_(const WiFiAP &ap);
347 #endif // USE_WIFI_AP
348 
349  bool wifi_disconnect_();
350 
351  network::IPAddress wifi_subnet_mask_();
352  network::IPAddress wifi_gateway_ip_();
353  network::IPAddress wifi_dns_ip_(int num);
354 
355  bool is_captive_portal_active_();
356  bool is_esp32_improv_active_();
357 
358  void load_fast_connect_settings_();
359  void save_fast_connect_settings_();
360 
361 #ifdef USE_ESP8266
362  static void wifi_event_callback(System_Event_t *event);
363  void wifi_scan_done_callback_(void *arg, STATUS status);
364  static void s_wifi_scan_done_callback(void *arg, STATUS status);
365 #endif
366 
367 #ifdef USE_ESP32_FRAMEWORK_ARDUINO
368  void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info);
369  void wifi_scan_done_callback_();
370 #endif
371 #ifdef USE_ESP_IDF
372  void wifi_process_event_(IDFWiFiEvent *data);
373 #endif
374 
375 #ifdef USE_RP2040
376  static int s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result);
377  void wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result);
378 #endif
379 
380 #ifdef USE_LIBRETINY
381  void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info);
382  void wifi_scan_done_callback_();
383 #endif
384 
385  std::string use_address_;
386  std::vector<WiFiAP> sta_;
387  std::vector<WiFiSTAPriority> sta_priorities_;
389  bool fast_connect_{false};
390  bool retry_hidden_{false};
391 
392  bool has_ap_{false};
395  bool handled_connected_state_{false};
396  uint32_t action_started_;
397  uint8_t num_retried_{0};
398  uint32_t last_connected_{0};
399  uint32_t reboot_timeout_{};
400  uint32_t ap_timeout_{};
402  bool error_from_callback_{false};
403  std::vector<WiFiScanResult> scan_result_;
404  bool scan_done_{false};
405  bool ap_setup_{false};
407  bool passive_scan_{false};
410  bool has_saved_wifi_settings_{false};
411 #ifdef USE_WIFI_11KV_SUPPORT
412  bool btm_{false};
413  bool rrm_{false};
414 #endif
416  bool got_ipv4_address_{false};
417 #if USE_NETWORK_IPV6
418  uint8_t num_ipv6_addresses_{0};
419 #endif /* USE_NETWORK_IPV6 */
420 
421  Trigger<> *connect_trigger_{new Trigger<>()};
422  Trigger<> *disconnect_trigger_{new Trigger<>()};
423 };
424 
425 extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
426 
427 template<typename... Ts> class WiFiConnectedCondition : public Condition<Ts...> {
428  public:
429  bool check(Ts... x) override { return global_wifi_component->is_connected(); }
430 };
431 
432 template<typename... Ts> class WiFiEnabledCondition : public Condition<Ts...> {
433  public:
434  bool check(Ts... x) override { return !global_wifi_component->is_disabled(); }
435 };
436 
437 template<typename... Ts> class WiFiEnableAction : public Action<Ts...> {
438  public:
439  void play(Ts... x) override { global_wifi_component->enable(); }
440 };
441 
442 template<typename... Ts> class WiFiDisableAction : public Action<Ts...> {
443  public:
444  void play(Ts... x) override { global_wifi_component->disable(); }
445 };
446 
447 template<typename... Ts> class WiFiConfigureAction : public Action<Ts...>, public Component {
448  public:
449  TEMPLATABLE_VALUE(std::string, ssid)
450  TEMPLATABLE_VALUE(std::string, password)
451  TEMPLATABLE_VALUE(bool, save)
452  TEMPLATABLE_VALUE(uint32_t, connection_timeout)
453 
454  void play(Ts... x) override {
455  auto ssid = this->ssid_.value(x...);
456  auto password = this->password_.value(x...);
457  // Avoid multiple calls
458  if (this->connecting_)
459  return;
460  // If already connected to the same AP, do nothing
461  if (global_wifi_component->wifi_ssid() == ssid) {
462  // Callback to notify the user that the connection was successful
463  this->connect_trigger_->trigger();
464  return;
465  }
466  // Create a new WiFiAP object with the new SSID and password
467  this->new_sta_.set_ssid(ssid);
468  this->new_sta_.set_password(password);
469  // Save the current STA
470  this->old_sta_ = global_wifi_component->get_sta();
471  // Disable WiFi
472  global_wifi_component->disable();
473  // Set the state to connecting
474  this->connecting_ = true;
475  // Store the new STA so once the WiFi is enabled, it will connect to it
476  // This is necessary because the WiFiComponent will raise an error and fallback to the saved STA
477  // if trying to connect to a new STA while already connected to another one
478  if (this->save_.value(x...)) {
479  global_wifi_component->save_wifi_sta(new_sta_.get_ssid(), new_sta_.get_password());
480  } else {
481  global_wifi_component->set_sta(new_sta_);
482  }
483  // Enable WiFi
484  global_wifi_component->enable();
485  // Set timeout for the connection
486  this->set_timeout("wifi-connect-timeout", this->connection_timeout_.value(x...), [this]() {
487  this->connecting_ = false;
488  // If the timeout is reached, stop connecting and revert to the old AP
489  global_wifi_component->disable();
490  global_wifi_component->save_wifi_sta(old_sta_.get_ssid(), old_sta_.get_password());
491  global_wifi_component->enable();
492  // Callback to notify the user that the connection failed
493  this->error_trigger_->trigger();
494  });
495  }
496 
497  Trigger<> *get_connect_trigger() const { return this->connect_trigger_; }
498  Trigger<> *get_error_trigger() const { return this->error_trigger_; }
499 
500  void loop() override {
501  if (!this->connecting_)
502  return;
503  if (global_wifi_component->is_connected()) {
504  // The WiFi is connected, stop the timeout and reset the connecting flag
505  this->cancel_timeout("wifi-connect-timeout");
506  this->connecting_ = false;
507  if (global_wifi_component->wifi_ssid() == this->new_sta_.get_ssid()) {
508  // Callback to notify the user that the connection was successful
509  this->connect_trigger_->trigger();
510  } else {
511  // Callback to notify the user that the connection failed
512  this->error_trigger_->trigger();
513  }
514  }
515  }
516 
517  protected:
518  bool connecting_{false};
521  Trigger<> *connect_trigger_{new Trigger<>()};
522  Trigger<> *error_trigger_{new Trigger<>()};
523 };
524 
525 } // namespace wifi
526 } // namespace esphome
527 #endif
void setup()
Nothing has been initialized yet.
void loop()
This component is responsible for managing the ESP WiFi interface.
void set_enable_on_boot(bool enable_on_boot)
void set_priority(float priority)
std::array< uint8_t, 6 > bssid_t
std::string get_use_address()
Get the active network hostname.
Definition: util.cpp:52
float get_priority() const
void save_wifi_sta(const std::string &ssid, const std::string &password)
void set_output_power(float output_power)
Trigger * get_disconnect_trigger() const
const std::vector< WiFiScanResult > & get_scan_result() const
uint16_t x
Definition: tt21100.cpp:17
void set_sta_priority(const bssid_t bssid, float priority)
optional< ManualIP > manual_ip_
WiFi is in STA(+AP) mode and currently connecting to an AP a second time.
void play(Ts... x) override
WiFi is in STA(+AP) mode and successfully connected.
void set_priority(float priority)
network::IPAddress static_ip
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition: util.cpp:15
network::IPAddress gateway
void set_ap_timeout(uint32_t ap_timeout)
std::vector< WiFiScanResult > scan_result_
WiFi is in STA-only mode and currently scanning for APs.
network::IPAddresses get_ip_addresses()
Definition: util.cpp:40
Struct for setting static IPs in WiFiComponent.
network::IPAddress dns1
The first DNS server. 0.0.0.0 for default.
Base class for all automation conditions.
Definition: automation.h:74
WiFi is in STA(+AP) mode and currently connecting to an AP.
bool has_sta_priority(const bssid_t &bssid)
WiFi is in cooldown mode because something went wrong, scanning will begin after a short period of ti...
optional< bssid_t > bssid_
esp_eap_ttls_phase2_types ttls_phase_2
Trigger * get_connect_trigger() const
TEMPLATABLE_VALUE(std::string, ssid) TEMPLATABLE_VALUE(std WiFiAP new_sta_
WiFiComponent * global_wifi_component
std::array< IPAddress, 5 > IPAddresses
Definition: ip_address.h:141
optional< uint8_t > channel_
uint8_t priority
uint8_t status
Definition: bl0942.h:74
ESPPreferenceObject pref_
bool is_disabled()
Return whether the network is disabled (only wifi for now)
Definition: util.cpp:32
network::IPAddress dns2
The second DNS server. 0.0.0.0 for default.
struct esphome::wifi::SavedWifiSettings PACKED
std::vector< WiFiSTAPriority > sta_priorities_
bool operator==(optional< T > const &x, optional< U > const &y)
Definition: optional.h:113
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
ESPPreferenceObject fast_connect_pref_
void play(Ts... x) override
float get_sta_priority(const bssid_t bssid)
optional< EAPAuth > eap_
WiFi is in AP-only mode and internal AP is already enabled.