From 65a32b4166832d8c627db1dc89fde995d17d9175 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 20 Feb 2020 00:45:09 +0100 Subject: [PATCH] Added new auto white modes (related to #573 ) --- wled00/FX.h | 4 +- wled00/FX_fcn.cpp | 21 +- wled00/const.h | 9 +- wled00/data/index.htm | 1970 ++++++++++++------------- wled00/html_settings.h | 10 +- wled00/html_ui.h | 2987 +++++++++++++++++++------------------- wled00/wled00.ino | 3 +- wled00/wled01_eeprom.ino | 4 +- wled00/wled02_xml.ino | 4 +- wled00/wled03_set.ino | 2 +- wled00/wled08_led.ino | 2 +- wled00/wled14_colors.ino | 2 +- wled00/wled19_json.ino | 2 +- 13 files changed, 2526 insertions(+), 2494 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index abfc11d3..9f1fda41 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -28,6 +28,7 @@ #define WS2812FX_h #include "NpbWrapper.h" +#include "const.h" #define FASTLED_INTERNAL //remove annoying pragma messages #include "FastLED.h" @@ -426,6 +427,7 @@ class WS2812FX { uint8_t mainSegment = 0, + rgbwMode = RGBW_MODE_DUAL, paletteFade = 0, paletteBlend = 0, colorOrder = 0, @@ -589,7 +591,7 @@ class WS2812FX { void fill(uint32_t); bool - _rgbwMode, + _useRgbw = false, _cronixieMode, _cronixieBacklightEnabled, _skipFirstMode, diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index c1ee11af..73ddbf79 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -32,9 +32,9 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) { - if (supportWhite == _rgbwMode && countPixels == _length) return; + if (supportWhite == _useRgbw && countPixels == _length) return; RESET_RUNTIME; - _rgbwMode = supportWhite; + _useRgbw = supportWhite; _skipFirstMode = skipFirst; _length = countPixels; @@ -65,6 +65,7 @@ void WS2812FX::service() { { if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary { + if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check _virtualSegmentLength = SEGMENT.virtualLength(); doShow = true; handle_palette(); @@ -106,6 +107,20 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) { void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { + //auto calculate white channel value if enabled + if (_useRgbw) { + if (rgbwMode == RGBW_MODE_AUTO_BRIGHTER || (w == 0 && (rgbwMode == RGBW_MODE_DUAL || rgbwMode == RGBW_MODE_LEGACY))) + { + //white value is set to lowest RGB channel + //thank you to @Def3nder! + w = r < g ? (r < b ? r : b) : (g < b ? g : b); + } else if (rgbwMode == RGBW_MODE_AUTO_ACCURATE && w == 0) + { + w = r < g ? (r < b ? r : b) : (g < b ? g : b); + r -= w; g -= w; b -= w; + } + } + RgbwColor col; switch (colorOrder) { @@ -256,7 +271,7 @@ void WS2812FX::show(void) { } - if (_rgbwMode) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less + if (_useRgbw) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less { powerSum *= 3; powerSum = powerSum >> 2; //same as /= 4 diff --git a/wled00/const.h b/wled00/const.h index 15345c7c..1308bf5f 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -3,10 +3,17 @@ //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot -#define AP_BEHAVIOR_NO_CONN 1 //Open when no connection +#define AP_BEHAVIOR_NO_CONN 1 //Open when no connection (either after boot or if connection is lost) #define AP_BEHAVIOR_ALWAYS 2 //Always open #define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec +//RGB to RGBW conversion mode +#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider +#define RGBW_MODE_AUTO_BRIGHTER 1 //New algorithm. Adds as much white as the darkest RGBW channel +#define RGBW_MODE_AUTO_ACCURATE 2 //New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel +#define RGBW_MODE_DUAL 3 //Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) +#define RGBW_MODE_LEGACY 4 //Old floating algorithm. Too slow for realtime and palette support + //realtime modes #define REALTIME_MODE_INACTIVE 0 #define REALTIME_MODE_GENERIC 1 diff --git a/wled00/data/index.htm b/wled00/data/index.htm index d9d1ccc9..3446f7ce 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -1,668 +1,668 @@ - - - - - -WLED - + + + + + + WLED + @@ -672,144 +672,144 @@ display: none;
-
-
- - - - - - -
-
-

Brightness

-
- -
- -
-
-
-
- -
+
+
+ + + + + + +
+
+

Brightness

+
+ +
+ +
+
+
+
+ +
-
-
-
-

White channel

-
- -
-
-
-
-
-
-
-
-
-

-
-
-
-
-
R
-
-
- - - -
-

Color palette

-
- - -
-
+
+
+
+

White channel

+
+ +
+
+
+
+
+
+
+
+
+

+
+
+
+
+
R
+
+
+ + + +
+

Color palette

+
+ + +
+
-
-

Effect speed

-
- -
- -
-
-
-

Effect intensity

-
- -
- -
-
-
-

Effect mode

-
- -
-
-Loading... -
-
-
+
+

Effect speed

+
+ +
+ +
+
+
+

Effect intensity

+
+ +
+ +
+
+
+

Effect mode

+
+ +
+
+ Loading... +
+
+
+ +
+
+ Loading... +
+
+ +
+
-
-
-Loading... -
-
- -
-
- -
-

Load from slot

- - - -
- - - -
- - - -
- - - -
-
-Slot 16 can save all segments.

-
-First preset:
-Last preset:
-Time per preset: s
-Transition: s -
+
+

Load from slot

+ + + +
+ + + +
+ + + +
+ + + +
+
+ Slot 16 can save all segments.

+
+ First preset:
+ Last preset:
+ Time per preset: s
+ Transition: s +
- - - - + + + +
@@ -817,16 +817,16 @@ Transition: /*! -* iro.js v4.4.0 -* 2016-2019 James Daniel -* Licensed under MPL 2.0 -* github.com/jaames/iro.js -*/ + * iro.js v4.4.0 + * 2016-2019 James Daniel + * Licensed under MPL 2.0 + * github.com/jaames/iro.js + */ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.iro=e()}(this,function(){"use strict";var c=function(){},i={},h=[],u=[];function p(t,e){var o,n,r,i,s=arguments,a=u;for(i=arguments.length;2t){var n=function(e){var t="".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0}(t);return parseFloat(e.toFixed(n))}return Math.round(e/t)*t}return function(){function n(e,r){(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,n),l.element(e)?this.element=e:l.string(e)&&(this.element=document.querySelector(e)),l.element(this.element)&&l.empty(this.element.rangeTouch)&&(this.config=Object.assign({},t,r),this.init())}return r=n,i=[{key:"setup",value:function(e){var r=1(t=100/c.width*(u.clientX-c.left))?t=0:100t?t-=(100-2*t)*f:50 3000) { -requestJson({}, true); -} + if (!document.hidden && new Date () - lastUpdate > 3000) { + requestJson({}, true); + } } function onLoad() { -size(); - -var cd = d.getElementById('csl').children; -for (i = 0; i < cd.length; i++) { -cd[i].style.backgroundColor = "rgb(0, 0, 0)"; -} -selectSlot(0); -updateTablinks(0); -resetUtil(); -cpick.on("input:end", function() { -setColor(true); -}); -setTimeout(requestJson, 25); -d.addEventListener("visibilitychange", handleVisibilityChange, false); - -d.getElementById("cv").style.opacity=0; + size(); + + var cd = d.getElementById('csl').children; + for (i = 0; i < cd.length; i++) { + cd[i].style.backgroundColor = "rgb(0, 0, 0)"; + } + selectSlot(0); + updateTablinks(0); + resetUtil(); + cpick.on("input:end", function() { + setColor(true); + }); + setTimeout(requestJson, 25); + d.addEventListener("visibilitychange", handleVisibilityChange, false); + + d.getElementById("cv").style.opacity=0; } function updateTablinks(tabI) { -tablinks = d.getElementsByClassName("tablinks"); -for (i = 0; i < tablinks.length; i++) { -tablinks[i].className = tablinks[i].className.replace(" active", ""); -} -if (pcMode) return; -tablinks[tabI].className += " active"; + tablinks = d.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + } + if (pcMode) return; + tablinks[tabI].className += " active"; } function openTab(tabI) { -if (pcMode) return; -var i, tabcontent, tablinks; -iSlide = tabI; -_C.classList.toggle('smooth', false); -_C.style.setProperty('--i', iSlide); -updateTablinks(tabI); + if (pcMode) return; + var i, tabcontent, tablinks; + iSlide = tabI; + _C.classList.toggle('smooth', false); + _C.style.setProperty('--i', iSlide); + updateTablinks(tabI); } var timeout; function showToast(text, error = false) { -var x = d.getElementById("toast"); -x.innerHTML = text; -x.className = error ? "error":"show"; -clearTimeout(timeout); -x.style.animation = 'none'; -x.offsetHeight; -x.style.animation = null; -timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); + var x = d.getElementById("toast"); + x.innerHTML = text; + x.className = error ? "error":"show"; + clearTimeout(timeout); + x.style.animation = 'none'; + x.offsetHeight; + x.style.animation = null; + timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); } function showErrorToast() { -showToast('Connection to light failed. Please refresh the page. If the issue persists, reboot your ESP.', true); + showToast('Connection to light failed. Please refresh the page. If the issue persists, reboot your ESP.', true); } function clearErrorToast() { -d.getElementById("toast").className = d.getElementById("toast").className.replace("error", ""); + d.getElementById("toast").className = d.getElementById("toast").className.replace("error", ""); } function populateSegments(s) { -var cn = ""; -segCount = 0, lowestUnused = 0; + var cn = ""; + segCount = 0, lowestUnused = 0; + + for (y in s.seg) + { + segCount++; + + var inst=s.seg[y]; + var i = parseInt(inst.id); + if (i == lowestUnused) lowestUnused = i+1; + + cn += `
+ + +
+ + + + + + + + + +
Start LEDStop LED
+ + + + + + + + + +
GroupingSpacing
+
${inst.stop - inst.start} LED${inst.stop - inst.start > 1 ? "s":""} (${Math.ceil((inst.stop - inst.start)/(inst.grp + inst.spc))} virtual)
+ + + +
+

`; + } -for (y in s.seg) -{ -segCount++; - -var inst=s.seg[y]; -var i = parseInt(inst.id); -if (i == lowestUnused) lowestUnused = i+1; - -cn += `
- - -
- - - - - - - - - -
Start LEDStop LED
- - - - - - - - - -
GroupingSpacing
-
${inst.stop - inst.start} LED${inst.stop - inst.start > 1 ? "s":""} (${Math.ceil((inst.stop - inst.start)/(inst.grp + inst.spc))} virtual)
- - - -
-

`; -} - -d.getElementById('segcont').innerHTML = cn; -if (lowestUnused >= maxSeg) { -d.getElementById('segutil').innerHTML = 'Maximum number of segments reached.'; -noNewSegs = true; -} else if (noNewSegs) { -resetUtil(); -noNewSegs = false; -} + d.getElementById('segcont').innerHTML = cn; + if (lowestUnused >= maxSeg) { + d.getElementById('segutil').innerHTML = 'Maximum number of segments reached.'; + noNewSegs = true; + } else if (noNewSegs) { + resetUtil(); + noNewSegs = false; + } } function updateTrail(e) { -var progress = e.value *100 /255; -progress = parseInt(progress); -var val = `linear-gradient(90deg, white ${progress}%, #444 ${progress}%)`; -e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; + var progress = e.value *100 /255; + progress = parseInt(progress); + var val = `linear-gradient(90deg, white ${progress}%, #444 ${progress}%)`; + e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; } function updateLen(s) { -var start = parseInt(d.getElementById(`seg${s}s`).value); -var stop = parseInt(d.getElementById(`seg${s}e`).value); -var len = stop - start; -var out = "(delete)" -if (len > 1) { -out = `${len} LEDs`; -} else if (len == 1) { -out = "1 LED" -} - -if (d.getElementById(`seg${s}grp`) != null) -{ -var grp = parseInt(d.getElementById(`seg${s}grp`).value); -var spc = parseInt(d.getElementById(`seg${s}spc`).value); -if (grp == 0) grp = 1; -var virt = Math.ceil(len/(grp + spc)); -if (!isNaN(virt) && (grp > 1 || spc > 0)) out += ` (${virt} virtual)`; -} - -d.getElementById(`seg${s}len`).innerHTML = out; + var start = parseInt(d.getElementById(`seg${s}s`).value); + var stop = parseInt(d.getElementById(`seg${s}e`).value); + var len = stop - start; + var out = "(delete)" + if (len > 1) { + out = `${len} LEDs`; + } else if (len == 1) { + out = "1 LED" + } + + if (d.getElementById(`seg${s}grp`) != null) + { + var grp = parseInt(d.getElementById(`seg${s}grp`).value); + var spc = parseInt(d.getElementById(`seg${s}spc`).value); + if (grp == 0) grp = 1; + var virt = Math.ceil(len/(grp + spc)); + if (!isNaN(virt) && (grp > 1 || spc > 0)) out += ` (${virt} virtual)`; + } + + d.getElementById(`seg${s}len`).innerHTML = out; } function updateUI() { -d.getElementById('buttonPower').className = (isOn) ? "active":""; -d.getElementById('buttonNl').className = (nlA) ? "active":""; -d.getElementById('buttonSync').className = (syncSend) ? "active":""; + d.getElementById('buttonPower').className = (isOn) ? "active":""; + d.getElementById('buttonNl').className = (nlA) ? "active":""; + d.getElementById('buttonSync').className = (syncSend) ? "active":""; -d.getElementById('fxb' + selectedFx).style.backgroundColor = "#666"; -updateTrail(d.getElementById('sliderBri')); -updateTrail(d.getElementById('sliderSpeed')); -updateTrail(d.getElementById('sliderIntensity')); -updateTrail(d.getElementById('sliderW')); -if (isRgbw) d.getElementById('wwrap').style.display = "block"; + d.getElementById('fxb' + selectedFx).style.backgroundColor = "#666"; + updateTrail(d.getElementById('sliderBri')); + updateTrail(d.getElementById('sliderSpeed')); + updateTrail(d.getElementById('sliderIntensity')); + updateTrail(d.getElementById('sliderW')); + if (isRgbw) d.getElementById('wwrap').style.display = "block"; + + var btns = document.getElementsByClassName("psts"); + for (i = 0; i < btns.length; i++) { + btns[i].className = btns[i].className.replace(" active", ""); + if ((savedPresets >> i) & 0x01) btns[i].className += " stored"; + } + if (currentPreset > 0 && currentPreset <= btns.length) btns[currentPreset -1].className += " active"; -var btns = document.getElementsByClassName("psts"); -for (i = 0; i < btns.length; i++) { -btns[i].className = btns[i].className.replace(" active", ""); -if ((savedPresets >> i) & 0x01) btns[i].className += " stored"; -} -if (currentPreset > 0 && currentPreset <= btns.length) btns[currentPreset -1].className += " active"; - -spal = d.getElementById("selectPalette"); -spal.style.backgroundColor = (spal.selectedIndex > 0) ? "#666":"#333"; + spal = d.getElementById("selectPalette"); + spal.style.backgroundColor = (spal.selectedIndex > 0) ? "#666":"#333"; } function compare(a, b) { -if (a.name < b.name){ -return -1; -} -return 1; + if (a.name < b.name){ + return -1; + } + return 1; } var jsonTimeout; function requestJson(command, verbose = true) { -lastUpdate = new Date(); -if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); -var req = null; -e1 = d.getElementById('fxlist'); -e2 = d.getElementById('selectPalette'); + lastUpdate = new Date(); + if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); + var req = null; + e1 = d.getElementById('fxlist'); + e2 = d.getElementById('selectPalette'); -url = command ? '/json/state':'/json'; + url = command ? '/json/state':'/json'; + + type = command ? 'post':'get'; + if (command) + { + command.v = verbose; + req = JSON.stringify(command); + } + fetch + (url, { + method: type, + headers: { + "Content-type": "application/json; charset=UTF-8" + }, + body: req + }) + .then(res => { + if (!res.ok) { + showErrorToast(); + } + return res.json(); + }) + .then(json => { + clearTimeout(jsonTimeout); + jsonTimeout = null; + clearErrorToast(); + if (!json) showToast('Empty response', true); + if (json.success) return; + var s = json; + if (!command) { + var x='',y=''; + json.effects.shift(); //remove solid + for (i in json.effects) json.effects[i] = {"id": parseInt(i)+1, "name":json.effects[i]}; + json.effects.sort(compare); + for (i in json.effects) { + x += '
'; + } -type = command ? 'post':'get'; -if (command) -{ -command.v = verbose; -req = JSON.stringify(command); -} -fetch -(url, { -method: type, -headers: { -"Content-type": "application/json; charset=UTF-8" -}, -body: req -}) -.then(res => { -if (!res.ok) { -showErrorToast(); -} -return res.json(); -}) -.then(json => { -clearTimeout(jsonTimeout); -jsonTimeout = null; -clearErrorToast(); -if (!json) showToast('Empty response', true); -if (json.success) return; -var s = json; -if (!command) { -var x='',y=''; -json.effects.shift(); //remove solid -for (i in json.effects) json.effects[i] = {"id": parseInt(i)+1, "name":json.effects[i]}; -json.effects.sort(compare); -for (i in json.effects) { -x += '
'; -} + json.palettes.shift(); //remove default + for (i in json.palettes) json.palettes[i] = {"id": parseInt(i)+1, "name":json.palettes[i]}; + json.palettes.sort(compare); + for (i in json.palettes) { + y += ''; + } + e1.innerHTML=x; e2.innerHTML=y; + var info = json.info; + var name = info.name; + d.getElementById('namelabel').innerHTML = name; + if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; + if (info.live) name = "(Live) " + name; + d.title = name; + isRgbw = info.leds.wv; + ledCount = info.leds.count; + syncTglRecv = info.str; + maxSeg = info.leds.maxseg; + s = json.state; + } + isOn = s.on; + d.getElementById('sliderBri').value= s.bri; + nlA = s.nl.on; + nlDur = s.nl.dur; + syncSend = s.udpn.send; + savedPresets = s.pss; + currentPreset = s.ps; + d.getElementById('cyToggle').checked = (s.pl < 0) ? false : true; + d.getElementById('cycs').value = s.ccnf.min; + d.getElementById('cyce').value = s.ccnf.max; + d.getElementById('cyct').value = s.ccnf.time /10; + d.getElementById('cyctt').value = s.transition /10; + + var selc=0; var ind=0; + populateSegments(s); + for (i in s.seg) + { + if(s.seg[i].sel) {selc = ind; break;} ind++; + } + var i=s.seg[selc]; + if (!i) { + showToast('No Segments!', true); + updateUI(); + return; + } + var cd = d.getElementById('csl').children; + for (e = 2; e >= 0; e--) + { + cd[e].style.backgroundColor = "rgb(" + i.col[e][0] + "," + i.col[e][1] + "," + i.col[e][2] + ")"; + if (isRgbw) whites[e] = i.col[e][3]; + selectSlot(csel); + } + d.getElementById('sliderSpeed').value = whites[csel]; -json.palettes.shift(); //remove default -for (i in json.palettes) json.palettes[i] = {"id": parseInt(i)+1, "name":json.palettes[i]}; -json.palettes.sort(compare); -for (i in json.palettes) { -y += ''; -} -e1.innerHTML=x; e2.innerHTML=y; -var info = json.info; -var name = info.name; -d.getElementById('namelabel').innerHTML = name; -if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; -if (info.live) name = "(Live) " + name; -d.title = name; -isRgbw = info.leds.wv; -ledCount = info.leds.count; -syncTglRecv = info.str; -maxSeg = info.leds.maxseg; -s = json.state; -} -isOn = s.on; -d.getElementById('sliderBri').value= s.bri; -nlA = s.nl.on; -nlDur = s.nl.dur; -syncSend = s.udpn.send; -savedPresets = s.pss; -currentPreset = s.ps; -d.getElementById('cyToggle').checked = (s.pl < 0) ? false : true; -d.getElementById('cycs').value = s.ccnf.min; -d.getElementById('cyce').value = s.ccnf.max; -d.getElementById('cyct').value = s.ccnf.time /10; -d.getElementById('cyctt').value = s.transition /10; + d.getElementById('sliderSpeed').value = i.sx; + d.getElementById('sliderIntensity').value = i.ix; -var selc=0; var ind=0; -populateSegments(s); -for (i in s.seg) -{ -if(s.seg[i].sel) {selc = ind; break;} ind++; -} -var i=s.seg[selc]; -if (!i) { -showToast('No Segments!', true); -updateUI(); -return; -} -var cd = d.getElementById('csl').children; -for (e = 2; e >= 0; e--) -{ -cd[e].style.backgroundColor = "rgb(" + i.col[e][0] + "," + i.col[e][1] + "," + i.col[e][2] + ")"; -if (isRgbw) whites[e] = i.col[e][3]; -selectSlot(csel); -} -d.getElementById('sliderSpeed').value = whites[csel]; + d.getElementById('fxb' + selectedFx).style.backgroundColor = "#333"; + selectedFx = i.fx; + e2.value = i.pal; + if (!command) d.getElementById('Effects').scrollTop = d.getElementById('fxb' + selectedFx).offsetTop - d.getElementById('Effects').clientHeight/1.8; -d.getElementById('sliderSpeed').value = i.sx; -d.getElementById('sliderIntensity').value = i.ix; - -d.getElementById('fxb' + selectedFx).style.backgroundColor = "#333"; -selectedFx = i.fx; -e2.value = i.pal; -if (!command) d.getElementById('Effects').scrollTop = d.getElementById('fxb' + selectedFx).offsetTop - d.getElementById('Effects').clientHeight/1.8; - -if (s.error) showToast('WLED error ' + s.error, true); -updateUI(); -}) -.catch(function (error) { -showToast(error, true); -}) + if (s.error) showToast('WLED error ' + s.error, true); + updateUI(); + }) + .catch(function (error) { + showToast(error, true); + }) } function togglePower() { -isOn = !isOn; -var obj = {"on": isOn}; -obj.transition = parseInt(d.getElementById('cyctt').value*10); -requestJson(obj); + isOn = !isOn; + var obj = {"on": isOn}; + obj.transition = parseInt(d.getElementById('cyctt').value*10); + requestJson(obj); } function toggleNl() { -nlA = !nlA; -if (nlA) -{ -showToast('Nightlight active. Your light will turn off after ' + nlDur + ' minutes.'); -} else { -showToast('Nightlight deactivated.'); -} -var obj = {"nl": {"on": nlA}}; -requestJson(obj); + nlA = !nlA; + if (nlA) + { + showToast('Nightlight active. Your light will turn off after ' + nlDur + ' minutes.'); + } else { + showToast('Nightlight deactivated.'); + } + var obj = {"nl": {"on": nlA}}; + requestJson(obj); } function toggleSync() { -syncSend = !syncSend; -if (syncSend) -{ -showToast('Other lights in the network will now sync to this one.'); -} else { -showToast('This light and other lights in the network will no longer sync.'); -} -var obj = {"udpn": {"send": syncSend}}; -if (syncTglRecv) obj.udpn.recv = syncSend; -requestJson(obj); + syncSend = !syncSend; + if (syncSend) + { + showToast('Other lights in the network will now sync to this one.'); + } else { + showToast('This light and other lights in the network will no longer sync.'); + } + var obj = {"udpn": {"send": syncSend}}; + if (syncTglRecv) obj.udpn.recv = syncSend; + requestJson(obj); } function toggleLiveview() { -isLv = !isLv; -d.getElementById('liveview').style.display = (isLv) ? "block":"none"; -d.getElementById('liveview').src = (isLv) ? "/liveview":"about:blank"; -d.getElementById('buttonSr').className = (isLv) ? "active":""; + isLv = !isLv; + d.getElementById('liveview').style.display = (isLv) ? "block":"none"; + d.getElementById('liveview').src = (isLv) ? "/liveview":"about:blank"; + d.getElementById('buttonSr').className = (isLv) ? "active":""; } function makeSeg() { -var ns = 0; -if (lowestUnused > 0) { -var pend = d.getElementById(`seg${lowestUnused -1}e`).value; -if (pend < ledCount) ns = pend; -} -var cn = `
- -
- - - - - - - - - -
Start LEDStop LED
-
${ledCount - ns} LEDs
- -
-
`; -d.getElementById('segutil').innerHTML = cn; + var ns = 0; + if (lowestUnused > 0) { + var pend = d.getElementById(`seg${lowestUnused -1}e`).value; + if (pend < ledCount) ns = pend; + } + var cn = `
+ +
+ + + + + + + + + +
Start LEDStop LED
+
${ledCount - ns} LEDs
+ +
+
`; + d.getElementById('segutil').innerHTML = cn; } function resetUtil() { -var cn = `
`; -d.getElementById('segutil').innerHTML = cn; + var cn = `
`; + d.getElementById('segutil').innerHTML = cn; } function selSeg(s){ -var sel = d.getElementById(`seg${s}sel`).checked; -var obj = {"seg": {"id": s, "sel": sel}}; -requestJson(obj); + var sel = d.getElementById(`seg${s}sel`).checked; + var obj = {"seg": {"id": s, "sel": sel}}; + requestJson(obj); } function setSeg(s){ -var start = parseInt(d.getElementById(`seg${s}s`).value); -var stop = parseInt(d.getElementById(`seg${s}e`).value); -if (stop <= start) {delSeg(s); return;}; -var obj = {"seg": {"id": s, "start": start, "stop": stop}}; -if (d.getElementById(`seg${s}grp`)) -{ -var grp = parseInt(d.getElementById(`seg${s}grp`).value); -var spc = parseInt(d.getElementById(`seg${s}spc`).value); -obj.seg.grp = grp; -obj.seg.spc = spc; -} -requestJson(obj); + var start = parseInt(d.getElementById(`seg${s}s`).value); + var stop = parseInt(d.getElementById(`seg${s}e`).value); + if (stop <= start) {delSeg(s); return;}; + var obj = {"seg": {"id": s, "start": start, "stop": stop}}; + if (d.getElementById(`seg${s}grp`)) + { + var grp = parseInt(d.getElementById(`seg${s}grp`).value); + var spc = parseInt(d.getElementById(`seg${s}spc`).value); + obj.seg.grp = grp; + obj.seg.spc = spc; + } + requestJson(obj); } function delSeg(s){ -if (segCount < 2) { -showToast("You need to have multiple segments in order to delete one."); -return; -} -expanded[s] = false; -segCount--; -var obj = {"seg": {"id": s, "stop": 0}}; -requestJson(obj); + if (segCount < 2) { + showToast("You need to have multiple segments in order to delete one."); + return; + } + expanded[s] = false; + segCount--; + var obj = {"seg": {"id": s, "stop": 0}}; + requestJson(obj); } function setRev(s){ -var rev = d.getElementById(`seg${s}rev`).checked; -var obj = {"seg": {"id": s, "rev": rev}}; -requestJson(obj); + var rev = d.getElementById(`seg${s}rev`).checked; + var obj = {"seg": {"id": s, "rev": rev}}; + requestJson(obj); } function setX(ind) { -var obj = {"seg": {"fx": parseInt(ind)}}; -requestJson(obj); + var obj = {"seg": {"fx": parseInt(ind)}}; + requestJson(obj); } function setPalette() { -var obj = {"seg": {"pal": parseInt(d.getElementById('selectPalette').value)}}; -requestJson(obj); + var obj = {"seg": {"pal": parseInt(d.getElementById('selectPalette').value)}}; + requestJson(obj); } function setBri() { -var obj = {"bri": parseInt(d.getElementById('sliderBri').value)}; -obj.transition = parseInt(d.getElementById('cyctt').value*10); -requestJson(obj); + var obj = {"bri": parseInt(d.getElementById('sliderBri').value)}; + obj.transition = parseInt(d.getElementById('cyctt').value*10); + requestJson(obj); } function setSpeed() { -var obj = {"seg": {"sx": parseInt(d.getElementById('sliderSpeed').value)}}; -requestJson(obj); + var obj = {"seg": {"sx": parseInt(d.getElementById('sliderSpeed').value)}}; + requestJson(obj); } function setIntensity() { -var obj = {"seg": {"ix": parseInt(d.getElementById('sliderIntensity').value)}}; -requestJson(obj); + var obj = {"seg": {"ix": parseInt(d.getElementById('sliderIntensity').value)}}; + requestJson(obj); } function toggleCY() { -var obj = {"pl" : -1}; -if (d.getElementById('cyToggle').checked) -{ -obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}}; -obj.transition = parseInt(d.getElementById('cyctt').value*10); -} - -requestJson(obj); + var obj = {"pl" : -1}; + if (d.getElementById('cyToggle').checked) + { + obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}}; + obj.transition = parseInt(d.getElementById('cyctt').value*10); + } + + requestJson(obj); } function togglePS() { -ps = !ps; - -var btns = document.getElementsByClassName("psts"); -for (i = 0; i < btns.length; i++) { -if (ps) { -btns[i].className += " saving"; -} else { -btns[i].className = btns[i].className.replace(" saving", ""); -} -} - -d.getElementById("psLabel").innerHTML = (ps) ? "Save to slot":"Load from slot"; + ps = !ps; + + var btns = document.getElementsByClassName("psts"); + for (i = 0; i < btns.length; i++) { + if (ps) { + btns[i].className += " saving"; + } else { + btns[i].className = btns[i].className.replace(" saving", ""); + } + } + + d.getElementById("psLabel").innerHTML = (ps) ? "Save to slot":"Load from slot"; } function setPreset(i) { -var obj = {"ps": i} -if ((savedPresets >> (i-1)) & 0x01) { -showToast("Loading config from slot " + i +"."); -} else { -showToast("Slot " + i +" is empty! Use saving mode to save the current config to it."); -} -if (ps) { -obj = {"psave": i}; -showToast("Saving config to slot " + i +"."); -} -requestJson(obj); + var obj = {"ps": i} + if ((savedPresets >> (i-1)) & 0x01) { + showToast("Loading config from slot " + i +"."); + } else { + showToast("Slot " + i +" is empty! Use saving mode to save the current config to it."); + } + if (ps) { + obj = {"psave": i}; + showToast("Saving config to slot " + i +"."); + } + requestJson(obj); } function selectSlot(b) { -csel = b; -var cd = d.getElementById('csl').children; -for (i = 0; i < cd.length; i++) { -cd[i].style.border="2px solid white"; -cd[i].style.margin="5px"; -cd[i].style.width="42px"; -} -cd[csel].style.border="5px solid white"; -cd[csel].style.margin="2px"; -cd[csel].style.width="50px"; -if (cpick.color.rgbString !== cd[csel].style.backgroundColor) { -cpick.color.set(cd[csel].style.backgroundColor); -} -d.getElementById('sliderW').value = whites[csel]; -updateTrail(d.getElementById('sliderW')); + csel = b; + var cd = d.getElementById('csl').children; + for (i = 0; i < cd.length; i++) { + cd[i].style.border="2px solid white"; + cd[i].style.margin="5px"; + cd[i].style.width="42px"; + } + cd[csel].style.border="5px solid white"; + cd[csel].style.margin="2px"; + cd[csel].style.width="50px"; + if (cpick.color.rgbString !== cd[csel].style.backgroundColor) { + cpick.color.set(cd[csel].style.backgroundColor); + } + d.getElementById('sliderW').value = whites[csel]; + updateTrail(d.getElementById('sliderW')); } var lasth = 0; function pC(col) { -if (col == "rnd") -{ -col = {h: 0, s: 0, v: 100}; -col.s = Math.floor((Math.random() * 50) + 50); -do { -col.h = Math.floor(Math.random() * 360); -} while (Math.abs(col.h - lasth) < 50); -lasth = col.h; -} -cpick.color.set(col); -setColor(false); + if (col == "rnd") + { + col = {h: 0, s: 0, v: 100}; + col.s = Math.floor((Math.random() * 50) + 50); + do { + col.h = Math.floor(Math.random() * 360); + } while (Math.abs(col.h - lasth) < 50); + lasth = col.h; + } + cpick.color.set(col); + setColor(false); } function setColor(fromPicker) { -var cd = d.getElementById('csl').children; -if (fromPicker && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); -cd[csel].style.backgroundColor = cpick.color.rgbString; -whites[csel] = d.getElementById('sliderW').value; -var col = cpick.color.rgb; -var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; -if (csel == 1) { -obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; -} else if (csel == 2) { -obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}}; -} -obj.transition = parseInt(d.getElementById('cyctt').value*10); -if (d.getElementById('selectPalette').value > 5) { -d.getElementById('selectPalette').value = 0; -obj.seg.pal = 0; -} -requestJson(obj); + var cd = d.getElementById('csl').children; + if (fromPicker && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); + cd[csel].style.backgroundColor = cpick.color.rgbString; + whites[csel] = d.getElementById('sliderW').value; + var col = cpick.color.rgb; + var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; + if (csel == 1) { + obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; + } else if (csel == 2) { + obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}}; + } + obj.transition = parseInt(d.getElementById('cyctt').value*10); + if (d.getElementById('selectPalette').value > 5) { + d.getElementById('selectPalette').value = 0; + obj.seg.pal = 0; + } + requestJson(obj); } function expand(i) { -expanded[i] = !expanded[i]; -d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none"; -d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)" + expanded[i] = !expanded[i]; + d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none"; + d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)" } function unfocusSliders() { -d.getElementById("sliderBri").blur(); -d.getElementById("sliderSpeed").blur(); -d.getElementById("sliderIntensity").blur(); + d.getElementById("sliderBri").blur(); + d.getElementById("sliderSpeed").blur(); + d.getElementById("sliderIntensity").blur(); } //sliding UI @@ -1404,67 +1404,67 @@ let iSlide = 0, x0 = null, y0 = null, scrollS = 0, locked = false, w; function unify(e) { return e.changedTouches ? e.changedTouches[0] : e } function lock(e) { -if (pcMode) return; -var l = e.target.classList; -if (l.contains('noslide') || l.contains('iro__wheel__saturation') || l.contains('iro__slider__value') || l.contains('iro__slider')) return; -x0 = unify(e).clientX; -y0 = unify(e).clientY; -scrollS = d.getElementsByClassName("tabcontent")[iSlide].scrollTop; + if (pcMode) return; + var l = e.target.classList; + if (l.contains('noslide') || l.contains('iro__wheel__saturation') || l.contains('iro__slider__value') || l.contains('iro__slider')) return; + x0 = unify(e).clientX; + y0 = unify(e).clientY; + scrollS = d.getElementsByClassName("tabcontent")[iSlide].scrollTop; -_C.classList.toggle('smooth', !(locked = true)) + _C.classList.toggle('smooth', !(locked = true)) } function drag(e) { -if (!locked || pcMode) return; -if (d.getElementsByClassName("tabcontent")[iSlide].scrollTop != scrollS) { -move(e); return; -} - -//if (Math.abs(unify(e).clientX - x0) > Math.abs(unify(e).clientY - y0)) { -//if (e.cancelable) e.preventDefault(); -//} - -_C.style.setProperty('--tx', `${Math.round(unify(e).clientX - x0)}px`) + if (!locked || pcMode) return; + if (d.getElementsByClassName("tabcontent")[iSlide].scrollTop != scrollS) { + move(e); return; + } + + //if (Math.abs(unify(e).clientX - x0) > Math.abs(unify(e).clientY - y0)) { + //if (e.cancelable) e.preventDefault(); + //} + + _C.style.setProperty('--tx', `${Math.round(unify(e).clientX - x0)}px`) } function move(e) { -if(!locked || pcMode) return; -var dx = unify(e).clientX - x0, s = Math.sign(dx), -f = +(s*dx/w).toFixed(2); + if(!locked || pcMode) return; + var dx = unify(e).clientX - x0, s = Math.sign(dx), + f = +(s*dx/w).toFixed(2); -if((iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) && f > .12) { -_C.style.setProperty('--i', iSlide -= s); -f = 1 - f; -updateTablinks(iSlide); -} - -_C.style.setProperty('--tx', '0px'); -_C.style.setProperty('--f', f); -_C.classList.toggle('smooth', !(locked = false)); -x0 = null + if((iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) && f > .12) { + _C.style.setProperty('--i', iSlide -= s); + f = 1 - f; + updateTablinks(iSlide); + } + + _C.style.setProperty('--tx', '0px'); + _C.style.setProperty('--f', f); + _C.classList.toggle('smooth', !(locked = false)); + x0 = null } function size() { -w = window.innerWidth; -_C.style.setProperty('--tp', d.getElementById('top').clientHeight + "px"); -_C.style.setProperty('--bt', d.getElementById('bot').clientHeight + "px"); -if (w < 1250) togglePcMode(); + w = window.innerWidth; + _C.style.setProperty('--tp', d.getElementById('top').clientHeight + "px"); + _C.style.setProperty('--bt', d.getElementById('bot').clientHeight + "px"); + if (w < 1250) togglePcMode(); } function togglePcMode() { -if (w < 1250 && !pcMode) return; -openTab(0); -pcMode = !pcMode; -if (w < 1250) pcMode = false; -updateTablinks(0); -d.getElementById('buttonPcm').className = (pcMode) ? "active":""; -if (pcMode) -{ -_C.style.width = '100%'; -} else { -_C.style.width = '400%'; -} + if (w < 1250 && !pcMode) return; + openTab(0); + pcMode = !pcMode; + if (w < 1250) pcMode = false; + updateTablinks(0); + d.getElementById('buttonPcm').className = (pcMode) ? "active":""; + if (pcMode) + { + _C.style.width = '100%'; + } else { + _C.style.width = '400%'; + } } size(); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 5fe0e919..07e49cea 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -118,7 +118,15 @@ LED voltage (Max. current for a single LED):

LEDs are 4-channel type (RGBW):
-Auto-calculate white channel from RGB:
+Auto-calculate white channel from RGB:
+ +
Color order: