9#include "lwip/ip_addr.h"
18#include <wlan_ui_pub.h>
25#include <wifi_structures.h>
37static const char *
const TAG =
"wifi_lt";
52static constexpr size_t EVENT_QUEUE_SIZE = 16;
53static QueueHandle_t s_event_queue =
nullptr;
54static volatile uint32_t s_event_queue_overflow_count =
60 arduino_event_id_t event_id;
78 } sta_authmode_change;
101static uint8_t s_ignored_disconnect_count = 0;
106static constexpr uint8_t IGNORED_DISCONNECT_THRESHOLD = 3;
109 uint8_t current_mode = WiFi.getMode();
110 bool current_sta = current_mode & 0b01;
111 bool current_ap = current_mode & 0b10;
112 bool enable_sta = sta.value_or(current_sta);
113 bool enable_ap = ap.value_or(current_ap);
114 if (current_sta == enable_sta && current_ap == enable_ap)
117 if (enable_sta && !current_sta) {
118 ESP_LOGV(TAG,
"Enabling STA");
119 }
else if (!enable_sta && current_sta) {
120 ESP_LOGV(TAG,
"Disabling STA");
122 if (enable_ap && !current_ap) {
123 ESP_LOGV(TAG,
"Enabling AP");
124 }
else if (!enable_ap && current_ap) {
125 ESP_LOGV(TAG,
"Disabling AP");
133 bool ret = WiFi.mode(
static_cast<wifi_mode_t
>(
mode));
136 ESP_LOGW(TAG,
"Setting mode failed");
145 int8_t
val =
static_cast<int8_t
>(output_power * 4);
146 return WiFi.setTxPower(
val);
152 WiFi.setAutoReconnect(
false);
158#ifdef USE_WIFI_POWER_SAVE_LISTENERS
172 if (!manual_ip.has_value()) {
176 WiFi.config(manual_ip->static_ip, manual_ip->gateway, manual_ip->subnet, manual_ip->dns1, manual_ip->dns2);
185 addresses[0] = WiFi.localIP();
188 auto v6_addresses = WiFi.allLocalIPv6();
189 for (
auto address : v6_addresses) {
190 addresses[i++] = network::IPAddress(
address.toString().c_str());
206 String ssid = WiFi.SSID();
207 if (ssid && strcmp(ssid.c_str(), ap.ssid_.c_str()) != 0) {
211#ifdef USE_WIFI_MANUAL_IP
225 s_ignored_disconnect_count = 0;
227 WiFiStatus
status = WiFi.begin(ap.ssid_.c_str(), ap.password_.empty() ? NULL : ap.password_.c_str(),
229 ap.has_bssid() ? ap.get_bssid().data() : NULL);
230 if (status != WL_CONNECTED) {
231 ESP_LOGW(TAG,
"esp_wifi_connect failed: %d", status);
243 case WIFI_AUTH_WPA_PSK:
245 case WIFI_AUTH_WPA2_PSK:
247 case WIFI_AUTH_WPA_WPA2_PSK:
248 return "WPA/WPA2 PSK";
270 case WIFI_REASON_AUTH_EXPIRE:
271 return "Auth Expired";
272 case WIFI_REASON_AUTH_LEAVE:
274 case WIFI_REASON_ASSOC_EXPIRE:
275 return "Association Expired";
276 case WIFI_REASON_ASSOC_TOOMANY:
277 return "Too Many Associations";
278 case WIFI_REASON_NOT_AUTHED:
279 return "Not Authenticated";
280 case WIFI_REASON_NOT_ASSOCED:
281 return "Not Associated";
282 case WIFI_REASON_ASSOC_LEAVE:
283 return "Association Leave";
284 case WIFI_REASON_ASSOC_NOT_AUTHED:
285 return "Association not Authenticated";
286 case WIFI_REASON_DISASSOC_PWRCAP_BAD:
287 return "Disassociate Power Cap Bad";
288 case WIFI_REASON_DISASSOC_SUPCHAN_BAD:
289 return "Disassociate Supported Channel Bad";
290 case WIFI_REASON_IE_INVALID:
292 case WIFI_REASON_MIC_FAILURE:
293 return "Mic Failure";
294 case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
295 return "4-Way Handshake Timeout";
296 case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT:
297 return "Group Key Update Timeout";
298 case WIFI_REASON_IE_IN_4WAY_DIFFERS:
299 return "IE In 4-Way Handshake Differs";
300 case WIFI_REASON_GROUP_CIPHER_INVALID:
301 return "Group Cipher Invalid";
302 case WIFI_REASON_PAIRWISE_CIPHER_INVALID:
303 return "Pairwise Cipher Invalid";
304 case WIFI_REASON_AKMP_INVALID:
305 return "AKMP Invalid";
306 case WIFI_REASON_UNSUPP_RSN_IE_VERSION:
307 return "Unsupported RSN IE version";
308 case WIFI_REASON_INVALID_RSN_IE_CAP:
309 return "Invalid RSN IE Cap";
310 case WIFI_REASON_802_1X_AUTH_FAILED:
311 return "802.1x Authentication Failed";
312 case WIFI_REASON_CIPHER_SUITE_REJECTED:
313 return "Cipher Suite Rejected";
314 case WIFI_REASON_BEACON_TIMEOUT:
315 return "Beacon Timeout";
316 case WIFI_REASON_NO_AP_FOUND:
317 return "AP Not Found";
318 case WIFI_REASON_AUTH_FAIL:
319 return "Authentication Failed";
320 case WIFI_REASON_ASSOC_FAIL:
321 return "Association Failed";
322 case WIFI_REASON_HANDSHAKE_TIMEOUT:
323 return "Handshake Failed";
324 case WIFI_REASON_CONNECTION_FAIL:
325 return "Connection Failed";
326 case WIFI_REASON_UNSPECIFIED:
328 return "Unspecified";
332#define ESPHOME_EVENT_ID_WIFI_READY ARDUINO_EVENT_WIFI_READY
333#define ESPHOME_EVENT_ID_WIFI_SCAN_DONE ARDUINO_EVENT_WIFI_SCAN_DONE
334#define ESPHOME_EVENT_ID_WIFI_STA_START ARDUINO_EVENT_WIFI_STA_START
335#define ESPHOME_EVENT_ID_WIFI_STA_STOP ARDUINO_EVENT_WIFI_STA_STOP
336#define ESPHOME_EVENT_ID_WIFI_STA_CONNECTED ARDUINO_EVENT_WIFI_STA_CONNECTED
337#define ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED ARDUINO_EVENT_WIFI_STA_DISCONNECTED
338#define ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE
339#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP ARDUINO_EVENT_WIFI_STA_GOT_IP
340#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6 ARDUINO_EVENT_WIFI_STA_GOT_IP6
341#define ESPHOME_EVENT_ID_WIFI_STA_LOST_IP ARDUINO_EVENT_WIFI_STA_LOST_IP
342#define ESPHOME_EVENT_ID_WIFI_AP_START ARDUINO_EVENT_WIFI_AP_START
343#define ESPHOME_EVENT_ID_WIFI_AP_STOP ARDUINO_EVENT_WIFI_AP_STOP
344#define ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED ARDUINO_EVENT_WIFI_AP_STACONNECTED
345#define ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED ARDUINO_EVENT_WIFI_AP_STADISCONNECTED
346#define ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED
347#define ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED
348#define ESPHOME_EVENT_ID_WIFI_AP_GOT_IP6 ARDUINO_EVENT_WIFI_AP_GOT_IP6
355 if (s_event_queue ==
nullptr) {
360 auto *to_send =
new LTWiFiEvent{};
361 to_send->event_id = event;
365 case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: {
366 auto &it = info.wifi_sta_connected;
367 to_send->data.sta_connected.ssid_len = it.ssid_len;
368 memcpy(to_send->data.sta_connected.ssid, it.ssid,
369 std::min(
static_cast<size_t>(it.ssid_len),
sizeof(to_send->data.sta_connected.ssid) - 1));
370 memcpy(to_send->data.sta_connected.bssid, it.bssid, 6);
371 to_send->data.sta_connected.channel = it.channel;
372 to_send->data.sta_connected.authmode = it.authmode;
375 case ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED: {
376 auto &it = info.wifi_sta_disconnected;
377 to_send->data.sta_disconnected.ssid_len = it.ssid_len;
378 memcpy(to_send->data.sta_disconnected.ssid, it.ssid,
379 std::min(
static_cast<size_t>(it.ssid_len),
sizeof(to_send->data.sta_disconnected.ssid) - 1));
380 memcpy(to_send->data.sta_disconnected.bssid, it.bssid, 6);
381 to_send->data.sta_disconnected.reason = it.reason;
384 case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: {
385 auto &it = info.wifi_sta_authmode_change;
386 to_send->data.sta_authmode_change.old_mode = it.old_mode;
387 to_send->data.sta_authmode_change.new_mode = it.new_mode;
390 case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: {
391 auto &it = info.wifi_scan_done;
392 to_send->data.scan_done.status = it.status;
393 to_send->data.scan_done.number = it.number;
394 to_send->data.scan_done.scan_id = it.scan_id;
397 case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: {
398 auto &it = info.wifi_ap_probereqrecved;
399 memcpy(to_send->data.ap_probe_req.mac, it.mac, 6);
400 to_send->data.ap_probe_req.rssi = it.rssi;
403 case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: {
404 auto &it = info.wifi_sta_connected;
405 memcpy(to_send->data.sta_connected.bssid, it.bssid, 6);
408 case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: {
409 auto &it = info.wifi_sta_disconnected;
410 memcpy(to_send->data.sta_disconnected.bssid, it.bssid, 6);
413 case ESPHOME_EVENT_ID_WIFI_READY:
414 case ESPHOME_EVENT_ID_WIFI_STA_START:
415 case ESPHOME_EVENT_ID_WIFI_STA_STOP:
416 case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP:
417 case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6:
418 case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP:
419 case ESPHOME_EVENT_ID_WIFI_AP_START:
420 case ESPHOME_EVENT_ID_WIFI_AP_STOP:
421 case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED:
431 if (xQueueSend(s_event_queue, &to_send, 0) != pdPASS) {
433 s_event_queue_overflow_count++;
442 switch (event->event_id) {
443 case ESPHOME_EVENT_ID_WIFI_READY: {
444 ESP_LOGV(TAG,
"Ready");
447 case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: {
448 auto &it =
event->data.scan_done;
449 ESP_LOGV(TAG,
"Scan done: status=%" PRIu32
" number=%u scan_id=%u", it.status, it.number, it.scan_id);
453 case ESPHOME_EVENT_ID_WIFI_STA_START: {
454 ESP_LOGV(TAG,
"STA start");
458 case ESPHOME_EVENT_ID_WIFI_STA_STOP: {
459 ESP_LOGV(TAG,
"STA stop");
463 case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: {
464 auto &it =
event->data.sta_connected;
465 char bssid_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
467 ESP_LOGV(TAG,
"Connected ssid='%.*s' bssid=" LOG_SECRET(
"%s")
" channel=%u, authmode=%s", it.ssid_len,
472#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
475 this->
pending_.connect_state =
true;
478#ifdef USE_WIFI_MANUAL_IP
481#ifdef USE_WIFI_IP_STATE_LISTENERS
488 case ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED: {
489 auto &it =
event->data.sta_disconnected;
503 it.reason != WIFI_REASON_NO_AP_FOUND) {
504 s_ignored_disconnect_count++;
505 if (s_ignored_disconnect_count >= IGNORED_DISCONNECT_THRESHOLD) {
506 ESP_LOGW(TAG,
"Too many disconnect events (%u) while connecting, treating as failure (reason=%s)",
513 ESP_LOGV(TAG,
"Ignoring disconnect event with empty ssid while connecting (reason=%s, count=%u)",
519 if (it.reason == WIFI_REASON_NO_AP_FOUND) {
520 ESP_LOGW(TAG,
"Disconnected ssid='%.*s' reason='Probe Request Unsuccessful'", it.ssid_len,
521 (
const char *) it.ssid);
524 char bssid_s[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
526 ESP_LOGW(TAG,
"Disconnected ssid='%.*s' bssid=" LOG_SECRET(
"%s")
" reason='%s'", it.ssid_len,
531 uint8_t reason = it.reason;
532 if (reason == WIFI_REASON_AUTH_EXPIRE || reason == WIFI_REASON_BEACON_TIMEOUT ||
533 reason == WIFI_REASON_NO_AP_FOUND || reason == WIFI_REASON_ASSOC_FAIL ||
534 reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
539#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
544 case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: {
545 auto &it =
event->data.sta_authmode_change;
549 if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) {
550 ESP_LOGW(TAG,
"Potential Authmode downgrade detected, disconnecting");
557 case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP: {
558 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE], gw_buf[network::IP_ADDRESS_BUFFER_SIZE];
559 ESP_LOGV(TAG,
"static_ip=%s gateway=%s",
network::IPAddress(WiFi.localIP()).str_to(ip_buf),
562#ifdef USE_WIFI_IP_STATE_LISTENERS
567 case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: {
568 ESP_LOGV(TAG,
"Got IPv6");
569#ifdef USE_WIFI_IP_STATE_LISTENERS
574 case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: {
575 ESP_LOGV(TAG,
"Lost IP");
579 case ESPHOME_EVENT_ID_WIFI_AP_START: {
580 ESP_LOGV(TAG,
"AP start");
583 case ESPHOME_EVENT_ID_WIFI_AP_STOP: {
584 ESP_LOGV(TAG,
"AP stop");
587 case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: {
588#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
589 auto &it =
event->data.sta_connected;
590 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
592 ESP_LOGV(TAG,
"AP client connected MAC=%s", mac_buf);
596 case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: {
597#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
598 auto &it =
event->data.sta_disconnected;
599 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
601 ESP_LOGV(TAG,
"AP client disconnected MAC=%s", mac_buf);
605 case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: {
606 ESP_LOGV(TAG,
"AP client assigned IP");
609 case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: {
610#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
611 auto &it =
event->data.ap_probe_req;
612 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
614 ESP_LOGVV(TAG,
"AP receive Probe Request MAC=%s RSSI=%d", mac_buf, it.rssi);
625 s_event_queue = xQueueCreate(EVENT_QUEUE_SIZE,
sizeof(LTWiFiEvent *));
626 if (s_event_queue ==
nullptr) {
627 ESP_LOGE(TAG,
"Failed to create event queue");
632 [
this](arduino_event_id_t event, arduino_event_info_t info) { this->
wifi_event_callback_(event, info); });
663 int16_t err = WiFi.scanNetworks(
true,
true, passive, 200);
664 if (err != WIFI_SCAN_RUNNING) {
665 ESP_LOGV(TAG,
"WiFi.scanNetworks failed: %d", err);
675 int16_t num = WiFi.scanComplete();
683 auto *scan = WiFi.scan;
687 for (
int i = 0; i < num; i++) {
688 const char *ssid_cstr = scan->ap[i].ssid;
697 for (
int i = 0; i < num; i++) {
698 const char *ssid_cstr = scan->ap[i].ssid;
700 auto &ap = scan->ap[i];
701 this->
scan_result_.emplace_back(
bssid_t{ap.bssid.addr[0], ap.bssid.addr[1], ap.bssid.addr[2], ap.bssid.addr[3],
702 ap.bssid.addr[4], ap.bssid.addr[5]},
703 ssid_cstr, strlen(ssid_cstr), ap.channel, ap.rssi, ap.auth != WIFI_AUTH_OPEN,
704 ssid_cstr[0] ==
'\0');
706 auto &ap = scan->ap[i];
710 ESP_LOGV(TAG,
"Scan complete: %d found, %zu stored%s", num, this->
scan_result_.size(),
711 needs_full ?
"" :
" (filtered)");
713#ifdef USE_WIFI_SCAN_RESULTS_LISTENERS
724 if (manual_ip.has_value()) {
725 return WiFi.softAPConfig(manual_ip->static_ip, manual_ip->gateway, manual_ip->subnet);
727 return WiFi.softAPConfig(IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0));
736#ifdef USE_WIFI_MANUAL_IP
738 ESP_LOGV(TAG,
"wifi_ap_ip_config_ failed");
743 ESP_LOGV(TAG,
"wifi_ap_ip_config_ failed");
750 return WiFi.softAP(ap.ssid_.c_str(), ap.password_.empty() ? NULL : ap.password_.c_str(),
751 ap.has_channel() ? ap.get_channel() : 1, ap.get_hidden());
761 return WiFi.disconnect();
766 uint8_t *raw_bssid = WiFi.BSSID();
767 if (raw_bssid !=
nullptr) {
768 for (
size_t i = 0; i < bssid.size(); i++)
769 bssid[i] = raw_bssid[i];
773std::string WiFiComponent::wifi_ssid() {
return WiFi.SSID().c_str(); }
776 LinkStatusTypeDef link_status{};
777 bk_wlan_get_link_status(&link_status);
778 size_t len = strnlen(
reinterpret_cast<const char *
>(link_status.ssid), SSID_BUFFER_SIZE - 1);
779 memcpy(buffer.data(), link_status.ssid,
len);
780#elif defined(USE_RTL87XX)
781 rtw_wifi_setting_t setting{};
782 wifi_get_setting(
"wlan0", &setting);
783 size_t len = strnlen(
reinterpret_cast<const char *
>(setting.ssid), SSID_BUFFER_SIZE - 1);
784 memcpy(buffer.data(), setting.ssid,
len);
787 String ssid = WiFi.SSID();
788 size_t len = std::min(
static_cast<size_t>(ssid.length()), SSID_BUFFER_SIZE - 1);
789 memcpy(buffer.data(), ssid.c_str(),
len);
792 return buffer.data();
801 if (s_event_queue ==
nullptr) {
806 if (s_event_queue_overflow_count > 0) {
807 ESP_LOGW(TAG,
"Event queue overflow, %" PRIu32
" events dropped", s_event_queue_overflow_count);
808 s_event_queue_overflow_count = 0;
813 if (xQueueReceive(s_event_queue, &event, 0) != pdTRUE) {
BedjetMode mode
BedJet operating mode.
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
constexpr const char * c_str() const
const optional< ManualIP > & get_manual_ip() const
void notify_scan_results_listeners_()
Notify scan results listeners with current scan results.
bool error_from_callback_
bool wifi_apply_power_save_()
WiFiPowerSaveMode power_save_
const WiFiAP * get_selected_sta_() const
WiFiSTAConnectStatus wifi_sta_connect_status_() const
wifi_scan_vector_t< WiFiScanResult > scan_result_
struct esphome::wifi::WiFiComponent::@190 pending_
void notify_ip_state_listeners_()
Notify IP state listeners with current addresses.
bool wifi_sta_ip_config_(const optional< ManualIP > &manual_ip)
void wifi_process_event_(IDFWiFiEvent *data)
void notify_disconnect_state_listeners_()
Notify connect state listeners of disconnection.
bool wifi_start_ap_(const WiFiAP &ap)
void log_discarded_scan_result_(const char *ssid, const uint8_t *bssid, int8_t rssi, uint8_t channel)
Log a discarded scan result at VERBOSE level (skipped during roaming scans to avoid log overflow)
int32_t get_wifi_channel()
ESPDEPRECATED("Use wifi_ssid_to() instead. Removed in 2026.9.0", "2026.3.0") std const char * wifi_ssid_to(std::span< char, SSID_BUFFER_SIZE > buffer)
Write SSID to buffer without heap allocation.
network::IPAddress wifi_subnet_mask_()
network::IPAddress wifi_soft_ap_ip()
void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info)
network::IPAddress wifi_gateway_ip_()
network::IPAddress wifi_dns_ip_(int num)
bool wifi_apply_hostname_()
bool wifi_sta_pre_setup_()
bool matches_configured_network_(const char *ssid, const uint8_t *bssid) const
Check if network matches any configured network (for scan result filtering) Matches by SSID when conf...
bool wifi_ap_ip_config_(const optional< ManualIP > &manual_ip)
bool needs_full_scan_results_() const
Check if full scan results are needed (captive portal active, improv, listeners)
StaticVector< WiFiPowerSaveListener *, ESPHOME_WIFI_POWER_SAVE_LISTENERS > power_save_listeners_
bool wifi_apply_output_power_(float output_power)
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)
void wifi_scan_done_callback_()
std::array< IPAddress, 5 > IPAddresses
std::array< uint8_t, 6 > bssid_t
const LogString * get_auth_mode_str(uint8_t mode)
arduino_event_info_t esphome_wifi_event_info_t
const LogString * get_disconnect_reason_str(uint8_t reason)
@ ERROR_NETWORK_NOT_FOUND
arduino_event_id_t esphome_wifi_event_id_t
const LogString * get_op_mode_str(uint8_t mode)
void HOT delay(uint32_t ms)
Application App
Global storage of Application pointer - only one Application can exist.
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)