MqttClient.cpp 6.5 KB

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