diff --git a/CHANGELOG.md b/CHANGELOG.md index a989eaf32..596ab9067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ### Builds after release 0.12.0 +#### Build 2106100 + +- Added support for multiple buttons with various types (PR #1977) +- Fixed infinite playlists (PR #2020) +- Added `r` to playlist object, allows for shuffle regardless of the `repeat` value +- Improved accuracy of NTP time sync +- Added possibility for WLED UDP sync to sync system time +- Improved UDP sync accuracy, if both sender and receiver are NTP synced +- Fixed a cache issue with restored tabs +- Cache CORS request +- Disable WiFi sleep by default on ESP32 + #### Build 2105230 - No longer retain MQTT `/v` topic to alleviate storage loads on MQTT broker diff --git a/wled00/const.h b/wled00/const.h index 85dbab0e1..85f8c25fb 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -192,6 +192,9 @@ #define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed #define SEG_OPTION_TRANSITIONAL 7 +//Playlist option byte +#define PL_OPTION_SHUFFLE 0x01 + // WLED Error modes #define ERR_NONE 0 // All good :) #define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 2913078b1..e624e5248 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -97,8 +97,8 @@ void handleIR(); #include "src/dependencies/json/AsyncJson-v6.h" #include "FX.h" -void deserializeSegment(JsonObject elem, byte it); -bool deserializeState(JsonObject root); +void deserializeSegment(JsonObject elem, byte it, byte presetId = 0); +bool deserializeState(JsonObject root, byte presetId = 0); void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true); void serializeInfo(JsonObject root); @@ -155,7 +155,7 @@ void _drawOverlayCronixie(); //playlist.cpp void shufflePlaylist(); void unloadPlaylist(); -void loadPlaylist(JsonObject playlistObject); +void loadPlaylist(JsonObject playlistObject, byte presetId = 0); void handlePlaylist(); //presets.cpp diff --git a/wled00/json.cpp b/wled00/json.cpp index 9cd70c277..444cf52af 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -6,7 +6,7 @@ * JSON API (De)serialization */ -void deserializeSegment(JsonObject elem, byte it) +void deserializeSegment(JsonObject elem, byte it, byte presetId) { byte id = elem["id"] | it; if (id < strip.getMaxSegments()) @@ -95,13 +95,18 @@ void deserializeSegment(JsonObject elem, byte it) //temporary, strip object gets updated via colorUpdated() if (id == strip.getMainSegmentId()) { + byte effectPrev = effectCurrent; effectCurrent = elem[F("fx")] | effectCurrent; + if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually effectSpeed = elem[F("sx")] | effectSpeed; effectIntensity = elem[F("ix")] | effectIntensity; effectPalette = elem["pal"] | effectPalette; } else { //permanent byte fx = elem[F("fx")] | seg.mode; - if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx); + if (fx != seg.mode && fx < strip.getModeCount()) { + strip.setMode(id, fx); + if (!presetId) unloadPlaylist(); //stop playlist if active and FX changed manually + } seg.speed = elem[F("sx")] | seg.speed; seg.intensity = elem[F("ix")] | seg.intensity; seg.palette = elem["pal"] | seg.palette; @@ -156,7 +161,7 @@ void deserializeSegment(JsonObject elem, byte it) } } -bool deserializeState(JsonObject root) +bool deserializeState(JsonObject root, byte presetId) { strip.applyToAllSelected = false; bool stateResponse = root[F("v")] | false; @@ -168,12 +173,15 @@ bool deserializeState(JsonObject root) if (root["on"].is() && root["on"].as()[0] == 't') toggleOnOff(); - int tr = root[F("transition")] | -1; - if (tr >= 0) - { - transitionDelay = tr; - transitionDelay *= 100; - transitionDelayTemp = transitionDelay; + 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; + } } tr = root[F("tt")] | -1; @@ -245,20 +253,20 @@ bool deserializeState(JsonObject root) { if (lowestActive == 99) lowestActive = s; if (sg.isSelected()) { - deserializeSegment(segVar, s); + deserializeSegment(segVar, s, presetId); didSet = true; } } } - if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive); + if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId); } else { //set only the segment with the specified ID - deserializeSegment(segVar, it); + deserializeSegment(segVar, it, presetId); } } else { JsonArray segs = segVar.as(); for (JsonObject elem : segs) { - deserializeSegment(elem, it); + deserializeSegment(elem, it, presetId); it++; } } @@ -280,7 +288,11 @@ bool deserializeState(JsonObject root) deletePreset(ps); } ps = root["ps"] | -1; //load preset (clears state request!) - if (ps >= 0) {applyPreset(ps); return stateResponse;} + if (ps >= 0) { + if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually + applyPreset(ps); + return stateResponse; + } //HTTP API commands const char* httpwin = root["win"]; @@ -293,7 +305,7 @@ bool deserializeState(JsonObject root) JsonObject playlist = root[F("playlist")]; if (!playlist.isNull()) { - loadPlaylist(playlist); + loadPlaylist(playlist, presetId); noNotification = true; //do not notify both for this request and the first playlist entry } @@ -309,7 +321,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo root[F("start")] = seg.start; root["stop"] = seg.stop; } - if (!forPreset) root[F("len")] = seg.stop - seg.start; + if (!forPreset) root[F("len")] = seg.stop - seg.start; root[F("grp")] = seg.grouping; root[F("spc")] = seg.spacing; root["on"] = seg.getOption(SEG_OPTION_ON); diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index 190e2fdc0..ed45220bf 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -5,18 +5,24 @@ */ typedef struct PlaylistEntry { - uint8_t preset; - uint16_t dur; - uint16_t tr; + uint8_t preset; //ID of the preset to apply + uint16_t dur; //Duration of the entry (in tenths of seconds) + uint16_t tr; //Duration of the transition TO this entry (in tenths of seconds) } ple; -bool playlistEndless = false; -int8_t playlistRepeat = 1; -byte playlistEndPreset = 0; +byte playlistRepeat = 1; //how many times to repeat the playlist (0 = infinitely) +byte playlistEndPreset = 0; //what preset to apply after playlist end (0 = stay on last preset) +byte playlistOptions = 0; //bit 0: shuffle playlist after each iteration. bits 1-7 TBD + PlaylistEntry *playlistEntries = nullptr; -byte playlistLen; +byte playlistLen; //number of playlist entries int8_t playlistIndex = -1; -uint16_t playlistEntryDur = 0; +uint16_t playlistEntryDur = 0; //duration of the current entry in tenths of seconds + +//values we need to keep about the parent playlist while inside sub-playlist +//int8_t parentPlaylistIndex = -1; +//byte parentPlaylistRepeat = 0; +//byte parentPlaylistPresetId = 0; //for re-loading void shufflePlaylist() { @@ -42,12 +48,12 @@ void unloadPlaylist() { playlistEntries = nullptr; } currentPlaylist = playlistIndex = -1; - playlistLen = playlistEntryDur = 0; + playlistLen = playlistEntryDur = playlistOptions = 0; DEBUG_PRINTLN(F("Playlist unloaded.")); } -void loadPlaylist(JsonObject playlistObj) { +void loadPlaylist(JsonObject playlistObj, byte presetId) { unloadPlaylist(); JsonArray presets = playlistObj["ps"]; @@ -68,12 +74,12 @@ void loadPlaylist(JsonObject playlistObj) { it = 0; JsonArray durations = playlistObj["dur"]; if (durations.isNull()) { - playlistEntries[0].dur = playlistObj["dur"] | 100; + playlistEntries[0].dur = playlistObj["dur"] | 100; //10 seconds as fallback it = 1; } else { for (int dur : durations) { if (it >= playlistLen) break; - playlistEntries[it].dur = (dur > 0) ? dur : presetCycleTime; + playlistEntries[it].dur = (dur > 1) ? dur : 100; it++; } } @@ -93,12 +99,19 @@ void loadPlaylist(JsonObject playlistObj) { } for (int i = it; i < playlistLen; i++) playlistEntries[i].tr = playlistEntries[it -1].tr; - playlistRepeat = playlistObj[F("repeat")] | 0; + int rep = playlistObj[F("repeat")]; + bool shuffle = false; + if (rep < 0) { //support negative values as infinite + shuffle + rep = 0; shuffle = true; + } + + playlistRepeat = rep; + if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start playlistEndPreset = playlistObj[F("end")] | 0; + shuffle = shuffle || playlistObj["r"]; + if (shuffle) playlistOptions += PL_OPTION_SHUFFLE; - if (playlistRepeat <= 0) playlistRepeat--; // make it endless (-2 == endless & random) - - currentPlaylist = 0; //TODO here we need the preset ID where the playlist is saved + currentPlaylist = presetId; DEBUG_PRINTLN(F("Playlist loaded.")); } @@ -112,16 +125,16 @@ void handlePlaylist() { ++playlistIndex %= playlistLen; // -1 at 1st run (limit to playlistLen) - if (!playlistRepeat && !playlistIndex) { //stop if repeat == 0 and restart of playlist - unloadPlaylist(); - if (playlistEndPreset) applyPreset(playlistEndPreset); - return; - } // playlist roll-over if (!playlistIndex) { - // playlistRepeat < 0 => endless loop - if (playlistRepeat > 0) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist - if (playlistRepeat < -1) shufflePlaylist(); // shuffle playlist and start over + if (playlistRepeat == 1) { //stop if all repetitions are done + unloadPlaylist(); + if (playlistEndPreset) applyPreset(playlistEndPreset); + return; + } + if (playlistRepeat > 1) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist + // playlistRepeat == 0: endless loop + if (playlistOptions & PL_OPTION_SHUFFLE) shufflePlaylist(); // shuffle playlist and start over } jsonTransitionOnce = true; diff --git a/wled00/presets.cpp b/wled00/presets.cpp index c3c15afc3..568ff5fca 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -14,7 +14,7 @@ bool applyPreset(byte index) #ifdef WLED_DEBUG_FS serializeJson(*fileDoc, Serial); #endif - deserializeState(fdo); + deserializeState(fdo, index); } else { DEBUGFS_PRINTLN(F("Make read buf")); DynamicJsonDocument fDoc(JSON_BUFFER_SIZE); @@ -24,7 +24,7 @@ bool applyPreset(byte index) #ifdef WLED_DEBUG_FS serializeJson(fDoc, Serial); #endif - deserializeState(fdo); + deserializeState(fdo, index); } if (!errorFlag) { @@ -35,6 +35,7 @@ bool applyPreset(byte index) return false; } +//persist=false is not currently honored void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) { if (index == 0 || index > 250) return; diff --git a/wled00/wled.h b/wled00/wled.h index adddf9fe5..88055a16d 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2106070 +#define VERSION 2106100 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG