kopia lustrzana https://github.com/Aircoookie/WLED
				
				
				
			Playlist additions
							rodzic
							
								
									374457df70
								
							
						
					
					
						commit
						623694ab73
					
				
							
								
								
									
										12
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										12
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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?)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<const char*>() && root["on"].as<const char*>()[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<JsonArray>();
 | 
			
		||||
    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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue