Date controlled timed presets. (#2447)

* Date controlled timed presets.

* C/P fix for sunset.

* Fixed % escape character

* Date range support

* Date logic fix

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
This commit is contained in:
Blaž Kristan 2021-12-31 14:09:48 +01:00 committed by GitHub
parent 754682577c
commit 18868a5bd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1018 additions and 916 deletions

View File

@ -366,7 +366,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(timerMinutes[it], timer["min"]); CJSON(timerMinutes[it], timer["min"]);
CJSON(timerMacro[it], timer["macro"]); CJSON(timerMacro[it], timer["macro"]);
byte dowPrev = timerWeekday[it]; byte dowPrev = timerWeekday[it];
//note: act is currently only 0 or 1. //note: act is currently only 0 or 1.
//the reason we are not using bool is that the on-disk type in 0.11.0 was already int //the reason we are not using bool is that the on-disk type in 0.11.0 was already int
int actPrev = timerWeekday[it] & 0x01; int actPrev = timerWeekday[it] & 0x01;
@ -376,7 +376,17 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
int act = timer["en"] | actPrev; int act = timer["en"] | actPrev;
if (act) timerWeekday[it]++; if (act) timerWeekday[it]++;
} }
if (it<8) {
JsonObject start = timer["start"];
byte startm = start["mon"];
if (startm) timerMonth[it] = (startm << 4);
CJSON(timerDay[it], start["day"]);
JsonObject end = timer["end"];
CJSON(timerDayEnd[it], end["day"]);
byte endm = end["mon"];
if (startm) timerMonth[it] += endm & 0x0F;
if (!(timerMonth[it] & 0x0F)) timerMonth[it] += 12; //default end month to 12
}
it++; it++;
} }
@ -742,6 +752,14 @@ void serializeConfig() {
timers_ins0["min"] = timerMinutes[i]; timers_ins0["min"] = timerMinutes[i];
timers_ins0["macro"] = timerMacro[i]; timers_ins0["macro"] = timerMacro[i];
timers_ins0[F("dow")] = timerWeekday[i] >> 1; timers_ins0[F("dow")] = timerWeekday[i] >> 1;
if (i<8) {
JsonObject start = timers_ins0.createNestedObject("start");
start["mon"] = (timerMonth[i] >> 4) & 0xF;
start["day"] = timerDay[i];
JsonObject end = timers_ins0.createNestedObject("end");
end["mon"] = timerMonth[i] & 0xF;
end["day"] = timerDayEnd[i];
}
} }
JsonObject ota = doc.createNestedObject("ota"); JsonObject ota = doc.createNestedObject("ota");

View File

@ -6,9 +6,11 @@
<title>Time Settings</title> <title>Time Settings</title>
<script> <script>
var d=document; var d=document;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var cals = 'style="font-size:27px;margin-top:-6px;cursor:pointer"'; //hack as to not repeat CSS on all pages
function H() function H()
{ {
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings"); window.open("https://kno.wled.ge/features/settings/#time-settings");
} }
function B() function B()
{ {
@ -22,6 +24,15 @@
{ {
return d.getElementById(s); return d.getElementById(s);
} }
function gN(s) {
return d.getElementsByName(s)[0];
}
function expand(o,i)
{
var t = gId("WD"+i);
t.style.display = t.style.display!=="none" ? "none" : "";
o.innerHTML = t.style.display==="none" ? "&#128467;" : "&#11148;";
}
function Cs() function Cs()
{ {
gId("cac").style.display="none"; gId("cac").style.display="none";
@ -41,56 +52,83 @@
gId("coc").style.display="none"; gId("coc").style.display="none";
} }
} }
function BTa() function BTa()
{ {
var ih="<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>"; var ih="<tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr>";
for (i=0;i<8;i++) for (i=0;i<8;i++) {
{ ih+=`<tr><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td>
ih+="<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+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>"; <td><input name="H${i}" class="xs" type="number" min="0" max="24"></td>
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>"; <td><input name="N${i}" class="xs" type="number" min="0" max="59"></td>
} <td><input name="T${i}" class="s" type="number" min="0" max="250"></td>
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" type=\"number\" min=\"0\" max=\"250\"></td>"; <td><div id="CB${i}" onclick="expand(this,${i})" ${cals}>&#128467;</div></td></tr>`;
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>"; ih+=`<tr><td colspan=5><div id="WD${i}" style="display:none;">Run on weekdays`;
ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+=`<table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+=`<td><input id="W${i}${j}" type="checkbox"></td>`;
gId("TMT").innerHTML=ih; ih+=`</tr></table>from
} <select name="M${i}">`;
function FC() for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
{ ih+=`</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to
for(j=0;j<8;j++) <select name="P${i}">`;
{ for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
for(i=0;i<10;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1; ih+=`</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input>
} <hr></div></td></tr>`;
} }
function Wd() ih+=`<tr><td><input name="W8" id="W8" type="hidden"><input id="W80" type="checkbox"></td>
{ <td>Sunrise<input name="H8" value="255" type="hidden"></td>
a=[0,0,0,0,0,0,0,0,0,0]; <td><input name="N8" class="xs" type="number" min="-59" max="59"></td>
for(i=0;i<10;i++) <td><input name="T8" class="s" type="number" min="0" max="250"></td>
{ <td><div onclick="expand(this,8)" ${cals}>&#128467;</div></td></tr><tr><td colspan=5>`;
m=1; ih+=`<div id="WD8"style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
for(j=0;j<8;j++) for (j=1;j<8;j++) ih+=`<td><input id="W8${j}" type="checkbox"></td>`;
{ ih+="</tr></table><hr></div></td></tr>";
a[i]+=gId("W"+i+j).checked*m;m*=2; ih+=`<tr><td><input name="W9" id="W9" type="hidden"><input id="W90" type="checkbox"></td>
} <td>Sunset<input name="H9" value="255" type="hidden"></td>
gId("W"+i).value=a[i]; <td><input name="N9" class="xs" type="number" min="-59" max="59"></td>
} <td><input name="T9" class="s" type="number" min="0" max="250"></td>
if (d.Sf.LTR.value==="S") { d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } <td><div onclick="expand(this,9)" ${cals}>&#128467;</div></td></tr><tr><td colspan=5>`;
if (d.Sf.LNR.value==="W") { d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } ih+=`<div id="WD9" style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
} for (j=1;j<8;j++) ih+=`<td><input id="W9${j}" type="checkbox"></td>`;
ih+="</tr></table><hr></div></td></tr>";
gId("TMT").innerHTML=ih;
}
function FC()
{
for(i=0;i<10;i++)
{
let wd = gId("W"+i).value;
for(j=0;j<8;j++) {
gId("W"+i+j).checked=wd>>j&1;
}
if ((wd&127) != 127 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
}
}
}
function Wd()
{
a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++) {
m=1;
for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;}
gId("W"+i).value=a[i];
}
if (d.Sf.LTR.value==="S") { d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); }
if (d.Sf.LNR.value==="W") { d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); }
}
function addRow(i,p,l,d) { function addRow(i,p,l,d) {
var t = gId("macros"); // table var t = gId("macros"); // table
var rCnt = t.rows.length; // get the number of rows. var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); // table row. var tr = t.insertRow(rCnt); // table row.
var b = String.fromCharCode((i<10?48:55)+i); var b = String.fromCharCode((i<10?48:55)+i);
var td = document.createElement('td'); // TABLE DEFINITION. var td = document.createElement('td'); // TABLE DEFINITION.
td = tr.insertCell(0); td = tr.insertCell(0);
td.innerHTML = `Button ${i}:`; td.innerHTML = `Button ${i}:`;
td = tr.insertCell(1); td = tr.insertCell(1);
td.innerHTML = `<input name="MP${b}" type="number" min="0" max="250" value="${p}" required>`; td.innerHTML = `<input name="MP${b}" type="number" class="s" min="0" max="250" value="${p}" required>`;
td = tr.insertCell(2); td = tr.insertCell(2);
td.innerHTML = `<input name="ML${b}" type="number" min="0" max="250" value="${l}" required>`; td.innerHTML = `<input name="ML${b}" type="number" class="s" min="0" max="250" value="${l}" required>`;
td = tr.insertCell(3); td = tr.insertCell(3);
td.innerHTML = `<input name="MD${b}" type="number" min="0" max="250" value="${d}" required>`; td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
} }
function updLoc(i) { function updLoc(i) {
if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N"; if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N";
@ -111,7 +149,7 @@
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
<h2>Time setup</h2> <h2>Time setup</h2>
Get time from NTP server: <input type="checkbox" name="NT"><br> Get time from NTP server: <input type="checkbox" name="NT"><br>
<input name="NS" maxlength="32"><br> <input type="text" name="NS" maxlength="32"><br>
Use 24h format: <input type="checkbox" name="CF"><br> Use 24h format: <input type="checkbox" name="CF"><br>
Time zone: Time zone:
<select name="TZ"> <select name="TZ">
@ -140,8 +178,8 @@
</select><br> </select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br> UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<br> Current local time is <span class="times">unknown</span>.<br>
Latitude: <select name="LTR"><option value="1">N</option><option value="-1">S</option></select><input name="LT" type="number" min="0" max="66.6" step="0.01"><br> Latitude: <select name="LTR"><option value="N">N</option><option value="S">S</option></select><input name="LT" type="number" class="xl" min="0" max="66.6" step="0.01"><br>
Longitude: <select name="LNR"><option value="1">E</option><option value="-1">W</option></select><input name="LN" type="number" min="0" max="180" step="0.01"> Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01">
<div id="sun" class="times"></div> <div id="sun" class="times"></div>
<h3>Clock</h3> <h3>Clock</h3>
Clock Overlay: Clock Overlay:
@ -164,16 +202,16 @@
</div> </div>
Countdown Mode: <input type="checkbox" name="CE"><br> Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br> Countdown Goal:<br>
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br> Date:&nbsp;<nowrap>20<input name="CY" class="xs" type="number" min="0" max="99" required>-<input name="CI" class="xs" type="number" min="1" max="12" required>-<input name="CD" class="xs" type="number" min="1" max="31" required></nowrap><br>
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br> Time: <nowrap><input name="CH" class="xs" type="number" min="0" max="23" required>:<input name="CM" class="xs" type="number" min="0" max="59" required>:<input name="CS" class="xs" type="number" min="0" max="59" required></nowrap><br>
<h3>Macro presets</h3> <h3>Macro presets</h3>
<b>Macros have moved!</b><br> <b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br> <i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset id below!</i> Just enter the preset ID below!</i>
<i>Use 0 for the default action instead of a preset</i><br> <i>Use 0 for the default action instead of a preset</i><br>
Alexa On/Off Preset: <input name="A0" type="number" min="0" max="250" required> <input name="A1" type="number" min="0" max="250" required><br> Alexa On/Off Preset: <input name="A0" class="m" type="number" min="0" max="250" required> <input name="A1" class="m" type="number" min="0" max="250" required><br>
Countdown-Over Preset: <input name="MC" type="number" min="0" max="250" required><br> Countdown-Over Preset: <input name="MC" class="m" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" required><br> Timed-Light-Over Presets: <input name="MN" class="m" type="number" min="0" max="250" required><br>
<h3>Button actions</h3> <h3>Button actions</h3>
<table style="margin: 0 auto;" id="macros"> <table style="margin: 0 auto;" id="macros">
<thead> <thead>
@ -187,11 +225,12 @@
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<a href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button" target="_blank">Analog Button setup</a> <a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
<h3>Time-controlled presets</h3> <h3>Time-controlled presets</h3>
<div style="display: inline-block"> <div style="display: inline-block">
<table id="TMT"> <table id="TMT" style="min-width:330px;"></table>
</table></div><hr> </div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button> <button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form> </form>
</body> </body>

View File

@ -66,7 +66,6 @@ input[type="number"].xs {
} }
input[type="checkbox"] { input[type="checkbox"] {
transform: scale(1.5); transform: scale(1.5);
margin-right: 10px;
} }
select { select {
background: #333; background: #333;
@ -80,7 +79,6 @@ td {
.d5 { .d5 {
width: 4.5em !important; width: 4.5em !important;
} }
#toast { #toast {
opacity: 0; opacity: 0;
background-color: #444; background-color: #444;

View File

@ -6,7 +6,7 @@
*/ */
// Autogenerated from wled00/data/style.css, do not edit!! // 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}a{color:#28f;text-decoration:none}.btn,button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:1px 6px;cursor:pointer;text-decoration:none}.lnk{border:0}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input:disabled{color:#888}input[type=number]{width:4em;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}input[type=checkbox]{transform:scale(1.5);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}a{color:#28f;text-decoration:none}.btn,button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:1px 6px;cursor:pointer;text-decoration:none}.lnk{border:0}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input:disabled{color:#888}input[type=number]{width:4em;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}input[type=checkbox]{transform:scale(1.5)}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!! // Autogenerated from wled00/data/settings.htm, do not edit!!
@ -339,32 +339,33 @@ type="submit">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_time.htm, do not edit!! // Autogenerated from wled00/data/settings_time.htm, do not edit!!
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(),updLoc(),Cs(),FC()}function gId(e){return d.getElementById(e)}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 e="<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(e+='<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+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)e+='<td><input id="W'+i+j+'" type="checkbox"></td>';for(e+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" value="255" type="hidden"></td><td><input name="N8" type="number" min="-59" max="59"></td><td><input name="T8" type="number" min="0" max="250"></td>',j=1;j<8;j++)e+='<td><input id="W8'+j+'" type="checkbox"></td>';for(e+='<tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" value="255" type="hidden"></td><td><input name="N9" type="number" min="-59" max="59"><td><input name="T9" type="number" min="0" max="250"></td>',j=1;j<8;j++)e+='<td><input id="W9'+j+'" type="checkbox"></td>';gId("TMT").innerHTML=e}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]}"S"===d.Sf.LTR.value&&(d.Sf.LT.value=-1*parseFloat(d.Sf.LT.value)),"W"===d.Sf.LNR.value&&(d.Sf.LN.value=-1*parseFloat(d.Sf.LN.value))}function addRow(e,t,n,i){var d=gId("macros"),a=d.rows.length,u=d.insertRow(a),l=String.fromCharCode((e<10?48:55)+e);document.createElement("td");u.insertCell(0).innerHTML=`Button ${e}:`,u.insertCell(1).innerHTML=`<input name="MP${l}" type="number" min="0" max="250" value="${t}" required>`,u.insertCell(2).innerHTML=`<input name="ML${l}" type="number" min="0" max="250" value="${n}" required>`,u.insertCell(3).innerHTML=`<input name="MD${l}" type="number" min="0" max="250" value="${i}" required>`}function updLoc(e){parseFloat(d.Sf.LT.value)<0?(d.Sf.LTR.value="S",d.Sf.LT.value=-1*parseFloat(d.Sf.LT.value)):d.Sf.LTR.value="N",parseFloat(d.Sf.LN.value)<0?(d.Sf.LNR.value="W",d.Sf.LN.value=-1*parseFloat(d.Sf.LN.value)):d.Sf.LNR.value="E"}function GetV() { var d=document,ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],cals='style="font-size:27px;margin-top:-6px;cursor:pointer"';function H(){window.open("https://kno.wled.ge/features/settings/#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),updLoc(),Cs(),FC()}function gId(t){return d.getElementById(t)}function gN(t){return d.getElementsByName(t)[0]}function expand(t,e){var n=gId("WD"+e);n.style.display="none"!==n.style.display?"none":"",t.innerHTML="none"===n.style.display?"&#128467;":"&#11148;"}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>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr>";for(i=0;i<8;i++){for(t+=`<tr><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td>\n<td><input name="H${i}" class="xs" type="number" min="0" max="24"></td>\n<td><input name="N${i}" class="xs" type="number" min="0" max="59"></td>\n<td><input name="T${i}" class="s" type="number" min="0" max="250"></td>\n<td><div id="CB${i}" onclick="expand(this,${i})" ${cals}>&#128467;</div></td></tr>`,t+=`<tr><td colspan=5><div id="WD${i}" style="display:none;">Run on weekdays`,t+='<table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>',j=1;j<8;j++)t+=`<td><input id="W${i}${j}" type="checkbox"></td>`;for(t+=`</tr></table>from\n<select name="M${i}">`,j=0;j<12;j++)t+=`<option value="${j+1}">${ms[j]}</option>`;for(t+=`</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to\n<select name="P${i}">`,j=0;j<12;j++)t+=`<option value="${j+1}">${ms[j]}</option>`;t+=`</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input>\n\t\t<hr></div></td></tr>`}for(t+=`<tr><td><input name="W8" id="W8" type="hidden"><input id="W80" type="checkbox"></td>\n<td>Sunrise<input name="H8" value="255" type="hidden"></td>\n<td><input name="N8" class="xs" type="number" min="-59" max="59"></td>\n<td><input name="T8" class="s" type="number" min="0" max="250"></td>\n<td><div onclick="expand(this,8)" ${cals}>&#128467;</div></td></tr><tr><td colspan=5>`,t+='<div id="WD8"style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>',j=1;j<8;j++)t+=`<td><input id="W8${j}" type="checkbox"></td>`;for(t+="</tr></table><hr></div></td></tr>",t+=`<tr><td><input name="W9" id="W9" type="hidden"><input id="W90" type="checkbox"></td>\n<td>Sunset<input name="H9" value="255" type="hidden"></td>\n<td><input name="N9" class="xs" type="number" min="-59" max="59"></td>\n<td><input name="T9" class="s" type="number" min="0" max="250"></td>\n<td><div onclick="expand(this,9)" ${cals}>&#128467;</div></td></tr><tr><td colspan=5>`,t+='<div id="WD9" style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>',j=1;j<8;j++)t+=`<td><input id="W9${j}" type="checkbox"></td>`;t+="</tr></table><hr></div></td></tr>",gId("TMT").innerHTML=t}function FC(){for(i=0;i<10;i++){let t=gId("W"+i).value;for(j=0;j<8;j++)gId("W"+i+j).checked=t>>j&1;(127!=(127&t)||i<8&&(1!=gN("M"+i).value||1!=gN("D"+i).value||12!=gN("P"+i).value||31!=gN("E"+i).value))&&expand(gId("CB"+i),i)}}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]}"S"===d.Sf.LTR.value&&(d.Sf.LT.value=-1*parseFloat(d.Sf.LT.value)),"W"===d.Sf.LNR.value&&(d.Sf.LN.value=-1*parseFloat(d.Sf.LN.value))}function addRow(t,e,n,i){var d=gId("macros"),a=d.rows.length,l=d.insertRow(a),s=String.fromCharCode((t<10?48:55)+t);document.createElement("td");l.insertCell(0).innerHTML=`Button ${t}:`,l.insertCell(1).innerHTML=`<input name="MP${s}" type="number" class="s" min="0" max="250" value="${e}" required>`,l.insertCell(2).innerHTML=`<input name="ML${s}" type="number" class="s" min="0" max="250" value="${n}" required>`,l.insertCell(3).innerHTML=`<input name="MD${s}" type="number" class="s" min="0" max="250" value="${i}" required>`}function updLoc(t){parseFloat(d.Sf.LT.value)<0?(d.Sf.LTR.value="S",d.Sf.LT.value=-1*parseFloat(d.Sf.LT.value)):d.Sf.LTR.value="N",parseFloat(d.Sf.LN.value)<0?(d.Sf.LNR.value="W",d.Sf.LN.value=-1*parseFloat(d.Sf.LN.value)):d.Sf.LNR.value="E"}function GetV() {
%CSS%%SCSS%</head><body onload="S()"><form %CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="helpB"><button id="form_s" name="Sf" method="post" onsubmit="Wd()"><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()">
Back</button><button type="submit">Save</button><hr><h2>Time setup</h2> Back</button><button type="submit">Save</button><hr><h2>Time setup</h2>
Get time from NTP server: <input type="checkbox" name="NT"><br><input name="NS" Get time from NTP server: <input type="checkbox" name="NT"><br><input
maxlength="32"><br>Use 24h format: <input type="checkbox" name="CF"><br> type="text" name="NS" maxlength="32"><br>Use 24h format: <input type="checkbox"
Time zone: <select name="TZ"><option value="0" selected="selected">GMT(UTC) name="CF"><br>Time zone: <select name="TZ"><option value="0"
</option><option value="1">GMT/BST</option><option value="2">CET/CEST</option> selected="selected">GMT(UTC)</option><option value="1">GMT/BST</option><option
<option value="3">EET/EEST</option><option value="4">US-EST/EDT</option><option value="2">CET/CEST</option><option value="3">EET/EEST</option><option value="4">
value="5">US-CST/CDT</option><option value="6">US-MST/MDT</option><option US-EST/EDT</option><option value="5">US-CST/CDT</option><option value="6">
value="7">US-AZ</option><option value="8">US-PST/PDT</option><option value="9"> US-MST/MDT</option><option value="7">US-AZ</option><option value="8">US-PST/PDT
CST(AWST)</option><option value="10">JST(KST)</option><option value="11"> </option><option value="9">CST(AWST)</option><option value="10">JST(KST)
AEST/AEDT</option><option value="12">NZST/NZDT</option><option value="13"> </option><option value="11">AEST/AEDT</option><option value="12">NZST/NZDT
North Korea</option><option value="14">IST (India)</option><option value="15"> </option><option value="13">North Korea</option><option value="14">IST (India)
CA-Saskatchewan</option><option value="16">ACST</option><option value="17"> </option><option value="15">CA-Saskatchewan</option><option value="16">ACST
ACST/ACDT</option><option value="18">HST (Hawaii)</option><option value="19"> </option><option value="17">ACST/ACDT</option><option value="18">HST (Hawaii)
NOVT (Novosibirsk)</option><option value="20">AKST/AKDT (Anchorage)</option> </option><option value="19">NOVT (Novosibirsk)</option><option value="20">
<option value="21">MX-CST/CDT</option></select><br>UTC offset: <input name="UO" AKST/AKDT (Anchorage)</option><option value="21">MX-CST/CDT</option></select>
type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br> <br>UTC offset: <input name="UO" type="number" min="-65500" max="65500"
Current local time is <span class="times">unknown</span>.<br>Latitude: <select required> seconds (max. 18 hours)<br>Current local time is <span class="times">
name="LTR"><option value="1">N</option><option value="-1">S</option></select> unknown</span>.<br>Latitude: <select name="LTR"><option value="N">N</option>
<input name="LT" type="number" min="0" max="66.6" step="0.01"><br>Longitude: <option value="S">S</option></select><input name="LT" type="number" class="xl"
<select name="LNR"><option value="1">E</option><option value="-1">W</option> min="0" max="66.6" step="0.01"><br>Longitude: <select name="LNR"><option
</select><input name="LN" type="number" min="0" max="180" step="0.01"><div value="E">E</option><option value="W">W</option></select><input name="LN"
id="sun" class="times"></div><h3>Clock</h3>Clock Overlay: <select name="OL" type="number" class="xl" min="0" max="180" step="0.01"><div id="sun"
class="times"></div><h3>Clock</h3>Clock Overlay: <select name="OL"
onchange="Cs()"><option value="0" id="cn" selected="selected">None</option> onchange="Cs()"><option value="0" id="cn" selected="selected">None</option>
<option value="1" id="ca">Analog Clock</option><option value="2"> <option value="1" id="ca">Analog Clock</option><option value="2">
Single Digit Clock</option><option value="3" id="cc">Cronixie Clock</option> Single Digit Clock</option><option value="3" id="cc">Cronixie Clock</option>
@ -375,28 +376,30 @@ max="255" required><br>Show 5min marks: <input type="checkbox" name="O5"><br>
</div>Seconds (as trail): <input type="checkbox" name="OS"><br></div><div </div>Seconds (as trail): <input type="checkbox" name="OS"><br></div><div
id="ccc">Cronixie Display: <input name="CX" maxlength="6"><br> id="ccc">Cronixie Display: <input name="CX" maxlength="6"><br>
Cronixie Backlight: <input type="checkbox" name="CB"><br></div>Countdown Mode: Cronixie Backlight: <input type="checkbox" name="CB"><br></div>Countdown Mode:
<input type="checkbox" name="CE"><br>Countdown Goal:<br>Year: 20 <input <input type="checkbox" name="CE"><br>Countdown Goal:<br>Date:&nbsp;<nowrap>20
name="CY" type="number" min="0" max="99" required> Month: <input name="CI" <input name="CY" class="xs" type="number" min="0" max="99" required>-<input
type="number" min="1" max="12" required> Day: <input name="CD" type="number" name="CI" class="xs" type="number" min="1" max="12" required>-<input name="CD"
min="1" max="31" required><br>Hour: <input name="CH" type="number" min="0" class="xs" type="number" min="1" max="31" required></nowrap><br>Time:<nowrap>
max="23" required> Minute: <input name="CM" type="number" min="0" max="59" <input name="CH" class="xs" type="number" min="0" max="23" required>:<input
required> Second: <input name="CS" type="number" min="0" max="59" required><br> name="CM" class="xs" type="number" min="0" max="59" required>:<input name="CS"
<h3>Macro presets</h3><b>Macros have moved!</b><br><i> class="xs" type="number" min="0" max="59" required></nowrap><br><h3>
Macro presets</h3><b>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:
<input name="A0" type="number" min="0" max="250" required> <input name="A1" <input name="A0" class="m" type="number" min="0" max="250" required> <input
type="number" min="0" max="250" required><br>Countdown-Over Preset: <input name="A1" class="m" type="number" min="0" max="250" required><br>
name="MC" type="number" min="0" max="250" required><br> Countdown-Over Preset: <input name="MC" class="m" type="number" min="0"
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" max="250" required><br>Timed-Light-Over Presets: <input name="MN" class="m"
required><br><h3>Button actions</h3><table style="margin:0 auto" id="macros"> type="number" min="0" max="250" required><br><h3>Button actions</h3><table
<thead><tr><td>push<br>switch</td><td>short<br>on-&gt;off</td><td>long<br> style="margin:0 auto" id="macros"><thead><tr><td>push<br>switch</td><td>short
off-&gt;on</td><td>double<br>N/A</td></tr></thead><tbody></tbody></table><a <br>on-&gt;off</td><td>long<br>off-&gt;on</td><td>double<br>N/A</td></tr>
href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button" </thead><tbody></tbody></table><a
target="_blank">Analog Button setup</a><h3>Time-controlled presets</h3><div href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">
style="display:inline-block"><table id="TMT"></table></div><hr><button Analog Button setup</a><h3>Time-controlled presets</h3><div
type="button" onclick="B()">Back</button><button type="submit">Save</button> style="display:inline-block"><table id="TMT" style="min-width:330px"></table>
</form></body></html>)====="; </div><hr><button type="button" onclick="B()">Back</button><button
type="submit">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_sec.htm, do not edit!! // Autogenerated from wled00/data/settings_sec.htm, do not edit!!

File diff suppressed because it is too large Load Diff

View File

@ -318,6 +318,32 @@ byte weekdayMondayFirst()
return wd; return wd;
} }
bool isTodayInDateRange(byte monthStart, byte dayStart, byte monthEnd, byte dayEnd)
{
if (monthStart == 0 || dayStart == 0) return true;
if (monthEnd == 0) monthEnd = monthStart;
if (dayEnd == 0) dayEnd = 31;
byte d = day(localTime);
byte m = month(localTime);
if (monthStart < monthEnd) {
if (m > monthStart && m < monthEnd) return true;
if (m == monthStart) return (d >= dayStart);
if (m == monthEnd) return (d <= dayEnd);
return false;
}
if (monthEnd < monthStart) { //range spans change of year
if (m > monthStart || m < monthEnd) return true;
if (m == monthStart) return (d >= dayStart);
if (m == monthEnd) return (d <= dayEnd);
return false;
}
//start month and end month are the same
if (dayEnd < dayStart) return (m != monthStart || (d <= dayEnd || d >= dayStart)); //all year, except the designated days in this month
return (m == monthStart && d >= dayStart && d <= dayEnd); //just the designated days this month
}
void checkTimers() void checkTimers()
{ {
if (lastTimerMinute != minute(localTime)) //only check once a new minute begins if (lastTimerMinute != minute(localTime)) //only check once a new minute begins
@ -331,10 +357,12 @@ void checkTimers()
for (uint8_t i = 0; i < 8; i++) for (uint8_t i = 0; i < 8; i++)
{ {
if (timerMacro[i] != 0 if (timerMacro[i] != 0
&& (timerWeekday[i] & 0x01) //timer is enabled
&& (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour && (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour
&& timerMinutes[i] == minute(localTime) && timerMinutes[i] == minute(localTime)
&& (timerWeekday[i] & 0x01) //timer is enabled && ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01) //timer should activate at current day of week
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week && isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])
)
{ {
unloadPlaylist(); unloadPlaylist();
applyPreset(timerMacro[i]); applyPreset(timerMacro[i]);

View File

@ -107,7 +107,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
playlistRepeat = rep; playlistRepeat = rep;
if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start
playlistEndPreset = playlistObj[F("end")] | 0; playlistEndPreset = playlistObj["end"] | 0;
shuffle = shuffle || playlistObj["r"]; shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE; if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;

View File

@ -352,21 +352,27 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
char k[3]; k[2] = 0; char k[3]; k[2] = 0;
for (int i = 0; i<10; i++) for (int i = 0; i<10; i++) {
{ k[1] = i+48;//ascii 0,1,2,3,...
k[1] = i+48;//ascii 0,1,2,3
k[0] = 'H'; //timer hours k[0] = 'H'; //timer hours
timerHours[i] = request->arg(k).toInt(); timerHours[i] = request->arg(k).toInt();
k[0] = 'N'; //minutes k[0] = 'N'; //minutes
timerMinutes[i] = request->arg(k).toInt(); timerMinutes[i] = request->arg(k).toInt();
k[0] = 'T'; //macros k[0] = 'T'; //macros
timerMacro[i] = request->arg(k).toInt(); timerMacro[i] = request->arg(k).toInt();
k[0] = 'W'; //weekdays k[0] = 'W'; //weekdays
timerWeekday[i] = request->arg(k).toInt(); timerWeekday[i] = request->arg(k).toInt();
if (i<8) {
k[0] = 'M'; //start month
timerMonth[i] = request->arg(k).toInt() & 0x0F;
timerMonth[i] <<= 4;
k[0] = 'P'; //end month
timerMonth[i] += (request->arg(k).toInt() & 0x0F);
k[0] = 'D'; //start day
timerDay[i] = request->arg(k).toInt();
k[0] = 'E'; //end day
timerDayEnd[i] = request->arg(k).toInt();
}
} }
} }
@ -754,7 +760,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
strip.applyToAllSelected = true; strip.applyToAllSelected = true;
strip.setColor(2, t[0], t[1], t[2], t[3]); strip.setColor(2, t[0], t[1], t[2], t[3]);
} else { } else {
selseg.setColor(2,((t[0] << 16) + (t[1] << 8) + t[2] + (t[3] << 24)), selectedSeg); // defined above (SS=) selseg.setColor(2, RGBW32(t[0], t[1], t[2], t[3]), selectedSeg); // defined above (SS=)
} }
} }

View File

@ -497,13 +497,17 @@ WLED_GLOBAL byte dP[] _INIT_N(({ 255, 255, 255, 255, 255, 255 }));
WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L); WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L);
WLED_GLOBAL bool countdownOverTriggered _INIT(true); WLED_GLOBAL bool countdownOverTriggered _INIT(true);
// timer //timer
WLED_GLOBAL byte lastTimerMinute _INIT(0); WLED_GLOBAL byte lastTimerMinute _INIT(0);
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on //weekdays to activate on, bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }));
//upper 4 bits start, lower 4 bits end month (default 28: start month 1 and end month 12)
WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28}));
WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1}));
WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31}));
// blynk // blynk
WLED_GLOBAL bool blynkEnabled _INIT(false); WLED_GLOBAL bool blynkEnabled _INIT(false);

View File

@ -506,7 +506,7 @@ void getSettingsJS(byte subPage, char* dest)
memset(fpass,'*',l); memset(fpass,'*',l);
sappends('s',SET_F("MQPASS"),fpass); sappends('s',SET_F("MQPASS"),fpass);
sappends('s',SET_F("MQCID"),mqttClientID); sappends('s',SET_F("MQCID"),mqttClientID);
sappends('s',SET_F("MD"),mqttDeviceTopic); sappends('s',"MD",mqttDeviceTopic);
sappends('s',SET_F("MG"),mqttGroupTopic); sappends('s',SET_F("MG"),mqttGroupTopic);
sappend('c',SET_F("BM"),buttonPublishMqtt); sappend('c',SET_F("BM"),buttonPublishMqtt);
#endif #endif
@ -597,6 +597,12 @@ void getSettingsJS(byte subPage, char* dest)
k[0] = 'N'; sappend('v',k,timerMinutes[i]); k[0] = 'N'; sappend('v',k,timerMinutes[i]);
k[0] = 'T'; sappend('v',k,timerMacro[i]); k[0] = 'T'; sappend('v',k,timerMacro[i]);
k[0] = 'W'; sappend('v',k,timerWeekday[i]); k[0] = 'W'; sappend('v',k,timerWeekday[i]);
if (i<8) {
k[0] = 'M'; sappend('v',k,(timerMonth[i] >> 4) & 0x0F);
k[0] = 'P'; sappend('v',k,timerMonth[i] & 0x0F);
k[0] = 'D'; sappend('v',k,timerDay[i]);
k[0] = 'E'; sappend('v',k,timerDayEnd[i]);
}
} }
} }