2020-11-06 22:12:48 +01:00
# include "wled.h"
/*
* Methods to handle saving and loading presets to / from the filesystem
*/
2022-01-01 12:52:50 +01:00
# ifdef ARDUINO_ARCH_ESP32
static char * tmpRAMbuffer = nullptr ;
# endif
static volatile byte presetToApply = 0 ;
static volatile byte callModeToApply = 0 ;
2022-02-02 20:53:16 +01:00
static volatile bool checkPlaylist = false ;
2022-01-01 12:52:50 +01:00
2022-02-02 20:53:16 +01:00
bool applyPreset ( byte index , byte callMode , bool fromJson )
2020-11-06 22:12:48 +01:00
{
2022-01-01 12:52:50 +01:00
presetToApply = index ;
callModeToApply = callMode ;
2022-02-02 20:53:16 +01:00
checkPlaylist = fromJson ;
2022-04-16 16:28:43 +02:00
// 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
2022-01-01 12:52:50 +01:00
return true ;
}
2022-04-16 16:28:43 +02:00
void handlePresets ( bool force )
2022-01-01 12:52:50 +01:00
{
2022-04-16 16:28:43 +02:00
if ( presetToApply = = 0 | | ( fileDoc & & ! force ) ) return ; // JSON buffer already allocated and not force apply or no preset waiting
2022-01-01 12:52:50 +01:00
JsonObject fdo ;
2022-02-02 22:00:31 +01:00
const char * filename = presetToApply < 255 ? " /presets.json " : " /tmp.json " ;
2022-01-01 12:52:50 +01:00
2022-04-16 16:28:43 +02:00
//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 & & presetToApply < 255 ) {
// 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 , presetToApply , fileDoc ) ? ERR_NONE : ERR_FS_PLOAD ;
JsonObject fdo = fileDoc - > as < JsonObject > ( ) ;
// if we applyPreset from JSON and preset contains "seg" we must unload playlist
if ( checkPlaylist & & ! fdo [ " seg " ] . isNull ( ) ) unloadPlaylist ( ) ;
fdo . remove ( " ps " ) ; //remove load request for presets to prevent recursive crash
deserializeState ( fdo , callModeToApply , presetToApply ) ;
if ( ! errorFlag ) currentPreset = presetToApply ;
presetToApply = 0 ; //clear request for preset
callModeToApply = 0 ;
checkPlaylist = false ;
return ;
}
2022-01-01 12:52:50 +01:00
// allocate buffer
DEBUG_PRINTLN ( F ( " Apply preset JSON buffer requested. " ) ) ;
if ( ! requestJSONBufferLock ( 9 ) ) return ; // will also assign fileDoc
# ifdef ARDUINO_ARCH_ESP32
if ( presetToApply = = 255 & & tmpRAMbuffer ! = nullptr ) {
deserializeJson ( * fileDoc , tmpRAMbuffer ) ;
errorFlag = ERR_NONE ;
} else
# endif
{
errorFlag = readObjectFromFileUsingId ( filename , presetToApply , fileDoc ) ? ERR_NONE : ERR_FS_PLOAD ;
2020-11-06 22:12:48 +01:00
}
2022-01-01 12:52:50 +01:00
fdo = fileDoc - > as < JsonObject > ( ) ;
2022-01-03 22:23:03 +01:00
//HTTP API commands
const char * httpwin = fdo [ " win " ] ;
if ( httpwin ) {
2022-02-19 20:34:37 +01:00
String apireq = " win " ; apireq + = ' & ' ; // reduce flash string usage
2022-04-16 16:28:43 +02:00
apireq + = F ( " IN& " ) ; // interenal call
2022-01-03 22:23:03 +01:00
apireq + = httpwin ;
handleSet ( nullptr , apireq , false ) ;
2022-04-16 16:28:43 +02:00
setValuesFromFirstSelectedSeg ( ) ; // fills legacy values
2022-01-03 22:23:03 +01:00
} else {
fdo . remove ( " ps " ) ; //remove load request for presets to prevent recursive crash
2022-02-02 20:53:16 +01:00
// if we applyPreset from JSON and preset contains "seg" we must unload playlist
if ( checkPlaylist & & ! fdo [ " seg " ] . isNull ( ) ) unloadPlaylist ( ) ;
2022-01-03 22:23:03 +01:00
deserializeState ( fdo , CALL_MODE_NO_NOTIFY , presetToApply ) ;
}
2020-11-06 22:12:48 +01:00
2022-01-01 12:52:50 +01:00
# if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer
if ( presetToApply = = 255 & & tmpRAMbuffer ! = nullptr ) {
free ( tmpRAMbuffer ) ;
tmpRAMbuffer = nullptr ;
2020-11-06 22:12:48 +01:00
}
2022-01-01 12:52:50 +01:00
# endif
releaseJSONBufferLock ( ) ; // will also clear fileDoc
if ( ! errorFlag & & presetToApply < 255 ) currentPreset = presetToApply ;
2022-01-03 22:23:03 +01:00
colorUpdated ( callModeToApply ) ;
updateInterfaces ( callModeToApply ) ;
2022-01-01 12:52:50 +01:00
presetToApply = 0 ; //clear request for preset
callModeToApply = 0 ;
2022-02-02 20:53:16 +01:00
checkPlaylist = false ;
2020-11-06 22:12:48 +01:00
}
2022-01-01 12:52:50 +01:00
//called from handleSet(PS=) [network callback (fileDoc==nullptr), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
2022-03-15 09:55:23 +01:00
void savePreset ( byte index , const char * pname , JsonObject saveobj )
2020-11-06 22:12:48 +01:00
{
2022-03-15 09:55:23 +01:00
if ( index = = 0 | | ( index > 250 & & index < 255 ) ) return ;
2022-02-19 11:37:48 +01:00
char tmp [ 12 ] ;
2020-11-06 22:12:48 +01:00
JsonObject sObj = saveobj ;
2022-01-01 12:52:50 +01:00
bool bufferAllocated = false ;
2020-11-06 22:12:48 +01:00
2022-03-15 09:55:23 +01:00
bool persist = ( index ! = 255 ) ;
2022-02-02 22:00:31 +01:00
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 ) {
2022-01-01 12:52:50 +01:00
// called from handleSet() HTTP API
2021-11-12 23:33:10 +01:00
DEBUG_PRINTLN ( F ( " Save preset JSON buffer requested. " ) ) ;
2021-11-14 16:56:34 +01:00
if ( ! requestJSONBufferLock ( 10 ) ) return ;
2022-01-01 12:52:50 +01:00
sObj = fileDoc - > to < JsonObject > ( ) ;
bufferAllocated = true ;
}
2022-02-19 11:37:48 +01:00
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
2022-01-01 12:52:50 +01:00
sObj . remove ( F ( " psave " ) ) ;
sObj . remove ( F ( " v " ) ) ;
2021-11-03 14:52:22 +01:00
2022-01-01 12:52:50 +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 " ) ;
2022-01-14 14:27:11 +01:00
sObj . remove ( F ( " sc " ) ) ;
2022-01-01 12:52:50 +01:00
sObj . remove ( F ( " error " ) ) ;
sObj . remove ( F ( " time " ) ) ;
2020-11-06 22:12:48 +01:00
2022-01-01 12:52:50 +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
}
2022-01-01 12:52:50 +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
2022-01-01 12:52:50 +01:00
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 ( ) ;
}