Added experimental /json/cfg endpoint for changing settings from JSON

This commit is contained in:
cschwinne 2021-05-11 01:11:16 +02:00
parent 119826cb9b
commit d94d3d4bc5
7 changed files with 130 additions and 93 deletions

View File

@ -2,6 +2,11 @@
### Builds after release 0.12.0 ### 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 #### Build 2105070
- Fixed not turning on after pressing "Off" on IR remote twice (#1950) - Fixed not turning on after pressing "Off" on IR remote twice (#1950)

View File

@ -12,24 +12,7 @@ void getStringFromJson(char* dest, const char* src, size_t len) {
if (src != nullptr) strlcpy(dest, src, len); if (src != nullptr) strlcpy(dest, src, len);
} }
void deserializeConfig() { bool deserializeConfig(JsonObject doc, bool fromFS) {
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;
}
//int rev_major = doc["rev"][0]; // 1 //int rev_major = doc["rev"][0]; // 1
//int rev_minor = doc["rev"][1]; // 0 //int rev_minor = doc["rev"][1]; // 0
@ -98,6 +81,7 @@ void deserializeConfig() {
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]); CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonArray ins = hw_led["ins"]; JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) {
uint8_t s = 0; //bus iterator uint8_t s = 0; //bus iterator
strip.isRgbw = false; strip.isRgbw = false;
busses.removeAll(); busses.removeAll();
@ -134,44 +118,48 @@ void deserializeConfig() {
if (mem <= MAX_LED_MEMORY) busses.add(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 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]; JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
CJSON(buttonType, hw_btn_ins_0["type"]); CJSON(buttonType, hw_btn_ins_0["type"]);
int hw_btn_pin = hw_btn_ins_0[F("pin")][0]; 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)) { if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin = hw_btn_pin; btnPin = hw_btn_pin;
pinMode(btnPin, INPUT_PULLUP); pinMode(btnPin, INPUT_PULLUP);
} else { } else {
btnPin = -1; btnPin = -1;
} }
}
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")]; JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")];
CJSON(macroButton, hw_btn_ins_0_macros[0]); CJSON(macroButton, hw_btn_ins_0_macros[0]);
CJSON(macroLongPress,hw_btn_ins_0_macros[1]); CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]); CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);
//int hw_btn_ins_0_type = hw_btn_ins_0["type"]; // 0
#ifndef WLED_DISABLE_INFRARED #ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -1; // 4 int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) {
if (pinManager.allocatePin(hw_ir_pin,false)) { if (pinManager.allocatePin(hw_ir_pin,false)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
} else { } else {
irPin = -1; irPin = -1;
} }
}
#endif #endif
CJSON(irEnabled, hw["ir"]["type"]); CJSON(irEnabled, hw["ir"]["type"]);
JsonObject relay = hw[F("relay")]; JsonObject relay = hw[F("relay")];
int hw_relay_pin = relay["pin"] | -2;
int hw_relay_pin = relay["pin"]; if (hw_relay_pin > -2) {
if (pinManager.allocatePin(hw_relay_pin,true)) { if (pinManager.allocatePin(hw_relay_pin,true)) {
rlyPin = hw_relay_pin; rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT); pinMode(rlyPin, OUTPUT);
} else { } else {
rlyPin = -1; rlyPin = -1;
} }
}
if (relay.containsKey("rev")) { if (relay.containsKey("rev")) {
rlyMde = !relay["rev"]; rlyMde = !relay["rev"];
} }
@ -193,12 +181,13 @@ void deserializeConfig() {
CJSON(fadeTransition, light_tr[F("mode")]); CJSON(fadeTransition, light_tr[F("mode")]);
int tdd = light_tr[F("dur")] | -1; int tdd = light_tr[F("dur")] | -1;
if (tdd >= 0) transitionDelayDefault = tdd * 100; if (tdd >= 0) transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr[F("pal")]); CJSON(strip.paletteFade, light_tr["pal"]);
JsonObject light_nl = light["nl"]; JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl[F("mode")]); CJSON(nightlightMode, light_nl[F("mode")]);
byte prev = nightlightDelayMinsDefault;
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]); CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
nightlightDelayMins = nightlightDelayMinsDefault; if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;
CJSON(nightlightTargetBri, light_nl[F("tbri")]); CJSON(nightlightTargetBri, light_nl[F("tbri")]);
CJSON(macroNl, light_nl[F("macro")]); CJSON(macroNl, light_nl[F("macro")]);
@ -227,11 +216,13 @@ void deserializeConfig() {
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]); CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv["col"]); CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]); CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
//! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
JsonObject if_sync_send = if_sync["send"]; JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault;
CJSON(notifyDirectDefault, if_sync_send[F("dir")]); CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
notifyDirect = notifyDirectDefault; if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault;
CJSON(notifyButton, if_sync_send[F("btn")]); CJSON(notifyButton, if_sync_send[F("btn")]);
CJSON(notifyAlexa, if_sync_send[F("va")]); CJSON(notifyAlexa, if_sync_send[F("va")]);
CJSON(notifyHue, if_sync_send[F("hue")]); CJSON(notifyHue, if_sync_send[F("hue")]);
@ -310,9 +301,10 @@ void deserializeConfig() {
CJSON(latitude, if_ntp[F("lt")]); CJSON(latitude, if_ntp[F("lt")]);
JsonObject ol = doc[F("ol")]; JsonObject ol = doc[F("ol")];
prev = overlayDefault;
CJSON(overlayDefault ,ol[F("clock")]); // 0 CJSON(overlayDefault ,ol[F("clock")]); // 0
CJSON(countdownMode, ol[F("cntdwn")]); CJSON(countdownMode, ol[F("cntdwn")]);
overlayCurrent = overlayDefault; if (prev != overlayDefault) overlayCurrent = overlayDefault;
CJSON(overlayMin, ol[F("min")]); CJSON(overlayMin, ol[F("min")]);
CJSON(overlayMax, ol[F("max")]); CJSON(overlayMax, ol[F("max")]);
@ -386,7 +378,32 @@ void deserializeConfig() {
#endif #endif
JsonObject usermods_settings = doc["um"]; 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() { void serializeConfig() {
@ -513,7 +530,7 @@ void serializeConfig() {
JsonObject light_tr = light.createNestedObject("tr"); JsonObject light_tr = light.createNestedObject("tr");
light_tr[F("mode")] = fadeTransition; light_tr[F("mode")] = fadeTransition;
light_tr[F("dur")] = transitionDelayDefault / 100; light_tr[F("dur")] = transitionDelayDefault / 100;
light_tr[F("pal")] = strip.paletteFade; light_tr["pal"] = strip.paletteFade;
JsonObject light_nl = light.createNestedObject("nl"); JsonObject light_nl = light.createNestedObject("nl");
light_nl[F("mode")] = nightlightMode; light_nl[F("mode")] = nightlightMode;

View File

@ -26,7 +26,8 @@ void handleButton();
void handleIO(); void handleIO();
//cfg.cpp //cfg.cpp
void deserializeConfig(); bool deserializeConfig(JsonObject doc, bool fromFS = false);
void deserializeConfigFromFS();
bool deserializeConfigSec(); bool deserializeConfigSec();
void serializeConfig(); void serializeConfig();
void serializeConfigSec(); void serializeConfigSec();

View File

@ -98,13 +98,13 @@ void deserializeSegment(JsonObject elem, byte it)
effectCurrent = elem[F("fx")] | effectCurrent; effectCurrent = elem[F("fx")] | effectCurrent;
effectSpeed = elem[F("sx")] | effectSpeed; effectSpeed = elem[F("sx")] | effectSpeed;
effectIntensity = elem[F("ix")] | effectIntensity; effectIntensity = elem[F("ix")] | effectIntensity;
effectPalette = elem[F("pal")] | effectPalette; effectPalette = elem["pal"] | effectPalette;
} else { //permanent } else { //permanent
byte fx = elem[F("fx")] | seg.mode; byte fx = elem[F("fx")] | seg.mode;
if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx); if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx);
seg.speed = elem[F("sx")] | seg.speed; seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity; 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 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("fx")] = seg.mode;
root[F("sx")] = seg.speed; root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity; root[F("ix")] = seg.intensity;
root[F("pal")] = seg.palette; root["pal"] = seg.palette;
root[F("sel")] = seg.isSelected(); root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED); root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR); root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
@ -740,10 +740,13 @@ void serveJson(AsyncWebServerRequest* request)
request->send_P(200, "application/json", JSON_mode_names); request->send_P(200, "application/json", JSON_mode_names);
return; return;
} }
else if (url.indexOf(F("pal")) > 0) { else if (url.indexOf("pal") > 0) {
request->send_P(200, "application/json", JSON_palette_names); request->send_P(200, "application/json", JSON_palette_names);
return; return;
} }
else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) {
return;
}
else if (url.length() > 6) { //not just /json else if (url.length() > 6) { //not just /json
request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}")); request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}"));
return; return;

View File

@ -333,7 +333,7 @@ void WLED::setup()
errorFlag = ERR_FS_BEGIN; errorFlag = ERR_FS_BEGIN;
} else deEEP(); } else deEEP();
updateFSInfo(); updateFSInfo();
deserializeConfig(); deserializeConfigFromFS();
#if STATUSLED #if STATUSLED
bool lStatusLed = false; bool lStatusLed = false;

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // 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 //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG

View File

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