Welle 1 · Vorhersage „voll in etwa N Tagen“: Pool, NAS, Systemplatte, Container, VMs und die Platte der KI-Box

Aus dem Verlauf der Woche (Tagesmittel, Schutz gegen einmalige Sprünge, schneller werdendes Volllaufen zählt sofort):
gelb unter 14, rot unter 3 Tagen. Der Wächter meldet es, auch wenn der Füllstand selbst noch unauffällig ist;
Speicherkarte, Geräte-Tabelle, Instrumente der Übersicht und die KI-Box-Seite zeigen es an. Jeder Bericht des
Ausführers schreibt dafür die Belegungen in die Messreihe „speicher“.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 16:21:38 +02:00
co-authored by Claude Opus 5.5
parent 34360c9644
commit f7a9b8ad50
19 changed files with 691 additions and 72 deletions
@@ -15,6 +15,7 @@ export function Instrument({
unter,
beschriftung,
ariaText,
hinweis,
}: {
anteil: number | null
farbe?: string
@@ -23,6 +24,8 @@ export function Instrument({
unter?: string
beschriftung: string
ariaText: string
/** Eine Zeile unter der Beschriftung, etwa „voll in etwa 9 Tagen“ (seit 25.09.2026); className setzt die Farbe. */
hinweis?: { text: string; className: string } | null
}) {
const zeigen = anteil != null && anteil > 0.004
return (
@@ -46,6 +49,7 @@ export function Instrument({
)}
</svg>
<figcaption className="etikett text-[11.5px] tracking-[0.14em] text-text-3">{beschriftung}</figcaption>
{hinweis && <span className={`text-center text-xs ${hinweis.className}`}>{hinweis.text}</span>}
</figure>
)
}
@@ -2,6 +2,7 @@ import type { CSSProperties } from "react"
import { cn } from "cn"
import type { Bereich } from "@/lib/navigation"
import { type Einheit, wertText } from "@/lib/messung"
import { STUFE_FARBE, type VorhersageStufe } from "@/lib/prognose"
import type { Messpunkt } from "@/lib/typen"
import { Sparkline } from "./Sparkline"
@@ -26,7 +27,9 @@ function randpunkt(anteil: number, r: number) {
return { x: MITTE + r * Math.cos(winkel), y: MITTE + r * Math.sin(winkel) }
}
export function Messring({ label, wert, einheit, punkte, zustand = "ruhig", bereich, grenzen, max = 100, zeitraumText, i = 0 }: {
export function Messring({
label, wert, einheit, punkte, zustand = "ruhig", bereich, grenzen, max = 100, zeitraumText, i = 0, hinweis,
}: {
label: string
wert: number | null | undefined
einheit: Einheit
@@ -39,6 +42,8 @@ export function Messring({ label, wert, einheit, punkte, zustand = "ruhig", bere
zeitraumText: string
/** Reihenfolge für das Auftauchen. */
i?: number
/** Eine Zeile unter der Linie, etwa „voll in etwa 9 Tagen“ (seit 25.09.2026). */
hinweis?: { text: string; stufe: VorhersageStufe } | null
}) {
const z = ZUSTAND[zustand]
const farbe = zustand === "rot" ? "var(--rot)" : zustand === "gelb" ? "var(--bernstein)" : bereich === "box" ? "var(--box)" : "var(--lab)"
@@ -77,6 +82,7 @@ export function Messring({ label, wert, einheit, punkte, zustand = "ruhig", bere
? <Sparkline punkte={punkte} max={max} farbe={farbe} hoehe={20} beschriftung={`${label}, ${zeitraumText}`} />
: <span className="block h-5" aria-hidden />}
</div>
{hinweis && <span className={cn("text-center text-[11px] leading-tight", STUFE_FARBE[hinweis.stufe])}>{hinweis.text}</span>}
</div>
)
}
+12 -3
View File
@@ -4,6 +4,7 @@ import { Geraetesymbol } from "@/components/cockpit/Geraetesymbol"
import { geraetebild } from "@/lib/geraetebild"
import { artText, plattenAnteil } from "@/lib/homelab"
import { laufzeit } from "@/lib/messung"
import { knappeVorhersage, STUFE_FARBE } from "@/lib/prognose"
import type { GeraetMesswerte, ZielStand } from "@/lib/typen"
const SPALTEN = "md:grid-cols-[minmax(0,1.35fr)_110px_minmax(0,1fr)_minmax(0,1.2fr)_minmax(0,1fr)_90px]"
@@ -11,8 +12,14 @@ const SPALTEN = "md:grid-cols-[minmax(0,1.35fr)_110px_minmax(0,1fr)_minmax(0,1.2
const gb = (b: number) => (b / 1e9).toFixed(1).replace(".", ",")
/** Balken mit Wert (v5): ruhig in der Farbe des Homelabs, ab gelb bernstein, ab rot rot — der Text sagt es zusätzlich
* in Zahlen. */
function Balken({ wert, gelb, rot, zusatz }: { wert: number | null | undefined; gelb: number; rot: number; zusatz?: string }) {
* in Zahlen. Darunter auf Wunsch eine Zeile (bei der Platte: „voll in etwa 9 Tagen“, seit 25.09.2026). */
function Balken({ wert, gelb, rot, zusatz, unter }: {
wert: number | null | undefined
gelb: number
rot: number
zusatz?: string
unter?: { text: string; stufe: keyof typeof STUFE_FARBE } | null
}) {
if (wert === null || wert === undefined) return <span className="text-sm text-text-3">–</span>
const farbe = wert >= rot ? "var(--rot)" : wert >= gelb ? "var(--bernstein)" : "color-mix(in srgb, var(--lab) 80%, transparent)"
const text = wert >= rot ? "text-rot-text" : wert >= gelb ? "text-bernstein" : "text-text-2"
@@ -24,6 +31,7 @@ function Balken({ wert, gelb, rot, zusatz }: { wert: number | null | undefined;
<span className="h-1.5 w-full max-w-40 overflow-hidden rounded-full bg-white/[0.07]" aria-hidden>
<span className="block h-full rounded-full" style={{ width: `${Math.min(100, Math.max(2, wert))}%`, background: farbe }} />
</span>
{unter && <span className={cn("text-xs", STUFE_FARBE[unter.stufe])}>{unter.text}</span>}
</span>
)
}
@@ -43,6 +51,7 @@ function rang(z: ZielStand): number {
*/
export function Gesundheit({ ziele, messwerte }: { ziele: ZielStand[]; messwerte: GeraetMesswerte[] | undefined }) {
const mess = new Map((messwerte ?? []).map((g) => [g.id, g.aktuell]))
const prognose = new Map((messwerte ?? []).map((g) => [g.id, g.prognose?.platte]))
const sortiert = [...ziele].sort((a, b) => rang(a) - rang(b))
return (
<div role="table" aria-label="Zustand der Geräte" className="flex flex-col">
@@ -77,7 +86,7 @@ export function Gesundheit({ ziele, messwerte }: { ziele: ZielStand[]; messwerte
</span>
<span role="cell"><span className="text-xs text-text-3 md:hidden">CPU </span><Balken wert={a?.cpu} gelb={85} rot={95} /></span>
<span role="cell"><span className="text-xs text-text-3 md:hidden">Speicher </span><Balken wert={a?.ram} gelb={85} rot={95} zusatz={ram} /></span>
<span role="cell"><span className="text-xs text-text-3 md:hidden">Platte </span><Balken wert={platte} gelb={80} rot={90} /></span>
<span role="cell"><span className="text-xs text-text-3 md:hidden">Platte </span><Balken wert={platte} gelb={80} rot={90} unter={knappeVorhersage(prognose.get(z.id))} /></span>
<span role="cell" className="ziffern text-sm text-text-2"><span className="font-sans text-xs text-text-3 md:hidden">Läuft seit </span>{laufzeit(a?.uptime_s)}</span>
</div>
)
@@ -35,6 +35,21 @@ describe("Speicher", () => {
expect(screen.getByText(/Alle 8 Geräte gesichert/)).toBeInTheDocument()
})
it("sagt, wann es bei diesem Tempo voll ist (seit 25.09.2026)", () => {
render(<Speicher daten={{
...daten,
pools: [{ ...daten.pools[0], vorhersage: { tage: 9.4, rate: 5.4, anteil: 49, grund: null } }],
systemplatte: { ...daten.systemplatte!, vorhersage: { tage: null, rate: 0, anteil: 50, grund: "gleich" } },
nas: [{ ...daten.nas[0], vorhersage: { tage: null, rate: null, anteil: null, grund: "verlauf" } }],
sicherungen: { ...daten.sicherungen, ziele: [{ speicher: "pbs-qnap", art: "pbs", aktiv: true, belegt: 1_517_685_571_584,
gesamt: 3_847_503_609_856, vorhersage: { tage: 200, rate: 0.3, anteil: 39, grund: null } }] },
}} />)
const pool = screen.getByText("+5,4 % am Tag · voll in etwa 9 Tagen")
expect(pool).toHaveClass("text-bernstein")
expect(screen.getByText("Belegung gleichbleibend")).toBeInTheDocument()
expect(screen.getByText("Ziel pbs-qnap: 39 % belegt · voll in etwa 7 Monaten")).toHaveClass("text-text-3")
})
it("klappt die Vorschläge erst auf, wenn der Pool knapp wird", () => {
const { container, rerender } = render(<Speicher daten={daten} />)
const klappe = container.querySelector("details")!
+24 -1
View File
@@ -4,9 +4,11 @@ import { cn } from "cn"
import { Bestaetigen } from "@/components/cockpit/Bestaetigen"
import { Chip, type ChipArt } from "@/components/cockpit/Chip"
import { Button } from "@/components/ui/button"
import { STUFE_FARBE, tempoText, vollText, vorhersageStufe } from "@/lib/prognose"
import { auftraegeText, groesse, STUFE_TEXT } from "@/lib/speicher"
import type {
HomelabSpeicher, NasStand, SicherungsLage, SpeicherEmpfehlung, SpeicherPool, SpeicherStufe, Systemplatte, ZielAktion,
HomelabSpeicher, NasStand, SicherungsLage, SpeicherEmpfehlung, SpeicherPool, SpeicherStufe, Systemplatte, Vorhersage,
ZielAktion,
} from "@/lib/typen"
import { flugplanZeit } from "@/lib/zeit"
@@ -35,6 +37,15 @@ function Fuellstand({ anteil, stufe, name }: { anteil: number; stufe: SpeicherSt
)
}
/** Das Tempo der Belegung (seit 25.09.2026): „+1,2 % am Tag · voll in etwa 9 Tagen“, unter 14 Tagen bernstein, unter
* 3 Tagen rot. Ohne zwei Tage Verlauf steht nichts da. */
function Tempo({ v }: { v?: Vorhersage | null }) {
if (v?.grund === "gleich") return <p className="text-xs text-text-3">Belegung gleichbleibend</p>
const voll = vollText(v)
if (!voll) return null
return <p className={cn("ziffern text-xs", STUFE_FARBE[vorhersageStufe(v)])}>{tempoText(v)} · {voll}</p>
}
function Kachel({ titel, unter, chip, chipText, children }: {
titel: string
unter?: string
@@ -67,6 +78,7 @@ function PoolKachel({ p }: { p: SpeicherPool }) {
{p.meta_anteil !== null && (
<p className="text-xs text-text-3">Metadaten {Math.round(p.meta_anteil * 100)} % belegt</p>
)}
<Tempo v={p.vorhersage} />
</Kachel>
)
}
@@ -78,6 +90,7 @@ function SystemplatteKachel({ s }: { s: Systemplatte }) {
<p className="ziffern text-sm text-text-2">
{Math.round(s.anteil * 100)} % · {groesse(s.belegt)} von {groesse(s.gesamt)} · {groesse(s.frei)} frei
</p>
<Tempo v={s.vorhersage} />
</Kachel>
)
}
@@ -100,6 +113,7 @@ function NasKachel({ n }: { n: NasStand }) {
<p className="ziffern text-sm text-text-2">
{Math.round(n.anteil * 100)} % · {groesse(n.belegt)} von {groesse(n.gesamt)} · {groesse((n.gesamt ?? 0) - (n.belegt ?? 0))} frei
</p>
<Tempo v={n.vorhersage} />
</>
)
)}
@@ -131,6 +145,15 @@ function SicherungsKachel({ s }: { s: SicherungsLage }) {
))}
</ul>
)}
{s.ziele.filter((z) => z.gesamt).map((z) => {
const voll = vollText(z.vorhersage)
return (
<p key={z.speicher} className={cn("ziffern text-xs", voll ? STUFE_FARBE[vorhersageStufe(z.vorhersage)] : "text-text-3")}>
Ziel {z.speicher}: {Math.round(((z.belegt ?? 0) / (z.gesamt ?? 1)) * 100)} % belegt
{voll ? ` · ${voll}` : z.vorhersage?.grund === "gleich" ? " · gleichbleibend" : ""}
</p>
)
})}
</Kachel>
)
}
+24
View File
@@ -0,0 +1,24 @@
import { inTagen, knappeVorhersage, tempoText, vollText, vorhersageStufe } from "@/lib/prognose"
import type { Vorhersage } from "@/lib/typen"
const v = (tage: number | null, rate: number | null = 1.25): Vorhersage => ({ tage, rate, anteil: 70, grund: tage == null ? "gleich" : null })
it("nennt die Zeit bis voll wie das Backend", () => {
expect([0.4, 1.2, 9.4, 35, 200, 400].map(inTagen)).toEqual([
"in weniger als einem Tag", "in etwa einem Tag", "in etwa 9 Tagen", "in etwa 5 Wochen", "in etwa 7 Monaten",
"frühestens in einem Jahr",
])
})
it("stuft wie der Wächter: gelb unter 14, rot unter 3 Tagen", () => {
expect([v(2.9), v(3), v(13.9), v(14), v(null), null].map(vorhersageStufe)).toEqual(["rot", "gelb", "gelb", "ok", "ok", "ok"])
})
it("sagt nur etwas, wenn es etwas zu sagen gibt", () => {
expect(vollText(v(9.4))).toBe("voll in etwa 9 Tagen")
expect(vollText(v(null))).toBeNull()
expect(vollText(undefined)).toBeNull()
expect(tempoText(v(9.4))).toBe("+1,3 % am Tag")
expect(knappeVorhersage(v(9.4))).toEqual({ text: "voll in etwa 9 Tagen", stufe: "gelb" })
expect(knappeVorhersage(v(75))).toBeNull()
})
+51
View File
@@ -0,0 +1,51 @@
import type { Vorhersage } from "@/lib/typen"
// „Voll in etwa N Tagen“ (Ausbauplan Welle 1, seit 25.09.2026): Das Backend rechnet aus dem Verlauf der letzten Woche
// (kern/prognose.py), hier stehen nur die Worte. Dieselben Stufen wie beim Wächter: gelb unter 14, rot unter 3 Tagen.
export type VorhersageStufe = "ok" | "gelb" | "rot"
export const GELB_TAGE = 14
export const ROT_TAGE = 3
/** Knappe Stellen (Ringe, Geräte-Tabelle) nennen nur, was näher liegt — weiter weg wäre es Rauschen. */
export const KNAPP_TAGE = 60
/** „in weniger als einem Tag“, „in etwa 9 Tagen“, „in etwa 5 Wochen“ … — wie im Backend (prognose.in_tagen). */
export function inTagen(tage: number): string {
if (tage < 1) return "in weniger als einem Tag"
if (tage < 1.5) return "in etwa einem Tag"
if (tage < 21) return `in etwa ${Math.round(tage)} Tagen`
if (tage < 90) return `in etwa ${Math.round(tage / 7)} Wochen`
return tage < 365 ? `in etwa ${Math.round(tage / 30)} Monaten` : "frühestens in einem Jahr"
}
export function vorhersageStufe(v: Vorhersage | null | undefined): VorhersageStufe {
if (v?.tage == null) return "ok"
return v.tage < ROT_TAGE ? "rot" : v.tage < GELB_TAGE ? "gelb" : "ok"
}
/** „voll in etwa 9 Tagen“ — oder null, wenn es nichts zu sagen gibt (zu wenig Verlauf, gleichbleibend, weiter weg
* als bis Tage). */
export function vollText(v: Vorhersage | null | undefined, bis = Infinity): string | null {
if (v?.tage == null || v.tage >= bis) return null
return `voll ${inTagen(v.tage)}`
}
/** „+1,2 % am Tag“ */
export function tempoText(v: Vorhersage | null | undefined): string | null {
if (v?.rate == null) return null
return `+${v.rate.toFixed(1).replace(".", ",")} % am Tag`
}
/** Die Textfarbe zur Stufe: ruhig grau, dann bernstein, dann rot. */
export const STUFE_FARBE: Record<VorhersageStufe, string> = {
ok: "text-text-3",
gelb: "text-bernstein",
rot: "text-rot-text",
}
/** Für die knappen Stellen: {text: „voll in etwa 9 Tagen“, stufe} oder null (weiter weg als KNAPP_TAGE). */
export function knappeVorhersage(v: Vorhersage | null | undefined): { text: string; stufe: VorhersageStufe } | null {
const text = vollText(v, KNAPP_TAGE)
return text ? { text, stufe: vorhersageStufe(v) } : null
}
+27 -2
View File
@@ -61,7 +61,7 @@ export interface Start {
ram_total: number | null
temp_cpu: number | null
temp_gpu: number | null
platte: { used: number; total: number; percent: number } | null
platte: { used: number; total: number; percent: number; vorhersage?: Vorhersage | null } | null
uptime_s: number | null
}
updates: {
@@ -427,12 +427,25 @@ export type Zeitraum = "1h" | "24h" | "7d"
/** Ein Punkt einer Messreihe: [Unix-Sekunden des Schrittbeginns, Wert oder null bei einer Lücke]. */
export type Messpunkt = [number, number | null]
/** Vorhersage „voll in etwa N Tagen“ aus dem Verlauf der letzten Woche (kern/prognose.py, seit 25.09.2026). */
export interface Vorhersage {
/** Tage bis voll; null ohne Vorhersage (grund sagt, warum). */
tage: number | null
/** Zuwachs in Prozentpunkten am Tag. */
rate: number | null
/** Jüngstes Mittel in %. */
anteil: number | null
/** verlauf = weniger als zwei Tage Verlauf, gleich = wächst nicht. */
grund: "verlauf" | "gleich" | null
}
/** KI-Box: cpu, ram, gpu, platte in %, temp_cpu/temp_gpu in °C, netz_rx/netz_tx in Bytes/s, tokens je Minute. */
export interface BoxMesswerte {
zeitraum: Zeitraum
schritt_s: number
reihen: Partial<Record<"cpu" | "ram" | "gpu" | "temp_cpu" | "temp_gpu" | "platte" | "netz_rx" | "netz_tx" | "tokens", Messpunkt[]>>
aktuell: Partial<Record<string, number | null>>
prognose?: { platte?: Vorhersage | null }
}
export interface GeraetMesswerte {
@@ -452,6 +465,7 @@ export interface GeraetMesswerte {
load1?: number | null
uptime_s?: number | null
}
prognose?: { platte?: Vorhersage | null }
}
export interface HomelabMesswerte {
@@ -517,6 +531,8 @@ export interface SpeicherPool {
meta_anteil: number | null
aktiv: boolean
stufe: SpeicherStufe
/** Daten oder Metadaten, was früher voll ist. */
vorhersage?: Vorhersage | null
}
export interface NasStand {
@@ -532,11 +548,19 @@ export interface NasStand {
anteil: number | null
stufe: SpeicherStufe
problem: string | null
vorhersage?: Vorhersage | null
}
export interface SicherungsLage {
jobs: { zeitplan: string | null; speicher: string | null; alle: boolean }[]
ziele: { speicher: string; art: string | null; aktiv: boolean | null; belegt: number | null; gesamt: number | null }[]
ziele: {
speicher: string
art: string | null
aktiv: boolean | null
belegt: number | null
gesamt: number | null
vorhersage?: Vorhersage | null
}[]
erfasst: number
fehlend: { ziel: string; name: string; zuletzt: number | null }[]
zuletzt: number | null
@@ -562,6 +586,7 @@ export interface Systemplatte {
frei: number
anteil: number
stufe: SpeicherStufe
vorhersage?: Vorhersage | null
}
export interface HomelabSpeicher {
+3
View File
@@ -10,6 +10,7 @@ import { Zaehlwerk } from "@/components/cockpit/Zaehlwerk"
import { Button } from "@/components/ui/button"
import { useRadar, useStart } from "@/lib/abfragen"
import { RADAR_STATUS_TEXT, temperaturFarbe } from "@/lib/anzeige"
import { knappeVorhersage, STUFE_FARBE } from "@/lib/prognose"
import { useLetzteMetrik } from "@/lib/strom"
import type { Radar, Start } from "@/lib/typen"
import { gb, gbText } from "@/lib/zeit"
@@ -22,6 +23,7 @@ function Instrumente({ box }: { box: Start["box"] }) {
const temp = m?.temp_cpu ?? box.temp_cpu
const tempGpu = m?.temp_gpu ?? box.temp_gpu
const platte = box.platte
const vorhersage = knappeVorhersage(platte?.vorhersage)
const uptime = m?.uptime_s ?? box.uptime_s
const ramAnteil = ramBelegt != null && ramGesamt ? ramBelegt / ramGesamt : null
@@ -53,6 +55,7 @@ function Instrumente({ box }: { box: Start["box"] }) {
mitte={platte ? `${Math.round(platte.percent)}%` : "–"}
unter={platte ? `${gbText(platte.used)} / ${gbText(platte.total)}` : undefined}
ariaText={platte ? `Platte zu ${Math.round(platte.percent)} Prozent belegt` : "Platte unbekannt"}
hinweis={vorhersage ? { text: vorhersage.text, className: STUFE_FARBE[vorhersage.stufe] } : null}
/>
<Zaehlwerk sekunden={uptime} />
</section>
+8 -3
View File
@@ -17,6 +17,7 @@ import { boxbild, type Geraetebild, geraetebild, PUNKT, type Punktzustand } from
import { artText, kurzname } from "@/lib/homelab"
import { laufzeit, zustandBei } from "@/lib/messung"
import { type Bereich, useBereichStand, type Verbindung } from "@/lib/navigation"
import { knappeVorhersage, type VorhersageStufe } from "@/lib/prognose"
import { istSnapshotLauf } from "@/lib/snapshots"
import { groesse } from "@/lib/speicher"
import { useLetzteMetrik } from "@/lib/strom"
@@ -115,6 +116,7 @@ interface Messwert {
punkte?: Messpunkt[]
gelb: number
rot: number
hinweis?: { text: string; stufe: VorhersageStufe } | null
}
/** Vier Messringe eines Rechners unter seinem Namen. */
@@ -135,7 +137,8 @@ function Instrumentgruppe({ bereich, titel, unterzeile, werte, basis }: {
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
{werte.map((w, n) => (
<Messring key={w.label} label={w.label} wert={w.wert} einheit={w.einheit} punkte={w.punkte} bereich={bereich}
grenzen={[w.gelb, w.rot]} zustand={zustandBei(w.wert ?? null, w.gelb, w.rot)} zeitraumText="letzte Stunde" i={basis + n} />
grenzen={[w.gelb, w.rot]} zustand={zustandBei(w.wert ?? null, w.gelb, w.rot)} zeitraumText="letzte Stunde" i={basis + n}
hinweis={w.hinweis} />
))}
</div>
</div>
@@ -159,7 +162,8 @@ function Instrumente({ homelabAktiv }: { homelabAktiv: boolean }) {
{ label: "CPU", wert: live?.cpu ?? mess.data?.aktuell.cpu, einheit: "prozent", punkte: r?.cpu, gelb: 85, rot: 95 },
{ label: "Speicher", wert: ramProzent, einheit: "prozent", punkte: r?.ram, gelb: 85, rot: 95 },
{ label: "Temp.", wert: live?.temp_cpu ?? b?.temp_cpu, einheit: "grad", punkte: r?.temp_cpu, gelb: 80, rot: 90 },
{ label: "Platte", wert: live?.disk ?? b?.platte?.percent, einheit: "prozent", punkte: r?.platte, gelb: 80, rot: 90 },
{ label: "Platte", wert: live?.disk ?? b?.platte?.percent, einheit: "prozent", punkte: r?.platte, gelb: 80, rot: 90,
hinweis: knappeVorhersage(mess.data?.prognose?.platte ?? b?.platte?.vorhersage) },
]} />
{homelabAktiv ? (
<Instrumentgruppe bereich="homelab" titel="Proxmox-Host" basis={4}
@@ -168,7 +172,8 @@ function Instrumente({ homelabAktiv }: { homelabAktiv: boolean }) {
{ label: "CPU", wert: host?.aktuell.cpu, einheit: "prozent", punkte: host?.reihen.cpu, gelb: 85, rot: 95 },
{ label: "Speicher", wert: host?.aktuell.ram, einheit: "prozent", punkte: host?.reihen.ram, gelb: 90, rot: 95 },
{ label: "Temp.", wert: host?.aktuell.temp_cpu, einheit: "grad", punkte: host?.reihen.temp_cpu, gelb: 80, rot: 90 },
{ label: "Platte", wert: host?.aktuell.platte, einheit: "prozent", punkte: host?.reihen.platte, gelb: 80, rot: 90 },
{ label: "Platte", wert: host?.aktuell.platte, einheit: "prozent", punkte: host?.reihen.platte, gelb: 80, rot: 90,
hinweis: knappeVorhersage(host?.prognose?.platte) },
]} />
) : (
<p className="self-center text-sm text-text-2">Der Homelab-Teil antwortet gerade nicht; hier steht nichts Veraltetes.</p>