MqttClient.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. #include <stddef.h>
  2. #include <MqttClient.h>
  3. #include <TokenIterator.h>
  4. #include <UrlTokenBindings.h>
  5. #include <IntParsing.h>
  6. #include <ArduinoJson.h>
  7. #include <WiFiClient.h>
  8. #include <MiLightRadioConfig.h>
  9. MqttClient::MqttClient(Settings& settings, MiLightClient*& milightClient)
  10. : milightClient(milightClient),
  11. settings(settings),
  12. lastConnectAttempt(0)
  13. {
  14. String strDomain = settings.mqttServer();
  15. this->domain = new char[strDomain.length() + 1];
  16. strcpy(this->domain, strDomain.c_str());
  17. this->mqttClient = new PubSubClient(tcpClient);
  18. }
  19. MqttClient::~MqttClient() {
  20. mqttClient->disconnect();
  21. delete this->domain;
  22. }
  23. void MqttClient::begin() {
  24. #ifdef MQTT_DEBUG
  25. printf_P(
  26. PSTR("MqttClient - Connecting to: %s\nparsed:%s:%u\n"),
  27. settings._mqttServer.c_str(),
  28. settings.mqttServer().c_str(),
  29. settings.mqttPort()
  30. );
  31. #endif
  32. mqttClient->setServer(this->domain, settings.mqttPort());
  33. mqttClient->setCallback(
  34. [this](char* topic, byte* payload, int length) {
  35. this->publishCallback(topic, payload, length);
  36. }
  37. );
  38. reconnect();
  39. }
  40. bool MqttClient::connect() {
  41. char nameBuffer[30];
  42. sprintf_P(nameBuffer, PSTR("milight-hub-%u"), ESP.getChipId());
  43. #ifdef MQTT_DEBUG
  44. Serial.println(F("MqttClient - connecting"));
  45. #endif
  46. if (settings.mqttUsername.length() > 0 && settings.mqttLwtTopic.length() > 0) {
  47. return mqttClient->connect(
  48. nameBuffer,
  49. settings.mqttUsername.c_str(),
  50. settings.mqttPassword.c_str(),
  51. settings.mqttLwtTopic.c_str(),
  52. 2,
  53. true,
  54. settings.mqttLwtMessage.c_str()
  55. );
  56. } else if (settings.mqttUsername.length() > 0) {
  57. return mqttClient->connect(
  58. nameBuffer,
  59. settings.mqttUsername.c_str(),
  60. settings.mqttPassword.c_str()
  61. );
  62. } else if (settings.mqttLwtTopic.length() > 0) {
  63. return mqttClient->connect(
  64. nameBuffer,
  65. settings.mqttLwtTopic.c_str(),
  66. 2,
  67. true,
  68. settings.mqttLwtMessage.c_str()
  69. );
  70. } else {
  71. return mqttClient->connect(nameBuffer);
  72. }
  73. }
  74. void MqttClient::reconnect() {
  75. if (lastConnectAttempt > 0 && (millis() - lastConnectAttempt) < MQTT_CONNECTION_ATTEMPT_FREQUENCY) {
  76. return;
  77. }
  78. if (! mqttClient->connected()) {
  79. if (connect()) {
  80. subscribe();
  81. #ifdef MQTT_DEBUG
  82. Serial.println(F("MqttClient - Successfully connected to MQTT server"));
  83. #endif
  84. } else {
  85. Serial.println(F("ERROR: Failed to connect to MQTT server"));
  86. }
  87. }
  88. lastConnectAttempt = millis();
  89. }
  90. void MqttClient::handleClient() {
  91. reconnect();
  92. mqttClient->loop();
  93. }
  94. void MqttClient::sendUpdate(const MiLightRemoteConfig& remoteConfig, uint16_t deviceId, uint16_t groupId, const char* update) {
  95. publish(settings.mqttUpdateTopicPattern, remoteConfig, deviceId, groupId, update);
  96. }
  97. void MqttClient::sendState(const MiLightRemoteConfig& remoteConfig, uint16_t deviceId, uint16_t groupId, const char* update) {
  98. publish(settings.mqttStateTopicPattern, remoteConfig, deviceId, groupId, update, true);
  99. }
  100. void MqttClient::subscribe() {
  101. String topic = settings.mqttTopicPattern;
  102. topic.replace(":device_id", "+");
  103. topic.replace(":group_id", "+");
  104. topic.replace(":device_type", "+");
  105. #ifdef MQTT_DEBUG
  106. printf_P(PSTR("MqttClient - subscribing to topic: %s\n"), topic.c_str());
  107. #endif
  108. mqttClient->subscribe(topic.c_str());
  109. }
  110. void MqttClient::publish(
  111. const String& _topic,
  112. const MiLightRemoteConfig &remoteConfig,
  113. uint16_t deviceId,
  114. uint16_t groupId,
  115. const char* message,
  116. const bool retain
  117. ) {
  118. if (_topic.length() == 0) {
  119. return;
  120. }
  121. String topic = _topic;
  122. MqttClient::bindTopicString(topic, remoteConfig, deviceId, groupId);
  123. #ifdef MQTT_DEBUG
  124. printf("MqttClient - publishing update to %s\n", topic.c_str());
  125. #endif
  126. mqttClient->publish(topic.c_str(), message, retain);
  127. }
  128. void MqttClient::publishCallback(char* topic, byte* payload, int length) {
  129. uint16_t deviceId = 0;
  130. uint8_t groupId = 0;
  131. const MiLightRemoteConfig* config = &FUT092Config;
  132. char cstrPayload[length + 1];
  133. cstrPayload[length] = 0;
  134. memcpy(cstrPayload, payload, sizeof(byte)*length);
  135. #ifdef MQTT_DEBUG
  136. printf("MqttClient - Got message on topic: %s\n%s\n", topic, cstrPayload);
  137. #endif
  138. char topicPattern[settings.mqttTopicPattern.length()];
  139. strcpy(topicPattern, settings.mqttTopicPattern.c_str());
  140. TokenIterator patternIterator(topicPattern, settings.mqttTopicPattern.length(), '/');
  141. TokenIterator topicIterator(topic, strlen(topic), '/');
  142. UrlTokenBindings tokenBindings(patternIterator, topicIterator);
  143. if (tokenBindings.hasBinding("device_id")) {
  144. deviceId = parseInt<uint16_t>(tokenBindings.get("device_id"));
  145. }
  146. if (tokenBindings.hasBinding("group_id")) {
  147. groupId = parseInt<uint16_t>(tokenBindings.get("group_id"));
  148. }
  149. if (tokenBindings.hasBinding("device_type")) {
  150. config = MiLightRemoteConfig::fromType(tokenBindings.get("device_type"));
  151. if (config == NULL) {
  152. Serial.println(F("MqttClient - ERROR: could not extract device_type from topic"));
  153. return;
  154. }
  155. } else {
  156. Serial.println(F("MqttClient - WARNING: could not find device_type token. Defaulting to FUT092.\n"));
  157. }
  158. StaticJsonBuffer<400> buffer;
  159. JsonObject& obj = buffer.parseObject(cstrPayload);
  160. #ifdef MQTT_DEBUG
  161. printf("MqttClient - device %04X, group %u\n", deviceId, groupId);
  162. #endif
  163. milightClient->prepare(config, deviceId, groupId);
  164. milightClient->update(obj);
  165. }
  166. inline void MqttClient::bindTopicString(
  167. String& topicPattern,
  168. const MiLightRemoteConfig& remoteConfig,
  169. const uint16_t deviceId,
  170. const uint16_t groupId
  171. ) {
  172. String deviceIdHex = String(deviceId, 16);
  173. deviceIdHex.toUpperCase();
  174. deviceIdHex = String("0x") + deviceIdHex;
  175. topicPattern.replace(":device_id", deviceIdHex);
  176. topicPattern.replace(":hex_device_id", deviceIdHex);
  177. topicPattern.replace(":dec_device_id", String(deviceId));
  178. topicPattern.replace(":group_id", String(groupId));
  179. topicPattern.replace(":device_type", remoteConfig.name);
  180. }