2021-11-25 22:05:16 +01:00
# include "wled.h"
# include "palettes.h"
2022-11-24 02:50:24 +01:00
# define JSON_PATH_STATE 1
# define JSON_PATH_INFO 2
# define JSON_PATH_STATE_INFO 3
# define JSON_PATH_NODES 4
# define JSON_PATH_PALETTES 5
# define JSON_PATH_FXDATA 6
# define JSON_PATH_NETWORKS 7
2023-05-30 19:36:14 +02:00
# define JSON_PATH_EFFECTS 8
2022-11-24 02:50:24 +01:00
2021-11-25 22:05:16 +01:00
/*
* JSON API ( De ) serialization
*/
2023-03-10 15:20:50 +01:00
bool deserializeSegment ( JsonObject elem , byte it , byte presetId )
2021-11-25 22:05:16 +01:00
{
byte id = elem [ " id " ] | it ;
2023-03-10 15:20:50 +01:00
if ( id > = strip . getMaxSegments ( ) ) return false ;
2021-11-25 22:05:16 +01:00
2023-10-01 13:26:31 +02:00
bool newSeg = false ;
2022-07-17 15:58:41 +02:00
int stop = elem [ " stop " ] | - 1 ;
2023-07-12 20:52:34 +02:00
// append segment
2022-07-19 16:16:43 +02:00
if ( id > = strip . getSegmentsNum ( ) ) {
2023-03-10 15:20:50 +01:00
if ( stop < = 0 ) return false ; // ignore empty/inactive segments
2022-07-17 15:58:41 +02:00
strip . appendSegment ( Segment ( 0 , strip . getLengthTotal ( ) ) ) ;
2022-07-19 16:16:43 +02:00
id = strip . getSegmentsNum ( ) - 1 ; // segments are added at the end of list
2023-10-01 13:26:31 +02:00
newSeg = true ;
2022-07-17 15:58:41 +02:00
}
2023-08-07 16:50:18 +02:00
//DEBUG_PRINTLN("-- JSON deserialize segment.");
2022-07-06 13:13:54 +02:00
Segment & seg = strip . getSegment ( id ) ;
2023-08-07 16:50:18 +02:00
//DEBUG_PRINTF("-- Original segment: %p\n", &seg);
2022-07-06 13:13:54 +02:00
Segment prev = seg ; //make a backup so we can tell if something changed
2023-08-07 16:50:18 +02:00
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev);
2021-11-25 22:05:16 +01:00
uint16_t start = elem [ " start " ] | seg . start ;
if ( stop < 0 ) {
2023-07-03 17:00:43 +02:00
int len = elem [ " len " ] ;
2021-11-25 22:05:16 +01:00
stop = ( len > 0 ) ? start + len : seg . stop ;
}
2022-05-08 10:50:48 +02:00
// 2D segments
uint16_t startY = elem [ " startY " ] | seg . startY ;
uint16_t stopY = elem [ " stopY " ] | seg . stopY ;
2021-11-25 22:05:16 +01:00
2022-01-24 16:44:47 +01:00
//repeat, multiplies segment until all LEDs are used, or max segments reached
2021-11-25 22:17:40 +01:00
bool repeat = elem [ " rpt " ] | false ;
if ( repeat & & stop > 0 ) {
elem . remove ( " id " ) ; // remove for recursive call
elem . remove ( " rpt " ) ; // remove for recursive call
elem . remove ( " n " ) ; // remove for recursive call
uint16_t len = stop - start ;
2022-07-19 22:14:46 +02:00
for ( size_t i = id + 1 ; i < strip . getMaxSegments ( ) ; i + + ) {
2021-11-25 22:17:40 +01:00
start = start + len ;
if ( start > = strip . getLengthTotal ( ) ) break ;
2022-05-08 10:50:48 +02:00
//TODO: add support for 2D
2021-11-25 22:17:40 +01:00
elem [ " start " ] = start ;
elem [ " stop " ] = start + len ;
elem [ " rev " ] = ! elem [ " rev " ] ; // alternate reverse on even/odd segments
deserializeSegment ( elem , i , presetId ) ; // recursive call with new id
}
2023-03-10 15:20:50 +01:00
return true ;
2021-11-25 22:17:40 +01:00
}
2021-11-25 22:05:16 +01:00
if ( elem [ " n " ] ) {
// name field exists
if ( seg . name ) { //clear old name
delete [ ] seg . name ;
seg . name = nullptr ;
}
const char * name = elem [ " n " ] . as < const char * > ( ) ;
size_t len = 0 ;
if ( name ! = nullptr ) len = strlen ( name ) ;
2023-06-27 16:01:20 +02:00
if ( len > 0 ) {
if ( len > WLED_MAX_SEGNAME_LEN ) len = WLED_MAX_SEGNAME_LEN ;
2021-11-25 22:05:16 +01:00
seg . name = new char [ len + 1 ] ;
2023-06-27 16:01:20 +02:00
if ( seg . name ) strlcpy ( seg . name , name , WLED_MAX_SEGNAME_LEN + 1 ) ;
2021-11-25 22:05:16 +01:00
} else {
// but is empty (already deleted above)
elem . remove ( " n " ) ;
}
} else if ( start ! = seg . start | | stop ! = seg . stop ) {
// clearing or setting segment without name field
if ( seg . name ) {
delete [ ] seg . name ;
seg . name = nullptr ;
}
}
uint16_t grp = elem [ " grp " ] | seg . grouping ;
uint16_t spc = elem [ F ( " spc " ) ] | seg . spacing ;
2022-07-21 07:24:03 +02:00
uint16_t of = seg . offset ;
2022-11-24 04:15:24 +01:00
uint8_t soundSim = elem [ " si " ] | seg . soundSim ;
uint8_t map1D2D = elem [ " m12 " ] | seg . map1D2D ;
2021-11-25 22:05:16 +01:00
2022-07-21 07:24:03 +02:00
if ( ( spc > 0 & & spc ! = seg . spacing ) | | seg . map1D2D ! = map1D2D ) seg . fill ( BLACK ) ; // clear spacing gaps
2022-07-20 21:22:23 +02:00
2022-08-31 14:24:02 +02:00
seg . map1D2D = constrain ( map1D2D , 0 , 7 ) ;
2023-04-04 17:16:50 +02:00
seg . soundSim = constrain ( soundSim , 0 , 1 ) ;
2023-04-01 23:40:43 +02:00
2023-05-15 17:06:29 +02:00
uint8_t set = elem [ F ( " set " ) ] | seg . set ;
seg . set = constrain ( set , 0 , 3 ) ;
2022-06-26 23:01:22 +02:00
2021-11-25 22:05:16 +01:00
uint16_t len = 1 ;
if ( stop > start ) len = stop - start ;
int offset = elem [ F ( " of " ) ] | INT32_MAX ;
if ( offset ! = INT32_MAX ) {
int offsetAbs = abs ( offset ) ;
if ( offsetAbs > len - 1 ) offsetAbs % = len ;
if ( offset < 0 ) offsetAbs = len - offsetAbs ;
2021-12-02 00:52:36 +01:00
of = offsetAbs ;
2021-11-25 22:05:16 +01:00
}
2021-12-02 00:52:36 +01:00
if ( stop > start & & of > len - 1 ) of = len - 1 ;
2023-07-12 20:52:34 +02:00
// update segment (delete if necessary)
2023-07-13 13:08:36 +02:00
// do not call seg.setUp() here, as it may cause a crash due to concurrent access if the segment is currently drawing effects
// WS2812FX handles queueing of the change
strip . setSegment ( id , start , stop , grp , spc , of , startY , stopY ) ;
2023-10-01 13:26:31 +02:00
if ( newSeg ) seg . refreshLightCapabilities ( ) ; // fix for #3403
2021-11-25 22:05:16 +01:00
2023-10-01 13:26:31 +02:00
if ( seg . reset & & seg . stop = = 0 ) {
if ( id = = strip . getMainSegmentId ( ) ) strip . setMainSegmentId ( 0 ) ; // fix for #3403
return true ; // segment was deleted & is marked for reset, no need to change anything else
}
2023-03-02 18:21:55 +01:00
2022-05-29 18:20:30 +02:00
byte segbri = seg . opacity ;
2021-11-25 22:05:16 +01:00
if ( getVal ( elem [ " bri " ] , & segbri ) ) {
2022-07-10 22:23:25 +02:00
if ( segbri > 0 ) seg . setOpacity ( segbri ) ;
2022-08-23 16:00:50 +02:00
seg . setOption ( SEG_OPTION_ON , segbri ) ; // use transition
2021-11-25 22:05:16 +01:00
}
2022-08-19 21:37:26 +02:00
bool on = elem [ " on " ] | seg . on ;
2021-11-25 22:05:16 +01:00
if ( elem [ " on " ] . is < const char * > ( ) & & elem [ " on " ] . as < const char * > ( ) [ 0 ] = = ' t ' ) on = ! on ;
2022-08-23 16:00:50 +02:00
seg . setOption ( SEG_OPTION_ON , on ) ; // use transition
2022-08-19 21:37:26 +02:00
bool frz = elem [ " frz " ] | seg . freeze ;
if ( elem [ " frz " ] . is < const char * > ( ) & & elem [ " frz " ] . as < const char * > ( ) [ 0 ] = = ' t ' ) frz = ! seg . freeze ;
seg . freeze = frz ;
2021-11-25 22:05:16 +01:00
2022-07-10 22:23:25 +02:00
seg . setCCT ( elem [ " cct " ] | seg . cct ) ;
2021-11-25 22:05:16 +01:00
JsonArray colarr = elem [ " col " ] ;
if ( ! colarr . isNull ( ) )
{
2023-05-28 22:50:19 +02:00
if ( seg . getLightCapabilities ( ) & 3 ) {
// segment has RGB or White
for ( size_t i = 0 ; i < 3 ; i + + )
{
int rgbw [ ] = { 0 , 0 , 0 , 0 } ;
bool colValid = false ;
JsonArray colX = colarr [ i ] ;
if ( colX . isNull ( ) ) {
byte brgbw [ ] = { 0 , 0 , 0 , 0 } ;
const char * hexCol = colarr [ i ] ;
if ( hexCol = = nullptr ) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr [ i ] | - 1 ;
if ( kelvin < 0 ) continue ;
if ( kelvin = = 0 ) seg . setColor ( i , 0 ) ;
if ( kelvin > 0 ) colorKtoRGB ( kelvin , brgbw ) ;
colValid = true ;
} else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString ( brgbw , hexCol ) ;
}
for ( size_t c = 0 ; c < 4 ; c + + ) rgbw [ c ] = brgbw [ c ] ;
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX . size ( ) ;
if ( sz = = 0 ) continue ; //do nothing on empty array
copyArray ( colX , rgbw , 4 ) ;
2021-11-25 22:05:16 +01:00
colValid = true ;
}
2023-05-28 22:50:19 +02:00
if ( ! colValid ) continue ;
2023-03-02 18:21:55 +01:00
2023-05-28 22:50:19 +02:00
seg . setColor ( i , RGBW32 ( rgbw [ 0 ] , rgbw [ 1 ] , rgbw [ 2 ] , rgbw [ 3 ] ) ) ;
if ( seg . mode = = FX_MODE_STATIC ) strip . trigger ( ) ; //instant refresh
}
} else {
// non RGB & non White segment (usually On/Off bus)
seg . setColor ( 0 , ULTRAWHITE ) ;
seg . setColor ( 1 , BLACK ) ;
2021-11-25 22:05:16 +01:00
}
}
// lx parser
# ifdef WLED_ENABLE_LOXONE
int lx = elem [ F ( " lx " ) ] | - 1 ;
if ( lx > 0 ) {
parseLxJson ( lx , id , false ) ;
}
int ly = elem [ F ( " ly " ) ] | - 1 ;
if ( ly > 0 ) {
parseLxJson ( ly , id , true ) ;
}
# endif
2023-01-12 19:13:07 +01:00
# ifndef WLED_DISABLE_2D
bool reverse = seg . reverse ;
bool mirror = seg . mirror ;
# endif
2022-08-31 08:02:11 +02:00
seg . selected = elem [ " sel " ] | seg . selected ;
seg . reverse = elem [ " rev " ] | seg . reverse ;
seg . mirror = elem [ " mi " ] | seg . mirror ;
2022-07-10 22:23:25 +02:00
# ifndef WLED_DISABLE_2D
2023-01-12 19:13:07 +01:00
bool reverse_y = seg . reverse_y ;
bool mirror_y = seg . mirror_y ;
seg . reverse_y = elem [ " rY " ] | seg . reverse_y ;
seg . mirror_y = elem [ " mY " ] | seg . mirror_y ;
seg . transpose = elem [ F ( " tp " ) ] | seg . transpose ;
if ( seg . is2D ( ) & & seg . map1D2D = = M12_pArc & & ( reverse ! = seg . reverse | | reverse_y ! = seg . reverse_y | | mirror ! = seg . mirror | | mirror_y ! = seg . mirror_y ) ) seg . fill ( BLACK ) ; // clear entire segment (in case of Arc 1D to 2D expansion)
2022-07-10 22:23:25 +02:00
# endif
2021-11-25 22:05:16 +01:00
2022-01-14 14:27:11 +01:00
byte fx = seg . mode ;
2022-07-25 21:31:50 +02:00
if ( getVal ( elem [ " fx " ] , & fx , 0 , strip . getModeCount ( ) ) ) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
2022-01-27 21:01:03 +01:00
if ( ! presetId & & currentPlaylist > = 0 ) unloadPlaylist ( ) ;
2022-09-29 12:49:12 +02:00
if ( fx ! = seg . mode ) seg . setMode ( fx , elem [ F ( " fxdef " ) ] ) ;
2022-07-23 22:00:19 +02:00
}
2022-02-20 22:24:11 +01:00
//getVal also supports inc/decrementing and random
2022-08-31 04:31:56 +02:00
getVal ( elem [ " sx " ] , & seg . speed ) ;
getVal ( elem [ " ix " ] , & seg . intensity ) ;
2022-09-29 12:49:12 +02:00
2022-07-29 16:26:15 +02:00
uint8_t pal = seg . palette ;
2023-05-28 22:50:19 +02:00
if ( seg . getLightCapabilities ( ) & 1 ) { // ignore palette for White and On/Off segments
if ( getVal ( elem [ " pal " ] , & pal ) ) seg . setPalette ( pal ) ;
}
2022-09-29 12:49:12 +02:00
2022-08-31 04:31:56 +02:00
getVal ( elem [ " c1 " ] , & seg . custom1 ) ;
getVal ( elem [ " c2 " ] , & seg . custom2 ) ;
2022-08-12 17:58:20 +02:00
uint8_t cust3 = seg . custom3 ;
2022-08-31 04:31:56 +02:00
getVal ( elem [ " c3 " ] , & cust3 ) ; // we can't pass reference to bifield
2022-08-31 14:24:02 +02:00
seg . custom3 = constrain ( cust3 , 0 , 31 ) ;
2022-08-12 17:58:20 +02:00
2022-08-31 04:31:56 +02:00
seg . check1 = elem [ " o1 " ] | seg . check1 ;
seg . check2 = elem [ " o2 " ] | seg . check2 ;
seg . check3 = elem [ " o3 " ] | seg . check3 ;
2023-01-06 09:24:29 +01:00
2021-11-25 22:05:16 +01:00
JsonArray iarr = elem [ F ( " i " ) ] ; //set individual LEDs
if ( ! iarr . isNull ( ) ) {
2023-01-31 12:49:36 +01:00
uint8_t oldMap1D2D = seg . map1D2D ;
seg . map1D2D = M12_Pixels ; // no mapping
2022-03-10 20:40:48 +01:00
// set brightness immediately and disable transition
transitionDelayTemp = 0 ;
jsonTransitionOnce = true ;
strip . setBrightness ( scaledBri ( bri ) , true ) ;
// freeze and init to black
2022-08-19 21:37:26 +02:00
if ( ! seg . freeze ) {
seg . freeze = true ;
2022-07-20 21:22:23 +02:00
seg . fill ( BLACK ) ;
2021-11-25 22:05:16 +01:00
}
uint16_t start = 0 , stop = 0 ;
byte set = 0 ; //0 nothing set, 1 start set, 2 range set
2022-07-19 22:14:46 +02:00
for ( size_t i = 0 ; i < iarr . size ( ) ; i + + ) {
2021-11-25 22:05:16 +01:00
if ( iarr [ i ] . is < JsonInteger > ( ) ) {
if ( ! set ) {
2023-01-14 16:01:46 +01:00
start = abs ( iarr [ i ] . as < int > ( ) ) ;
set + + ;
2021-11-25 22:05:16 +01:00
} else {
2023-01-14 16:01:46 +01:00
stop = abs ( iarr [ i ] . as < int > ( ) ) ;
set + + ;
2021-11-25 22:05:16 +01:00
}
} else { //color
2022-05-08 10:50:48 +02:00
uint8_t rgbw [ ] = { 0 , 0 , 0 , 0 } ;
2021-11-25 22:05:16 +01:00
JsonArray icol = iarr [ i ] ;
if ( ! icol . isNull ( ) ) { //array, e.g. [255,0,0]
byte sz = icol . size ( ) ;
2022-03-04 21:49:07 +01:00
if ( sz > 0 & & sz < 5 ) copyArray ( icol , rgbw ) ;
2021-11-25 22:05:16 +01:00
} else { //hex string, e.g. "FF0000"
byte brgbw [ ] = { 0 , 0 , 0 , 0 } ;
const char * hexCol = iarr [ i ] ;
if ( colorFromHexString ( brgbw , hexCol ) ) {
2022-07-19 22:14:46 +02:00
for ( size_t c = 0 ; c < 4 ; c + + ) rgbw [ c ] = brgbw [ c ] ;
2021-11-25 22:05:16 +01:00
}
}
2023-01-14 16:01:46 +01:00
if ( set < 2 | | stop < = start ) stop = start + 1 ;
2022-09-04 20:17:05 +02:00
uint32_t c = gamma32 ( RGBW32 ( rgbw [ 0 ] , rgbw [ 1 ] , rgbw [ 2 ] , rgbw [ 3 ] ) ) ;
2023-01-14 16:01:46 +01:00
while ( start < stop ) seg . setPixelColor ( start + + , c ) ;
2021-11-25 22:05:16 +01:00
set = 0 ;
}
}
2023-01-31 12:49:36 +01:00
seg . map1D2D = oldMap1D2D ; // restore mapping
2023-01-14 16:01:46 +01:00
strip . trigger ( ) ; // force segment update
2021-11-25 22:05:16 +01:00
}
2022-11-13 12:13:49 +01:00
// send UDP/WS if segment options changed (except selection; will also deselect current preset)
if ( seg . differs ( prev ) & 0x7F ) stateChanged = true ;
2023-03-10 15:20:50 +01:00
return true ;
2021-11-25 22:05:16 +01:00
}
2021-12-17 20:33:48 +01:00
// deserializes WLED state (fileDoc points to doc object if called from web server)
2022-04-16 16:28:43 +02:00
// presetId is non-0 if called from handlePreset()
2021-11-25 22:05:16 +01:00
bool deserializeState ( JsonObject root , byte callMode , byte presetId )
{
bool stateResponse = root [ F ( " v " ) ] | false ;
2022-11-10 21:50:21 +01:00
# if defined(WLED_DEBUG) && defined(WLED_DEBUG_HOST)
netDebugEnabled = root [ F ( " debug " ) ] | netDebugEnabled ;
# endif
2022-04-01 00:59:19 +02:00
bool onBefore = bri ;
2022-09-09 17:16:52 +02:00
getVal ( root [ " bri " ] , & bri ) ;
2021-11-25 22:05:16 +01:00
2022-09-09 17:16:52 +02:00
bool on = root [ " on " ] | ( bri > 0 ) ;
if ( ! on ! = ! bri ) toggleOnOff ( ) ;
if ( root [ " on " ] . is < const char * > ( ) & & root [ " on " ] . as < const char * > ( ) [ 0 ] = = ' t ' ) {
if ( onBefore | | ! bri ) toggleOnOff ( ) ; // do not toggle off again if just turned on by bri (makes e.g. "{"on":"t","bri":32}" work)
2022-08-08 13:36:13 +02:00
}
2021-11-25 22:05:16 +01:00
2022-04-01 00:59:19 +02:00
if ( bri & & ! onBefore ) { // unfreeze all segments when turning on
2022-07-19 22:14:46 +02:00
for ( size_t s = 0 ; s < strip . getSegmentsNum ( ) ; s + + ) {
2022-08-19 21:37:26 +02:00
strip . getSegment ( s ) . freeze = false ;
2022-04-01 00:59:19 +02:00
}
if ( realtimeMode & & ! realtimeOverride & & useMainSegmentOnly ) { // keep live segment frozen if live
2022-08-19 21:37:26 +02:00
strip . getMainSegment ( ) . freeze = true ;
2022-04-01 00:59:19 +02:00
}
}
2021-11-25 22:05:16 +01:00
int tr = - 1 ;
if ( ! presetId | | currentPlaylist < 0 ) { //do not apply transition time from preset if playlist active, as it would override playlist transition times
tr = root [ F ( " transition " ) ] | - 1 ;
if ( tr > = 0 )
{
transitionDelay = tr ;
transitionDelay * = 100 ;
transitionDelayTemp = transitionDelay ;
}
}
2022-07-29 12:15:56 +02:00
// temporary transition (applies only once)
2021-11-25 22:05:16 +01:00
tr = root [ F ( " tt " ) ] | - 1 ;
if ( tr > = 0 )
{
transitionDelayTemp = tr ;
transitionDelayTemp * = 100 ;
jsonTransitionOnce = true ;
}
2022-03-10 20:40:48 +01:00
strip . setTransition ( transitionDelayTemp ) ; // required here for color transitions to have correct duration
2021-11-25 22:05:16 +01:00
tr = root [ F ( " tb " ) ] | - 1 ;
if ( tr > = 0 ) strip . timebase = ( ( uint32_t ) tr ) - millis ( ) ;
JsonObject nl = root [ " nl " ] ;
nightlightActive = nl [ " on " ] | nightlightActive ;
2022-03-25 16:36:05 +01:00
nightlightDelayMins = nl [ " dur " ] | nightlightDelayMins ;
nightlightMode = nl [ " mode " ] | nightlightMode ;
2021-11-25 22:05:16 +01:00
nightlightTargetBri = nl [ F ( " tbri " ) ] | nightlightTargetBri ;
JsonObject udpn = root [ " udpn " ] ;
2023-09-10 18:52:14 +02:00
sendNotificationsRT = udpn [ " send " ] | sendNotificationsRT ;
2023-08-10 21:52:29 +02:00
syncGroups = udpn [ " sgrp " ] | syncGroups ;
receiveGroups = udpn [ " rgrp " ] | receiveGroups ;
2021-11-25 22:05:16 +01:00
if ( ( bool ) udpn [ F ( " nn " ) ] ) callMode = CALL_MODE_NO_NOTIFY ; //send no notification just for this request
2022-10-09 12:09:46 +02:00
unsigned long timein = root [ " time " ] | UINT32_MAX ; //backup time source if NTP not synced
2021-11-25 22:05:16 +01:00
if ( timein ! = UINT32_MAX ) {
setTimeFromAPI ( timein ) ;
if ( presetsModifiedTime = = 0 ) presetsModifiedTime = timein ;
}
2023-04-26 19:47:12 +02:00
if ( root [ F ( " psave " ) ] . isNull ( ) ) doReboot = root [ F ( " rb " ) ] | doReboot ;
2021-11-25 22:05:16 +01:00
2022-04-07 21:54:55 +02:00
// do not allow changing main segment while in realtime mode (may get odd results else)
if ( ! realtimeMode ) strip . setMainSegmentId ( root [ F ( " mainseg " ) ] | strip . getMainSegmentId ( ) ) ; // must be before realtimeLock() if "live"
2022-03-25 16:36:05 +01:00
2021-11-25 22:05:16 +01:00
realtimeOverride = root [ F ( " lor " ) ] | realtimeOverride ;
if ( realtimeOverride > 2 ) realtimeOverride = REALTIME_OVERRIDE_ALWAYS ;
2022-04-01 00:59:19 +02:00
if ( realtimeMode & & useMainSegmentOnly ) {
2022-08-19 21:37:26 +02:00
strip . getMainSegment ( ) . freeze = ! realtimeOverride ;
2022-04-01 00:59:19 +02:00
}
2021-11-25 22:05:16 +01:00
if ( root . containsKey ( " live " ) ) {
2022-03-25 16:36:05 +01:00
if ( root [ " live " ] . as < bool > ( ) ) {
2022-03-10 20:40:48 +01:00
transitionDelayTemp = 0 ;
jsonTransitionOnce = true ;
realtimeLock ( 65000 ) ;
2022-03-25 16:36:05 +01:00
} else {
2022-04-01 00:59:19 +02:00
exitRealtime ( ) ;
2022-03-10 20:40:48 +01:00
}
2021-11-25 22:05:16 +01:00
}
int it = 0 ;
JsonVariant segVar = root [ " seg " ] ;
if ( segVar . is < JsonObject > ( ) )
{
int id = segVar [ " id " ] | - 1 ;
2022-01-14 14:27:11 +01:00
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if ( id < 0 ) {
//apply all selected segments
//bool didSet = false;
2022-07-19 22:14:46 +02:00
for ( size_t s = 0 ; s < strip . getSegmentsNum ( ) ; s + + ) {
2022-07-06 13:13:54 +02:00
Segment & sg = strip . getSegment ( s ) ;
2022-07-17 15:58:41 +02:00
if ( sg . isSelected ( ) ) {
deserializeSegment ( segVar , s , presetId ) ;
//didSet = true;
2021-11-25 22:05:16 +01:00
}
}
2022-01-14 14:27:11 +01:00
//TODO: not sure if it is good idea to change first active but unselected segment
2022-02-20 22:24:11 +01:00
//if (!didSet) deserializeSegment(segVar, strip.getMainSegmentId(), presetId);
2022-01-14 14:27:11 +01:00
} else {
deserializeSegment ( segVar , id , presetId ) ; //apply only the segment with the specified ID
2021-11-25 22:05:16 +01:00
}
} else {
2023-03-10 15:20:50 +01:00
size_t deleted = 0 ;
2021-11-25 22:05:16 +01:00
JsonArray segs = segVar . as < JsonArray > ( ) ;
2022-07-19 16:16:43 +02:00
for ( JsonObject elem : segs ) {
2023-03-10 15:20:50 +01:00
if ( deserializeSegment ( elem , it + + , presetId ) & & ! elem [ " stop " ] . isNull ( ) & & elem [ " stop " ] = = 0 ) deleted + + ;
2021-11-25 22:05:16 +01:00
}
2023-03-10 15:20:50 +01:00
if ( strip . getSegmentsNum ( ) > 3 & & deleted > = strip . getSegmentsNum ( ) / 2U ) strip . purgeSegments ( ) ; // batch deleting more than half segments
2021-11-25 22:05:16 +01:00
}
2022-02-20 22:24:11 +01:00
2021-11-25 22:05:16 +01:00
usermods . readFromJsonState ( root ) ;
2021-12-04 01:05:01 +01:00
loadLedmap = root [ F ( " ledmap " ) ] | loadLedmap ;
2021-11-25 22:05:16 +01:00
byte ps = root [ F ( " psave " ) ] ;
2022-06-05 10:16:56 +02:00
if ( ps > 0 & & ps < 251 ) savePreset ( ps , nullptr , root ) ;
2021-11-25 22:05:16 +01:00
2022-06-03 22:22:18 +02:00
ps = root [ F ( " pdel " ) ] ; //deletion
2022-06-05 10:16:56 +02:00
if ( ps > 0 & & ps < 251 ) deletePreset ( ps ) ;
// HTTP API commands (must be handled before "ps")
const char * httpwin = root [ " win " ] ;
if ( httpwin ) {
String apireq = " win " ; apireq + = ' & ' ; // reduce flash string usage
apireq + = httpwin ;
handleSet ( nullptr , apireq , false ) ; // may set stateChanged
2022-06-03 22:22:18 +02:00
}
2022-06-03 18:38:46 +02:00
2022-12-13 14:27:44 +01:00
// applying preset (2 cases: a) API call includes all preset values ("pd"), b) API only specifies preset ID ("ps"))
byte presetToRestore = 0 ;
// a) already applied preset content (requires "seg" or "win" but will ignore the rest)
if ( ! root [ " pd " ] . isNull ( ) & & stateChanged ) {
currentPreset = root [ F ( " pd " ) ] | currentPreset ;
2023-06-27 16:01:20 +02:00
if ( root [ " win " ] . isNull ( ) ) presetCycCurr = currentPreset ; // otherwise it was set in handleSet() [set.cpp]
2022-12-13 14:27:44 +01:00
presetToRestore = currentPreset ; // stateUpdated() will clear the preset, so we need to restore it after
//unloadPlaylist(); // applying a preset unloads the playlist, may be needed here too?
} else if ( ! root [ " ps " ] . isNull ( ) ) {
2022-06-03 22:22:18 +02:00
ps = presetCycCurr ;
2022-12-13 14:27:44 +01:00
if ( root [ " win " ] . isNull ( ) & & getVal ( root [ " ps " ] , & ps , 0 , 0 ) & & ps > 0 & & ps < 251 & & ps ! = currentPreset ) {
2022-09-09 17:16:52 +02:00
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
2022-06-03 22:22:18 +02:00
presetCycCurr = ps ;
2022-12-13 14:27:44 +01:00
unloadPlaylist ( ) ; // applying a preset unloads the playlist
applyPreset ( ps , callMode ) ; // async load from file system (only preset ID was specified)
return stateResponse ;
2021-11-25 22:05:16 +01:00
}
}
JsonObject playlist = root [ F ( " playlist " ) ] ;
if ( ! playlist . isNull ( ) & & loadPlaylist ( playlist , presetId ) ) {
//do not notify here, because the first playlist entry will do
if ( root [ " on " ] . isNull ( ) ) callMode = CALL_MODE_NO_NOTIFY ;
else callMode = CALL_MODE_DIRECT_CHANGE ; // possible bugfix for playlist only containing HTTP API preset FX=~
}
2023-04-14 17:15:02 +02:00
if ( root . containsKey ( F ( " rmcpal " ) ) & & root [ F ( " rmcpal " ) ] . as < bool > ( ) ) {
if ( strip . customPalettes . size ( ) ) {
char fileName [ 32 ] ;
sprintf_P ( fileName , PSTR ( " /palette%d.json " ) , strip . customPalettes . size ( ) - 1 ) ;
if ( WLED_FS . exists ( fileName ) ) WLED_FS . remove ( fileName ) ;
strip . loadCustomPalettes ( ) ;
}
}
2022-02-20 22:24:11 +01:00
stateUpdated ( callMode ) ;
2022-12-13 14:27:44 +01:00
if ( presetToRestore ) currentPreset = presetToRestore ;
2021-11-25 22:05:16 +01:00
return stateResponse ;
}
2022-07-06 13:13:54 +02:00
void serializeSegment ( JsonObject & root , Segment & seg , byte id , bool forPreset , bool segmentBounds )
2021-11-25 22:05:16 +01:00
{
2022-01-25 12:47:14 +01:00
root [ " id " ] = id ;
2021-11-25 22:05:16 +01:00
if ( segmentBounds ) {
root [ " start " ] = seg . start ;
root [ " stop " ] = seg . stop ;
2023-07-12 20:52:34 +02:00
# ifndef WLED_DISABLE_2D
2022-05-08 10:50:48 +02:00
if ( strip . isMatrix ) {
root [ F ( " startY " ) ] = seg . startY ;
root [ F ( " stopY " ) ] = seg . stopY ;
}
2023-07-12 20:52:34 +02:00
# endif
2021-11-25 22:05:16 +01:00
}
2023-07-12 20:52:34 +02:00
if ( ! forPreset ) root [ " len " ] = seg . stop - seg . start ;
2022-08-19 21:37:26 +02:00
root [ " grp " ] = seg . grouping ;
2021-11-25 22:05:16 +01:00
root [ F ( " spc " ) ] = seg . spacing ;
2022-08-19 21:37:26 +02:00
root [ F ( " of " ) ] = seg . offset ;
root [ " on " ] = seg . on ;
root [ " frz " ] = seg . freeze ;
byte segbri = seg . opacity ;
root [ " bri " ] = ( segbri ) ? segbri : 255 ;
root [ " cct " ] = seg . cct ;
2023-05-15 17:06:29 +02:00
root [ F ( " set " ) ] = seg . set ;
2021-11-25 22:05:16 +01:00
2021-09-18 01:20:17 +02:00
if ( segmentBounds & & seg . name ! = nullptr ) root [ " n " ] = reinterpret_cast < const char * > ( seg . name ) ; //not good practice, but decreases required JSON buffer
2021-11-25 22:05:16 +01:00
// to conserve RAM we will serialize the col array manually
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
char colstr [ 70 ] ; colstr [ 0 ] = ' [ ' ; colstr [ 1 ] = ' \0 ' ; //max len 68 (5 chan, all 255)
2022-02-04 13:28:00 +01:00
const char * format = strip . hasWhiteChannel ( ) ? PSTR ( " [%u,%u,%u,%u] " ) : PSTR ( " [%u,%u,%u] " ) ;
2022-07-19 22:14:46 +02:00
for ( size_t i = 0 ; i < 3 ; i + + )
2021-11-25 22:05:16 +01:00
{
byte segcol [ 4 ] ; byte * c = segcol ;
2022-02-20 22:24:11 +01:00
segcol [ 0 ] = R ( seg . colors [ i ] ) ;
segcol [ 1 ] = G ( seg . colors [ i ] ) ;
segcol [ 2 ] = B ( seg . colors [ i ] ) ;
segcol [ 3 ] = W ( seg . colors [ i ] ) ;
2021-11-25 22:05:16 +01:00
char tmpcol [ 22 ] ;
sprintf_P ( tmpcol , format , ( unsigned ) c [ 0 ] , ( unsigned ) c [ 1 ] , ( unsigned ) c [ 2 ] , ( unsigned ) c [ 3 ] ) ;
2022-02-01 18:21:30 +01:00
strcat ( colstr , i < 2 ? strcat ( tmpcol , " , " ) : tmpcol ) ;
2021-11-25 22:05:16 +01:00
}
2022-02-01 18:21:30 +01:00
strcat ( colstr , " ] " ) ;
2021-11-25 22:05:16 +01:00
root [ " col " ] = serialized ( colstr ) ;
2022-08-31 08:02:11 +02:00
root [ " fx " ] = seg . mode ;
2022-08-31 04:31:56 +02:00
root [ " sx " ] = seg . speed ;
root [ " ix " ] = seg . intensity ;
2022-08-31 08:02:11 +02:00
root [ " pal " ] = seg . palette ;
2022-08-31 04:31:56 +02:00
root [ " c1 " ] = seg . custom1 ;
root [ " c2 " ] = seg . custom2 ;
root [ " c3 " ] = seg . custom3 ;
root [ " sel " ] = seg . isSelected ( ) ;
2022-08-31 08:02:11 +02:00
root [ " rev " ] = seg . reverse ;
2022-08-31 04:31:56 +02:00
root [ " mi " ] = seg . mirror ;
2023-01-06 09:24:29 +01:00
# ifndef WLED_DISABLE_2D
2022-05-08 10:50:48 +02:00
if ( strip . isMatrix ) {
2022-08-31 04:31:56 +02:00
root [ " rY " ] = seg . reverse_y ;
root [ " mY " ] = seg . mirror_y ;
2022-08-19 21:37:26 +02:00
root [ F ( " tp " ) ] = seg . transpose ;
2022-05-08 10:50:48 +02:00
}
2023-01-06 09:24:29 +01:00
# endif
root [ " o1 " ] = seg . check1 ;
root [ " o2 " ] = seg . check2 ;
root [ " o3 " ] = seg . check3 ;
root [ " si " ] = seg . soundSim ;
2022-11-24 04:15:24 +01:00
root [ " m12 " ] = seg . map1D2D ;
2021-11-25 22:05:16 +01:00
}
2022-10-08 18:25:51 +02:00
void serializeState ( JsonObject root , bool forPreset , bool includeBri , bool segmentBounds , bool selectedSegmentsOnly )
2021-11-25 22:05:16 +01:00
{
if ( includeBri ) {
root [ " on " ] = ( bri > 0 ) ;
root [ " bri " ] = briLast ;
root [ F ( " transition " ) ] = transitionDelay / 100 ; //in 100ms
}
if ( ! forPreset ) {
2022-01-15 14:04:16 +01:00
if ( errorFlag ) { root [ F ( " error " ) ] = errorFlag ; errorFlag = ERR_NONE ; } //prevent error message to persist on screen
2021-11-25 22:05:16 +01:00
2021-11-23 13:17:33 +01:00
root [ " ps " ] = ( currentPreset > 0 ) ? currentPreset : - 1 ;
2021-11-25 22:05:16 +01:00
root [ F ( " pl " ) ] = currentPlaylist ;
usermods . addToJsonState ( root ) ;
JsonObject nl = root . createNestedObject ( " nl " ) ;
nl [ " on " ] = nightlightActive ;
2022-02-04 10:10:37 +01:00
nl [ " dur " ] = nightlightDelayMins ;
nl [ " mode " ] = nightlightMode ;
2021-11-25 22:05:16 +01:00
nl [ F ( " tbri " ) ] = nightlightTargetBri ;
if ( nightlightActive ) {
nl [ F ( " rem " ) ] = ( nightlightDelayMs - ( millis ( ) - nightlightStartTime ) ) / 1000 ; // seconds remaining
} else {
nl [ F ( " rem " ) ] = - 1 ;
}
JsonObject udpn = root . createNestedObject ( " udpn " ) ;
2023-09-10 18:52:14 +02:00
udpn [ " send " ] = sendNotificationsRT ;
udpn [ " recv " ] = receiveGroups ! = 0 ;
2023-08-10 21:52:29 +02:00
udpn [ " sgrp " ] = syncGroups ;
udpn [ " rgrp " ] = receiveGroups ;
2021-11-25 22:05:16 +01:00
2022-03-28 22:36:58 +02:00
root [ F ( " lor " ) ] = realtimeOverride ;
2021-11-25 22:05:16 +01:00
}
root [ F ( " mainseg " ) ] = strip . getMainSegmentId ( ) ;
JsonArray seg = root . createNestedArray ( " seg " ) ;
2022-07-19 22:14:46 +02:00
for ( size_t s = 0 ; s < strip . getMaxSegments ( ) ; s + + ) {
2022-07-19 16:16:43 +02:00
if ( s > = strip . getSegmentsNum ( ) ) {
2022-10-08 18:25:51 +02:00
if ( forPreset & & segmentBounds & & ! selectedSegmentsOnly ) { //disable segments not part of preset
2022-07-17 15:58:41 +02:00
JsonObject seg0 = seg . createNestedObject ( ) ;
seg0 [ " stop " ] = 0 ;
continue ;
} else
break ;
}
2022-07-06 13:13:54 +02:00
Segment & sg = strip . getSegment ( s ) ;
2022-10-08 18:25:51 +02:00
if ( forPreset & & selectedSegmentsOnly & & ! sg . isSelected ( ) ) continue ;
2022-01-14 14:27:11 +01:00
if ( sg . isActive ( ) ) {
2021-11-25 22:05:16 +01:00
JsonObject seg0 = seg . createNestedObject ( ) ;
s erializeSegment ( seg0 , sg , s , forPreset , segmentBounds ) ;
} else if ( forPreset & & segmentBounds ) { //disable segments not part of preset
JsonObject seg0 = seg . createNestedObject ( ) ;
seg0 [ " stop " ] = 0 ;
}
}
}
void serializeInfo ( JsonObject root )
{
root [ F ( " ver " ) ] = versionString ;
root [ F ( " vid " ) ] = VERSION ;
2023-09-26 20:34:52 +02:00
root [ F ( " cn " ) ] = F ( WLED_CODENAME ) ;
2021-11-25 22:05:16 +01:00
JsonObject leds = root . createNestedObject ( " leds " ) ;
leds [ F ( " count " ) ] = strip . getLengthTotal ( ) ;
leds [ F ( " pwr " ) ] = strip . currentMilliamps ;
2021-12-25 01:30:27 +01:00
leds [ " fps " ] = strip . getFps ( ) ;
2021-11-25 22:05:16 +01:00
leds [ F ( " maxpwr " ) ] = ( strip . currentMilliamps ) ? strip . ablMilliampsMax : 0 ;
leds [ F ( " maxseg " ) ] = strip . getMaxSegments ( ) ;
2022-09-14 22:28:06 +02:00
//leds[F("actseg")] = strip.getActiveSegmentsNum();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
2022-05-08 10:50:48 +02:00
2022-07-10 22:23:25 +02:00
# ifndef WLED_DISABLE_2D
2022-05-08 10:50:48 +02:00
if ( strip . isMatrix ) {
JsonObject matrix = leds . createNestedObject ( " matrix " ) ;
2022-12-16 22:31:07 +01:00
matrix [ " w " ] = Segment : : maxWidth ;
matrix [ " h " ] = Segment : : maxHeight ;
2022-05-08 10:50:48 +02:00
}
2022-07-10 22:23:25 +02:00
# endif
2022-05-08 10:50:48 +02:00
2022-02-20 22:24:11 +01:00
uint8_t totalLC = 0 ;
JsonArray lcarr = leds . createNestedArray ( F ( " seglc " ) ) ;
2022-07-19 22:14:46 +02:00
size_t nSegs = strip . getSegmentsNum ( ) ;
for ( size_t s = 0 ; s < nSegs ; s + + ) {
if ( ! strip . getSegment ( s ) . isActive ( ) ) continue ;
2022-02-20 22:24:11 +01:00
uint8_t lc = strip . getSegment ( s ) . getLightCapabilities ( ) ;
totalLC | = lc ;
lcarr . add ( lc ) ;
}
leds [ " lc " ] = totalLC ;
2021-11-25 22:05:16 +01:00
2022-03-08 02:16:33 +01:00
leds [ F ( " rgbw " ) ] = strip . hasRGBWBus ( ) ; // deprecated, use info.leds.lc
leds [ F ( " wv " ) ] = totalLC & 0x02 ; // deprecated, true if white slider should be displayed for any segment
leds [ " cct " ] = totalLC & 0x04 ; // deprecated, use info.leds.lc
2022-08-14 13:05:59 +02:00
# ifdef WLED_DEBUG
JsonArray i2c = root . createNestedArray ( F ( " i2c " ) ) ;
i2c . add ( i2c_sda ) ;
i2c . add ( i2c_scl ) ;
JsonArray spi = root . createNestedArray ( F ( " spi " ) ) ;
spi . add ( spi_mosi ) ;
spi . add ( spi_sclk ) ;
2022-08-31 21:31:59 +02:00
spi . add ( spi_miso ) ;
2022-08-14 13:05:59 +02:00
# endif
2023-09-10 18:52:14 +02:00
root [ F ( " str " ) ] = false ; //syncToggleReceive;
2021-11-25 22:05:16 +01:00
root [ F ( " name " ) ] = serverDescription ;
root [ F ( " udpport " ) ] = udpPort ;
root [ " live " ] = ( bool ) realtimeMode ;
2022-03-28 22:36:58 +02:00
root [ F ( " liveseg " ) ] = useMainSegmentOnly ? strip . getMainSegmentId ( ) : - 1 ; // if using main segment only for live
2021-11-25 22:05:16 +01:00
switch ( realtimeMode ) {
case REALTIME_MODE_INACTIVE : root [ " lm " ] = " " ; break ;
case REALTIME_MODE_GENERIC : root [ " lm " ] = " " ; break ;
case REALTIME_MODE_UDP : root [ " lm " ] = F ( " UDP " ) ; break ;
case REALTIME_MODE_HYPERION : root [ " lm " ] = F ( " Hyperion " ) ; break ;
case REALTIME_MODE_E131 : root [ " lm " ] = F ( " E1.31 " ) ; break ;
case REALTIME_MODE_ADALIGHT : root [ " lm " ] = F ( " USB Adalight/TPM2 " ) ; break ;
case REALTIME_MODE_ARTNET : root [ " lm " ] = F ( " Art-Net " ) ; break ;
case REALTIME_MODE_TPM2NET : root [ " lm " ] = F ( " tpm2.net " ) ; break ;
case REALTIME_MODE_DDP : root [ " lm " ] = F ( " DDP " ) ; break ;
}
if ( realtimeIP [ 0 ] = = 0 )
{
root [ F ( " lip " ) ] = " " ;
} else {
root [ F ( " lip " ) ] = realtimeIP . toString ( ) ;
}
# ifdef WLED_ENABLE_WEBSOCKETS
root [ F ( " ws " ) ] = ws . count ( ) ;
# else
root [ F ( " ws " ) ] = - 1 ;
# endif
root [ F ( " fxcount " ) ] = strip . getModeCount ( ) ;
root [ F ( " palcount " ) ] = strip . getPaletteCount ( ) ;
2022-11-25 17:33:29 +01:00
root [ F ( " cpalcount " ) ] = strip . customPalettes . size ( ) ; //number of custom palettes
2021-11-25 22:05:16 +01:00
2022-02-10 23:14:48 +01:00
JsonArray ledmaps = root . createNestedArray ( F ( " maps " ) ) ;
2023-02-14 17:11:58 +01:00
for ( size_t i = 0 ; i < WLED_MAX_LEDMAPS ; i + + ) {
if ( ( ledMaps > > i ) & 0x00000001U ) {
JsonObject ledmaps0 = ledmaps . createNestedObject ( ) ;
ledmaps0 [ " id " ] = i ;
# ifndef ESP8266
2023-02-14 20:25:26 +01:00
if ( i & & ledmapNames [ i - 1 ] ) ledmaps0 [ " n " ] = ledmapNames [ i - 1 ] ;
2023-02-14 17:11:58 +01:00
# endif
}
2022-02-10 23:14:48 +01:00
}
2021-11-25 22:05:16 +01:00
JsonObject wifi_info = root . createNestedObject ( " wifi " ) ;
wifi_info [ F ( " bssid " ) ] = WiFi . BSSIDstr ( ) ;
int qrssi = WiFi . RSSI ( ) ;
wifi_info [ F ( " rssi " ) ] = qrssi ;
wifi_info [ F ( " signal " ) ] = getSignalQuality ( qrssi ) ;
wifi_info [ F ( " channel " ) ] = WiFi . channel ( ) ;
JsonObject fs_info = root . createNestedObject ( " fs " ) ;
fs_info [ " u " ] = fsBytesUsed / 1000 ;
fs_info [ " t " ] = fsBytesTotal / 1000 ;
fs_info [ F ( " pmt " ) ] = presetsModifiedTime ;
root [ F ( " ndc " ) ] = nodeListEnabled ? ( int ) Nodes . size ( ) : - 1 ;
2023-01-06 09:24:29 +01:00
2021-11-25 22:05:16 +01:00
# ifdef ARDUINO_ARCH_ESP32
# ifdef WLED_DEBUG
wifi_info [ F ( " txPower " ) ] = ( int ) WiFi . getTxPower ( ) ;
wifi_info [ F ( " sleep " ) ] = ( bool ) WiFi . getSleep ( ) ;
# endif
2022-09-11 00:19:32 +02:00
# if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
root [ F ( " arch " ) ] = " esp32 " ;
# else
root [ F ( " arch " ) ] = ESP . getChipModel ( ) ;
# endif
2021-11-25 22:05:16 +01:00
root [ F ( " core " ) ] = ESP . getSdkVersion ( ) ;
//root[F("maxalloc")] = ESP.getMaxAllocHeap();
# ifdef WLED_DEBUG
root [ F ( " resetReason0 " ) ] = ( int ) rtc_get_reset_reason ( 0 ) ;
root [ F ( " resetReason1 " ) ] = ( int ) rtc_get_reset_reason ( 1 ) ;
# endif
root [ F ( " lwip " ) ] = 0 ; //deprecated
# else
root [ F ( " arch " ) ] = " esp8266 " ;
root [ F ( " core " ) ] = ESP . getCoreVersion ( ) ;
//root[F("maxalloc")] = ESP.getMaxFreeBlockSize();
# ifdef WLED_DEBUG
root [ F ( " resetReason " ) ] = ( int ) ESP . getResetInfoPtr ( ) - > reason ;
# endif
root [ F ( " lwip " ) ] = LWIP_VERSION_MAJOR ;
# endif
root [ F ( " freeheap " ) ] = ESP . getFreeHeap ( ) ;
2023-05-28 22:50:19 +02:00
# if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
2021-11-25 22:05:16 +01:00
if ( psramFound ( ) ) root [ F ( " psram " ) ] = ESP . getFreePsram ( ) ;
# endif
root [ F ( " uptime " ) ] = millis ( ) / 1000 + rolloverMillis * 4294967 ;
2023-08-29 23:44:08 +02:00
char time [ 32 ] ;
getTimeString ( time ) ;
root [ F ( " time " ) ] = time ;
2021-11-25 22:05:16 +01:00
usermods . addToJsonInfo ( root ) ;
2022-11-10 21:50:21 +01:00
uint16_t os = 0 ;
2021-11-25 22:05:16 +01:00
# ifdef WLED_DEBUG
os = 0x80 ;
2022-11-10 21:50:21 +01:00
# ifdef WLED_DEBUG_HOST
os | = 0x0100 ;
if ( ! netDebugEnabled ) os & = ~ 0x0080 ;
# endif
2021-11-25 22:05:16 +01:00
# endif
# ifndef WLED_DISABLE_ALEXA
os + = 0x40 ;
# endif
2023-02-21 17:07:32 +01:00
//os += 0x20; // indicated now removed Blynk support, may be reused to indicate another build-time option
2022-03-06 23:47:36 +01:00
# ifdef USERMOD_CRONIXIE
2021-11-25 22:05:16 +01:00
os + = 0x10 ;
# endif
# ifndef WLED_DISABLE_FILESYSTEM
os + = 0x08 ;
# endif
# ifndef WLED_DISABLE_HUESYNC
os + = 0x04 ;
# endif
# ifdef WLED_ENABLE_ADALIGHT
os + = 0x02 ;
# endif
# ifndef WLED_DISABLE_OTA
os + = 0x01 ;
# endif
root [ F ( " opt " ) ] = os ;
root [ F ( " brand " ) ] = " WLED " ;
root [ F ( " product " ) ] = F ( " FOSS " ) ;
root [ " mac " ] = escapedMac ;
char s [ 16 ] = " " ;
if ( Network . isConnected ( ) )
{
IPAddress localIP = Network . localIP ( ) ;
sprintf ( s , " %d.%d.%d.%d " , localIP [ 0 ] , localIP [ 1 ] , localIP [ 2 ] , localIP [ 3 ] ) ;
}
root [ " ip " ] = s ;
}
void setPaletteColors ( JsonArray json , CRGBPalette16 palette )
{
for ( int i = 0 ; i < 16 ; i + + ) {
JsonArray colors = json . createNestedArray ( ) ;
CRGB color = palette [ i ] ;
2022-01-02 14:27:24 +01:00
colors . add ( i < < 4 ) ;
2021-11-25 22:05:16 +01:00
colors . add ( color . red ) ;
colors . add ( color . green ) ;
colors . add ( color . blue ) ;
}
}
void setPaletteColors ( JsonArray json , byte * tcp )
{
TRGBGradientPaletteEntryUnion * ent = ( TRGBGradientPaletteEntryUnion * ) ( tcp ) ;
TRGBGradientPaletteEntryUnion u ;
// Count entries
uint16_t count = 0 ;
do {
u = * ( ent + count ) ;
count + + ;
} while ( u . index ! = 255 ) ;
u = * ent ;
int indexstart = 0 ;
while ( indexstart < 255 ) {
indexstart = u . index ;
JsonArray colors = json . createNestedArray ( ) ;
colors . add ( u . index ) ;
colors . add ( u . r ) ;
colors . add ( u . g ) ;
colors . add ( u . b ) ;
ent + + ;
u = * ent ;
}
}
2023-05-30 19:36:14 +02:00
void serializePalettes ( JsonObject root , int page )
2021-11-25 22:05:16 +01:00
{
2022-07-28 23:19:58 +02:00
byte tcp [ 72 ] ;
2021-11-25 22:05:16 +01:00
# ifdef ESP8266
int itemPerPage = 5 ;
# else
int itemPerPage = 8 ;
# endif
int palettesCount = strip . getPaletteCount ( ) ;
2022-07-28 23:19:58 +02:00
int customPalettes = strip . customPalettes . size ( ) ;
2021-11-25 22:05:16 +01:00
2022-07-28 23:19:58 +02:00
int maxPage = ( palettesCount + customPalettes - 1 ) / itemPerPage ;
2021-11-25 22:05:16 +01:00
if ( page > maxPage ) page = maxPage ;
int start = itemPerPage * page ;
int end = start + itemPerPage ;
2022-07-28 23:19:58 +02:00
if ( end > palettesCount + customPalettes ) end = palettesCount + customPalettes ;
2021-11-25 22:05:16 +01:00
2022-07-28 23:19:58 +02:00
root [ F ( " m " ) ] = maxPage ; // inform caller how many pages there are
2021-11-25 22:05:16 +01:00
JsonObject palettes = root . createNestedObject ( " p " ) ;
for ( int i = start ; i < end ; i + + ) {
2022-07-28 23:19:58 +02:00
JsonArray curPalette = palettes . createNestedArray ( String ( i > = palettesCount ? 255 - i + palettesCount : i ) ) ;
2021-11-25 22:05:16 +01:00
switch ( i ) {
case 0 : //default palette
2023-01-06 09:24:29 +01:00
setPaletteColors ( curPalette , PartyColors_p ) ;
2021-11-25 22:05:16 +01:00
break ;
case 1 : //random
curPalette . add ( " r " ) ;
curPalette . add ( " r " ) ;
curPalette . add ( " r " ) ;
curPalette . add ( " r " ) ;
break ;
case 2 : //primary color only
curPalette . add ( " c1 " ) ;
break ;
case 3 : //primary + secondary
curPalette . add ( " c1 " ) ;
curPalette . add ( " c1 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c2 " ) ;
break ;
case 4 : //primary + secondary + tertiary
curPalette . add ( " c3 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c1 " ) ;
break ;
2022-02-28 23:32:24 +01:00
case 5 : //primary + secondary (+tert if not off), more distinct
2021-11-25 22:05:16 +01:00
curPalette . add ( " c1 " ) ;
curPalette . add ( " c1 " ) ;
curPalette . add ( " c1 " ) ;
curPalette . add ( " c1 " ) ;
curPalette . add ( " c1 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c2 " ) ;
curPalette . add ( " c3 " ) ;
curPalette . add ( " c3 " ) ;
curPalette . add ( " c3 " ) ;
curPalette . add ( " c3 " ) ;
curPalette . add ( " c3 " ) ;
curPalette . add ( " c1 " ) ;
2022-02-28 23:32:24 +01:00
break ;
2021-11-25 22:05:16 +01:00
case 6 : //Party colors
setPaletteColors ( curPalette , PartyColors_p ) ;
break ;
case 7 : //Cloud colors
setPaletteColors ( curPalette , CloudColors_p ) ;
break ;
case 8 : //Lava colors
setPaletteColors ( curPalette , LavaColors_p ) ;
break ;
case 9 : //Ocean colors
setPaletteColors ( curPalette , OceanColors_p ) ;
break ;
case 10 : //Forest colors
setPaletteColors ( curPalette , ForestColors_p ) ;
break ;
case 11 : //Rainbow colors
setPaletteColors ( curPalette , RainbowColors_p ) ;
break ;
case 12 : //Rainbow stripe colors
setPaletteColors ( curPalette , RainbowStripeColors_p ) ;
break ;
default :
2022-02-28 23:32:24 +01:00
{
2022-07-28 23:19:58 +02:00
if ( i > = palettesCount ) {
setPaletteColors ( curPalette , strip . customPalettes [ i - palettesCount ] ) ;
} else {
memcpy_P ( tcp , ( byte * ) pgm_read_dword ( & ( gGradientPalettes [ i - 13 ] ) ) , 72 ) ;
setPaletteColors ( curPalette , tcp ) ;
}
2022-02-28 23:32:24 +01:00
}
2021-11-25 22:05:16 +01:00
break ;
}
}
}
2022-11-24 02:50:24 +01:00
void serializeNetworks ( JsonObject root )
{
JsonArray networks = root . createNestedArray ( F ( " networks " ) ) ;
int16_t status = WiFi . scanComplete ( ) ;
switch ( status ) {
case WIFI_SCAN_FAILED :
WiFi . scanNetworks ( true ) ;
return ;
case WIFI_SCAN_RUNNING :
return ;
}
for ( int i = 0 ; i < status ; i + + ) {
JsonObject node = networks . createNestedObject ( ) ;
node [ " ssid " ] = WiFi . SSID ( i ) ;
node [ " rssi " ] = WiFi . RSSI ( i ) ;
node [ " bssid " ] = WiFi . BSSIDstr ( i ) ;
node [ " channel " ] = WiFi . channel ( i ) ;
node [ " enc " ] = WiFi . encryptionType ( i ) ;
}
WiFi . scanDelete ( ) ;
if ( WiFi . scanComplete ( ) = = WIFI_SCAN_FAILED ) {
WiFi . scanNetworks ( true ) ;
}
}
2021-11-25 22:05:16 +01:00
void serializeNodes ( JsonObject root )
{
JsonArray nodes = root . createNestedArray ( " nodes " ) ;
for ( NodesMap : : iterator it = Nodes . begin ( ) ; it ! = Nodes . end ( ) ; + + it )
{
if ( it - > second . ip [ 0 ] ! = 0 )
{
JsonObject node = nodes . createNestedObject ( ) ;
node [ F ( " name " ) ] = it - > second . nodeName ;
node [ " type " ] = it - > second . nodeType ;
node [ " ip " ] = it - > second . ip . toString ( ) ;
node [ F ( " age " ) ] = it - > second . age ;
node [ F ( " vid " ) ] = it - > second . build ;
}
}
}
2022-11-11 03:10:41 +01:00
// deserializes mode data string into JsonArray
2021-11-25 22:05:16 +01:00
void serializeModeData ( JsonArray fxdata )
{
2023-08-17 16:24:16 +02:00
char lineBuffer [ 256 ] ;
2022-06-26 23:01:22 +02:00
for ( size_t i = 0 ; i < strip . getModeCount ( ) ; i + + ) {
2023-08-17 17:10:29 +02:00
strncpy_P ( lineBuffer , strip . getModeData ( i ) , sizeof ( lineBuffer ) / sizeof ( char ) - 1 ) ;
lineBuffer [ sizeof ( lineBuffer ) / sizeof ( char ) - 1 ] = ' \0 ' ; // terminate string
2022-08-31 04:31:56 +02:00
if ( lineBuffer [ 0 ] ! = 0 ) {
char * dataPtr = strchr ( lineBuffer , ' @ ' ) ;
2022-11-11 03:10:41 +01:00
if ( dataPtr ) fxdata . add ( dataPtr + 1 ) ;
2022-08-31 04:31:56 +02:00
else fxdata . add ( " " ) ;
2022-05-21 18:37:09 +02:00
}
}
2021-11-25 22:05:16 +01:00
}
// deserializes mode names string into JsonArray
2022-11-11 03:10:41 +01:00
// also removes effect data extensions (@...) from deserialised names
2023-08-17 16:24:16 +02:00
void serializeModeNames ( JsonArray arr )
{
char lineBuffer [ 256 ] ;
2022-06-26 23:01:22 +02:00
for ( size_t i = 0 ; i < strip . getModeCount ( ) ; i + + ) {
2023-08-17 17:10:29 +02:00
strncpy_P ( lineBuffer , strip . getModeData ( i ) , sizeof ( lineBuffer ) / sizeof ( char ) - 1 ) ;
lineBuffer [ sizeof ( lineBuffer ) / sizeof ( char ) - 1 ] = ' \0 ' ; // terminate string
2022-08-31 04:31:56 +02:00
if ( lineBuffer [ 0 ] ! = 0 ) {
char * dataPtr = strchr ( lineBuffer , ' @ ' ) ;
if ( dataPtr ) * dataPtr = 0 ; // terminate mode data after name
arr . add ( lineBuffer ) ;
2022-05-21 18:37:09 +02:00
}
}
2021-11-25 22:05:16 +01:00
}
void serveJson ( AsyncWebServerRequest * request )
{
byte subJson = 0 ;
const String & url = request - > url ( ) ;
2022-11-24 02:50:24 +01:00
if ( url . indexOf ( " state " ) > 0 ) subJson = JSON_PATH_STATE ;
else if ( url . indexOf ( " info " ) > 0 ) subJson = JSON_PATH_INFO ;
else if ( url . indexOf ( " si " ) > 0 ) subJson = JSON_PATH_STATE_INFO ;
else if ( url . indexOf ( " nodes " ) > 0 ) subJson = JSON_PATH_NODES ;
2023-05-30 19:36:14 +02:00
else if ( url . indexOf ( " eff " ) > 0 ) subJson = JSON_PATH_EFFECTS ;
2022-11-24 02:50:24 +01:00
else if ( url . indexOf ( " palx " ) > 0 ) subJson = JSON_PATH_PALETTES ;
else if ( url . indexOf ( " fxda " ) > 0 ) subJson = JSON_PATH_FXDATA ;
2023-05-30 19:36:14 +02:00
else if ( url . indexOf ( " net " ) > 0 ) subJson = JSON_PATH_NETWORKS ;
2022-02-01 12:02:04 +01:00
# ifdef WLED_ENABLE_JSONLIVE
2021-11-25 22:05:16 +01:00
else if ( url . indexOf ( " live " ) > 0 ) {
serveLiveLeds ( request ) ;
return ;
}
2022-02-01 12:02:04 +01:00
# endif
2021-11-25 22:05:16 +01:00
else if ( url . indexOf ( " pal " ) > 0 ) {
request - > send_P ( 200 , " application/json " , JSON_palette_names ) ;
return ;
}
else if ( url . indexOf ( " cfg " ) > 0 & & handleFileRead ( request , " /cfg.json " ) ) {
return ;
}
else if ( url . length ( ) > 6 ) { //not just /json
2022-08-09 15:20:00 +02:00
request - > send ( 501 , " application/json " , F ( " { \" error \" : \" Not implemented \" } " ) ) ;
2021-11-25 22:05:16 +01:00
return ;
}
2022-08-09 15:20:00 +02:00
if ( ! requestJSONBufferLock ( 17 ) ) {
request - > send ( 503 , " application/json " , F ( " { \" error \" :3} " ) ) ;
return ;
}
2023-05-30 19:36:14 +02:00
AsyncJsonResponse * response = new AsyncJsonResponse ( & doc , subJson = = JSON_PATH_FXDATA | | subJson = = JSON_PATH_EFFECTS ) ; // will clear and convert JsonDocument into JsonArray if necessary
2021-11-25 22:05:16 +01:00
JsonVariant lDoc = response - > getRoot ( ) ;
switch ( subJson )
{
2022-11-24 02:50:24 +01:00
case JSON_PATH_STATE :
2021-11-25 22:05:16 +01:00
serializeState ( lDoc ) ; break ;
2022-11-24 02:50:24 +01:00
case JSON_PATH_INFO :
2021-11-25 22:05:16 +01:00
serializeInfo ( lDoc ) ; break ;
2022-11-24 02:50:24 +01:00
case JSON_PATH_NODES :
2021-11-25 22:05:16 +01:00
serializeNodes ( lDoc ) ; break ;
2022-11-24 02:50:24 +01:00
case JSON_PATH_PALETTES :
2023-05-30 19:36:14 +02:00
serializePalettes ( lDoc , request - > hasParam ( " page " ) ? request - > getParam ( " page " ) - > value ( ) . toInt ( ) : 0 ) ; break ;
case JSON_PATH_EFFECTS :
serializeModeNames ( lDoc ) ; break ;
2022-11-24 02:50:24 +01:00
case JSON_PATH_FXDATA :
2023-05-30 19:36:14 +02:00
serializeModeData ( lDoc ) ; break ;
2022-11-24 02:50:24 +01:00
case JSON_PATH_NETWORKS :
serializeNetworks ( lDoc ) ; break ;
2021-11-25 22:05:16 +01:00
default : //all
JsonObject state = lDoc . createNestedObject ( " state " ) ;
serializeState ( state ) ;
JsonObject info = lDoc . createNestedObject ( " info " ) ;
serializeInfo ( info ) ;
2022-11-24 02:50:24 +01:00
if ( subJson ! = JSON_PATH_STATE_INFO )
2021-11-25 22:05:16 +01:00
{
JsonArray effects = lDoc . createNestedArray ( F ( " effects " ) ) ;
2022-05-24 16:43:21 +02:00
serializeModeNames ( effects ) ; // remove WLED-SR extensions from effect names
2021-11-25 22:05:16 +01:00
lDoc [ F ( " palettes " ) ] = serialized ( ( const __FlashStringHelper * ) JSON_palette_names ) ;
}
2022-08-31 04:31:56 +02:00
//lDoc["m"] = lDoc.memoryUsage(); // JSON buffer usage, for remote debugging
2021-11-25 22:05:16 +01:00
}
DEBUG_PRINTF ( " JSON buffer size: %u for request: %d \n " , lDoc . memoryUsage ( ) , subJson ) ;
2023-07-13 03:09:42 +02:00
# ifdef WLED_DEBUG
size_t len =
# endif
response - > setLength ( ) ;
2023-05-30 19:36:14 +02:00
DEBUG_PRINT ( F ( " JSON content length: " ) ) ; DEBUG_PRINTLN ( len ) ;
2021-11-25 22:05:16 +01:00
request - > send ( response ) ;
releaseJSONBufferLock ( ) ;
}
2022-02-01 12:02:04 +01:00
# ifdef WLED_ENABLE_JSONLIVE
2021-11-25 22:05:16 +01:00
# define MAX_LIVE_LEDS 180
bool serveLiveLeds ( AsyncWebServerRequest * request , uint32_t wsClient )
{
# ifdef WLED_ENABLE_WEBSOCKETS
AsyncWebSocketClient * wsc = nullptr ;
if ( ! request ) { //not HTTP, use Websockets
wsc = ws . client ( wsClient ) ;
if ( ! wsc | | wsc - > queueLength ( ) > 0 ) return false ; //only send if queue free
}
# endif
uint16_t used = strip . getLengthTotal ( ) ;
uint16_t n = ( used - 1 ) / MAX_LIVE_LEDS + 1 ; //only serve every n'th LED if count over MAX_LIVE_LEDS
char buffer [ 2000 ] ;
strcpy_P ( buffer , PSTR ( " { \" leds \" :[ " ) ) ;
obuf = buffer ;
olen = 9 ;
2022-07-19 22:14:46 +02:00
for ( size_t i = 0 ; i < used ; i + = n )
2021-11-25 22:05:16 +01:00
{
2022-01-26 13:26:57 +01:00
uint32_t c = strip . getPixelColor ( i ) ;
2023-07-06 22:48:13 +02:00
uint8_t r = R ( c ) ;
uint8_t g = G ( c ) ;
uint8_t b = B ( c ) ;
uint8_t w = W ( c ) ;
r = scale8 ( qadd8 ( w , r ) , strip . getBrightness ( ) ) ; //R, add white channel to RGB channels as a simple RGBW -> RGB map
g = scale8 ( qadd8 ( w , g ) , strip . getBrightness ( ) ) ; //G
b = scale8 ( qadd8 ( w , b ) , strip . getBrightness ( ) ) ; //B
2022-01-26 13:26:57 +01:00
olen + = sprintf ( obuf + olen , " \" %06X \" , " , RGBW32 ( r , g , b , 0 ) ) ;
2021-11-25 22:05:16 +01:00
}
olen - = 1 ;
oappend ( ( const char * ) F ( " ], \" n \" : " ) ) ;
oappendi ( n ) ;
oappend ( " } " ) ;
if ( request ) {
request - > send ( 200 , " application/json " , buffer ) ;
}
# ifdef WLED_ENABLE_WEBSOCKETS
else {
wsc - > text ( obuf , olen ) ;
}
# endif
return true ;
}
2022-11-24 02:50:24 +01:00
# endif