WLED/wled00/wled17_mqtt.ino
Debashish Sahu 2fafe42c18 HA Light Auto Discovery
- Send HA MQTT Discovery message ~2.4kB based on input by @YeonV from here: https://github.com/Aircoookie/WLED/issues/131
2019-03-18 12:23:39 -04:00

359 lines
9.9 KiB
C++

/*
* MQTT communication protocol for home automation
*/
#define WLED_MQTT_PORT 1883
void parseMQTTBriPayload(char* payload)
{
if (strcmp(payload, "ON") == 0 || strcmp(payload, "on") == 0) {bri = briLast; colorUpdated(1);}
else if (strcmp(payload, "T" ) == 0 || strcmp(payload, "t" ) == 0) {toggleOnOff(); colorUpdated(1);}
else {
uint8_t in = strtoul(payload, NULL, 10);
if (in == 0 && bri > 0) briLast = bri;
bri = in;
colorUpdated(1);
}
}
void onMqttConnect(bool sessionPresent)
{
//(re)subscribe to required topics
char subuf[38];
strcpy(subuf, mqttDeviceTopic);
if (mqttDeviceTopic[0] != 0)
{
strcpy(subuf, mqttDeviceTopic);
mqtt->subscribe(subuf, 0);
strcat(subuf, "/col");
mqtt->subscribe(subuf, 0);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/api");
mqtt->subscribe(subuf, 0);
}
if (mqttGroupTopic[0] != 0)
{
strcpy(subuf, mqttGroupTopic);
mqtt->subscribe(subuf, 0);
strcat(subuf, "/col");
mqtt->subscribe(subuf, 0);
strcpy(subuf, mqttGroupTopic);
strcat(subuf, "/api");
mqtt->subscribe(subuf, 0);
}
sendHADiscoveryMQTT();
publishMqtt();
}
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
DEBUG_PRINT("MQTT callb rec: ");
DEBUG_PRINTLN(topic);
DEBUG_PRINTLN(payload);
//no need to check the topic because we only get topics we are subscribed to
if (strstr(topic, "/col"))
{
colorFromDecOrHexString(col, (char*)payload);
colorUpdated(1);
} else if (strstr(topic, "/api"))
{
String apireq = "win&";
apireq += (char*)payload;
handleSet(nullptr, apireq);
} else parseMQTTBriPayload(payload);
}
void publishMqtt()
{
if (mqtt == NULL) return;
if (!mqtt->connected()) return;
DEBUG_PRINTLN("Publish MQTT");
char s[10];
char subuf[38];
sprintf(s, "%ld", bri);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/g");
mqtt->publish(subuf, 0, true, s);
sprintf(s, "#%X", col[3]*16777216 + col[0]*65536 + col[1]*256 + col[2]);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/c");
mqtt->publish(subuf, 0, true, s);
char apires[1024];
XML_response(nullptr, false, apires);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/v");
mqtt->publish(subuf, 0, true, apires);
}
void sendHADiscoveryMQTT(){
/*
YYYY is discovery tipic
XXXX is device name
Send out HA MQTT Discovery message on MQTT connect (~2.4kB):
{
"name": "XXXX",
"stat_t":"YYYY/c",
"cmd_t":"YYYY",
"rgb_stat_t":"YYYY/c",
"rgb_cmd_t":"YYYY/col",
"bri_cmd_t":"YYYY",
"bri_stat_t":"YYYY/g",
"bri_val_tpl":"{{value}}",
"rgb_cmd_tpl":"{{'#%02x%02x%02x' | format(red, green, blue)}}",
"rgb_val_tpl":"{{value[1:3]|int(base=16)}},{{value[3:5]|int(base=16)}},{{value[5:7]|int(base=16)}}",
"qos": 0,
"opt":true,
"pl_on": "ON",
"pl_off": "OFF",
"fx_cmd_t":"YYYY/api",
"fx_stat_t":"YYYY/api",
"fx_val_tpl":"{{value}}",
"fx_list":[
"[FX=00] STATIC",
"[FX=01] BLINK",
"[FX=02] BREATH",
"[FX=03] COLOR_WIPE",
"[FX=04] COLOR_WIPE_RANDOM",
"[FX=05] RANDOM_COLOR",
"[FX=06] COLOR_SWEEP",
"[FX=07] DYNAMIC",
"[FX=08] RAINBOW",
"[FX=09] RAINBOW_CYCLE",
"[FX=10] SCAN",
"[FX=11] DUAL_SCAN",
"[FX=12] FADE",
"[FX=13] THEATER_CHASE",
"[FX=14] THEATER_C_RAINBOW",
"[FX=15] RUNNING_LIGHTS",
"[FX=16] SAW",
"[FX=17] TWINKLE",
"[FX=18] DISSOLVE",
"[FX=19] DISSOLVE_RANDOM",
"[FX=20] SPARKLE",
"[FX=21] FLASH_SPARKLE",
"[FX=22] HYPER_SPARKLE",
"[FX=23] STROBE",
"[FX=24] STROBE_RAINBOW",
"[FX=25] MULTI_STROBE",
"[FX=26] BLINK_RAINBOW",
"[FX=27] ANDROID",
"[FX=28] CHASE_COLOR",
"[FX=29] CHASE_RANDOM",
"[FX=30] CHASE_RAINBOW",
"[FX=31] CHASE_FLASH",
"[FX=32] CHASE_FLASH_RANDOM",
"[FX=33] CHASE_RAINBOW_WHITE",
"[FX=34] COLORFUL",
"[FX=35] TRAFFIC_LIGHT",
"[FX=36] COLOR_SWEEP_RANDOM",
"[FX=37] RUNNING_COLOR",
"[FX=38] RUNNING_RED_BLUE",
"[FX=39] RUNNING_RANDOM",
"[FX=40] LARSON_SCANNER",
"[FX=41] COMET",
"[FX=42] FIREWORKS",
"[FX=43] RAIN",
"[FX=44] MERRY_CHRISTMAS",
"[FX=45] FIRE_FLICKER",
"[FX=46] GRADIENT",
"[FX=47] LOADING",
"[FX=48] DUAL_COLOR_WIPE_IN_OUT",
"[FX=49] DUAL_COLOR_WIPE_IN_IN",
"[FX=50] DUAL_COLOR_WIPE_OUT_OUT",
"[FX=51] DUAL_COLOR_WIPE_OUT_IN",
"[FX=52] CIRCUS_COMBUSTUS",
"[FX=53] HALLOWEEN",
"[FX=54] TRICOLOR_CHASE",
"[FX=55] TRICOLOR_WIPE",
"[FX=56] TRICOLOR_FADE",
"[FX=57] LIGHTNING",
"[FX=58] ICU",
"[FX=59] MULTI_COMET",
"[FX=60] DUAL_LARSON_SCANNER",
"[FX=61] RANDOM_CHASE",
"[FX=62] OSCILLATE",
"[FX=63] PRIDE_2015",
"[FX=64] JUGGLE",
"[FX=65] PALETTE",
"[FX=66] FIRE_2012",
"[FX=67] COLORWAVES",
"[FX=68] BPM",
"[FX=69] FILLNOISE8",
"[FX=70] NOISE16_1",
"[FX=71] NOISE16_2",
"[FX=72] NOISE16_3",
"[FX=73] NOISE16_4",
"[FX=74] COLORTWINKLE",
"[FX=75] LAKE",
"[FX=76] METEOR",
"[FX=77] METEOR_SMOOTH",
"[FX=78] RAILWAY",
"[FX=79] RIPPLE"
]
}
*/
char bufc[38], bufcol[38], bufg[38], bufapi[38];
strcpy(bufc, mqttDeviceTopic);
strcpy(bufcol, mqttDeviceTopic);
strcpy(bufg, mqttDeviceTopic);
strcpy(bufapi, mqttDeviceTopic);
strcat(bufc, "/c");
strcat(bufcol, "/col");
strcat(bufg, "/g");
strcat(bufapi, "/api");
DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(80) + JSON_OBJECT_SIZE(18) + 3000);
JsonObject& root = jsonBuffer.createObject();
root["name"] = serverDescription;
root["stat_t"] = bufc;
root["cmd_t"] = mqttDeviceTopic;
root["rgb_stat_t"] = bufc;
root["rgb_cmd_t"] = bufcol;
root["bri_cmd_t"] = mqttDeviceTopic;
root["bri_stat_t"] = bufg;
root["bri_val_tpl"] = "{{value}}";
root["rgb_cmd_tpl"] = "{{'#%02x%02x%02x' | format(red, green, blue)}}";
root["rgb_val_tpl"] = "{{value[1:3]|int(base=16)}},{{value[3:5]|int(base=16)}},{{value[5:7]|int(base=16)}}";
root["qos"] = 0;
root["opt"] = true;
root["pl_on"] = "ON";
root["pl_off"] = "OFF";
root["fx_cmd_t"] = bufapi;
root["fx_stat_t"] = bufapi;
root["fx_val_tpl"] = "{{value}}";
JsonArray& fx_list = root.createNestedArray("fx_list");
// There should be a better way to do this like a for loop but there is no WS2812FX::getModeCount() or WS2812FX::getModeName(), instead there is "JSON_mode_names"
fx_list.add("[FX=00] STATIC");
fx_list.add("[FX=01] BLINK");
fx_list.add("[FX=02] BREATH");
fx_list.add("[FX=03] COLOR_WIPE");
fx_list.add("[FX=04] COLOR_WIPE_RANDOM");
fx_list.add("[FX=05] RANDOM_COLOR");
fx_list.add("[FX=06] COLOR_SWEEP");
fx_list.add("[FX=07] DYNAMIC");
fx_list.add("[FX=08] RAINBOW");
fx_list.add("[FX=09] RAINBOW_CYCLE");
fx_list.add("[FX=10] SCAN");
fx_list.add("[FX=11] DUAL_SCAN");
fx_list.add("[FX=12] FADE");
fx_list.add("[FX=13] THEATER_CHASE");
fx_list.add("[FX=14] THEATER_C_RAINBOW");
fx_list.add("[FX=15] RUNNING_LIGHTS");
fx_list.add("[FX=16] SAW");
fx_list.add("[FX=17] TWINKLE");
fx_list.add("[FX=18] DISSOLVE");
fx_list.add("[FX=19] DISSOLVE_RANDOM");
fx_list.add("[FX=20] SPARKLE");
fx_list.add("[FX=21] FLASH_SPARKLE");
fx_list.add("[FX=22] HYPER_SPARKLE");
fx_list.add("[FX=23] STROBE");
fx_list.add("[FX=24] STROBE_RAINBOW");
fx_list.add("[FX=25] MULTI_STROBE");
fx_list.add("[FX=26] BLINK_RAINBOW");
fx_list.add("[FX=27] ANDROID");
fx_list.add("[FX=28] CHASE_COLOR");
fx_list.add("[FX=29] CHASE_RANDOM");
fx_list.add("[FX=30] CHASE_RAINBOW");
fx_list.add("[FX=31] CHASE_FLASH");
fx_list.add("[FX=32] CHASE_FLASH_RANDOM");
fx_list.add("[FX=33] CHASE_RAINBOW_WHITE");
fx_list.add("[FX=34] COLORFUL");
fx_list.add("[FX=35] TRAFFIC_LIGHT");
fx_list.add("[FX=36] COLOR_SWEEP_RANDOM");
fx_list.add("[FX=37] RUNNING_COLOR");
fx_list.add("[FX=38] RUNNING_RED_BLUE");
fx_list.add("[FX=39] RUNNING_RANDOM");
fx_list.add("[FX=40] LARSON_SCANNER");
fx_list.add("[FX=41] COMET");
fx_list.add("[FX=42] FIREWORKS");
fx_list.add("[FX=43] RAIN");
fx_list.add("[FX=44] MERRY_CHRISTMAS");
fx_list.add("[FX=45] FIRE_FLICKER");
fx_list.add("[FX=46] GRADIENT");
fx_list.add("[FX=47] LOADING");
fx_list.add("[FX=48] DUAL_COLOR_WIPE_IN_OUT");
fx_list.add("[FX=49] DUAL_COLOR_WIPE_IN_IN");
fx_list.add("[FX=50] DUAL_COLOR_WIPE_OUT_OUT");
fx_list.add("[FX=51] DUAL_COLOR_WIPE_OUT_IN");
fx_list.add("[FX=52] CIRCUS_COMBUSTUS");
fx_list.add("[FX=53] HALLOWEEN");
fx_list.add("[FX=54] TRICOLOR_CHASE");
fx_list.add("[FX=55] TRICOLOR_WIPE");
fx_list.add("[FX=56] TRICOLOR_FADE");
fx_list.add("[FX=57] LIGHTNING");
fx_list.add("[FX=58] ICU");
fx_list.add("[FX=59] MULTI_COMET");
fx_list.add("[FX=60] DUAL_LARSON_SCANNER");
fx_list.add("[FX=61] RANDOM_CHASE");
fx_list.add("[FX=62] OSCILLATE");
fx_list.add("[FX=63] PRIDE_2015");
fx_list.add("[FX=64] JUGGLE");
fx_list.add("[FX=65] PALETTE");
fx_list.add("[FX=66] FIRE_2012");
fx_list.add("[FX=67] COLORWAVES");
fx_list.add("[FX=68] BPM");
fx_list.add("[FX=69] FILLNOISE8");
fx_list.add("[FX=70] NOISE16_1");
fx_list.add("[FX=71] NOISE16_2");
fx_list.add("[FX=72] NOISE16_3");
fx_list.add("[FX=73] NOISE16_4");
fx_list.add("[FX=74] COLORTWINKLE");
fx_list.add("[FX=75] LAKE");
fx_list.add("[FX=76] METEOR");
fx_list.add("[FX=77] METEOR_SMOOTH");
fx_list.add("[FX=78] RAILWAY");
fx_list.add("[FX=79] RIPPLE");
size_t jlen = root.measureLength() + 1;
char buffer[jlen], pubt[21 + sizeof(serverDescription) + 8];
root.printTo(buffer, jlen);
DEBUG_PRINT("HA Discovery Sending >>");
DEBUG_PRINTLN(buffer);
strcpy(pubt, "homeassistant/light/");
strcat(pubt, serverDescription);
strcat(pubt, "/config");
mqtt->publish(pubt, 0, true, buffer);
}
bool initMqtt()
{
if (WiFi.status() != WL_CONNECTED) return false;
if (mqttServer[0] == 0) return false;
IPAddress mqttIP;
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
{
mqtt->setServer(mqttIP, WLED_MQTT_PORT);
} else {
mqtt->setServer(mqttServer, WLED_MQTT_PORT);
}
mqtt->setClientId(escapedMac.c_str());
mqtt->onMessage(onMqttMessage);
mqtt->onConnect(onMqttConnect);
mqtt->connect();
DEBUG_PRINTLN("MQTT ready.");
return true;
}