Working deletion
Added HTTP API support to JSON API
This commit is contained in:
parent
8a713b2bbb
commit
606cd18dc4
@ -142,7 +142,7 @@ void _drawOverlayCronixie();
|
||||
void _setRandomColor(bool _sec,bool fromButton=false);
|
||||
bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req);
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
|
||||
int getNumVal(const String* req, uint16_t pos);
|
||||
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
||||
|
||||
@ -200,7 +200,8 @@ void saveSettingsToEEPROM();
|
||||
void loadSettingsFromEEPROM(bool first);
|
||||
void savedToPresets();
|
||||
bool applyPreset(byte index, bool loadBri = true);
|
||||
void savePreset(byte index, bool persist = true, const char* pname = nullptr, byte prio = 50, JsonObject saveobj = JsonObject());
|
||||
void savePreset(byte index, bool persist = true, const char* pname = nullptr, byte prio = 5, JsonObject saveobj = JsonObject());
|
||||
void deletePreset(byte index);
|
||||
void loadMacro(byte index, char* m);
|
||||
void applyMacro(byte index);
|
||||
void saveMacro(byte index, const String& mc, bool persist = true); //only commit on single save, not in settings
|
||||
|
111
wled00/file.cpp
111
wled00/file.cpp
@ -6,6 +6,21 @@
|
||||
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
|
||||
#define FS_BUFSIZE 256
|
||||
|
||||
/*
|
||||
* Structural requirements for files managed by writeObjectToFile() and readObjectFromFile() utilities:
|
||||
* 1. File must be a string representation of a valid JSON object
|
||||
* 2. File must have '{' as first character
|
||||
* 3. There must not be any additional characters between a root-level key and its value object (e.g. space, tab, newline)
|
||||
* 4. There must not be any characters between an root object-separating ',' and the next object key string
|
||||
* 5. There may be any number of spaces, tabs, and/or newlines before such object-separating ','
|
||||
* 6. There must not be more than 5 consecutive spaces at any point except for those permitted in condition 5
|
||||
* 7. If it is desired to delete the first usable object (e.g. preset file), a dummy object '"0":{}' is inserted at the beginning.
|
||||
* It shall be disregarded by receiving software.
|
||||
* The reason for it is that deleting the first preset would require special code to handle commas between it and the 2nd preset
|
||||
*/
|
||||
|
||||
//find() that reads and buffers data from file stream in 256-byte blocks.
|
||||
//Significantly faster, f.find(key) can take SECONDS for multi-kB files
|
||||
bool bufferedFind(const char *target, File f) {
|
||||
@ -21,11 +36,11 @@ bool bufferedFind(const char *target, File f) {
|
||||
size_t index = 0;
|
||||
byte c;
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
byte buf[256];
|
||||
byte buf[FS_BUFSIZE];
|
||||
f.seek(0);
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, 256);
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
count = 0;
|
||||
while (count < bufsize) {
|
||||
if(buf[count] != target[index])
|
||||
@ -56,11 +71,11 @@ bool bufferedFindSpace(uint16_t targetLen, File f) {
|
||||
|
||||
uint16_t index = 0;
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
byte buf[256];
|
||||
byte buf[FS_BUFSIZE];
|
||||
f.seek(0);
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, 256);
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
count = 0;
|
||||
|
||||
while (count < bufsize) {
|
||||
@ -81,15 +96,71 @@ bool bufferedFindSpace(uint16_t targetLen, File f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//find the closing bracket corresponding to the opening bracket at the file pos when calling this function
|
||||
bool bufferedFindObjectEnd(File f) {
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTLN(F("Find obj end"));
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
|
||||
if (!f || !f.size()) return false;
|
||||
|
||||
uint16_t objDepth = 0; //num of '{' minus num of '}'. return once 0
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
//size_t start = f.position();
|
||||
byte buf[256];
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
count = 0;
|
||||
|
||||
while (count < bufsize) {
|
||||
if (buf[count] == '{') objDepth++;
|
||||
if (buf[count] == '}') objDepth--;
|
||||
if (objDepth == 0) {
|
||||
f.seek((f.position() - bufsize) + count +1);
|
||||
DEBUGFS_PRINTF("} at pos %d, took %d ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
//fills n bytes from current file pos with ' ' characters
|
||||
void writeSpace(File f, uint16_t l)
|
||||
{
|
||||
byte buf[FS_BUFSIZE];
|
||||
memset(buf, ' ', FS_BUFSIZE);
|
||||
|
||||
while (l > 0) {
|
||||
uint16_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l;
|
||||
f.write(buf, block);
|
||||
l -= block;
|
||||
}
|
||||
}
|
||||
|
||||
bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t s)
|
||||
{
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTLN("Append");
|
||||
DEBUGFS_PRINTLN(F("Append"));
|
||||
uint32_t s1 = millis();
|
||||
#endif
|
||||
uint32_t pos = 0;
|
||||
if (!f) return false;
|
||||
if (f.size() < 3) f.print("{}");
|
||||
|
||||
if (f.size() < 3) {
|
||||
char init[10];
|
||||
strcpy_P(init, PSTR("{\"0\":{}}"));
|
||||
f.print(init);
|
||||
}
|
||||
|
||||
if (content->isNull()) {
|
||||
f.close();
|
||||
return true; //nothing to append
|
||||
}
|
||||
|
||||
//if there is enough empty space in file, insert there instead of appending
|
||||
uint32_t contentLen = measureJson(*content);
|
||||
@ -137,7 +208,7 @@ bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t
|
||||
bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content)
|
||||
{
|
||||
char objKey[10];
|
||||
sprintf(objKey, "\"%ld\":", id);
|
||||
sprintf(objKey, "\"%d\":", id);
|
||||
writeObjectToFile(file, objKey, content);
|
||||
}
|
||||
|
||||
@ -154,7 +225,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
|
||||
File f = WLED_FS.open(file, "r+");
|
||||
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+");
|
||||
if (!f) {
|
||||
DEBUGFS_PRINTLN("Failed to open!");
|
||||
DEBUGFS_PRINTLN(F("Failed to open!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -166,34 +237,24 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
|
||||
//exists
|
||||
pos = f.position();
|
||||
//measure out end of old object
|
||||
StaticJsonDocument<1024> doc;
|
||||
deserializeJson(doc, f);
|
||||
bufferedFindObjectEnd(f);
|
||||
uint32_t pos2 = f.position();
|
||||
|
||||
uint32_t oldLen = pos2 - pos;
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTF("Old obj len %d >>> ", oldLen);
|
||||
serializeJson(doc, Serial);
|
||||
DEBUGFS_PRINTLN();
|
||||
#endif
|
||||
DEBUGFS_PRINTF("Old obj len %d\n", oldLen);
|
||||
|
||||
if (!content->isNull() && measureJson(*content) <= oldLen) //replace
|
||||
{
|
||||
DEBUGFS_PRINTLN("replace");
|
||||
DEBUGFS_PRINTLN(F("replace"));
|
||||
f.seek(pos);
|
||||
serializeJson(*content, f);
|
||||
//pad rest
|
||||
for (uint32_t i = f.position(); i < pos2; i++) {
|
||||
f.write(' ');
|
||||
}
|
||||
writeSpace(f, pos2 - f.position());
|
||||
} else { //delete
|
||||
DEBUGFS_PRINTLN("delete");
|
||||
DEBUGFS_PRINTLN(F("delete"));
|
||||
pos -= strlen(key);
|
||||
if (pos > 3) pos--; //also delete leading comma if not first object
|
||||
f.seek(pos);
|
||||
for (uint32_t i = pos; i < pos2; i++) {
|
||||
f.write(' ');
|
||||
}
|
||||
writeSpace(f, pos2 - pos);
|
||||
if (!content->isNull()) return appendObjectToFile(f, key, content, s);
|
||||
}
|
||||
f.close();
|
||||
@ -204,7 +265,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest)
|
||||
{
|
||||
char objKey[10];
|
||||
sprintf(objKey, "\"%ld\":", id);
|
||||
sprintf(objKey, "\"%d\":", id);
|
||||
readObjectFromFile(file, objKey, dest);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
#include "wled.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp_spiffs.h" //FS info bare IDF function until FS wrapper is available for ESP32
|
||||
#endif
|
||||
|
||||
/*
|
||||
* JSON API (De)serialization
|
||||
*/
|
||||
@ -148,6 +152,14 @@ bool deserializeState(JsonObject root)
|
||||
strip.applyToAllSelected = false;
|
||||
bool stateResponse = root[F("v")] | false;
|
||||
|
||||
//HTTP API commands
|
||||
const char* httpwin = root[F("win")];
|
||||
if (httpwin) {
|
||||
String apireq = "win&";
|
||||
apireq += httpwin;
|
||||
handleSet(nullptr, apireq, false);
|
||||
}
|
||||
|
||||
int ps = root[F("ps")] | -1;
|
||||
if (ps >= 0) applyPreset(ps);
|
||||
|
||||
@ -192,7 +204,7 @@ bool deserializeState(JsonObject root)
|
||||
bool noNotification = udpn[F("nn")]; //send no notification just for this request
|
||||
|
||||
int timein = root[F("time")] | -1;
|
||||
if (timein != -1) setTime(timein);
|
||||
if (timein != -1 && millis() - ntpLastSyncTime > 50000000L) setTime(timein);
|
||||
doReboot = root[F("rb")] | doReboot;
|
||||
|
||||
realtimeOverride = root[F("lor")] | realtimeOverride;
|
||||
@ -244,7 +256,14 @@ bool deserializeState(JsonObject root)
|
||||
bool persistSaves = !(root[F("np")] | false);
|
||||
|
||||
ps = root[F("psave")] | -1;
|
||||
if (ps >= 0) savePreset(ps, persistSaves, root["n"], root["p"] | 50, root["o"].as<JsonObject>());
|
||||
if (ps > 0) {
|
||||
savePreset(ps, persistSaves, root["n"], root["p"] | 50, root["o"].as<JsonObject>());
|
||||
} else {
|
||||
ps = root[F("pdel")] | -1; //deletion
|
||||
if (ps > 0) {
|
||||
deletePreset(ps);
|
||||
}
|
||||
}
|
||||
|
||||
return stateResponse;
|
||||
}
|
||||
@ -422,10 +441,18 @@ void serializeInfo(JsonObject root)
|
||||
wifi_info[F("channel")] = WiFi.channel();
|
||||
|
||||
JsonObject fs_info = root.createNestedObject("fs");
|
||||
FSInfo fsi;
|
||||
WLED_FS.info(fsi);
|
||||
fs_info["u"] = fsi.usedBytes;
|
||||
fs_info["t"] = fsi.totalBytes;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
size_t used, total;
|
||||
esp_spiffs_info(nullptr, &total, &used);
|
||||
fs_info["u"] = used;
|
||||
fs_info["t"] = total;
|
||||
#else
|
||||
FSInfo fsi;
|
||||
WLED_FS.info(fsi);
|
||||
fs_info["u"] = fsi.usedBytes;
|
||||
fs_info["t"] = fsi.totalBytes;
|
||||
#endif
|
||||
fs_info[F("pmt")] = presetsModifiedTime;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef WLED_DEBUG
|
||||
|
@ -375,7 +375,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv, byte ma
|
||||
|
||||
|
||||
//HTTP API request parser
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
{
|
||||
if (!(req.indexOf("win") >= 0)) return false;
|
||||
|
||||
@ -746,6 +746,8 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
}
|
||||
//you can add more if you need
|
||||
|
||||
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
|
||||
|
||||
//internal call, does not send XML response
|
||||
pos = req.indexOf(F("IN"));
|
||||
if (pos < 1) XML_response(request);
|
||||
|
@ -177,10 +177,17 @@ void WLED::setup()
|
||||
DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
|
||||
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
bool fsinit = false;
|
||||
DEBUGFS_PRINTLN(F("Mount FS"));
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
WLED_FS.begin(true);
|
||||
fsinit = WLED_FS.begin(true);
|
||||
#else
|
||||
fsinit = WLED_FS.begin();
|
||||
#endif
|
||||
WLED_FS.begin();
|
||||
if (!fsinit) {
|
||||
DEBUGFS_PRINTLN(F("FS failed!"));
|
||||
errorFlag = ERR_FS_BEGIN;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN(F("Load EEPROM"));
|
||||
|
@ -36,8 +36,8 @@
|
||||
#endif
|
||||
|
||||
//#define WLED_DISABLE_FILESYSTEM // FS used by new preset functionality
|
||||
//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version
|
||||
//#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
|
||||
#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version
|
||||
#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
|
||||
|
||||
// to toggle usb serial debug (un)comment the following line
|
||||
//#define WLED_DEBUG
|
||||
@ -474,9 +474,11 @@ WLED_GLOBAL uint16_t olen _INIT(0);
|
||||
|
||||
// presets
|
||||
WLED_GLOBAL uint16_t savedPresets _INIT(0);
|
||||
WLED_GLOBAL int8_t currentPreset _INIT(-1);
|
||||
WLED_GLOBAL int16_t currentPreset _INIT(-1);
|
||||
WLED_GLOBAL bool isPreset _INIT(false);
|
||||
|
||||
WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L);
|
||||
|
||||
WLED_GLOBAL byte errorFlag _INIT(0);
|
||||
|
||||
WLED_GLOBAL String messageHead, messageSub;
|
||||
|
@ -633,8 +633,12 @@ bool applyPreset(byte index, bool loadBri)
|
||||
errorFlag = readObjectFromFileUsingId("/presets.json", index, &temp) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
serializeJson(temp, Serial);
|
||||
deserializeState(temp.as<JsonObject>());
|
||||
//presetToApply = index;
|
||||
return true;
|
||||
if (!errorFlag) {
|
||||
currentPreset = index;
|
||||
isPreset = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (index == 255 || index == 0)
|
||||
{
|
||||
loadSettingsFromEEPROM(false);//load boot defaults
|
||||
@ -688,6 +692,7 @@ void savePreset(byte index, bool persist, const char* pname, byte priority, Json
|
||||
if (saveobj.isNull()) {
|
||||
DEBUGFS_PRINTLN("Save current state");
|
||||
serializeState(doc.to<JsonObject>(), true);
|
||||
currentPreset = index;
|
||||
} else {
|
||||
DEBUGFS_PRINTLN("Save custom");
|
||||
sObj.set(saveobj);
|
||||
@ -695,9 +700,8 @@ void savePreset(byte index, bool persist, const char* pname, byte priority, Json
|
||||
sObj["p"] = priority;
|
||||
if (pname) sObj["n"] = pname;
|
||||
|
||||
//serializeJson(doc, Serial);
|
||||
writeObjectToFileUsingId("/presets.json", index, &doc);
|
||||
//Serial.println("Done!");
|
||||
presetsModifiedTime = now(); //unix time
|
||||
return;
|
||||
|
||||
if (index > 16) return;
|
||||
@ -736,6 +740,12 @@ void savePreset(byte index, bool persist, const char* pname, byte priority, Json
|
||||
isPreset = true;
|
||||
}
|
||||
|
||||
void deletePreset(byte index) {
|
||||
StaticJsonDocument<24> empty;
|
||||
writeObjectToFileUsingId("/presets.json", index, &empty);
|
||||
presetsModifiedTime = now(); //unix time
|
||||
}
|
||||
|
||||
|
||||
void loadMacro(byte index, char* m)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user