1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-08 12:53:45 +01:00

[mdns][openthread] Use StaticVector for services storage with compile-time capacity (#10976)

This commit is contained in:
J. Nick Koston
2025-10-05 15:30:17 -05:00
committed by GitHub
parent 19439199cc
commit 0fd71ca211
7 changed files with 44 additions and 51 deletions

View File

@@ -17,6 +17,11 @@ from esphome.coroutine import CoroPriority
CODEOWNERS = ["@esphome/core"]
DEPENDENCIES = ["network"]
# Components that create mDNS services at runtime
# IMPORTANT: If you add a new component here, you must also update the corresponding
# #ifdef blocks in mdns_component.cpp compile_records_() method
COMPONENTS_WITH_MDNS_SERVICES = ("api", "prometheus", "web_server")
mdns_ns = cg.esphome_ns.namespace("mdns")
MDNSComponent = mdns_ns.class_("MDNSComponent", cg.Component)
MDNSTXTRecord = mdns_ns.struct("MDNSTXTRecord")
@@ -91,12 +96,20 @@ async def to_code(config):
cg.add_define("USE_MDNS")
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
# Calculate compile-time service count
service_count = sum(
1 for key in COMPONENTS_WITH_MDNS_SERVICES if key in CORE.config
) + len(config[CONF_SERVICES])
if config[CONF_SERVICES]:
cg.add_define("USE_MDNS_EXTRA_SERVICES")
# Ensure at least 1 service (fallback service)
cg.add_define("MDNS_SERVICE_COUNT", max(1, service_count))
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
for service in config[CONF_SERVICES]:
txt = [
cg.StructInitializer(

View File

@@ -74,32 +74,12 @@ MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread");
void MDNSComponent::compile_records_() {
this->hostname_ = App.get_name();
// Calculate exact capacity needed for services vector
size_t services_count = 0;
#ifdef USE_API
if (api::global_api_server != nullptr) {
services_count++;
}
#endif
#ifdef USE_PROMETHEUS
services_count++;
#endif
#ifdef USE_WEBSERVER
services_count++;
#endif
#ifdef USE_MDNS_EXTRA_SERVICES
services_count += this->services_extra_.size();
#endif
// Reserve for fallback service if needed
if (services_count == 0) {
services_count = 1;
}
this->services_.reserve(services_count);
// IMPORTANT: The #ifdef blocks below must match COMPONENTS_WITH_MDNS_SERVICES
// in mdns/__init__.py. If you add a new service here, update both locations.
#ifdef USE_API
if (api::global_api_server != nullptr) {
this->services_.emplace_back();
auto &service = this->services_.back();
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();
@@ -178,30 +158,23 @@ void MDNSComponent::compile_records_() {
#endif // USE_API
#ifdef USE_PROMETHEUS
this->services_.emplace_back();
auto &prom_service = this->services_.back();
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
this->services_.emplace_back();
auto &web_service = this->services_.back();
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;
#endif
#ifdef USE_MDNS_EXTRA_SERVICES
this->services_.insert(this->services_.end(), this->services_extra_.begin(), this->services_extra_.end());
#endif
#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
this->services_.emplace_back();
auto &fallback_service = this->services_.back();
auto &fallback_service = this->services_.emplace_next();
fallback_service.service_type = "_http";
fallback_service.proto = "_tcp";
fallback_service.port = USE_WEBSERVER_PORT;
@@ -214,7 +187,7 @@ void MDNSComponent::dump_config() {
"mDNS:\n"
" Hostname: %s",
this->hostname_.c_str());
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGV(TAG, " Services:");
for (const auto &service : this->services_) {
ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(),
@@ -227,8 +200,6 @@ void MDNSComponent::dump_config() {
#endif
}
std::vector<MDNSService> MDNSComponent::get_services() { return this->services_; }
} // namespace mdns
} // namespace esphome
#endif

View File

@@ -2,13 +2,16 @@
#include "esphome/core/defines.h"
#ifdef USE_MDNS
#include <string>
#include <vector>
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace mdns {
// Service count is calculated at compile time by Python codegen
// MDNS_SERVICE_COUNT will always be defined
struct MDNSTXTRecord {
std::string key;
TemplatableValue<std::string> value;
@@ -36,18 +39,15 @@ 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) { services_extra_.push_back(std::move(service)); }
void add_extra_service(MDNSService service) { this->services_.emplace_next() = std::move(service); }
#endif
std::vector<MDNSService> get_services();
const StaticVector<MDNSService, MDNS_SERVICE_COUNT> &get_services() const { return this->services_; }
void on_shutdown() override;
protected:
#ifdef USE_MDNS_EXTRA_SERVICES
std::vector<MDNSService> services_extra_{};
#endif
std::vector<MDNSService> services_{};
StaticVector<MDNSService, MDNS_SERVICE_COUNT> services_{};
std::string hostname_;
void compile_records_();
};

View File

@@ -143,11 +143,10 @@ void OpenThreadSrpComponent::setup() {
return;
}
// Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
// component
this->mdns_services_ = this->mdns_->get_services();
ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
for (const auto &service : this->mdns_services_) {
// Get mdns services and copy their data (strings are copied with strdup below)
const auto &mdns_services = this->mdns_->get_services();
ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", mdns_services.size());
for (const auto &service : mdns_services) {
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
if (!entry) {
ESP_LOGW(TAG, "Failed to allocate service entry");

View File

@@ -57,7 +57,6 @@ class OpenThreadSrpComponent : public Component {
protected:
esphome::mdns::MDNSComponent *mdns_{nullptr};
std::vector<esphome::mdns::MDNSService> mdns_services_;
std::vector<std::unique_ptr<uint8_t[]>> memory_pool_;
void *pool_alloc_(size_t size);
};

View File

@@ -82,6 +82,7 @@
#define USE_LVGL_TILEVIEW
#define USE_LVGL_TOUCHSCREEN
#define USE_MDNS
#define MDNS_SERVICE_COUNT 3
#define USE_MEDIA_PLAYER
#define USE_NEXTION_TFT_UPLOAD
#define USE_NUMBER

View File

@@ -127,6 +127,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; }