xdrv_02_mqtt.ino 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. /*
  2. xdrv_02_mqtt.ino - mqtt support for Sonoff-Tasmota
  3. Copyright (C) 2018 Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #define XDRV_02 2
  16. /*********************************************************************************************\
  17. * Select ONE of possible MQTT library types below
  18. \*********************************************************************************************/
  19. // Default MQTT driver for both non-TLS and TLS connections. Blocks network if MQTT server is unavailable.
  20. //#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library
  21. // Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support
  22. //#define MQTT_LIBRARY_TYPE MQTT_TASMOTAMQTT // Use TasmotaMqtt library (+4k4 (core 2.3.0), +14k4 (core 2.4.2 lwip2) code, +4k mem) - non-TLS only
  23. // Alternative MQTT driver does not block network when MQTT server is unavailable. TLS should work but needs to be tested.
  24. //#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Use arduino-mqtt (lwmqtt) library (+3k3 code, +2k mem)
  25. #if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete as of v6.2.1.11
  26. #undef MQTT_LIBRARY_TYPE
  27. #define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT
  28. #endif
  29. /*
  30. #if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
  31. #undef MQTT_LIBRARY_TYPE
  32. #define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Obsolete in near future
  33. #endif
  34. */
  35. #ifdef USE_MQTT_TLS
  36. #if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
  37. #undef MQTT_LIBRARY_TYPE
  38. #endif
  39. #ifndef MQTT_LIBRARY_TYPE
  40. #define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library as it only supports TLS
  41. #endif
  42. #endif // USE_MQTT_TLS
  43. /*********************************************************************************************/
  44. #ifdef USE_MQTT_TLS
  45. #ifdef USE_MQTT_TLS_CA_CERT
  46. #include "sonoff_letsencrypt.h" // LetsEncrypt certificate
  47. #endif
  48. WiFiClientSecure EspClient; // Wifi Secure Client
  49. #else
  50. WiFiClient EspClient; // Wifi Client
  51. #endif
  52. enum MqttCommands {
  53. CMND_MQTTHOST, CMND_MQTTPORT, CMND_MQTTRETRY, CMND_STATETEXT, CMND_MQTTFINGERPRINT, CMND_MQTTCLIENT,
  54. CMND_MQTTUSER, CMND_MQTTPASSWORD, CMND_FULLTOPIC, CMND_PREFIX, CMND_GROUPTOPIC, CMND_TOPIC, CMND_PUBLISH,
  55. CMND_BUTTONTOPIC, CMND_SWITCHTOPIC, CMND_BUTTONRETAIN, CMND_SWITCHRETAIN, CMND_POWERRETAIN, CMND_SENSORRETAIN };
  56. const char kMqttCommands[] PROGMEM =
  57. D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTFINGERPRINT "|" D_CMND_MQTTCLIENT "|"
  58. D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|"
  59. D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ;
  60. uint16_t mqtt_retry_counter = 1; // MQTT connection retry counter
  61. uint8_t mqtt_initial_connection_state = 2; // MQTT connection messages state
  62. bool mqtt_connected = false; // MQTT virtual connection status
  63. /*********************************************************************************************\
  64. * MQTT driver specific code need to provide the following functions:
  65. *
  66. * bool MqttIsConnected()
  67. * void MqttDisconnect()
  68. * void MqttSubscribeLib(char *topic)
  69. * bool MqttPublishLib(const char* topic, boolean retained)
  70. * void MqttLoop()
  71. \*********************************************************************************************/
  72. #if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) /***********************************************/
  73. #include <PubSubClient.h>
  74. // Max message size calculated by PubSubClient is (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength)
  75. #if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MIN_MESSZ // If the max message size is too small, throw an error at compile time. See PubSubClient.cpp line 359
  76. #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1000"
  77. #endif
  78. PubSubClient MqttClient(EspClient);
  79. bool MqttIsConnected(void)
  80. {
  81. return MqttClient.connected();
  82. }
  83. void MqttDisconnect(void)
  84. {
  85. MqttClient.disconnect();
  86. }
  87. void MqttSubscribeLib(char *topic)
  88. {
  89. MqttClient.subscribe(topic);
  90. MqttClient.loop(); // Solve LmacRxBlk:1 messages
  91. }
  92. bool MqttPublishLib(const char* topic, boolean retained)
  93. {
  94. bool result = MqttClient.publish(topic, mqtt_data, retained);
  95. yield(); // #3313
  96. return result;
  97. }
  98. void MqttLoop(void)
  99. {
  100. MqttClient.loop();
  101. }
  102. #elif (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) /**********************************************/
  103. #include <TasmotaMqtt.h>
  104. TasmotaMqtt MqttClient;
  105. bool MqttIsConnected(void)
  106. {
  107. return MqttClient.Connected();
  108. }
  109. void MqttDisconnect(void)
  110. {
  111. MqttClient.Disconnect();
  112. }
  113. void MqttDisconnectedCb(void)
  114. {
  115. MqttDisconnected(MqttClient.State()); // status codes are documented in file mqtt.h as tConnState
  116. }
  117. void MqttSubscribeLib(char *topic)
  118. {
  119. MqttClient.Subscribe(topic, 0);
  120. }
  121. bool MqttPublishLib(const char* topic, boolean retained)
  122. {
  123. return MqttClient.Publish(topic, mqtt_data, strlen(mqtt_data), 0, retained);
  124. }
  125. void MqttLoop(void)
  126. {
  127. }
  128. #elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) /**********************************************/
  129. #include <MQTTClient.h>
  130. MQTTClient MqttClient(MQTT_MAX_PACKET_SIZE);
  131. bool MqttIsConnected(void)
  132. {
  133. return MqttClient.connected();
  134. }
  135. void MqttDisconnect(void)
  136. {
  137. MqttClient.disconnect();
  138. }
  139. /*
  140. void MqttMyDataCb(MQTTClient* client, char* topic, char* data, int data_len)
  141. //void MqttMyDataCb(MQTTClient *client, char topic[], char data[], int data_len)
  142. {
  143. // MqttDataHandler((char*)topic, (byte*)data, data_len);
  144. }
  145. */
  146. void MqttMyDataCb(String &topic, String &data)
  147. {
  148. MqttDataHandler((char*)topic.c_str(), (byte*)data.c_str(), data.length());
  149. }
  150. void MqttSubscribeLib(char *topic)
  151. {
  152. MqttClient.subscribe(topic, 0);
  153. }
  154. bool MqttPublishLib(const char* topic, boolean retained)
  155. {
  156. return MqttClient.publish(topic, mqtt_data, strlen(mqtt_data), retained, 0);
  157. }
  158. void MqttLoop(void)
  159. {
  160. MqttClient.loop();
  161. // delay(10);
  162. }
  163. #endif // MQTT_LIBRARY_TYPE
  164. /*********************************************************************************************/
  165. #ifdef USE_DISCOVERY
  166. #ifdef MQTT_HOST_DISCOVERY
  167. boolean MqttDiscoverServer(void)
  168. {
  169. if (!mdns_begun) { return false; }
  170. int n = MDNS.queryService("mqtt", "tcp"); // Search for mqtt service
  171. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n);
  172. AddLog(LOG_LEVEL_INFO);
  173. if (n > 0) {
  174. // Note: current strategy is to get the first MQTT service (even when many are found)
  175. snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(0).toString().c_str());
  176. Settings.mqtt_port = MDNS.port(0);
  177. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"),
  178. MDNS.hostname(0).c_str(), Settings.mqtt_host, Settings.mqtt_port);
  179. AddLog(LOG_LEVEL_INFO);
  180. }
  181. return n > 0;
  182. }
  183. #endif // MQTT_HOST_DISCOVERY
  184. #endif // USE_DISCOVERY
  185. int MqttLibraryType(void)
  186. {
  187. return (int)MQTT_LIBRARY_TYPE;
  188. }
  189. void MqttRetryCounter(uint8_t value)
  190. {
  191. mqtt_retry_counter = value;
  192. }
  193. void MqttSubscribe(char *topic)
  194. {
  195. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic);
  196. AddLog(LOG_LEVEL_DEBUG);
  197. MqttSubscribeLib(topic);
  198. }
  199. void MqttPublishDirect(const char* topic, boolean retained)
  200. {
  201. char sretained[CMDSZ];
  202. char slog_type[10];
  203. ShowFreeMem(PSTR("MqttPublishDirect"));
  204. sretained[0] = '\0';
  205. snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT));
  206. if (Settings.flag.mqtt_enabled) {
  207. if (MqttIsConnected()) {
  208. if (MqttPublishLib(topic, retained)) {
  209. snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT));
  210. if (retained) {
  211. snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")"));
  212. }
  213. }
  214. }
  215. }
  216. snprintf_P(log_data, sizeof(log_data), PSTR("%s%s = %s"), slog_type, (Settings.flag.mqtt_enabled) ? topic : strrchr(topic,'/')+1, mqtt_data);
  217. if (strlen(log_data) >= (sizeof(log_data) - strlen(sretained) -1)) {
  218. log_data[sizeof(log_data) - strlen(sretained) -5] = '\0';
  219. snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data);
  220. }
  221. snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained);
  222. AddLog(LOG_LEVEL_INFO);
  223. if (Settings.ledstate &0x04) {
  224. blinks++;
  225. }
  226. }
  227. void MqttPublish(const char* topic, boolean retained)
  228. {
  229. char *me;
  230. if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1])) {
  231. me = strstr(topic,Settings.mqtt_prefix[0]);
  232. if (me == topic) {
  233. mqtt_cmnd_publish += 3;
  234. }
  235. }
  236. MqttPublishDirect(topic, retained);
  237. }
  238. void MqttPublish(const char* topic)
  239. {
  240. MqttPublish(topic, false);
  241. }
  242. void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, boolean retained)
  243. {
  244. /* prefix 0 = cmnd using subtopic
  245. * prefix 1 = stat using subtopic
  246. * prefix 2 = tele using subtopic
  247. * prefix 4 = cmnd using subtopic or RESULT
  248. * prefix 5 = stat using subtopic or RESULT
  249. * prefix 6 = tele using subtopic or RESULT
  250. */
  251. char romram[33];
  252. char stopic[TOPSZ];
  253. snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic);
  254. for (byte i = 0; i < strlen(romram); i++) {
  255. romram[i] = toupper(romram[i]);
  256. }
  257. prefix &= 3;
  258. GetTopic_P(stopic, prefix, mqtt_topic, romram);
  259. MqttPublish(stopic, retained);
  260. }
  261. void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic)
  262. {
  263. MqttPublishPrefixTopic_P(prefix, subtopic, false);
  264. }
  265. void MqttPublishPowerState(byte device)
  266. {
  267. char stopic[TOPSZ];
  268. char scommand[33];
  269. if ((device < 1) || (device > devices_present)) { device = 1; }
  270. if ((SONOFF_IFAN02 == Settings.module) && (device > 1)) {
  271. if (GetFanspeed() < MAX_FAN_SPEED) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed
  272. #ifdef USE_DOMOTICZ
  273. DomoticzUpdateFanState(); // RC Button feedback
  274. #endif // USE_DOMOTICZ
  275. snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED));
  276. GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT);
  277. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, scommand, GetFanspeed());
  278. MqttPublish(stopic);
  279. }
  280. } else {
  281. GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable);
  282. GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT);
  283. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1)));
  284. MqttPublish(stopic);
  285. GetTopic_P(stopic, STAT, mqtt_topic, scommand);
  286. snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(bitRead(power, device -1)));
  287. MqttPublish(stopic, Settings.flag.mqtt_power_retain);
  288. }
  289. }
  290. void MqttPublishPowerBlinkState(byte device)
  291. {
  292. char scommand[33];
  293. if ((device < 1) || (device > devices_present)) {
  294. device = 1;
  295. }
  296. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_BLINK " %s\"}"),
  297. GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable), GetStateText(bitRead(blink_mask, device -1)));
  298. MqttPublishPrefixTopic_P(RESULT_OR_STAT, S_RSLT_POWER);
  299. }
  300. /*********************************************************************************************/
  301. void MqttDisconnected(int state)
  302. {
  303. mqtt_connected = false;
  304. mqtt_retry_counter = Settings.mqtt_retry;
  305. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND),
  306. Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter);
  307. AddLog(LOG_LEVEL_INFO);
  308. rules_flag.mqtt_disconnected = 1;
  309. }
  310. void MqttConnected(void)
  311. {
  312. char stopic[TOPSZ];
  313. if (Settings.flag.mqtt_enabled) {
  314. AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED));
  315. mqtt_connected = true;
  316. mqtt_retry_counter = 0;
  317. GetTopic_P(stopic, TELE, mqtt_topic, S_LWT);
  318. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_ONLINE));
  319. MqttPublish(stopic, true);
  320. // Satisfy iobroker (#299)
  321. mqtt_data[0] = '\0';
  322. MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER);
  323. GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#"));
  324. MqttSubscribe(stopic);
  325. if (strstr(Settings.mqtt_fulltopic, MQTT_TOKEN_TOPIC) != NULL) {
  326. GetTopic_P(stopic, CMND, Settings.mqtt_grptopic, PSTR("#"));
  327. MqttSubscribe(stopic);
  328. GetFallbackTopic_P(stopic, CMND, PSTR("#"));
  329. MqttSubscribe(stopic);
  330. }
  331. XdrvCall(FUNC_MQTT_SUBSCRIBE);
  332. }
  333. if (mqtt_initial_connection_state) {
  334. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"),
  335. my_module.name, my_version, my_image, GetFallbackTopic_P(stopic, CMND, ""), Settings.mqtt_grptopic);
  336. // my_module.name, my_version, my_image, mqtt_client, Settings.mqtt_grptopic);
  337. MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1"));
  338. #ifdef USE_WEBSERVER
  339. if (Settings.webserver) {
  340. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"),
  341. (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str());
  342. MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2"));
  343. }
  344. #endif // USE_WEBSERVER
  345. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"),
  346. (GetResetReason() == "Exception") ? ESP.getResetInfo().c_str() : GetResetReason().c_str());
  347. MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3"));
  348. for (byte i = 1; i <= devices_present; i++) {
  349. MqttPublishPowerState(i);
  350. if (SONOFF_IFAN02 == Settings.module) { break; } // Report status of light relay only
  351. }
  352. if (Settings.tele_period) { tele_period = Settings.tele_period -9; } // Enable TelePeriod in 9 seconds
  353. rules_flag.system_boot = 1;
  354. XdrvCall(FUNC_MQTT_INIT);
  355. }
  356. mqtt_initial_connection_state = 0;
  357. global_state.mqtt_down = 0;
  358. if (Settings.flag.mqtt_enabled) {
  359. rules_flag.mqtt_connected = 1;
  360. }
  361. }
  362. #ifdef USE_MQTT_TLS
  363. boolean MqttCheckTls(void)
  364. {
  365. char fingerprint1[60];
  366. char fingerprint2[60];
  367. boolean result = false;
  368. fingerprint1[0] = '\0';
  369. fingerprint2[0] = '\0';
  370. for (byte i = 0; i < sizeof(Settings.mqtt_fingerprint[0]); i++) {
  371. snprintf_P(fingerprint1, sizeof(fingerprint1), PSTR("%s%s%02X"), fingerprint1, (i) ? " " : "", Settings.mqtt_fingerprint[0][i]);
  372. snprintf_P(fingerprint2, sizeof(fingerprint2), PSTR("%s%s%02X"), fingerprint2, (i) ? " " : "", Settings.mqtt_fingerprint[1][i]);
  373. }
  374. AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_FINGERPRINT));
  375. //#ifdef ARDUINO_ESP8266_RELEASE_2_4_1
  376. EspClient = WiFiClientSecure(); // Wifi Secure Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497)
  377. //#endif
  378. if (!EspClient.connect(Settings.mqtt_host, Settings.mqtt_port)) {
  379. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_TLS_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND),
  380. Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter);
  381. AddLog(LOG_LEVEL_DEBUG);
  382. } else {
  383. #ifdef USE_MQTT_TLS_CA_CERT
  384. unsigned char tls_ca_cert[] = MQTT_TLS_CA_CERT;
  385. if (EspClient.setCACert(tls_ca_cert, MQTT_TLS_CA_CERT_LENGTH)) {
  386. if (EspClient.verifyCertChain(Settings.mqtt_host)) {
  387. AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "CA"));
  388. result = true;
  389. }
  390. }
  391. #else
  392. if (EspClient.verify(fingerprint1, Settings.mqtt_host)) {
  393. AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "1"));
  394. result = true;
  395. }
  396. else if (EspClient.verify(fingerprint2, Settings.mqtt_host)) {
  397. AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "2"));
  398. result = true;
  399. }
  400. #endif
  401. }
  402. if (!result) AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_FAILED));
  403. EspClient.stop();
  404. yield();
  405. return result;
  406. }
  407. #endif // USE_MQTT_TLS
  408. void MqttReconnect(void)
  409. {
  410. char stopic[TOPSZ];
  411. if (!Settings.flag.mqtt_enabled) {
  412. MqttConnected();
  413. return;
  414. }
  415. #if defined(USE_WEBSERVER) && defined(USE_EMULATION)
  416. UdpDisconnect();
  417. #endif // USE_EMULATION
  418. AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION));
  419. mqtt_connected = false;
  420. mqtt_retry_counter = Settings.mqtt_retry;
  421. global_state.mqtt_down = 1;
  422. #ifndef USE_MQTT_TLS
  423. #ifdef USE_DISCOVERY
  424. #ifdef MQTT_HOST_DISCOVERY
  425. if (!strlen(Settings.mqtt_host) && !MqttDiscoverServer()) { return; }
  426. #endif // MQTT_HOST_DISCOVERY
  427. #endif // USE_DISCOVERY
  428. #endif // USE_MQTT_TLS
  429. char *mqtt_user = NULL;
  430. char *mqtt_pwd = NULL;
  431. if (strlen(Settings.mqtt_user) > 0) mqtt_user = Settings.mqtt_user;
  432. if (strlen(Settings.mqtt_pwd) > 0) mqtt_pwd = Settings.mqtt_pwd;
  433. GetTopic_P(stopic, TELE, mqtt_topic, S_LWT);
  434. snprintf_P(mqtt_data, sizeof(mqtt_data), S_OFFLINE);
  435. //#ifdef ARDUINO_ESP8266_RELEASE_2_4_1
  436. #ifdef USE_MQTT_TLS
  437. EspClient = WiFiClientSecure(); // Wifi Secure Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497)
  438. #else
  439. EspClient = WiFiClient(); // Wifi Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497)
  440. #endif
  441. //#endif
  442. if (2 == mqtt_initial_connection_state) { // Executed once just after power on and wifi is connected
  443. #ifdef USE_MQTT_TLS
  444. if (!MqttCheckTls()) return;
  445. #endif // USE_MQTT_TLS
  446. #if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
  447. MqttClient.InitConnection(Settings.mqtt_host, Settings.mqtt_port);
  448. MqttClient.InitClient(mqtt_client, mqtt_user, mqtt_pwd, MQTT_KEEPALIVE);
  449. MqttClient.InitLWT(stopic, mqtt_data, 1, true);
  450. MqttClient.OnConnected(MqttConnected);
  451. MqttClient.OnDisconnected(MqttDisconnectedCb);
  452. MqttClient.OnData(MqttDataHandler);
  453. #elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT)
  454. MqttClient.begin(Settings.mqtt_host, Settings.mqtt_port, EspClient);
  455. MqttClient.setWill(stopic, mqtt_data, true, 1);
  456. MqttClient.setOptions(MQTT_KEEPALIVE, true, MQTT_TIMEOUT);
  457. // MqttClient.onMessageAdvanced(MqttMyDataCb);
  458. MqttClient.onMessage(MqttMyDataCb);
  459. #endif
  460. mqtt_initial_connection_state = 1;
  461. }
  462. #if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT)
  463. MqttClient.setCallback(MqttDataHandler);
  464. MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port);
  465. if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data)) {
  466. MqttConnected();
  467. } else {
  468. MqttDisconnected(MqttClient.state()); // status codes are documented here http://pubsubclient.knolleary.net/api.html#state
  469. }
  470. #elif (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
  471. MqttClient.Connect();
  472. #elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT)
  473. if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd)) {
  474. MqttConnected();
  475. } else {
  476. MqttDisconnected(MqttClient.lastError()); // status codes are documented here https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11
  477. }
  478. #endif // MQTT_LIBRARY_TYPE
  479. }
  480. void MqttCheck(void)
  481. {
  482. if (Settings.flag.mqtt_enabled) {
  483. if (!MqttIsConnected()) {
  484. global_state.mqtt_down = 1;
  485. if (!mqtt_retry_counter) {
  486. #ifndef USE_MQTT_TLS
  487. #ifdef USE_DISCOVERY
  488. #ifdef MQTT_HOST_DISCOVERY
  489. if (!strlen(Settings.mqtt_host) && !mdns_begun) { return; }
  490. #endif // MQTT_HOST_DISCOVERY
  491. #endif // USE_DISCOVERY
  492. #endif // USE_MQTT_TLS
  493. MqttReconnect();
  494. } else {
  495. mqtt_retry_counter--;
  496. }
  497. } else {
  498. global_state.mqtt_down = 0;
  499. }
  500. } else {
  501. global_state.mqtt_down = 0;
  502. if (mqtt_initial_connection_state) MqttReconnect();
  503. }
  504. }
  505. /*********************************************************************************************/
  506. bool MqttCommand(void)
  507. {
  508. char command [CMDSZ];
  509. bool serviced = true;
  510. char stemp1[TOPSZ];
  511. char scommand[CMDSZ];
  512. uint16_t i;
  513. uint16_t index = XdrvMailbox.index;
  514. uint16_t data_len = XdrvMailbox.data_len;
  515. uint16_t payload16 = XdrvMailbox.payload16;
  516. int16_t payload = XdrvMailbox.payload;
  517. uint8_t grpflg = XdrvMailbox.grpflg;
  518. char *type = XdrvMailbox.topic;
  519. char *dataBuf = XdrvMailbox.data;
  520. int command_code = GetCommandCode(command, sizeof(command), type, kMqttCommands);
  521. if (-1 == command_code) {
  522. serviced = false; // Unknown command
  523. }
  524. else if (CMND_MQTTHOST == command_code) {
  525. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_host))) {
  526. strlcpy(Settings.mqtt_host, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_HOST : dataBuf, sizeof(Settings.mqtt_host));
  527. restart_flag = 2;
  528. }
  529. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_host);
  530. }
  531. else if (CMND_MQTTPORT == command_code) {
  532. if (payload16 > 0) {
  533. Settings.mqtt_port = (1 == payload16) ? MQTT_PORT : payload16;
  534. restart_flag = 2;
  535. }
  536. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.mqtt_port);
  537. }
  538. else if (CMND_MQTTRETRY == command_code) {
  539. if ((payload >= MQTT_RETRY_SECS) && (payload < 32001)) {
  540. Settings.mqtt_retry = payload;
  541. mqtt_retry_counter = Settings.mqtt_retry;
  542. }
  543. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.mqtt_retry);
  544. }
  545. else if ((CMND_STATETEXT == command_code) && (index > 0) && (index <= 4)) {
  546. if ((data_len > 0) && (data_len < sizeof(Settings.state_text[0]))) {
  547. for(i = 0; i <= data_len; i++) {
  548. if (dataBuf[i] == ' ') dataBuf[i] = '_';
  549. }
  550. strlcpy(Settings.state_text[index -1], dataBuf, sizeof(Settings.state_text[0]));
  551. }
  552. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(index -1));
  553. }
  554. #ifdef USE_MQTT_TLS
  555. else if ((CMND_MQTTFINGERPRINT == command_code) && (index > 0) && (index <= 2)) {
  556. char fingerprint[60];
  557. if ((data_len > 0) && (data_len < sizeof(fingerprint))) {
  558. strlcpy(fingerprint, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : dataBuf, sizeof(fingerprint));
  559. char *p = fingerprint;
  560. for (byte i = 0; i < 20; i++) {
  561. Settings.mqtt_fingerprint[index -1][i] = strtol(p, &p, 16);
  562. }
  563. restart_flag = 2;
  564. }
  565. fingerprint[0] = '\0';
  566. for (byte i = 0; i < sizeof(Settings.mqtt_fingerprint[index -1]); i++) {
  567. snprintf_P(fingerprint, sizeof(fingerprint), PSTR("%s%s%02X"), fingerprint, (i) ? " " : "", Settings.mqtt_fingerprint[index -1][i]);
  568. }
  569. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, fingerprint);
  570. }
  571. #endif
  572. else if ((CMND_MQTTCLIENT == command_code) && !grpflg) {
  573. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_client))) {
  574. strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_CLIENT_ID : dataBuf, sizeof(Settings.mqtt_client));
  575. restart_flag = 2;
  576. }
  577. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_client);
  578. }
  579. else if (CMND_MQTTUSER == command_code) {
  580. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_user))) {
  581. strlcpy(Settings.mqtt_user, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_USER : dataBuf, sizeof(Settings.mqtt_user));
  582. restart_flag = 2;
  583. }
  584. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_user);
  585. }
  586. else if (CMND_MQTTPASSWORD == command_code) {
  587. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_pwd))) {
  588. strlcpy(Settings.mqtt_pwd, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_PASS : dataBuf, sizeof(Settings.mqtt_pwd));
  589. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_pwd);
  590. restart_flag = 2;
  591. } else {
  592. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_ASTERIX, command);
  593. }
  594. }
  595. else if (CMND_FULLTOPIC == command_code) {
  596. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_fulltopic))) {
  597. MakeValidMqtt(1, dataBuf);
  598. if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
  599. strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_FULLTOPIC : dataBuf, sizeof(stemp1));
  600. if (strcmp(stemp1, Settings.mqtt_fulltopic)) {
  601. snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : "");
  602. MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic
  603. strlcpy(Settings.mqtt_fulltopic, stemp1, sizeof(Settings.mqtt_fulltopic));
  604. restart_flag = 2;
  605. }
  606. }
  607. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_fulltopic);
  608. }
  609. else if ((CMND_PREFIX == command_code) && (index > 0) && (index <= 3)) {
  610. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_prefix[0]))) {
  611. MakeValidMqtt(0, dataBuf);
  612. strlcpy(Settings.mqtt_prefix[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? (1==index)?SUB_PREFIX:(2==index)?PUB_PREFIX:PUB_PREFIX2 : dataBuf, sizeof(Settings.mqtt_prefix[0]));
  613. // if (Settings.mqtt_prefix[index -1][strlen(Settings.mqtt_prefix[index -1])] == '/') Settings.mqtt_prefix[index -1][strlen(Settings.mqtt_prefix[index -1])] = 0;
  614. restart_flag = 2;
  615. }
  616. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mqtt_prefix[index -1]);
  617. }
  618. else if (CMND_PUBLISH == command_code) {
  619. if (data_len > 0) {
  620. char *mqtt_part = strtok(dataBuf, " ");
  621. if (mqtt_part) {
  622. snprintf(stemp1, sizeof(stemp1), mqtt_part);
  623. mqtt_part = strtok(NULL, " ");
  624. if (mqtt_part) {
  625. snprintf(mqtt_data, sizeof(mqtt_data), mqtt_part);
  626. } else {
  627. mqtt_data[0] = '\0';
  628. }
  629. MqttPublishDirect(stemp1, (index == 2));
  630. // snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
  631. mqtt_data[0] = '\0';
  632. }
  633. }
  634. }
  635. else if (CMND_GROUPTOPIC == command_code) {
  636. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_grptopic))) {
  637. MakeValidMqtt(0, dataBuf);
  638. if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
  639. strlcpy(Settings.mqtt_grptopic, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_GRPTOPIC : dataBuf, sizeof(Settings.mqtt_grptopic));
  640. restart_flag = 2;
  641. }
  642. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_grptopic);
  643. }
  644. else if ((CMND_TOPIC == command_code) && !grpflg) {
  645. if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_topic))) {
  646. MakeValidMqtt(0, dataBuf);
  647. if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
  648. strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_TOPIC : dataBuf, sizeof(stemp1));
  649. if (strcmp(stemp1, Settings.mqtt_topic)) {
  650. snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : "");
  651. MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic
  652. strlcpy(Settings.mqtt_topic, stemp1, sizeof(Settings.mqtt_topic));
  653. restart_flag = 2;
  654. }
  655. }
  656. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_topic);
  657. }
  658. else if ((CMND_BUTTONTOPIC == command_code) && !grpflg) {
  659. if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) {
  660. MakeValidMqtt(0, dataBuf);
  661. if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
  662. switch (Shortcut(dataBuf)) {
  663. case SC_CLEAR: strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic)); break;
  664. case SC_DEFAULT: strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic)); break;
  665. case SC_USER: strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); break;
  666. default: strlcpy(Settings.button_topic, dataBuf, sizeof(Settings.button_topic));
  667. }
  668. }
  669. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.button_topic);
  670. }
  671. else if (CMND_SWITCHTOPIC == command_code) {
  672. if ((data_len > 0) && (data_len < sizeof(Settings.switch_topic))) {
  673. MakeValidMqtt(0, dataBuf);
  674. if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
  675. switch (Shortcut(dataBuf)) {
  676. case SC_CLEAR: strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic)); break;
  677. case SC_DEFAULT: strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic)); break;
  678. case SC_USER: strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); break;
  679. default: strlcpy(Settings.switch_topic, dataBuf, sizeof(Settings.switch_topic));
  680. }
  681. }
  682. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.switch_topic);
  683. }
  684. else if (CMND_BUTTONRETAIN == command_code) {
  685. if ((payload >= 0) && (payload <= 1)) {
  686. if (!payload) {
  687. for(i = 1; i <= MAX_KEYS; i++) {
  688. SendKey(0, i, 9); // Clear MQTT retain in broker
  689. }
  690. }
  691. Settings.flag.mqtt_button_retain = payload;
  692. }
  693. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_button_retain));
  694. }
  695. else if (CMND_SWITCHRETAIN == command_code) {
  696. if ((payload >= 0) && (payload <= 1)) {
  697. if (!payload) {
  698. for(i = 1; i <= MAX_SWITCHES; i++) {
  699. SendKey(1, i, 9); // Clear MQTT retain in broker
  700. }
  701. }
  702. Settings.flag.mqtt_switch_retain = payload;
  703. }
  704. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_switch_retain));
  705. }
  706. else if (CMND_POWERRETAIN == command_code) {
  707. if ((payload >= 0) && (payload <= 1)) {
  708. if (!payload) {
  709. for(i = 1; i <= devices_present; i++) { // Clear MQTT retain in broker
  710. GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable));
  711. mqtt_data[0] = '\0';
  712. MqttPublish(stemp1, Settings.flag.mqtt_power_retain);
  713. }
  714. }
  715. Settings.flag.mqtt_power_retain = payload;
  716. }
  717. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_power_retain));
  718. }
  719. else if (CMND_SENSORRETAIN == command_code) {
  720. if ((payload >= 0) && (payload <= 1)) {
  721. if (!payload) {
  722. mqtt_data[0] = '\0';
  723. MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
  724. MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain);
  725. }
  726. Settings.flag.mqtt_sensor_retain = payload;
  727. }
  728. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_sensor_retain));
  729. }
  730. else serviced = false; // Unknown command
  731. return serviced;
  732. }
  733. /*********************************************************************************************\
  734. * Presentation
  735. \*********************************************************************************************/
  736. #ifdef USE_WEBSERVER
  737. #define WEB_HANDLE_MQTT "mq"
  738. const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT;
  739. const char HTTP_BTN_MENU_MQTT[] PROGMEM =
  740. "<br/><form action='" WEB_HANDLE_MQTT "' method='get'><button>" D_CONFIGURE_MQTT "</button></form>";
  741. const char HTTP_FORM_MQTT[] PROGMEM =
  742. "<fieldset><legend><b>&nbsp;" D_MQTT_PARAMETERS "&nbsp;</b></legend><form method='get' action='" WEB_HANDLE_MQTT "'>"
  743. "<br/><b>" D_HOST "</b> (" MQTT_HOST ")<br/><input id='mh' name='mh' placeholder='" MQTT_HOST" ' value='{m1'><br/>"
  744. "<br/><b>" D_PORT "</b> (" STR(MQTT_PORT) ")<br/><input id='ml' name='ml' placeholder='" STR(MQTT_PORT) "' value='{m2'><br/>"
  745. "<br/><b>" D_CLIENT "</b> ({m0)<br/><input id='mc' name='mc' placeholder='" MQTT_CLIENT_ID "' value='{m3'><br/>"
  746. "<br/><b>" D_USER "</b> (" MQTT_USER ")<br/><input id='mu' name='mu' placeholder='" MQTT_USER "' value='{m4'><br/>"
  747. "<br/><b>" D_PASSWORD "</b><br/><input id='mp' name='mp' type='password' placeholder='" D_PASSWORD "' value='" D_ASTERIX "'><br/>"
  748. "<br/><b>" D_TOPIC "</b> = %topic% (" MQTT_TOPIC ")<br/><input id='mt' name='mt' placeholder='" MQTT_TOPIC" ' value='{m6'><br/>"
  749. "<br/><b>" D_FULL_TOPIC "</b> (" MQTT_FULLTOPIC ")<br/><input id='mf' name='mf' placeholder='" MQTT_FULLTOPIC" ' value='{m7'><br/>";
  750. void HandleMqttConfiguration(void)
  751. {
  752. if (HttpUser()) { return; }
  753. if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
  754. AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT);
  755. if (WebServer->hasArg("save")) {
  756. MqttSaveSettings();
  757. WebRestart(1);
  758. return;
  759. }
  760. String page = FPSTR(HTTP_HEAD);
  761. page.replace(F("{v}"), FPSTR(S_CONFIGURE_MQTT));
  762. page += FPSTR(HTTP_HEAD_STYLE);
  763. page += FPSTR(HTTP_FORM_MQTT);
  764. char str[sizeof(Settings.mqtt_client)];
  765. page.replace(F("{m0"), Format(str, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)));
  766. page.replace(F("{m1"), Settings.mqtt_host);
  767. page.replace(F("{m2"), String(Settings.mqtt_port));
  768. page.replace(F("{m3"), Settings.mqtt_client);
  769. page.replace(F("{m4"), (Settings.mqtt_user[0] == '\0')?"0":Settings.mqtt_user);
  770. page.replace(F("{m6"), Settings.mqtt_topic);
  771. page.replace(F("{m7"), Settings.mqtt_fulltopic);
  772. page += FPSTR(HTTP_FORM_END);
  773. page += FPSTR(HTTP_BTN_CONF);
  774. ShowPage(page);
  775. }
  776. void MqttSaveSettings(void)
  777. {
  778. char tmp[100];
  779. char stemp[TOPSZ];
  780. char stemp2[TOPSZ];
  781. WebGetArg("mt", tmp, sizeof(tmp));
  782. strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp));
  783. MakeValidMqtt(0, stemp);
  784. WebGetArg("mf", tmp, sizeof(tmp));
  785. strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2));
  786. MakeValidMqtt(1,stemp2);
  787. if ((strcmp(stemp, Settings.mqtt_topic)) || (strcmp(stemp2, Settings.mqtt_fulltopic))) {
  788. snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : "");
  789. MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic
  790. }
  791. strlcpy(Settings.mqtt_topic, stemp, sizeof(Settings.mqtt_topic));
  792. strlcpy(Settings.mqtt_fulltopic, stemp2, sizeof(Settings.mqtt_fulltopic));
  793. WebGetArg("mh", tmp, sizeof(tmp));
  794. strlcpy(Settings.mqtt_host, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_host));
  795. WebGetArg("ml", tmp, sizeof(tmp));
  796. Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp);
  797. WebGetArg("mc", tmp, sizeof(tmp));
  798. strlcpy(Settings.mqtt_client, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp, sizeof(Settings.mqtt_client));
  799. WebGetArg("mu", tmp, sizeof(tmp));
  800. strlcpy(Settings.mqtt_user, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_user));
  801. WebGetArg("mp", tmp, sizeof(tmp));
  802. strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.mqtt_pwd : tmp, sizeof(Settings.mqtt_pwd));
  803. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"),
  804. Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_topic, Settings.mqtt_fulltopic);
  805. AddLog(LOG_LEVEL_INFO);
  806. }
  807. #endif // USE_WEBSERVER
  808. /*********************************************************************************************\
  809. * Interface
  810. \*********************************************************************************************/
  811. boolean Xdrv02(byte function)
  812. {
  813. boolean result = false;
  814. if (Settings.flag.mqtt_enabled) {
  815. switch (function) {
  816. #ifdef USE_WEBSERVER
  817. case FUNC_WEB_ADD_BUTTON:
  818. strncat_P(mqtt_data, HTTP_BTN_MENU_MQTT, sizeof(mqtt_data) - strlen(mqtt_data) -1);
  819. break;
  820. case FUNC_WEB_ADD_HANDLER:
  821. WebServer->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration);
  822. break;
  823. #endif // USE_WEBSERVER
  824. case FUNC_LOOP:
  825. if (!global_state.mqtt_down) { MqttLoop(); }
  826. break;
  827. case FUNC_COMMAND:
  828. result = MqttCommand();
  829. break;
  830. }
  831. }
  832. return result;
  833. }