f1a1b89d13
- correct preset cycling - updated rotary encoder preset selection
199 lines
6.8 KiB
C++
199 lines
6.8 KiB
C++
#include "wled.h"
|
|
|
|
/*
|
|
* Methods to handle saving and loading presets to/from the filesystem
|
|
*/
|
|
|
|
#ifdef ARDUINO_ARCH_ESP32
|
|
static char *tmpRAMbuffer = nullptr;
|
|
#endif
|
|
|
|
static volatile byte presetToApply = 0;
|
|
static volatile byte callModeToApply = 0;
|
|
|
|
bool applyPreset(byte index, byte callMode, bool fromJson)
|
|
{
|
|
DEBUG_PRINT(F("Request to apply preset: "));
|
|
DEBUG_PRINTLN(index);
|
|
presetToApply = index;
|
|
callModeToApply = callMode;
|
|
// the following is needed in case of HTTP JSON API call to return correct state to the caller
|
|
// fromJson is true in case when deserializeState() was called with presetId==0
|
|
if (fromJson) handlePresets(true); // force immediate processing
|
|
return true;
|
|
}
|
|
|
|
void handlePresets(bool force)
|
|
{
|
|
bool changePreset = false;
|
|
uint8_t tmpPreset = presetToApply; // store temporary since deserializeState() may call applyPreset()
|
|
uint8_t tmpMode = callModeToApply;
|
|
|
|
if (tmpPreset == 0 || (fileDoc && !force)) return; // JSON buffer already allocated and not force apply or no preset waiting
|
|
|
|
JsonObject fdo;
|
|
const char *filename = tmpPreset < 255 ? "/presets.json" : "/tmp.json";
|
|
|
|
//crude way to determine if this was called by a network request
|
|
uint8_t core = 1;
|
|
#ifdef ARDUINO_ARCH_ESP32
|
|
core = xPortGetCoreID();
|
|
#endif
|
|
//only allow use of fileDoc from the core responsible for network requests (AKA HTTP JSON API)
|
|
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
|
|
if (fileDoc && core && force && tmpPreset < 255) {
|
|
DEBUG_PRINT(F("Force applying preset: "));
|
|
DEBUG_PRINTLN(presetToApply);
|
|
|
|
presetToApply = 0; //clear request for preset
|
|
callModeToApply = 0;
|
|
|
|
// this will overwrite doc with preset content but applyPreset() is the last in such case and content of doc is no longer needed
|
|
errorFlag = readObjectFromFileUsingId(filename, tmpPreset, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
|
|
|
JsonObject fdo = fileDoc->as<JsonObject>();
|
|
|
|
//HTTP API commands
|
|
const char* httpwin = fdo["win"];
|
|
if (httpwin) {
|
|
String apireq = "win"; // reduce flash string usage
|
|
apireq += F("&IN&"); // internal call
|
|
apireq += httpwin;
|
|
handleSet(nullptr, apireq, false); // may call applyPreset() via PL=
|
|
setValuesFromFirstSelectedSeg(); // fills legacy values
|
|
changePreset = true;
|
|
} else {
|
|
if (!fdo["seg"].isNull()) unloadPlaylist(); // if preset contains "seg" we must unload playlist
|
|
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
|
|
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
|
|
|
|
deserializeState(fdo, tmpMode, tmpPreset); // may call applyPreset() which will overwrite presetToApply
|
|
}
|
|
|
|
if (!errorFlag && changePreset) presetCycCurr = currentPreset = tmpPreset;
|
|
|
|
colorUpdated(tmpMode);
|
|
return;
|
|
}
|
|
|
|
if (force) return; // something went wrong with force option (most likely WS request), quit and wait for async load
|
|
|
|
// allocate buffer
|
|
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
|
|
|
|
presetToApply = 0; //clear request for preset
|
|
callModeToApply = 0;
|
|
|
|
DEBUG_PRINTLN(F("Applying preset: "));
|
|
DEBUG_PRINTLN(tmpPreset);
|
|
|
|
#ifdef ARDUINO_ARCH_ESP32
|
|
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
|
|
deserializeJson(*fileDoc,tmpRAMbuffer);
|
|
errorFlag = ERR_NONE;
|
|
} else
|
|
#endif
|
|
{
|
|
errorFlag = readObjectFromFileUsingId(filename, tmpPreset, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
|
}
|
|
fdo = fileDoc->as<JsonObject>();
|
|
|
|
//HTTP API commands
|
|
const char* httpwin = fdo["win"];
|
|
if (httpwin) {
|
|
String apireq = "win"; // reduce flash string usage
|
|
apireq += F("&IN&"); // internal call
|
|
apireq += httpwin;
|
|
handleSet(nullptr, apireq, false); // may call applyPreset() via PL=
|
|
setValuesFromFirstSelectedSeg(); // fills legacy values
|
|
changePreset = true;
|
|
} else {
|
|
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["nl"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
|
|
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
|
|
deserializeState(fdo, CALL_MODE_NO_NOTIFY, tmpPreset); // may change presetToApply by calling applyPreset()
|
|
}
|
|
if (!errorFlag && tmpPreset < 255 && changePreset) presetCycCurr = currentPreset = tmpPreset;
|
|
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
//Aircoookie recommended not to delete buffer
|
|
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
|
|
free(tmpRAMbuffer);
|
|
tmpRAMbuffer = nullptr;
|
|
}
|
|
#endif
|
|
|
|
releaseJSONBufferLock(); // will also clear fileDoc
|
|
colorUpdated(tmpMode);
|
|
updateInterfaces(tmpMode);
|
|
}
|
|
|
|
//called from handleSet(PS=) [network callback (fileDoc==nullptr), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
|
|
void savePreset(byte index, const char* pname, JsonObject saveobj)
|
|
{
|
|
if (index == 0 || (index > 250 && index < 255)) return;
|
|
char tmp[12];
|
|
JsonObject sObj = saveobj;
|
|
bool bufferAllocated = false;
|
|
|
|
bool persist = (index != 255);
|
|
const char *filename = persist ? "/presets.json" : "/tmp.json";
|
|
|
|
if (!fileDoc) {
|
|
// called from handleSet() HTTP API
|
|
if (!requestJSONBufferLock(10)) return;
|
|
sObj = fileDoc->to<JsonObject>();
|
|
bufferAllocated = true;
|
|
}
|
|
if (sObj["n"].isNull() && pname == nullptr) {
|
|
sprintf_P(tmp, PSTR("Preset %d"), index);
|
|
sObj["n"] = tmp;
|
|
} else if (pname) sObj["n"] = pname;
|
|
|
|
sObj.remove(F("psave"));
|
|
sObj.remove(F("v"));
|
|
|
|
if (!sObj["o"]) {
|
|
DEBUGFS_PRINTLN(F("Serialize current state"));
|
|
if (sObj["ib"].isNull() && sObj["sb"].isNull()) serializeState(sObj, true);
|
|
else serializeState(sObj, true, sObj["ib"], sObj["sb"]);
|
|
if (persist) currentPreset = index;
|
|
}
|
|
sObj.remove("o");
|
|
sObj.remove("ib");
|
|
sObj.remove("sb");
|
|
sObj.remove(F("sc"));
|
|
sObj.remove(F("error"));
|
|
sObj.remove(F("time"));
|
|
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
if (index==255) {
|
|
if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer);
|
|
size_t len = measureJson(*fileDoc) + 1;
|
|
DEBUG_PRINTLN(len);
|
|
// if possible use SPI RAM on ESP32
|
|
#ifdef WLED_USE_PSRAM
|
|
if (psramFound())
|
|
tmpRAMbuffer = (char*) ps_malloc(len);
|
|
else
|
|
#endif
|
|
tmpRAMbuffer = (char*) malloc(len);
|
|
if (tmpRAMbuffer!=nullptr) {
|
|
serializeJson(*fileDoc, tmpRAMbuffer, len);
|
|
} else {
|
|
writeObjectToFileUsingId(filename, index, fileDoc);
|
|
}
|
|
} else
|
|
#endif
|
|
writeObjectToFileUsingId(filename, index, fileDoc);
|
|
|
|
if (persist) presetsModifiedTime = toki.second(); //unix time
|
|
if (bufferAllocated) releaseJSONBufferLock();
|
|
updateFSInfo();
|
|
}
|
|
|
|
void deletePreset(byte index) {
|
|
StaticJsonDocument<24> empty;
|
|
writeObjectToFileUsingId("/presets.json", index, &empty);
|
|
presetsModifiedTime = toki.second(); //unix time
|
|
updateFSInfo();
|
|
} |