WLED/wled00/presets.cpp

240 lines
7.2 KiB
C++
Raw 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;
2022-10-08 18:25:51 +02:00
static volatile byte presetToSave = 0;
static volatile int8_t saveLedmap = -1;
static char quickLoad[9];
2022-10-08 18:25:51 +02:00
static char saveName[33];
static bool includeBri = true, segBounds = true, selectedOnly = false, playlistSave = false;;
static const char *getName(bool persist = true) {
return persist ? "/presets.json" : "/tmp.json";
}
static void doSaveState() {
bool persist = (presetToSave < 251);
const char *filename = getName(persist);
if (!requestJSONBufferLock(10)) return; // will set fileDoc
initPresetsFile(); // just in case if someone deleted presets.json using /edit
2022-10-08 18:25:51 +02:00
JsonObject sObj = doc.to<JsonObject>();
DEBUG_PRINTLN(F("Serialize current state"));
if (playlistSave) {
serializePlaylist(sObj);
if (includeBri) sObj["on"] = true;
} else {
serializeState(sObj, true, includeBri, segBounds, selectedOnly);
}
sObj["n"] = saveName;
if (quickLoad[0]) sObj[F("ql")] = quickLoad;
if (saveLedmap >= 0) sObj[F("ledmap")] = saveLedmap;
2022-10-09 12:09:46 +02:00
/*
2022-10-08 18:25:51 +02:00
#ifdef WLED_DEBUG
DEBUG_PRINTLN(F("Serialized preset"));
serializeJson(doc,Serial);
DEBUG_PRINTLN();
#endif
2022-10-09 12:09:46 +02:00
*/
2022-10-08 18:25:51 +02:00
#if defined(ARDUINO_ARCH_ESP32)
if (!persist) {
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, presetToSave, fileDoc);
}
} else
#endif
writeObjectToFileUsingId(filename, presetToSave, fileDoc);
if (persist) presetsModifiedTime = toki.second(); //unix time
releaseJSONBufferLock();
updateFSInfo();
// clean up
saveLedmap = -1;
2022-10-08 18:25:51 +02:00
presetToSave = 0;
saveName[0] = '\0';
quickLoad[0] = '\0';
playlistSave = false;
}
bool getPresetName(byte index, String& name)
{
if (!requestJSONBufferLock(9)) return false;
bool presetExists = false;
if (readObjectFromFileUsingId(getName(), index, &doc))
{
JsonObject fdo = doc.as<JsonObject>();
if (fdo["n"]) {
name = (const char*)(fdo["n"]);
presetExists = true;
}
}
releaseJSONBufferLock();
return presetExists;
}
void initPresetsFile()
{
if (WLED_FS.exists(getName())) return;
StaticJsonDocument<64> doc;
JsonObject sObj = doc.to<JsonObject>();
sObj.createNestedObject("0");
File f = WLED_FS.open(getName(), "w");
if (!f) {
errorFlag = ERR_FS_GENERAL;
return;
}
serializeJson(doc, f);
f.close();
}
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;
return true;
}
void handlePresets()
{
2022-10-08 18:25:51 +02:00
if (presetToSave) {
doSaveState();
return;
}
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;
2022-10-08 18:25:51 +02:00
const char *filename = getName(tmpPreset < 255);
// 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)]
2022-10-08 18:25:51 +02:00
void savePreset(byte index, const char* pname, JsonObject sObj)
2020-11-06 22:12:48 +01:00
{
if (index == 0 || (index > 250 && index < 255)) return;
2022-10-08 18:25:51 +02:00
if (pname) strlcpy(saveName, pname, 33);
else {
if (sObj["n"].is<const char*>()) strlcpy(saveName, sObj["n"].as<const char*>(), 33);
else sprintf_P(saveName, PSTR("Preset %d"), index);
}
2020-11-29 22:07:12 +01:00
2022-10-08 18:25:51 +02:00
DEBUG_PRINT(F("Saving preset (")); DEBUG_PRINT(index); DEBUG_PRINT(F(") ")); DEBUG_PRINTLN(saveName);
2021-11-03 14:52:22 +01:00
2022-10-08 18:25:51 +02:00
presetToSave = index;
playlistSave = false;
if (sObj[F("ql")].is<const char*>()) strlcpy(quickLoad, sObj[F("ql")].as<const char*>(), 9); // client limits QL to 2 chars, buffer for 8 bytes to allow unicode
2022-10-08 18:25:51 +02:00
sObj.remove("v");
2022-10-09 12:09:46 +02:00
sObj.remove("time");
2022-10-08 18:25:51 +02:00
sObj.remove(F("error"));
sObj.remove(F("psave"));
if (sObj["o"].isNull()) { // "o" marks a playlist or manually entered API
2022-10-08 21:31:59 +02:00
includeBri = sObj["ib"].as<bool>() || index==255; // temporary preset needs brightness
segBounds = sObj["sb"].as<bool>() || index==255; // temporary preset needs bounds
2022-10-08 18:25:51 +02:00
selectedOnly = sObj[F("sc")].as<bool>();
saveLedmap = sObj[F("ledmap")] | -1;
2022-10-08 18:25:51 +02:00
sObj.remove("ib");
sObj.remove("sb");
sObj.remove(F("sc"));
} else {
// this is a playlist or API
sObj.remove("o");
if (sObj[F("playlist")].isNull()) {
presetToSave = 0; // we will save API immediately
if (index < 251 && fileDoc) {
if (sObj["n"].isNull()) sObj["n"] = saveName;
initPresetsFile(); // just in case if someone deleted presets.json using /edit
2022-10-08 18:25:51 +02:00
writeObjectToFileUsingId(getName(index), index, fileDoc);
presetsModifiedTime = toki.second(); //unix time
updateFSInfo();
}
} else {
2022-10-08 18:25:51 +02:00
// store playlist
includeBri = true; // !sObj["on"].isNull();
playlistSave = true;
2020-11-29 22:07:12 +01:00
}
2022-10-08 18:25:51 +02:00
}
2020-11-06 22:12:48 +01:00
}
void deletePreset(byte index) {
StaticJsonDocument<24> empty;
2022-10-09 12:09:46 +02:00
writeObjectToFileUsingId(getName(), index, &empty);
2021-05-25 09:59:19 +02:00
presetsModifiedTime = toki.second(); //unix time
2020-11-06 22:12:48 +01:00
updateFSInfo();
}