Public Access
Stopwatch counts in real time from a start date, kept over a restart
The stopwatch counted with millis(), so it started at 0 after every restart and would overflow after 49 days. It now keeps the real start time (UTC ms) and the time counted before a pause, stored in NVS (system/stopwatch_store) on every change, so it keeps counting while the display is off or restarting. Stop pauses, Start continues, and the Stopwatch tab can set the start to a date and time (local, summer time by mktime) to count from, e.g. days since an event. Start, stop and set need the synced time. New page formats: 12d 4:05:09 and 3w 2d. Version 2.19.0, tested on the display over OTA (set start, future and invalid dates refused, pause, restart). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f5e77c7eca
commit
0166d0ee46
20 files changed
+278
-67
No files matched your search
@@ -108,9 +108,11 @@ details{margin-top:10px}summary{cursor:pointer;color:var(--dim);font-size:13px;m
|
||||
|
||||
<section id="t-stopwatch"><div class="card"><h2>Stopwatch</h2>
|
||||
<div class="row"><b id="swtime" style="font-size:32px;font-variant-numeric:tabular-nums">0:00</b></div>
|
||||
<div class="row"><span id="swsince" class="hint"></span></div>
|
||||
<div class="row"><button class="b" onclick="stopwatch('start')">Start</button><button class="s" onclick="stopwatch('stop')">Stop</button>
|
||||
<button class="s" onclick="stopwatch('reset')">Reset</button></div>
|
||||
<p class="hint">Shown by pages with a stopwatch element (e.g. the Stopwatch page). Also in Home Assistant: the Stopwatch switch and the Stopwatch reset button.</p></div></section>
|
||||
<div class="row"><label>Counting since <input type="datetime-local" id="swstart"></label><button class="s" onclick="swSetStart()">Set start</button></div>
|
||||
<p class="hint">Shown by pages with a stopwatch element (e.g. the Stopwatch page). It counts in real time from its start, also while the display is off or restarting. Stop pauses it, Start continues. Set start: counts from that date and time on (e.g. days since an event). Also in Home Assistant: the Stopwatch switch and the Stopwatch reset button.</p></div></section>
|
||||
|
||||
<section id="t-bitmaps"><div class="card"><h2>Bitmaps</h2><div class="grid" id="bitmaps"></div>
|
||||
<details id="bitmaps_used_box"><summary></summary><div class="grid" id="bitmaps_used"></div></details>
|
||||
@@ -315,9 +317,10 @@ async function toBitmap(n){await post('/api/icon/to_bitmap',{name:n});msg(n+' is
|
||||
async function resetPage(n){if(!confirm('Reset '+n+' to the original page? Your changes to it are lost.'))return;await post('/api/pages/delete',{name:n});msg(n+' reset');if(pe&&same(pe.orig,n))peClose();loadLists()}
|
||||
async function delPage(n){if(!confirm('Delete page '+n+'?'))return;await post('/api/pages/delete',{name:n});msg('Deleted');if(pe&&same(pe.orig,n))peClose();settings=await api('/api/settings');loadLists()}
|
||||
async function delFont(n){if(!confirm('Delete font '+n+'?'))return;await post('/api/fonts/delete',{name:n});msg('Deleted');loadLists()}
|
||||
function swText(ms){const s=Math.floor(ms/1000),h=Math.floor(s/3600),m=Math.floor(s%3600/60),p=n=>String(n).padStart(2,'0');return(h?h+':'+p(m):m)+':'+p(s%60)}
|
||||
function swShow(sw){if(sw)$('swtime').textContent=swText(sw.ms)+(sw.running?' (running)':'')}
|
||||
async function stopwatch(action){swShow(await post('/api/stopwatch',{action}));if(pe)pePreview()}
|
||||
function swText(ms){const s=Math.floor(ms/1000),d=Math.floor(s/86400),h=Math.floor(s%86400/3600),m=Math.floor(s%3600/60),p=n=>String(n).padStart(2,'0');return(d?d+'d '+h+':'+p(m):h?h+':'+p(m):m)+':'+p(s%60)}
|
||||
function swShow(sw){if(!sw)return;$('swtime').textContent=swText(sw.ms)+(sw.running?' (running)':'');$('swsince').textContent=sw.start?'Counting since '+sw.start.replace('T',' '):sw.ms?'Paused':''}
|
||||
async function stopwatch(action,extra){swShow(await post('/api/stopwatch',Object.assign({action},extra)));if(pe)pePreview()}
|
||||
async function swSetStart(){const v=$('swstart').value;if(!v){msg('Pick a date and time first',true);return}await stopwatch('set',{start:v.slice(0,16)});msg('Counting since '+v.replace('T',' '))}
|
||||
async function live(bits){await post('/api/live',{data:toHex(bits)});msg('Shown on the display')}
|
||||
async function stopLive(){await post('/api/live/stop');msg('Back to normal')}
|
||||
// schedule and rules
|
||||
@@ -428,7 +431,7 @@ function peElems(){const d=$('pe_elems');d.innerHTML='';d.append(mk('datalist',{
|
||||
if(e.type=='countdown'){row(field('Until',inp(e,'target','datetime-local')),field('Format',sel(e,'format',CDF.map((x,k)=>[k,x[e.language=='en'?1:0]]))),
|
||||
field('Language',sel(e,'language',[['nl','Nederlands'],['en','English']],peElems)));
|
||||
row(field('Before',inp(e,'text','text',{size:10,maxLength:32})),field('When passed',inp(e,'suffix','text',{size:8,maxLength:16,placeholder:'0'})))}
|
||||
if(e.type=='stopwatch'){row(field('Format',sel(e,'format',[[0,'4:05 (every second)'],[1,'0:04 (hours, minutes)'],[2,'0:04:05 (every second)']])),
|
||||
if(e.type=='stopwatch'){row(field('Format',sel(e,'format',[[0,'4:05 (every second)'],[1,'0:04 (hours, minutes)'],[2,'0:04:05 (every second)'],[3,'12d 4:05:09 (days, every second)'],[4,'3w 2d (weeks, days)']])),
|
||||
field('Before',inp(e,'text','text',{size:8,maxLength:32})),field('After',inp(e,'suffix','text',{size:6,maxLength:16})));
|
||||
row(btn('Start',()=>stopwatch('start')),btn('Stop',()=>stopwatch('stop')),btn('Reset',()=>stopwatch('reset')))}
|
||||
if(e.type=='image'){const name=inp(e,'text','text',{size:20,maxLength:24,placeholder:e.entity?'weather ':'name'});name.setAttribute('list','bmnames');
|
||||
|
||||
Reference in new issue
Block a user