206 lines
8.4 KiB
C++
206 lines
8.4 KiB
C++
#include "MQTT/MQTT.h"
|
|
|
|
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
|
public:
|
|
MQTTTelemetry() : BufferedInputSource("MQTT"), m_client("relay.malloc.hackerbots.net", 1883, 512, 15, MQTTTelemetry::s_callback, true) {
|
|
strcpy(m_deviceName, System.deviceID().c_str());
|
|
}
|
|
|
|
void loop() override {
|
|
BufferedInputSource::loop();
|
|
OnlineTaskMixin::loop();
|
|
}
|
|
|
|
void handleEventOnline(const InputEvent& event) override {
|
|
char response[255];
|
|
if (event.intent == InputEvent::SetPower) {
|
|
JSONBufferWriter writer(response, sizeof(response));
|
|
writer.beginObject();
|
|
writer.name("state").value(event.asInt() == 0 ? "OFF" : "ON");
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
} else if (event.intent == InputEvent::SetBrightness) {
|
|
JSONBufferWriter writer(response, sizeof(response));
|
|
writer.beginObject();
|
|
writer.name("brightness").value(event.asInt());
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
} else if (event.intent == InputEvent::SetColor) {
|
|
JSONBufferWriter writer(response, sizeof(response));
|
|
writer.beginObject();
|
|
writer.name("color").beginObject();
|
|
CRGB rgb = event.asRGB();
|
|
writer.name("r").value(rgb.r);
|
|
writer.name("g").value(rgb.g);
|
|
writer.name("b").value(rgb.b);
|
|
writer.endObject();
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
} else if (event.intent == InputEvent::SetPattern) {
|
|
JSONBufferWriter writer(response, sizeof(response));
|
|
writer.beginObject();
|
|
writer.name("effect").value(event.asString());
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
} else {
|
|
if (m_lastIntent != event.intent) {
|
|
m_lastIntent = event.intent;
|
|
JSONBufferWriter writer(response, sizeof(response));
|
|
writer.beginObject();
|
|
writer.name("intent").value(event.intent);
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
m_client.publish("renderbug/events", response, MQTT::QOS1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void loopOnline() override {
|
|
if (m_client.isConnected()) {
|
|
m_client.loop();
|
|
EVERY_N_SECONDS(10) {
|
|
char heartbeatBuf[255];
|
|
JSONBufferWriter writer(heartbeatBuf, sizeof(heartbeatBuf));
|
|
writer.beginObject();
|
|
writer.name("fps").value(FastLED.getFPS());
|
|
writer.name("os_version").value(System.version());
|
|
writer.name("free_ram").value((unsigned int)System.freeMemory());
|
|
//writer.name("uptime").value(System.uptime());
|
|
/*WiFiSignal sig = WiFi.RSSI();
|
|
writer.name("strength").value(sig.getStrength());
|
|
writer.name("quality").value(sig.getQuality());*/
|
|
writer.name("RSSI").value(WiFi.RSSI());
|
|
writer.name("SSID").value(WiFi.SSID());
|
|
//writer.name("MAC").value(WiFi.macAddress());
|
|
writer.name("localip").value(WiFi.localIP().toString());
|
|
writer.name("startPixel").value(Static<ConfigService>::instance()->coordMap()->startPixel);
|
|
writer.name("pixelCount").value(Static<ConfigService>::instance()->coordMap()->pixelCount);
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
m_client.publish(m_attrTopic, heartbeatBuf);
|
|
}
|
|
} else {
|
|
m_client.connect("renderbug_" + String(m_deviceName) + "_" + String(Time.now()));
|
|
char response[512];
|
|
JSONBufferWriter writer(response, sizeof(response));
|
|
|
|
String devTopic = String("homeassistant/light/renderbug/") + m_deviceName;
|
|
writer.beginObject();
|
|
writer.name("~").value(devTopic.c_str());
|
|
writer.name("name").value("Renderbug Photon");
|
|
writer.name("unique_id").value(m_deviceName);
|
|
writer.name("cmd_t").value("~/set");
|
|
writer.name("stat_t").value("~/state");
|
|
writer.name("json_attr_t").value("~/attributes");
|
|
writer.name("schema").value("json");
|
|
writer.name("brightness").value(true);
|
|
writer.name("rgb").value(true);
|
|
writer.name("ret").value(true);
|
|
|
|
writer.name("dev").beginObject();
|
|
writer.name("name").value("Renderbug");
|
|
writer.name("mdl").value("Renderbug");
|
|
writer.name("sw").value(RENDERBUG_VERSION);
|
|
writer.name("mf").value("Phong Robotics");
|
|
|
|
writer.name("ids").beginArray();
|
|
writer.value(m_deviceName);
|
|
writer.endArray();
|
|
|
|
writer.endObject();
|
|
|
|
writer.name("fx_list").beginArray();
|
|
for(const Sequencer::Scene& scene : sequencer.scenes()) {
|
|
writer.value(scene.name);
|
|
}
|
|
writer.endArray();
|
|
|
|
writer.endObject();
|
|
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
|
|
String configTopic = devTopic + "/config";
|
|
m_client.publish(configTopic, response, true);
|
|
|
|
String statTopic = devTopic + "/state";
|
|
String cmdTopic = devTopic + "/set";
|
|
String attrTopic = devTopic + "/attributes";
|
|
strcpy(m_statTopic, statTopic.c_str());
|
|
strcpy(m_attrTopic, attrTopic.c_str());
|
|
strcpy(m_cmdTopic, cmdTopic.c_str());
|
|
m_client.subscribe(m_cmdTopic, MQTT::QOS1);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void callback(char* topic, byte* payload, unsigned int length) {
|
|
MainLoop::instance()->dispatch(InputEvent::NetworkActivity);
|
|
if (!strcmp(topic, m_cmdTopic)) {
|
|
JSONValue cmd = JSONValue::parseCopy((char*)payload, length);
|
|
JSONObjectIterator cmdIter(cmd);
|
|
while(cmdIter.next()) {
|
|
if (cmdIter.name() == "state" && cmdIter.value().toString() == "ON") {
|
|
setEvent({InputEvent::SetPower, 1});
|
|
} else if (cmdIter.name() == "state" && cmdIter.value().toString() == "OFF") {
|
|
setEvent({InputEvent::SetPower, 0});
|
|
}
|
|
|
|
if (cmdIter.name() == "color") {
|
|
JSONObjectIterator colorIter(cmdIter.value());
|
|
uint8_t r, g, b;
|
|
while(colorIter.next()) {
|
|
if (colorIter.name() == "r") {
|
|
r = colorIter.value().toInt();
|
|
} else if (colorIter.name() == "g") {
|
|
g = colorIter.value().toInt();
|
|
} else if (colorIter.name() == "b") {
|
|
b = colorIter.value().toInt();
|
|
}
|
|
}
|
|
setEvent(InputEvent{InputEvent::SetColor, CRGB{r, g, b}});
|
|
}
|
|
|
|
if (cmdIter.name() == "brightness") {
|
|
uint8_t brightness = cmdIter.value().toInt();
|
|
setEvent(InputEvent{InputEvent::SetBrightness, brightness});
|
|
}
|
|
|
|
if (cmdIter.name() == "effect") {
|
|
strcpy(m_patternBuf, cmdIter.value().toString().c_str());
|
|
setEvent(InputEvent{InputEvent::SetPattern, m_patternBuf});
|
|
}
|
|
|
|
if (cmdIter.name() == "pixelCount") {
|
|
setEvent(InputEvent{InputEvent::SetDisplayLength, cmdIter.value().toInt()});
|
|
}
|
|
|
|
if (cmdIter.name() == "startPixel") {
|
|
setEvent(InputEvent{InputEvent::SetDisplayOffset, cmdIter.value().toInt()});
|
|
}
|
|
|
|
if (cmdIter.name() == "save") {
|
|
setEvent(InputEvent{InputEvent::SaveConfigurationRequest});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void s_callback(char* topic, byte* payload, unsigned int length) {
|
|
Static<MQTTTelemetry>::instance()->callback(topic, payload, length);
|
|
};
|
|
|
|
MQTT m_client;
|
|
InputEvent::Intent m_lastIntent;
|
|
char m_deviceName[100];
|
|
char m_statTopic[100];
|
|
char m_attrTopic[100];
|
|
char m_cmdTopic[100];
|
|
char m_patternBuf[48];
|
|
};
|
|
|
|
STATIC_ALLOC(MQTTTelemetry);
|
|
|