Adopted WLED-SR slider, color & palette control.

Added "freeze" toggle to a stopwatch icon.
This commit is contained in:
Blaz Kristan 2021-10-31 15:52:41 +01:00
parent 044d830b64
commit cc7d745ce6
7 changed files with 2709 additions and 2339 deletions

View File

@ -907,20 +907,147 @@ class WS2812FX {
transitionProgress(uint8_t tNr);
};
//10 names per line
// WLEDSR: extensions
// Technical notes
// ===============
// If an effect name is followed by an @, slider and color control is effective.
// See setSliderAndColorControl in index.js for implementation
// If not effective then:
// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown
// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown
// If effective (@)
// - a ; seperates slider controls (left) from color controls (middle) and palette control (right)
// - if left, middle or right is empty no controls are shown
// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value
// - a ! means that the default is used.
// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3
// - For colors: Fx color, Background color, Custom
// - For palette: prompt Color palette
//
// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle.
// If a color is specified, the 1,2 or 3 is replaced by that specification.
// Note: Effects can override default pattern behaviour
// - FadeToBlack can override the background setting
// - Defining SEGCOL(<i>) can override a specific palette using these values (e.g. Color Gradient)
const char JSON_mode_names[] PROGMEM = R"=====([
"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow",
"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random",
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Aurora","Stream",
"Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Police All",
"Two Dots","Two Areas","Running Dual","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple",
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",
"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow",
"Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise",
"Flow","Chunchun","Dancing Shadows","Washing Machine","Candy Cane","Blends","TV Simulator","Dynamic Smooth"
"Solid",
"Blink@;!;!",
"Breathe@Speed;,!;!",
"Wipe",
"Wipe Random",
"Random Colors",
"Sweep",
"Dynamic",
"Colorloop",
"Rainbow",
"Scan@!,# of dots;,!,?;!",
"Scan Dual@!,# of dots;,!,?;!",
"Fade",
"Theater",
"Theater Rainbow",
"Running",
"Saw",
"Twinkle",
"Dissolve",
"Dissolve Rnd",
"Sparkle",
"Sparkle Dark",
"Sparkle+",
"Strobe",
"Strobe Rainbow",
"Strobe Mega",
"Blink Rainbow",
"Android",
"Chase",
"Chase Random",
"Chase Rainbow",
"Chase Flash",
"Chase Flash Rnd",
"Rainbow Runner",
"Colorful",
"Traffic Light",
"Sweep Random",
"Running 2",
"Aurora",
"Stream",
"Scanner",
"Lighthouse",
"Fireworks",
"Rain",
"Tetrix@Speed,Width;Fx,BG,;!",
"Fire Flicker",
"Gradient",
"Loading",
"Police",
"Police All",
"Two Dots",
"Two Areas",
"Running Dual",
"Halloween",
"Tri Chase",
"Tri Wipe",
"Tri Fade",
"Lightning",
"ICU",
"Multi Comet",
"Scanner Dual",
"Stream 2",
"Oscillate",
"Pride 2015",
"Juggle",
"Palette",
"Fire 2012",
"Colorwaves",
"Bpm",
"Fill Noise",
"Noise 1",
"Noise 2",
"Noise 3",
"Noise 4",
"Colortwinkles",
"Lake",
"Meteor",
"Meteor Smooth",
"Railway",
"Ripple",
"Twinklefox",
"Twinklecat",
"Halloween Eyes",
"Solid Pattern",
"Solid Pattern Tri",
"Spots",
"Spots Fade",
"Glitter",
"Candle",
"Fireworks Starburst",
"Fireworks 1D@Gravity,Firing side;;!",
"Bouncing Balls",
"Sinelon",
"Sinelon Dual",
"Sinelon Rainbow",
"Popcorn",
"Drip@Gravity,# of drips;!,!;!",
"Plasma",
"Percent",
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi",
"Solid Glitter",
"Sunrise",
"Phased",
"Twinkleup@Speed,Intensity,Min;,!;!",
"Noise Pal",
"Sine",
"Phased Noise",
"Flow",
"Chunchun",
"Dancing Shadows",
"Washing Machine",
"Candy Cane",
"Blends",
"TV Simulator",
"Dynamic Smooth"
])=====";

View File

@ -357,21 +357,25 @@ button {
bottom: 0;
}
#staytop, #staytop1 {
#slider0, #slider1, #slider2, #slider3, #slider4 {
background: var(--c-2);
width: 310px;
margin: auto;
border-radius: 15px;
}
#staytop1 {
#sliderLabel2, #sliderLabel3, #sliderLabel4,
#slider2, #slider3, #slider4 {
display: none;
}
/*
#slider1 {
top: 28px;
}
#staytop2 {
#fxFind {
top: 58px;
}
*/
.first {
margin-top: 10px;
}
@ -1078,18 +1082,20 @@ input[type=number]::-webkit-outer-spin-button {
.lstI.sticky, .lstI.selected {
z-index: 1;
}
/*
#fxlist .lstI.selected {
top: 142px;
}
*/
#pallist .lstI.selected {
top: 84px;
}
/* must be after .selected */
/*
#fxlist .lstI.sticky {
top: 99px;
}
*/
#pallist .lstI.sticky {
top: 42px;
}

View File

@ -52,21 +52,18 @@
<input id="sliderR" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div><br>
<div id="gwrap" class="il">
<div class="sliderwrap il">
<input id="sliderG" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div><br>
<div id="bwrap" class="il">
<div class="sliderwrap il">
<input id="sliderB" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div><br>
</div>
<div id="wwrap">
@ -75,7 +72,6 @@
<input id="sliderW" class="noslide" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div>
<div id="wbal">
<p class="labels hd">White balance</p>
@ -83,7 +79,6 @@
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div>
<div id="qcs-w">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
@ -99,16 +94,17 @@
<div class="qcs" onclick="pC('rnd');" title="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
</div>
<div id="csl">
<button id="csl-0" class="btn xxs" onclick="selectSlot(0);">1</button>
<button id="csl-1" class="btn xxs" onclick="selectSlot(1);">2</button>
<button id="csl-2" class="btn xxs" onclick="selectSlot(2);">3</button>
<button id="csl0" class="btn xxs" onclick="selectSlot(0);">1</button>
<button id="csl1" class="btn xxs" onclick="selectSlot(1);">2</button>
<button id="csl2" class="btn xxs" onclick="selectSlot(2);">3</button>
</div>
<p class="labels h" id="cslLabel"></p>
<div id="hexw">
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter(this)" autocomplete="off" maxlength="8" />
<button id="hexcnf" class="btn btn-xs" onclick="fromHex();"><i class="icons btn-icon">&#xe390;</i></button>
</div>
<div id="palw" class="il">
<p class="labels"><i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette</p>
<p class="labels" id="pall"><i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette</p>
<div class="staytop fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pallist')" onfocus="search(this,'pallist')" />
<i class="icons clear-icon" onclick="clean(this)">&#xe38f;</i>
@ -140,17 +136,17 @@
</div>
<div id="Effects" class="tabcontent">
<p class="labels">Effect speed</p>
<div class="staytop" id="staytop">
<i class="icons slider-icon">&#xe325;</i>
<p class="labels" id="sliderLabel0">Effect speed</p>
<div class="staytop" id="slider0">
<i class="icons slider-icon" onclick="tglFreeze()">&#xe325;</i>
<div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<p class="labels">Effect intensity</p>
<div class="staytop" id="staytop1">
<p class="labels" id="sliderLabel1">Effect intensity</p>
<div class="staytop" id="slider1">
<i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il">
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
@ -158,9 +154,36 @@
</div>
<output class="sliderbubble"></output>
</div>
<p class="labels" id="sliderLabel2">Custom 1</p>
<div class="staytop" id="slider2">
<i class="icons slider-icon">&#xe410;</i>
<div class="sliderwrap il">
<input id="sliderC1" class="noslide" onchange="setFFT1()" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<p class="labels" id="sliderLabel3">Custom 2</p>
<div class="staytop" id="slider3">
<i class="icons slider-icon">&#xe410;</i>
<div class="sliderwrap il">
<input id="sliderC2" class="noslide" onchange="setFFT2()" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<p class="labels" id="sliderLabel4">Custom 3</p>
<div class="staytop" id="slider4">
<i class="icons slider-icon">&#xe410;</i>
<div class="sliderwrap il">
<input id="sliderC3" class="noslide" onchange="setFFT3()" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<div class="il">
<p class="labels">Effect mode</p>
<div class="staytop fnd" id="staytop2">
<p class="labels" id="modeLabel">Effect mode</p>
<div class="staytop fnd" id="fxFind">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist')" />
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>

View File

@ -10,6 +10,7 @@ var nlDur = 60, nlTar = 0;
var nlMode = false;
var selectedFx = 0;
var selectedPal = 0;
var sliderControl = ""; //WLEDSR: used by togglePcMode
var csel = 0;
var currentPreset = -1;
var lastUpdate = 0;
@ -731,66 +732,6 @@ function populateSegments(s)
gId('rsbtn').style.display = (segCount > 1) ? "inline":"none";
}
function btype(b)
{
switch (b) {
case 32: return "ESP32";
case 82: return "ESP8266";
}
return "?";
}
function bname(o)
{
if (o.name=="WLED") return o.ip;
return o.name;
}
function populateNodes(i,n)
{
var cn="";
var urows="";
var nnodes = 0;
if (n.nodes) {
n.nodes.sort((a,b) => (a.name).localeCompare(b.name));
for (var x=0;x<n.nodes.length;x++) {
var o = n.nodes[x];
if (o.name) {
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
nnodes++;
}
}
}
if (i.ndc < 0) cn += `Instance List is disabled.`;
else if (nnodes == 0) cn += `No other instances found.`;
cn += `<table>
${inforow("Current instance:",i.name)}
${urows}
</table>`;
gId('kn').innerHTML = cn;
}
function loadNodes()
{
var url = (loc?`http://${locip}`:'') + '/json/nodes';
fetch(url, {
method: 'get'
})
.then(res => {
if (!res.ok) showToast('Could not load Node list!', true);
return res.json();
})
.then(json => {
clearErrorToast();
populateNodes(lastinfo, json);
})
.catch(function (error) {
showToast(error, true);
console.log(error);
});
}
function populateEffects()
{
var effects = eJson;
@ -802,16 +743,27 @@ function populateEffects()
effects.unshift({
"id": 0,
"name": "Solid"
"name": "Solid@;!;"
});
for (let i = 0; i < effects.length; i++) {
html += generateListItemHtml(
'fx',
effects[i].id,
effects[i].name,
'setX'
);
// WLEDSR: add slider and color control to setX (used by requestjson)
if (effects[i].name.indexOf("Reserved") < 0) {
var posAt = effects[i].name.indexOf("@");
var extra = '';
if (posAt > 0)
extra = effects[i].name.substr(posAt);
else
posAt = 999;
html += generateListItemHtml(
'fx',
effects[i].id,
effects[i].name.substr(0,posAt),
'setX',
'','',
extra
);
}
}
gId('fxlist').innerHTML=html;
@ -894,15 +846,15 @@ function genPalPrevCss(id)
} else {
if (selColors) {
let e = element[1] - 1;
if (Array.isArray(selColors[e])) {
//if (Array.isArray(selColors[e])) {
r = selColors[e][0];
g = selColors[e][1];
b = selColors[e][2];
} else {
r = (selColors[e]>>16) & 0xFF;
g = (selColors[e]>> 8) & 0xFF;
b = (selColors[e] ) & 0xFF;
}
//} else {
// r = (selColors[e]>>16) & 0xFF;
// g = (selColors[e]>> 8) & 0xFF;
// b = (selColors[e] ) & 0xFF;
//}
}
}
if (index === false) {
@ -915,9 +867,9 @@ function genPalPrevCss(id)
return `background: linear-gradient(to right,${gradient.join()});`;
}
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '')
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '', extraPar = '')
{
return `<div class="lstI${id==0?' sticky':''}" data-id="${id}" onClick="${clickAction}(${id})">
return `<div class="lstI${id==0?' sticky':''} ${extraClass}" data-id="${id}" onClick="${clickAction}(${id},'${extraPar}')">
<label class="radio schkl" onclick="event.preventDefault()">
&nbsp;
<input type="radio" value="${id}" name="${listName}">
@ -932,6 +884,66 @@ function generateListItemHtml(listName, id, name, clickAction, extraHtml = '')
</div>`;
}
function btype(b)
{
switch (b) {
case 32: return "ESP32";
case 82: return "ESP8266";
}
return "?";
}
function bname(o)
{
if (o.name=="WLED") return o.ip;
return o.name;
}
function populateNodes(i,n)
{
var cn="";
var urows="";
var nnodes = 0;
if (n.nodes) {
n.nodes.sort((a,b) => (a.name).localeCompare(b.name));
for (var x=0;x<n.nodes.length;x++) {
var o = n.nodes[x];
if (o.name) {
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
nnodes++;
}
}
}
if (i.ndc < 0) cn += `Instance List is disabled.`;
else if (nnodes == 0) cn += `No other instances found.`;
cn += `<table>
${inforow("Current instance:",i.name)}
${urows}
</table>`;
gId('kn').innerHTML = cn;
}
function loadNodes()
{
var url = (loc?`http://${locip}`:'') + '/json/nodes';
fetch(url, {
method: 'get'
})
.then(res => {
if (!res.ok) showToast('Could not load Node list!', true);
return res.json();
})
.then(json => {
clearErrorToast();
populateNodes(lastinfo, json);
})
.catch(function (error) {
showToast(error, true);
console.log(error);
});
}
function updateTrail(e)
{
if (e==null) return;
@ -1014,19 +1026,14 @@ function updateUI()
updateTrail(gId('sliderBri'));
updateTrail(gId('sliderSpeed'));
updateTrail(gId('sliderIntensity'));
updateTrail(gId('sliderC1'));
updateTrail(gId('sliderC2'));
updateTrail(gId('sliderC3'));
gId('wwrap').style.display = (isRgbw) ? "block":"none";
gId("wbal").style.display = (lastinfo.leds.cct) ? "block":"none";
if (selectedFx===0) {
gId("csl-1").style.display = "none";
gId("csl-2").style.display = "none";
gId("palw").style.display = "none";
} else {
gId("csl-1").style.display = "inline-block";
gId("csl-2").style.display = "inline-block";
gId("palw").style.display = "inline-block";
}
updatePA();
updateHex();
updateRgb();
@ -1055,7 +1062,20 @@ function updateSelectedFx()
if (selElement) selElement.classList.remove('selected');
var selectedEffect = parent.querySelector(`.lstI[data-id="${selectedFx}"]`);
if (selectedEffect) selectedEffect.classList.add('selected');
if (selectedEffect) {
selectedEffect.classList.add('selected');
// WLEDSR: extract the Slider and color control string from the HTML element and set it.
sliderControl = selectedEffect.outerHTML.replace(/&amp;/g, "&");
var posAt = sliderControl.indexOf("@");
if (posAt > 0) {
sliderControl = sliderControl.substring(posAt);
var posAt = sliderControl.indexOf(')"');
sliderControl = sliderControl.substring(0,posAt-1);
} else
sliderControl = "";
setSliderAndColorControl(selectedFx, sliderControl);
}
}
function displayRover(i,s)
@ -1124,7 +1144,7 @@ function readState(s,command=false)
else currentPreset = s.pl;
tr = s.transition;
d.gId('tt').value = tr/10;
gId('tt').value = tr/10;
var selc=0; var ind=0;
populateSegments(s);
@ -1144,18 +1164,18 @@ function readState(s,command=false)
for (let e = cd.length-1; e >= 0; e--)
{
var r,g,b,w;
if (Array.isArray(i.col[e])) {
//if (Array.isArray(i.col[e])) {
r = i.col[e][0];
g = i.col[e][1];
b = i.col[e][2];
if (isRgbw) w = i.col[e][3];
} else {
// unsigned long RGBW (@blazoncek v2 experimental API implementation, obsolete & will be removed)
r = (i.col[e]>>16) & 0xFF;
g = (i.col[e]>> 8) & 0xFF;
b = (i.col[e] ) & 0xFF;
if (isRgbw) w = (i.col[e] >> 24) & 0xFF;
}
//} else {
// // unsigned long RGBW (@blazoncek v2 experimental API implementation, obsolete & will be removed)
// r = (i.col[e]>>16) & 0xFF;
// g = (i.col[e]>> 8) & 0xFF;
// b = (i.col[e] ) & 0xFF;
// if (isRgbw) w = (i.col[e] >> 24) & 0xFF;
//}
cd[e].style.backgroundColor = "rgb(" + r + "," + g + "," + b + ")";
if (isRgbw) whites[e] = parseInt(w);
}
@ -1166,6 +1186,10 @@ function readState(s,command=false)
gId('sliderSpeed').value = i.sx;
gId('sliderIntensity').value = i.ix;
gId('sliderC1').value = i.f1x ? i.f1x : 0;
gId('sliderC2').value = i.f2x ? i.f2x : 0;
gId('sliderC3').value = i.f3x ? i.f3x : 0;
if (s.error && s.error != 0) {
var errstr = "";
switch (s.error) {
@ -1194,6 +1218,99 @@ function readState(s,command=false)
updateUI();
}
// WLEDSR: control HTML elements for Slider and Color Control
function setSliderAndColorControl(idx, extra)
{
var topPosition = 0;
var controlDefined = (extra.substr(0,1) == "@")?true:false;
extra = extra.substr(1);
var extras = (extra == '')?[]:extra.split(";");
var slOnOff = (extras.length==0 || extras[0]=='')?[]:extras[0].split(",");
var coOnOff = (extras.length<2 || extras[1]=='')?[]:extras[1].split(",");
var paOnOff = (extras.length<3 || extras[2]=='')?[]:extras[2].split(",");
// set html slider items on/off
var nSliders = (gId("Effects").children.length - 1) / 2; // p (label) & div for each slider + FX list
for (let i=0; i<nSliders; i++) {
var slider = gId("slider" + i);
var label = gId("sliderLabel" + i);
// if (not controlDefined and for AC speed or intensity and for SR alle sliders) or slider has a value
if ((!controlDefined && i < ((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i] != "")) {
label.style.display = "block";
if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
else if (i==0) label.innerHTML = "Effect speed";
else if (i==1) label.innerHTML = "Effect intensity";
else label.innerHTML = "Custom" + (i-1);
label.style.top = "auto";
slider.style.display = "block";
slider.style.top = topPosition + "px";
topPosition += 28; // increase top position for the next control
slider.setAttribute('title',label.innerHTML);
} else {
// disable label and slider
slider.style.display = "none";
label.style.display = "none";
}
}
if (topPosition>0) topPosition += 2;
// set top position of the effect list
gId("fxFind").style.top = topPosition + "px";
topPosition += 42;
var fxList = gId("fxlist");
for (var i=0; i<fxList.children.length; i++) fxList.children[i].style.top = null; // remove top
var selected = fxList.querySelector('.selected');
var sticky = fxList.querySelector('.sticky');
if (sticky) {
sticky.style.top = topPosition + "px";
topPosition += 42;
}
if (selected && !selected.style.top) { // is the sticky element also selected one?
selected.style.top = topPosition + "px";
}
// set html color items on/off
var cslLabel = '';
var sep = '';
for (let i=0; i<gId("csl").children.length; i++) {
var btn = gId("csl" + i);
// if no controlDefined or coOnOff has a value
if (coOnOff.length>i && coOnOff[i] != "") {
btn.style.display = "inline";
if (coOnOff.length>i && coOnOff[i] != "!") {
var abbreviation = coOnOff[i].substr(0,2);
btn.innerHTML = abbreviation;
if (abbreviation != coOnOff[i]) {
cslLabel += sep + abbreviation + '=' + coOnOff[i];
sep = ', ';
}
}
else if (i==0) btn.innerHTML = "Fx";
else if (i==1) btn.innerHTML = "Bg";
else btn.innerHTML = "Cs";
} else if (!controlDefined || paOnOff.length>0) { // if no controls or palette then all buttons should be shown for color 1..3 palettes
btn.style.display = "inline";
btn.innerHTML = `${i+1}`;
} else {
btn.style.display = "none";
}
}
gId("cslLabel").innerHTML = cslLabel;
// set palette on/off
var palw = gId("palw"); // wrapper
var pall = gId("pall"); // list
// if not controlDefined or palette has a value
if ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="")) {
palw.style.display = "inline-block";
if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0];
else pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette';
} else {
// disable label and slider
palw.style.display = "none";
}
}
var jsonTimeout;
var reqsLegal = false;
@ -1211,7 +1328,7 @@ function requestJson(command=null)
command.v = true; // force complete /json/si API response
command.time = Math.floor(Date.now() / 1000);
var t = d.gId('tt');
var t = gId('tt');
if (t.validity.valid && command.transition==null) {
var tn = parseInt(t.value*10);
if (tn != tr) command.transition = tn;
@ -1641,6 +1758,13 @@ function setSegBri(s)
requestJson(obj);
}
function tglFreeze(s=null)
{
var obj = {"seg": {"frz": "t"}}; // toggle
if (s!==null) obj.id = s;
requestJson(obj);
}
function setX(ind = null)
{
if (ind === null) {

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id);
bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE);
seg.setOption(SEG_OPTION_FREEZE, frz, id);
seg.cct = elem["cct"] | seg.cct;
JsonArray colarr = elem["col"];
@ -133,7 +136,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
//if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal);
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
//temporary, strip object gets updated via colorUpdated()
@ -201,7 +204,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
}
strip.setPixelSegment(255);
strip.trigger();
} else { //return to regular effect
// this is now handled using the "frz" toggle.
} else if (!elem["frz"] && iarr.isNull()) { //return to regular effect
seg.setOption(SEG_OPTION_FREEZE, false);
}
return; // seg.differs(prev);
@ -244,13 +248,13 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
tr = root[F("tb")] | -1;
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
JsonObject nl = root["nl"];
JsonObject nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl[F("dur")] | nightlightDelayMins;
nightlightMode = nl[F("mode")] | nightlightMode;
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
JsonObject udpn = root["udpn"];
JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect;
receiveNotifications = udpn["recv"] | receiveNotifications;
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
@ -376,6 +380,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset;
root["on"] = seg.getOption(SEG_OPTION_ON);
root["frz"] = seg.getOption(SEG_OPTION_FREEZE);
byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255;
root["cct"] = seg.cct;
@ -791,6 +796,37 @@ void serializeNodes(JsonObject root)
}
}
void serializeSRNames(JsonVariant arr, const char *qstring) {
String lineBuffer;
bool insideQuotes = false;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(qstring); i++) {
singleJsonSymbol = pgm_read_byte_near(qstring + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
break;
case ']':
case ',':
if (lineBuffer.length() > 0) {
uint8_t endPos = lineBuffer.indexOf('@');
if (endPos>0) arr.add(lineBuffer.substring(0,endPos));
else arr.add(lineBuffer);
lineBuffer.clear();
}
break;
default:
if (!insideQuotes) break;
lineBuffer += singleJsonSymbol;
}
}
}
void serveJson(AsyncWebServerRequest* request)
{
byte subJson = 0;
@ -805,6 +841,7 @@ void serveJson(AsyncWebServerRequest* request)
return;
}
else if (url.indexOf(F("eff")) > 0) {
// this is going to serve raw effect names which will include WLED-SR extensions in names
request->send_P(200, "application/json", JSON_mode_names);
return;
}
@ -840,7 +877,9 @@ void serveJson(AsyncWebServerRequest* request)
serializeInfo(info);
if (subJson != 3)
{
doc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names);
//doc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names);
JsonArray effects = doc.createNestedArray(F("effects"));
serializeSRNames(effects, JSON_mode_names); // remove WLED-SR extensions from effect names
doc[F("palettes")] = serialized((const __FlashStringHelper*)JSON_palette_names);
}
}

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2110291
#define VERSION 2110311
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG