WLED/wled00/mqtt.cpp

199 lines
5.8 KiB
C++
Raw Normal View History

#include "wled.h"
/*
* MQTT communication protocol for home automation
*/
#ifdef WLED_ENABLE_MQTT
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
void parseMQTTBriPayload(char* payload)
{
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);}
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(CALL_MODE_DIRECT_CHANGE);}
else {
uint8_t in = strtoul(payload, NULL, 10);
if (in == 0 && bri > 0) briLast = bri;
bri = in;
stateUpdated(CALL_MODE_DIRECT_CHANGE);
}
}
void onMqttConnect(bool sessionPresent)
{
//(re)subscribe to required topics
char subuf[38];
2019-08-17 12:27:06 +02:00
if (mqttDeviceTopic[0] != 0) {
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttDeviceTopic, 33);
mqtt->subscribe(subuf, 0);
strcat_P(subuf, PSTR("/col"));
mqtt->subscribe(subuf, 0);
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/api"));
mqtt->subscribe(subuf, 0);
}
if (mqttGroupTopic[0] != 0) {
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttGroupTopic, 33);
mqtt->subscribe(subuf, 0);
strcat_P(subuf, PSTR("/col"));
mqtt->subscribe(subuf, 0);
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttGroupTopic, 33);
strcat_P(subuf, PSTR("/api"));
mqtt->subscribe(subuf, 0);
}
usermods.onMqttConnect(sessionPresent);
2019-10-25 00:14:58 +02:00
doPublishMqtt = true;
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("MQTT ready"));
}
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
2023-05-23 18:37:24 +02:00
static char *payloadStr;
2020-09-20 01:18:31 +02:00
DEBUG_PRINT(F("MQTT msg: "));
DEBUG_PRINTLN(topic);
2020-08-20 00:13:06 +02:00
// paranoia check to avoid npe if no payload
if (payload==nullptr) {
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("no payload -> leave"));
2020-08-20 00:13:06 +02:00
return;
}
2023-05-23 18:37:24 +02:00
if (index == 0) { // start (1st partial packet or the only packet)
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
payloadStr = new char[total+1]; // allocate new buffer
}
if (payloadStr == nullptr) return; // buffer not allocated
// copy (partial) packet to buffer and 0-terminate it if it is last packet
char* buff = payloadStr + index;
memcpy(buff, payload, len);
if (index + len >= total) { // at end
payloadStr[total] = '\0'; // terminate c style string
} else {
DEBUG_PRINTLN(F("Partial packet received."));
return; // process next packet
}
DEBUG_PRINTLN(payloadStr);
size_t topicPrefixLen = strlen(mqttDeviceTopic);
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
topic += topicPrefixLen;
} else {
topicPrefixLen = strlen(mqttGroupTopic);
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
topic += topicPrefixLen;
} else {
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
usermods.onMqttMessage(topic, payloadStr);
2021-05-17 10:38:07 +02:00
delete[] payloadStr;
2023-05-23 18:37:24 +02:00
payloadStr = nullptr;
return;
}
}
//Prefix is stripped from the topic at this point
if (strcmp_P(topic, PSTR("/col")) == 0) {
2023-05-23 18:37:24 +02:00
colorFromDecOrHexString(col, payloadStr);
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
2023-05-23 18:37:24 +02:00
if (!requestJSONBufferLock(15)) {
delete[] payloadStr;
payloadStr = nullptr;
return;
}
if (payloadStr[0] == '{') { //JSON API
deserializeJson(doc, payloadStr);
2020-11-12 09:13:08 +01:00
deserializeState(doc.as<JsonObject>());
} else { //HTTP API
2022-02-19 20:34:37 +01:00
String apireq = "win"; apireq += '&'; // reduce flash string usage
2023-05-23 18:37:24 +02:00
apireq += payloadStr;
2020-11-12 09:13:08 +01:00
handleSet(nullptr, apireq);
}
releaseJSONBufferLock();
} else if (strlen(topic) != 0) {
// non standard topic, check with usermods
usermods.onMqttMessage(topic, payloadStr);
} else {
// topmost topic (just wled/MAC)
parseMQTTBriPayload(payloadStr);
}
2021-05-17 10:38:07 +02:00
delete[] payloadStr;
2023-05-23 18:37:24 +02:00
payloadStr = nullptr;
}
void publishMqtt()
{
2019-10-20 17:38:25 +02:00
doPublishMqtt = false;
2020-05-28 02:20:02 +02:00
if (!WLED_MQTT_CONNECTED) return;
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Publish MQTT"));
#ifndef USERMOD_SMARTNEST
char s[10];
char subuf[38];
2019-08-17 12:27:06 +02:00
sprintf_P(s, PSTR("%u"), bri);
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/g"));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2236)
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c"));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2236)
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/status"));
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
2019-11-10 22:13:07 +01:00
char apires[1024]; // allocating 1024 bytes from stack can be risky
XML_response(nullptr, apires);
2021-09-11 01:17:42 +02:00
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/v"));
mqtt->publish(subuf, 0, retainMqttMsg, apires); // optionally retain message (#2236)
#endif
}
//HA autodiscovery was removed in favor of the native integration in HA v0.102.0
bool initMqtt()
{
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
2019-10-18 14:06:07 +02:00
2019-10-20 12:48:29 +02:00
if (mqtt == nullptr) {
mqtt = new AsyncMqttClient();
mqtt->onMessage(onMqttMessage);
mqtt->onConnect(onMqttConnect);
}
2019-05-21 18:50:56 +02:00
if (mqtt->connected()) return true;
2019-08-17 12:27:06 +02:00
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Reconnecting MQTT"));
IPAddress mqttIP;
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
{
mqtt->setServer(mqttIP, mqttPort);
} else {
mqtt->setServer(mqttServer, mqttPort);
}
2019-08-17 12:27:06 +02:00
mqtt->setClientId(mqttClientID);
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
2019-11-10 22:13:07 +01:00
#ifndef USERMOD_SMARTNEST
2021-09-11 01:17:42 +02:00
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
strcat_P(mqttStatusTopic, PSTR("/status"));
2021-05-19 08:22:41 +02:00
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
#endif
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
mqtt->connect();
return true;
}
#endif