|
8 | 8 | <script> |
9 | 9 | var el=false; |
10 | 10 | var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; |
| 11 | + var maxTimePresets = 16; // Maximum number of time-controlled presets |
| 12 | + var currentPresetCount = 3; // Current number of presets (sunrise + sunset + 1 permanent time preset) |
11 | 13 | function S() { |
12 | 14 | getLoc(); |
13 | 15 | loadJS(getURL('/settings/s.js?p=5'), false, ()=>{BTa();}, ()=>{ |
|
27 | 29 | function BTa() |
28 | 30 | { |
29 | 31 | var ih="<thead><tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr></thead>"; |
30 | | - for (i=0;i<8;i++) { |
31 | | - ih+=`<tr><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td> |
32 | | -<td><input name="H${i}" class="xs" type="number" min="0" max="24"></td> |
33 | | -<td><input name="N${i}" class="xs" type="number" min="0" max="59"></td> |
| 32 | + // Generate sunrise preset (0) |
| 33 | + ih += generatePresetRow(0, true, "Sunrise"); |
| 34 | + // Generate sunset preset (1) |
| 35 | + ih += generatePresetRow(1, true, "Sunset"); |
| 36 | + // Generate one permanent time preset (2) |
| 37 | + ih += generatePresetRow(2, false, null, false); |
| 38 | + // Generate any additional dynamic presets (starting from 3) |
| 39 | + for (i=3; i<currentPresetCount; i++) { |
| 40 | + ih += generatePresetRow(i, false, null, true); |
| 41 | + } |
| 42 | + gId("TMT").innerHTML=ih; |
| 43 | + } |
| 44 | + |
| 45 | + function generatePresetRow(i, isSunEvent = false, sunType = null, isDynamic = false) { |
| 46 | + var ih = ""; |
| 47 | + var hourInput = isSunEvent ? |
| 48 | + `${sunType}<input name="H${i}" value="255" type="hidden">` : |
| 49 | + `<input name="H${i}" class="xs" type="number" min="0" max="24">`; |
| 50 | + |
| 51 | + ih += `<tr id="preset-row-${i}"><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td> |
| 52 | +<td>${hourInput}</td> |
| 53 | +<td><input name="N${i}" class="xs" type="number" min="${isSunEvent ? '-59' : '0'}" max="59"></td> |
34 | 54 | <td><input name="T${i}" class="s" type="number" min="0" max="250"></td> |
35 | 55 | <td><div id="CB${i}" onclick="expand(this,${i})" class="cal">📅</div></td></tr>`; |
36 | | - ih+=`<tr><td colspan=5><div id="WD${i}" style="display:none;background-color:#444;"><hr>Run on weekdays`; |
37 | | - ih+=`<table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>` |
38 | | - for (j=1;j<8;j++) ih+=`<td><input id="W${i}${j}" type="checkbox"></td>`; |
39 | | - ih+=`</tr></table>from <select name="M${i}">`; |
40 | | - for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`; |
41 | | - ih+=`</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to <select name="P${i}">`; |
42 | | - for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`; |
43 | | - ih+=`</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input> |
44 | | - <hr></div></td></tr>`; |
| 56 | + |
| 57 | + ih += `<tr id="preset-detail-${i}"><td colspan=5><div id="WD${i}" style="display:none;background-color:#444;"><hr>`; |
| 58 | + |
| 59 | + if (!isSunEvent) { |
| 60 | + ih += "Run on weekdays"; |
| 61 | + } |
| 62 | + |
| 63 | + ih += `<table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`; |
| 64 | + for (j=1;j<8;j++) ih += `<td><input id="W${i}${j}" type="checkbox"></td>`; |
| 65 | + ih += "</tr></table>"; |
| 66 | + |
| 67 | + if (!isSunEvent) { |
| 68 | + ih += `from <select name="M${i}">`; |
| 69 | + for (j=0;j<12;j++) ih += `<option value="${j+1}">${ms[j]}</option>`; |
| 70 | + ih += `</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to <select name="P${i}">`; |
| 71 | + for (j=0;j<12;j++) ih += `<option value="${j+1}">${ms[j]}</option>`; |
| 72 | + ih += `</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input>`; |
| 73 | + } |
| 74 | + |
| 75 | + ih += "<hr></div></td></tr>"; |
| 76 | + return ih; |
| 77 | + } |
| 78 | + |
| 79 | + function addTimePreset() { |
| 80 | + if (currentPresetCount >= maxTimePresets) { |
| 81 | + alert(`Maximum of ${maxTimePresets} time presets allowed.`); |
| 82 | + return; |
| 83 | + } |
| 84 | + |
| 85 | + var table = gId("TMT"); |
| 86 | + var newRowHTML = generatePresetRow(currentPresetCount, false, null, true); |
| 87 | + table.insertAdjacentHTML("beforeend", newRowHTML); |
| 88 | + |
| 89 | + currentPresetCount++; |
| 90 | + updateButtonStates(); |
| 91 | + } |
| 92 | + |
| 93 | + function removePreset(index) { |
| 94 | + if (index < 3) { |
| 95 | + alert("Cannot remove built-in presets (Sunrise, Sunset, and first time preset)."); |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + // Remove the preset rows |
| 100 | + var presetRow = gId(`preset-row-${index}`); |
| 101 | + var detailRow = gId(`preset-detail-${index}`); |
| 102 | + |
| 103 | + if (presetRow) presetRow.remove(); |
| 104 | + if (detailRow) detailRow.remove(); |
| 105 | + |
| 106 | + // Reindex remaining presets |
| 107 | + reindexPresets(); |
| 108 | + currentPresetCount--; |
| 109 | + updateButtonStates(); |
| 110 | + } |
| 111 | + |
| 112 | + function removeLastPreset() { |
| 113 | + if (currentPresetCount <= 3) { |
| 114 | + alert("Cannot remove built-in presets. Only dynamic presets (3+) can be removed."); |
| 115 | + return; |
| 116 | + } |
| 117 | + |
| 118 | + // Find the highest index dynamic preset and remove it |
| 119 | + var lastIndex = currentPresetCount - 1; |
| 120 | + removePreset(lastIndex); |
| 121 | + } |
| 122 | + |
| 123 | + function reindexPresets() { |
| 124 | + var table = gId("TMT"); |
| 125 | + var rows = table.querySelectorAll("tr[id^='preset-row-']"); |
| 126 | + var newIndex = 3; |
| 127 | + |
| 128 | + rows.forEach((row) => { |
| 129 | + var oldIndex = row.id.split('-')[2]; |
| 130 | + if (parseInt(oldIndex) >= 3) { |
| 131 | + updatePresetRowIndex(row, oldIndex, newIndex); |
| 132 | + var detailRow = gId(`preset-detail-${oldIndex}`); |
| 133 | + if (detailRow) { |
| 134 | + updatePresetDetailRowIndex(detailRow, oldIndex, newIndex); |
| 135 | + } |
| 136 | + newIndex++; |
| 137 | + } |
| 138 | + }); |
| 139 | + } |
| 140 | + |
| 141 | + function updatePresetRowIndex(row, oldIndex, newIndex) { |
| 142 | + row.id = `preset-row-${newIndex}`; |
| 143 | + row.innerHTML = row.innerHTML |
| 144 | + .replace(new RegExp(`name="W${oldIndex}"`, 'g'), `name="W${newIndex}"`) |
| 145 | + .replace(new RegExp(`id="W${oldIndex}`, 'g'), `id="W${newIndex}`) |
| 146 | + .replace(new RegExp(`name="H${oldIndex}"`, 'g'), `name="H${newIndex}"`) |
| 147 | + .replace(new RegExp(`name="N${oldIndex}"`, 'g'), `name="N${newIndex}"`) |
| 148 | + .replace(new RegExp(`name="T${oldIndex}"`, 'g'), `name="T${newIndex}"`) |
| 149 | + .replace(new RegExp(`id="CB${oldIndex}"`, 'g'), `id="CB${newIndex}"`) |
| 150 | + .replace(new RegExp(`expand\(this,${oldIndex}\)`, 'g'), `expand(this,${newIndex})`) |
| 151 | + .replace(new RegExp(`removePreset\(${oldIndex}\)`, 'g'), `removePreset(${newIndex})`); |
| 152 | + } |
| 153 | + |
| 154 | + function updatePresetDetailRowIndex(row, oldIndex, newIndex) { |
| 155 | + row.id = `preset-detail-${newIndex}`; |
| 156 | + row.innerHTML = row.innerHTML |
| 157 | + .replace(new RegExp(`id="WD${oldIndex}"`, 'g'), `id="WD${newIndex}"`) |
| 158 | + .replace(new RegExp(`id="W${oldIndex}`, 'g'), `id="W${newIndex}`) |
| 159 | + .replace(new RegExp(`name="M${oldIndex}"`, 'g'), `name="M${newIndex}"`) |
| 160 | + .replace(new RegExp(`name="D${oldIndex}"`, 'g'), `name="D${newIndex}"`) |
| 161 | + .replace(new RegExp(`name="P${oldIndex}"`, 'g'), `name="P${newIndex}"`) |
| 162 | + .replace(new RegExp(`name="E${oldIndex}"`, 'g'), `name="E${newIndex}"`); |
| 163 | + } |
| 164 | + |
| 165 | + function updateButtonStates() { |
| 166 | + var addBtn = gId("addPresetBtn"); |
| 167 | + var removeBtn = gId("removePresetBtn"); |
| 168 | + |
| 169 | + if (addBtn) { |
| 170 | + addBtn.disabled = currentPresetCount >= maxTimePresets; |
| 171 | + } |
| 172 | + |
| 173 | + if (removeBtn) { |
| 174 | + removeBtn.disabled = currentPresetCount <= 3; |
45 | 175 | } |
46 | | - ih+=`<tr><td><input name="W8" id="W8" type="hidden"><input id="W80" type="checkbox"></td> |
47 | | -<td>Sunrise<input name="H8" value="255" type="hidden"></td> |
48 | | -<td><input name="N8" class="xs" type="number" min="-59" max="59"></td> |
49 | | -<td><input name="T8" class="s" type="number" min="0" max="250"></td> |
50 | | -<td><div id="CB8" onclick="expand(this,8)" class="cal">📅</div></td></tr><tr><td colspan=5>`; |
51 | | - ih+=`<div id="WD8" style="display:none;background-color:#444;"><hr><table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`; |
52 | | - for (j=1;j<8;j++) ih+=`<td><input id="W8${j}" type="checkbox"></td>`; |
53 | | - ih+="</tr></table><hr></div></td></tr>"; |
54 | | - ih+=`<tr><td><input name="W9" id="W9" type="hidden"><input id="W90" type="checkbox"></td> |
55 | | -<td>Sunset<input name="H9" value="255" type="hidden"></td> |
56 | | -<td><input name="N9" class="xs" type="number" min="-59" max="59"></td> |
57 | | -<td><input name="T9" class="s" type="number" min="0" max="250"></td> |
58 | | -<td><div id="CB9" onclick="expand(this,9)" class="cal">📅</div></td></tr><tr><td colspan=5>`; |
59 | | - ih+=`<div id="WD9" style="display:none;background-color:#444;"><hr><table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`; |
60 | | - for (j=1;j<8;j++) ih+=`<td><input id="W9${j}" type="checkbox"></td>`; |
61 | | - ih+="</tr></table><hr></div></td></tr>"; |
62 | | - gId("TMT").innerHTML=ih; |
63 | 176 | } |
64 | 177 | function FC() |
65 | 178 | { |
66 | | - for(i=0;i<10;i++) |
| 179 | + for(i=0;i<currentPresetCount;i++) |
67 | 180 | { |
68 | | - let wd = gId("W"+i).value; |
| 181 | + let wd = gId("W"+i); |
| 182 | + if (!wd) continue; // Skip if preset doesn't exist |
| 183 | + wd = wd.value; |
69 | 184 | for(j=0;j<8;j++) { |
70 | | - gId("W"+i+j).checked=wd>>j&1; |
| 185 | + let checkbox = gId("W"+i+j); |
| 186 | + if (checkbox) checkbox.checked=wd>>j&1; |
71 | 187 | } |
72 | | - if ((wd&254) != 254 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) { |
73 | | - expand(gId("CB"+i),i); //expand macros with custom DOW or date range set |
| 188 | + if ((wd&254) != 254 || (i<8 && (gN("M"+i) && gN("D"+i) && gN("P"+i) && gN("E"+i) && |
| 189 | + (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31)))) { |
| 190 | + let cbElement = gId("CB"+i); |
| 191 | + if (cbElement) expand(cbElement,i); //expand macros with custom DOW or date range set |
74 | 192 | } |
75 | 193 | } |
| 194 | + updateAddButtonState(); |
76 | 195 | } |
77 | 196 | function Wd() |
78 | 197 | { |
79 | | - a = [0,0,0,0,0,0,0,0,0,0]; |
80 | | - for (i=0; i<10; i++) { |
| 198 | + a = []; |
| 199 | + for (i=0; i<currentPresetCount; i++) { |
| 200 | + a[i] = 0; |
81 | 201 | m=1; |
82 | | - for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;} |
83 | | - gId("W"+i).value=a[i]; |
| 202 | + for (j=0; j<8; j++) { |
| 203 | + let checkbox = gId("W"+i+j); |
| 204 | + if (checkbox && checkbox.checked) a[i]+=m; |
| 205 | + m*=2; |
| 206 | + } |
| 207 | + let hiddenInput = gId("W"+i); |
| 208 | + if (hiddenInput) hiddenInput.value = a[i]; |
84 | 209 | } |
85 | 210 | if (d.Sf.LTR.value==="S") { d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } |
86 | 211 | if (d.Sf.LNR.value==="W") { d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } |
@@ -207,7 +332,10 @@ <h3>Button actions</h3> |
207 | 332 | <a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a> |
208 | 333 | <h3>Time-controlled presets</h3> |
209 | 334 | <div style="display: inline-block"> |
210 | | - <table id="TMT" style="min-width:330px;"></table> |
| 335 | + <table id="TMT" style="min-width:380px; margin: 0 auto;"></table> |
| 336 | + <br> |
| 337 | + <button type="button" id="addPresetBtn" onclick="addTimePreset()">+</button> |
| 338 | + <button type="button" id="removePresetBtn" onclick="removeLastPreset()" style="margin-left: 5px;">-</button> |
211 | 339 | </div> |
212 | 340 | <hr> |
213 | 341 | <button type="button" onclick="B()">Back</button><button type="submit">Save</button> |
|
0 commit comments