WLED/wled00/presets.cpp

209 lines
7.1 KiB
C++
Raw Permalink Normal View History

2020-11-06 22:12:48 +01:00
#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)
2020-11-06 22:12:48 +01:00
{
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()
{
2022-04-24 19:30:14 +02:00
bool changePreset = false;
uint8_t tmpPreset = presetToApply; // store temporary since deserializeState() may call applyPreset()
uint8_t tmpMode = callModeToApply;
2022-04-24 19:30:14 +02:00
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";
/*
* The following code is no longer needed as handlePreset() is never run from
* network callback.
* **************************************************************************
*
//crude way to determine if this was called by a network request
uint8_t core = 1;
#ifdef ARDUINO_ARCH_ESP32
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2)
// this does not make sense on single core
core = xPortGetCoreID();
#endif
#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;
2022-04-24 19:30:14 +02:00
JsonObject fdo = fileDoc->as<JsonObject>();
2022-04-24 19:30:14 +02:00
//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;
}
2022-04-24 19:30:14 +02:00
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_PRINT(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;
2020-11-06 22:12:48 +01:00
}
fdo = fileDoc->as<JsonObject>();
//HTTP API commands
const char* httpwin = fdo["win"];
if (httpwin) {
2022-04-24 19:47:55 +02:00
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
2022-04-24 19:30:14 +02:00
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;
2020-11-06 22:12:48 +01:00
#if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
free(tmpRAMbuffer);
tmpRAMbuffer = nullptr;
2020-11-06 22:12:48 +01:00
}
#endif
releaseJSONBufferLock(); // will also clear fileDoc
colorUpdated(tmpMode);
updateInterfaces(tmpMode);
2020-11-06 22:12:48 +01:00
}
//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)
2020-11-06 22:12:48 +01:00
{
if (index == 0 || (index > 250 && index < 255)) return;
char tmp[12];
2020-11-06 22:12:48 +01:00
JsonObject sObj = saveobj;
bool bufferAllocated = false;
2020-11-06 22:12:48 +01:00
bool persist = (index != 255);
const char *filename = persist ? "/presets.json" : "/tmp.json";
2021-10-04 20:22:04 +02:00
2021-11-03 14:52:22 +01:00
if (!fileDoc) {
// called from handleSet() HTTP API
2021-11-14 16:56:34 +01:00
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;
2020-11-29 22:07:12 +01:00
sObj.remove(F("psave"));
sObj.remove(F("v"));
2021-11-03 14:52:22 +01:00
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"));
2020-11-06 22:12:48 +01:00
#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);
2020-11-29 22:07:12 +01:00
}
} else
#endif
writeObjectToFileUsingId(filename, index, fileDoc);
2020-11-06 22:12:48 +01:00
2021-10-04 20:22:04 +02:00
if (persist) presetsModifiedTime = toki.second(); //unix time
if (bufferAllocated) releaseJSONBufferLock();
2020-11-06 22:12:48 +01:00
updateFSInfo();
}
void deletePreset(byte index) {
StaticJsonDocument<24> empty;
writeObjectToFileUsingId("/presets.json", index, &empty);
2021-05-25 09:59:19 +02:00
presetsModifiedTime = toki.second(); //unix time
2020-11-06 22:12:48 +01:00
updateFSInfo();
}