ESPHome 2025.5.0
Loading...
Searching...
No Matches
mqtt_client.cpp
Go to the documentation of this file.
1#include "mqtt_client.h"
2
3#ifdef USE_MQTT
4
5#include <utility>
9#include "esphome/core/log.h"
11#ifdef USE_LOGGER
13#endif
14#include "lwip/dns.h"
15#include "lwip/err.h"
16#include "mqtt_component.h"
17
18#ifdef USE_API
20#endif
21#ifdef USE_DASHBOARD_IMPORT
23#endif
24
25namespace esphome {
26namespace mqtt {
27
28static const char *const TAG = "mqtt";
29
31 global_mqtt_client = this;
33}
34
35// Connection
37 ESP_LOGCONFIG(TAG, "Setting up MQTT...");
39 [this](const char *topic, const char *payload, size_t len, size_t index, size_t total) {
40 if (index == 0)
41 this->payload_buffer_.reserve(total);
42
43 // append new payload, may contain incomplete MQTT message
44 this->payload_buffer_.append(payload, len);
45
46 // MQTT fully received
47 if (len + index == total) {
48 this->on_message(topic, this->payload_buffer_);
49 this->payload_buffer_.clear();
50 }
51 });
53 if (this->state_ == MQTT_CLIENT_DISABLED)
54 return;
56 this->disconnect_reason_ = reason;
57 });
58#ifdef USE_LOGGER
59 if (this->is_log_message_enabled() && logger::global_logger != nullptr) {
60 logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
61 if (level <= this->log_level_ && this->is_connected()) {
62 this->publish({.topic = this->log_message_.topic,
63 .payload = message,
64 .qos = this->log_message_.qos,
65 .retain = this->log_message_.retain});
66 }
67 });
68 }
69#endif
70
71 if (this->is_discovery_ip_enabled()) {
72 this->subscribe(
73 "esphome/discover", [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); },
74 2);
75
76 std::string topic = "esphome/ping/";
77 topic.append(App.get_name());
78 this->subscribe(
79 topic, [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); }, 2);
80 }
81
82 if (this->enable_on_boot_) {
83 this->enable();
84 }
85}
86
88 if (!this->is_connected() or !this->is_discovery_ip_enabled()) {
89 return;
90 }
91 std::string topic = "esphome/discover/";
92 topic.append(App.get_name());
93
94 this->publish_json(
95 topic,
96 [](JsonObject root) {
97 uint8_t index = 0;
98 for (auto &ip : network::get_ip_addresses()) {
99 if (ip.is_set()) {
100 root["ip" + (index == 0 ? "" : esphome::to_string(index))] = ip.str();
101 index++;
102 }
103 }
104 root["name"] = App.get_name();
105 if (!App.get_friendly_name().empty()) {
106 root["friendly_name"] = App.get_friendly_name();
107 }
108#ifdef USE_API
109 root["port"] = api::global_api_server->get_port();
110#endif
111 root["version"] = ESPHOME_VERSION;
112 root["mac"] = get_mac_address();
113
114#ifdef USE_ESP8266
115 root["platform"] = "ESP8266";
116#endif
117#ifdef USE_ESP32
118 root["platform"] = "ESP32";
119#endif
120#ifdef USE_LIBRETINY
121 root["platform"] = lt_cpu_get_model_name();
122#endif
123
124 root["board"] = ESPHOME_BOARD;
125#if defined(USE_WIFI)
126 root["network"] = "wifi";
127#elif defined(USE_ETHERNET)
128 root["network"] = "ethernet";
129#endif
130
131#ifdef ESPHOME_PROJECT_NAME
132 root["project_name"] = ESPHOME_PROJECT_NAME;
133 root["project_version"] = ESPHOME_PROJECT_VERSION;
134#endif // ESPHOME_PROJECT_NAME
135
136#ifdef USE_DASHBOARD_IMPORT
137 root["package_import_url"] = dashboard_import::get_package_import_url();
138#endif
139
140#ifdef USE_API_NOISE
141 if (api::global_api_server->get_noise_ctx()->has_psk()) {
142 root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
143 } else {
144 root["api_encryption_supported"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
145 }
146#endif
147 },
148 2, this->discovery_info_.retain);
149}
150
152 ESP_LOGCONFIG(TAG, "MQTT:");
153 ESP_LOGCONFIG(TAG, " Server Address: %s:%u (%s)", this->credentials_.address.c_str(), this->credentials_.port,
154 this->ip_.str().c_str());
155 ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str());
156 ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str());
157 ESP_LOGCONFIG(TAG, " Clean Session: %s", YESNO(this->credentials_.clean_session));
158 if (this->is_discovery_ip_enabled()) {
159 ESP_LOGCONFIG(TAG, " Discovery IP enabled");
160 }
161 if (!this->discovery_info_.prefix.empty()) {
162 ESP_LOGCONFIG(TAG, " Discovery prefix: '%s'", this->discovery_info_.prefix.c_str());
163 ESP_LOGCONFIG(TAG, " Discovery retain: %s", YESNO(this->discovery_info_.retain));
164 }
165 ESP_LOGCONFIG(TAG, " Topic Prefix: '%s'", this->topic_prefix_.c_str());
166 if (!this->log_message_.topic.empty()) {
167 ESP_LOGCONFIG(TAG, " Log Topic: '%s'", this->log_message_.topic.c_str());
168 }
169 if (!this->availability_.topic.empty()) {
170 ESP_LOGCONFIG(TAG, " Availability: '%s'", this->availability_.topic.c_str());
171 }
172}
174 return network::is_disabled() || this->state_ == MQTT_CLIENT_DISABLED || this->is_connected();
175}
176
178 for (auto &subscription : this->subscriptions_) {
179 subscription.subscribed = false;
180 subscription.resubscribe_timeout = 0;
181 }
182
183 this->status_set_warning();
184 this->dns_resolve_error_ = false;
185 this->dns_resolved_ = false;
186 ip_addr_t addr;
187#if USE_NETWORK_IPV6
188 err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
189 MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
190#else
191 err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
192 MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4);
193#endif /* USE_NETWORK_IPV6 */
194 switch (err) {
195 case ERR_OK: {
196 // Got IP immediately
197 this->dns_resolved_ = true;
198 this->ip_ = network::IPAddress(&addr);
199 this->start_connect_();
200 return;
201 }
202 case ERR_INPROGRESS: {
203 // wait for callback
204 ESP_LOGD(TAG, "Resolving MQTT broker IP address...");
205 break;
206 }
207 default:
208 case ERR_ARG: {
209 // error
210 ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err);
211 break;
212 }
213 }
214
216 this->connect_begin_ = millis();
217}
219 if (!this->dns_resolved_ && millis() - this->connect_begin_ > 20000) {
220 this->dns_resolve_error_ = true;
221 }
222
223 if (this->dns_resolve_error_) {
224 ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'!", this->credentials_.address.c_str());
226 return;
227 }
228
229 if (!this->dns_resolved_) {
230 return;
231 }
232
233 ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.str().c_str());
234 this->start_connect_();
235}
236#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1
237void MQTTClientComponent::dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg) {
238#else
239void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
240#endif
241 auto *a_this = (MQTTClientComponent *) callback_arg;
242 if (ipaddr == nullptr) {
243 a_this->dns_resolve_error_ = true;
244 } else {
245 a_this->ip_ = network::IPAddress(ipaddr);
246 a_this->dns_resolved_ = true;
247 }
248}
249
252 return;
253
254 ESP_LOGI(TAG, "Connecting to MQTT...");
255 // Force disconnect first
257
260 const char *username = nullptr;
261 if (!this->credentials_.username.empty())
262 username = this->credentials_.username.c_str();
263 const char *password = nullptr;
264 if (!this->credentials_.password.empty())
265 password = this->credentials_.password.c_str();
266
267 this->mqtt_backend_.set_credentials(username, password);
268
269 this->mqtt_backend_.set_server(this->credentials_.address.c_str(), this->credentials_.port);
270 if (!this->last_will_.topic.empty()) {
271 this->mqtt_backend_.set_will(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain,
272 this->last_will_.payload.c_str());
273 }
274
275 this->mqtt_backend_.connect();
277 this->connect_begin_ = millis();
278}
280 return this->state_ == MQTT_CLIENT_CONNECTED && this->mqtt_backend_.connected();
281}
282
284 if (!this->mqtt_backend_.connected()) {
285 if (millis() - this->connect_begin_ > 60000) {
287 this->start_dnslookup_();
288 }
289 return;
290 }
291
293 this->sent_birth_message_ = false;
294 this->status_clear_warning();
295 ESP_LOGI(TAG, "MQTT Connected!");
296 // MQTT Client needs some time to be fully set up.
297 delay(100); // NOLINT
298
300 this->send_device_info_();
301
302 for (MQTTComponent *component : this->children_)
303 component->schedule_resend_state();
304}
305
307 // Call the backend loop first
309
310 if (this->disconnect_reason_.has_value()) {
311 const LogString *reason_s;
312 switch (*this->disconnect_reason_) {
314 reason_s = LOG_STR("TCP disconnected");
315 break;
317 reason_s = LOG_STR("Unacceptable Protocol Version");
318 break;
320 reason_s = LOG_STR("Identifier Rejected");
321 break;
323 reason_s = LOG_STR("Server Unavailable");
324 break;
326 reason_s = LOG_STR("Malformed Credentials");
327 break;
329 reason_s = LOG_STR("Not Authorized");
330 break;
332 reason_s = LOG_STR("Not Enough Space");
333 break;
335 reason_s = LOG_STR("TLS Bad Fingerprint");
336 break;
337 default:
338 reason_s = LOG_STR("Unknown");
339 break;
340 }
341 if (!network::is_connected()) {
342 reason_s = LOG_STR("WiFi disconnected");
343 }
344 ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s));
346 }
347
348 const uint32_t now = App.get_loop_component_start_time();
349
350 switch (this->state_) {
352 return; // Return to avoid a reboot when disabled
354 if (now - this->connect_begin_ > 5000) {
355 this->start_dnslookup_();
356 }
357 break;
359 this->check_dnslookup_();
360 break;
362 this->check_connected();
363 break;
365 if (!this->mqtt_backend_.connected()) {
367 ESP_LOGW(TAG, "Lost MQTT Client connection!");
368 this->start_dnslookup_();
369 } else {
370 if (!this->birth_message_.topic.empty() && !this->sent_birth_message_) {
371 this->sent_birth_message_ = this->publish(this->birth_message_);
372 }
373
374 this->last_connected_ = now;
376 }
377 break;
378 }
379
380 if (millis() - this->last_connected_ > this->reboot_timeout_ && this->reboot_timeout_ != 0) {
381 ESP_LOGE(TAG, "Can't connect to MQTT... Restarting...");
382 App.reboot();
383 }
384}
386
387// Subscribe
388bool MQTTClientComponent::subscribe_(const char *topic, uint8_t qos) {
389 if (!this->is_connected())
390 return false;
391
392 bool ret = this->mqtt_backend_.subscribe(topic, qos);
393 yield();
394
395 if (ret) {
396 ESP_LOGV(TAG, "subscribe(topic='%s')", topic);
397 } else {
398 delay(5);
399 ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry later.", topic);
400 this->status_momentary_warning("subscribe", 1000);
401 }
402 return ret != 0;
403}
404void MQTTClientComponent::resubscribe_subscription_(MQTTSubscription *sub) {
405 if (sub->subscribed)
406 return;
407
408 const uint32_t now = millis();
409 bool do_resub = sub->resubscribe_timeout == 0 || now - sub->resubscribe_timeout > 1000;
410
411 if (do_resub) {
412 sub->subscribed = this->subscribe_(sub->topic.c_str(), sub->qos);
413 sub->resubscribe_timeout = now;
414 }
415}
417 for (auto &subscription : this->subscriptions_) {
418 this->resubscribe_subscription_(&subscription);
419 }
420}
421
422void MQTTClientComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) {
423 MQTTSubscription subscription{
424 .topic = topic,
425 .qos = qos,
426 .callback = std::move(callback),
427 .subscribed = false,
428 .resubscribe_timeout = 0,
429 };
430 this->resubscribe_subscription_(&subscription);
431 this->subscriptions_.push_back(subscription);
432}
433
434void MQTTClientComponent::subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos) {
435 auto f = [callback](const std::string &topic, const std::string &payload) {
436 json::parse_json(payload, [topic, callback](JsonObject root) -> bool {
437 callback(topic, root);
438 return true;
439 });
440 };
441 MQTTSubscription subscription{
442 .topic = topic,
443 .qos = qos,
444 .callback = f,
445 .subscribed = false,
446 .resubscribe_timeout = 0,
447 };
448 this->resubscribe_subscription_(&subscription);
449 this->subscriptions_.push_back(subscription);
450}
451
452void MQTTClientComponent::unsubscribe(const std::string &topic) {
453 bool ret = this->mqtt_backend_.unsubscribe(topic.c_str());
454 yield();
455 if (ret) {
456 ESP_LOGV(TAG, "unsubscribe(topic='%s')", topic.c_str());
457 } else {
458 delay(5);
459 ESP_LOGV(TAG, "Unsubscribe failed for topic='%s'.", topic.c_str());
460 this->status_momentary_warning("unsubscribe", 1000);
461 }
462
463 auto it = subscriptions_.begin();
464 while (it != subscriptions_.end()) {
465 if (it->topic == topic) {
466 it = subscriptions_.erase(it);
467 } else {
468 ++it;
469 }
470 }
471}
472
473// Publish
474bool MQTTClientComponent::publish(const std::string &topic, const std::string &payload, uint8_t qos, bool retain) {
475 return this->publish(topic, payload.data(), payload.size(), qos, retain);
476}
477
478bool MQTTClientComponent::publish(const std::string &topic, const char *payload, size_t payload_length, uint8_t qos,
479 bool retain) {
480 return publish({.topic = topic, .payload = payload, .qos = qos, .retain = retain});
481}
482
483bool MQTTClientComponent::publish(const MQTTMessage &message) {
484 if (!this->is_connected()) {
485 // critical components will re-transmit their messages
486 return false;
487 }
488 bool logging_topic = this->log_message_.topic == message.topic;
489 bool ret = this->mqtt_backend_.publish(message);
490 delay(0);
491 if (!ret && !logging_topic && this->is_connected()) {
492 delay(0);
493 ret = this->mqtt_backend_.publish(message);
494 delay(0);
495 }
496
497 if (!logging_topic) {
498 if (ret) {
499 ESP_LOGV(TAG, "Publish(topic='%s' payload='%s' retain=%d qos=%d)", message.topic.c_str(), message.payload.c_str(),
500 message.retain, message.qos);
501 } else {
502 ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). will retry later..", message.topic.c_str(),
503 message.payload.length());
504 this->status_momentary_warning("publish", 1000);
505 }
506 }
507 return ret != 0;
508}
509bool MQTTClientComponent::publish_json(const std::string &topic, const json::json_build_t &f, uint8_t qos,
510 bool retain) {
511 std::string message = json::build_json(f);
512 return this->publish(topic, message, qos, retain);
513}
514
516 if (this->state_ != MQTT_CLIENT_DISABLED)
517 return;
518 ESP_LOGD(TAG, "Enabling MQTT...");
520 this->last_connected_ = millis();
521 this->start_dnslookup_();
522}
523
525 if (this->state_ == MQTT_CLIENT_DISABLED)
526 return;
527 ESP_LOGD(TAG, "Disabling MQTT...");
529 this->on_shutdown();
530}
531
543static bool topic_match(const char *message, const char *subscription, bool is_normal, bool past_separator) {
544 // Reached end of both strings at the same time, this means we have a successful match
545 if (*message == '\0' && *subscription == '\0')
546 return true;
547
548 // Either the message or the subscribe are at the end. This means they don't match.
549 if (*message == '\0' || *subscription == '\0')
550 return false;
551
552 bool do_wildcards = is_normal || past_separator;
553
554 if (*subscription == '+' && do_wildcards) {
555 // single level wildcard
556 // consume + from subscription
557 subscription++;
558 // consume everything from message until '/' found or end of string
559 while (*message != '\0' && *message != '/') {
560 message++;
561 }
562 // after this, both pointers will point to a '/' or to the end of the string
563
564 return topic_match(message, subscription, is_normal, true);
565 }
566
567 if (*subscription == '#' && do_wildcards) {
568 // multilevel wildcard - MQTT mandates that this must be at end of subscribe topic
569 return true;
570 }
571
572 // this handles '/' and normal characters at the same time.
573 if (*message != *subscription)
574 return false;
575
576 past_separator = past_separator || *subscription == '/';
577
578 // consume characters
579 subscription++;
580 message++;
581
582 return topic_match(message, subscription, is_normal, past_separator);
583}
584
585static bool topic_match(const char *message, const char *subscription) {
586 return topic_match(message, subscription, *message != '\0' && *message != '$', false);
587}
588
589void MQTTClientComponent::on_message(const std::string &topic, const std::string &payload) {
590#ifdef USE_ESP8266
591 // on ESP8266, this is called in lwIP/AsyncTCP task; some components do not like running
592 // from a different task.
593 this->defer([this, topic, payload]() {
594#endif
595 for (auto &subscription : this->subscriptions_) {
596 if (topic_match(topic.c_str(), subscription.topic.c_str()))
597 subscription.callback(topic, payload);
598 }
599#ifdef USE_ESP8266
600 });
601#endif
602}
603
604// Setters
606bool MQTTClientComponent::is_log_message_enabled() const { return !this->log_message_.topic.empty(); }
607void MQTTClientComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
608void MQTTClientComponent::register_mqtt_component(MQTTComponent *component) { this->children_.push_back(component); }
609void MQTTClientComponent::set_log_level(int level) { this->log_level_ = level; }
610void MQTTClientComponent::set_keep_alive(uint16_t keep_alive_s) { this->mqtt_backend_.set_keep_alive(keep_alive_s); }
611void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this->log_message_ = std::move(message); }
612const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; }
613void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix, const std::string &check_topic_prefix) {
614 if (App.is_name_add_mac_suffix_enabled() && (topic_prefix == check_topic_prefix)) {
616 } else {
617 this->topic_prefix_ = topic_prefix;
618 }
619}
620const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; }
621void MQTTClientComponent::set_publish_nan_as_none(bool publish_nan_as_none) {
622 this->publish_nan_as_none_ = publish_nan_as_none;
623}
626 this->birth_message_.topic = "";
628}
630 this->shutdown_message_.topic = "";
632}
633bool MQTTClientComponent::is_discovery_enabled() const { return !this->discovery_info_.prefix.empty(); }
635const Availability &MQTTClientComponent::get_availability() { return this->availability_; }
637 if (this->birth_message_.topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
638 this->availability_.topic = "";
639 return;
640 }
644}
645
646void MQTTClientComponent::set_last_will(MQTTMessage &&message) {
647 this->last_will_ = std::move(message);
649}
650
651void MQTTClientComponent::set_birth_message(MQTTMessage &&message) {
652 this->birth_message_ = std::move(message);
654}
655
656void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->shutdown_message_ = std::move(message); }
657
658void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
659 MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain,
660 bool discover_ip, bool clean) {
661 this->discovery_info_.prefix = std::move(prefix);
662 this->discovery_info_.discover_ip = discover_ip;
663 this->discovery_info_.unique_id_generator = unique_id_generator;
664 this->discovery_info_.object_id_generator = object_id_generator;
665 this->discovery_info_.retain = retain;
666 this->discovery_info_.clean = clean;
667}
668
670
672 this->discovery_info_ = MQTTDiscoveryInfo{
673 .prefix = "",
674 .retain = false,
675 .discover_ip = false,
676 .clean = false,
677 .unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR,
678 .object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR,
679 };
680}
682 if (!this->shutdown_message_.topic.empty()) {
683 yield();
684 this->publish(this->shutdown_message_);
685 yield();
686 }
688}
689
691 this->mqtt_backend_.set_on_connect(std::forward<mqtt_on_connect_callback_t>(callback));
692}
693
695 this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback));
696}
697
698#if ASYNC_TCP_SSL_ENABLED
699void MQTTClientComponent::add_ssl_fingerprint(const std::array<uint8_t, SHA1_SIZE> &fingerprint) {
700 this->mqtt_backend_.setSecure(true);
701 this->mqtt_backend_.addServerFingerprint(fingerprint.data());
702}
703#endif
704
705MQTTClientComponent *global_mqtt_client = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
706
707// MQTTMessageTrigger
708MQTTMessageTrigger::MQTTMessageTrigger(std::string topic) : topic_(std::move(topic)) {}
709void MQTTMessageTrigger::set_qos(uint8_t qos) { this->qos_ = qos; }
710void MQTTMessageTrigger::set_payload(const std::string &payload) { this->payload_ = payload; }
711void MQTTMessageTrigger::setup() {
713 this->topic_,
714 [this](const std::string &topic, const std::string &payload) {
715 if (this->payload_.has_value() && payload != *this->payload_) {
716 return;
717 }
718
719 this->trigger(payload);
720 },
721 this->qos_);
722}
723void MQTTMessageTrigger::dump_config() {
724 ESP_LOGCONFIG(TAG, "MQTT Message Trigger:");
725 ESP_LOGCONFIG(TAG, " Topic: '%s'", this->topic_.c_str());
726 ESP_LOGCONFIG(TAG, " QoS: %u", this->qos_);
727}
728float MQTTMessageTrigger::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
729
730} // namespace mqtt
731} // namespace esphome
732
733#endif // USE_MQTT
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.
void status_momentary_warning(const std::string &name, uint32_t length=5000)
void status_set_warning(const char *message="unspecified")
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
void status_clear_warning()
uint16_t get_port() const
void add_on_log_callback(std::function< void(int, const char *, const char *)> &&callback)
Register a callback that will be called for every log message sent.
Definition logger.cpp:217
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
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.
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)
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)
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 add_ssl_fingerprint(const std::array< uint8_t, SHA1_SIZE > &fingerprint)
Add a SSL fingerprint to use for TCP SSL connections to the MQTT broker.
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.
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.
std::vector< MQTTComponent * > children_
void set_on_connect(mqtt_on_connect_callback_t &&callback)
optional< MQTTClientDisconnectReason > disconnect_reason_
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 resubscribe_subscription_(MQTTSubscription *sub)
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback)
bool subscribe_(const char *topic, uint8_t qos)
const Availability & get_availability()
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.
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.
MQTTDiscoveryInfo discovery_info_
The discovery info options for Home Assistant.
Availability availability_
Caches availability.
MQTTMessageTrigger(std::string topic)
bool has_value() const
Definition optional.h:87
in_addr ip_addr_t
Definition ip_address.h:22
APIServer * global_api_server
std::function< void(JsonObject)> json_build_t
Callback function typedef for building JsonObjects.
Definition json_util.h:20
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.
Definition json_util.cpp:47
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition json_util.cpp:12
Logger * global_logger
Definition logger.cpp:251
std::function< MQTTBackend::on_disconnect_callback_t > mqtt_on_disconnect_callback_t
Definition mqtt_client.h:29
MQTTDiscoveryObjectIdGenerator
available discovery object_id generators
Definition mqtt_client.h:71
@ MQTT_NONE_OBJECT_ID_GENERATOR
Definition mqtt_client.h:72
MQTTDiscoveryUniqueIdGenerator
available discovery unique_id generators
Definition mqtt_client.h:65
@ MQTT_LEGACY_UNIQUE_ID_GENERATOR
Definition mqtt_client.h:66
std::function< void(const std::string &, JsonObject)> mqtt_json_callback_t
Definition mqtt_client.h:36
std::function< void(const std::string &, const std::string &)> mqtt_callback_t
Callback for MQTT subscriptions.
Definition mqtt_client.h:35
MQTTClientComponent * global_mqtt_client
@ MQTT_CLIENT_DISCONNECTED
Definition mqtt_client.h:91
@ MQTT_CLIENT_RESOLVING_ADDRESS
Definition mqtt_client.h:92
std::function< MQTTBackend::on_connect_callback_t > mqtt_on_connect_callback_t
Callback for MQTT events.
Definition mqtt_client.h:28
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition util.cpp:15
network::IPAddresses get_ip_addresses()
Definition util.cpp:40
bool is_disabled()
Return whether the network is disabled (only wifi for now)
Definition util.cpp:32
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition component.cpp:26
const char *const TAG
Definition spi.cpp:8
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string str_sanitize(const std::string &str)
Sanitizes the input string by removing all characters but alphanumerics, dashes and underscores.
Definition helpers.cpp:299
std::string size_t len
Definition helpers.h:301
void IRAM_ATTR HOT yield()
Definition core.cpp:26
std::string to_string(int value)
Definition helpers.cpp:82
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition helpers.cpp:726
Application App
Global storage of Application pointer - only one Application can exist.
std::string payload_not_available
Definition mqtt_client.h:61
std::string topic
Empty means disabled.
Definition mqtt_client.h:59
std::string address
The address of the server without port number.
Definition mqtt_client.h:49
bool clean_session
Whether the session will be cleaned or remembered between connects.
Definition mqtt_client.h:54
std::string client_id
The client ID. Will automatically be truncated to 23 characters.
Definition mqtt_client.h:53
MQTTDiscoveryUniqueIdGenerator unique_id_generator
Definition mqtt_client.h:85
bool discover_ip
Enable the Home Assistant device discovery.
Definition mqtt_client.h:83
std::string prefix
The Home Assistant discovery prefix. Empty means disabled.
Definition mqtt_client.h:81
MQTTDiscoveryObjectIdGenerator object_id_generator
Definition mqtt_client.h:86
bool retain
Whether to retain discovery messages.
Definition mqtt_client.h:82
uint8_t qos
QoS. Only for last will testaments.
std::string str() const
Definition ip_address.h:52