Skip to content

Commit

Permalink
Added experimental /json/cfg endpoint for changing settings from JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
Aircoookie committed May 10, 2021
1 parent 119826c commit d94d3d4
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 93 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

### Builds after release 0.12.0

#### Build 2105110

- Added Usermod settings page and configurable usermods (PR #1951)
- Added experimental `/json/cfg` endpoint for changing settings from JSON (see #1944, not part of official API)

#### Build 2105070

- Fixed not turning on after pressing "Off" on IR remote twice (#1950)
Expand Down
173 changes: 95 additions & 78 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,7 @@ void getStringFromJson(char* dest, const char* src, size_t len) {
if (src != nullptr) strlcpy(dest, src, len);
}

void deserializeConfig() {
bool fromeep = false;
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
fromeep = true;
}

DynamicJsonDocument doc(JSON_BUFFER_SIZE);

DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));

success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
if (!fromeep) deEEPSettings();
return;
}

bool deserializeConfig(JsonObject doc, bool fromFS) {
//int rev_major = doc["rev"][0]; // 1
//int rev_minor = doc["rev"][1]; // 0

Expand Down Expand Up @@ -98,79 +81,84 @@ void deserializeConfig() {
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);

JsonArray ins = hw_led["ins"];
uint8_t s = 0; //bus iterator
strip.isRgbw = false;
busses.removeAll();
uint32_t mem = 0;
for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm[F("pin")];
if (pinArr.size() == 0) continue;
pins[0] = pinArr[0];
uint8_t i = 0;
for (int p : pinArr) {
pins[i] = p;
i++;
if (i>4) break;
if (fromFS || !ins.isNull()) {
uint8_t s = 0; //bus iterator
strip.isRgbw = false;
busses.removeAll();
uint32_t mem = 0;
for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm[F("pin")];
if (pinArr.size() == 0) continue;
pins[0] = pinArr[0];
uint8_t i = 0;
for (int p : pinArr) {
pins[i] = p;
i++;
if (i>4) break;
}

uint16_t length = elm[F("len")];
if (length==0) continue;
uint8_t colorOrder = (int)elm[F("order")];
//only use skip from the first strip (this shouldn't have been in ins obj. but remains here for compatibility)
if (s==0) skipFirstLed = elm[F("skip")];
uint16_t start = elm[F("start")] | 0;
if (start >= ledCount) continue;
//limit length of strip if it would exceed total configured LEDs
if (start + length > ledCount) length = ledCount - start;
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
//RGBW mode is enabled if at least one of the strips is RGBW
strip.isRgbw = (strip.isRgbw || BusManager::isRgbw(ledType));
s++;
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed);
mem += busses.memUsage(bc);
if (mem <= MAX_LED_MEMORY) busses.add(bc);
}

uint16_t length = elm[F("len")];
if (length==0) continue;
uint8_t colorOrder = (int)elm[F("order")];
//only use skip from the first strip (this shouldn't have been in ins obj. but remains here for compatibility)
if (s==0) skipFirstLed = elm[F("skip")];
uint16_t start = elm[F("start")] | 0;
if (start >= ledCount) continue;
//limit length of strip if it would exceed total configured LEDs
if (start + length > ledCount) length = ledCount - start;
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
//RGBW mode is enabled if at least one of the strips is RGBW
strip.isRgbw = (strip.isRgbw || BusManager::isRgbw(ledType));
s++;
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed);
mem += busses.memUsage(bc);
if (mem <= MAX_LED_MEMORY) busses.add(bc);
strip.finalizeInit(ledCount, skipFirstLed);
}
strip.finalizeInit(ledCount, skipFirstLed);
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus

JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
CJSON(buttonType, hw_btn_ins_0["type"]);
int hw_btn_pin = hw_btn_ins_0[F("pin")][0];
if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin = hw_btn_pin;
pinMode(btnPin, INPUT_PULLUP);
} else {
btnPin = -1;
int hw_btn_pin = hw_btn_ins_0[F("pin")][0] | -2; //-2 = not present in doc, keep current. -1 = disable
if (hw_btn_pin > -2) {
if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin = hw_btn_pin;
pinMode(btnPin, INPUT_PULLUP);
} else {
btnPin = -1;
}
}

JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")];
CJSON(macroButton, hw_btn_ins_0_macros[0]);
CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);

//int hw_btn_ins_0_type = hw_btn_ins_0["type"]; // 0

#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -1; // 4
if (pinManager.allocatePin(hw_ir_pin,false)) {
irPin = hw_ir_pin;
} else {
irPin = -1;
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) {
if (pinManager.allocatePin(hw_ir_pin,false)) {
irPin = hw_ir_pin;
} else {
irPin = -1;
}
}
#endif
CJSON(irEnabled, hw["ir"]["type"]);

JsonObject relay = hw[F("relay")];

int hw_relay_pin = relay["pin"];
if (pinManager.allocatePin(hw_relay_pin,true)) {
rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT);
} else {
rlyPin = -1;
int hw_relay_pin = relay["pin"] | -2;
if (hw_relay_pin > -2) {
if (pinManager.allocatePin(hw_relay_pin,true)) {
rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT);
} else {
rlyPin = -1;
}
}
if (relay.containsKey("rev")) {
rlyMde = !relay["rev"];
Expand All @@ -193,12 +181,13 @@ void deserializeConfig() {
CJSON(fadeTransition, light_tr[F("mode")]);
int tdd = light_tr[F("dur")] | -1;
if (tdd >= 0) transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr[F("pal")]);
CJSON(strip.paletteFade, light_tr["pal"]);

JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl[F("mode")]);
byte prev = nightlightDelayMinsDefault;
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
nightlightDelayMins = nightlightDelayMinsDefault;
if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;

CJSON(nightlightTargetBri, light_nl[F("tbri")]);
CJSON(macroNl, light_nl[F("macro")]);
Expand Down Expand Up @@ -227,11 +216,13 @@ void deserializeConfig() {
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
//! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);

JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault;
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
notifyDirect = notifyDirectDefault;
if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault;
CJSON(notifyButton, if_sync_send[F("btn")]);
CJSON(notifyAlexa, if_sync_send[F("va")]);
CJSON(notifyHue, if_sync_send[F("hue")]);
Expand Down Expand Up @@ -310,9 +301,10 @@ void deserializeConfig() {
CJSON(latitude, if_ntp[F("lt")]);

JsonObject ol = doc[F("ol")];
prev = overlayDefault;
CJSON(overlayDefault ,ol[F("clock")]); // 0
CJSON(countdownMode, ol[F("cntdwn")]);
overlayCurrent = overlayDefault;
if (prev != overlayDefault) overlayCurrent = overlayDefault;

CJSON(overlayMin, ol[F("min")]);
CJSON(overlayMax, ol[F("max")]);
Expand Down Expand Up @@ -386,7 +378,32 @@ void deserializeConfig() {
#endif

JsonObject usermods_settings = doc["um"];
usermods.readFromConfig(usermods_settings);
if (!usermods_settings.isNull()) usermods.readFromConfig(usermods_settings);

if (fromFS) return false;
doReboot = doc[F("rb")] | doReboot;
return (doc["sv"] | true);
}

void deserializeConfigFromFS() {
bool fromeep = false;
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
fromeep = true;
}

DynamicJsonDocument doc(JSON_BUFFER_SIZE);

DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));

success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
if (!fromeep) deEEPSettings();
return;
}

deserializeConfig(doc.as<JsonObject>(), true);
}

void serializeConfig() {
Expand Down Expand Up @@ -513,7 +530,7 @@ void serializeConfig() {
JsonObject light_tr = light.createNestedObject("tr");
light_tr[F("mode")] = fadeTransition;
light_tr[F("dur")] = transitionDelayDefault / 100;
light_tr[F("pal")] = strip.paletteFade;
light_tr["pal"] = strip.paletteFade;

JsonObject light_nl = light.createNestedObject("nl");
light_nl[F("mode")] = nightlightMode;
Expand Down
3 changes: 2 additions & 1 deletion wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ void handleButton();
void handleIO();

//cfg.cpp
void deserializeConfig();
bool deserializeConfig(JsonObject doc, bool fromFS = false);
void deserializeConfigFromFS();
bool deserializeConfigSec();
void serializeConfig();
void serializeConfigSec();
Expand Down
17 changes: 10 additions & 7 deletions wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ void deserializeSegment(JsonObject elem, byte it)
effectCurrent = elem[F("fx")] | effectCurrent;
effectSpeed = elem[F("sx")] | effectSpeed;
effectIntensity = elem[F("ix")] | effectIntensity;
effectPalette = elem[F("pal")] | effectPalette;
effectPalette = elem["pal"] | effectPalette;
} else { //permanent
byte fx = elem[F("fx")] | seg.mode;
if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx);
seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity;
seg.palette = elem[F("pal")] | seg.palette;
seg.palette = elem["pal"] | seg.palette;
}

JsonArray iarr = elem[F("i")]; //set individual LEDs
Expand Down Expand Up @@ -339,7 +339,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
root[F("fx")] = seg.mode;
root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity;
root[F("pal")] = seg.palette;
root["pal"] = seg.palette;
root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
Expand Down Expand Up @@ -729,21 +729,24 @@ void serveJson(AsyncWebServerRequest* request)
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;
else if (url.indexOf("si") > 0) subJson = 3;
else if (url.indexOf("nodes") > 0) subJson = 4;
else if (url.indexOf("palx") > 0) subJson = 5;
else if (url.indexOf("palx") > 0) subJson = 5;
else if (url.indexOf("live") > 0) {
serveLiveLeds(request);
return;
}
else if (url.indexOf(F("eff")) > 0) {
else if (url.indexOf(F("eff")) > 0) {
request->send_P(200, "application/json", JSON_mode_names);
return;
}
else if (url.indexOf(F("pal")) > 0) {
else if (url.indexOf("pal") > 0) {
request->send_P(200, "application/json", JSON_palette_names);
return;
}
else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) {
return;
}
else if (url.length() > 6) { //not just /json
request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}"));
return;
Expand Down
2 changes: 1 addition & 1 deletion wled00/wled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ void WLED::setup()
errorFlag = ERR_FS_BEGIN;
} else deEEP();
updateFSInfo();
deserializeConfig();
deserializeConfigFromFS();

#if STATUSLED
bool lStatusLed = false;
Expand Down
2 changes: 1 addition & 1 deletion wled00/wled.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

// version code in format yymmddb (b = daily build)
#define VERSION 2105070
#define VERSION 2105110

//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
Expand Down
21 changes: 16 additions & 5 deletions wled00/wled_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,30 @@ void initServer()

AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) {
bool verboseResponse = false;
bool isConfig = false;
{ //scope JsonDocument so it releases its buffer
DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE);
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
JsonObject root = jsonBuffer.as<JsonObject>();
if (error || root.isNull()) {
request->send(400, "application/json", F("{\"error\":9}")); return;
}
fileDoc = &jsonBuffer;
verboseResponse = deserializeState(root);
fileDoc = nullptr;
const String& url = request->url();
isConfig = url.indexOf("cfg") > -1;
if (!isConfig) {
fileDoc = &jsonBuffer;
verboseResponse = deserializeState(root);
fileDoc = nullptr;
} else {
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
}
}
if (verboseResponse) { //if JSON contains "v"
serveJson(request); return;
if (verboseResponse) {
if (!isConfig) {
serveJson(request); return; //if JSON contains "v"
} else {
serializeConfig(); //Save new settings to FS
}
}
request->send(200, "application/json", F("{\"success\":true}"));
});
Expand Down

0 comments on commit d94d3d4

Please sign in to comment.