ESPHome  2024.9.0
mqtt_client.cpp
Go to the documentation of this file.
1 #include "mqtt_client.h"
2 
3 #ifdef USE_MQTT
4 
5 #include <utility>
8 #include "esphome/core/helpers.h"
9 #include "esphome/core/log.h"
10 #include "esphome/core/version.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 
25 namespace esphome {
26 namespace mqtt {
27 
28 static 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  });
54  this->disconnect_reason_ = reason;
55  });
56 #ifdef USE_LOGGER
57  if (this->is_log_message_enabled() && logger::global_logger != nullptr) {
58  logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
59  if (level <= this->log_level_ && this->is_connected()) {
60  this->publish({.topic = this->log_message_.topic,
61  .payload = message,
62  .qos = this->log_message_.qos,
63  .retain = this->log_message_.retain});
64  }
65  });
66  }
67 #endif
68 
69  if (this->is_discovery_ip_enabled()) {
70  this->subscribe(
71  "esphome/discover", [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); },
72  2);
73 
74  std::string topic = "esphome/ping/";
75  topic.append(App.get_name());
76  this->subscribe(
77  topic, [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); }, 2);
78  }
79 
80  this->last_connected_ = millis();
81  this->start_dnslookup_();
82 }
83 
85  if (!this->is_connected() or !this->is_discovery_ip_enabled()) {
86  return;
87  }
88  std::string topic = "esphome/discover/";
89  topic.append(App.get_name());
90 
91  this->publish_json(
92  topic,
93  [](JsonObject root) {
94  uint8_t index = 0;
95  for (auto &ip : network::get_ip_addresses()) {
96  if (ip.is_set()) {
97  root["ip" + (index == 0 ? "" : esphome::to_string(index))] = ip.str();
98  index++;
99  }
100  }
101  root["name"] = App.get_name();
102  if (!App.get_friendly_name().empty()) {
103  root["friendly_name"] = App.get_friendly_name();
104  }
105 #ifdef USE_API
106  root["port"] = api::global_api_server->get_port();
107 #endif
108  root["version"] = ESPHOME_VERSION;
109  root["mac"] = get_mac_address();
110 
111 #ifdef USE_ESP8266
112  root["platform"] = "ESP8266";
113 #endif
114 #ifdef USE_ESP32
115  root["platform"] = "ESP32";
116 #endif
117 #ifdef USE_LIBRETINY
118  root["platform"] = lt_cpu_get_model_name();
119 #endif
120 
121  root["board"] = ESPHOME_BOARD;
122 #if defined(USE_WIFI)
123  root["network"] = "wifi";
124 #elif defined(USE_ETHERNET)
125  root["network"] = "ethernet";
126 #endif
127 
128 #ifdef ESPHOME_PROJECT_NAME
129  root["project_name"] = ESPHOME_PROJECT_NAME;
130  root["project_version"] = ESPHOME_PROJECT_VERSION;
131 #endif // ESPHOME_PROJECT_NAME
132 
133 #ifdef USE_DASHBOARD_IMPORT
134  root["package_import_url"] = dashboard_import::get_package_import_url();
135 #endif
136 
137 #ifdef USE_API_NOISE
138  root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
139 #endif
140  },
141  2, this->discovery_info_.retain);
142 }
143 
145  ESP_LOGCONFIG(TAG, "MQTT:");
146  ESP_LOGCONFIG(TAG, " Server Address: %s:%u (%s)", this->credentials_.address.c_str(), this->credentials_.port,
147  this->ip_.str().c_str());
148  ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str());
149  ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str());
150  if (this->is_discovery_ip_enabled()) {
151  ESP_LOGCONFIG(TAG, " Discovery IP enabled");
152  }
153  if (!this->discovery_info_.prefix.empty()) {
154  ESP_LOGCONFIG(TAG, " Discovery prefix: '%s'", this->discovery_info_.prefix.c_str());
155  ESP_LOGCONFIG(TAG, " Discovery retain: %s", YESNO(this->discovery_info_.retain));
156  }
157  ESP_LOGCONFIG(TAG, " Topic Prefix: '%s'", this->topic_prefix_.c_str());
158  if (!this->log_message_.topic.empty()) {
159  ESP_LOGCONFIG(TAG, " Log Topic: '%s'", this->log_message_.topic.c_str());
160  }
161  if (!this->availability_.topic.empty()) {
162  ESP_LOGCONFIG(TAG, " Availability: '%s'", this->availability_.topic.c_str());
163  }
164 }
166 
168  for (auto &subscription : this->subscriptions_) {
169  subscription.subscribed = false;
170  subscription.resubscribe_timeout = 0;
171  }
172 
173  this->status_set_warning();
174  this->dns_resolve_error_ = false;
175  this->dns_resolved_ = false;
176  ip_addr_t addr;
177 #if USE_NETWORK_IPV6
178  err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
179  MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
180 #else
181  err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
182  MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4);
183 #endif /* USE_NETWORK_IPV6 */
184  switch (err) {
185  case ERR_OK: {
186  // Got IP immediately
187  this->dns_resolved_ = true;
188  this->ip_ = network::IPAddress(&addr);
189  this->start_connect_();
190  return;
191  }
192  case ERR_INPROGRESS: {
193  // wait for callback
194  ESP_LOGD(TAG, "Resolving MQTT broker IP address...");
195  break;
196  }
197  default:
198  case ERR_ARG: {
199  // error
200  ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err);
201  break;
202  }
203  }
204 
206  this->connect_begin_ = millis();
207 }
209  if (!this->dns_resolved_ && millis() - this->connect_begin_ > 20000) {
210  this->dns_resolve_error_ = true;
211  }
212 
213  if (this->dns_resolve_error_) {
214  ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'!", this->credentials_.address.c_str());
216  return;
217  }
218 
219  if (!this->dns_resolved_) {
220  return;
221  }
222 
223  ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.str().c_str());
224  this->start_connect_();
225 }
226 #if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1
227 void MQTTClientComponent::dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg) {
228 #else
229 void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
230 #endif
231  auto *a_this = (MQTTClientComponent *) callback_arg;
232  if (ipaddr == nullptr) {
233  a_this->dns_resolve_error_ = true;
234  } else {
235  a_this->ip_ = network::IPAddress(ipaddr);
236  a_this->dns_resolved_ = true;
237  }
238 }
239 
241  if (!network::is_connected())
242  return;
243 
244  ESP_LOGI(TAG, "Connecting to MQTT...");
245  // Force disconnect first
246  this->mqtt_backend_.disconnect();
247 
248  this->mqtt_backend_.set_client_id(this->credentials_.client_id.c_str());
249  const char *username = nullptr;
250  if (!this->credentials_.username.empty())
251  username = this->credentials_.username.c_str();
252  const char *password = nullptr;
253  if (!this->credentials_.password.empty())
254  password = this->credentials_.password.c_str();
255 
256  this->mqtt_backend_.set_credentials(username, password);
257 
258  this->mqtt_backend_.set_server(this->credentials_.address.c_str(), this->credentials_.port);
259  if (!this->last_will_.topic.empty()) {
260  this->mqtt_backend_.set_will(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain,
261  this->last_will_.payload.c_str());
262  }
263 
264  this->mqtt_backend_.connect();
266  this->connect_begin_ = millis();
267 }
269  return this->state_ == MQTT_CLIENT_CONNECTED && this->mqtt_backend_.connected();
270 }
271 
273  if (!this->mqtt_backend_.connected()) {
274  if (millis() - this->connect_begin_ > 60000) {
276  this->start_dnslookup_();
277  }
278  return;
279  }
280 
282  this->sent_birth_message_ = false;
283  this->status_clear_warning();
284  ESP_LOGI(TAG, "MQTT Connected!");
285  // MQTT Client needs some time to be fully set up.
286  delay(100); // NOLINT
287 
289  this->send_device_info_();
290 
291  for (MQTTComponent *component : this->children_)
292  component->schedule_resend_state();
293 }
294 
296  // Call the backend loop first
298 
299  if (this->disconnect_reason_.has_value()) {
300  const LogString *reason_s;
301  switch (*this->disconnect_reason_) {
303  reason_s = LOG_STR("TCP disconnected");
304  break;
306  reason_s = LOG_STR("Unacceptable Protocol Version");
307  break;
309  reason_s = LOG_STR("Identifier Rejected");
310  break;
312  reason_s = LOG_STR("Server Unavailable");
313  break;
315  reason_s = LOG_STR("Malformed Credentials");
316  break;
318  reason_s = LOG_STR("Not Authorized");
319  break;
321  reason_s = LOG_STR("Not Enough Space");
322  break;
324  reason_s = LOG_STR("TLS Bad Fingerprint");
325  break;
326  default:
327  reason_s = LOG_STR("Unknown");
328  break;
329  }
330  if (!network::is_connected()) {
331  reason_s = LOG_STR("WiFi disconnected");
332  }
333  ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s));
334  this->disconnect_reason_.reset();
335  }
336 
337  const uint32_t now = millis();
338 
339  switch (this->state_) {
341  if (now - this->connect_begin_ > 5000) {
342  this->start_dnslookup_();
343  }
344  break;
346  this->check_dnslookup_();
347  break;
349  this->check_connected();
350  break;
352  if (!this->mqtt_backend_.connected()) {
354  ESP_LOGW(TAG, "Lost MQTT Client connection!");
355  this->start_dnslookup_();
356  } else {
357  if (!this->birth_message_.topic.empty() && !this->sent_birth_message_) {
358  this->sent_birth_message_ = this->publish(this->birth_message_);
359  }
360 
361  this->last_connected_ = now;
363  }
364  break;
365  }
366 
367  if (millis() - this->last_connected_ > this->reboot_timeout_ && this->reboot_timeout_ != 0) {
368  ESP_LOGE(TAG, "Can't connect to MQTT... Restarting...");
369  App.reboot();
370  }
371 }
373 
374 // Subscribe
375 bool MQTTClientComponent::subscribe_(const char *topic, uint8_t qos) {
376  if (!this->is_connected())
377  return false;
378 
379  bool ret = this->mqtt_backend_.subscribe(topic, qos);
380  yield();
381 
382  if (ret) {
383  ESP_LOGV(TAG, "subscribe(topic='%s')", topic);
384  } else {
385  delay(5);
386  ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry later.", topic);
387  this->status_momentary_warning("subscribe", 1000);
388  }
389  return ret != 0;
390 }
391 void MQTTClientComponent::resubscribe_subscription_(MQTTSubscription *sub) {
392  if (sub->subscribed)
393  return;
394 
395  const uint32_t now = millis();
396  bool do_resub = sub->resubscribe_timeout == 0 || now - sub->resubscribe_timeout > 1000;
397 
398  if (do_resub) {
399  sub->subscribed = this->subscribe_(sub->topic.c_str(), sub->qos);
400  sub->resubscribe_timeout = now;
401  }
402 }
404  for (auto &subscription : this->subscriptions_) {
405  this->resubscribe_subscription_(&subscription);
406  }
407 }
408 
409 void MQTTClientComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) {
410  MQTTSubscription subscription{
411  .topic = topic,
412  .qos = qos,
413  .callback = std::move(callback),
414  .subscribed = false,
415  .resubscribe_timeout = 0,
416  };
417  this->resubscribe_subscription_(&subscription);
418  this->subscriptions_.push_back(subscription);
419 }
420 
421 void MQTTClientComponent::subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos) {
422  auto f = [callback](const std::string &topic, const std::string &payload) {
423  json::parse_json(payload, [topic, callback](JsonObject root) -> bool {
424  callback(topic, root);
425  return true;
426  });
427  };
428  MQTTSubscription subscription{
429  .topic = topic,
430  .qos = qos,
431  .callback = f,
432  .subscribed = false,
433  .resubscribe_timeout = 0,
434  };
435  this->resubscribe_subscription_(&subscription);
436  this->subscriptions_.push_back(subscription);
437 }
438 
439 void MQTTClientComponent::unsubscribe(const std::string &topic) {
440  bool ret = this->mqtt_backend_.unsubscribe(topic.c_str());
441  yield();
442  if (ret) {
443  ESP_LOGV(TAG, "unsubscribe(topic='%s')", topic.c_str());
444  } else {
445  delay(5);
446  ESP_LOGV(TAG, "Unsubscribe failed for topic='%s'.", topic.c_str());
447  this->status_momentary_warning("unsubscribe", 1000);
448  }
449 
450  auto it = subscriptions_.begin();
451  while (it != subscriptions_.end()) {
452  if (it->topic == topic) {
453  it = subscriptions_.erase(it);
454  } else {
455  ++it;
456  }
457  }
458 }
459 
460 // Publish
461 bool MQTTClientComponent::publish(const std::string &topic, const std::string &payload, uint8_t qos, bool retain) {
462  return this->publish(topic, payload.data(), payload.size(), qos, retain);
463 }
464 
465 bool MQTTClientComponent::publish(const std::string &topic, const char *payload, size_t payload_length, uint8_t qos,
466  bool retain) {
467  return publish({.topic = topic, .payload = payload, .qos = qos, .retain = retain});
468 }
469 
470 bool MQTTClientComponent::publish(const MQTTMessage &message) {
471  if (!this->is_connected()) {
472  // critical components will re-transmit their messages
473  return false;
474  }
475  bool logging_topic = this->log_message_.topic == message.topic;
476  bool ret = this->mqtt_backend_.publish(message);
477  delay(0);
478  if (!ret && !logging_topic && this->is_connected()) {
479  delay(0);
480  ret = this->mqtt_backend_.publish(message);
481  delay(0);
482  }
483 
484  if (!logging_topic) {
485  if (ret) {
486  ESP_LOGV(TAG, "Publish(topic='%s' payload='%s' retain=%d qos=%d)", message.topic.c_str(), message.payload.c_str(),
487  message.retain, message.qos);
488  } else {
489  ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). will retry later..", message.topic.c_str(),
490  message.payload.length());
491  this->status_momentary_warning("publish", 1000);
492  }
493  }
494  return ret != 0;
495 }
496 bool MQTTClientComponent::publish_json(const std::string &topic, const json::json_build_t &f, uint8_t qos,
497  bool retain) {
498  std::string message = json::build_json(f);
499  return this->publish(topic, message, qos, retain);
500 }
501 
513 static bool topic_match(const char *message, const char *subscription, bool is_normal, bool past_separator) {
514  // Reached end of both strings at the same time, this means we have a successful match
515  if (*message == '\0' && *subscription == '\0')
516  return true;
517 
518  // Either the message or the subscribe are at the end. This means they don't match.
519  if (*message == '\0' || *subscription == '\0')
520  return false;
521 
522  bool do_wildcards = is_normal || past_separator;
523 
524  if (*subscription == '+' && do_wildcards) {
525  // single level wildcard
526  // consume + from subscription
527  subscription++;
528  // consume everything from message until '/' found or end of string
529  while (*message != '\0' && *message != '/') {
530  message++;
531  }
532  // after this, both pointers will point to a '/' or to the end of the string
533 
534  return topic_match(message, subscription, is_normal, true);
535  }
536 
537  if (*subscription == '#' && do_wildcards) {
538  // multilevel wildcard - MQTT mandates that this must be at end of subscribe topic
539  return true;
540  }
541 
542  // this handles '/' and normal characters at the same time.
543  if (*message != *subscription)
544  return false;
545 
546  past_separator = past_separator || *subscription == '/';
547 
548  // consume characters
549  subscription++;
550  message++;
551 
552  return topic_match(message, subscription, is_normal, past_separator);
553 }
554 
555 static bool topic_match(const char *message, const char *subscription) {
556  return topic_match(message, subscription, *message != '\0' && *message != '$', false);
557 }
558 
559 void MQTTClientComponent::on_message(const std::string &topic, const std::string &payload) {
560 #ifdef USE_ESP8266
561  // on ESP8266, this is called in lwIP/AsyncTCP task; some components do not like running
562  // from a different task.
563  this->defer([this, topic, payload]() {
564 #endif
565  for (auto &subscription : this->subscriptions_) {
566  if (topic_match(topic.c_str(), subscription.topic.c_str()))
567  subscription.callback(topic, payload);
568  }
569 #ifdef USE_ESP8266
570  });
571 #endif
572 }
573 
574 // Setters
576 bool MQTTClientComponent::is_log_message_enabled() const { return !this->log_message_.topic.empty(); }
577 void MQTTClientComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
578 void MQTTClientComponent::register_mqtt_component(MQTTComponent *component) { this->children_.push_back(component); }
579 void MQTTClientComponent::set_log_level(int level) { this->log_level_ = level; }
580 void MQTTClientComponent::set_keep_alive(uint16_t keep_alive_s) { this->mqtt_backend_.set_keep_alive(keep_alive_s); }
581 void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this->log_message_ = std::move(message); }
582 const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; }
583 void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix) { this->topic_prefix_ = topic_prefix; }
584 const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; }
586  this->birth_message_.topic = "";
588 }
590  this->shutdown_message_.topic = "";
592 }
593 bool MQTTClientComponent::is_discovery_enabled() const { return !this->discovery_info_.prefix.empty(); }
595 const Availability &MQTTClientComponent::get_availability() { return this->availability_; }
597  if (this->birth_message_.topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
598  this->availability_.topic = "";
599  return;
600  }
601  this->availability_.topic = this->birth_message_.topic;
604 }
605 
606 void MQTTClientComponent::set_last_will(MQTTMessage &&message) {
607  this->last_will_ = std::move(message);
609 }
610 
611 void MQTTClientComponent::set_birth_message(MQTTMessage &&message) {
612  this->birth_message_ = std::move(message);
614 }
615 
616 void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->shutdown_message_ = std::move(message); }
617 
618 void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
619  MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain,
620  bool discover_ip, bool clean) {
621  this->discovery_info_.prefix = std::move(prefix);
622  this->discovery_info_.discover_ip = discover_ip;
623  this->discovery_info_.unique_id_generator = unique_id_generator;
624  this->discovery_info_.object_id_generator = object_id_generator;
625  this->discovery_info_.retain = retain;
626  this->discovery_info_.clean = clean;
627 }
628 
630 
632  this->discovery_info_ = MQTTDiscoveryInfo{
633  .prefix = "",
634  .retain = false,
635  .discover_ip = false,
636  .clean = false,
637  .unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR,
638  .object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR,
639  };
640 }
642  if (!this->shutdown_message_.topic.empty()) {
643  yield();
644  this->publish(this->shutdown_message_);
645  yield();
646  }
647  this->mqtt_backend_.disconnect();
648 }
649 
651  this->mqtt_backend_.set_on_connect(std::forward<mqtt_on_connect_callback_t>(callback));
652 }
653 
655  this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback));
656 }
657 
658 #if ASYNC_TCP_SSL_ENABLED
659 void MQTTClientComponent::add_ssl_fingerprint(const std::array<uint8_t, SHA1_SIZE> &fingerprint) {
660  this->mqtt_backend_.setSecure(true);
661  this->mqtt_backend_.addServerFingerprint(fingerprint.data());
662 }
663 #endif
664 
665 MQTTClientComponent *global_mqtt_client = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
666 
667 // MQTTMessageTrigger
668 MQTTMessageTrigger::MQTTMessageTrigger(std::string topic) : topic_(std::move(topic)) {}
669 void MQTTMessageTrigger::set_qos(uint8_t qos) { this->qos_ = qos; }
670 void MQTTMessageTrigger::set_payload(const std::string &payload) { this->payload_ = payload; }
672  global_mqtt_client->subscribe(
673  this->topic_,
674  [this](const std::string &topic, const std::string &payload) {
675  if (this->payload_.has_value() && payload != *this->payload_) {
676  return;
677  }
678 
679  this->trigger(payload);
680  },
681  this->qos_);
682 }
684  ESP_LOGCONFIG(TAG, "MQTT Message Trigger:");
685  ESP_LOGCONFIG(TAG, " Topic: '%s'", this->topic_.c_str());
686  ESP_LOGCONFIG(TAG, " QoS: %u", this->qos_);
687 }
689 
690 } // namespace mqtt
691 } // namespace esphome
692 
693 #endif // USE_MQTT
const Availability & get_availability()
const char * name
Definition: stm32flash.h:78
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:178
void set_on_disconnect(std::function< on_disconnect_callback_t > &&callback) final
void disable_last_will()
Remove the last will testament message.
const float AFTER_CONNECTION
For components that should be initialized after a data connection (API/MQTT) is connected.
Definition: component.cpp:27
std::string topic
Empty means disabled.
Definition: mqtt_client.h:58
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition: component.cpp:26
MQTTDiscoveryUniqueIdGenerator unique_id_generator
Definition: mqtt_client.h:84
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
std::function< void(const std::string &, const std::string &)> mqtt_callback_t
Callback for MQTT subscriptions.
Definition: mqtt_client.h:35
std::string client_id
The client ID. Will automatically be truncated to 23 characters.
Definition: mqtt_client.h:53
bool unsubscribe(const char *topic) final
MQTTDiscoveryInfo discovery_info_
The discovery info options for Home Assistant.
Definition: mqtt_client.h:293
void status_momentary_warning(const std::string &name, uint32_t length=5000)
Definition: component.cpp:178
void set_server(network::IPAddress ip, uint16_t port) final
std::string str() const
Definition: ip_address.h:122
std::vector< MQTTComponent * > children_
Definition: mqtt_client.h:319
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&#39;s valid.
Definition: json_util.cpp:65
void set_client_id(const char *client_id) final
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition: component.cpp:130
STL namespace.
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
Definition: application.h:205
MQTTMessage last_will_
The last will message.
Definition: mqtt_client.h:283
void set_topic_prefix(const std::string &topic_prefix)
Set the topic prefix that will be prepended to all topics together with "/".
bool discover_ip
Enable the Home Assistant device discovery.
Definition: mqtt_client.h:82
std::string prefix
The Home Assistant discovery prefix. Empty means disabled.
Definition: mqtt_client.h:80
bool has_value() const
Definition: optional.h:87
void unsubscribe(const std::string &topic)
Unsubscribe from an MQTT topic.
void recalculate_availability_()
Re-calculate the availability property.
bool subscribe_(const char *topic, uint8_t qos)
const std::string & get_topic_prefix() const
Get the topic prefix of this device, using default if necessary.
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
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
void set_on_connect(mqtt_on_connect_callback_t &&callback)
void set_log_message_template(MQTTMessage &&message)
Manually set the topic used for logging.
float get_setup_priority() const override
void set_reboot_timeout(uint32_t reboot_timeout)
void set_credentials(const char *username, const char *password) final
void resubscribe_subscription_(MQTTSubscription *sub)
MQTTClientComponent * global_mqtt_client
void set_keep_alive(uint16_t keep_alive) final
Logger * global_logger
Definition: logger.cpp:198
network::IPAddresses get_ip_addresses()
Definition: util.cpp:40
const char *const TAG
Definition: spi.cpp:8
void set_payload(const std::string &payload)
void register_mqtt_component(MQTTComponent *component)
void loop() override
Reconnect if required.
uint16_t port
The port number of the server.
Definition: mqtt_client.h:50
bool publish(const MQTTMessage &message)
Publish a MQTTMessage.
void status_clear_warning()
Definition: component.cpp:166
void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final
MQTTDiscoveryUniqueIdGenerator
available discovery unique_id generators
Definition: mqtt_client.h:64
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition: helpers.cpp:688
static void dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg)
void disable_birth_message()
Remove the birth message.
std::function< void(JsonObject)> json_build_t
Callback function typedef for building JsonObjects.
Definition: json_util.h:20
Application App
Global storage of Application pointer - only one Application can exist.
std::string get_package_import_url()
MQTTMessageTrigger(std::string topic)
void on_message(const std::string &topic, const std::string &payload)
bool subscribe(const char *topic, uint8_t qos) final
MQTTDiscoveryObjectIdGenerator
available discovery object_id generators
Definition: mqtt_client.h:70
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition: json_util.cpp:21
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 setup() override
Setup the MQTT client, registering a bunch of callbacks and attempting to connect.
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:202
void set_on_message(std::function< on_message_callback_t > &&callback) final
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_log_message()
Get the topic used for logging. Defaults to "<topic_prefix>/debug" and the value is cached for speed...
const MQTTDiscoveryInfo & get_discovery_info() const
Get Home Assistant discovery info.
std::string address
The address of the server without port number.
Definition: mqtt_client.h:49
bool publish(const char *topic, const char *payload, size_t length, uint8_t qos, bool retain) final
float get_setup_priority() const override
MQTT client setup priority.
bool is_disabled()
Return whether the network is disabled (only wifi for now)
Definition: util.cpp:32
std::string to_string(int value)
Definition: helpers.cpp:82
void set_last_will(MQTTMessage &&message)
Set the last will testament message.
std::string size_t len
Definition: helpers.h:292
void IRAM_ATTR HOT yield()
Definition: core.cpp:24
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.
std::function< MQTTBackend::on_disconnect_callback_t > mqtt_on_disconnect_callback_t
Definition: mqtt_client.h:29
void start_connect_()
Reconnect to the MQTT broker if not already connected.
MQTTMessage birth_message_
The birth message (e.g.
Definition: mqtt_client.h:286
in_addr ip_addr_t
Definition: ip_address.h:22
void set_shutdown_message(MQTTMessage &&message)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::string payload_not_available
Definition: mqtt_client.h:60
std::vector< MQTTSubscription > subscriptions_
Definition: mqtt_client.h:306
MQTTDiscoveryObjectIdGenerator object_id_generator
Definition: mqtt_client.h:85
void disable_discovery()
Globally disable Home Assistant discovery.
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.
uint8_t qos
QoS. Only for last will testaments.
Definition: mqtt_backend.h:27
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.
optional< MQTTClientDisconnectReason > disconnect_reason_
Definition: mqtt_client.h:323
uint16_t get_port() const
Definition: api_server.cpp:377
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback)
APIServer * global_api_server
Definition: api_server.cpp:346
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.
Availability availability_
Caches availability.
Definition: mqtt_client.h:290
void set_birth_message(MQTTMessage &&message)
Set the birth message.
void set_on_connect(std::function< on_connect_callback_t > &&callback) final
bool retain
Whether to retain discovery messages.
Definition: mqtt_client.h:81
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26
std::function< void(const std::string &, JsonObject)> mqtt_json_callback_t
Definition: mqtt_client.h:36