Merge branch 'master' into dev

This commit is contained in:
Blaz Kristan 2021-08-19 21:57:04 +02:00
commit 4e28e2cb59
21 changed files with 1029 additions and 1019 deletions

View File

@ -2,6 +2,30 @@
### Builds after release 0.12.0 ### Builds after release 0.12.0
#### Build 2108180
- Fixed JSON IR remote not working with codes greater than 0xFFFFFF (fixes #2135)
- Fixed transition 0 edge case
#### Build 2108170
- Added application level pong websockets reply (#2139)
- Use AsyncTCP 1.0.3 as it mitigates the flickering issue from 0.13.0-b2
- Fixed transition manually updated in preset overriden by field value
#### Build 2108050
- Fixed undesirable color transition from Orange to boot preset color on first boot
- Removed misleading Delete button on new playlist with one entry
- Updated NeoPixelBus to 2.6.7 and AsyncTCP to 1.1.1
#### Build 2107230
- Added skinning (extra custom CSS) (PR #2084)
- Added presets/config backup/restore (PR #2084)
- Added option for using length instead of Stop LED in UI (PR #2048)
- Added custom `holidays.json` holiday list (PR #2048)
#### Build 2107100 #### Build 2107100
- Version bump to 0.13.0-b2 "Toki" - Version bump to 0.13.0-b2 "Toki"

View File

@ -196,7 +196,7 @@ lib_deps =
${env.lib_deps} ${env.lib_deps}
# ESPAsyncTCP @ 1.2.0 # ESPAsyncTCP @ 1.2.0
ESPAsyncUDP ESPAsyncUDP
makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 do not compile on ESP core < 3.0.0 makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 and newer do not compile on ESP core < 3.0.0
[esp32] [esp32]
build_flags = -g build_flags = -g

View File

@ -12,9 +12,9 @@ board = esp01_1m
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
lib_deps = ${esp8266.lib_deps}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
; ********************************************************************* ; *********************************************************************
; *** Use custom settings from file my_config.h ; *** Use custom settings from file my_config.h
-DWLED_USE_MY_CONFIG -DWLED_USE_MY_CONFIG

View File

@ -9,6 +9,7 @@
<a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a> <a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
</p> </p>
# Welcome to my project WLED! ✨ # Welcome to my project WLED! ✨
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102! A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!

View File

@ -191,7 +191,6 @@
<button class="tablinks" onclick="openTab(1)"><i class="icons">&#xe23d;</i><p class="tab-label">Effects</p></button> <button class="tablinks" onclick="openTab(1)"><i class="icons">&#xe23d;</i><p class="tab-label">Effects</p></button>
<button class="tablinks" onclick="openTab(2)"><i class="icons">&#xe34b;</i><p class="tab-label">Segments</p></button> <button class="tablinks" onclick="openTab(2)"><i class="icons">&#xe34b;</i><p class="tab-label">Segments</p></button>
<button class="tablinks" onclick="openTab(3)"><i class="icons">&#xe04c;</i><p class="tab-label">Presets</p></button> <button class="tablinks" onclick="openTab(3)"><i class="icons">&#xe04c;</i><p class="tab-label">Presets</p></button>
<!--button class="tablinks" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button-->
</div> </div>
<div id="connind"></div> <div id="connind"></div>

View File

@ -161,6 +161,16 @@ function loadBg(iUrl)
let bg = document.getElementById('bg'); let bg = document.getElementById('bg');
let img = document.createElement("img"); let img = document.createElement("img");
img.src = iUrl; img.src = iUrl;
if (iUrl == "" || iUrl==="https://picsum.photos/1920/1080") {
var today = new Date();
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];
}
}
img.addEventListener('load', (event) => { img.addEventListener('load', (event) => {
var a = parseFloat(cfg.theme.alpha.bg); var a = parseFloat(cfg.theme.alpha.bg);
if (isNaN(a)) a = 0.6; if (isNaN(a)) a = 0.6;
@ -170,19 +180,6 @@ function loadBg(iUrl)
}); });
} }
function getHdayBg()
{
var today = new Date();
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) return hol[i][4];
}
return "";
}
function loadSkinCSS(cId) function loadSkinCSS(cId)
{ {
if (!gId(cId)) // check if element exists if (!gId(cId)) // check if element exists
@ -215,13 +212,12 @@ function onLoad()
resetPUtil(); resetPUtil();
applyCfg(); applyCfg();
var iUrl = cfg.theme.bg.url; if (cfg.comp.hdays) { //load custom holiday list
if (iUrl==="" || iUrl==="https://picsum.photos/1920/1080") { fetch((loc?`http://${locip}`:'.') + "/holidays.json", { // may be loaded from external source
if (cfg.comp.hdays) {
fetch((loc?`http://${locip}`:'.') + "/holidays.json", {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
//if (!res.ok) showErrorToast();
return res.json(); return res.json();
}) })
.then(json => { .then(json => {
@ -231,16 +227,11 @@ function onLoad()
.catch(function (error) { .catch(function (error) {
console.log("holidays.json does not contain array of holidays. Defaults loaded."); console.log("holidays.json does not contain array of holidays. Defaults loaded.");
}) })
.finally(()=>{ .finally(function(){
iUrl = getHdayBg(); loadBg(cfg.theme.bg.url);
if (iUrl!=="") loadBg(iUrl);
}); });
} else {
iUrl = getHdayBg();
if (iUrl!=="") loadBg(iUrl);
}
} else } else
loadBg(iUrl); loadBg(cfg.theme.bg.url);
if (cfg.comp.css) loadSkinCSS('skinCss'); if (cfg.comp.css) loadSkinCSS('skinCss');
var cd = gId('csl').children; var cd = gId('csl').children;
@ -843,18 +834,17 @@ function populatePalettes()
palettes.unshift({ palettes.unshift({
"id": 0, "id": 0,
"name": "Default", "name": "Default"
}); });
var html = ""; var html = "";
for (let i = 0; i < palettes.length; i++) { for (let i = 0; i < palettes.length; i++) {
let previewCss = genPalPrevCss(palettes[i].id);
html += generateListItemHtml( html += generateListItemHtml(
'palette', 'palette',
palettes[i].id, palettes[i].id,
palettes[i].name, palettes[i].name,
'setPalette', 'setPalette',
`<div class="lstIprev" style="${previewCss}"></div>` `<div class="lstIprev" style="${genPalPrevCss(palettes[i].id)}"></div>`
); );
} }
@ -877,7 +867,6 @@ function genPalPrevCss(id)
if (!palettesData) return; if (!palettesData) return;
var paletteData = palettesData[id]; var paletteData = palettesData[id];
var previewCss = "";
if (!paletteData) return 'display: none'; if (!paletteData) return 'display: none';
@ -1389,7 +1378,6 @@ function refreshPlE(p) {
content += `<div class="hrz"></div>`; content += `<div class="hrz"></div>`;
plEDiv.innerHTML = content; plEDiv.innerHTML = content;
var dels = plEDiv.getElementsByClassName("btn-pl-del"); var dels = plEDiv.getElementsByClassName("btn-pl-del");
// if (dels.length < 2 && p > 0) dels[0].style.display = "none";
if (dels.length < 2) dels[0].style.display = "none"; if (dels.length < 2) dels[0].style.display = "none";
var sels = gId(`seg${p+100}`).getElementsByClassName("sel"); var sels = gId(`seg${p+100}`).getElementsByClassName("sel");
@ -1410,7 +1398,6 @@ function addPl(p,i) {
} }
function delPl(p,i) { function delPl(p,i) {
// if (plJson[p].ps.length < 2) {if (p == 0) resetPUtil(); return;}
if (plJson[p].ps.length < 2) return; if (plJson[p].ps.length < 2) return;
plJson[p].ps.splice(i,1); plJson[p].ps.splice(i,1);
plJson[p].dur.splice(i,1); plJson[p].dur.splice(i,1);

View File

@ -32,7 +32,6 @@ function S(){GCH(15);GetV();mMap();}function H(){window.open("https://github.com
function GetV(){} function GetV(){}
</script> </script>
<style>@import url("/style.css");</style> <style>@import url("/style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">

View File

@ -45,11 +45,6 @@
req.send(formData); req.send(formData);
fO.value = ''; fO.value = '';
return false; return false;
}
function clearLS()
{
localStorage.removeItem('wledP');
localStorage.removeItem('wledPmt');
} }
function GetV() function GetV()
{ {
@ -78,19 +73,18 @@
Factory reset: <input type="checkbox" name="RS"><br> Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><br> All settings and presets will be erased.<br><br>
HTTP traffic is unencrypted. An attacker in the same network can intercept form data! HTTP traffic is unencrypted. An attacker in the same network can intercept form data!
<h3>Backup & Restore</h3>
<a class="btn lnk" href="/presets.json?download" target="download-frame">Backup presets</a><br>
<div id="presets">Restore presets:<input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/presets.json');clearLS();"><br></div>
<a class="btn lnk" href="/cfg.json?download" target="download-frame">Backup configuration</a><br>
<div id="presets">Restore configuration:<input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/cfg.json');"><br></div>
<div style="color: #fa0;">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect configuration may require factory reset or re-flashing of ESP.</div>
For security reasons, passwords are not backed up.
<hr> <hr>
<h3>Software Update</h3> <h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br> <button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO"><br> Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
<hr> <h3>Backup & Restore</h3>
<a class="btn lnk" href="/presets.json?download" target="download-frame">Backup presets</a><br>
<div>Restore presets<br><input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/presets.json');"><br></div><br>
<a class="btn lnk" href="/cfg.json?download" target="download-frame">Backup configuration</a><br>
<div>Restore configuration<br><input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/cfg.json');"><br></div>
<div style="color: #fa0;">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up.
<h3>About</h3> <h3>About</h3>
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br> <a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br> <a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>

View File

@ -10,7 +10,6 @@ function S(){GetV();SetVal();}
function GetV(){var d=document;} function GetV(){var d=document;}
</script> </script>
<style>@import url("/style.css");</style> <style>@import url("/style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">

View File

@ -98,7 +98,6 @@
} }
</script> </script>
<style>@import url("/style.css");</style> <style>@import url("/style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="Wd()"> <form id="form_s" name="Sf" method="post" onsubmit="Wd()">

View File

@ -206,7 +206,6 @@
function GetV(){var d=document;} function GetV(){var d=document;}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">

View File

@ -134,7 +134,6 @@
function GetV() {} function GetV() {}
</script> </script>
<style>@import url("/style.css");</style> <style>@import url("/style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head> </head>
<body onload="S()"> <body onload="S()">

View File

@ -19,7 +19,6 @@
} }
</script> </script>
<style>@import url("/style.css");</style> <style>@import url("/style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head> </head>
<body onload="GetV()"> <body onload="GetV()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">

View File

@ -31,39 +31,39 @@ action="/settings/sec"><button type="submit">Security & Updates</button></form>
const char PAGE_settings_wifi[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta const char PAGE_settings_wifi[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta
name="viewport" content="width=500"><title>WiFi Settings</title><script> name="viewport" content="width=500"><title>WiFi Settings</title><script>
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings")}function B(){window.open("/settings","_self")}function GetV() {var d=document; function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings")}function B(){window.open("/settings","_self")}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css" %CSS%%SCSS%</head><body onload="GetV()">
rel="stylesheet"></head><body onload="GetV()"><form id="form_s" name="Sf" <form id="form_s" name="Sf" method="post"><div class="toprow"><div
method="post"><div class="toprow"><div class="helpB"><button type="button" class="helpB"><button type="button" onclick="H()">?</button></div><button
onclick="H()">?</button></div><button type="button" onclick="B()">Back</button> type="button" onclick="B()">Back</button><button type="submit">Save & Connect
<button type="submit">Save & Connect</button><hr></div><h2>WiFi setup</h2><h3> </button><hr></div><h2>WiFi setup</h2><h3>Connect to existing network</h3>
Connect to existing network</h3>Network name (SSID, empty to not connect):<br> Network name (SSID, empty to not connect):<br><input name="CS" maxlength="32">
<input name="CS" maxlength="32"><br>Network password:<br><input type="password" <br>Network password:<br><input type="password" name="CP" maxlength="63"><br>
name="CP" maxlength="63"><br>Static IP (leave at 0.0.0.0 for DHCP):<br><input Static IP (leave at 0.0.0.0 for DHCP):<br><input name="I0" type="number"
name="I0" type="number" class="s" min="0" max="255" required> . <input class="s" min="0" max="255" required> . <input name="I1" type="number"
name="I1" type="number" class="s" min="0" max="255" required> . <input class="s" min="0" max="255" required> . <input name="I2" type="number"
name="I2" type="number" class="s" min="0" max="255" required> . <input class="s" min="0" max="255" required> . <input name="I3" type="number"
name="I3" type="number" class="s" min="0" max="255" required><br>Static gateway: class="s" min="0" max="255" required><br>Static gateway:<br><input name="G0"
<br><input name="G0" type="number" class="s" min="0" max="255" required> . type="number" class="s" min="0" max="255" required> . <input name="G1"
<input name="G1" type="number" class="s" min="0" max="255" required> . <input type="number" class="s" min="0" max="255" required> . <input name="G2"
name="G2" type="number" class="s" min="0" max="255" required> . <input type="number" class="s" min="0" max="255" required> . <input name="G3"
name="G3" type="number" class="s" min="0" max="255" required><br> type="number" class="s" min="0" max="255" required><br>Static subnet mask:<br>
Static subnet mask:<br><input name="S0" type="number" class="s" min="0" <input name="S0" type="number" class="s" min="0" max="255" required> . <input
max="255" required> . <input name="S1" type="number" class="s" min="0" name="S1" type="number" class="s" min="0" max="255" required> . <input
max="255" required> . <input name="S2" type="number" class="s" min="0" name="S2" type="number" class="s" min="0" max="255" required> . <input
max="255" required> . <input name="S3" type="number" class="s" min="0" name="S3" type="number" class="s" min="0" max="255" required><br>
max="255" required><br>mDNS address (leave empty for no mDNS):<br>http:// <input mDNS address (leave empty for no mDNS):<br>http:// <input name="CM"
name="CM" maxlength="32"> .local<br>Client IP: <span class="sip">Not connected maxlength="32"> .local<br>Client IP: <span class="sip">Not connected</span><br>
</span><br><h3>Configure Access Point</h3>AP SSID (leave empty for no AP):<br> <h3>Configure Access Point</h3>AP SSID (leave empty for no AP):<br><input
<input name="AS" maxlength="32"><br>Hide AP name: <input type="checkbox" name="AS" maxlength="32"><br>Hide AP name: <input type="checkbox" name="AH"><br>
name="AH"><br>AP password (leave empty for open):<br><input type="password" AP password (leave empty for open):<br><input type="password" name="AP"
name="AP" maxlength="63" pattern="(.{8,63})|()" maxlength="63" pattern="(.{8,63})|()" title="Empty or min. 8 characters"><br>
title="Empty or min. 8 characters"><br>Access Point WiFi channel: <input Access Point WiFi channel: <input name="AC" type="number" class="xs" min="1"
name="AC" type="number" class="xs" min="1" max="13" required><br>AP opens: max="13" required><br>AP opens: <select name="AB"><option value="0">
<select name="AB"><option value="0">No connection after boot</option><option No connection after boot</option><option value="1">Disconnected</option><option
value="1">Disconnected</option><option value="2">Always</option><option value="2">Always</option><option value="3">Never (not recommended)</option>
value="3">Never (not recommended)</option></select><br>AP IP: <span class="sip"> </select><br>AP IP: <span class="sip">Not active</span><br><h3>Experimental</h3>
Not active</span><br><h3>Experimental</h3>Disable WiFi sleep: <input Disable WiFi sleep: <input type="checkbox" name="WS"><br><i>
type="checkbox" name="WS"><br><i>Can help with connectivity issues.<br> Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i><div Do not enable if WiFi is working correctly, increases power consumption.</i><div
id="ethd"><h3>Ethernet Type</h3><select name="ETH"><option value="0">None id="ethd"><h3>Ethernet Type</h3><select name="ETH"><option value="0">None
</option><option value="2">ESP32-POE</option><option value="6">ESP32Deux </option><option value="2">ESP32-POE</option><option value="6">ESP32Deux
@ -162,15 +162,15 @@ Accurate</option><option value="3">Dual</option><option value="4">Legacy
const char PAGE_settings_dmx[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500"> const char PAGE_settings_dmx[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>DMX Settings</title><script> <meta charset="utf-8"><title>DMX Settings</title><script>
function GCH(n){for(d=document,d.getElementById("dmxchannels").innerHTML+="",i=0;i<n;i++)d.getElementById("dmxchannels").innerHTML+="<span id=CH"+(i+1)+"s >Channel "+(i+1)+": <select name=CH"+(i+1)+' id="CH'+(i+1)+'"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n'}function mMap(){for(d=document,numCh=document.Sf.CN.value,numGap=document.Sf.CG.value,parseInt(numCh)>parseInt(numGap)?d.getElementById("gapwarning").style.display="block":d.getElementById("gapwarning").style.display="none",i=0;i<15;i++)i>=numCh?(d.getElementById("CH"+(i+1)+"s").style.opacity="0.5",d.getElementById("CH"+(i+1)).disabled=!0):(d.getElementById("CH"+(i+1)+"s").style.opacity="1",d.getElementById("CH"+(i+1)).disabled=!1)}function S(){GCH(15),GetV(),mMap()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX")}function B(){window.history.back()}function GetV() {var d=document; function GCH(n){for(d=document,d.getElementById("dmxchannels").innerHTML+="",i=0;i<n;i++)d.getElementById("dmxchannels").innerHTML+="<span id=CH"+(i+1)+"s >Channel "+(i+1)+": <select name=CH"+(i+1)+' id="CH'+(i+1)+'"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n'}function mMap(){for(d=document,numCh=document.Sf.CN.value,numGap=document.Sf.CG.value,parseInt(numCh)>parseInt(numGap)?d.getElementById("gapwarning").style.display="block":d.getElementById("gapwarning").style.display="none",i=0;i<15;i++)i>=numCh?(d.getElementById("CH"+(i+1)+"s").style.opacity="0.5",d.getElementById("CH"+(i+1)).disabled=!0):(d.getElementById("CH"+(i+1)+"s").style.opacity="1",d.getElementById("CH"+(i+1)).disabled=!1)}function S(){GCH(15),GetV(),mMap()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX")}function B(){window.history.back()}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css" %CSS%%SCSS%</head><body onload="S()">
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf" <form id="form_s" name="Sf" method="post"><div class="toprow"><div
method="post"><div class="toprow"><div class="helpB"><button type="button" class="helpB"><button type="button" onclick="H()">?</button></div><button
onclick="H()">?</button></div><button type="button" onclick="B()">Back</button> type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
<button type="submit">Save</button><hr></div><h2> </div><h2>Imma firin ma lazer (if it has DMX support)</h2>Proxy Universe <input
Imma firin ma lazer (if it has DMX support)</h2>Proxy Universe <input name="PU" name="PU" type="number" min="0" max="63999" required>
type="number" min="0" max="63999" required> from E1.31 to DMX (0=disabled)<br> from E1.31 to DMX (0=disabled)<br><i>
<i>This will disable the LED data output to DMX configurable below</i><br><br> This will disable the LED data output to DMX configurable below</i><br><br><i>
<i>Number of fixtures is taken from LED config page</i><br> Number of fixtures is taken from LED config page</i><br>
Channels per fixture (15 max): <input type="number" min="1" max="15" name="CN" Channels per fixture (15 max): <input type="number" min="1" max="15" name="CN"
maxlength="2" onchange="mMap()"><br>Start channel: <input type="number" min="1" maxlength="2" onchange="mMap()"><br>Start channel: <input type="number" min="1"
max="512" name="CS" maxlength="2"><br>Spacing between start channels: <input max="512" name="CS" maxlength="2"><br>Spacing between start channels: <input
@ -193,13 +193,13 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta 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> name="viewport" content="width=500"><title>UI Settings</title><script>
var initial_ds,initial_st,initial_su,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:"Set segment length instead of stop LED",css:"Enable custom CSS",hdays:"Enable custom Holidays list"},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,s=e.split("_"),o=s.length,a=0;a<o-1;a++){var l=s[a];n[l]||(n[l]={}),n=n[l]}n[s[o-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 s="";for(i in e){var o=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(s+=`<h3>${n[i].LABEL}</h3>`),s+=addRec(e[i],o,n?n[i]:null);else{var a=o;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(o)?("boolean"===l?gId(o).checked=e[i]:gId(o).value=e[i],gId(o).previousElementSibling.matches(".l")&&(gId(o).previousElementSibling.innerHTML=a)):"boolean"===l?s+=`${a}: <input class="agi cb" type="checkbox" id=${o} ${e[i]?"checked":""}><br>`:"number"===l?s+=`${a}: <input class="agi" type="number" id=${o} value=${e[i]}><br>`:"string"===l&&(s+=`${a}:<br><input class="agi" id=${o} value=${e[i]}><br>`)}}return s}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.SU.checked==initial_su||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,initial_su=d.Sf.SU.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; var initial_ds,initial_st,initial_su,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:"Set segment length instead of stop LED",css:"Enable custom CSS",hdays:"Enable custom Holidays list"},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,s=e.split("_"),o=s.length,a=0;a<o-1;a++){var l=s[a];n[l]||(n[l]={}),n=n[l]}n[s[o-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 s="";for(i in e){var o=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(s+=`<h3>${n[i].LABEL}</h3>`),s+=addRec(e[i],o,n?n[i]:null);else{var a=o;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(o)?("boolean"===l?gId(o).checked=e[i]:gId(o).value=e[i],gId(o).previousElementSibling.matches(".l")&&(gId(o).previousElementSibling.innerHTML=a)):"boolean"===l?s+=`${a}: <input class="agi cb" type="checkbox" id=${o} ${e[i]?"checked":""}><br>`:"number"===l?s+=`${a}: <input class="agi" type="number" id=${o} value=${e[i]}><br>`:"string"===l&&(s+=`${a}:<br><input class="agi" id=${o} value=${e[i]}><br>`)}}return s}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.SU.checked==initial_su||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,initial_su=d.Sf.SU.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" %CSS%%SCSS%</head><body onload="S()"><form
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf" id="form_s" name="Sf" method="post"><div class="toprow"><div class="helpB">
method="post"><div class="toprow"><div class="helpB"><button type="button" <button type="button" onclick="H()">?</button></div><button type="button"
onclick="H()">?</button></div><button type="button" onclick="B()">Back</button> onclick="B()">Back</button><button type="button" onclick="Save()">Save</button>
<button type="button" onclick="Save()">Save</button><br><span id="lssuc" <br><span id="lssuc" style="color:green;display:none">
style="color:green;display:none">&#10004; Local UI settings saved!</span> <span &#10004; Local UI settings saved!</span> <span id="lserr"
id="lserr" style="color:red;display:none"> style="color:red;display:none">
&#9888; Could not access local storage. Make sure it is enabled in your browser. &#9888; Could not access local storage. Make sure it is enabled in your browser.
</span><hr></div><h2>Web Setup</h2>Server description: <input name="DS" </span><hr></div><h2>Web Setup</h2>Server description: <input name="DS"
maxlength="32"><br>Sync button toggles both send and receive: <input maxlength="32"><br>Sync button toggles both send and receive: <input
@ -239,20 +239,20 @@ type="button" onclick="Save()">Save</button></form></body></html>)=====";
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 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 SP(){var e=d.Sf.DI.value;d.getElementById("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()}function S(){GetV(),SetVal()}function GetV() { var d=document;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 SP(){var e=d.Sf.DI.value;d.getElementById("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()}function S(){GetV(),SetVal()}function GetV() {
%CSS%%SCSS%<link href="/skin.css" %CSS%%SCSS%</head><body onload="S()">
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf" <form id="form_s" name="Sf" method="post"><div class="toprow"><div
method="post"><div class="toprow"><div class="helpB"><button type="button" class="helpB"><button type="button" onclick="H()">?</button></div><button
onclick="H()">?</button></div><button type="button" onclick="B()">Back</button> type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
<button type="submit">Save</button><hr></div><h2>Sync setup</h2><h3> </div><h2>Sync setup</h2><h3>WLED Broadcast</h3>UDP Port: <input name="UP"
WLED Broadcast</h3>UDP Port: <input name="UP" type="number" min="1" max="65535" type="number" min="1" max="65535" class="d5" required><br>2nd Port: <input
class="d5" required><br>2nd Port: <input name="U2" type="number" min="1" name="U2" type="number" min="1" max="65535" class="d5" required><br>Receive:
max="65535" class="d5" required><br>Receive: <input type="checkbox" name="RB"> <input type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">
Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" Color, and <input type="checkbox" name="RX">Effects<br>
name="RX">Effects<br>Send notifications on direct change: <input Send notifications on direct change: <input type="checkbox" name="SD"><br>
type="checkbox" name="SD"><br>Send notifications on button press or IR: <input Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
type="checkbox" name="SB"><br>Send Alexa notifications: <input type="checkbox" Send Alexa notifications: <input type="checkbox" name="SA"><br>
name="SA"><br>Send Philips Hue change notifications: <input type="checkbox" Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
name="SH"><br>Send Macro notifications: <input type="checkbox" name="SM"><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>
@ -319,47 +319,46 @@ type="submit">Save</button></form></body></html>)=====";
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500"> const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>Time Settings</title><script> <meta charset="utf-8"><title>Time Settings</title><script>
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++){for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" class="sml" type="number" min="0" max="24"></td><td><input name="N'+i+'" class="sml" type="number" min="0" max="59"></td><td><input name="T'+i+'" class="med" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';t+="</tr>"}for(t+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" class="sml" value="255" type="hidden"></td><td><input name="N8" class="sml" type="number" min="-59" max="59"></td><td><input name="T8" class="med" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W8'+j+'" type="checkbox"></td>';for(t+='</tr><tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" class="sml" value="255" type="hidden"></td><td><input name="N9" class="sml" type="number" min="-59" max="59"></td><td><input name="T9" class="med" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W9'+j+'" type="checkbox"></td>';t+="</tr>",gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<10;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0,0,0],i=0;i<10;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function addRow(t,e,n,i){var d=gId("macros"),s=d.rows.length,c=d.insertRow(s);document.createElement("td");c.insertCell(0).innerHTML=`Button ${t}:`,c.insertCell(1).innerHTML=`<input name="MP${t}" type="number" class="m" min="0" max="250" value="${e}" required>`,c.insertCell(2).innerHTML=`<input name="ML${t}" type="number" class="m" min="0" max="250" value="${n}" required>`,c.insertCell(3).innerHTML=`<input name="MD${t}" type="number" class="m" min="0" max="250" value="${i}" required>`}function GetV() { var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++){for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" class="sml" type="number" min="0" max="24"></td><td><input name="N'+i+'" class="sml" type="number" min="0" max="59"></td><td><input name="T'+i+'" class="med" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';t+="</tr>"}for(t+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" class="sml" value="255" type="hidden"></td><td><input name="N8" class="sml" type="number" min="-59" max="59"></td><td><input name="T8" class="med" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W8'+j+'" type="checkbox"></td>';for(t+='</tr><tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" class="sml" value="255" type="hidden"></td><td><input name="N9" class="sml" type="number" min="-59" max="59"></td><td><input name="T9" class="med" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W9'+j+'" type="checkbox"></td>';t+="</tr>",gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<10;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0,0,0],i=0;i<10;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function addRow(t,e,n,i){var d=gId("macros"),s=d.rows.length,c=d.insertRow(s);document.createElement("td");c.insertCell(0).innerHTML=`Button ${t}:`,c.insertCell(1).innerHTML=`<input name="MP${t}" type="number" class="m" min="0" max="250" value="${e}" required>`,c.insertCell(2).innerHTML=`<input name="ML${t}" type="number" class="m" min="0" max="250" value="${n}" required>`,c.insertCell(3).innerHTML=`<input name="MD${t}" type="number" class="m" min="0" max="250" value="${i}" required>`}function GetV() {
%CSS%%SCSS%<link href="/skin.css" %CSS%%SCSS%</head><body onload="S()">
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf" <form id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="toprow">
method="post" onsubmit="Wd()"><div class="toprow"><div class="helpB"><button <div class="helpB"><button type="button" onclick="H()">?</button></div><button
type="button" onclick="H()">?</button></div><button type="button" onclick="B()"> type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
Back</button><button type="submit">Save</button><hr></div><h2>Time setup</h2> </div><h2>Time setup</h2>Get time from NTP server: <input type="checkbox"
Get time from NTP server: <input type="checkbox" name="NT"><br><input name="NS" name="NT"><br><input name="NS" maxlength="32"><br>Use 24h format: <input
maxlength="32"><br>Use 24h format: <input type="checkbox" name="CF"><br> type="checkbox" name="CF"><br>Time zone: <select name="TZ"><option value="0"
Time zone: <select name="TZ"><option value="0" selected="selected">GMT(UTC) selected="selected">GMT(UTC)</option><option value="1">GMT/BST</option><option
</option><option value="1">GMT/BST</option><option value="2">CET/CEST</option> value="2">CET/CEST</option><option value="3">EET/EEST</option><option value="4">
<option value="3">EET/EEST</option><option value="4">US-EST/EDT</option><option US-EST/EDT</option><option value="5">US-CST/CDT</option><option value="6">
value="5">US-CST/CDT</option><option value="6">US-MST/MDT</option><option US-MST/MDT</option><option value="7">US-AZ</option><option value="8">US-PST/PDT
value="7">US-AZ</option><option value="8">US-PST/PDT</option><option value="9"> </option><option value="9">CST(AWST)</option><option value="10">JST(KST)
CST(AWST)</option><option value="10">JST(KST)</option><option value="11"> </option><option value="11">AEST/AEDT</option><option value="12">NZST/NZDT
AEST/AEDT</option><option value="12">NZST/NZDT</option><option value="13"> </option><option value="13">North Korea</option><option value="14">IST (India)
North Korea</option><option value="14">IST (India)</option><option value="15"> </option><option value="15">CA-Saskatchewan</option><option value="16">ACST
CA-Saskatchewan</option><option value="16">ACST</option><option value="17"> </option><option value="17">ACST/ACDT</option><option value="18">HST (Hawaii)
ACST/ACDT</option><option value="18">HST (Hawaii)</option></select><br> </option></select><br>UTC offset: <input name="UO" type="number" min="-65500"
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> max="65500" required> seconds (max. 18 hours)<br>Current local time is <span
seconds (max. 18 hours)<br>Current local time is <span class="times">unknown class="times">unknown</span>.<br>Latitude (N): <input name="LT" type="number"
</span>.<br>Latitude (N): <input name="LT" type="number" class="xl" min="-66.6" class="xl" min="-66.6" max="66.6" step="0.01"> Longitude (E): <input name="LN"
max="66.6" step="0.01"> Longitude (E): <input name="LN" type="number" type="number" class="xl" min="-180" max="180" step="0.01"><div id="sun"
class="xl" min="-180" max="180" step="0.01"><div id="sun" class="times"></div> class="times"></div><h3>Clock</h3>Clock Overlay: <select name="OL"
<h3>Clock</h3>Clock Overlay: <select name="OL" onchange="Cs()"><option onchange="Cs()"><option value="0" id="cn" selected="selected">None</option>
value="0" id="cn" selected="selected">None</option><option value="1" id="ca"> <option value="1" id="ca">Analog Clock</option><option value="2">
Analog Clock</option><option value="2">Single Digit Clock</option><option Single Digit Clock</option><option value="3" id="cc">Cronixie Clock</option>
value="3" id="cc">Cronixie Clock</option></select><br><div id="coc">First LED: </select><br><div id="coc">First LED: <input name="O1" type="number" min="0"
<input name="O1" type="number" min="0" max="255" required> Last LED: <input max="255" required> Last LED: <input name="O2" type="number" min="0" max="255"
name="O2" type="number" min="0" max="255" required><br><div id="cac">12h LED: required><br><div id="cac">12h LED: <input name="OM" type="number" min="0"
<input name="OM" type="number" min="0" max="255" required><br>Show 5min marks: max="255" required><br>Show 5min marks: <input type="checkbox" name="O5"><br>
<input type="checkbox" name="O5"><br></div>Seconds (as trail): <input </div>Seconds (as trail): <input type="checkbox" name="OS"><br></div><div
type="checkbox" name="OS"><br></div><div id="ccc">Cronixie Display: <input id="ccc">Cronixie Display: <input name="CX" maxlength="6"><br>
name="CX" maxlength="6"><br>Cronixie Backlight: <input type="checkbox" Cronixie Backlight: <input type="checkbox" name="CB"><br></div>Countdown Mode:
name="CB"><br></div>Countdown Mode: <input type="checkbox" name="CE"><br> <input type="checkbox" name="CE"><br>Countdown Goal:<br>Year: 20 <input
Countdown Goal:<br>Year: 20 <input name="CY" class="small" type="number" name="CY" class="small" type="number" min="0" max="99" required> Month: <input
min="0" max="99" required> Month: <input name="CI" class="small" type="number" name="CI" class="small" type="number" min="1" max="12" required> Day: <input
min="1" max="12" required> Day: <input name="CD" class="small" type="number" name="CD" class="small" type="number" min="1" max="31" required><br>Hour: <input
min="1" max="31" required><br>Hour: <input name="CH" class="small" name="CH" class="small" type="number" min="0" max="23" required> Minute: <input
type="number" min="0" max="23" required> Minute: <input name="CM" class="small" name="CM" class="small" type="number" min="0" max="59" required> Second: <input
type="number" min="0" max="59" required> Second: <input name="CS" class="small" name="CS" class="small" type="number" min="0" max="59" required><br><h3>
type="number" min="0" max="59" required><br><h3>Macro presets</h3><b> Macro presets</h3><b>Macros have moved!</b><br><i>
Macros have moved!</b><br><i>
Presets now also can be used as macros to save both JSON and HTTP API commands. Presets now also can be used as macros to save both JSON and HTTP API commands.
<br>Just enter the preset id below!</i> <i> <br>Just enter the preset id below!</i> <i>
Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset: Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset:
@ -380,7 +379,7 @@ type="button" onclick="B()">Back</button><button type="submit">Save</button>
// Autogenerated from wled00/data/settings_sec.htm, do not edit!! // 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"> 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> <meta charset="utf-8"><title>Misc Settings</title><script>
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(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function showToast(e,t=!1){var o=gId("toast");o.innerHTML=e,o.className=t?"error":"show",clearTimeout(timeout),o.style.animation="none",timeout=setTimeout((function(){o.className=o.className.replace("show","")}),2900)}function uploadFile(e,t){var o=new XMLHttpRequest;o.addEventListener("load",(function(){showToast(this.responseText)})),o.addEventListener("error",(function(e){showToast(e.stack,!0)})),o.open("POST","/upload");var n=new FormData;return n.append("data",e.files[0],t),o.send(n),e.value="",!1}function clearLS(){localStorage.removeItem("wledP"),localStorage.removeItem("wledPmt")}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,e){var n=new XMLHttpRequest;n.addEventListener("load",(function(){showToast(this.responseText)})),n.addEventListener("error",(function(t){showToast(t.stack,!0)})),n.open("POST","/upload");var o=new FormData;return o.append("data",t.files[0],e),n.send(o),t.value="",!1}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css" %CSS%%SCSS%<link href="/skin.css"
rel="stylesheet"></head><body onload="GetV()"><form id="form_s" name="Sf" rel="stylesheet"></head><body onload="GetV()"><form id="form_s" name="Sf"
method="post"><div class="toprow"><div class="helpB"><button type="button" method="post"><div class="toprow"><div class="helpB"><button type="button"
@ -397,19 +396,21 @@ Disable OTA when not in use, otherwise an attacker can reflash device software!
<br><br>Factory reset: <input type="checkbox" name="RS"><br> <br><br>Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><br> All settings and presets will be erased.<br><br>
HTTP traffic is unencrypted. An attacker in the same network can intercept form data! HTTP traffic is unencrypted. An attacker in the same network can intercept form data!
<h3>Backup & Restore</h3><a class="btn lnk" href="/presets.json?download" <hr><h3>Software Update</h3><button type="button" onclick="U()">
target="download-frame">Backup presets</a><br><div id="presets">Restore presets: Manual OTA Update</button><br>Enable ArduinoOTA: <input type="checkbox"
<input type="file" name="data" accept=".json"> <input type="button" name="AO"><br><h3>Backup & Restore</h3><a class="btn lnk"
value="Upload" onclick='uploadFile(d.Sf.data,"/presets.json"),clearLS()'><br> href="/presets.json?download" target="download-frame">Backup presets</a><br>
</div><a class="btn lnk" href="/cfg.json?download" target="download-frame"> <div>Restore presets<br><input type="file" name="data" accept=".json"> <input
Backup configuration</a><br><div id="presets">Restore configuration:<input type="button" value="Upload" onclick='uploadFile(d.Sf.data,"/presets.json")'>
type="file" name="data2" accept=".json"> <input type="button" value="Upload" <br></div><br><a class="btn lnk" href="/cfg.json?download"
onclick='uploadFile(d.Sf.data2,"/cfg.json")'><br></div><div style="color:#fa0"> target="download-frame">Backup configuration</a><br><div>Restore configuration
<br><input type="file" name="data2" accept=".json"> <input type="button"
value="Upload" onclick='uploadFile(d.Sf.data2,"/cfg.json")'><br></div><div
style="color:#fa0">
&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration. &#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.
<br>Incorrect configuration may require factory reset or re-flashing of ESP. <br>
</div>For security reasons, passwords are not backed up.<hr><h3>Software Update Incorrect configuration may require a factory reset or re-flashing of your ESP.
</h3><button type="button" onclick="U()">Manual OTA Update</button><br> </div>For security reasons, passwords are not backed up.<h3>About</h3><a
Enable ArduinoOTA: <input type="checkbox" name="AO"><br><hr><h3>About</h3><a
href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a>
version 0.13.0-bl3<br><br><a version 0.13.0-bl3<br><br><a
href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits"
@ -427,14 +428,14 @@ name="download-frame" style="display:none"></iframe></body></html>)=====";
const char PAGE_settings_um[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta const char PAGE_settings_um[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta
name="viewport" content="width=500"><title>Usermod Settings</title><script> name="viewport" content="width=500"><title>Usermod Settings</title><script>
var owner,locip,urows,d=document,umCfg={},pins=[6,7,8,9,10,11],pinO=["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"],loc=!1,numM=0;function gId(e){return d.getElementById(e)}function isO(e){return e&&"object"==typeof e&&!Array.isArray(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings")}function B(){window.open("/settings","_self")}function S(){"file:"==window.location.protocol&&(loc=!0,(locip=localStorage.getItem("locIp"))||(locip=prompt("File Mode. Please enter WLED IP!"),localStorage.setItem("locIp",locip))),GetV(),numM>0||locip?ldS():gId("um").innerHTML="No Usermods installed."}function isF(e){return e===+e&&e!==(0|e)}function isI(e){return e===+e&&e===(0|e)}function check(e,n){var i=e.name.replace("[]","").substr(-3);if("number"==e.type&&"pin"==i.substr(0,3))for(var s=0;s<pins.length;s++)if(n!=pinO[s]){if(e.value==pins[s]||e.value<-1||e.value>39){e.style.color="red";break}e.style.color=e.value>33?"orange":"#fff"}}function getPins(e){if(isO(e))for(const[i,s]of Object.entries(e))if(isO(s))owner=i,getPins(s);else if("pin"==i.replace("[]","").substr(-3))if(Array.isArray(s))for(var n=0;n<s.length;n++)s[n]>=0&&(pins.push(s[n]),pinO.push(owner));else s>=0&&(pins.push(s),pinO.push(owner));else if(Array.isArray(s))for(n=0;n<s.length;n++)getPins(s[n])}function addField(e,n,i,s=!1){if(isO(i))for(const[s,t]of Object.entries(i))"unknown"===n||e.includes(":")?addField(e,s,t):addField(e+":"+n,s,t);else if(Array.isArray(i))for(var t=0;t<i.length;t++)addField(e,n,i[t],!0);else{var o,r=typeof i;switch(r){case"boolean":r="checkbox",o='value="true"'+(i?" checked":"");break;case"number":o=`value="${i}"`,"pin"===n.substr(-3)?(o+=' max="39" min="-1" class="s"',r="int"):o+=' step="any" class="xxl"';break;default:r="text",o=`value="${i}" style="width:250px;"`}e.includes(":")&&(urows+=e.substr(e.indexOf(":")+1)),urows+=` ${n}: `,"checkbox"==r?urows+=`<input type="hidden" name="${e}:${n}${s?"[]":""}" value="false">`:s||(urows+=`<input type="hidden" name="${e}:${n}${s?"[]":""}" value="${r}">`),urows+=`<input type="${"int"===r?"number":r}" name="${e}:${n}${s?"[]":""}" ${o} oninput="check(this,'${e.substr(e.indexOf(":")+1)}')"><br>`}}function ldS(){fetch((loc?"http://"+locip:"")+"/cfg.json",{method:"get"}).then(e=>(e.ok||(gId("lserr").style.display="inline"),e.json())).then(e=>{if(umCfg=e.um,getPins(e),urows="",isO(umCfg))for(const[e,n]of Object.entries(umCfg))urows+=`<hr><h3>${e}</h3>`,addField(e,"unknown",n);""===urows&&(urows="Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults."),gId("um").innerHTML=urows}).catch((function(e){gId("lserr").style.display="inline",console.log(e)}))}function svS(e){e.preventDefault(),console.log(d.Sf),d.Sf.checkValidity()&&d.Sf.submit()}function GetV() {var d=document; var owner,locip,urows,d=document,umCfg={},pins=[6,7,8,9,10,11],pinO=["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"],loc=!1,numM=0;function gId(e){return d.getElementById(e)}function isO(e){return e&&"object"==typeof e&&!Array.isArray(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings")}function B(){window.open("/settings","_self")}function S(){"file:"==window.location.protocol&&(loc=!0,(locip=localStorage.getItem("locIp"))||(locip=prompt("File Mode. Please enter WLED IP!"),localStorage.setItem("locIp",locip))),GetV(),numM>0||locip?ldS():gId("um").innerHTML="No Usermods installed."}function isF(e){return e===+e&&e!==(0|e)}function isI(e){return e===+e&&e===(0|e)}function check(e,n){var i=e.name.replace("[]","").substr(-3);if("number"==e.type&&"pin"==i.substr(0,3))for(var s=0;s<pins.length;s++)if(n!=pinO[s]){if(e.value==pins[s]||e.value<-1||e.value>39){e.style.color="red";break}e.style.color=e.value>33?"orange":"#fff"}}function getPins(e){if(isO(e))for(const[i,s]of Object.entries(e))if(isO(s))owner=i,getPins(s);else if("pin"==i.replace("[]","").substr(-3))if(Array.isArray(s))for(var n=0;n<s.length;n++)s[n]>=0&&(pins.push(s[n]),pinO.push(owner));else s>=0&&(pins.push(s),pinO.push(owner));else if(Array.isArray(s))for(n=0;n<s.length;n++)getPins(s[n])}function addField(e,n,i,s=!1){if(isO(i))for(const[s,t]of Object.entries(i))"unknown"===n||e.includes(":")?addField(e,s,t):addField(e+":"+n,s,t);else if(Array.isArray(i))for(var t=0;t<i.length;t++)addField(e,n,i[t],!0);else{var o,r=typeof i;switch(r){case"boolean":r="checkbox",o='value="true"'+(i?" checked":"");break;case"number":o=`value="${i}"`,"pin"===n.substr(-3)?(o+=' max="39" min="-1" class="s"',r="int"):o+=' step="any" class="xxl"';break;default:r="text",o=`value="${i}" style="width:250px;"`}e.includes(":")&&(urows+=e.substr(e.indexOf(":")+1)),urows+=` ${n}: `,"checkbox"==r?urows+=`<input type="hidden" name="${e}:${n}${s?"[]":""}" value="false">`:s||(urows+=`<input type="hidden" name="${e}:${n}${s?"[]":""}" value="${r}">`),urows+=`<input type="${"int"===r?"number":r}" name="${e}:${n}${s?"[]":""}" ${o} oninput="check(this,'${e.substr(e.indexOf(":")+1)}')"><br>`}}function ldS(){fetch((loc?"http://"+locip:"")+"/cfg.json",{method:"get"}).then(e=>(e.ok||(gId("lserr").style.display="inline"),e.json())).then(e=>{if(umCfg=e.um,getPins(e),urows="",isO(umCfg))for(const[e,n]of Object.entries(umCfg))urows+=`<hr><h3>${e}</h3>`,addField(e,"unknown",n);""===urows&&(urows="Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults."),gId("um").innerHTML=urows}).catch((function(e){gId("lserr").style.display="inline",console.log(e)}))}function svS(e){e.preventDefault(),console.log(d.Sf),d.Sf.checkValidity()&&d.Sf.submit()}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css" %CSS%%SCSS%</head><body onload="S()">
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf" <form id="form_s" name="Sf" method="post" onsubmit="svS(event)"><div
method="post" onsubmit="svS(event)"><div class="toprow"><div class="helpB"> class="toprow"><div class="helpB"><button type="button" onclick="H()">?</button>
<button type="button" onclick="H()">?</button></div><button type="button" </div><button type="button" onclick="B()">Back</button><button type="submit">
onclick="B()">Back</button><button type="submit">Save</button><br><span Save</button><br><span id="lssuc" style="color:green;display:none">
id="lssuc" style="color:green;display:none">&#10004; Configuration saved!</span> &#10004; Configuration saved!</span> <span id="lserr"
<span id="lserr" style="color:red;display:none"> style="color:red;display:none">&#9888; Could not load configuration.</span><hr>
&#9888; Could not load configuration.</span><hr></div><h2>Usermod Setup</h2><div </div><h2>Usermod Setup</h2><div id="um">Loading settings...</div><hr><button
id="um">Loading settings...</div><hr><button type="button" onclick="B()">Back type="button" onclick="B()">Back</button><button type="submit">Save</button>
</button><button type="submit">Save</button></form></body></html>)====="; </form></body></html>)=====";

File diff suppressed because it is too large Load Diff

View File

@ -165,6 +165,10 @@ void decodeIR(uint32_t code)
} }
lastValidCode = 0; irTimesRepeated = 0; lastValidCode = 0; irTimesRepeated = 0;
//if (decodeIRCustom(code)) return; //if (decodeIRCustom(code)) return;
if (irEnabled == 8) { // any remote configurable with ir.json file
decodeIRJson(code);
return;
}
if (code > 0xFFFFFF) return; //invalid code if (code > 0xFFFFFF) return; //invalid code
switch (irEnabled) { switch (irEnabled) {
case 1: case 1:
@ -182,7 +186,7 @@ void decodeIR(uint32_t code)
// "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE" // "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE"
// sets bright plain white // sets bright plain white
case 7: decodeIR9(code); break; case 7: decodeIR9(code); break;
case 8: decodeIRJson(code); break; // any remote configurable with ir.json file //case 8: return; // ir.json file, handled above switch statement
default: return; default: return;
} }

View File

@ -142,7 +142,6 @@ void colorUpdated(int callMode)
} }
if (briT == 0) if (briT == 0)
{ {
//setLedsStandard(true); //do not color transition if starting from off!
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
} }

View File

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

View File

@ -15,9 +15,11 @@ bool isIp(String str) {
return true; return true;
} }
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
if (!index) { if (!index) {
request->_tempFile = WLED_FS.open(filename, "w"); request->_tempFile = WLED_FS.open(filename, "w");
DEBUG_PRINT("Uploading ");
DEBUG_PRINTLN(filename);
if (filename == "/presets.json") presetsModifiedTime = toki.second(); if (filename == "/presets.json") presetsModifiedTime = toki.second();
} }
if (len) { if (len) {
@ -289,7 +291,9 @@ void setStaticContentCacheHeaders(AsyncWebServerResponse *response)
char tmp[12]; char tmp[12];
// https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c // https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c
#ifndef WLED_DEBUG #ifndef WLED_DEBUG
response->addHeader(F("Cache-Control"),"max-age=604800"); // 7 day caching //this header name is misleading, "no-cache" will not disable cache,
//it just revalidates on every load using the "If-None-Match" header with the last ETag value
response->addHeader(F("Cache-Control"),"no-cache");
#else #else
response->addHeader(F("Cache-Control"),"no-store,max-age=0"); // prevent caching if debug build response->addHeader(F("Cache-Control"),"no-store,max-age=0"); // prevent caching if debug build
#endif #endif

View File

@ -28,6 +28,12 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
//the whole message is in a single frame and we got all of its data (max. 1450byte) //the whole message is in a single frame and we got all of its data (max. 1450byte)
if(info->opcode == WS_TEXT) if(info->opcode == WS_TEXT)
{ {
if (len > 0 && len < 10 && data[0] == 'p') {
//application layer ping/pong heartbeat.
//client-side socket layer ping packets are unresponded (investigate)
client->text(F("pong"));
return;
}
bool verboseResponse = false; bool verboseResponse = false;
{ //scope JsonDocument so it releases its buffer { //scope JsonDocument so it releases its buffer
DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE);