114 lines
4.1 KiB
JavaScript
114 lines
4.1 KiB
JavaScript
// Fragt alle paar Sekunden die Messwerte vom Backend ab und zeigt sie an.
|
||
const API_URL = "/api/messwerte";
|
||
const ABFRAGE_INTERVALL_MS = 2000;
|
||
const VERALTET_NACH_MS = 10000;
|
||
const CO2_GRENZWERT_PPM = 1000; // Hard-Constraint laut Konzept (Standard 1000 ppm)
|
||
|
||
const el = (id) => document.getElementById(id);
|
||
|
||
function formatiere(wert, stellen) {
|
||
return wert === null || wert === undefined ? "–" : wert.toFixed(stellen);
|
||
}
|
||
|
||
function zeigeStatus(text, klasse) {
|
||
const status = el("status");
|
||
status.textContent = text;
|
||
status.className = "status " + klasse;
|
||
}
|
||
|
||
function zeigeWerte(aktuell, veraltet) {
|
||
el("wert-temperatur").textContent = formatiere(aktuell && aktuell.temperatur, 1);
|
||
el("wert-luftfeuchte").textContent = formatiere(aktuell && aktuell.luftfeuchte, 1);
|
||
el("wert-eco2").textContent = formatiere(aktuell && aktuell.eco2, 0);
|
||
el("wert-tvoc").textContent = formatiere(aktuell && aktuell.tvoc, 0);
|
||
|
||
for (const kachel of document.querySelectorAll(".kachel")) {
|
||
kachel.classList.toggle("veraltet", veraltet);
|
||
}
|
||
|
||
const co2Alarm = !veraltet && aktuell && aktuell.eco2 !== null && aktuell.eco2 >= CO2_GRENZWERT_PPM;
|
||
el("kachel-eco2").classList.toggle("alarm", Boolean(co2Alarm));
|
||
el("hinweis-eco2").hidden = !co2Alarm;
|
||
el("hinweis-eco2").textContent = `Grenzwert ${CO2_GRENZWERT_PPM} ppm erreicht – bitte lüften`;
|
||
}
|
||
|
||
function zeichneDiagramm(verlauf) {
|
||
const canvas = el("diagramm");
|
||
const ctx = canvas.getContext("2d");
|
||
const stil = getComputedStyle(document.documentElement);
|
||
const farbe = (name) => stil.getPropertyValue(name).trim();
|
||
const b = canvas.width;
|
||
const h = canvas.height;
|
||
ctx.clearRect(0, 0, b, h);
|
||
if (verlauf.length < 2) return;
|
||
|
||
// Temperatur (links) und CO2 (rechts) haben getrennte Skalen
|
||
const reihen = [
|
||
{ feld: "temperatur", farbe: farbe("--linie-temp"), einheit: "°C" },
|
||
{ feld: "eco2", farbe: farbe("--linie-co2"), einheit: "ppm" },
|
||
];
|
||
const rand = 8;
|
||
const text = farbe("--text-leise");
|
||
ctx.font = "12px system-ui, sans-serif";
|
||
|
||
reihen.forEach((reihe, index) => {
|
||
const werte = verlauf.map((e) => e[reihe.feld]);
|
||
const vorhanden = werte.filter((w) => w !== null);
|
||
if (vorhanden.length === 0) return;
|
||
let min = Math.min(...vorhanden);
|
||
let max = Math.max(...vorhanden);
|
||
if (max - min < 1) { min -= 1; max += 1; }
|
||
|
||
ctx.strokeStyle = reihe.farbe;
|
||
ctx.lineWidth = 2;
|
||
ctx.beginPath();
|
||
let gestartet = false;
|
||
werte.forEach((w, i) => {
|
||
if (w === null) { gestartet = false; return; }
|
||
const x = rand + (i / (werte.length - 1)) * (b - 2 * rand);
|
||
const y = h - rand - 16 - ((w - min) / (max - min)) * (h - 2 * rand - 32);
|
||
if (gestartet) ctx.lineTo(x, y); else ctx.moveTo(x, y);
|
||
gestartet = true;
|
||
});
|
||
ctx.stroke();
|
||
|
||
ctx.fillStyle = reihe.farbe;
|
||
ctx.textAlign = index === 0 ? "left" : "right";
|
||
ctx.fillText(`${reihe.feld === "eco2" ? "CO₂" : "Temperatur"} ${min.toFixed(0)}–${max.toFixed(0)} ${reihe.einheit}`,
|
||
index === 0 ? rand : b - rand, 14);
|
||
});
|
||
|
||
ctx.fillStyle = text;
|
||
ctx.textAlign = "center";
|
||
ctx.fillText(`${verlauf.length} Messungen`, b / 2, h - 4);
|
||
}
|
||
|
||
async function aktualisiere() {
|
||
try {
|
||
const antwort = await fetch(API_URL, { cache: "no-store" });
|
||
if (!antwort.ok) throw new Error("HTTP " + antwort.status);
|
||
const { aktuell, verlauf } = await antwort.json();
|
||
|
||
if (!aktuell) {
|
||
zeigeWerte(null, true);
|
||
zeigeStatus("Noch keine Daten vom Sensor empfangen", "status-warte");
|
||
return;
|
||
}
|
||
const alter = Date.now() - new Date(aktuell.zeit).getTime();
|
||
const veraltet = alter > VERALTET_NACH_MS;
|
||
zeigeWerte(aktuell, veraltet);
|
||
zeichneDiagramm(verlauf);
|
||
const uhrzeit = new Date(aktuell.zeit).toLocaleTimeString("de-DE");
|
||
zeigeStatus(
|
||
veraltet ? `Daten veraltet – letzte Messung um ${uhrzeit}` : `Live – letzte Messung um ${uhrzeit}`,
|
||
veraltet ? "status-veraltet" : "status-ok"
|
||
);
|
||
} catch (fehler) {
|
||
zeigeWerte(null, true);
|
||
zeigeStatus("Server nicht erreichbar", "status-veraltet");
|
||
}
|
||
}
|
||
|
||
aktualisiere();
|
||
setInterval(aktualisiere, ABFRAGE_INTERVALL_MS);
|