You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

66 lines
2.5 KiB

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laundry status</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; background: #fff; color: #111; font-family: system-ui, sans-serif; }
main { max-width: 44rem; margin: 0 auto; padding: 3rem 1.25rem; text-align: center; }
h1 { margin: 1rem 0 2rem; font-size: clamp(3rem, 12vw, 5rem); line-height: 1.1; overflow-wrap: anywhere; }
.running { color: #b91c1c; }
.waiting, .incomplete, .unavailable { color: #666; }
.label { color: #666; font-size: 1rem; }
#readings { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.25rem; padding: 0; list-style: none; color: #000; font-size: 1rem; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
#readings li { min-width: 0; max-width: 100%; }
</style>
</head>
<body>
<main>
<p class="label">Laundry status</p>
<section aria-live="polite" aria-atomic="true">
<h1 id="status" class="waiting">Waiting for data...</h1>
<p class="label">Latest power readings · oldest first</p>
<ul id="readings" aria-label="Latest power readings"></ul>
</section>
<noscript>Enable JavaScript to see the current laundry status.</noscript>
</main>
<script>
const statusText = document.getElementById('status');
const readings = document.getElementById('readings');
let refreshing = false;
async function refresh() {
if (refreshing) return;
refreshing = true;
try {
const response = await fetch('/api/status', { cache: 'no-store', signal: AbortSignal.timeout(10000) });
if (!response.ok) throw new Error('Status request failed');
const data = await response.json();
const items = data.readings.map(reading => {
const item = document.createElement('li');
item.textContent = reading.valid ? reading.power.toFixed(2) + 'W' : '--';
return item;
});
statusText.textContent = data.message;
statusText.className = data.state;
readings.replaceChildren(...items);
} catch {
statusText.textContent = 'Status unavailable';
statusText.className = 'unavailable';
readings.replaceChildren();
} finally {
refreshing = false;
}
}
refresh();
setInterval(refresh, 60000);
if (typeof EventSource !== 'undefined') {
const events = new EventSource('/api/events');
events.addEventListener('update', refresh);
}
</script>
</body>
</html>