mirror of
https://github.com/esphome/esphome.git
synced 2026-01-26 14:32:09 -07:00
Compare commits
6 Commits
dependabot
...
wifi_scan_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
217ce9d082 | ||
|
|
b20a16c0de | ||
|
|
1968d6c101 | ||
|
|
25b6913e8d | ||
|
|
abcf363a3c | ||
|
|
127c910207 |
@@ -29,18 +29,17 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
|
||||
if (scan.get_is_hidden())
|
||||
continue;
|
||||
|
||||
// Assumes no " in ssid, possible unicode isses?
|
||||
// Assumes no " in ssid, possible unicode issues?
|
||||
#ifdef USE_ESP8266
|
||||
stream->print(ESPHOME_F(",{\"ssid\":\""));
|
||||
stream->print(scan.get_ssid().c_str());
|
||||
stream->print(scan.get_ssid());
|
||||
stream->print(ESPHOME_F("\",\"rssi\":"));
|
||||
stream->print(scan.get_rssi());
|
||||
stream->print(ESPHOME_F(",\"lock\":"));
|
||||
stream->print(scan.get_with_auth());
|
||||
stream->print(ESPHOME_F("}"));
|
||||
#else
|
||||
stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", scan.get_ssid().c_str(), scan.get_rssi(),
|
||||
scan.get_with_auth());
|
||||
stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", scan.get_ssid(), scan.get_rssi(), scan.get_with_auth());
|
||||
#endif
|
||||
}
|
||||
stream->print(ESPHOME_F("]}"));
|
||||
|
||||
@@ -259,14 +259,14 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command
|
||||
for (auto &scan : results) {
|
||||
if (scan.get_is_hidden())
|
||||
continue;
|
||||
const std::string &ssid = scan.get_ssid();
|
||||
const char *ssid = scan.get_ssid();
|
||||
if (std::find(networks.begin(), networks.end(), ssid) != networks.end())
|
||||
continue;
|
||||
// Send each ssid separately to avoid overflowing the buffer
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
||||
improv::GET_WIFI_NETWORKS, {ssid, str_sprintf("%d", scan.get_rssi()), YESNO(scan.get_with_auth())}, false);
|
||||
this->send_response_(data);
|
||||
networks.push_back(ssid);
|
||||
networks.emplace_back(ssid);
|
||||
}
|
||||
// Send empty response to signify the end of the list.
|
||||
std::vector<uint8_t> data =
|
||||
|
||||
@@ -1045,7 +1045,7 @@ __attribute__((noinline)) static void log_scan_result(const WiFiScanResult &res)
|
||||
auto bssid = res.get_bssid();
|
||||
format_mac_addr_upper(bssid.data(), bssid_s);
|
||||
|
||||
ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(),
|
||||
ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid(),
|
||||
res.get_is_hidden() ? LOG_STR_LITERAL("(HIDDEN) ") : LOG_STR_LITERAL(""), bssid_s,
|
||||
LOG_STR_ARG(get_signal_bars(res.get_rssi())));
|
||||
ESP_LOGD(TAG, " Channel: %2u, RSSI: %3d dB, Priority: %4d", res.get_channel(), res.get_rssi(), res.get_priority());
|
||||
@@ -1058,7 +1058,7 @@ __attribute__((noinline)) static void log_scan_result_non_matching(const WiFiSca
|
||||
auto bssid = res.get_bssid();
|
||||
format_mac_addr_upper(bssid.data(), bssid_s);
|
||||
|
||||
ESP_LOGV(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s,
|
||||
ESP_LOGV(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid(), bssid_s,
|
||||
LOG_STR_ARG(get_signal_bars(res.get_rssi())));
|
||||
}
|
||||
#endif
|
||||
@@ -1532,11 +1532,11 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() {
|
||||
}
|
||||
|
||||
// Get SSID for logging (use pointer to avoid copy)
|
||||
const std::string *ssid = nullptr;
|
||||
const char *ssid = nullptr;
|
||||
if (this->retry_phase_ == WiFiRetryPhase::SCAN_CONNECTING && !this->scan_result_.empty()) {
|
||||
ssid = &this->scan_result_[0].get_ssid();
|
||||
ssid = this->scan_result_[0].get_ssid();
|
||||
} else if (const WiFiAP *config = this->get_selected_sta_()) {
|
||||
ssid = &config->get_ssid();
|
||||
ssid = config->get_ssid().c_str();
|
||||
}
|
||||
|
||||
// Only decrease priority on the last attempt for this phase
|
||||
@@ -1556,8 +1556,8 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() {
|
||||
}
|
||||
char bssid_s[18];
|
||||
format_mac_addr_upper(failed_bssid.value().data(), bssid_s);
|
||||
ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %d → %d",
|
||||
ssid != nullptr ? ssid->c_str() : "", bssid_s, old_priority, new_priority);
|
||||
ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %d → %d", ssid != nullptr ? ssid : "",
|
||||
bssid_s, old_priority, new_priority);
|
||||
|
||||
// After adjusting priority, check if all priorities are now at minimum
|
||||
// If so, clear the vector to save memory and reset for fresh start
|
||||
@@ -1818,19 +1818,22 @@ const optional<ManualIP> &WiFiAP::get_manual_ip() const { return this->manual_ip
|
||||
#endif
|
||||
bool WiFiAP::get_hidden() const { return this->hidden_; }
|
||||
|
||||
WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth,
|
||||
bool is_hidden)
|
||||
WiFiScanResult::WiFiScanResult(const bssid_t &bssid, const char *ssid, uint8_t ssid_len, uint8_t channel, int8_t rssi,
|
||||
bool with_auth, bool is_hidden)
|
||||
: bssid_(bssid),
|
||||
channel_(channel),
|
||||
rssi_(rssi),
|
||||
ssid_(std::move(ssid)),
|
||||
with_auth_(with_auth),
|
||||
is_hidden_(is_hidden) {}
|
||||
flags_((with_auth ? FLAG_WITH_AUTH : 0) | (is_hidden ? FLAG_IS_HIDDEN : 0)) {
|
||||
// Copy SSID with length limit and null-terminate
|
||||
uint8_t len = ssid_len > ESPHOME_MAX_SSID_LEN ? ESPHOME_MAX_SSID_LEN : ssid_len;
|
||||
memcpy(this->ssid_, ssid, len);
|
||||
this->ssid_[len] = '\0';
|
||||
}
|
||||
bool WiFiScanResult::matches(const WiFiAP &config) const {
|
||||
if (config.get_hidden()) {
|
||||
// User configured a hidden network, only match actually hidden networks
|
||||
// don't match SSID
|
||||
if (!this->is_hidden_)
|
||||
if (!this->get_is_hidden())
|
||||
return false;
|
||||
} else if (!config.get_ssid().empty()) {
|
||||
// check if SSID matches
|
||||
@@ -1845,15 +1848,15 @@ bool WiFiScanResult::matches(const WiFiAP &config) const {
|
||||
|
||||
#ifdef USE_WIFI_WPA2_EAP
|
||||
// BSSID requires auth but no PSK or EAP credentials given
|
||||
if (this->with_auth_ && (config.get_password().empty() && !config.get_eap().has_value()))
|
||||
if (this->get_with_auth() && (config.get_password().empty() && !config.get_eap().has_value()))
|
||||
return false;
|
||||
|
||||
// BSSID does not require auth, but PSK or EAP credentials given
|
||||
if (!this->with_auth_ && (!config.get_password().empty() || config.get_eap().has_value()))
|
||||
if (!this->get_with_auth() && (!config.get_password().empty() || config.get_eap().has_value()))
|
||||
return false;
|
||||
#else
|
||||
// If PSK given, only match for networks with auth (and vice versa)
|
||||
if (config.get_password().empty() == this->with_auth_)
|
||||
if (config.get_password().empty() == this->get_with_auth())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -1863,14 +1866,13 @@ bool WiFiScanResult::matches(const WiFiAP &config) const {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool WiFiScanResult::get_matches() const { return this->matches_; }
|
||||
void WiFiScanResult::set_matches(bool matches) { this->matches_ = matches; }
|
||||
const bssid_t &WiFiScanResult::get_bssid() const { return this->bssid_; }
|
||||
const std::string &WiFiScanResult::get_ssid() const { return this->ssid_; }
|
||||
uint8_t WiFiScanResult::get_channel() const { return this->channel_; }
|
||||
int8_t WiFiScanResult::get_rssi() const { return this->rssi_; }
|
||||
bool WiFiScanResult::get_with_auth() const { return this->with_auth_; }
|
||||
bool WiFiScanResult::get_is_hidden() const { return this->is_hidden_; }
|
||||
void WiFiScanResult::set_matches(bool matches) {
|
||||
if (matches) {
|
||||
this->flags_ |= FLAG_MATCHES;
|
||||
} else {
|
||||
this->flags_ &= ~FLAG_MATCHES;
|
||||
}
|
||||
}
|
||||
|
||||
bool WiFiScanResult::operator==(const WiFiScanResult &rhs) const { return this->bssid_ == rhs.bssid_; }
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -61,12 +62,21 @@ namespace esphome::wifi {
|
||||
/// Sentinel value for RSSI when WiFi is not connected
|
||||
static constexpr int8_t WIFI_RSSI_DISCONNECTED = -127;
|
||||
|
||||
/// Buffer size for SSID (IEEE 802.11 max 32 bytes + null terminator)
|
||||
static constexpr size_t SSID_BUFFER_SIZE = 33;
|
||||
/// Maximum SSID length per IEEE 802.11
|
||||
static constexpr uint8_t ESPHOME_MAX_SSID_LEN = 32;
|
||||
|
||||
/// Buffer size for SSID (max length + null terminator)
|
||||
static constexpr size_t SSID_BUFFER_SIZE = ESPHOME_MAX_SSID_LEN + 1;
|
||||
|
||||
/// Maximum password length per WPA2
|
||||
static constexpr uint8_t MAX_PASSWORD_LEN = 64;
|
||||
|
||||
/// Buffer size for password (max length + null terminator)
|
||||
static constexpr size_t PASSWORD_BUFFER_SIZE = MAX_PASSWORD_LEN + 1;
|
||||
|
||||
struct SavedWifiSettings {
|
||||
char ssid[33];
|
||||
char password[65];
|
||||
char ssid[SSID_BUFFER_SIZE];
|
||||
char password[PASSWORD_BUFFER_SIZE];
|
||||
} PACKED; // NOLINT
|
||||
|
||||
struct SavedWifiFastConnectSettings {
|
||||
@@ -202,32 +212,35 @@ class WiFiAP {
|
||||
|
||||
class WiFiScanResult {
|
||||
public:
|
||||
WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden);
|
||||
WiFiScanResult(const bssid_t &bssid, const char *ssid, uint8_t ssid_len, uint8_t channel, int8_t rssi, bool with_auth,
|
||||
bool is_hidden);
|
||||
|
||||
bool matches(const WiFiAP &config) const;
|
||||
|
||||
bool get_matches() const;
|
||||
bool get_matches() const { return this->flags_ & FLAG_MATCHES; }
|
||||
void set_matches(bool matches);
|
||||
const bssid_t &get_bssid() const;
|
||||
const std::string &get_ssid() const;
|
||||
uint8_t get_channel() const;
|
||||
int8_t get_rssi() const;
|
||||
bool get_with_auth() const;
|
||||
bool get_is_hidden() const;
|
||||
int8_t get_priority() const { return priority_; }
|
||||
void set_priority(int8_t priority) { priority_ = priority; }
|
||||
const bssid_t &get_bssid() const { return this->bssid_; }
|
||||
const char *get_ssid() const { return this->ssid_; }
|
||||
uint8_t get_channel() const { return this->channel_; }
|
||||
int8_t get_rssi() const { return this->rssi_; }
|
||||
bool get_with_auth() const { return this->flags_ & FLAG_WITH_AUTH; }
|
||||
bool get_is_hidden() const { return this->flags_ & FLAG_IS_HIDDEN; }
|
||||
int8_t get_priority() const { return this->priority_; }
|
||||
void set_priority(int8_t priority) { this->priority_ = priority; }
|
||||
|
||||
bool operator==(const WiFiScanResult &rhs) const;
|
||||
|
||||
protected:
|
||||
bssid_t bssid_;
|
||||
uint8_t channel_;
|
||||
int8_t rssi_;
|
||||
std::string ssid_;
|
||||
int8_t priority_{0};
|
||||
bool matches_{false};
|
||||
bool with_auth_;
|
||||
bool is_hidden_;
|
||||
static constexpr uint8_t FLAG_MATCHES = 1 << 0;
|
||||
static constexpr uint8_t FLAG_WITH_AUTH = 1 << 1;
|
||||
static constexpr uint8_t FLAG_IS_HIDDEN = 1 << 2;
|
||||
|
||||
bssid_t bssid_; // 6 bytes
|
||||
uint8_t channel_; // 1 byte
|
||||
int8_t rssi_; // 1 byte
|
||||
char ssid_[SSID_BUFFER_SIZE]; // 33 bytes - inline storage, no heap
|
||||
int8_t priority_{0}; // 1 byte
|
||||
uint8_t flags_{0}; // 1 byte (+ 1 byte padding)
|
||||
};
|
||||
|
||||
struct WiFiSTAPriority {
|
||||
@@ -378,7 +391,7 @@ class WiFiComponent : public Component {
|
||||
const char *get_use_address() const;
|
||||
void set_use_address(const char *use_address);
|
||||
|
||||
const wifi_scan_vector_t<WiFiScanResult> &get_scan_result() const { return scan_result_; }
|
||||
const wifi_scan_vector_t<WiFiScanResult> &get_scan_result() const { return this->scan_result_; }
|
||||
|
||||
network::IPAddress wifi_soft_ap_ip();
|
||||
|
||||
|
||||
@@ -751,7 +751,7 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count the number of results first
|
||||
// Count results first
|
||||
auto *head = reinterpret_cast<bss_info *>(arg);
|
||||
size_t count = 0;
|
||||
for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) {
|
||||
@@ -760,9 +760,10 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) {
|
||||
|
||||
this->scan_result_.init(count);
|
||||
for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) {
|
||||
uint8_t len = std::min(it->ssid_len, static_cast<uint8>(ESPHOME_MAX_SSID_LEN));
|
||||
this->scan_result_.emplace_back(
|
||||
bssid_t{it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]},
|
||||
std::string(reinterpret_cast<char *>(it->ssid), it->ssid_len), it->channel, it->rssi, it->authmode != AUTH_OPEN,
|
||||
reinterpret_cast<const char *>(it->ssid), len, it->channel, it->rssi, it->authmode != AUTH_OPEN,
|
||||
it->is_hidden != 0);
|
||||
}
|
||||
this->scan_done_ = true;
|
||||
|
||||
@@ -842,9 +842,11 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
auto &record = records[i];
|
||||
bssid_t bssid;
|
||||
std::copy(record.bssid, record.bssid + 6, bssid.begin());
|
||||
std::string ssid(reinterpret_cast<const char *>(record.ssid));
|
||||
scan_result_.emplace_back(bssid, ssid, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN,
|
||||
ssid.empty());
|
||||
// ESP-IDF ssid is null-terminated uint8_t[33]
|
||||
const char *ssid = reinterpret_cast<const char *>(record.ssid);
|
||||
uint8_t ssid_len = static_cast<uint8_t>(strlen(ssid));
|
||||
scan_result_.emplace_back(bssid, ssid, ssid_len, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN,
|
||||
ssid_len == 0);
|
||||
}
|
||||
#ifdef USE_WIFI_LISTENERS
|
||||
for (auto *listener : this->scan_results_listeners_) {
|
||||
@@ -1099,8 +1101,8 @@ const char *WiFiComponent::wifi_ssid_to(std::span<char, SSID_BUFFER_SIZE> buffer
|
||||
buffer[0] = '\0';
|
||||
return buffer.data();
|
||||
}
|
||||
// info.ssid is uint8[33], but only 32 bytes are SSID data
|
||||
size_t len = strnlen(reinterpret_cast<const char *>(info.ssid), 32);
|
||||
// info.ssid is uint8[33], but only ESPHOME_MAX_SSID_LEN bytes are SSID data
|
||||
size_t len = strnlen(reinterpret_cast<const char *>(info.ssid), ESPHOME_MAX_SSID_LEN);
|
||||
memcpy(buffer.data(), info.ssid, len);
|
||||
buffer[len] = '\0';
|
||||
return buffer.data();
|
||||
|
||||
@@ -485,10 +485,10 @@ void WiFiComponent::wifi_scan_done_callback_() {
|
||||
int32_t rssi = WiFi.RSSI(i);
|
||||
uint8_t *bssid = WiFi.BSSID(i);
|
||||
int32_t channel = WiFi.channel(i);
|
||||
uint8_t ssid_len = static_cast<uint8_t>(ssid.length());
|
||||
|
||||
this->scan_result_.emplace_back(bssid_t{bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]},
|
||||
std::string(ssid.c_str()), channel, rssi, authmode != WIFI_AUTH_OPEN,
|
||||
ssid.length() == 0);
|
||||
this->scan_result_.emplace_back(bssid_t{bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]}, ssid.c_str(),
|
||||
ssid_len, channel, rssi, authmode != WIFI_AUTH_OPEN, ssid_len == 0);
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
#ifdef USE_WIFI_LISTENERS
|
||||
|
||||
@@ -139,11 +139,18 @@ int WiFiComponent::s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *r
|
||||
void WiFiComponent::wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result) {
|
||||
bssid_t bssid;
|
||||
std::copy(result->bssid, result->bssid + 6, bssid.begin());
|
||||
std::string ssid(reinterpret_cast<const char *>(result->ssid));
|
||||
WiFiScanResult res(bssid, ssid, result->channel, result->rssi, result->auth_mode != CYW43_AUTH_OPEN, ssid.empty());
|
||||
if (std::find(this->scan_result_.begin(), this->scan_result_.end(), res) == this->scan_result_.end()) {
|
||||
this->scan_result_.push_back(res);
|
||||
const char *ssid = reinterpret_cast<const char *>(result->ssid);
|
||||
uint8_t ssid_len = result->ssid_len;
|
||||
|
||||
// Check for duplicates by BSSID (same AP) - RP2040 can report same AP multiple times
|
||||
for (const auto &existing : this->scan_result_) {
|
||||
if (existing.get_bssid() == bssid) {
|
||||
return; // Already have this BSSID
|
||||
}
|
||||
}
|
||||
|
||||
this->scan_result_.emplace_back(bssid, ssid, ssid_len, result->channel, result->rssi,
|
||||
result->auth_mode != CYW43_AUTH_OPEN, ssid_len == 0);
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
|
||||
@@ -84,11 +84,12 @@ void ScanResultsWiFiInfo::on_wifi_scan_results(const wifi::wifi_scan_vector_t<wi
|
||||
for (const auto &scan : results) {
|
||||
if (scan.get_is_hidden())
|
||||
continue;
|
||||
const std::string &ssid = scan.get_ssid();
|
||||
const char *ssid = scan.get_ssid();
|
||||
size_t ssid_len = strlen(ssid);
|
||||
// Max space: ssid + ": " (2) + "-128" (4) + "dB\n" (3) = ssid + 9
|
||||
if (ptr + ssid.size() + 9 > end)
|
||||
if (ptr + ssid_len + 9 > end)
|
||||
break;
|
||||
ptr = format_scan_entry(ptr, ssid.c_str(), ssid.size(), scan.get_rssi());
|
||||
ptr = format_scan_entry(ptr, ssid, ssid_len, scan.get_rssi());
|
||||
}
|
||||
|
||||
*ptr = '\0';
|
||||
|
||||
Reference in New Issue
Block a user