Backup/restore presets & holiday array upload.

Reduced number of segments on 8266.
pull/2737/head
Blaz Kristan 2021-07-14 23:10:19 +02:00
rodzic b10568e917
commit c1a1fb8d87
7 zmienionych plików z 2309 dodań i 2262 usunięć

Wyświetl plik

@ -53,7 +53,7 @@
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
#define MAX_NUM_SEGMENTS 24
#define MAX_NUM_SEGMENTS 18
/* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */

Wyświetl plik

@ -28,6 +28,13 @@ var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, labels:true, pcmbot:false, pid:true, seglen:false}
};
var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
[0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day
[2022,3,17,2,"https://aircoookie.github.io/easter.png"],
[2023,3,9,2,"https://aircoookie.github.io/easter.png"],
[2024,2,31,2,"https://aircoookie.github.io/easter.png"]
];
var cpick = new iro.ColorPicker("#picker", {
width: 260,
@ -151,33 +158,12 @@ function loadBg(iUrl)
img.src = iUrl;
if (iUrl == "") {
var today = new Date();
var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
[0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day
[2022,3,17,2,"https://aircoookie.github.io/easter.png"],
[2023,3,9,2,"https://aircoookie.github.io/easter.png"],
[2024,2,31,2,"https://aircoookie.github.io/easter.png"]
];
fetch("./holidays.json", {
method: 'get'
})
.then(res => {
//if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
if (Array.isArray(json)) hol = json;
//TODO: do some parsing first
})
.catch(function (error) {
console.log("holidays.json does not contain array of holidays.");
});
for (var i=0; i<hol.length; i++) {
var yr = hol[i][0]==0 ? today.getFullYear() : hol[i][0];
var hs = new Date(yr,hol[i][1],hol[i][2]);
var he = new Date(hs);
he.setDate(he.getDate() + hol[i][3]);
if (today>=hs && today<=he) img.src = hol[i][4];
if (today>=hs && today<he) img.src = hol[i][4];
}
}
img.addEventListener('load', (event) => {
@ -198,7 +184,7 @@ function loadSkinCSS(cId)
l.id = cId;
l.rel = 'stylesheet';
l.type = 'text/css';
l.href = '/skin.css';
l.href = (loc?`http://${locip}`:'.') + '/skin.css';
l.media = 'all';
h.appendChild(l);
}
@ -221,7 +207,25 @@ function onLoad()
resetPUtil();
applyCfg();
loadBg(cfg.theme.bg.url);
if (cfg.theme.bg.url=="") {
fetch((loc?`http://${locip}`:'.') + "/holidays.json", {
method: 'get'
})
.then(res => {
//if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
if (Array.isArray(json)) hol = json;
//TODO: do some parsing first
loadBg(cfg.theme.bg.url);
})
.catch(function (error) {
loadBg(cfg.theme.bg.url);
console.log("holidays.json does not contain array of holidays. Defaults loaded.");
});
} else
loadBg(cfg.theme.bg.url);
loadSkinCSS('skinCss');
var cd = gId('csl').children;

Wyświetl plik

@ -5,6 +5,7 @@
<meta charset="utf-8">
<title>Misc Settings</title>
<script>
var d = document;
function H()
{
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings");
@ -17,6 +18,34 @@
{
window.open("/update","_self");
}
function gId(s)
{
return d.getElementById(s);
}
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
var timeout;
function showToast(text, error = false)
{
var x = gId("toast");
x.innerHTML = text;
x.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
}
function uploadFile(name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", d.Sf.data.files[0], name);
req.send(formData);
d.Sf.data.value = '';
return false;
}
function GetV()
{
//values injected by server while sending HTML
@ -44,6 +73,10 @@
Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><br>
HTTP traffic is unencrypted. An attacker in the same network can intercept form data!
<hr>
<a class="btn" href="/presets.json" target="_blank">Backup presets</a><br>
<div id="presets">Restore presets:<input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile('/presets.json');"><br></div>
<hr>
<h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
@ -54,6 +87,7 @@
(c) 2016-2021 Christian Schwinne <br>
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
Server message: <span class="sip"> Response error! </span><hr>
<div id="toast"></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
</form>
</body>

Wyświetl plik

@ -189,15 +189,15 @@
gId("theme_bg_random").checked = false;
}
}
function uploadFile(name) {
function uploadFile(fO,name) {
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", d.Sf.data.files[0], name);
formData.append("data", fO.files[0], name);
req.send(formData);
d.Sf.data.value = '';
fO.value = '';
return false;
}
function GetV(){var d=document;}
@ -235,7 +235,8 @@
<span class="l">BG image URL</span>: <input id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br>
<span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br>
<input id="theme_base" class="agi" style="display:none">
<div id="skin">Skinning CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile('/skin.css');"><br></div>
<div id="skin">Skinning CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/skin.css');"><br></div>
<div id="holidays">Holidays: <input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/holidays.json');"><br></div>
<div id="toast"></div>
<hr><button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button>
</form>

Wyświetl plik

@ -9,7 +9,7 @@ body {
hr {
border-color: #666;
}
button {
button, .btn {
background: #333;
color: #fff;
font-family: Verdana, sans-serif;
@ -21,6 +21,7 @@ button {
padding: 8px 12px;
min-width: 48px;
cursor: pointer;
text-decoration: none;
}
.toprow {
top: 0;

Wyświetl plik

@ -6,7 +6,7 @@
*/
// Autogenerated from wled00/data/style.css, do not edit!!
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;border-radius:24px;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:8px 12px;min-width:48px;cursor:pointer}.toprow{top:0;position:sticky;background-color:#222;z-index:1}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=text]{font-size:medium}input[type=number]{width:4em;font-size:medium;margin:2px}input[type=number].xxl{width:100px}input[type=number].xl{width:85px}input[type=number].l{width:63px}input[type=number].m{width:56px}input[type=number].s{width:49px}input[type=number].xs{width:42px}select{margin:2px;font-size:medium}input[type=checkbox]{transform:scale(2);margin-right:10px}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}#toast{opacity:0;background-color:#444;border-radius:5px;bottom:64px;color:#fff;font-size:17px;padding:16px;pointer-events:none;position:fixed;text-align:center;z-index:5;transform:translateX(-50%%);max-width:90%%;left:50%%}#toast.show{opacity:1;background-color:#264;animation:fadein .5s,fadein .5s 2.5s reverse}#toast.error{opacity:1;background-color:#b21;animation:fadein .5s}</style>)=====";
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}hr{border-color:#666}.btn,button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;border-radius:24px;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:8px 12px;min-width:48px;cursor:pointer;text-decoration:none}.toprow{top:0;position:sticky;background-color:#222;z-index:1}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=text]{font-size:medium}input[type=number]{width:4em;font-size:medium;margin:2px}input[type=number].xxl{width:100px}input[type=number].xl{width:85px}input[type=number].l{width:63px}input[type=number].m{width:56px}input[type=number].s{width:49px}input[type=number].xs{width:42px}select{margin:2px;font-size:medium}input[type=checkbox]{transform:scale(2);margin-right:10px}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}#toast{opacity:0;background-color:#444;border-radius:5px;bottom:64px;color:#fff;font-size:17px;padding:16px;pointer-events:none;position:fixed;text-align:center;z-index:5;transform:translateX(-50%%);max-width:90%%;left:50%%}#toast.show{opacity:1;background-color:#264;animation:fadein .5s,fadein .5s 2.5s reverse}#toast.error{opacity:1;background-color:#b21;animation:fadein .5s}</style>)=====";
// Autogenerated from wled00/data/settings.htm, do not edit!!
@ -192,7 +192,7 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
// Autogenerated from wled00/data/settings_ui.htm, do not edit!!
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta
name="viewport" content="width=500"><title>UI Settings</title><script>
var initial_ds,initial_st,timeout,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs",seglen:"Use LED Count instead of Stop LED on segments"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,o=e.split("_"),s=o.length,a=0;a<s-1;a++){var l=o[a];n[l]||(n[l]={}),n=n[l]}n[o[s-1]]=i}function showToast(e,t=!1){var i=gId("toast");i.innerHTML=e,i.className=t?"error":"show",clearTimeout(timeout),i.style.animation="none",timeout=setTimeout((function(){i.className=i.className.replace("show","")}),2900)}function addRec(e,t="",n=null){var o="";for(i in e){var s=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(o+=`<h3>${n[i].LABEL}</h3>`),o+=addRec(e[i],s,n?n[i]:null);else{var a=s;if(n&&n[i]?a=n[i]:e[i+"LABEL"]&&(a=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var l=typeof e[i];gId(s)?("boolean"===l?gId(s).checked=e[i]:gId(s).value=e[i],gId(s).previousElementSibling.matches(".l")&&(gId(s).previousElementSibling.innerHTML=a)):"boolean"===l?o+=`${a}: <input class="agi cb" type="checkbox" id=${s} ${e[i]?"checked":""}><br>`:"number"===l?o+=`${a}: <input class="agi" type="number" id=${s} value=${e[i]}><br>`:"string"===l&&(o+=`${a}:<br><input class="agi" id=${s} value=${e[i]}><br>`)}}return o}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function uploadFile(e){var t=new XMLHttpRequest;t.addEventListener("load",(function(){showToast(this.responseText)})),t.addEventListener("error",(function(e){showToast(e.stack,!0)})),t.open("POST","/upload");var i=new FormData;return i.append("data",d.Sf.data.files[0],e),t.send(i),d.Sf.data.value="",!1}function GetV() {var d=document;
var initial_ds,initial_st,timeout,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs",seglen:"Use LED Count instead of Stop LED on segments"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,o=e.split("_"),s=o.length,a=0;a<s-1;a++){var l=o[a];n[l]||(n[l]={}),n=n[l]}n[o[s-1]]=i}function showToast(e,t=!1){var i=gId("toast");i.innerHTML=e,i.className=t?"error":"show",clearTimeout(timeout),i.style.animation="none",timeout=setTimeout((function(){i.className=i.className.replace("show","")}),2900)}function addRec(e,t="",n=null){var o="";for(i in e){var s=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(o+=`<h3>${n[i].LABEL}</h3>`),o+=addRec(e[i],s,n?n[i]:null);else{var a=s;if(n&&n[i]?a=n[i]:e[i+"LABEL"]&&(a=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var l=typeof e[i];gId(s)?("boolean"===l?gId(s).checked=e[i]:gId(s).value=e[i],gId(s).previousElementSibling.matches(".l")&&(gId(s).previousElementSibling.innerHTML=a)):"boolean"===l?o+=`${a}: <input class="agi cb" type="checkbox" id=${s} ${e[i]?"checked":""}><br>`:"number"===l?o+=`${a}: <input class="agi" type="number" id=${s} value=${e[i]}><br>`:"string"===l&&(o+=`${a}:<br><input class="agi" id=${s} value=${e[i]}><br>`)}}return o}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function uploadFile(e,t){var i=new XMLHttpRequest;i.addEventListener("load",(function(){showToast(this.responseText)})),i.addEventListener("error",(function(e){showToast(e.stack,!0)})),i.open("POST","/upload");var n=new FormData;return n.append("data",e.files[0],t),i.send(n),e.value="",!1}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css"
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf"
method="post"><div class="toprow"><div class="helpB"><button type="button"
@ -225,9 +225,11 @@ class="agi" oninput="checkRandomBg()"><br><span class="l">Random BG image</span>
onchange="setRandomBg()"><br><input id="theme_base" class="agi"
style="display:none"><div id="skin">Skinning CSS: <input type="file"
name="data" accept=".css"> <input type="button" value="Upload"
onclick='uploadFile("/skin.css")'><br></div><div id="toast"></div><hr><button
type="button" onclick="B()">Back</button><button type="button" onclick="Save()">
Save</button></form></body></html>)=====";
onclick='uploadFile(d.Sf.data,"/skin.css")'><br></div><div id="holidays">
Holidays: <input type="file" name="data2" accept=".json"> <input type="button"
value="Upload" onclick='uploadFile(d.Sf.data2,"/holidays.json")'><br></div><div
id="toast"></div><hr><button type="button" onclick="B()">Back</button><button
type="button" onclick="Save()">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_sync.htm, do not edit!!
@ -375,7 +377,7 @@ type="button" onclick="B()">Back</button><button type="submit">Save</button>
// Autogenerated from wled00/data/settings_sec.htm, do not edit!!
const char PAGE_settings_sec[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>Misc Settings</title><script>
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings")}function B(){window.open("/settings","_self")}function U(){window.open("/update","_self")}function GetV() {var d=document;
var timeout,d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings")}function B(){window.open("/settings","_self")}function U(){window.open("/update","_self")}function gId(t){return d.getElementById(t)}function isObject(t){return t&&"object"==typeof t&&!Array.isArray(t)}function showToast(t,e=!1){var n=gId("toast");n.innerHTML=t,n.className=e?"error":"show",clearTimeout(timeout),n.style.animation="none",timeout=setTimeout((function(){n.className=n.className.replace("show","")}),2900)}function uploadFile(t){var e=new XMLHttpRequest;e.addEventListener("load",(function(){showToast(this.responseText)})),e.addEventListener("error",(function(t){showToast(t.stack,!0)})),e.open("POST","/upload");var n=new FormData;return n.append("data",d.Sf.data.files[0],t),e.send(n),d.Sf.data.value="",!1}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css"
rel="stylesheet"></head><body onload="GetV()"><form id="form_s" name="Sf"
method="post"><div class="toprow"><div class="helpB"><button type="button"
@ -392,18 +394,21 @@ Disable OTA when not in use, otherwise an attacker can reflash device software!
<br><br>Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><br>
HTTP traffic is unencrypted. An attacker in the same network can intercept form data!
<h3>Software Update</h3><button type="button" onclick="U()">Manual OTA Update
</button><br>Enable ArduinoOTA: <input type="checkbox" name="AO"><br><h3>About
</h3><a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a>
version 0.13.0-bl2<br><br><a
<hr><a class="btn" href="/presets.json" target="_blank">Backup presets</a><br>
<div id="presets">Restore presets:<input type="file" name="data" accept=".json">
<input type="button" value="Upload" onclick='uploadFile("/presets.json")'><br>
</div><hr><h3>Software Update</h3><button type="button" onclick="U()">
Manual OTA Update</button><br>Enable ArduinoOTA: <input type="checkbox"
name="AO"><br><h3>About</h3><a href="https://github.com/Aircoookie/WLED/"
target="_blank">WLED</a> version 0.13.0-bl2<br><br><a
href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits"
target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2021 Christian Schwinne<br><i>Licensed under the <a
href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">
MIT license</a></i><br><br>Server message: <span class="sip">Response error!
</span><hr><button type="button" onclick="B()">Back</button><button
type="submit">Save & Reboot</button></form></body></html>)=====";
</span><hr><div id="toast"></div><button type="button" onclick="B()">Back
</button><button type="submit">Save & Reboot</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_um.htm, do not edit!!

Plik diff jest za duży Load Diff