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) {
56 if (
len + index == total) {
70 this, [](
void *self, uint8_t level,
const char *
tag,
const char *
message,
size_t message_len) {
78 "esphome/discover", [
this](
const std::string &topic,
const std::string &payload) { this->
send_device_info_(); },
83 constexpr size_t ping_topic_buffer_size = 13 + ESPHOME_DEVICE_NAME_MAX_LEN + 1;
84 char ping_topic[ping_topic_buffer_size];
85 buf_append_printf(ping_topic,
sizeof(ping_topic), 0,
"esphome/ping/%s",
App.
get_name().
c_str());
87 ping_topic, [
this](
const std::string &topic,
const std::string &payload) { this->
send_device_info_(); }, 2);
101 constexpr size_t topic_buffer_size = 17 + ESPHOME_DEVICE_NAME_MAX_LEN + 1;
102 char topic[topic_buffer_size];
103 buf_append_printf(topic,
sizeof(topic), 0,
"esphome/discover/%s",
App.
get_name().
c_str());
108 [](JsonObject root) {
113 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
119 buf_append_printf(key,
sizeof(key), 0,
"ip%u", index);
133 root[ESPHOME_F(
"version")] = ESPHOME_VERSION;
134 char mac_buf[MAC_ADDRESS_BUFFER_SIZE];
136 root[ESPHOME_F(
"mac")] = mac_buf;
139 root[ESPHOME_F(
"platform")] = ESPHOME_F(
"ESP8266");
142 root[ESPHOME_F(
"platform")] = ESPHOME_F(
"ESP32");
145 root[ESPHOME_F(
"platform")] = lt_cpu_get_model_name();
148 root[ESPHOME_F(
"board")] = ESPHOME_BOARD;
150 root[ESPHOME_F(
"network")] = ESPHOME_F(
"wifi");
151#elif defined(USE_ETHERNET)
152 root[ESPHOME_F(
"network")] = ESPHOME_F(
"ethernet");
155#ifdef ESPHOME_PROJECT_NAME
156 root[ESPHOME_F(
"project_name")] = ESPHOME_PROJECT_NAME;
157 root[ESPHOME_F(
"project_version")] = ESPHOME_PROJECT_VERSION;
160#ifdef USE_DASHBOARD_IMPORT
166 : ESPHOME_F(
"api_encryption_supported")] =
167 ESPHOME_F(
"Noise_NNpsk0_25519_ChaChaPoly_SHA256");
179 this->log_message_.retain);
185 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
189 " Server Address: %s:%u (%s)\n"
190 " Username: " LOG_SECRET(
"'%s'")
"\n"
191 " Client ID: " LOG_SECRET(
"'%s'")
"\n"
192 " Clean Session: %s",
194 this->credentials_.username.c_str(), this->credentials_.client_id.c_str(),
198 ESP_LOGCONFIG(TAG,
" Discovery IP enabled");
202 " Discovery prefix: '%s'\n"
203 " Discovery retain: %s",
206 ESP_LOGCONFIG(TAG,
" Topic Prefix: '%s'", this->
topic_prefix_.c_str());
221 subscription.subscribed =
false;
222 subscription.resubscribe_timeout = 0;
234 this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
237 this, LWIP_DNS_ADDRTYPE_IPV4);
244 this->
ip_ = network::IPAddress(&addr);
248 case ERR_INPROGRESS: {
250 ESP_LOGD(TAG,
"Resolving broker IP address");
256 ESP_LOGW(TAG,
"Error resolving broker IP address: %d", err);
281 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
282 ESP_LOGD(TAG,
"Resolved broker IP address to %s", this->
ip_.str_to(ip_buf));
285#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1
291 if (ipaddr ==
nullptr) {
292 a_this->dns_resolve_error_ =
true;
294 a_this->ip_ = network::IPAddress(ipaddr);
295 a_this->dns_resolved_ =
true;
303 ESP_LOGI(TAG,
"Connecting");
309 const char *username =
nullptr;
312 const char *password =
nullptr;
321 this->last_will_.payload.c_str());
344 ESP_LOGI(TAG,
"Connected");
360 const LogString *reason_s = MQTTDisconnectReasonStrings::get_log_str(
361 static_cast<uint8_t
>(*this->
disconnect_reason_), MQTTDisconnectReasonStrings::LAST_INDEX);
363 reason_s = LOG_STR(
"WiFi disconnected");
365 ESP_LOGW(TAG,
"Disconnected: %s", LOG_STR_ARG(reason_s));
388 ESP_LOGW(TAG,
"Lost client connection");
400 for (MQTTComponent *
component : this->children_) {
408 ESP_LOGE(TAG,
"Can't connect; restarting");
423 ESP_LOGV(TAG,
"subscribe(topic='%s')", topic);
426 ESP_LOGV(TAG,
"Subscribe failed for topic='%s'. Will retry", topic);
436 bool do_resub = sub->resubscribe_timeout == 0 || now - sub->resubscribe_timeout > 1000;
439 sub->subscribed = this->
subscribe_(sub->topic.c_str(), sub->qos);
440 sub->resubscribe_timeout = now;
444 for (
auto &subscription : this->subscriptions_) {
450 MQTTSubscription subscription{
453 .callback = std::move(callback),
455 .resubscribe_timeout = 0,
458 this->subscriptions_.push_back(subscription);
462 auto f = [callback](
const std::string &topic,
const std::string &payload) {
464 callback(topic, root);
468 MQTTSubscription subscription{
473 .resubscribe_timeout = 0,
476 this->subscriptions_.push_back(subscription);
483 ESP_LOGV(TAG,
"unsubscribe(topic='%s')", topic.c_str());
486 ESP_LOGV(TAG,
"Unsubscribe failed for topic='%s'.", topic.c_str());
492 if (it->topic == topic) {
502 return this->
publish(topic, payload.data(), payload.size(), qos, retain);
507 return this->
publish(topic.c_str(), payload, payload_length, qos, retain);
524 size_t topic_len = strlen(topic);
535 if (!logging_topic) {
537 ESP_LOGV(TAG,
"Publish(topic='%s' retain=%d qos=%d)", topic, retain, qos);
538 ESP_LOGVV(TAG,
"Publish payload (len=%u): '%.*s'", payload_length,
static_cast<int>(payload_length), payload);
540 ESP_LOGV(TAG,
"Publish failed for topic='%s' (len=%u). Will retry", topic, payload_length);
555 ESP_LOGD(TAG,
"Enabling");
564 ESP_LOGD(TAG,
"Disabling");
580static bool topic_match(
const char *
message,
const char *subscription,
bool is_normal,
bool past_separator) {
582 if (*
message ==
'\0' && *subscription ==
'\0')
586 if (*
message ==
'\0' || *subscription ==
'\0')
589 bool do_wildcards = is_normal || past_separator;
591 if (*subscription ==
'+' && do_wildcards) {
601 return topic_match(
message, subscription, is_normal,
true);
604 if (*subscription ==
'#' && do_wildcards) {
613 past_separator = past_separator || *subscription ==
'/';
619 return topic_match(
message, subscription, is_normal, past_separator);
622static bool topic_match(
const char *
message,
const char *subscription) {
646 this->
defer([
this, topic, payload]() {
648 for (
auto &subscription : this->subscriptions_) {
649 if (topic_match(topic.c_str(), subscription.topic.c_str()))
650 subscription.callback(topic, payload);
668 char buf[ESPHOME_DEVICE_NAME_MAX_LEN + 1];
691 if (this->
birth_message_.
topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
714 bool discover_ip,
bool clean) {
729 .discover_ip =
false,
749 auto callback_copy = callback;
758void MQTTMessageTrigger::set_qos(uint8_t qos) { this->qos_ = qos; }
759void MQTTMessageTrigger::set_payload(
const std::string &payload) { this->payload_ = payload; }
760void MQTTMessageTrigger::setup() {
763 [
this](
const std::string &topic,
const std::string &payload) {
764 if (this->payload_.has_value() && payload != *this->payload_) {
768 this->trigger(payload);
772void MQTTMessageTrigger::dump_config() {
774 "MQTT Message Trigger:\n"
777 this->topic_.c_str(), this->qos_);
779float MQTTMessageTrigger::get_setup_priority()
const {
return setup_priority::AFTER_CONNECTION; }
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
const StringRef & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
bool is_name_add_mac_suffix_enabled() const
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()
constexpr const char * c_str() const
constexpr bool empty() const
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.
ESPHOME_ALWAYS_INLINE 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.