// 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);