WLED/wled00/json.cpp

546 lines
15 KiB
C++
Raw Normal View History

#include "wled.h"
/*
* JSON API (De)serialization
*/
2019-03-05 10:59:15 +01:00
2019-11-30 19:17:25 +01:00
void deserializeSegment(JsonObject elem, byte it)
{
byte id = elem["id"] | it;
if (id < strip.getMaxSegments())
{
WS2812FX::Segment& seg = strip.getSegment(id);
2020-01-14 10:57:23 +01:00
uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1;
2019-11-30 19:17:25 +01:00
2020-01-14 10:57:23 +01:00
if (stop < 0) {
uint16_t len = elem["len"];
stop = (len > 0) ? start + len : seg.stop;
}
uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem["spc"] | seg.spacing;
strip.setSegment(id, start, stop, grp, spc);
int segbri = elem["bri"] | -1;
if (segbri == 0) {
seg.setOption(SEG_OPTION_ON, 0);
} else if (segbri > 0) {
seg.opacity = segbri;
seg.setOption(SEG_OPTION_ON, 1);
}
seg.setOption(SEG_OPTION_ON, elem["on"] | seg.getOption(SEG_OPTION_ON));
2019-11-30 19:17:25 +01:00
JsonArray colarr = elem["col"];
if (!colarr.isNull())
{
for (uint8_t i = 0; i < 3; i++)
{
JsonArray colX = colarr[i];
if (colX.isNull()) break;
byte sz = colX.size();
if (sz > 0 && sz < 5)
{
int rgbw[] = {0,0,0,0};
byte cp = copyArray(colX, rgbw);
2019-11-30 19:17:25 +01:00
if (cp == 1 && rgbw[0] == 0) seg.colors[i] = 0;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
2019-11-30 19:17:25 +01:00
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
} else {
seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF)));
2019-11-30 19:17:25 +01:00
}
}
}
}
//if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal);
seg.setOption(SEG_OPTION_SELECTED, elem["sel"] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
2020-08-07 00:50:19 +02:00
seg.setOption(SEG_OPTION_MIRROR , elem["mi"] | seg.getOption(SEG_OPTION_MIRROR ));
2019-11-30 19:17:25 +01:00
//temporary, strip object gets updated via colorUpdated()
2019-12-01 01:42:52 +01:00
if (id == strip.getMainSegmentId()) {
2019-11-30 19:17:25 +01:00
effectCurrent = elem["fx"] | effectCurrent;
effectSpeed = elem["sx"] | effectSpeed;
2019-12-01 01:42:52 +01:00
effectIntensity = elem["ix"] | effectIntensity;
2019-11-30 19:17:25 +01:00
effectPalette = elem["pal"] | effectPalette;
} else { //permanent
byte fx = elem["fx"] | seg.mode;
if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx);
seg.speed = elem["sx"] | seg.speed;
seg.intensity = elem["ix"] | seg.intensity;
seg.palette = elem["pal"] | seg.palette;
}
JsonArray iarr = elem["i"]; //set individual LEDs
if (!iarr.isNull()) {
strip.setPixelSegment(id);
//freeze and init to black
if (!seg.getOption(SEG_OPTION_FREEZE)) {
seg.setOption(SEG_OPTION_FREEZE, true);
strip.fill(0);
}
uint16_t start = 0, stop = 0;
byte set = 0; //0 nothing set, 1 start set, 2 range set
for (uint16_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) {
if (!set) {
start = iarr[i];
set = 1;
} else {
stop = iarr[i];
set = 2;
}
} else {
JsonArray icol = iarr[i];
if (icol.isNull()) break;
byte sz = icol.size();
if (sz == 0 && sz > 4) break;
int rgbw[] = {0,0,0,0};
byte cp = copyArray(icol, rgbw);
if (set < 2) stop = start + 1;
for (uint16_t i = start; i < stop; i++) {
strip.setPixelColor(i, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
}
if (!set) start++;
set = 0;
}
}
strip.setPixelSegment(255);
strip.trigger();
} else { //return to regular effect
seg.setOption(SEG_OPTION_FREEZE, false);
}
2019-11-30 19:17:25 +01:00
}
}
bool deserializeState(JsonObject root)
2019-03-05 10:59:15 +01:00
{
2019-12-02 12:41:35 +01:00
strip.applyToAllSelected = false;
bool stateResponse = root["v"] | false;
int ps = root["ps"] | -1;
if (ps >= 0) applyPreset(ps);
bri = root["bri"] | bri;
2019-03-06 01:20:38 +01:00
bool on = root["on"] | (bri > 0);
if (!on != !bri) toggleOnOff();
2019-11-30 19:17:25 +01:00
int tr = root["transition"] | -1;
if (tr >= 0)
2019-03-06 01:20:38 +01:00
{
2019-11-30 19:17:25 +01:00
transitionDelay = tr;
2019-03-06 01:20:38 +01:00
transitionDelay *= 100;
}
2019-11-30 19:17:25 +01:00
tr = root["tt"] | -1;
if (tr >= 0)
{
2019-11-30 19:17:25 +01:00
transitionDelayTemp = tr;
transitionDelayTemp *= 100;
jsonTransitionOnce = true;
}
2019-12-18 00:47:24 +01:00
int cy = root["pl"] | -2;
if (cy > -2) presetCyclingEnabled = (cy >= 0);
2019-12-05 01:58:03 +01:00
JsonObject ccnf = root["ccnf"];
presetCycleMin = ccnf["min"] | presetCycleMin;
presetCycleMax = ccnf["max"] | presetCycleMax;
tr = ccnf["time"] | -1;
2020-05-18 16:36:31 +02:00
if (tr >= 2) presetCycleTime = tr;
JsonObject nl = root["nl"];
2019-03-06 01:20:38 +01:00
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
nightlightMode = nl["fade"] | nightlightMode; //deprecated
nightlightMode = nl["mode"] | nightlightMode;
2019-03-06 01:20:38 +01:00
nightlightTargetBri = nl["tbri"] | nightlightTargetBri;
JsonObject udpn = root["udpn"];
2019-03-06 01:20:38 +01:00
notifyDirect = udpn["send"] | notifyDirect;
receiveNotifications = udpn["recv"] | receiveNotifications;
bool noNotification = udpn["nn"]; //send no notification just for this request
int timein = root["time"] | -1;
if (timein != -1) setTime(timein);
doReboot = root["rb"] | doReboot;
realtimeOverride = root["lor"] | realtimeOverride;
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root["mainseg"] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
2019-12-01 01:42:52 +01:00
2019-03-06 01:20:38 +01:00
int it = 0;
2019-11-30 19:17:25 +01:00
JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>())
2019-03-06 01:20:38 +01:00
{
2019-11-30 19:17:25 +01:00
int id = segVar["id"] | -1;
2019-11-30 19:17:25 +01:00
if (id < 0) { //set all selected segments
bool didSet = false;
byte lowestActive = 99;
2019-11-30 19:17:25 +01:00
for (byte s = 0; s < strip.getMaxSegments(); s++)
2019-03-06 01:20:38 +01:00
{
2019-11-30 19:17:25 +01:00
WS2812FX::Segment sg = strip.getSegment(s);
if (sg.isActive())
2019-03-06 01:20:38 +01:00
{
if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) {
deserializeSegment(segVar, s);
didSet = true;
}
2019-03-06 01:20:38 +01:00
}
}
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive);
2019-11-30 19:17:25 +01:00
} else { //set only the segment with the specified ID
deserializeSegment(segVar, it);
}
} else {
JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs)
{
deserializeSegment(elem, it);
it++;
2019-03-06 01:20:38 +01:00
}
}
2019-11-30 19:17:25 +01:00
2020-05-28 02:20:02 +02:00
usermods.readFromJsonState(root);
2020-02-22 16:17:32 +01:00
colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE);
//write presets to flash directly?
bool persistSaves = !(root["np"] | false);
2019-11-25 00:20:00 +01:00
ps = root["psave"] | -1;
if (ps >= 0) savePreset(ps, persistSaves);
2019-11-25 00:20:00 +01:00
return stateResponse;
2019-03-06 01:20:38 +01:00
}
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id)
{
root["id"] = id;
root["start"] = seg.start;
root["stop"] = seg.stop;
2020-01-14 10:57:23 +01:00
root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping;
root["spc"] = seg.spacing;
root["on"] = seg.getOption(SEG_OPTION_ON);
byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255;
JsonArray colarr = root.createNestedArray("col");
for (uint8_t i = 0; i < 3; i++)
{
JsonArray colX = colarr.createNestedArray();
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
if (i == 0) {
colX.add(col[0]); colX.add(col[1]); colX.add(col[2]); if (useRGBW) colX.add(col[3]);
} else {
colX.add(colSec[0]); colX.add(colSec[1]); colX.add(colSec[2]); if (useRGBW) colX.add(colSec[3]);
}
} else {
colX.add((seg.colors[i] >> 16) & 0xFF);
colX.add((seg.colors[i] >> 8) & 0xFF);
colX.add((seg.colors[i]) & 0xFF);
if (useRGBW)
colX.add((seg.colors[i] >> 24) & 0xFF);
}
}
2020-08-07 00:50:19 +02:00
root["fx"] = seg.mode;
root["sx"] = seg.speed;
root["ix"] = seg.intensity;
root["pal"] = seg.palette;
root["sel"] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
2020-08-07 00:50:19 +02:00
root["mi"] = seg.getOption(SEG_OPTION_MIRROR);
}
void serializeState(JsonObject root)
2019-03-06 01:20:38 +01:00
{
if (errorFlag) root["error"] = errorFlag;
2019-03-06 01:20:38 +01:00
root["on"] = (bri > 0);
root["bri"] = briLast;
root["transition"] = transitionDelay/100; //in 100ms
2019-12-01 01:42:52 +01:00
root["ps"] = currentPreset;
root["pss"] = savedPresets;
root["pl"] = (presetCyclingEnabled) ? 0: -1;
2019-12-05 01:58:03 +01:00
2020-05-28 02:20:02 +02:00
usermods.addToJsonState(root);
2020-05-18 16:36:31 +02:00
//temporary for preset cycle
2019-12-05 01:58:03 +01:00
JsonObject ccnf = root.createNestedObject("ccnf");
ccnf["min"] = presetCycleMin;
ccnf["max"] = presetCycleMax;
2020-05-18 16:36:31 +02:00
ccnf["time"] = presetCycleTime;
2019-03-05 10:59:15 +01:00
JsonObject nl = root.createNestedObject("nl");
2019-03-05 10:59:15 +01:00
nl["on"] = nightlightActive;
nl["dur"] = nightlightDelayMins;
nl["fade"] = (nightlightMode > NL_MODE_SET); //deprecated
nl["mode"] = nightlightMode;
2019-03-05 10:59:15 +01:00
nl["tbri"] = nightlightTargetBri;
JsonObject udpn = root.createNestedObject("udpn");
2019-03-05 10:59:15 +01:00
udpn["send"] = notifyDirect;
udpn["recv"] = receiveNotifications;
2019-12-01 01:42:52 +01:00
root["lor"] = realtimeOverride;
2019-12-01 01:42:52 +01:00
root["mainseg"] = strip.getMainSegmentId();
2019-03-05 10:59:15 +01:00
JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++)
{
WS2812FX::Segment sg = strip.getSegment(s);
if (sg.isActive())
{
JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s);
}
}
2019-03-05 10:59:15 +01:00
}
2020-03-30 10:41:42 +02:00
//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp
int getSignalQuality(int rssi)
{
int quality = 0;
if (rssi <= -100)
{
quality = 0;
}
else if (rssi >= -50)
{
quality = 100;
}
else
{
quality = 2 * (rssi + 100);
}
return quality;
}
void serializeInfo(JsonObject root)
2019-03-05 10:59:15 +01:00
{
2019-03-06 01:20:38 +01:00
root["ver"] = versionString;
root["vid"] = VERSION;
2020-05-02 01:59:41 +02:00
//root["cn"] = WLED_CODENAME;
2019-03-05 10:59:15 +01:00
JsonObject leds = root.createNestedObject("leds");
2019-03-05 10:59:15 +01:00
leds["count"] = ledCount;
leds["rgbw"] = useRGBW;
leds["wv"] = useRGBW && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed?
JsonArray leds_pin = leds.createNestedArray("pin");
2019-03-05 10:59:15 +01:00
leds_pin.add(LEDPIN);
leds["pwr"] = strip.currentMilliamps;
2019-12-30 17:34:15 +01:00
leds["maxpwr"] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
2019-03-06 01:20:38 +01:00
leds["maxseg"] = strip.getMaxSegments();
2019-12-01 01:42:52 +01:00
leds["seglock"] = false; //will be used in the future to prevent modifications to segment config
root["str"] = syncToggleReceive;
2019-03-06 01:20:38 +01:00
root["name"] = serverDescription;
root["udpport"] = udpPort;
2020-02-09 19:10:29 +01:00
root["live"] = (bool)realtimeMode;
switch (realtimeMode) {
case REALTIME_MODE_INACTIVE: root["lm"] = ""; break;
case REALTIME_MODE_GENERIC: root["lm"] = ""; break;
case REALTIME_MODE_UDP: root["lm"] = "UDP"; break;
case REALTIME_MODE_HYPERION: root["lm"] = "Hyperion"; break;
case REALTIME_MODE_E131: root["lm"] = "E1.31"; break;
2020-05-23 16:09:49 +02:00
case REALTIME_MODE_ADALIGHT: root["lm"] = F("USB Adalight/TPM2"); break;
case REALTIME_MODE_ARTNET: root["lm"] = "Art-Net"; break;
2020-05-18 16:36:31 +02:00
case REALTIME_MODE_TPM2NET: root["lm"] = F("tpm2.net"); break;
}
if (realtimeIP[0] == 0)
{
root["lip"] = "";
} else {
root["lip"] = realtimeIP.toString();
}
#ifdef WLED_ENABLE_WEBSOCKETS
root["ws"] = ws.count();
#else
root["ws"] = -1;
#endif
2019-03-06 01:20:38 +01:00
root["fxcount"] = strip.getModeCount();
root["palcount"] = strip.getPaletteCount();
JsonObject wifi_info = root.createNestedObject("wifi");
wifi_info["bssid"] = WiFi.BSSIDstr();
2020-01-14 10:57:23 +01:00
int qrssi = WiFi.RSSI();
wifi_info["rssi"] = qrssi;
wifi_info["signal"] = getSignalQuality(qrssi);
wifi_info["channel"] = WiFi.channel();
2019-03-05 10:59:15 +01:00
#ifdef ARDUINO_ARCH_ESP32
#ifdef WLED_DEBUG
wifi_info["txPower"] = (int) WiFi.getTxPower();
wifi_info["sleep"] = (bool) WiFi.getSleep();
#endif
2019-03-06 01:20:38 +01:00
root["arch"] = "esp32";
root["core"] = ESP.getSdkVersion();
//root["maxalloc"] = ESP.getMaxAllocHeap();
#ifdef WLED_DEBUG
root["resetReason0"] = (int)rtc_get_reset_reason(0);
root["resetReason1"] = (int)rtc_get_reset_reason(1);
#endif
root["lwip"] = 0;
2019-03-05 10:59:15 +01:00
#else
2019-03-06 01:20:38 +01:00
root["arch"] = "esp8266";
root["core"] = ESP.getCoreVersion();
//root["maxalloc"] = ESP.getMaxFreeBlockSize();
#ifdef WLED_DEBUG
root["resetReason"] = (int)ESP.getResetInfoPtr()->reason;
#endif
root["lwip"] = LWIP_VERSION_MAJOR;
2019-03-05 10:59:15 +01:00
#endif
2019-03-06 01:20:38 +01:00
root["freeheap"] = ESP.getFreeHeap();
root["uptime"] = millis()/1000 + rolloverMillis*4294967;
2020-05-28 02:20:02 +02:00
usermods.addToJsonInfo(root);
2019-03-05 10:59:15 +01:00
2019-03-06 01:20:38 +01:00
byte os = 0;
#ifdef WLED_DEBUG
os = 0x80;
#endif
2019-03-05 10:59:15 +01:00
#ifndef WLED_DISABLE_ALEXA
2019-03-06 01:20:38 +01:00
os += 0x40;
2019-03-05 10:59:15 +01:00
#endif
#ifndef WLED_DISABLE_BLYNK
2019-03-06 01:20:38 +01:00
os += 0x20;
2019-03-05 10:59:15 +01:00
#endif
#ifndef WLED_DISABLE_CRONIXIE
2019-03-06 01:20:38 +01:00
os += 0x10;
2019-03-05 10:59:15 +01:00
#endif
2019-03-16 02:09:37 +01:00
#ifndef WLED_DISABLE_FILESYSTEM
2019-03-06 01:20:38 +01:00
os += 0x08;
2019-03-05 10:59:15 +01:00
#endif
#ifndef WLED_DISABLE_HUESYNC
2019-03-06 01:20:38 +01:00
os += 0x04;
2019-03-05 10:59:15 +01:00
#endif
2019-12-03 14:15:12 +01:00
#ifdef WLED_ENABLE_ADALIGHT
2019-03-06 01:20:38 +01:00
os += 0x02;
2019-03-05 10:59:15 +01:00
#endif
#ifndef WLED_DISABLE_OTA
2019-03-06 01:20:38 +01:00
os += 0x01;
2019-03-05 10:59:15 +01:00
#endif
2019-03-06 01:20:38 +01:00
root["opt"] = os;
root["brand"] = "WLED";
root["product"] = "FOSS";
2019-03-06 01:20:38 +01:00
root["mac"] = escapedMac;
}
void serveJson(AsyncWebServerRequest* request)
{
byte subJson = 0;
const String& url = request->url();
if (url.indexOf("state") > 0) subJson = 1;
else if (url.indexOf("info") > 0) subJson = 2;
else if (url.indexOf("si") > 0) subJson = 3;
2019-11-18 12:29:36 +01:00
else if (url.indexOf("live") > 0) {
serveLiveLeds(request);
return;
}
2019-03-06 01:20:38 +01:00
else if (url.indexOf("eff") > 0) {
request->send_P(200, "application/json", JSON_mode_names);
return;
}
else if (url.indexOf("pal") > 0) {
request->send_P(200, "application/json", JSON_palette_names);
return;
}
else if (url.length() > 6) { //not just /json
request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}"));
2019-03-06 01:20:38 +01:00
return;
}
2019-03-05 10:59:15 +01:00
2019-03-06 01:20:38 +01:00
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject doc = response->getRoot();
2019-03-05 10:59:15 +01:00
2019-03-06 01:20:38 +01:00
switch (subJson)
{
case 1: //state
serializeState(doc); break;
case 2: //info
serializeInfo(doc); break;
default: //all
JsonObject state = doc.createNestedObject("state");
2019-03-06 01:20:38 +01:00
serializeState(state);
JsonObject info = doc.createNestedObject("info");
2019-03-06 01:20:38 +01:00
serializeInfo(info);
if (subJson != 3)
{
doc["effects"] = serialized((const __FlashStringHelper*)JSON_mode_names);
doc["palettes"] = serialized((const __FlashStringHelper*)JSON_palette_names);
}
2019-03-06 01:20:38 +01:00
}
2019-03-05 10:59:15 +01:00
response->setLength();
request->send(response);
}
2019-11-18 12:29:36 +01:00
#define MAX_LIVE_LEDS 180
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
2019-11-18 12:29:36 +01:00
{
AsyncWebSocketClient * wsc;
if (!request) { //not HTTP, use Websockets
#ifdef WLED_ENABLE_WEBSOCKETS
wsc = ws.client(wsClient);
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
#endif
}
uint16_t used = ledCount;
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
2019-11-18 12:29:36 +01:00
char buffer[2000] = "{\"leds\":[";
obuf = buffer;
olen = 9;
2019-11-18 12:29:36 +01:00
2019-12-01 01:42:52 +01:00
for (uint16_t i= 0; i < used; i += n)
2019-11-18 12:29:36 +01:00
{
olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i));
2019-11-18 12:29:36 +01:00
}
olen -= 1;
oappend("],\"n\":");
oappendi(n);
oappend("}");
if (request) {
request->send(200, "application/json", buffer);
}
#ifdef WLED_ENABLE_WEBSOCKETS
else {
wsc->text(obuf, olen);
}
#endif
return true;
2019-11-18 12:29:36 +01:00
}