ESPHome  2024.10.2
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  ESP_LOGCONFIG(TAG, " Clean Session: %s", YESNO(this->credentials_.clean_session));
151  if (this->is_discovery_ip_enabled()) {
152  ESP_LOGCONFIG(TAG, " Discovery IP enabled");
153  }
154  if (!this->discovery_info_.prefix.empty()) {
155  ESP_LOGCONFIG(TAG, " Discovery prefix: '%s'", this->discovery_info_.prefix.c_str());
156  ESP_LOGCONFIG(TAG, " Discovery retain: %s", YESNO(this->discovery_info_.retain));
157  }
158  ESP_LOGCONFIG(TAG, " Topic Prefix: '%s'", this->topic_prefix_.c_str());
159  if (!this->log_message_.topic.empty()) {
160  ESP_LOGCONFIG(TAG, " Log Topic: '%s'", this->log_message_.topic.c_str());
161  }
162  if (!this->availability_.topic.empty()) {
163  ESP_LOGCONFIG(TAG, " Availability: '%s'", this->availability_.topic.c_str());
164  }
165 }
167 
169  for (auto &subscription : this->subscriptions_) {
170  subscription.subscribed = false;
171  subscription.resubscribe_timeout = 0;
172  }
173 
174  this->status_set_warning();
175  this->dns_resolve_error_ = false;
176  this->dns_resolved_ = false;
177  ip_addr_t addr;
178 #if USE_NETWORK_IPV6
179  err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
180  MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV6_IPV4);
181 #else
182  err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
183  MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4);
184 #endif /* USE_NETWORK_IPV6 */
185  switch (err) {
186  case ERR_OK: {
187  // Got IP immediately
188  this->dns_resolved_ = true;
189  this->ip_ = network::IPAddress(&addr);
190  this->start_connect_();
191  return;
192  }
193  case ERR_INPROGRESS: {
194  // wait for callback
195  ESP_LOGD(TAG, "Resolving MQTT broker IP address...");
196  break;
197  }
198  default:
199  case ERR_ARG: {
200  // error
201  ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err);
202  break;
203  }
204  }
205 
207  this->connect_begin_ = millis();
208 }
210  if (!this->dns_resolved_ && millis() - this->connect_begin_ > 20000) {
211  this->dns_resolve_error_ = true;
212  }
213 
214  if (this->dns_resolve_error_) {
215  ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'!", this->credentials_.address.c_str());
217  return;
218  }
219 
220  if (!this->dns_resolved_) {
221  return;
222  }
223 
224  ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.str().c_str());
225  this->start_connect_();
226 }
227 #if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1
228 void MQTTClientComponent::dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg) {
229 #else
230 void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
231 #endif
232  auto *a_this = (MQTTClientComponent *) callback_arg;
233  if (ipaddr == nullptr) {
234  a_this->dns_resolve_error_ = true;
235  } else {
236  a_this->ip_ = network::IPAddress(ipaddr);
237  a_this->dns_resolved_ = true;
238  }
239 }
240 
242  if (!network::is_connected())
243  return;
244 
245  ESP_LOGI(TAG, "Connecting to MQTT...");
246  // Force disconnect first
247  this->mqtt_backend_.disconnect();
248 
249  this->mqtt_backend_.set_client_id(this->credentials_.client_id.c_str());
251  const char *username = nullptr;
252  if (!this->credentials_.username.empty())
253  username = this->credentials_.username.c_str();
254  const char *password = nullptr;
255  if (!this->credentials_.password.empty())
256  password = this->credentials_.password.c_str();
257 
258  this->mqtt_backend_.set_credentials(username, password);
259 
260  this->mqtt_backend_.set_server(this->credentials_.address.c_str(), this->credentials_.port);
261  if (!this->last_will_.topic.empty()) {
262  this->mqtt_backend_.set_will(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain,
263  this->last_will_.payload.c_str());
264  }
265 
266  this->mqtt_backend_.connect();
268  this->connect_begin_ = millis();
269 }
271  return this->state_ == MQTT_CLIENT_CONNECTED && this->mqtt_backend_.connected();
272 }
273 
275  if (!this->mqtt_backend_.connected()) {
276  if (millis() - this->connect_begin_ > 60000) {
278  this->start_dnslookup_();
279  }
280  return;
281  }
282 
284  this->sent_birth_message_ = false;
285  this->status_clear_warning();
286  ESP_LOGI(TAG, "MQTT Connected!");
287  // MQTT Client needs some time to be fully set up.
288  delay(100); // NOLINT
289 
291  this->send_device_info_();
292 
293  for (MQTTComponent *component : this->children_)
294  component->schedule_resend_state();
295 }
296 
298  // Call the backend loop first
300 
301  if (this->disconnect_reason_.has_value()) {
302  const LogString *reason_s;
303  switch (*this->disconnect_reason_) {
305  reason_s = LOG_STR("TCP disconnected");
306  break;
308  reason_s = LOG_STR("Unacceptable Protocol Version");
309  break;
311  reason_s = LOG_STR("Identifier Rejected");
312  break;
314  reason_s = LOG_STR("Server Unavailable");
315  break;
317  reason_s = LOG_STR("Malformed Credentials");
318  break;
320  reason_s = LOG_STR("Not Authorized");
321  break;
323  reason_s = LOG_STR("Not Enough Space");
324  break;
326  reason_s = LOG_STR("TLS Bad Fingerprint");
327  break;
328  default:
329  reason_s = LOG_STR("Unknown");
330  break;
331  }
332  if (!network::is_connected()) {
333  reason_s = LOG_STR("WiFi disconnected");
334  }
335  ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s));
336  this->disconnect_reason_.reset();
337  }
338 
339  const uint32_t now = millis();
340 
341  switch (this->state_) {
343  if (now - this->connect_begin_ > 5000) {
344  this->start_dnslookup_();
345  }
346  break;
348  this->check_dnslookup_();
349  break;
351  this->check_connected();
352  break;
354  if (!this->mqtt_backend_.connected()) {
356  ESP_LOGW(TAG, "Lost MQTT Client connection!");
357  this->start_dnslookup_();
358  } else {
359  if (!this->birth_message_.topic.empty() && !this->sent_birth_message_) {
360  this->sent_birth_message_ = this->publish(this->birth_message_);
361  }
362 
363  this->last_connected_ = now;
365  }
366  break;
367  }
368 
369  if (millis() - this->last_connected_ > this->reboot_timeout_ && this->reboot_timeout_ != 0) {
370  ESP_LOGE(TAG, "Can't connect to MQTT... Restarting...");
371  App.reboot();
372  }
373 }
375 
376 // Subscribe
377 bool MQTTClientComponent::subscribe_(const char *topic, uint8_t qos) {
378  if (!this->is_connected())
379  return false;
380 
381  bool ret = this->mqtt_backend_.subscribe(topic, qos);
382  yield();
383 
384  if (ret) {
385  ESP_LOGV(TAG, "subscribe(topic='%s')", topic);
386  } else {
387  delay(5);
388  ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry later.", topic);
389  this->status_momentary_warning("subscribe", 1000);
390  }
391  return ret != 0;
392 }
393 void MQTTClientComponent::resubscribe_subscription_(MQTTSubscription *sub) {
394  if (sub->subscribed)
395  return;
396 
397  const uint32_t now = millis();
398  bool do_resub = sub->resubscribe_timeout == 0 || now - sub->resubscribe_timeout > 1000;
399 
400  if (do_resub) {
401  sub->subscribed = this->subscribe_(sub->topic.c_str(), sub->qos);
402  sub->resubscribe_timeout = now;
403  }
404 }
406  for (auto &subscription : this->subscriptions_) {
407  this->resubscribe_subscription_(&subscription);
408  }
409 }
410 
411 void MQTTClientComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) {
412  MQTTSubscription subscription{
413  .topic = topic,
414  .qos = qos,
415  .callback = std::move(callback),
416  .subscribed = false,
417  .resubscribe_timeout = 0,
418  };
419  this->resubscribe_subscription_(&subscription);
420  this->subscriptions_.push_back(subscription);
421 }
422 
423 void MQTTClientComponent::subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos) {
424  auto f = [callback](const std::string &topic, const std::string &payload) {
425  json::parse_json(payload, [topic, callback](JsonObject root) -> bool {
426  callback(topic, root);
427  return true;
428  });
429  };
430  MQTTSubscription subscription{
431  .topic = topic,
432  .qos = qos,
433  .callback = f,
434  .subscribed = false,
435  .resubscribe_timeout = 0,
436  };
437  this->resubscribe_subscription_(&subscription);
438  this->subscriptions_.push_back(subscription);
439 }
440 
441 void MQTTClientComponent::unsubscribe(const std::string &topic) {
442  bool ret = this->mqtt_backend_.unsubscribe(topic.c_str());
443  yield();
444  if (ret) {
445  ESP_LOGV(TAG, "unsubscribe(topic='%s')", topic.c_str());
446  } else {
447  delay(5);
448  ESP_LOGV(TAG, "Unsubscribe failed for topic='%s'.", topic.c_str());
449  this->status_momentary_warning("unsubscribe", 1000);
450  }
451 
452  auto it = subscriptions_.begin();
453  while (it != subscriptions_.end()) {
454  if (it->topic == topic) {
455  it = subscriptions_.erase(it);
456  } else {
457  ++it;
458  }
459  }
460 }
461 
462 // Publish
463 bool MQTTClientComponent::publish(const std::string &topic, const std::string &payload, uint8_t qos, bool retain) {
464  return this->publish(topic, payload.data(), payload.size(), qos, retain);
465 }
466 
467 bool MQTTClientComponent::publish(const std::string &topic, const char *payload, size_t payload_length, uint8_t qos,
468  bool retain) {
469  return publish({.topic = topic, .payload = payload, .qos = qos, .retain = retain});
470 }
471 
472 bool MQTTClientComponent::publish(const MQTTMessage &message) {
473  if (!this->is_connected()) {
474  // critical components will re-transmit their messages
475  return false;
476  }
477  bool logging_topic = this->log_message_.topic == message.topic;
478  bool ret = this->mqtt_backend_.publish(message);
479  delay(0);
480  if (!ret && !logging_topic && this->is_connected()) {
481  delay(0);
482  ret = this->mqtt_backend_.publish(message);
483  delay(0);
484  }
485 
486  if (!logging_topic) {
487  if (ret) {
488  ESP_LOGV(TAG, "Publish(topic='%s' payload='%s' retain=%d qos=%d)", message.topic.c_str(), message.payload.c_str(),
489  message.retain, message.qos);
490  } else {
491  ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). will retry later..", message.topic.c_str(),
492  message.payload.length());
493  this->status_momentary_warning("publish", 1000);
494  }
495  }
496  return ret != 0;
497 }
498 bool MQTTClientComponent::publish_json(const std::string &topic, const json::json_build_t &f, uint8_t qos,
499  bool retain) {
500  std::string message = json::build_json(f);
501  return this->publish(topic, message, qos, retain);
502 }
503 
515 static bool topic_match(const char *message, const char *subscription, bool is_normal, bool past_separator) {
516  // Reached end of both strings at the same time, this means we have a successful match
517  if (*message == '\0' && *subscription == '\0')
518  return true;
519 
520  // Either the message or the subscribe are at the end. This means they don't match.
521  if (*message == '\0' || *subscription == '\0')
522  return false;
523 
524  bool do_wildcards = is_normal || past_separator;
525 
526  if (*subscription == '+' && do_wildcards) {
527  // single level wildcard
528  // consume + from subscription
529  subscription++;
530  // consume everything from message until '/' found or end of string
531  while (*message != '\0' && *message != '/') {
532  message++;
533  }
534  // after this, both pointers will point to a '/' or to the end of the string
535 
536  return topic_match(message, subscription, is_normal, true);
537  }
538 
539  if (*subscription == '#' && do_wildcards) {
540  // multilevel wildcard - MQTT mandates that this must be at end of subscribe topic
541  return true;
542  }
543 
544  // this handles '/' and normal characters at the same time.
545  if (*message != *subscription)
546  return false;
547 
548  past_separator = past_separator || *subscription == '/';
549 
550  // consume characters
551  subscription++;
552  message++;
553 
554  return topic_match(message, subscription, is_normal, past_separator);
555 }
556 
557 static bool topic_match(const char *message, const char *subscription) {
558  return topic_match(message, subscription, *message != '\0' && *message != '$', false);
559 }
560 
561 void MQTTClientComponent::on_message(const std::string &topic, const std::string &payload) {
562 #ifdef USE_ESP8266
563  // on ESP8266, this is called in lwIP/AsyncTCP task; some components do not like running
564  // from a different task.
565  this->defer([this, topic, payload]() {
566 #endif
567  for (auto &subscription : this->subscriptions_) {
568  if (topic_match(topic.c_str(), subscription.topic.c_str()))
569  subscription.callback(topic, payload);
570  }
571 #ifdef USE_ESP8266
572  });
573 #endif
574 }
575 
576 // Setters
578 bool MQTTClientComponent::is_log_message_enabled() const { return !this->log_message_.topic.empty(); }
579 void MQTTClientComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
580 void MQTTClientComponent::register_mqtt_component(MQTTComponent *component) { this->children_.push_back(component); }
581 void MQTTClientComponent::set_log_level(int level) { this->log_level_ = level; }
582 void MQTTClientComponent::set_keep_alive(uint16_t keep_alive_s) { this->mqtt_backend_.set_keep_alive(keep_alive_s); }
583 void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this->log_message_ = std::move(message); }
584 const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; }
585 void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix) { this->topic_prefix_ = topic_prefix; }
586 const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; }
588  this->birth_message_.topic = "";
590 }
592  this->shutdown_message_.topic = "";
594 }
595 bool MQTTClientComponent::is_discovery_enabled() const { return !this->discovery_info_.prefix.empty(); }
597 const Availability &MQTTClientComponent::get_availability() { return this->availability_; }
599  if (this->birth_message_.topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
600  this->availability_.topic = "";
601  return;
602  }
603  this->availability_.topic = this->birth_message_.topic;
606 }
607 
608 void MQTTClientComponent::set_last_will(MQTTMessage &&message) {
609  this->last_will_ = std::move(message);
611 }
612 
613 void MQTTClientComponent::set_birth_message(MQTTMessage &&message) {
614  this->birth_message_ = std::move(message);
616 }
617 
618 void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->shutdown_message_ = std::move(message); }
619 
620 void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
621  MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain,
622  bool discover_ip, bool clean) {
623  this->discovery_info_.prefix = std::move(prefix);
624  this->discovery_info_.discover_ip = discover_ip;
625  this->discovery_info_.unique_id_generator = unique_id_generator;
626  this->discovery_info_.object_id_generator = object_id_generator;
627  this->discovery_info_.retain = retain;
628  this->discovery_info_.clean = clean;
629 }
630 
632 
634  this->discovery_info_ = MQTTDiscoveryInfo{
635  .prefix = "",
636  .retain = false,
637  .discover_ip = false,
638  .clean = false,
639  .unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR,
640  .object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR,
641  };
642 }
644  if (!this->shutdown_message_.topic.empty()) {
645  yield();
646  this->publish(this->shutdown_message_);
647  yield();
648  }
649  this->mqtt_backend_.disconnect();
650 }
651 
653  this->mqtt_backend_.set_on_connect(std::forward<mqtt_on_connect_callback_t>(callback));
654 }
655 
657  this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback));
658 }
659 
660 #if ASYNC_TCP_SSL_ENABLED
661 void MQTTClientComponent::add_ssl_fingerprint(const std::array<uint8_t, SHA1_SIZE> &fingerprint) {
662  this->mqtt_backend_.setSecure(true);
663  this->mqtt_backend_.addServerFingerprint(fingerprint.data());
664 }
665 #endif
666 
667 MQTTClientComponent *global_mqtt_client = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
668 
669 // MQTTMessageTrigger
670 MQTTMessageTrigger::MQTTMessageTrigger(std::string topic) : topic_(std::move(topic)) {}
671 void MQTTMessageTrigger::set_qos(uint8_t qos) { this->qos_ = qos; }
672 void MQTTMessageTrigger::set_payload(const std::string &payload) { this->payload_ = payload; }
674  global_mqtt_client->subscribe(
675  this->topic_,
676  [this](const std::string &topic, const std::string &payload) {
677  if (this->payload_.has_value() && payload != *this->payload_) {
678  return;
679  }
680 
681  this->trigger(payload);
682  },
683  this->qos_);
684 }
686  ESP_LOGCONFIG(TAG, "MQTT Message Trigger:");
687  ESP_LOGCONFIG(TAG, " Topic: '%s'", this->topic_.c_str());
688  ESP_LOGCONFIG(TAG, " QoS: %u", this->qos_);
689 }
691 
692 } // namespace mqtt
693 } // namespace esphome
694 
695 #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
bool clean_session
Whether the session will be cleaned or remembered between connects.
Definition: mqtt_client.h:54
std::string topic
Empty means disabled.
Definition: mqtt_client.h:59
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:85
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:295
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:321
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:285
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:83
std::string prefix
The Home Assistant discovery prefix. Empty means disabled.
Definition: mqtt_client.h:81
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 set_clean_session(bool clean_session) final
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:65
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition: helpers.cpp:691
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:71
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:80
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:288
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:61
std::vector< MQTTSubscription > subscriptions_
Definition: mqtt_client.h:308
MQTTDiscoveryObjectIdGenerator object_id_generator
Definition: mqtt_client.h:86
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:325
uint16_t get_port() const
Definition: api_server.cpp:378
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback)
APIServer * global_api_server
Definition: api_server.cpp:347
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:292
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:82
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