23#ifdef USE_DASHBOARD_IMPORT
29static const char *
const TAG =
"mqtt";
32PROGMEM_STRING_TABLE(MQTTDisconnectReasonStrings,
"TCP disconnected",
"Unacceptable Protocol Version",
33 "Identifier Rejected",
"Server Unavailable",
"Malformed Credentials",
"Not Authorized",
34 "Not Enough Space",
"TLS Bad Fingerprint",
"DNS Resolve Error",
"Unknown");
38 char mac_addr[MAC_ADDRESS_BUFFER_SIZE];
46 [
this](
const char *topic,
const char *payload,
size_t len,
size_t index,
size_t total) {
54 if (
len + index == total) {
68 this, [](
void *self, uint8_t level,
const char *tag,
const char *
message,
size_t message_len) {
76 "esphome/discover", [
this](
const std::string &topic,
const std::string &payload) { this->
send_device_info_(); },
81 constexpr size_t ping_topic_buffer_size = 13 + ESPHOME_DEVICE_NAME_MAX_LEN + 1;
82 char ping_topic[ping_topic_buffer_size];
83 buf_append_printf(ping_topic,
sizeof(ping_topic), 0,
"esphome/ping/%s",
App.
get_name().c_str());
85 ping_topic, [
this](
const std::string &topic,
const std::string &payload) { this->
send_device_info_(); }, 2);
99 constexpr size_t topic_buffer_size = 17 + ESPHOME_DEVICE_NAME_MAX_LEN + 1;
100 char topic[topic_buffer_size];
101 buf_append_printf(topic,
sizeof(topic), 0,
"esphome/discover/%s",
App.
get_name().c_str());
106 [](JsonObject root) {
111 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
117 buf_append_printf(key,
sizeof(key), 0,
"ip%u", index);
131 root[ESPHOME_F(
"version")] = ESPHOME_VERSION;
132 char mac_buf[MAC_ADDRESS_BUFFER_SIZE];
134 root[ESPHOME_F(
"mac")] = mac_buf;
137 root[ESPHOME_F(
"platform")] = ESPHOME_F(
"ESP8266");
140 root[ESPHOME_F(
"platform")] = ESPHOME_F(
"ESP32");
143 root[ESPHOME_F(
"platform")] = lt_cpu_get_model_name();
146 root[ESPHOME_F(
"board")] = ESPHOME_BOARD;
148 root[ESPHOME_F(
"network")] = ESPHOME_F(
"wifi");
149#elif defined(USE_ETHERNET)
150 root[ESPHOME_F(
"network")] = ESPHOME_F(
"ethernet");
153#ifdef ESPHOME_PROJECT_NAME
154 root[ESPHOME_F(
"project_name")] = ESPHOME_PROJECT_NAME;
155 root[ESPHOME_F(
"project_version")] = ESPHOME_PROJECT_VERSION;
158#ifdef USE_DASHBOARD_IMPORT
164 : ESPHOME_F(
"api_encryption_supported")] =
165 ESPHOME_F(
"Noise_NNpsk0_25519_ChaChaPoly_SHA256");
177 this->log_message_.retain);
183 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
187 " Server Address: %s:%u (%s)\n"
188 " Username: " LOG_SECRET(
"'%s'")
"\n"
189 " Client ID: " LOG_SECRET(
"'%s'")
"\n"
190 " Clean Session: %s",
192 this->credentials_.username.c_str(), this->credentials_.client_id.c_str(),
196 ESP_LOGCONFIG(TAG,
" Discovery IP enabled");
200 " Discovery prefix: '%s'\n"
201 " Discovery retain: %s",
204 ESP_LOGCONFIG(TAG,
" Topic Prefix: '%s'", this->
topic_prefix_.c_str());
219 subscription.subscribed =
false;
220 subscription.resubscribe_timeout = 0;
232 this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
235 this, LWIP_DNS_ADDRTYPE_IPV4);
242 this->
ip_ = network::IPAddress(&addr);
246 case ERR_INPROGRESS: {
248 ESP_LOGD(TAG,
"Resolving broker IP address");
254 ESP_LOGW(TAG,
"Error resolving broker IP address: %d", err);
279 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
280 ESP_LOGD(TAG,
"Resolved broker IP address to %s", this->
ip_.str_to(ip_buf));
283#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1
289 if (ipaddr ==
nullptr) {
290 a_this->dns_resolve_error_ =
true;
292 a_this->ip_ = network::IPAddress(ipaddr);
293 a_this->dns_resolved_ =
true;
301 ESP_LOGI(TAG,
"Connecting");
307 const char *username =
nullptr;
310 const char *password =
nullptr;
319 this->last_will_.payload.c_str());
342 ESP_LOGI(TAG,
"Connected");
358 const LogString *reason_s = MQTTDisconnectReasonStrings::get_log_str(
359 static_cast<uint8_t
>(*this->
disconnect_reason_), MQTTDisconnectReasonStrings::LAST_INDEX);
361 reason_s = LOG_STR(
"WiFi disconnected");
363 ESP_LOGW(TAG,
"Disconnected: %s", LOG_STR_ARG(reason_s));
386 ESP_LOGW(TAG,
"Lost client connection");
398 for (MQTTComponent *
component : this->children_) {
406 ESP_LOGE(TAG,
"Can't connect; restarting");
421 ESP_LOGV(TAG,
"subscribe(topic='%s')", topic);
424 ESP_LOGV(TAG,
"Subscribe failed for topic='%s'. Will retry", topic);
434 bool do_resub = sub->resubscribe_timeout == 0 || now - sub->resubscribe_timeout > 1000;
437 sub->subscribed = this->
subscribe_(sub->topic.c_str(), sub->qos);
438 sub->resubscribe_timeout = now;
442 for (
auto &subscription : this->subscriptions_) {
448 MQTTSubscription subscription{
451 .callback = std::move(callback),
453 .resubscribe_timeout = 0,
456 this->subscriptions_.push_back(subscription);
460 auto f = [callback](
const std::string &topic,
const std::string &payload) {
462 callback(topic, root);
466 MQTTSubscription subscription{
471 .resubscribe_timeout = 0,
474 this->subscriptions_.push_back(subscription);
481 ESP_LOGV(TAG,
"unsubscribe(topic='%s')", topic.c_str());
484 ESP_LOGV(TAG,
"Unsubscribe failed for topic='%s'.", topic.c_str());
490 if (it->topic == topic) {
500 return this->
publish(topic, payload.data(), payload.size(), qos, retain);
505 return this->
publish(topic.c_str(), payload, payload_length, qos, retain);
522 size_t topic_len = strlen(topic);
533 if (!logging_topic) {
535 ESP_LOGV(TAG,
"Publish(topic='%s' retain=%d qos=%d)", topic, retain, qos);
536 ESP_LOGVV(TAG,
"Publish payload (len=%u): '%.*s'", payload_length,
static_cast<int>(payload_length), payload);
538 ESP_LOGV(TAG,
"Publish failed for topic='%s' (len=%u). Will retry", topic, payload_length);
553 ESP_LOGD(TAG,
"Enabling");
562 ESP_LOGD(TAG,
"Disabling");
578static bool topic_match(
const char *
message,
const char *subscription,
bool is_normal,
bool past_separator) {
580 if (*
message ==
'\0' && *subscription ==
'\0')
584 if (*
message ==
'\0' || *subscription ==
'\0')
587 bool do_wildcards = is_normal || past_separator;
589 if (*subscription ==
'+' && do_wildcards) {
599 return topic_match(
message, subscription, is_normal,
true);
602 if (*subscription ==
'#' && do_wildcards) {
611 past_separator = past_separator || *subscription ==
'/';
617 return topic_match(
message, subscription, is_normal, past_separator);
620static bool topic_match(
const char *
message,
const char *subscription) {
644 this->
defer([
this, topic, payload]() {
646 for (
auto &subscription : this->subscriptions_) {
647 if (topic_match(topic.c_str(), subscription.topic.c_str()))
648 subscription.callback(topic, payload);
666 char buf[ESPHOME_DEVICE_NAME_MAX_LEN + 1];
689 if (this->
birth_message_.
topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
712 bool discover_ip,
bool clean) {
727 .discover_ip =
false,
747 auto callback_copy = callback;
756void MQTTMessageTrigger::set_qos(uint8_t qos) { this->qos_ = qos; }
757void MQTTMessageTrigger::set_payload(
const std::string &payload) { this->payload_ = payload; }
758void MQTTMessageTrigger::setup() {
761 [
this](
const std::string &topic,
const std::string &payload) {
762 if (this->payload_.has_value() && payload != *this->payload_) {
766 this->trigger(payload);
770void MQTTMessageTrigger::dump_config() {
772 "MQTT Message Trigger:\n"
775 this->topic_.c_str(), this->qos_);
777float MQTTMessageTrigger::get_setup_priority()
const {
return setup_priority::AFTER_CONNECTION; }
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
bool is_name_add_mac_suffix_enabled() const
const std::string & get_name() const
Get the name of this Application set by pre_setup().
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
void status_set_warning(const char *message=nullptr)
void status_momentary_warning(const char *name, uint32_t length=5000)
Set warning status flag and automatically clear it after a timeout.
ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", "2026.2.0") void set_retry(const std uint32_t uint8_t std::function< RetryResult(uint8_t)> && f
void status_clear_warning()
APINoiseContext & get_noise_ctx()
uint16_t get_port() const
void add_log_callback(void *instance, void(*fn)(void *, uint8_t, const char *, const char *, size_t))
Register a log callback to receive log messages.
void set_keep_alive(uint16_t keep_alive) final
void set_on_message(std::function< on_message_callback_t > &&callback) final
void set_client_id(const char *client_id) final
void set_on_connect(std::function< on_connect_callback_t > &&callback) final
void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final
bool subscribe(const char *topic, uint8_t qos) final
void set_server(network::IPAddress ip, uint16_t port) final
bool publish(const char *topic, const char *payload, size_t length, uint8_t qos, bool retain) final
void set_clean_session(bool clean_session) final
bool unsubscribe(const char *topic) final
bool connected() const final
void set_on_disconnect(std::function< on_disconnect_callback_t > &&callback) final
void set_credentials(const char *username, const char *password) final
void set_birth_message(MQTTMessage &&message)
Set the birth message.
MQTTMessage shutdown_message_
bool is_log_message_enabled() const
void start_connect_()
Reconnect to the MQTT broker if not already connected.
void setup() override
Setup the MQTT client, registering a bunch of callbacks and attempting to connect.
void disable_discovery()
Globally disable Home Assistant discovery.
void recalculate_availability_()
Re-calculate the availability property.
void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator, MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, bool discover_ip, bool clean=false)
Set the Home Assistant discovery info.
void set_reboot_timeout(uint32_t reboot_timeout)
bool can_proceed() override
float get_setup_priority() const override
MQTT client setup priority.
void disable_log_message()
Get the topic used for logging. Defaults to "<topic_prefix>/debug" and the value is cached for speed.
const std::string & get_topic_prefix() const
Get the topic prefix of this device, using default if necessary.
void subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos=0)
Subscribe to a MQTT topic and automatically parse JSON payload.
void register_mqtt_component(MQTTComponent *component)
bool is_discovery_ip_enabled() const
void set_last_will(MQTTMessage &&message)
Set the last will testament message.
bool publish(const MQTTMessage &message)
Publish a MQTTMessage.
const MQTTDiscoveryInfo & get_discovery_info() const
Get Home Assistant discovery info.
void disable_birth_message()
Remove the birth message.
static void dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg)
void subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos=0)
Subscribe to an MQTT topic and call callback when a message is received.
void on_shutdown() override
MQTTMessage last_will_
The last will message.
void set_topic_prefix(const std::string &topic_prefix, const std::string &check_topic_prefix)
Set the topic prefix that will be prepended to all topics together with "/".
void set_shutdown_message(MQTTMessage &&message)
MQTTMessage birth_message_
The birth message (e.g.
MQTTCredentials credentials_
std::vector< MQTTComponent * > children_
void dump_config() override
void set_on_connect(mqtt_on_connect_callback_t &&callback)
optional< MQTTClientDisconnectReason > disconnect_reason_
bool is_publish_nan_as_none() const
void resubscribe_subscriptions_()
void disable_shutdown_message()
void unsubscribe(const std::string &topic)
Unsubscribe from an MQTT topic.
void set_publish_nan_as_none(bool publish_nan_as_none)
void on_message(const std::string &topic, const std::string &payload)
void set_log_message_template(MQTTMessage &&message)
Manually set the topic used for logging.
void set_log_level(int level)
void resubscribe_subscription_(MQTTSubscription *sub)
bool wait_for_connection_
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback)
bool subscribe_(const char *topic, uint8_t qos)
bool publish_nan_as_none_
const Availability & get_availability()
std::string topic_prefix_
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len)
std::vector< MQTTSubscription > subscriptions_
bool publish_json(const std::string &topic, const json::json_build_t &f, uint8_t qos=0, bool retain=false)
Construct and send a JSON MQTT message.
bool is_discovery_enabled() const
MQTTBackendESP32 mqtt_backend_
void disable_last_will()
Remove the last will testament message.
void set_keep_alive(uint16_t keep_alive_s)
Set the keep alive time in seconds, every 0.7*keep_alive a ping will be sent.
void loop() override
Reconnect if required.
std::string payload_buffer_
MQTTDiscoveryInfo discovery_info_
The discovery info options for Home Assistant.
CallbackManager< MQTTBackend::on_disconnect_callback_t > on_disconnect_
Availability availability_
Caches availability.
MQTTMessageTrigger(std::string topic)
const Component * component
PROGMEM_STRING_TABLE(AlarmControlPanelStateStrings, "DISARMED", "ARMED_HOME", "ARMED_AWAY", "ARMED_NIGHT", "ARMED_VACATION", "ARMED_CUSTOM_BYPASS", "PENDING", "ARMING", "DISARMING", "TRIGGERED", "UNKNOWN")
APIServer * global_api_server
const char * get_package_import_url()
std::function< void(JsonObject)> json_build_t
Callback function typedef for building JsonObjects.
bool parse_json(const std::string &data, const json_parse_t &f)
Parse a JSON string and run the provided json parse function if it's valid.
SerializationBuffer build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
std::function< MQTTBackend::on_disconnect_callback_t > mqtt_on_disconnect_callback_t
MQTTDiscoveryObjectIdGenerator
available discovery object_id generators
@ MQTT_NONE_OBJECT_ID_GENERATOR
MQTTDiscoveryUniqueIdGenerator
available discovery unique_id generators
@ MQTT_LEGACY_UNIQUE_ID_GENERATOR
std::function< void(const std::string &, JsonObject)> mqtt_json_callback_t
std::function< void(const std::string &, const std::string &)> mqtt_callback_t
Callback for MQTT subscriptions.
MQTTClientDisconnectReason
MQTTClientComponent * global_mqtt_client
@ MQTT_CLIENT_DISCONNECTED
@ MQTT_CLIENT_RESOLVING_ADDRESS
std::function< MQTTBackend::on_connect_callback_t > mqtt_on_connect_callback_t
Callback for MQTT events.
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
network::IPAddresses get_ip_addresses()
bool is_disabled()
Return whether the network is disabled (only wifi for now)
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
char * str_sanitize_to(char *buffer, size_t buffer_size, const char *str)
Sanitize a string to buffer, keeping only alphanumerics, dashes, and underscores.
void get_mac_address_into_buffer(std::span< char, MAC_ADDRESS_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in lowercase hex notation.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len)
Optimized string concatenation: name + separator + suffix (const char* overload) Uses a fixed stack b...
std::string payload_not_available
std::string topic
Empty means disabled.
std::string payload_available
std::string address
The address of the server without port number.
bool clean_session
Whether the session will be cleaned or remembered between connects.
std::string client_id
The client ID. Will automatically be truncated to 23 characters.
MQTTDiscoveryUniqueIdGenerator unique_id_generator
bool discover_ip
Enable the Home Assistant device discovery.
std::string prefix
The Home Assistant discovery prefix. Empty means disabled.
MQTTDiscoveryObjectIdGenerator object_id_generator
bool retain
Whether to retain discovery messages.