mirror of
https://github.com/esphome/esphome.git
synced 2026-01-22 02:49:10 -07:00
Compare commits
2 Commits
dev
...
wifi_scan_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,18 @@ 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,
|
||||
WiFiScanResult::WiFiScanResult(const bssid_t &bssid, const char *ssid, 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) {}
|
||||
ssid_(ssid),
|
||||
flags_((with_auth ? FLAG_WITH_AUTH : 0) | (is_hidden ? FLAG_IS_HIDDEN : 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 +1844,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 +1862,17 @@ 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; }
|
||||
void WiFiScanResult::set_matches(bool matches) {
|
||||
if (matches) {
|
||||
this->flags_ |= FLAG_MATCHES;
|
||||
} else {
|
||||
this->flags_ &= ~FLAG_MATCHES;
|
||||
}
|
||||
}
|
||||
const bssid_t &WiFiScanResult::get_bssid() const { return this->bssid_; }
|
||||
const std::string &WiFiScanResult::get_ssid() const { return this->ssid_; }
|
||||
const char *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_; }
|
||||
|
||||
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,25 @@ 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;
|
||||
|
||||
/// Maximum unique SSIDs tracked for deduplication during scan
|
||||
/// Beyond this limit, duplicate SSID storage may occur
|
||||
static constexpr size_t MAX_UNIQUE_SSIDS = 32;
|
||||
|
||||
struct SavedWifiSettings {
|
||||
char ssid[33];
|
||||
char password[65];
|
||||
char ssid[SSID_BUFFER_SIZE];
|
||||
char password[PASSWORD_BUFFER_SIZE];
|
||||
} PACKED; // NOLINT
|
||||
|
||||
struct SavedWifiFastConnectSettings {
|
||||
@@ -202,32 +216,159 @@ 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 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;
|
||||
const char *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; }
|
||||
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 (now 4-byte aligned for pointer)
|
||||
const char *ssid_; // 4 bytes - points into WiFiScanResults::ssid_pool_
|
||||
int8_t priority_{0}; // 1 byte
|
||||
uint8_t flags_{0}; // 1 byte (+ 2 bytes padding for struct alignment)
|
||||
};
|
||||
|
||||
/// SSID entry with length for efficient comparison
|
||||
struct SSIDEntry {
|
||||
char ssid[SSID_BUFFER_SIZE]; // SSID data, always null-terminated
|
||||
uint8_t len{0}; // Length of SSID (0-ESPHOME_MAX_SSID_LEN)
|
||||
|
||||
/// Get null-terminated SSID
|
||||
const char *c_str() const { return this->ssid; }
|
||||
|
||||
/// Check if this entry matches the given SSID
|
||||
bool matches(const char *other, uint8_t other_len) const {
|
||||
return this->len == other_len && memcmp(this->ssid, other, other_len) == 0;
|
||||
}
|
||||
|
||||
/// Store an SSID with its length (always null-terminates)
|
||||
void set(const char *data, uint8_t length) {
|
||||
this->len = length > ESPHOME_MAX_SSID_LEN ? ESPHOME_MAX_SSID_LEN : length;
|
||||
memcpy(this->ssid, data, this->len);
|
||||
this->ssid[this->len] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper to count unique SSIDs using length + memcmp comparison.
|
||||
/// Designed for single-pass counting during scan result iteration.
|
||||
/// Tracks up to MAX_UNIQUE_SSIDS - sufficient for typical environments.
|
||||
/// After capacity is reached, continues checking against stored SSIDs
|
||||
/// and counts additional unique ones separately.
|
||||
class UniqueSSIDCounter {
|
||||
public:
|
||||
/// Add an SSID and return true if not previously seen (vs stored SSIDs)
|
||||
/// @param ssid Pointer to SSID data (not required to be null-terminated)
|
||||
/// @param len Length of SSID (0-32)
|
||||
__attribute__((noinline)) bool add(const char *ssid, uint8_t len) {
|
||||
// Always check against stored SSIDs
|
||||
for (size_t i = 0; i < this->count_; i++) {
|
||||
if (this->ssids_[i].matches(ssid, len))
|
||||
return false; // Already seen
|
||||
}
|
||||
if (this->count_ < MAX_UNIQUE_SSIDS) {
|
||||
this->ssids_[this->count_].set(ssid, len);
|
||||
this->count_++;
|
||||
} else {
|
||||
// Can't store, but wasn't a duplicate of stored SSIDs
|
||||
// Count as potential unique (worst case for pool sizing)
|
||||
this->overflow_count_++;
|
||||
}
|
||||
return true; // Not seen before (in stored set)
|
||||
}
|
||||
/// Total pool size needed: stored + overflow (worst case)
|
||||
size_t pool_size() const { return this->count_ + this->overflow_count_; }
|
||||
|
||||
protected:
|
||||
std::array<SSIDEntry, MAX_UNIQUE_SSIDS> ssids_{};
|
||||
uint8_t count_{0};
|
||||
uint8_t overflow_count_{0};
|
||||
};
|
||||
|
||||
/// Container for WiFi scan results with SSID deduplication.
|
||||
/// SSIDs are interned into a pool - duplicates point to the same storage.
|
||||
class WiFiScanResults {
|
||||
public:
|
||||
#ifndef USE_RP2040
|
||||
/// Initialize storage for the expected number of results and SSIDs.
|
||||
/// @param result_count Number of scan results to store
|
||||
/// @param ssid_pool_size Size of SSID pool (use result_count if UniqueSSIDCounter overflowed)
|
||||
void init(size_t result_count, size_t ssid_pool_size) {
|
||||
this->results_.init(result_count);
|
||||
this->ssid_pool_.init(ssid_pool_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Add a scan result, interning the SSID
|
||||
/// @param ssid Pointer to SSID data (not required to be null-terminated)
|
||||
/// @param ssid_len Length of SSID (0-32)
|
||||
void emplace_back(const bssid_t &bssid, const char *ssid, uint8_t ssid_len, uint8_t channel, int8_t rssi,
|
||||
bool with_auth, bool is_hidden) {
|
||||
const char *interned_ssid = this->intern_ssid_(ssid, ssid_len);
|
||||
this->results_.emplace_back(bssid, interned_ssid, channel, rssi, with_auth, is_hidden);
|
||||
}
|
||||
|
||||
/// Clear both results and SSID pool
|
||||
void clear() {
|
||||
this->results_.clear();
|
||||
this->ssid_pool_.clear();
|
||||
}
|
||||
|
||||
/// Release memory (for shrink_to_fit semantics)
|
||||
void shrink_to_fit() {
|
||||
this->results_.shrink_to_fit();
|
||||
this->ssid_pool_.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Vector-like accessors
|
||||
bool empty() const { return this->results_.empty(); }
|
||||
size_t size() const { return this->results_.size(); }
|
||||
WiFiScanResult &operator[](size_t idx) { return this->results_[idx]; }
|
||||
const WiFiScanResult &operator[](size_t idx) const { return this->results_[idx]; }
|
||||
auto begin() { return this->results_.begin(); }
|
||||
auto end() { return this->results_.end(); }
|
||||
auto begin() const { return this->results_.begin(); }
|
||||
auto end() const { return this->results_.end(); }
|
||||
|
||||
/// Get underlying vector (for listener interface compatibility)
|
||||
const wifi_scan_vector_t<WiFiScanResult> &results() const { return this->results_; }
|
||||
|
||||
protected:
|
||||
/// Intern an SSID - returns pointer to existing or newly added pool entry
|
||||
/// @param ssid Pointer to SSID data (not required to be null-terminated)
|
||||
/// @param len Length of SSID (0-32)
|
||||
__attribute__((noinline)) const char *intern_ssid_(const char *ssid, uint8_t len) {
|
||||
// Check if already in pool
|
||||
for (auto &entry : this->ssid_pool_) {
|
||||
if (entry.matches(ssid, len)) {
|
||||
return entry.c_str();
|
||||
}
|
||||
}
|
||||
// Add new entry (pool is sized via UniqueSSIDCounter::pool_size())
|
||||
this->ssid_pool_.emplace_back();
|
||||
this->ssid_pool_.back().set(ssid, len);
|
||||
return this->ssid_pool_.back().c_str();
|
||||
}
|
||||
|
||||
wifi_scan_vector_t<WiFiScanResult> results_;
|
||||
wifi_scan_vector_t<SSIDEntry> ssid_pool_;
|
||||
};
|
||||
|
||||
struct WiFiSTAPriority {
|
||||
@@ -378,7 +519,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_.results(); }
|
||||
|
||||
network::IPAddress wifi_soft_ap_ip();
|
||||
|
||||
@@ -598,7 +739,7 @@ class WiFiComponent : public Component {
|
||||
|
||||
FixedVector<WiFiAP> sta_;
|
||||
std::vector<WiFiSTAPriority> sta_priorities_;
|
||||
wifi_scan_vector_t<WiFiScanResult> scan_result_;
|
||||
WiFiScanResults scan_result_;
|
||||
#ifdef USE_WIFI_AP
|
||||
WiFiAP ap_;
|
||||
#endif
|
||||
|
||||
@@ -751,24 +751,28 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count the number of results first
|
||||
// Count results and unique SSIDs in single pass
|
||||
auto *head = reinterpret_cast<bss_info *>(arg);
|
||||
size_t count = 0;
|
||||
UniqueSSIDCounter ssid_counter;
|
||||
for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) {
|
||||
count++;
|
||||
uint8_t len = std::min(it->ssid_len, static_cast<uint8>(ESPHOME_MAX_SSID_LEN));
|
||||
ssid_counter.add(reinterpret_cast<const char *>(it->ssid), len);
|
||||
}
|
||||
|
||||
this->scan_result_.init(count);
|
||||
this->scan_result_.init(count, ssid_counter.pool_size());
|
||||
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;
|
||||
#ifdef USE_WIFI_LISTENERS
|
||||
for (auto *listener : global_wifi_component->scan_results_listeners_) {
|
||||
listener->on_wifi_scan_results(global_wifi_component->scan_result_);
|
||||
listener->on_wifi_scan_results(global_wifi_component->scan_result_.results());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -837,18 +837,27 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
return;
|
||||
}
|
||||
|
||||
scan_result_.init(number);
|
||||
// Count unique SSIDs
|
||||
UniqueSSIDCounter ssid_counter;
|
||||
for (int i = 0; i < number; i++) {
|
||||
const char *ssid = reinterpret_cast<const char *>(records[i].ssid);
|
||||
ssid_counter.add(ssid, static_cast<uint8_t>(strlen(ssid)));
|
||||
}
|
||||
|
||||
scan_result_.init(number, ssid_counter.pool_size());
|
||||
for (int i = 0; i < number; i++) {
|
||||
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_) {
|
||||
listener->on_wifi_scan_results(this->scan_result_);
|
||||
listener->on_wifi_scan_results(this->scan_result_.results());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1099,8 +1108,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();
|
||||
|
||||
@@ -478,22 +478,29 @@ void WiFiComponent::wifi_scan_done_callback_() {
|
||||
if (num < 0)
|
||||
return;
|
||||
|
||||
this->scan_result_.init(static_cast<unsigned int>(num));
|
||||
// Count unique SSIDs
|
||||
UniqueSSIDCounter ssid_counter;
|
||||
for (int i = 0; i < num; i++) {
|
||||
String ssid = WiFi.SSID(i);
|
||||
ssid_counter.add(ssid.c_str(), static_cast<uint8_t>(ssid.length()));
|
||||
}
|
||||
|
||||
this->scan_result_.init(static_cast<unsigned int>(num), ssid_counter.pool_size());
|
||||
for (int i = 0; i < num; i++) {
|
||||
String ssid = WiFi.SSID(i);
|
||||
wifi_auth_mode_t authmode = WiFi.encryptionType(i);
|
||||
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
|
||||
for (auto *listener : this->scan_results_listeners_) {
|
||||
listener->on_wifi_scan_results(this->scan_result_);
|
||||
listener->on_wifi_scan_results(this->scan_result_.results());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -247,7 +254,7 @@ void WiFiComponent::wifi_loop_() {
|
||||
ESP_LOGV(TAG, "Scan done");
|
||||
#ifdef USE_WIFI_LISTENERS
|
||||
for (auto *listener : this->scan_results_listeners_) {
|
||||
listener->on_wifi_scan_results(this->scan_result_);
|
||||
listener->on_wifi_scan_results(this->scan_result_.results());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -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