mirror of
https://github.com/esphome/esphome.git
synced 2025-10-22 19:53:46 +01:00
Merge branch 'esp32_ble_server_unique_ptr_mfr_data' into integration
This commit is contained in:
@@ -68,6 +68,10 @@ void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->advertising_set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
this->advertising_init_();
|
||||
this->advertising_->set_manufacturer_data(data);
|
||||
this->advertising_start();
|
||||
|
@@ -118,6 +118,7 @@ class ESP32BLE : public Component {
|
||||
void advertising_start();
|
||||
void advertising_set_service_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void advertising_set_appearance(uint16_t appearance) { this->appearance_ = appearance; }
|
||||
void advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name);
|
||||
void advertising_add_service_uuid(ESPBTUUID uuid);
|
||||
|
@@ -59,6 +59,10 @@ void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
delete[] this->advertising_data_.p_manufacturer_data;
|
||||
this->advertising_data_.p_manufacturer_data = nullptr;
|
||||
this->advertising_data_.manufacturer_len = data.size();
|
||||
|
@@ -35,6 +35,7 @@ class BLEAdvertising {
|
||||
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
|
||||
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
|
||||
void set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void set_appearance(uint16_t appearance) { this->advertising_data_.appearance = appearance; }
|
||||
void set_service_data(const std::vector<uint8_t> &data);
|
||||
void set_service_data(std::span<const uint8_t> data);
|
||||
|
@@ -99,7 +99,8 @@ bool BLEServer::can_proceed() { return this->is_running() || !this->parent_->is_
|
||||
|
||||
void BLEServer::restart_advertising_() {
|
||||
if (this->is_running()) {
|
||||
this->parent_->advertising_set_manufacturer_data(this->manufacturer_data_);
|
||||
this->parent_->advertising_set_manufacturer_data(
|
||||
std::span<const uint8_t>(this->manufacturer_data_.get(), this->manufacturer_data_length_));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,11 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
||||
bool is_running();
|
||||
|
||||
void set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->manufacturer_data_ = data;
|
||||
this->manufacturer_data_length_ = data.size();
|
||||
this->manufacturer_data_.reset(data.empty() ? nullptr : new uint8_t[data.size()]);
|
||||
if (!data.empty()) {
|
||||
memcpy(this->manufacturer_data_.get(), data.data(), data.size());
|
||||
}
|
||||
this->restart_advertising_();
|
||||
}
|
||||
|
||||
@@ -87,24 +91,27 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
||||
void remove_client_(uint16_t conn_id);
|
||||
void dispatch_callbacks_(CallbackType type, uint16_t conn_id);
|
||||
|
||||
// 4-byte aligned (pointers and vectors on 32-bit)
|
||||
std::vector<CallbackEntry> callbacks_;
|
||||
|
||||
std::vector<uint8_t> manufacturer_data_{};
|
||||
esp_gatt_if_t gatts_if_{0};
|
||||
bool registered_{false};
|
||||
|
||||
uint16_t clients_[USE_ESP32_BLE_MAX_CONNECTIONS]{};
|
||||
uint8_t client_count_{0};
|
||||
std::vector<ServiceEntry> services_{};
|
||||
std::vector<BLEService *> services_to_start_{};
|
||||
std::unique_ptr<uint8_t[]> manufacturer_data_{};
|
||||
BLEService *device_information_service_{};
|
||||
|
||||
// 2-byte aligned
|
||||
uint16_t clients_[USE_ESP32_BLE_MAX_CONNECTIONS]{};
|
||||
|
||||
// 1-byte aligned
|
||||
uint8_t manufacturer_data_length_{0};
|
||||
uint8_t client_count_{0};
|
||||
esp_gatt_if_t gatts_if_{0};
|
||||
enum State : uint8_t {
|
||||
INIT = 0x00,
|
||||
REGISTERING,
|
||||
STARTING_SERVICE,
|
||||
RUNNING,
|
||||
} state_{INIT};
|
||||
bool registered_{false};
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
@@ -79,7 +79,7 @@ void MDNSComponent::compile_records_() {
|
||||
|
||||
#ifdef USE_API
|
||||
if (api::global_api_server != nullptr) {
|
||||
auto &service = this->services_[this->services_.count()++];
|
||||
auto &service = this->services_.emplace_next();
|
||||
service.service_type = MDNS_STR(SERVICE_ESPHOMELIB);
|
||||
service.proto = MDNS_STR(SERVICE_TCP);
|
||||
service.port = api::global_api_server->get_port();
|
||||
@@ -158,14 +158,14 @@ void MDNSComponent::compile_records_() {
|
||||
#endif // USE_API
|
||||
|
||||
#ifdef USE_PROMETHEUS
|
||||
auto &prom_service = this->services_[this->services_.count()++];
|
||||
auto &prom_service = this->services_.emplace_next();
|
||||
prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS);
|
||||
prom_service.proto = MDNS_STR(SERVICE_TCP);
|
||||
prom_service.port = USE_WEBSERVER_PORT;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
auto &web_service = this->services_[this->services_.count()++];
|
||||
auto &web_service = this->services_.emplace_next();
|
||||
web_service.service_type = MDNS_STR(SERVICE_HTTP);
|
||||
web_service.proto = MDNS_STR(SERVICE_TCP);
|
||||
web_service.port = USE_WEBSERVER_PORT;
|
||||
@@ -174,7 +174,7 @@ void MDNSComponent::compile_records_() {
|
||||
#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES)
|
||||
// Publish "http" service if not using native API or any other services
|
||||
// This is just to have *some* mDNS service so that .local resolution works
|
||||
auto &fallback_service = this->services_[this->services_.count()++];
|
||||
auto &fallback_service = this->services_.emplace_next();
|
||||
fallback_service.service_type = "_http";
|
||||
fallback_service.proto = "_tcp";
|
||||
fallback_service.port = USE_WEBSERVER_PORT;
|
||||
|
@@ -39,7 +39,7 @@ class MDNSComponent : public Component {
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; }
|
||||
|
||||
#ifdef USE_MDNS_EXTRA_SERVICES
|
||||
void add_extra_service(MDNSService service) { this->services_[this->services_.count()++] = std::move(service); }
|
||||
void add_extra_service(MDNSService service) { this->services_.emplace_next() = std::move(service); }
|
||||
#endif
|
||||
|
||||
const StaticVector<MDNSService, MDNS_SERVICE_COUNT> &get_services() const { return this->services_; }
|
||||
|
@@ -52,6 +52,20 @@ DefaultHeaders &DefaultHeaders::Instance() { return default_headers_instance; }
|
||||
|
||||
namespace {
|
||||
// Non-blocking send function to prevent watchdog timeouts when TCP buffers are full
|
||||
/**
|
||||
* Sends data on a socket in non-blocking mode.
|
||||
*
|
||||
* @param hd HTTP server handle (unused).
|
||||
* @param sockfd Socket file descriptor.
|
||||
* @param buf Buffer to send.
|
||||
* @param buf_len Length of buffer.
|
||||
* @param flags Flags for send().
|
||||
* @return
|
||||
* - Number of bytes sent on success.
|
||||
* - HTTPD_SOCK_ERR_INVALID if buf is nullptr.
|
||||
* - HTTPD_SOCK_ERR_TIMEOUT if the send buffer is full (EAGAIN/EWOULDBLOCK).
|
||||
* - HTTPD_SOCK_ERR_FAIL for other errors.
|
||||
*/
|
||||
int nonblocking_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags) {
|
||||
if (buf == nullptr) {
|
||||
return HTTPD_SOCK_ERR_INVALID;
|
||||
@@ -319,8 +333,8 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const std::string &name) {
|
||||
}
|
||||
}
|
||||
|
||||
// Don't cache misses to prevent memory exhaustion from malicious requests
|
||||
// with thousands of non-existent parameter lookups
|
||||
// Don't cache misses to avoid wasting memory when handlers check for
|
||||
// optional parameters that don't exist in the request
|
||||
if (!val.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -172,7 +172,8 @@ class AsyncWebServerRequest {
|
||||
AsyncWebServerResponse *rsp_{};
|
||||
// Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search
|
||||
// is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
|
||||
// duplicate storage. Only successful lookups are cached to prevent memory exhaustion attacks.
|
||||
// duplicate storage. Only successful lookups are cached to prevent cache pollution when
|
||||
// handlers check for optional parameters that don't exist.
|
||||
std::vector<AsyncWebParameter *> params_;
|
||||
std::string post_query_;
|
||||
AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}
|
||||
|
@@ -365,7 +365,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
|
||||
ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
|
||||
ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
|
||||
ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
|
||||
#if defined(USE_ESP32) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
#if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), eap_phase2_to_str(eap_config.ttls_phase_2));
|
||||
#endif
|
||||
bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert);
|
||||
|
@@ -33,12 +33,22 @@ static const char *const TAG = "component";
|
||||
// Using namespace-scope static to avoid guard variables (saves 16 bytes total)
|
||||
// This is safe because ESPHome is single-threaded during initialization
|
||||
namespace {
|
||||
struct ComponentErrorMessage {
|
||||
const Component *component;
|
||||
const char *message;
|
||||
};
|
||||
|
||||
struct ComponentPriorityOverride {
|
||||
const Component *component;
|
||||
float priority;
|
||||
};
|
||||
|
||||
// Error messages for failed components
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::unique_ptr<std::vector<std::pair<const Component *, const char *>>> component_error_messages;
|
||||
std::unique_ptr<std::vector<ComponentErrorMessage>> component_error_messages;
|
||||
// Setup priority overrides - freed after setup completes
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::unique_ptr<std::vector<std::pair<const Component *, float>>> setup_priority_overrides;
|
||||
std::unique_ptr<std::vector<ComponentPriorityOverride>> setup_priority_overrides;
|
||||
} // namespace
|
||||
|
||||
namespace setup_priority {
|
||||
@@ -134,9 +144,9 @@ void Component::call_dump_config() {
|
||||
// Look up error message from global vector
|
||||
const char *error_msg = nullptr;
|
||||
if (component_error_messages) {
|
||||
for (const auto &pair : *component_error_messages) {
|
||||
if (pair.first == this) {
|
||||
error_msg = pair.second;
|
||||
for (const auto &entry : *component_error_messages) {
|
||||
if (entry.component == this) {
|
||||
error_msg = entry.message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -306,17 +316,17 @@ void Component::status_set_error(const char *message) {
|
||||
if (message != nullptr) {
|
||||
// Lazy allocate the error messages vector if needed
|
||||
if (!component_error_messages) {
|
||||
component_error_messages = std::make_unique<std::vector<std::pair<const Component *, const char *>>>();
|
||||
component_error_messages = std::make_unique<std::vector<ComponentErrorMessage>>();
|
||||
}
|
||||
// Check if this component already has an error message
|
||||
for (auto &pair : *component_error_messages) {
|
||||
if (pair.first == this) {
|
||||
pair.second = message;
|
||||
for (auto &entry : *component_error_messages) {
|
||||
if (entry.component == this) {
|
||||
entry.message = message;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add new error message
|
||||
component_error_messages->emplace_back(this, message);
|
||||
component_error_messages->emplace_back(ComponentErrorMessage{this, message});
|
||||
}
|
||||
}
|
||||
void Component::status_clear_warning() {
|
||||
@@ -356,9 +366,9 @@ float Component::get_actual_setup_priority() const {
|
||||
// Check if there's an override in the global vector
|
||||
if (setup_priority_overrides) {
|
||||
// Linear search is fine for small n (typically < 5 overrides)
|
||||
for (const auto &pair : *setup_priority_overrides) {
|
||||
if (pair.first == this) {
|
||||
return pair.second;
|
||||
for (const auto &entry : *setup_priority_overrides) {
|
||||
if (entry.component == this) {
|
||||
return entry.priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,21 +377,21 @@ float Component::get_actual_setup_priority() const {
|
||||
void Component::set_setup_priority(float priority) {
|
||||
// Lazy allocate the vector if needed
|
||||
if (!setup_priority_overrides) {
|
||||
setup_priority_overrides = std::make_unique<std::vector<std::pair<const Component *, float>>>();
|
||||
setup_priority_overrides = std::make_unique<std::vector<ComponentPriorityOverride>>();
|
||||
// Reserve some space to avoid reallocations (most configs have < 10 overrides)
|
||||
setup_priority_overrides->reserve(10);
|
||||
}
|
||||
|
||||
// Check if this component already has an override
|
||||
for (auto &pair : *setup_priority_overrides) {
|
||||
if (pair.first == this) {
|
||||
pair.second = priority;
|
||||
for (auto &entry : *setup_priority_overrides) {
|
||||
if (entry.component == this) {
|
||||
entry.priority = priority;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new override
|
||||
setup_priority_overrides->emplace_back(this, priority);
|
||||
setup_priority_overrides->emplace_back(ComponentPriorityOverride{this, priority});
|
||||
}
|
||||
|
||||
bool Component::has_overridden_loop() const {
|
||||
|
@@ -130,6 +130,16 @@ template<typename T, size_t N> class StaticVector {
|
||||
}
|
||||
}
|
||||
|
||||
// Return reference to next element and increment count (with bounds checking)
|
||||
T &emplace_next() {
|
||||
if (count_ >= N) {
|
||||
// Should never happen with proper size calculation
|
||||
// Return reference to last element to avoid crash
|
||||
return data_[N - 1];
|
||||
}
|
||||
return data_[count_++];
|
||||
}
|
||||
|
||||
size_t size() const { return count_; }
|
||||
bool empty() const { return count_ == 0; }
|
||||
|
||||
|
Reference in New Issue
Block a user