ESPHome 2025.9.0
Loading...
Searching...
No Matches
mdns_component.cpp
Go to the documentation of this file.
2#ifdef USE_MDNS
4#include "esphome/core/log.h"
6#include "mdns_component.h"
7
8#ifdef USE_ESP8266
9#include <pgmspace.h>
10// Macro to define strings in PROGMEM on ESP8266, regular memory on other platforms
11#define MDNS_STATIC_CONST_CHAR(name, value) static const char name[] PROGMEM = value
12// Helper to get string from PROGMEM - returns a temporary std::string
13// Only define this function if we have services that will use it
14#if defined(USE_API) || defined(USE_PROMETHEUS) || defined(USE_WEBSERVER) || defined(USE_MDNS_EXTRA_SERVICES)
15static std::string mdns_string_p(const char *src) {
16 char buf[64];
17 strncpy_P(buf, src, sizeof(buf) - 1);
18 buf[sizeof(buf) - 1] = '\0';
19 return std::string(buf);
20}
21#define MDNS_STR(name) mdns_string_p(name)
22#else
23// If no services are configured, we still need the fallback service but it uses string literals
24#define MDNS_STR(name) std::string(name)
25#endif
26#else
27// On non-ESP8266 platforms, use regular const char*
28#define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char *name = value
29#define MDNS_STR(name) name
30#endif
31
32#ifdef USE_API
34#endif
35#ifdef USE_DASHBOARD_IMPORT
37#endif
38
39namespace esphome {
40namespace mdns {
41
42static const char *const TAG = "mdns";
43
44#ifndef USE_WEBSERVER_PORT
45#define USE_WEBSERVER_PORT 80 // NOLINT
46#endif
47
48// Define all constant strings using the macro
49MDNS_STATIC_CONST_CHAR(SERVICE_ESPHOMELIB, "_esphomelib");
50MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp");
51MDNS_STATIC_CONST_CHAR(SERVICE_PROMETHEUS, "_prometheus-http");
52MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http");
53
54MDNS_STATIC_CONST_CHAR(TXT_FRIENDLY_NAME, "friendly_name");
55MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version");
56MDNS_STATIC_CONST_CHAR(TXT_MAC, "mac");
57MDNS_STATIC_CONST_CHAR(TXT_PLATFORM, "platform");
58MDNS_STATIC_CONST_CHAR(TXT_BOARD, "board");
59MDNS_STATIC_CONST_CHAR(TXT_NETWORK, "network");
60MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION, "api_encryption");
61MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION_SUPPORTED, "api_encryption_supported");
62MDNS_STATIC_CONST_CHAR(TXT_PROJECT_NAME, "project_name");
63MDNS_STATIC_CONST_CHAR(TXT_PROJECT_VERSION, "project_version");
64MDNS_STATIC_CONST_CHAR(TXT_PACKAGE_IMPORT_URL, "package_import_url");
65
66MDNS_STATIC_CONST_CHAR(PLATFORM_ESP8266, "ESP8266");
67MDNS_STATIC_CONST_CHAR(PLATFORM_ESP32, "ESP32");
68MDNS_STATIC_CONST_CHAR(PLATFORM_RP2040, "RP2040");
69
70MDNS_STATIC_CONST_CHAR(NETWORK_WIFI, "wifi");
71MDNS_STATIC_CONST_CHAR(NETWORK_ETHERNET, "ethernet");
72MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread");
73
75 this->hostname_ = App.get_name();
76
77 // Calculate exact capacity needed for services vector
78 size_t services_count = 0;
79#ifdef USE_API
80 if (api::global_api_server != nullptr) {
81 services_count++;
82 }
83#endif
84#ifdef USE_PROMETHEUS
85 services_count++;
86#endif
87#ifdef USE_WEBSERVER
88 services_count++;
89#endif
90#ifdef USE_MDNS_EXTRA_SERVICES
91 services_count += this->services_extra_.size();
92#endif
93 // Reserve for fallback service if needed
94 if (services_count == 0) {
95 services_count = 1;
96 }
97 this->services_.reserve(services_count);
98
99#ifdef USE_API
100 if (api::global_api_server != nullptr) {
101 this->services_.emplace_back();
102 auto &service = this->services_.back();
103 service.service_type = MDNS_STR(SERVICE_ESPHOMELIB);
104 service.proto = MDNS_STR(SERVICE_TCP);
105 service.port = api::global_api_server->get_port();
106
107 const std::string &friendly_name = App.get_friendly_name();
108 bool friendly_name_empty = friendly_name.empty();
109
110 // Calculate exact capacity for txt_records
111 size_t txt_count = 3; // version, mac, board (always present)
112 if (!friendly_name_empty) {
113 txt_count++; // friendly_name
114 }
115#if defined(USE_ESP8266) || defined(USE_ESP32) || defined(USE_RP2040) || defined(USE_LIBRETINY)
116 txt_count++; // platform
117#endif
118#if defined(USE_WIFI) || defined(USE_ETHERNET) || defined(USE_OPENTHREAD)
119 txt_count++; // network
120#endif
121#ifdef USE_API_NOISE
122 txt_count++; // api_encryption or api_encryption_supported
123#endif
124#ifdef ESPHOME_PROJECT_NAME
125 txt_count += 2; // project_name and project_version
126#endif
127#ifdef USE_DASHBOARD_IMPORT
128 txt_count++; // package_import_url
129#endif
130
131 auto &txt_records = service.txt_records;
132 txt_records.reserve(txt_count);
133
134 if (!friendly_name_empty) {
135 txt_records.push_back({MDNS_STR(TXT_FRIENDLY_NAME), friendly_name});
136 }
137 txt_records.push_back({MDNS_STR(TXT_VERSION), ESPHOME_VERSION});
138 txt_records.push_back({MDNS_STR(TXT_MAC), get_mac_address()});
139
140#ifdef USE_ESP8266
141 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP8266)});
142#elif defined(USE_ESP32)
143 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP32)});
144#elif defined(USE_RP2040)
145 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_RP2040)});
146#elif defined(USE_LIBRETINY)
147 txt_records.emplace_back(MDNSTXTRecord{"platform", lt_cpu_get_model_name()});
148#endif
149
150 txt_records.push_back({MDNS_STR(TXT_BOARD), ESPHOME_BOARD});
151
152#if defined(USE_WIFI)
153 txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_WIFI)});
154#elif defined(USE_ETHERNET)
155 txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_ETHERNET)});
156#elif defined(USE_OPENTHREAD)
157 txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_THREAD)});
158#endif
159
160#ifdef USE_API_NOISE
161 MDNS_STATIC_CONST_CHAR(NOISE_ENCRYPTION, "Noise_NNpsk0_25519_ChaChaPoly_SHA256");
162 if (api::global_api_server->get_noise_ctx()->has_psk()) {
163 txt_records.push_back({MDNS_STR(TXT_API_ENCRYPTION), MDNS_STR(NOISE_ENCRYPTION)});
164 } else {
165 txt_records.push_back({MDNS_STR(TXT_API_ENCRYPTION_SUPPORTED), MDNS_STR(NOISE_ENCRYPTION)});
166 }
167#endif
168
169#ifdef ESPHOME_PROJECT_NAME
170 txt_records.push_back({MDNS_STR(TXT_PROJECT_NAME), ESPHOME_PROJECT_NAME});
171 txt_records.push_back({MDNS_STR(TXT_PROJECT_VERSION), ESPHOME_PROJECT_VERSION});
172#endif // ESPHOME_PROJECT_NAME
173
174#ifdef USE_DASHBOARD_IMPORT
175 txt_records.push_back({MDNS_STR(TXT_PACKAGE_IMPORT_URL), dashboard_import::get_package_import_url()});
176#endif
177 }
178#endif // USE_API
179
180#ifdef USE_PROMETHEUS
181 this->services_.emplace_back();
182 auto &prom_service = this->services_.back();
183 prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS);
184 prom_service.proto = MDNS_STR(SERVICE_TCP);
185 prom_service.port = USE_WEBSERVER_PORT;
186#endif
187
188#ifdef USE_WEBSERVER
189 this->services_.emplace_back();
190 auto &web_service = this->services_.back();
191 web_service.service_type = MDNS_STR(SERVICE_HTTP);
192 web_service.proto = MDNS_STR(SERVICE_TCP);
193 web_service.port = USE_WEBSERVER_PORT;
194#endif
195
196#ifdef USE_MDNS_EXTRA_SERVICES
197 this->services_.insert(this->services_.end(), this->services_extra_.begin(), this->services_extra_.end());
198#endif
199
200#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES)
201 // Publish "http" service if not using native API or any other services
202 // This is just to have *some* mDNS service so that .local resolution works
203 this->services_.emplace_back();
204 auto &fallback_service = this->services_.back();
205 fallback_service.service_type = "_http";
206 fallback_service.proto = "_tcp";
207 fallback_service.port = USE_WEBSERVER_PORT;
208 fallback_service.txt_records.emplace_back(MDNSTXTRecord{"version", ESPHOME_VERSION});
209#endif
210}
211
213 ESP_LOGCONFIG(TAG,
214 "mDNS:\n"
215 " Hostname: %s",
216 this->hostname_.c_str());
217#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
218 ESP_LOGV(TAG, " Services:");
219 for (const auto &service : this->services_) {
220 ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(),
221 const_cast<TemplatableValue<uint16_t> &>(service.port).value());
222 for (const auto &record : service.txt_records) {
223 ESP_LOGV(TAG, " TXT: %s = %s", record.key.c_str(),
224 const_cast<TemplatableValue<std::string> &>(record.value).value().c_str());
225 }
226 }
227#endif
228}
229
230std::vector<MDNSService> MDNSComponent::get_services() { return this->services_; }
231
232} // namespace mdns
233} // namespace esphome
234#endif
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
const std::string & get_name() const
Get the name of this Application set by pre_setup().
uint16_t get_port() const
std::vector< MDNSService > services_
std::vector< MDNSService > get_services()
std::vector< MDNSService > services_extra_
APIServer * global_api_server
MDNS_STATIC_CONST_CHAR(SERVICE_ESPHOMELIB, "_esphomelib")
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition helpers.cpp:590
Application App
Global storage of Application pointer - only one Application can exist.