ESPHome 2026.3.0-dev
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#else
13// On non-ESP8266 platforms, use regular const char*
14#define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char name[] = value
15#endif
16
17#ifdef USE_API
19#endif
20#ifdef USE_DASHBOARD_IMPORT
22#endif
23
24namespace esphome::mdns {
25
26static const char *const TAG = "mdns";
27
28#ifndef USE_WEBSERVER_PORT
29#define USE_WEBSERVER_PORT 80 // NOLINT
30#endif
31
32#ifndef USE_SENDSPIN_PORT
33#define USE_SENDSPIN_PORT 8928 // NOLINT
34#endif
35
36// Define all constant strings using the macro
37MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp");
38
39// Wrap build-time defines into flash storage
40MDNS_STATIC_CONST_CHAR(VALUE_VERSION, ESPHOME_VERSION);
41
43 // IMPORTANT: The #ifdef blocks below must match COMPONENTS_WITH_MDNS_SERVICES
44 // in mdns/__init__.py. If you add a new service here, update both locations.
45
46#ifdef USE_API
47 MDNS_STATIC_CONST_CHAR(SERVICE_ESPHOMELIB, "_esphomelib");
48 MDNS_STATIC_CONST_CHAR(TXT_FRIENDLY_NAME, "friendly_name");
49 MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version");
50 MDNS_STATIC_CONST_CHAR(TXT_MAC, "mac");
51 MDNS_STATIC_CONST_CHAR(TXT_PLATFORM, "platform");
52 MDNS_STATIC_CONST_CHAR(TXT_BOARD, "board");
53 MDNS_STATIC_CONST_CHAR(TXT_NETWORK, "network");
54 MDNS_STATIC_CONST_CHAR(VALUE_BOARD, ESPHOME_BOARD);
55
56 if (api::global_api_server != nullptr) {
57 auto &service = services.emplace_next();
58 service.service_type = MDNS_STR(SERVICE_ESPHOMELIB);
59 service.proto = MDNS_STR(SERVICE_TCP);
60 service.port = api::global_api_server->get_port();
61
62 const std::string &friendly_name = App.get_friendly_name();
63 bool friendly_name_empty = friendly_name.empty();
64
65 // Calculate exact capacity for txt_records
66 size_t txt_count = 3; // version, mac, board (always present)
67 if (!friendly_name_empty) {
68 txt_count++; // friendly_name
69 }
70#if defined(USE_ESP8266) || defined(USE_ESP32) || defined(USE_RP2040) || defined(USE_LIBRETINY)
71 txt_count++; // platform
72#endif
73#if defined(USE_WIFI) || defined(USE_ETHERNET) || defined(USE_OPENTHREAD)
74 txt_count++; // network
75#endif
76#ifdef USE_API_NOISE
77 txt_count++; // api_encryption or api_encryption_supported
78#endif
79#ifdef ESPHOME_PROJECT_NAME
80 txt_count += 2; // project_name and project_version
81#endif
82#ifdef USE_DASHBOARD_IMPORT
83 txt_count++; // package_import_url
84#endif
85
86 auto &txt_records = service.txt_records;
87 txt_records.init(txt_count);
88
89 if (!friendly_name_empty) {
90 txt_records.push_back({MDNS_STR(TXT_FRIENDLY_NAME), MDNS_STR(friendly_name.c_str())});
91 }
92 txt_records.push_back({MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)});
93
94 // MAC address: passed from caller (either member buffer or stack buffer depending on USE_MDNS_STORE_SERVICES)
95 txt_records.push_back({MDNS_STR(TXT_MAC), MDNS_STR(mac_address_buf)});
96
97#ifdef USE_ESP8266
98 MDNS_STATIC_CONST_CHAR(PLATFORM_ESP8266, "ESP8266");
99 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP8266)});
100#elif defined(USE_ESP32)
101 MDNS_STATIC_CONST_CHAR(PLATFORM_ESP32, "ESP32");
102 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP32)});
103#elif defined(USE_RP2040)
104 MDNS_STATIC_CONST_CHAR(PLATFORM_RP2040, "RP2040");
105 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_RP2040)});
106#elif defined(USE_LIBRETINY)
107 txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(lt_cpu_get_model_name())});
108#endif
109
110 txt_records.push_back({MDNS_STR(TXT_BOARD), MDNS_STR(VALUE_BOARD)});
111
112#if defined(USE_WIFI)
113 MDNS_STATIC_CONST_CHAR(NETWORK_WIFI, "wifi");
114 txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_WIFI)});
115#elif defined(USE_ETHERNET)
116 MDNS_STATIC_CONST_CHAR(NETWORK_ETHERNET, "ethernet");
117 txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_ETHERNET)});
118#elif defined(USE_OPENTHREAD)
119 MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread");
120 txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_THREAD)});
121#endif
122
123#ifdef USE_API_NOISE
124 MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION, "api_encryption");
125 MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION_SUPPORTED, "api_encryption_supported");
126 MDNS_STATIC_CONST_CHAR(NOISE_ENCRYPTION, "Noise_NNpsk0_25519_ChaChaPoly_SHA256");
127 bool has_psk = api::global_api_server->get_noise_ctx().has_psk();
128 const char *encryption_key = has_psk ? TXT_API_ENCRYPTION : TXT_API_ENCRYPTION_SUPPORTED;
129 txt_records.push_back({MDNS_STR(encryption_key), MDNS_STR(NOISE_ENCRYPTION)});
130#endif
131
132#ifdef ESPHOME_PROJECT_NAME
133 MDNS_STATIC_CONST_CHAR(TXT_PROJECT_NAME, "project_name");
134 MDNS_STATIC_CONST_CHAR(TXT_PROJECT_VERSION, "project_version");
135 MDNS_STATIC_CONST_CHAR(VALUE_PROJECT_NAME, ESPHOME_PROJECT_NAME);
136 MDNS_STATIC_CONST_CHAR(VALUE_PROJECT_VERSION, ESPHOME_PROJECT_VERSION);
137 txt_records.push_back({MDNS_STR(TXT_PROJECT_NAME), MDNS_STR(VALUE_PROJECT_NAME)});
138 txt_records.push_back({MDNS_STR(TXT_PROJECT_VERSION), MDNS_STR(VALUE_PROJECT_VERSION)});
139#endif // ESPHOME_PROJECT_NAME
140
141#ifdef USE_DASHBOARD_IMPORT
142 MDNS_STATIC_CONST_CHAR(TXT_PACKAGE_IMPORT_URL, "package_import_url");
143 txt_records.push_back({MDNS_STR(TXT_PACKAGE_IMPORT_URL), MDNS_STR(dashboard_import::get_package_import_url())});
144#endif
145 }
146#endif // USE_API
147
148#ifdef USE_PROMETHEUS
149 MDNS_STATIC_CONST_CHAR(SERVICE_PROMETHEUS, "_prometheus-http");
150
151 auto &prom_service = services.emplace_next();
152 prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS);
153 prom_service.proto = MDNS_STR(SERVICE_TCP);
154 prom_service.port = USE_WEBSERVER_PORT;
155#endif
156
157#ifdef USE_SENDSPIN
158 MDNS_STATIC_CONST_CHAR(SERVICE_SENDSPIN, "_sendspin");
159 MDNS_STATIC_CONST_CHAR(TXT_SENDSPIN_PATH, "path");
160 MDNS_STATIC_CONST_CHAR(VALUE_SENDSPIN_PATH, "/sendspin");
161
162 auto &sendspin_service = services.emplace_next();
163 sendspin_service.service_type = MDNS_STR(SERVICE_SENDSPIN);
164 sendspin_service.proto = MDNS_STR(SERVICE_TCP);
165 sendspin_service.port = USE_SENDSPIN_PORT;
166 sendspin_service.txt_records = {{MDNS_STR(TXT_SENDSPIN_PATH), MDNS_STR(VALUE_SENDSPIN_PATH)}};
167#endif
168
169#ifdef USE_WEBSERVER
170 MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http");
171
172 auto &web_service = services.emplace_next();
173 web_service.service_type = MDNS_STR(SERVICE_HTTP);
174 web_service.proto = MDNS_STR(SERVICE_TCP);
175 web_service.port = USE_WEBSERVER_PORT;
176#endif
177
178#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_SENDSPIN) && !defined(USE_WEBSERVER) && \
179 !defined(USE_MDNS_EXTRA_SERVICES)
180 MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http");
181 MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version");
182
183 // Publish "http" service if not using native API or any other services
184 // This is just to have *some* mDNS service so that .local resolution works
185 auto &fallback_service = services.emplace_next();
186 fallback_service.service_type = MDNS_STR(SERVICE_HTTP);
187 fallback_service.proto = MDNS_STR(SERVICE_TCP);
188 fallback_service.port = USE_WEBSERVER_PORT;
189 fallback_service.txt_records = {{MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)}};
190#endif
191}
192
194 ESP_LOGCONFIG(TAG,
195 "mDNS:\n"
196 " Hostname: %s",
197 App.get_name().c_str());
198#ifdef USE_MDNS_STORE_SERVICES
199 ESP_LOGV(TAG, " Services:");
200 for (const auto &service : this->services_) {
201 ESP_LOGV(TAG, " - %s, %s, %d", MDNS_STR_ARG(service.service_type), MDNS_STR_ARG(service.proto),
202 const_cast<TemplatableValue<uint16_t> &>(service.port).value());
203 for (const auto &record : service.txt_records) {
204 ESP_LOGV(TAG, " TXT: %s = %s", MDNS_STR_ARG(record.key), MDNS_STR_ARG(record.value));
205 }
206 }
207#endif
208}
209
210} // namespace esphome::mdns
211#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().
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:209
T value(X... x) const
Definition automation.h:160
APINoiseContext & get_noise_ctx()
Definition api_server.h:74
uint16_t get_port() const
void compile_records_(StaticVector< MDNSService, MDNS_SERVICE_COUNT > &services, char *mac_address_buf)
StaticVector< MDNSService, MDNS_SERVICE_COUNT > services_
APIServer * global_api_server
const char * get_package_import_url()
MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp")
Application App
Global storage of Application pointer - only one Application can exist.