MqttClient.cpp 4.7 KB

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