Added experimental /json/cfg
endpoint for changing settings from JSON
This commit is contained in:
parent
119826cb9b
commit
d94d3d4bc5
@ -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)
|
||||
|
173
wled00/cfg.cpp
173
wled00/cfg.cpp
@ -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
|
||||
|
||||
@ -98,52 +81,56 @@ 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")];
|
||||
@ -151,26 +138,27 @@ void deserializeConfig() {
|
||||
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"];
|
||||
@ -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")]);
|
||||
@ -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")]);
|
||||
@ -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")]);
|
||||
@ -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() {
|
||||
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
@ -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);
|
||||
@ -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;
|
||||
|
@ -333,7 +333,7 @@ void WLED::setup()
|
||||
errorFlag = ERR_FS_BEGIN;
|
||||
} else deEEP();
|
||||
updateFSInfo();
|
||||
deserializeConfig();
|
||||
deserializeConfigFromFS();
|
||||
|
||||
#if STATUSLED
|
||||
bool lStatusLed = false;
|
||||
|
@ -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
|
||||
|
@ -84,6 +84,7 @@ 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));
|
||||
@ -91,12 +92,22 @@ void initServer()
|
||||
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}"));
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user