Files
2026-10-20 19:41:43 +02:00

119 lines
4.1 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CPU Stats</title>
<style>
body { font-family: system-ui, sans-serif; background: #111; color: #eee; margin: 2em; }
h1 { font-weight: 500; }
canvas { background: #1a1a1a; border-radius: 8px; display: block; }
.legend span { margin-right: 1.5em; }
.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; }
#now { font-size: 1.2em; color: #9cf; }
.ranges { margin: 1em 0; }
.ranges button {
font: inherit; color: #eee; background: #222; border: 1px solid #444;
border-radius: 6px; padding: 6px 14px; cursor: pointer; margin-right: 6px;
}
.ranges button.active { background: #356; border-color: #57f; }
</style>
</head>
<body>
<h1>CPU Stats <span id="now"></span></h1>
<div class="legend">
<span><span class="dot" style="background:#4cf"></span>Usage %</span>
<span><span class="dot" style="background:#fc4"></span>Temp °C</span>
</div>
<div class="ranges">
<button data-range="minute">1 min</button>
<button data-range="hour">1 h</button>
<button data-range="day">24 h</button>
<button data-range="week">7 days</button>
</div>
<canvas id="chart" width="800" height="320"></canvas>
<script>
// name -> span in seconds (must match app.py RANGES)
const SPANS = { minute: 60, hour: 3600, day: 86400, week: 7 * 86400 };
// How often to reload the chart for each range
const RELOAD_MS = { minute: 1000, hour: 5000, day: 30000, week: 60000 };
const state = { range: 'minute', points: [] };
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
const nowEl = document.getElementById('now');
const rangeButtons = document.querySelectorAll('.ranges button');
function setRange(range) {
state.range = range;
rangeButtons.forEach(b => b.classList.toggle('active', b.dataset.range === range));
setRangeButtonsEnabled(false); // while loading first data
if (setRange._timer) clearInterval(setRange._timer);
setRange._timer = setInterval(load, RELOAD_MS[range]);
load();
}
function setRangeButtonsEnabled(enabled) {
rangeButtons.forEach(b => b.disabled = !enabled);
}
async function load() {
try {
const { points } = await (await fetch('/api/history?range=' + state.range)).json();
state.points = points;
setRangeButtonsEnabled(true);
const last = points[points.length - 1];
if (last && last.u != null) {
nowEl.textContent = `- ${last.u.toFixed(0)}% / ${
last.c == null ? '?' : last.c.toFixed(1)
}°C`;
} else {
nowEl.textContent = last ? '—' : ' (no data yet)';
}
draw();
} catch (err) {
nowEl.textContent = '(waiting for server…)';
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// grid
ctx.strokeStyle = '#333';
for (let i = 1; i < 4; i++) {
const y = (canvas.height / 4) * i;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
drawLine(p => p.u, v => v / 100, '#4cf');
drawLine(p => p.c, v => (v == null ? null : v / 100), '#fc4'); // scale 0–100 °C
}
// x axis: fixed window ending "now", so older data scrolls off the left
function drawLine(get, scale, color) {
const span = SPANS[state.range];
const t1 = Date.now() / 1000, t0 = t1 - span;
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
let started = false;
state.points.forEach(p => {
const v = get(p);
const s = v == null ? null : scale(v);
if (s == null) return;
const x = (p.t - t0) / span * (canvas.width - 10) + 5;
const y = canvas.height - s * (canvas.height - 10) - 5;
if (!started) { ctx.moveTo(x, y); started = true; }
else ctx.lineTo(x, y);
});
ctx.stroke();
}
rangeButtons.forEach(b => b.addEventListener('click', () => setRange(b.dataset.range)));
setRange('minute');
</script>
</body>
</html>