Merge pull request #2427 from Aircoookie/sync-seg

Sync segment options.
This commit is contained in:
Blaž Kristan 2022-01-25 12:54:24 +01:00 committed by GitHub
commit 0ca7699fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 2602 additions and 2503 deletions

View File

@ -240,8 +240,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(receiveNotificationColor, if_sync_recv["col"]); CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv["fx"]); CJSON(receiveNotificationEffects, if_sync_recv["fx"]);
CJSON(receiveGroups, if_sync_recv["grp"]); CJSON(receiveGroups, if_sync_recv["grp"]);
CJSON(receiveSegmentOptions, if_sync_recv["seg"]);
//! following line might be a problem if called after boot //! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
JsonObject if_sync_send = if_sync["send"]; JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault; prev = notifyDirectDefault;
@ -640,6 +641,7 @@ void serializeConfig() {
if_sync_recv["col"] = receiveNotificationColor; if_sync_recv["col"] = receiveNotificationColor;
if_sync_recv["fx"] = receiveNotificationEffects; if_sync_recv["fx"] = receiveNotificationEffects;
if_sync_recv["grp"] = receiveGroups; if_sync_recv["grp"] = receiveGroups;
if_sync_recv["seg"] = receiveSegmentOptions;
JsonObject if_sync_send = if_sync.createNestedObject("send"); JsonObject if_sync_send = if_sync.createNestedObject("send");
if_sync_send[F("dir")] = notifyDirect; if_sync_send[F("dir")] = notifyDirect;

View File

@ -22,11 +22,6 @@ void colorFromUint24(uint32_t in, bool secondary)
_col[2] = B(in); _col[2] = B(in);
} }
//store color components in uint32_t
uint32_t colorFromRgbw(byte* rgbw) {
return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
}
//relatively change white brightness, minumum A=5 //relatively change white brightness, minumum A=5
void relativeChangeWhite(int8_t amount, byte lowerBoundary) void relativeChangeWhite(int8_t amount, byte lowerBoundary)
{ {
@ -259,7 +254,7 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
rgbw[3] = W(rgb); rgbw[3] = W(rgb);
return colorFromRgbw(rgbw); return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
} }
//approximates a Kelvin color temperature from an RGB color. //approximates a Kelvin color temperature from an RGB color.
@ -299,4 +294,4 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
uint16_t k = 8080 + (225-r) *86; uint16_t k = 8080 + (225-r) *86;
return (k > 10091) ? 10091 : k; return (k > 10091) ? 10091 : k;
} }
} }

View File

@ -609,11 +609,12 @@ input[type=range]:active + .sliderbubble {
width: 216px; width: 216px;
} }
.btn-xs { .btn-xs {
width: 39px; width: 42px;
height: 42px;
margin: 2px 0 0 0; margin: 2px 0 0 0;
} }
.btn-pl-add { .btn-pl-add {
margin-left: 9px; margin-left: 5px;
} }
@ -645,7 +646,7 @@ input[type=range]:active + .sliderbubble {
.sel-pl { .sel-pl {
width: 192px; width: 192px;
background-position: 168px 16px; background-position: 168px 16px;
margin: 8px 7px 0 0; margin: 8px 3px 0 0;
} }
.sel-ple { .sel-ple {

View File

@ -141,7 +141,7 @@
<div id="Effects" class="tabcontent"> <div id="Effects" class="tabcontent">
<p class="labels">Effect speed</p> <p class="labels">Effect speed</p>
<div class="staytop"> <div class="staytop">
<i class="icons slider-icon">&#xe325;</i> <i class="icons slider-icon" onclick="tglFreeze()">&#xe325;</i>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" /> <input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<output class="sliderbubble hidden"></output> <output class="sliderbubble hidden"></output>

View File

@ -603,7 +603,6 @@ function populateSegments(s)
</tr> </tr>
</table> </table>
<div class="h" id="seg${i}len"></div> <div class="h" id="seg${i}len"></div>
<button class="btn btn-i btn-xs del" id="segd${i}" onclick="delSeg(${i})"><i class="icons btn-icon">&#xe037;</i></button>
<label class="check revchkl"> <label class="check revchkl">
Reverse direction Reverse direction
<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}> <input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}>
@ -614,6 +613,10 @@ function populateSegments(s)
<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi ? "checked":""}> <input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi ? "checked":""}>
<span class="checkmark schk"></span> <span class="checkmark schk"></span>
</label> </label>
<div class="del">
<button class="btn btn-i btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon">&#xe22d;</i></button>
<button class="btn btn-i btn-xs" id="segd${i}" title="Delete" onclick="delSeg(${i})"><i class="icons btn-icon">&#xe037;</i></button>
</div>
</div> </div>
</div><br>`; </div><br>`;
} }
@ -627,10 +630,13 @@ function populateSegments(s)
noNewSegs = false; noNewSegs = false;
} }
for (var i = 0; i <= lSeg; i++) { for (var i = 0; i <= lSeg; i++) {
updateLen(i); updateLen(i);
updateTrail(d.getElementById(`seg${i}bri`)); updateTrail(d.getElementById(`seg${i}bri`));
if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none"; let segr = d.getElementById(`segr${i}`);
if (segr) segr.style.display = "none";
} }
if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none";
if (!noNewSegs && (cfg.comp.seglen?parseInt(d.getElementById(`seg${lSeg}s`).value):0)+parseInt(d.getElementById(`seg${lSeg}e`).value)<ledCount) d.getElementById(`segr${lSeg}`).style.display = "inline";
d.getElementById('rsbtn').style.display = (segCount > 1) ? "inline":"none"; d.getElementById('rsbtn').style.display = (segCount > 1) ? "inline":"none";
} }
@ -872,7 +878,7 @@ function updateLen(s)
{ {
if (!d.getElementById(`seg${s}s`)) return; if (!d.getElementById(`seg${s}s`)) return;
var start = parseInt(d.getElementById(`seg${s}s`).value); var start = parseInt(d.getElementById(`seg${s}s`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value); var stop = parseInt(d.getElementById(`seg${s}e`).value);
var len = stop - (cfg.comp.seglen?0:start); var len = stop - (cfg.comp.seglen?0:start);
var out = "(delete)"; var out = "(delete)";
if (len > 1) { if (len > 1) {
@ -1491,6 +1497,29 @@ function selSeg(s){
requestJson(obj, false); requestJson(obj, false);
} }
function rptSeg(s)
{
var name = d.getElementById(`seg${s}t`).value;
var start = parseInt(d.getElementById(`seg${s}s`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value);
if (stop == 0) {return;}
var rev = d.getElementById(`seg${s}rev`).checked;
var mi = d.getElementById(`seg${s}mi`).checked;
var sel = d.getElementById(`seg${s}sel`).checked;
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": !powered[s], "bri": parseInt(d.getElementById(`seg${s}bri`).value), "sel": sel}};
if (d.getElementById(`seg${s}grp`)) {
var grp = parseInt(d.getElementById(`seg${s}grp`).value);
var spc = parseInt(d.getElementById(`seg${s}spc`).value);
var ofs = parseInt(d.getElementById(`seg${s}of` ).value);
obj.seg.grp = grp;
obj.seg.spc = spc;
obj.seg.of = ofs;
}
obj.seg.rpt = true;
expand(s);
requestJson(obj);
}
function setSeg(s){ function setSeg(s){
var name = d.getElementById(`seg${s}t`).value; var name = d.getElementById(`seg${s}t`).value;
var start = parseInt(d.getElementById(`seg${s}s`).value); var start = parseInt(d.getElementById(`seg${s}s`).value);
@ -1542,6 +1571,13 @@ function setSegBri(s){
requestJson(obj); requestJson(obj);
} }
function tglFreeze(s=null)
{
var obj = {"seg": {"frz": "t"}}; // toggle
if (s!==null) obj.id = s;
requestJson(obj);
}
function setX(ind = null) { function setX(ind = null) {
if (ind === null) { if (ind === null) {
ind = parseInt(d.querySelector('#fxlist input[name="fx"]:checked').value); ind = parseInt(d.querySelector('#fxlist input[name="fx"]:checked').value);

View File

@ -5,7 +5,8 @@ function gId(s)
{ {
return d.getElementById(s); return d.getElementById(s);
} }
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");} function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
function B(){window.open("/settings","_self");}
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;} function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} } else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
function FC() function FC()
@ -81,7 +82,8 @@ UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required
<td><input type="checkbox" id="R8" name="R8"></td> <td><input type="checkbox" id="R8" name="R8"></td>
</tr> </tr>
</table><br> </table><br>
Receive: <input type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" name="RX">Effects<br> Receive: <input type="checkbox" name="RB"> Brightness, <input type="checkbox" name="RC"> Color, and <input type="checkbox" name="RX"> Effects<br>
<input type="checkbox" name="SO"> Segment options<br>
Send notifications on direct change: <input type="checkbox" name="SD"><br> Send notifications on direct change: <input type="checkbox" name="SD"><br>
Send notifications on button press or IR: <input type="checkbox" name="SB"><br> Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
Send Alexa notifications: <input type="checkbox" name="SA"><br> Send Alexa notifications: <input type="checkbox" name="SA"><br>
@ -117,45 +119,45 @@ DMX mode:
<option value=5>Dimmer + Multi RGB</option> <option value=5>Dimmer + Multi RGB</option>
<option value=6>Multi RGBW</option> <option value=6>Multi RGBW</option>
</select><br> </select><br>
<a href="https://github.com/Aircoookie/WLED/wiki/E1.31-DMX" target="_blank">E1.31 info</a><br> <a href="https://kno.wled.ge/interfaces/e1.31-dmx/" target="_blank">E1.31 info</a><br>
Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br> Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
Force max brightness: <input type="checkbox" name="FB"><br> Force max brightness: <input type="checkbox" name="FB"><br>
Disable realtime gamma correction: <input type="checkbox" name="RG"><br> Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required> Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required>
<h3>Alexa Voice Assistant</h3> <h3>Alexa Voice Assistant</h3>
Emulate Alexa device: <input type="checkbox" name="AL"><br> Emulate Alexa device: <input type="checkbox" name="AL"><br>
Alexa invocation name: <input name="AI" maxlength="32"> Alexa invocation name: <input type="text" name="AI" maxlength="32">
<h3>Blynk</h3> <h3>Blynk</h3>
<b>Blynk, MQTT and Hue sync all connect to external hosts!<br> <b>Blynk, MQTT and Hue sync all connect to external hosts!<br>
This may impact the responsiveness of the ESP8266.</b><br> This may impact the responsiveness of the ESP8266.</b><br>
For best results, only use one of these services at a time.<br> For best results, only use one of these services at a time.<br>
(alternatively, connect a second ESP to them and use the UDP sync)<br><br> (alternatively, connect a second ESP to them and use the UDP sync)<br><br>
Host: <input name="BH" maxlength="32"> Host: <input type="text" name="BH" maxlength="32">
Port: <input name="BP" type="number" min="1" max="65535" value="80" class="d5"><br> Port: <input name="BP" type="number" min="1" max="65535" value="80" class="d5"><br>
Device Auth token: <input name="BK" maxlength="33"><br> Device Auth token: <input name="BK" maxlength="33"><br>
<i>Clear the token field to disable. </i><a href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info</a> <i>Clear the token field to disable. </i><a href="https://kno.wled.ge/interfaces/blynk/" target="_blank">Setup info</a>
<h3>MQTT</h3> <h3>MQTT</h3>
Enable MQTT: <input type="checkbox" name="MQ"><br> Enable MQTT: <input type="checkbox" name="MQ"><br>
Broker: <input name="MS" maxlength="32"> Broker: <input type="text" name="MS" maxlength="32">
Port: <input name="MQPORT" type="number" min="1" max="65535" class="d5"><br> Port: <input name="MQPORT" type="number" min="1" max="65535" class="d5"><br>
<b>The MQTT credentials are sent over an unsecured connection.<br> <b>The MQTT credentials are sent over an unsecured connection.<br>
Never use the MQTT password for another service!</b><br> Never use the MQTT password for another service!</b><br>
Username: <input name="MQUSER" maxlength="40"><br> Username: <input type="text" name="MQUSER" maxlength="40"><br>
Password: <input type="password" name="MQPASS" maxlength="64"><br> Password: <input type="password" name="MQPASS" maxlength="64"><br>
Client ID: <input name="MQCID" maxlength="40"><br> Client ID: <input type="text" name="MQCID" maxlength="40"><br>
Device Topic: <input name="MD" maxlength="32"><br> Device Topic: <input type="text" name="MD" maxlength="32"><br>
Group Topic: <input name="MG" maxlength="32"><br> Group Topic: <input type="text" name="MG" maxlength="32"><br>
Publish on button press: <input type="checkbox" name="BM"><br> Publish on button press: <input type="checkbox" name="BM"><br>
<i>Reboot required to apply changes. </i><a href="https://github.com/Aircoookie/WLED/wiki/MQTT" target="_blank">MQTT info</a> <i>Reboot required to apply changes. </i><a href="https://kno.wled.ge/interfaces/mqtt/" target="_blank">MQTT info</a>
<h3>Philips Hue</h3> <h3>Philips Hue</h3>
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br> <i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
Poll Hue light <input name="HL" type="number" min="1" max="99" > every <input name="HI" type="number" min="100" max="65000"> ms: <input type="checkbox" name="HP"><br> Poll Hue light <input name="HL" type="number" min="1" max="99" > every <input name="HI" type="number" min="100" max="65000"> ms: <input type="checkbox" name="HP"><br>
Then, receive <input type="checkbox" name="HO"> On/Off, <input type="checkbox" name="HB"> Brightness, and <input type="checkbox" name="HC"> Color<br> Then, receive <input type="checkbox" name="HO"> On/Off, <input type="checkbox" name="HB"> Brightness, and <input type="checkbox" name="HC"> Color<br>
Hue Bridge IP:<br> Hue Bridge IP:<br>
<input name="H0" type="number" min="0" max="255" > . <input name="H0" type="number" class="s" min="0" max="255" > .
<input name="H1" type="number" min="0" max="255" > . <input name="H1" type="number" class="s" min="0" max="255" > .
<input name="H2" type="number" min="0" max="255" > . <input name="H2" type="number" class="s" min="0" max="255" > .
<input name="H3" type="number" min="0" max="255" ><br> <input name="H3" type="number" class="s" min="0" max="255" ><br>
<b>Press the pushlink button on the bridge, after that save this page!</b><br> <b>Press the pushlink button on the bridge, after that save this page!</b><br>
(when first connecting)<br> (when first connecting)<br>
Hue status: <span class="sip"> Disabled in this build </span><hr> Hue status: <span class="sip"> Disabled in this build </span><hr>

View File

@ -15,9 +15,11 @@ void handleAlexa();
void onAlexaChange(EspalexaDevice* dev); void onAlexaChange(EspalexaDevice* dev);
//blynk.cpp //blynk.cpp
#ifndef WLED_DISABLE_BLYNK
void initBlynk(const char* auth, const char* host, uint16_t port); void initBlynk(const char* auth, const char* host, uint16_t port);
void handleBlynk(); void handleBlynk();
void updateBlynk(); void updateBlynk();
#endif
//button.cpp //button.cpp
void shortPressAction(uint8_t b=0); void shortPressAction(uint8_t b=0);
@ -56,7 +58,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp //colors.cpp
void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint32(uint32_t in, bool secondary = false);
void colorFromUint24(uint32_t in, bool secondary = false); void colorFromUint24(uint32_t in, bool secondary = false);
uint32_t colorFromRgbw(byte* rgbw); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -141,7 +143,6 @@ void resetTimebase();
void toggleOnOff(); void toggleOnOff();
void setAllLeds(); void setAllLeds();
void setLedsStandard(); void setLedsStandard();
bool colorChanged();
void colorUpdated(int callMode); void colorUpdated(int callMode);
void updateInterfaces(uint8_t callMode); void updateInterfaces(uint8_t callMode);
void handleTransitions(); void handleTransitions();

View File

@ -245,7 +245,7 @@ type="button" onclick="Save()">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_sync.htm, do not edit!! // Autogenerated from wled00/data/settings_sync.htm, do not edit!!
const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500"> const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>Sync Settings</title><script> <meta charset="utf-8"><title>Sync Settings</title><script>
var d=document;function gId(e){return d.getElementById(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings")}function B(){window.open("/settings","_self")}function adj(){6454==d.Sf.DI.value?(1==d.Sf.DA.value&&(d.Sf.DA.value=0),1==d.Sf.EU.value&&(d.Sf.EU.value=0)):5568==d.Sf.DI.value&&(0==d.Sf.DA.value&&(d.Sf.DA.value=1),0==d.Sf.EU.value&&(d.Sf.EU.value=1))}function FC(){for(j=0;j<8;j++)gId("G"+(j+1)).checked=gId("GS").value>>j&1,gId("R"+(j+1)).checked=gId("GR").value>>j&1}function GC(){var e=0,d=0,n=1;for(j=0;j<8;j++)e+=gId("G"+(j+1)).checked*n,d+=gId("R"+(j+1)).checked*n,n*=2;gId("GS").value=e,gId("GR").value=d}function SP(){var e=d.Sf.DI.value;gId("xp").style.display=e>0?"none":"block",e>0&&(d.Sf.EP.value=e)}function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568:d.Sf.DI.value=5568;break;case 6454:d.Sf.DI.value=6454;break;case 4048:d.Sf.DI.value=4048}SP(),FC()}function S(){GetV(),SetVal()}function GetV() { var d=document;function gId(e){return d.getElementById(e)}function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/")}function B(){window.open("/settings","_self")}function adj(){6454==d.Sf.DI.value?(1==d.Sf.DA.value&&(d.Sf.DA.value=0),1==d.Sf.EU.value&&(d.Sf.EU.value=0)):5568==d.Sf.DI.value&&(0==d.Sf.DA.value&&(d.Sf.DA.value=1),0==d.Sf.EU.value&&(d.Sf.EU.value=1))}function FC(){for(j=0;j<8;j++)gId("G"+(j+1)).checked=gId("GS").value>>j&1,gId("R"+(j+1)).checked=gId("GR").value>>j&1}function GC(){var e=0,d=0,n=1;for(j=0;j<8;j++)e+=gId("G"+(j+1)).checked*n,d+=gId("R"+(j+1)).checked*n,n*=2;gId("GS").value=e,gId("GR").value=d}function SP(){var e=d.Sf.DI.value;gId("xp").style.display=e>0?"none":"block",e>0&&(d.Sf.EP.value=e)}function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568:d.Sf.DI.value=5568;break;case 6454:d.Sf.DI.value=6454;break;case 4048:d.Sf.DI.value=4048}SP(),FC()}function S(){GetV(),SetVal()}function GetV() {
%CSS%%SCSS%</head><body onload="S()"><form %CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="GC()"><div class="helpB"><button id="form_s" name="Sf" method="post" onsubmit="GC()"><div class="helpB"><button
type="button" onclick="H()">?</button></div><button type="button" onclick="B()"> type="button" onclick="H()">?</button></div><button type="button" onclick="B()">
@ -269,11 +269,12 @@ name="R5"></td><td><input type="checkbox" id="R6" name="R6"></td><td><input
type="checkbox" id="R7" name="R7"></td><td><input type="checkbox" id="R8" type="checkbox" id="R7" name="R7"></td><td><input type="checkbox" id="R8"
name="R8"></td></tr></table><br>Receive: <input type="checkbox" name="RB"> name="R8"></td></tr></table><br>Receive: <input type="checkbox" name="RB">
Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox"
name="RX">Effects<br>Send notifications on direct change: <input name="RX">Effects<br><input type="checkbox" name="SO">Segment options<br>
type="checkbox" name="SD"><br>Send notifications on button press or IR: <input Send notifications on direct change: <input type="checkbox" name="SD"><br>
type="checkbox" name="SB"><br>Send Alexa notifications: <input type="checkbox" Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
name="SA"><br>Send Philips Hue change notifications: <input type="checkbox" Send Alexa notifications: <input type="checkbox" name="SA"><br>
name="SH"><br>Send Macro notifications: <input type="checkbox" name="SM"><br> Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
Send Macro notifications: <input type="checkbox" name="SM"><br>
Send notifications twice: <input type="checkbox" name="S2"><br><i> Send notifications twice: <input type="checkbox" name="S2"><br><i>
Reboot required to apply changes.</i><h3>Instance List</h3> Reboot required to apply changes.</i><h3>Instance List</h3>
Enable instance list: <input type="checkbox" name="NL"><br> Enable instance list: <input type="checkbox" name="NL"><br>
@ -293,43 +294,44 @@ Disabled</option><option value="1">Single RGB</option><option value="2">
Single DRGB</option><option value="3">Effect</option><option value="4">Multi RGB Single DRGB</option><option value="3">Effect</option><option value="4">Multi RGB
</option><option value="5">Dimmer + Multi RGB</option><option value="6"> </option><option value="5">Dimmer + Multi RGB</option><option value="6">
Multi RGBW</option></select><br><a Multi RGBW</option></select><br><a
href="https://github.com/Aircoookie/WLED/wiki/E1.31-DMX" target="_blank"> href="https://kno.wled.ge/interfaces/e1.31-dmx/" target="_blank">E1.31 info</a>
E1.31 info</a><br>Timeout: <input name="ET" type="number" min="1" max="65000" <br>Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
required> ms<br>Force max brightness: <input type="checkbox" name="FB"><br> Force max brightness: <input type="checkbox" name="FB"><br>
Disable realtime gamma correction: <input type="checkbox" name="RG"><br> Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" Realtime LED offset: <input name="WO" type="number" min="-255" max="255"
required><h3>Alexa Voice Assistant</h3>Emulate Alexa device: <input required><h3>Alexa Voice Assistant</h3>Emulate Alexa device: <input
type="checkbox" name="AL"><br>Alexa invocation name: <input name="AI" type="checkbox" name="AL"><br>Alexa invocation name: <input type="text"
maxlength="32"><h3>Blynk</h3><b> name="AI" maxlength="32"><h3>Blynk</h3><b>
Blynk, MQTT and Hue sync all connect to external hosts!<br> Blynk, MQTT and Hue sync all connect to external hosts!<br>
This may impact the responsiveness of the ESP8266.</b><br> This may impact the responsiveness of the ESP8266.</b><br>
For best results, only use one of these services at a time.<br> For best results, only use one of these services at a time.<br>
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>Host: (alternatively, connect a second ESP to them and use the UDP sync)<br><br>Host:
<input name="BH" maxlength="32"> Port: <input name="BP" type="number" min="1" <input type="text" name="BH" maxlength="32"> Port: <input name="BP"
max="65535" value="80" class="d5"><br>Device Auth token: <input name="BK" type="number" min="1" max="65535" value="80" class="d5"><br>Device Auth token:
maxlength="33"><br><i>Clear the token field to disable. </i><a <input name="BK" maxlength="33"><br><i>Clear the token field to disable. </i><a
href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info href="https://kno.wled.ge/interfaces/blynk/" target="_blank">Setup info</a><h3>
</a><h3>MQTT</h3>Enable MQTT: <input type="checkbox" name="MQ"><br>Broker: MQTT</h3>Enable MQTT: <input type="checkbox" name="MQ"><br>Broker: <input
<input name="MS" maxlength="32"> Port: <input name="MQPORT" type="number" type="text" name="MS" maxlength="32"> Port: <input name="MQPORT" type="number"
min="1" max="65535" class="d5"><br><b> min="1" max="65535" class="d5"><br><b>
The MQTT credentials are sent over an unsecured connection.<br> The MQTT credentials are sent over an unsecured connection.<br>
Never use the MQTT password for another service!</b><br>Username: <input Never use the MQTT password for another service!</b><br>Username: <input
name="MQUSER" maxlength="40"><br>Password: <input type="password" name="MQPASS" type="text" name="MQUSER" maxlength="40"><br>Password: <input type="password"
maxlength="64"><br>Client ID: <input name="MQCID" maxlength="40"><br> name="MQPASS" maxlength="64"><br>Client ID: <input type="text" name="MQCID"
Device Topic: <input name="MD" maxlength="32"><br>Group Topic: <input name="MG" maxlength="40"><br>Device Topic: <input type="text" name="MD" maxlength="32">
maxlength="32"><br>Publish on button press: <input type="checkbox" name="BM"> <br>Group Topic: <input type="text" name="MG" maxlength="32"><br>
<br><i>Reboot required to apply changes. </i><a Publish on button press: <input type="checkbox" name="BM"><br><i>
href="https://github.com/Aircoookie/WLED/wiki/MQTT" target="_blank">MQTT info Reboot required to apply changes. </i><a
</a><h3>Philips Hue</h3><i> href="https://kno.wled.ge/interfaces/mqtt/" target="_blank">MQTT info</a><h3>
Philips Hue</h3><i>
You can find the bridge IP and the light number in the 'About' section of the hue app. You can find the bridge IP and the light number in the 'About' section of the hue app.
</i><br>Poll Hue light <input name="HL" type="number" min="1" max="99"> every </i><br>Poll Hue light <input name="HL" type="number" min="1" max="99"> every
<input name="HI" type="number" min="100" max="65000"> ms: <input <input name="HI" type="number" min="100" max="65000"> ms: <input
type="checkbox" name="HP"><br>Then, receive <input type="checkbox" name="HO"> type="checkbox" name="HP"><br>Then, receive <input type="checkbox" name="HO">
On/Off, <input type="checkbox" name="HB"> Brightness, and <input On/Off, <input type="checkbox" name="HB"> Brightness, and <input
type="checkbox" name="HC"> Color<br>Hue Bridge IP:<br><input name="H0" type="checkbox" name="HC"> Color<br>Hue Bridge IP:<br><input name="H0"
type="number" min="0" max="255"> . <input name="H1" type="number" min="0" type="number" class="s" min="0" max="255"> . <input name="H1" type="number"
max="255"> . <input name="H2" type="number" min="0" max="255"> . <input class="s" min="0" max="255"> . <input name="H2" type="number" class="s" min="0"
name="H3" type="number" min="0" max="255"><br><b> max="255"> . <input name="H3" type="number" class="s" min="0" max="255"><br><b>
Press the pushlink button on the bridge, after that save this page!</b><br> Press the pushlink button on the bridge, after that save this page!</b><br>
(when first connecting)<br>Hue status: <span class="sip">Disabled in this build (when first connecting)<br>Hue status: <span class="sip">Disabled in this build
</span><hr><button type="button" onclick="B()">Back</button><button </span><hr><button type="button" onclick="B()">Back</button><button

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255) { bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255) {
if (elem.is<int>()) { if (elem.is<int>()) {
if (elem < 0) return false; //ignore e.g. {"ps":-1} if (elem < 0) return false; //ignore e.g. {"ps":-1}
*val = elem; *val = elem;
return true; return true;
} else if (elem.is<const char*>()) { } else if (elem.is<const char*>()) {
@ -37,6 +37,24 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
stop = (len > 0) ? start + len : seg.stop; stop = (len > 0) ? start + len : seg.stop;
} }
//repeat, multiplies segment until all LEDs are used, or max segments reached
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;
for (byte i=id+1; i<strip.getMaxSegments(); i++) {
start = start + len;
if (start >= strip.getLengthTotal()) break;
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
}
return;
}
if (elem["n"]) { if (elem["n"]) {
// name field exists // name field exists
if (seg.name) { //clear old name if (seg.name) { //clear old name
@ -64,7 +82,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t grp = elem["grp"] | seg.grouping; uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing; uint16_t spc = elem[F("spc")] | seg.spacing;
uint16_t of = seg.offset; uint16_t of = seg.offset;
if (!(elem[F("spc")].isNull() && elem["grp"].isNull())) effectChanged = true; //send UDP
uint16_t len = 1; uint16_t len = 1;
if (stop > start) len = stop - start; if (stop > start) len = stop - start;
@ -76,7 +95,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs; of = offsetAbs;
} }
if (stop > start && of > len -1) of = len -1; if (stop > start && of > len -1) of = len -1;
strip.setSegment(id, start, stop, grp, spc, of); strip.setSegment(id, start, stop, grp, spc, of);
byte segbri = 0; byte segbri = 0;
if (getVal(elem["bri"], &segbri)) { if (getVal(elem["bri"], &segbri)) {
@ -87,10 +106,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON); bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on; if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id); seg.setOption(SEG_OPTION_ON, on, id);
bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
uint8_t cctPrev = seg.cct; 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);
uint8_t cctPrev = seg.cct;
seg.setCCT(elem["cct"] | seg.cct, id); seg.setCCT(elem["cct"] | seg.cct, id);
if (seg.cct != cctPrev && id == strip.getMainSegmentId()) effectChanged = true; //send UDP if (seg.cct != cctPrev && id == strip.getMainSegmentId()) effectChanged = true; //send UDP
JsonArray colarr = elem["col"]; JsonArray colarr = elem["col"];
if (!colarr.isNull()) if (!colarr.isNull())
@ -124,14 +146,11 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
if (!colValid) continue; if (!colValid) continue;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{ uint32_t color = RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];} colorChanged |= (seg.colors[i] != color);
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];} seg.setColor(i, color, id);
} else { //normal case, apply directly to segment if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id);
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
} }
} }
@ -152,26 +171,20 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
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 )); seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
//temporary, strip object gets updated via colorUpdated() if (!(elem[F("sel")].isNull() && elem["rev"].isNull() && elem["on"].isNull() && elem[F("mi")].isNull())) effectChanged = true; //send UDP
if (id == strip.getMainSegmentId()) {
byte effectPrev = effectCurrent; byte fx = seg.mode;
if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value) byte fxPrev = fx;
if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
} strip.setMode(id, fx);
effectSpeed = elem[F("sx")] | effectSpeed; if (!presetId && seg.mode != fxPrev) effectChanged = true; //send UDP
effectIntensity = elem[F("ix")] | effectIntensity;
getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount());
} else { //permanent
byte fx = seg.mode;
byte fxPrev = fx;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
strip.setMode(id, fx);
if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity;
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
} }
byte prevSpd = seg.speed;
byte prevInt = seg.intensity;
byte prevPal = seg.palette;
if (getVal(elem[F("sx")], &seg.speed, 0, 255) && !presetId && prevSpd != seg.speed) effectChanged = true; //also supports inc/decrementing and random
if (getVal(elem[F("ix")], &seg.intensity, 0, 255) && !presetId && prevInt != seg.intensity) effectChanged = true; //also supports inc/decrementing and random
if (getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount()) && !presetId && prevPal != seg.palette) effectChanged = true; //also supports inc/decrementing and random
JsonArray iarr = elem[F("i")]; //set individual LEDs JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) { if (!iarr.isNull()) {
@ -223,12 +236,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
strip.setPixelSegment(255); strip.setPixelSegment(255);
strip.trigger(); strip.trigger();
} else { //return to regular effect } else if (!elem["frz"] && iarr.isNull()) { //return to regular effect
seg.setOption(SEG_OPTION_FREEZE, false); seg.setOption(SEG_OPTION_FREEZE, false);
} }
return; // seg.differs(prev); return; // seg.differs(prev);
} }
// deserializes WLED state (fileDoc points to doc object if called from web server)
bool deserializeState(JsonObject root, byte callMode, byte presetId) bool deserializeState(JsonObject root, byte callMode, byte presetId)
{ {
strip.applyToAllSelected = false; strip.applyToAllSelected = false;
@ -264,16 +278,16 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
tr = root[F("tb")] | -1; tr = root[F("tb")] | -1;
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis(); if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
JsonObject nl = root["nl"]; JsonObject nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive; nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl[F("dur")] | nightlightDelayMins; nightlightDelayMins = nl[F("dur")] | nightlightDelayMins;
nightlightMode = nl[F("mode")] | nightlightMode; nightlightMode = nl[F("mode")] | nightlightMode;
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri; nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
JsonObject udpn = root["udpn"]; JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect; notifyDirect = udpn["send"] | notifyDirect;
receiveNotifications = udpn["recv"] | receiveNotifications; receiveNotifications = udpn["recv"] | receiveNotifications;
bool noNotification = udpn[F("nn")]; //send no notification just for this request if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
if (timein != UINT32_MAX) { if (timein != UINT32_MAX) {
@ -294,22 +308,21 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
byte prevMain = strip.getMainSegmentId(); byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root[F("mainseg")] | prevMain; strip.mainSegment = root[F("mainseg")] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg(); //if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
int it = 0; int it = 0;
JsonVariant segVar = root["seg"]; JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>()) if (segVar.is<JsonObject>())
{ {
int id = segVar["id"] | -1; int id = segVar["id"] | -1;
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) { //set all selected segments if (id < 0) {
//apply all selected segments
bool didSet = false; bool didSet = false;
byte lowestActive = 99; byte lowestActive = 99;
for (byte s = 0; s < strip.getMaxSegments(); s++) for (byte s = 0; s < strip.getMaxSegments(); s++) {
{ WS2812FX::Segment &sg = strip.getSegment(s);
WS2812FX::Segment sg = strip.getSegment(s); if (sg.isActive()) {
if (sg.isActive())
{
if (lowestActive == 99) lowestActive = s; if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) { if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId); deserializeSegment(segVar, s, presetId);
@ -317,9 +330,10 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
} }
} }
} }
//TODO: not sure if it is good idea to change first active but unselected segment
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId); if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
} else { //set only the segment with the specified ID } else {
deserializeSegment(segVar, it, presetId); deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
} }
} else { } else {
JsonArray segs = segVar.as<JsonArray>(); JsonArray segs = segVar.as<JsonArray>();
@ -329,6 +343,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
it++; it++;
} }
} }
setValuesFromMainSeg(); //to make transition work on main segment
if (effectChanged) unloadPlaylist(); //if any of the effect parameter changed unload playlist
#ifndef WLED_DISABLE_CRONIXIE #ifndef WLED_DISABLE_CRONIXIE
if (root["nx"].is<const char*>()) { if (root["nx"].is<const char*>()) {
@ -369,63 +385,65 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
JsonObject playlist = root[F("playlist")]; JsonObject playlist = root[F("playlist")];
if (!playlist.isNull() && loadPlaylist(playlist, presetId)) { if (!playlist.isNull() && loadPlaylist(playlist, presetId)) {
//do not notify here, because the first playlist entry will do //do not notify here, because the first playlist entry will do
noNotification = true; 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=~
} else { } else {
interfaceUpdateCallMode = CALL_MODE_WS_SEND; interfaceUpdateCallMode = CALL_MODE_WS_SEND;
} }
colorUpdated(noNotification ? CALL_MODE_NO_NOTIFY : callMode); colorUpdated(callMode);
return stateResponse; return stateResponse;
} }
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds) void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
{ {
root["id"] = id; root["id"] = id;
if (segmentBounds) { if (segmentBounds) {
root["start"] = seg.start; root["start"] = seg.start;
root["stop"] = seg.stop; root["stop"] = seg.stop;
} }
if (!forPreset) root[F("len")] = seg.stop - seg.start; if (!forPreset) root[F("len")] = seg.stop - seg.start;
root["grp"] = seg.grouping; root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing; root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset; root[F("of")] = seg.offset;
root["on"] = seg.getOption(SEG_OPTION_ON); root["on"] = seg.getOption(SEG_OPTION_ON);
root["frz"] = seg.getOption(SEG_OPTION_FREEZE);
byte segbri = seg.opacity; byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255; root["bri"] = (segbri) ? segbri : 255;
root["cct"] = seg.cct; root["cct"] = seg.cct;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255) // to conserve RAM we will serialize the col array manually
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
for (uint8_t i = 0; i < 3; i++) char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
{ const char *format = strip.isRgbw ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]");
for (uint8_t i = 0; i < 3; i++)
{
byte segcol[4]; byte* c = segcol; byte segcol[4]; byte* c = segcol;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{ {
c = (i == 0)? col:colSec; c = (i == 0)? col:colSec;
} else { } else {
segcol[0] = (byte)(seg.colors[i] >> 16); segcol[1] = (byte)(seg.colors[i] >> 8); segcol[0] = R(seg.colors[i]);
segcol[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24); segcol[1] = G(seg.colors[i]);
segcol[2] = B(seg.colors[i]);
segcol[3] = W(seg.colors[i]);
} }
char tmpcol[22]; char tmpcol[22];
if (strip.isRgbw) sprintf_P(tmpcol, PSTR("[%u,%u,%u,%u]"), c[0], c[1], c[2], c[3]); sprintf_P(tmpcol, format, (unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3]);
else sprintf_P(tmpcol, PSTR("[%u,%u,%u]"), c[0], c[1], c[2]); strcat(colstr, i<2 ? strcat_P(tmpcol, PSTR(",")) : tmpcol);
}
strcat(colstr, i<2 ? strcat(tmpcol,",") : tmpcol); strcat_P(colstr, PSTR("]"));
}
strcat(colstr,"]");
root["col"] = serialized(colstr); root["col"] = serialized(colstr);
root["fx"] = seg.mode; root["fx"] = seg.mode;
root[F("sx")] = seg.speed; root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity; root[F("ix")] = seg.intensity;
root["pal"] = seg.palette; root["pal"] = seg.palette;
root[F("sel")] = seg.isSelected(); root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED); root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR); root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
} }
@ -438,7 +456,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
} }
if (!forPreset) { if (!forPreset) {
if (errorFlag) root[F("error")] = errorFlag; if (errorFlag) {root[F("error")] = errorFlag; errorFlag = ERR_NONE;} //prevent error message to persist on screen
root["ps"] = (currentPreset > 0) ? currentPreset : -1; root["ps"] = (currentPreset > 0) ? currentPreset : -1;
root[F("pl")] = currentPlaylist; root[F("pl")] = currentPlaylist;
@ -466,11 +484,9 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root[F("mainseg")] = strip.getMainSegmentId(); root[F("mainseg")] = strip.getMainSegmentId();
JsonArray seg = root.createNestedArray("seg"); JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) for (byte s = 0; s < strip.getMaxSegments(); s++) {
{ WS2812FX::Segment &sg = strip.getSegment(s);
WS2812FX::Segment sg = strip.getSegment(s); if (sg.isActive()) {
if (sg.isActive())
{
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds); serializeSegment(seg0, sg, s, forPreset, segmentBounds);
} else if (forPreset && segmentBounds) { //disable segments not part of preset } else if (forPreset && segmentBounds) { //disable segments not part of preset
@ -511,12 +527,12 @@ void serializeInfo(JsonObject root)
leds[F("rgbw")] = strip.isRgbw; leds[F("rgbw")] = strip.isRgbw;
leds[F("wv")] = false; leds[F("wv")] = false;
leds["cct"] = correctWB || strip.hasCCTBus(); leds["cct"] = correctWB || strip.hasCCTBus();
switch (Bus::getAutoWhiteMode()) { switch (Bus::getAutoWhiteMode()) {
case RGBW_MODE_MANUAL_ONLY: case RGBW_MODE_MANUAL_ONLY:
case RGBW_MODE_DUAL: case RGBW_MODE_DUAL:
if (strip.isRgbw) leds[F("wv")] = true; if (strip.isRgbw) leds[F("wv")] = true;
break; break;
} }
leds[F("pwr")] = strip.currentMilliamps; leds[F("pwr")] = strip.currentMilliamps;
leds["fps"] = strip.getFps(); leds["fps"] = strip.getFps();
@ -596,9 +612,11 @@ void serializeInfo(JsonObject root)
#endif #endif
root[F("freeheap")] = ESP.getFreeHeap(); root[F("freeheap")] = ESP.getFreeHeap();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) root[F("psram")] = ESP.getFreePsram();
#endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
usermods.addToJsonInfo(root); usermods.addToJsonInfo(root);
byte os = 0; byte os = 0;
@ -645,7 +663,7 @@ void setPaletteColors(JsonArray json, CRGBPalette16 palette)
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
JsonArray colors = json.createNestedArray(); JsonArray colors = json.createNestedArray();
CRGB color = palette[i]; CRGB color = palette[i];
colors.add((((float)i / (float)16) * 255)); colors.add(i<<4);
colors.add(color.red); colors.add(color.red);
colors.add(color.green); colors.add(color.green);
colors.add(color.blue); colors.add(color.blue);
@ -707,9 +725,6 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
JsonArray curPalette = palettes.createNestedArray(String(i)); JsonArray curPalette = palettes.createNestedArray(String(i));
CRGB prim;
CRGB sec;
CRGB ter;
switch (i) { switch (i) {
case 0: //default palette case 0: //default palette
setPaletteColors(curPalette, PartyColors_p); setPaletteColors(curPalette, PartyColors_p);
@ -877,13 +892,13 @@ void serveJson(AsyncWebServerRequest* request)
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
{ {
#ifdef WLED_ENABLE_WEBSOCKETS
AsyncWebSocketClient * wsc = nullptr; AsyncWebSocketClient * wsc = nullptr;
if (!request) { //not HTTP, use Websockets if (!request) { //not HTTP, use Websockets
#ifdef WLED_ENABLE_WEBSOCKETS
wsc = ws.client(wsClient); wsc = ws.client(wsClient);
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
#endif
} }
#endif
uint16_t used = strip.getLengthTotal(); 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 uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS

View File

@ -44,8 +44,8 @@ byte scaledBri(byte in)
void setAllLeds() { void setAllLeds() {
strip.setColor(0, col[0], col[1], col[2], col[3]); strip.setColor(0, RGBW32(col[0], col[1], col[2], col[3]));
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]); strip.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]));
if (!realtimeMode || !arlsForceMaxBri) if (!realtimeMode || !arlsForceMaxBri)
{ {
strip.setBrightness(scaledBri(briT)); strip.setBrightness(scaledBri(briT));
@ -61,106 +61,67 @@ void setLedsStandard()
} }
bool colorChanged()
{
for (byte i=0; i<4; i++)
{
if (col[i] != colIT[i]) return true;
if (colSec[i] != colSecIT[i]) return true;
}
if (bri != briIT) return true;
return false;
}
void colorUpdated(int callMode) void colorUpdated(int callMode)
{ {
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset
if (callMode != CALL_MODE_INIT &&
callMode != CALL_MODE_DIRECT_CHANGE &&
callMode != CALL_MODE_NO_NOTIFY &&
callMode != CALL_MODE_BUTTON_PRESET) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
bool someSel = false; if (bri != briOld || effectChanged || colorChanged) {
if (callMode == CALL_MODE_NOTIFICATION) {
someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
}
//Notifier: apply received FX to selected segments only if actually receiving FX
if (someSel) strip.applyToAllSelected = receiveNotificationEffects;
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette) || effectChanged;
bool colChanged = colorChanged();
//Notifier: apply received color to selected segments only if actually receiving color
if (someSel) strip.applyToAllSelected = receiveNotificationColor;
if (fxChanged || colChanged)
{
effectChanged = false;
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
currentPreset = 0; //something changed, so we are no longer in the preset if (effectChanged) currentPreset = 0; //something changed, so we are no longer in the preset
notify(callMode); if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
//set flag to update blynk, ws and mqtt //set flag to update blynk, ws and mqtt
interfaceUpdateCallMode = callMode; interfaceUpdateCallMode = callMode;
effectChanged = false;
colorChanged = false;
} else { } else {
if (nightlightActive && !nightlightActiveOld && if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
callMode != CALL_MODE_NOTIFICATION &&
callMode != CALL_MODE_NO_NOTIFY)
{
notify(CALL_MODE_NIGHTLIGHT); notify(CALL_MODE_NIGHTLIGHT);
interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT; interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT;
} }
} }
if (!colChanged) return; //following code is for e.g. initiating transitions if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) {
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE))
{
briNlT = bri; briNlT = bri;
nightlightDelayMs -= (millis() - nightlightStartTime); nightlightDelayMs -= (millis() - nightlightStartTime);
nightlightStartTime = millis(); nightlightStartTime = millis();
} }
for (byte i=0; i<4; i++) if (briT == 0) {
{
colIT[i] = col[i];
colSecIT[i] = colSec[i];
}
if (briT == 0)
{
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
} }
briIT = bri;
if (bri > 0) briLast = bri; if (bri > 0) briLast = bri;
//deactivate nightlight if target brightness is reached //deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false; if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false;
if (fadeTransition) if (fadeTransition) {
{
//set correct delay if not using notification delay //set correct delay if not using notification delay
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
jsonTransitionOnce = false; jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp); strip.setTransition(transitionDelayTemp);
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} if (transitionDelayTemp == 0) {
//setLedsStandard();
if (transitionActive) briOld = briT = bri;
{ if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger();
return;
}
if (transitionActive) {
briOld = briT; briOld = briT;
tperLast = 0; tperLast = 0;
} }
strip.setTransitionMode(true); strip.setTransitionMode(true);
transitionActive = true; transitionActive = true;
transitionStartTime = millis(); transitionStartTime = millis();
} else } else {
{
strip.setTransition(0); strip.setTransition(0);
setLedsStandard(); //setLedsStandard();
briOld = briT = bri;
if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger(); strip.trigger();
} }
} }
@ -169,10 +130,8 @@ void colorUpdated(int callMode)
void updateInterfaces(uint8_t callMode) void updateInterfaces(uint8_t callMode)
{ {
sendDataWs(); sendDataWs();
if (callMode == CALL_MODE_WS_SEND) { lastInterfaceUpdate = millis();
lastInterfaceUpdate = millis(); if (callMode == CALL_MODE_WS_SEND) return;
return;
}
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) { if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
@ -185,7 +144,6 @@ void updateInterfaces(uint8_t callMode)
callMode != CALL_MODE_NO_NOTIFY) updateBlynk(); callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
#endif #endif
doPublishMqtt = true; doPublishMqtt = true;
lastInterfaceUpdate = millis();
} }
@ -221,6 +179,13 @@ void handleTransitions()
void handleNightlight() void handleNightlight()
{ {
/*
static unsigned long lastNlUpdate;
unsigned long now = millis();
if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; //take care of millis() rollover
if (now - lastNlUpdate < 100) return; //allow only 10 NL updates per second
lastNlUpdate = now;
*/
if (nightlightActive) if (nightlightActive)
{ {
if (!nightlightActiveOld) //init if (!nightlightActiveOld) //init

View File

@ -218,7 +218,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
receiveNotificationBrightness = request->hasArg(F("RB")); receiveNotificationBrightness = request->hasArg(F("RB"));
receiveNotificationColor = request->hasArg(F("RC")); receiveNotificationColor = request->hasArg(F("RC"));
receiveNotificationEffects = request->hasArg(F("RX")); receiveNotificationEffects = request->hasArg(F("RX"));
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); receiveSegmentOptions = request->hasArg(F("SO"));
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
notifyDirectDefault = request->hasArg(F("SD")); notifyDirectDefault = request->hasArg(F("SD"));
notifyDirect = notifyDirectDefault; notifyDirect = notifyDirectDefault;
notifyButton = request->hasArg(F("SB")); notifyButton = request->hasArg(F("SB"));
@ -307,6 +308,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//start ntp if not already connected //start ntp if not already connected
if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort); if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort);
ntpLastSyncTime = 0; // force new NTP query
longitude = request->arg(F("LN")).toFloat(); longitude = request->arg(F("LN")).toFloat();
latitude = request->arg(F("LT")).toFloat(); latitude = request->arg(F("LT")).toFloat();
@ -592,7 +594,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
DEBUG_PRINT(F("API req: ")); DEBUG_PRINT(F("API req: "));
DEBUG_PRINTLN(req); DEBUG_PRINTLN(req);
strip.applyToAllSelected = false; strip.applyToAllSelected = true;
//segment select (sets main segment) //segment select (sets main segment)
byte prevMain = strip.getMainSegmentId(); byte prevMain = strip.getMainSegmentId();
@ -603,22 +605,28 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
byte selectedSeg = strip.getMainSegmentId(); byte selectedSeg = strip.getMainSegmentId();
if (selectedSeg != prevMain) setValuesFromMainSeg(); if (selectedSeg != prevMain) setValuesFromMainSeg();
//snapshot to check if request changed values later, temporary.
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
byte prevEffect = effectCurrent;
byte prevSpeed = effectSpeed;
byte prevIntensity = effectIntensity;
byte prevPalette = effectPalette;
pos = req.indexOf(F("SS=")); pos = req.indexOf(F("SS="));
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t < strip.getMaxSegments()) selectedSeg = t; if (t < strip.getMaxSegments()) {
selectedSeg = t;
strip.applyToAllSelected = false;
}
} }
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg); WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t == 2) { if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0);
}
}
selseg.setOption(SEG_OPTION_SELECTED, t); selseg.setOption(SEG_OPTION_SELECTED, t);
} }
@ -684,26 +692,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
applyPreset(presetCycCurr); applyPreset(presetCycCurr);
} }
//snapshot to check if request changed values later, temporary.
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
byte prevEffect = effectCurrent;
byte prevSpeed = effectSpeed;
byte prevIntensity = effectIntensity;
byte prevPalette = effectPalette;
//set brightness //set brightness
updateVal(&req, "&A=", &bri); updateVal(&req, "&A=", &bri);
bool col0Changed = false, col1Changed = false, col2Changed = false;
//set colors //set colors
updateVal(&req, "&R=", &col[0]); updateVal(&req, "&R=", &col[0]);
updateVal(&req, "&G=", &col[1]); updateVal(&req, "&G=", &col[1]);
updateVal(&req, "&B=", &col[2]); updateVal(&req, "&B=", &col[2]);
updateVal(&req, "&W=", &col[3]); updateVal(&req, "&W=", &col[3]);
for (byte i=0; i<4; i++) if (prevCol[i]!=col[i]) col0Changed = colorChanged = true;
updateVal(&req, "R2=", &colSec[0]); updateVal(&req, "R2=", &colSec[0]);
updateVal(&req, "G2=", &colSec[1]); updateVal(&req, "G2=", &colSec[1]);
updateVal(&req, "B2=", &colSec[2]); updateVal(&req, "B2=", &colSec[2]);
updateVal(&req, "W2=", &colSec[3]); updateVal(&req, "W2=", &colSec[3]);
for (byte i=0; i<4; i++) if (prevColSec[i]!=colSec[i]) col1Changed = colorChanged = true;
#ifdef WLED_ENABLE_LOXONE #ifdef WLED_ENABLE_LOXONE
//lox parser //lox parser
@ -734,52 +737,64 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
tempsat = getNumVal(&req, pos); tempsat = getNumVal(&req, pos);
} }
colorHStoRGB(temphue,tempsat,(req.indexOf(F("H2"))>0)? colSec:col); byte sec = req.indexOf(F("H2"));
colorHStoRGB(temphue, tempsat, (sec>0) ? colSec : col);
if (sec>0) col1Changed = true;
else col0Changed = true;
colorChanged = true;
} }
//set white spectrum (kelvin) //set white spectrum (kelvin)
pos = req.indexOf(F("&K=")); pos = req.indexOf(F("&K="));
if (pos > 0) { if (pos > 0) {
colorKtoRGB(getNumVal(&req, pos),(req.indexOf(F("K2"))>0)? colSec:col); byte sec = req.indexOf(F("K2"));
colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colSec : col);
if (sec>0) col1Changed = true;
else col0Changed = true;
colorChanged = true;
} }
//set color from HEX or 32bit DEC //set color from HEX or 32bit DEC
byte tmpCol[4];
pos = req.indexOf(F("CL=")); pos = req.indexOf(F("CL="));
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str()); colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str());
selseg.setColor(0, RGBW32(col[0], col[1], col[2], col[3]), selectedSeg); // defined above (SS= or main)
col0Changed = colorChanged = true;
} }
pos = req.indexOf(F("C2=")); pos = req.indexOf(F("C2="));
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str()); colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str());
selseg.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]), selectedSeg); // defined above (SS= or main)
col1Changed = colorChanged = true;
} }
pos = req.indexOf(F("C3=")); pos = req.indexOf(F("C3="));
if (pos > 0) { if (pos > 0) {
byte t[4]; colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str()); selseg.setColor(2, RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]), selectedSeg); // defined above (SS= or main)
if (selectedSeg != strip.getMainSegmentId()) { col2Changed = colorChanged = true;
strip.applyToAllSelected = true;
strip.setColor(2, t[0], t[1], t[2], t[3]);
} else {
selseg.setColor(2, RGBW32(t[0], t[1], t[2], t[3]), selectedSeg); // defined above (SS=)
}
} }
//set to random hue SR=0->1st SR=1->2nd //set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf(F("SR")); pos = req.indexOf(F("SR"));
if (pos > 0) { if (pos > 0) {
_setRandomColor(getNumVal(&req, pos)); byte sec = getNumVal(&req, pos);
_setRandomColor(sec);
if (sec>0) col1Changed = true;
else col0Changed = true;
colorChanged = true;
} }
//swap 2nd & 1st //swap 2nd & 1st
pos = req.indexOf(F("SC")); pos = req.indexOf(F("SC"));
if (pos > 0) { if (pos > 0) {
byte temp; byte temp;
for (uint8_t i=0; i<4; i++) for (uint8_t i=0; i<4; i++) {
{ temp = col[i];
temp = col[i]; col[i] = colSec[i];
col[i] = colSec[i];
colSec[i] = temp; colSec[i] = temp;
} }
col0Changed = col1Changed = colorChanged = true;
} }
//set effect parameters //set effect parameters
@ -787,6 +802,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
updateVal(&req, "SX=", &effectSpeed); updateVal(&req, "SX=", &effectSpeed);
updateVal(&req, "IX=", &effectIntensity); updateVal(&req, "IX=", &effectIntensity);
updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1); updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1);
strip.setMode(selectedSeg, effectCurrent);
selseg.speed = effectSpeed;
selseg.intensity = effectIntensity;
selseg.palette = effectPalette;
if (effectCurrent != prevEffect || effectSpeed != prevSpeed || effectIntensity != prevIntensity || effectPalette != prevPalette) effectChanged = true;
//set advanced overlay //set advanced overlay
pos = req.indexOf(F("OL=")); pos = req.indexOf(F("OL="));
@ -917,45 +937,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//you can add more if you need //you can add more if you need
//apply to all selected manually to prevent #1618. Temporary //apply to all selected manually to prevent #1618. Temporary
bool col0Changed = false, col1Changed = false; if (strip.applyToAllSelected) {
for (uint8_t i = 0; i < 4; i++) { for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
if (col[i] != prevCol[i]) col0Changed = true; WS2812FX::Segment& seg = strip.getSegment(i);
if (colSec[i] != prevColSec[i]) col1Changed = true; if (!seg.isActive() || !seg.isSelected() || i == selectedSeg) continue;
} if (effectCurrent != prevEffect) strip.setMode(i, effectCurrent);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) if (effectSpeed != prevSpeed) seg.speed = effectSpeed;
{ if (effectIntensity != prevIntensity) seg.intensity = effectIntensity;
WS2812FX::Segment& seg = strip.getSegment(i); if (effectPalette != prevPalette) seg.palette = effectPalette;
if (!seg.isSelected()) continue; if (col0Changed) seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
if (effectCurrent != prevEffect) { if (col1Changed) seg.colors[1] = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
strip.setMode(i, effectCurrent); if (col2Changed) seg.colors[2] = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
effectChanged = true;
}
if (effectSpeed != prevSpeed) {
seg.speed = effectSpeed;
effectChanged = true;
}
if (effectIntensity != prevIntensity) {
seg.intensity = effectIntensity;
effectChanged = true;
}
if (effectPalette != prevPalette) {
seg.palette = effectPalette;
effectChanged = true;
}
}
if (col0Changed) {
if (selectedSeg == strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(0, colorFromRgbw(col));
}
}
if (col1Changed) {
if (selectedSeg == strip.getMainSegmentId()) {
strip.applyToAllSelected = true;
strip.setColor(1, colorFromRgbw(colSec));
} }
} }
strip.applyToAllSelected = false;
setValuesFromMainSeg();
//end of temporary fix code //end of temporary fix code
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
@ -964,8 +960,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("IN")); pos = req.indexOf(F("IN"));
if (pos < 1) XML_response(request); if (pos < 1) XML_response(request);
strip.applyToAllSelected = false;
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE); colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);

View File

@ -4,7 +4,9 @@
* UDP sync notifier / Realtime / Hyperion / TPM2.NET * UDP sync notifier / Realtime / Hyperion / TPM2.NET
*/ */
#define WLEDPACKETSIZE 39 #define UDP_SEG_SIZE 28
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
#define UDP_IN_MAXSIZE 1472 #define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times #define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
@ -17,7 +19,7 @@ void notify(byte callMode, bool followUp)
case CALL_MODE_INIT: return; case CALL_MODE_INIT: return;
case CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break; case CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break;
case CALL_MODE_BUTTON: if (!notifyButton) return; break; case CALL_MODE_BUTTON: if (!notifyButton) return; break;
case CALL_MODE_BUTTON_PRESET: if (!notifyButton) return; break; case CALL_MODE_BUTTON_PRESET: if (!notifyButton) return; break;
case CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break; case CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break;
case CALL_MODE_HUE: if (!notifyHue) return; break; case CALL_MODE_HUE: if (!notifyHue) return; break;
case CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break; case CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break;
@ -26,7 +28,7 @@ void notify(byte callMode, bool followUp)
default: return; default: return;
} }
byte udpOut[WLEDPACKETSIZE]; byte udpOut[WLEDPACKETSIZE];
WS2812FX::Segment& mainseg = strip.getSegment(strip.getMainSegmentId()); WS2812FX::Segment& mainseg = strip.getSegment(strip.getMainSegmentId());
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
udpOut[1] = callMode; udpOut[1] = callMode;
udpOut[2] = bri; udpOut[2] = bri;
@ -42,8 +44,8 @@ void notify(byte callMode, bool followUp)
//0: old 1: supports white 2: supports secondary color //0: old 1: supports white 2: supports secondary color
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette //3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet //6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet //9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet 11: per segment options, variable packet length (40+MAX_NUM_SEGMENTS*3)
udpOut[11] = 10; udpOut[11] = 11;
udpOut[12] = colSec[0]; udpOut[12] = colSec[0];
udpOut[13] = colSec[1]; udpOut[13] = colSec[1];
udpOut[14] = colSec[2]; udpOut[14] = colSec[2];
@ -79,12 +81,50 @@ void notify(byte callMode, bool followUp)
//sync groups //sync groups
udpOut[36] = syncGroups; udpOut[36] = syncGroups;
//Might be changed to Kelvin in the future, receiver code should handle that case //Might be changed to Kelvin in the future, receiver code should handle that case
//0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB //0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
udpOut[38] = mainseg.cct; udpOut[38] = mainseg.cct;
udpOut[39] = strip.getMaxSegments();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i);
uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[0 +ofs] = i;
udpOut[1 +ofs] = selseg.start >> 8;
udpOut[2 +ofs] = selseg.start & 0xFF;
udpOut[3 +ofs] = selseg.stop >> 8;
udpOut[4 +ofs] = selseg.stop & 0xFF;
udpOut[5 +ofs] = selseg.grouping;
udpOut[6 +ofs] = selseg.spacing;
udpOut[7 +ofs] = selseg.offset >> 8;
udpOut[8 +ofs] = selseg.offset & 0xFF;
udpOut[9 +ofs] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed
udpOut[10+ofs] = selseg.opacity;
udpOut[11+ofs] = selseg.mode;
udpOut[12+ofs] = selseg.speed;
udpOut[13+ofs] = selseg.intensity;
udpOut[14+ofs] = selseg.palette;
udpOut[15+ofs] = R(selseg.colors[0]);
udpOut[16+ofs] = G(selseg.colors[0]);
udpOut[17+ofs] = B(selseg.colors[0]);
udpOut[18+ofs] = W(selseg.colors[0]);
udpOut[19+ofs] = R(selseg.colors[1]);
udpOut[20+ofs] = G(selseg.colors[1]);
udpOut[21+ofs] = B(selseg.colors[1]);
udpOut[22+ofs] = W(selseg.colors[1]);
udpOut[23+ofs] = R(selseg.colors[2]);
udpOut[24+ofs] = G(selseg.colors[2]);
udpOut[25+ofs] = B(selseg.colors[2]);
udpOut[26+ofs] = W(selseg.colors[2]);
udpOut[27+ofs] = selseg.cct;
}
//uint16_t offs = SEG_OFFSET;
//next value to be added has index: udpOut[offs + 0]
IPAddress broadcastIp; IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@ -247,47 +287,78 @@ void handleNotifications()
} else if (!(receiveGroups & udpIn[36])) return; } else if (!(receiveGroups & udpIn[36])) return;
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification
if (receiveNotificationColor || !someSel) //apply colors from notification to main segment, only if not syncing full segments
{ if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
col[0] = udpIn[3]; col[0] = udpIn[3];
col[1] = udpIn[4]; col[1] = udpIn[4];
col[2] = udpIn[5]; col[2] = udpIn[5];
if (version > 0) //sending module's white val is intended if (version > 0) //sending module's white val is intended
{ {
col[3] = udpIn[10]; col[3] = udpIn[10];
if (version > 1) if (version > 1) {
{
colSec[0] = udpIn[12]; colSec[0] = udpIn[12];
colSec[1] = udpIn[13]; colSec[1] = udpIn[13];
colSec[2] = udpIn[14]; colSec[2] = udpIn[14];
colSec[3] = udpIn[15]; colSec[3] = udpIn[15];
} }
if (version > 6) if (version > 6) {
{ strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); //tertiary color
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
uint8_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
}
uint8_t segid = strip.getMainSegmentId();
strip.getSegment(segid).setCCT(cct, segid);
}
} }
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
uint8_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
}
uint8_t segid = strip.getMainSegmentId();
strip.getSegment(segid).setCCT(cct, segid);
}
} }
} }
bool timebaseUpdated = false; bool timebaseUpdated = false;
//apply effects from notification //apply effects from notification
if (version < 200 && (receiveNotificationEffects || !someSel)) bool applyEffects = (receiveNotificationEffects || !someSel);
if (version < 200)
{ {
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8]; if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
effectSpeed = udpIn[9]; if (version > 10 && receiveSegmentOptions) {
if (version > 2) effectIntensity = udpIn[16]; //does not sync start & stop
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19]; uint8_t srcSegs = udpIn[39];
if (version > 5) //if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments();
{ for (uint8_t i = 0; i < srcSegs; i++) {
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
uint8_t id = udpIn[0 +ofs];
if (id > strip.getMaxSegments()) continue;
WS2812FX::Segment& selseg = strip.getSegment(id);
//bytes 1+2 contain start, 3+4 stop, unused at this time
for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed
selseg.setOpacity(udpIn[10+ofs], id);
if (applyEffects) {
strip.setMode(id, udpIn[11+ofs]);
selseg.speed = udpIn[12+ofs];
selseg.intensity = udpIn[13+ofs];
selseg.palette = udpIn[14+ofs];
}
if (receiveNotificationColor || !someSel) {
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]), id);
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]), id);
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]), id);
selseg.setCCT(udpIn[27+ofs], id);
}
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], (udpIn[7+ofs]<<8 | udpIn[8+ofs])); //also properly resets segments
}
setValuesFromMainSeg();
effectChanged = true;
colorChanged = true;
} else if (applyEffects) { //simple effect sync, applies to all selected
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
effectSpeed = udpIn[9];
if (version > 2) effectIntensity = udpIn[16];
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
}
if (applyEffects && version > 5) {
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]); uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis(); t -= millis();

View File

@ -16,6 +16,7 @@
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit. // ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS). // ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
// 2-step OTA may still be possible: https://github.com/Aircoookie/WLED/issues/2040#issuecomment-981111096
// Uncomment some of the following lines to disable features: // Uncomment some of the following lines to disable features:
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini // Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini
@ -313,6 +314,7 @@ WLED_GLOBAL uint8_t receiveGroups _INIT(0x01); // sync receiv
WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications
WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color
WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup
WLED_GLOBAL bool receiveSegmentOptions _INIT(false); // apply segment options
WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API
WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote
WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa
@ -424,9 +426,6 @@ WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false);
// color // color
WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs
WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 }));
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
// transitions // transitions
@ -477,6 +476,7 @@ WLED_GLOBAL byte effectSpeed _INIT(128);
WLED_GLOBAL byte effectIntensity _INIT(128); WLED_GLOBAL byte effectIntensity _INIT(128);
WLED_GLOBAL byte effectPalette _INIT(0); WLED_GLOBAL byte effectPalette _INIT(0);
WLED_GLOBAL bool effectChanged _INIT(false); WLED_GLOBAL bool effectChanged _INIT(false);
WLED_GLOBAL bool colorChanged _INIT(false);
// network // network
WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false); WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);

View File

@ -466,6 +466,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("RB"),receiveNotificationBrightness); sappend('c',SET_F("RB"),receiveNotificationBrightness);
sappend('c',SET_F("RC"),receiveNotificationColor); sappend('c',SET_F("RC"),receiveNotificationColor);
sappend('c',SET_F("RX"),receiveNotificationEffects); sappend('c',SET_F("RX"),receiveNotificationEffects);
sappend('c',SET_F("SO"),receiveSegmentOptions);
sappend('c',SET_F("SD"),notifyDirectDefault); sappend('c',SET_F("SD"),notifyDirectDefault);
sappend('c',SET_F("SB"),notifyButton); sappend('c',SET_F("SB"),notifyButton);
sappend('c',SET_F("SH"),notifyHue); sappend('c',SET_F("SH"),notifyHue);