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) {
73 "esphome/discover", [
this](
const std::string &topic,
const std::string &payload) { this->
send_device_info_(); },
78 constexpr size_t ping_topic_buffer_size = 13 + ESPHOME_DEVICE_NAME_MAX_LEN + 1;
79 char ping_topic[ping_topic_buffer_size];
80 buf_append_printf(ping_topic,
sizeof(ping_topic), 0,
"esphome/ping/%s",
App.
get_name().c_str());
82 ping_topic, [
this](
const std::string &topic,
const std::string &payload) { this->
send_device_info_(); }, 2);
96 constexpr size_t topic_buffer_size = 17 + ESPHOME_DEVICE_NAME_MAX_LEN + 1;
97 char topic[topic_buffer_size];
98 buf_append_printf(topic,
sizeof(topic), 0,
"esphome/discover/%s",
App.
get_name().c_str());
103 [](JsonObject root) {
108 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
114 buf_append_printf(key,
sizeof(key), 0,
"ip%u", index);
128 root[ESPHOME_F(
"version")] = ESPHOME_VERSION;
129 char mac_buf[MAC_ADDRESS_BUFFER_SIZE];
131 root[ESPHOME_F(
"mac")] = mac_buf;
134 root[ESPHOME_F(
"platform")] = ESPHOME_F(
"ESP8266");
137 root[ESPHOME_F(
"platform")] = ESPHOME_F(
"ESP32");
140 root[ESPHOME_F(
"platform")] = lt_cpu_get_model_name();
143 root[ESPHOME_F(
"board")] = ESPHOME_BOARD;
145 root[ESPHOME_F(
"network")] = ESPHOME_F(
"wifi");
146#elif defined(USE_ETHERNET)
147 root[ESPHOME_F(
"network")] = ESPHOME_F(
"ethernet");
150#ifdef ESPHOME_PROJECT_NAME
151 root[ESPHOME_F(
"project_name")] = ESPHOME_PROJECT_NAME;
152 root[ESPHOME_F(
"project_version")] = ESPHOME_PROJECT_VERSION;
155#ifdef USE_DASHBOARD_IMPORT
161 : ESPHOME_F(
"api_encryption_supported")] =
162 ESPHOME_F(
"Noise_NNpsk0_25519_ChaChaPoly_SHA256");
174 this->log_message_.retain);
180 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
184 " Server Address: %s:%u (%s)\n"
185 " Username: " LOG_SECRET(
"'%s'")
"\n"
186 " Client ID: " LOG_SECRET(
"'%s'")
"\n"
187 " Clean Session: %s",
189 this->credentials_.username.c_str(), this->credentials_.client_id.c_str(),
193 ESP_LOGCONFIG(TAG,
" Discovery IP enabled");
197 " Discovery prefix: '%s'\n"
198 " Discovery retain: %s",
201 ESP_LOGCONFIG(TAG,
" Topic Prefix: '%s'", this->
topic_prefix_.c_str());
216 subscription.subscribed =
false;
217 subscription.resubscribe_timeout = 0;
229 this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
232 this, LWIP_DNS_ADDRTYPE_IPV4);
239 this->
ip_ = network::IPAddress(&addr);
243 case ERR_INPROGRESS: {
245 ESP_LOGD(TAG,
"Resolving broker IP address");
251 ESP_LOGW(TAG,
"Error resolving broker IP address: %d", err);
276 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
277 ESP_LOGD(TAG,
"Resolved broker IP address to %s", this->
ip_.str_to(ip_buf));
280#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1
286 if (ipaddr ==
nullptr) {
287 a_this->dns_resolve_error_ =
true;
289 a_this->ip_ = network::IPAddress(ipaddr);
290 a_this->dns_resolved_ =
true;
298 ESP_LOGI(TAG,
"Connecting");
304 const char *username =
nullptr;
307 const char *password =
nullptr;
316 this->last_will_.payload.c_str());
339 ESP_LOGI(TAG,
"Connected");
355 const LogString *reason_s = MQTTDisconnectReasonStrings::get_log_str(
356 static_cast<uint8_t
>(*this->
disconnect_reason_), MQTTDisconnectReasonStrings::LAST_INDEX);
358 reason_s = LOG_STR(
"WiFi disconnected");
360 ESP_LOGW(TAG,
"Disconnected: %s", LOG_STR_ARG(reason_s));
383 ESP_LOGW(TAG,
"Lost client connection");
395 for (MQTTComponent *
component : this->children_) {
403 ESP_LOGE(TAG,
"Can't connect; restarting");
418 ESP_LOGV(TAG,
"subscribe(topic='%s')", topic);
421 ESP_LOGV(TAG,
"Subscribe failed for topic='%s'. Will retry", topic);
431 bool do_resub = sub->resubscribe_timeout == 0 || now - sub->resubscribe_timeout > 1000;
434 sub->subscribed = this->
subscribe_(sub->topic.c_str(), sub->qos);
435 sub->resubscribe_timeout = now;
439 for (
auto &subscription : this->subscriptions_) {
445 MQTTSubscription subscription{
448 .callback = std::move(callback),
450 .resubscribe_timeout = 0,
453 this->subscriptions_.push_back(subscription);
457 auto f = [callback](
const std::string &topic,
const std::string &payload) {
459 callback(topic, root);
463 MQTTSubscription subscription{
468 .resubscribe_timeout = 0,
471 this->subscriptions_.push_back(subscription);
478 ESP_LOGV(TAG,
"unsubscribe(topic='%s')", topic.c_str());
481 ESP_LOGV(TAG,
"Unsubscribe failed for topic='%s'.", topic.c_str());
487 if (it->topic == topic) {
497 return this->
publish(topic, payload.data(), payload.size(), qos, retain);
502 return this->
publish(topic.c_str(), payload, payload_length, qos, retain);
519 size_t topic_len = strlen(topic);
530 if (!logging_topic) {
532 ESP_LOGV(TAG,
"Publish(topic='%s' retain=%d qos=%d)", topic, retain, qos);
533 ESP_LOGVV(TAG,
"Publish payload (len=%u): '%.*s'", payload_length,
static_cast<int>(payload_length), payload);
535 ESP_LOGV(TAG,
"Publish failed for topic='%s' (len=%u). Will retry", topic, payload_length);
550 ESP_LOGD(TAG,
"Enabling");
559 ESP_LOGD(TAG,
"Disabling");
575static bool topic_match(
const char *
message,
const char *subscription,
bool is_normal,
bool past_separator) {
577 if (*
message ==
'\0' && *subscription ==
'\0')
581 if (*
message ==
'\0' || *subscription ==
'\0')
584 bool do_wildcards = is_normal || past_separator;
586 if (*subscription ==
'+' && do_wildcards) {
596 return topic_match(
message, subscription, is_normal,
true);
599 if (*subscription ==
'#' && do_wildcards) {
608 past_separator = past_separator || *subscription ==
'/';
614 return topic_match(
message, subscription, is_normal, past_separator);
617static bool topic_match(
const char *
message,
const char *subscription) {
641 this->
defer([
this, topic, payload]() {
643 for (
auto &subscription : this->subscriptions_) {
644 if (topic_match(topic.c_str(), subscription.topic.c_str()))
645 subscription.callback(topic, payload);
663 char buf[ESPHOME_DEVICE_NAME_MAX_LEN + 1];
686 if (this->
birth_message_.
topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
709 bool discover_ip,
bool clean) {
724 .discover_ip =
false,
744 auto callback_copy = callback;
753void MQTTMessageTrigger::set_qos(uint8_t qos) { this->qos_ = qos; }
754void MQTTMessageTrigger::set_payload(
const std::string &payload) { this->payload_ = payload; }
755void MQTTMessageTrigger::setup() {
758 [
this](
const std::string &topic,
const std::string &payload) {
759 if (this->payload_.has_value() && payload != *this->payload_) {
763 this->trigger(payload);
767void MQTTMessageTrigger::dump_config() {
769 "MQTT Message Trigger:\n"
772 this->topic_.c_str(), this->qos_);
774float 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_listener(LogListener *listener)
Register a log listener 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 on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override
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_
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.
std::string 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)
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
void IRAM_ATTR HOT yield()
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 IRAM_ATTR 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.