diff --git a/packages/integrations/src/beszel/beszel-types.ts b/packages/integrations/src/beszel/beszel-types.ts index 95b551416..52643c701 100644 --- a/packages/integrations/src/beszel/beszel-types.ts +++ b/packages/integrations/src/beszel/beszel-types.ts @@ -10,27 +10,53 @@ export interface BeszelAuthResponse { export type BeszelSystemStatus = "up" | "down" | "paused" | "pending"; +/** + * Real-time system info snapshot from Beszel agent. + * Returned as `system.info` on each BeszelSystem record. + * All resource values are percentages or already-scaled summaries. + */ export interface BeszelSystemInfo { + /** hostname */ h?: string; + /** CPU usage (%) */ cpu: number; + /** temperature (°C), overall */ t?: number; + /** CPU core count */ c?: number; + /** CPU thread count */ ct?: number; + /** CPU model string */ m?: string; + /** load average [1m, 5m, 15m] */ la?: [number, number, number]; + /** OS identifier string */ o?: string; + /** uptime (seconds) */ u: number; + /** memory usage (%) */ mp: number; + /** disk usage (%) */ dp: number; + /** battery [level%, charging state] */ bat?: [number, number]; + /** bandwidth — public interface (Mbps, legacy). Use bb when available */ b?: number; + /** bandwidth — public interface (bytes/s, newer format). Preferred over b */ bb?: number; + /** agent version */ v: string; + /** podman mode */ p?: boolean; + /** GPU usage (%) */ g?: number; + /** disk temperature (°C) */ dt?: number; + /** OS type identifier */ os?: number; + /** extra filesystem usage (%) keyed by mount path */ efs?: Record; + /** systemd services [running, total] */ sv?: [number, number]; } @@ -62,62 +88,123 @@ export interface BeszelSystemDetails { updated: string; } +/** + * Historical system stats record from Beszel PocketBase. + * Stored per-interval (1m, 10m, 20m, 120m, 480m depending on time range). + * + * Units: + * - CPU/GPU/memory/disk percentages: 0–100 + * - Memory/disk absolute values (mu, du, etc.): bytes + * - Disk I/O (dr, dw): bytes/s + * - Network (ns, nr): bytes/s — all interfaces (including loopback, docker bridges) + * - Bandwidth (b): bytes/s [sent, recv] — public interfaces only. Prefer b over ns/nr for user-facing charts + */ export interface BeszelSystemStats { + /** CPU usage (%) */ cpu: number; + /** CPU usage max (%) */ cpum?: number; + /** per-core busy (%) */ cpub?: number[]; + /** per-core speed (MHz) */ cpus?: number[]; + /** load average [1m, 5m, 15m] */ la?: [number, number, number]; + /** total memory (bytes) */ m: number; + /** memory used (bytes) */ mu: number; + /** memory usage (%) */ mp: number; + /** memory buffer/cache (bytes) */ mb: number; + /** memory max used (bytes) */ mm?: number; + /** memory zfs arc (bytes) */ mz?: number; + /** total swap (bytes) */ s: number; + /** swap used (bytes) */ su: number; + /** total disk (bytes) */ d: number; + /** disk used (bytes) */ du: number; + /** disk usage (%) */ dp: number; + /** disk read (bytes/s) */ dr: number; + /** disk write (bytes/s) */ dw: number; + /** disk read max (bytes/s) */ drm?: number; + /** disk write max (bytes/s) */ dwm?: number; + /** disk IOPS [read, write] */ dio?: [number, number]; + /** disk IOPS max [read, write] */ diom?: [number, number]; + /** network sent — all interfaces (bytes/s) */ ns: number; + /** network received — all interfaces (bytes/s) */ nr: number; + /** bandwidth — public interfaces only (bytes/s) [sent, recv]. Prefer over ns/nr */ b?: [number, number]; + /** network sent max (bytes/s) */ nsm?: number; + /** network received max (bytes/s) */ nrm?: number; + /** bandwidth max [sent, recv] (bytes/s) */ bm?: [number, number]; + /** temperatures keyed by sensor name (°C) */ t?: Record; + /** extra filesystem stats keyed by mount path */ efs?: Record; + /** GPU stats keyed by device id */ g?: Record; + /** battery [level%, charging state] */ bat?: [number, number]; + /** network interfaces [rx bytes/s, tx bytes/s, rx max, tx max] */ ni?: Record; } export interface BeszelExtraFsStats { + /** total disk (bytes) */ d: number; + /** disk used (bytes) */ du: number; + /** read (bytes/s) */ r: number; + /** write (bytes/s) */ w: number; + /** read max (bytes/s) */ rm: number; + /** write max (bytes/s) */ wm: number; + /** read (IOPS) */ rb: number; + /** write (IOPS) */ wb: number; + /** read max (IOPS) */ rbm: number; + /** write max (IOPS) */ wbm: number; } export interface BeszelGPUData { + /** GPU name/model */ n: string; + /** memory used (bytes) */ mu?: number; + /** memory total (bytes) */ mt?: number; + /** GPU utilization (%) */ u: number; + /** power draw (W) */ p?: number; + /** power peak (W) */ pp?: number; + /** encoder utilization keyed by engine name (%) */ e?: Record; } @@ -144,12 +231,24 @@ export interface BeszelContainer { updated: number; } +/** + * Per-container resource stats from Beszel agent. + * One entry per container in a BeszelContainerStatsRecord. + * + * Network fields: b (bandwidth) is preferred when available; fall back to ns/nr. + */ export interface BeszelContainerStats { + /** container name */ n: string; + /** CPU usage (%) */ c: number; + /** memory usage (bytes) */ m: number; + /** network sent (bytes/s) — legacy, use b when available */ ns?: number; + /** network received (bytes/s) — legacy, use b when available */ nr?: number; + /** network bandwidth [sent, recv] (bytes/s) — preferred over ns/nr */ b?: [number, number]; } @@ -180,13 +279,21 @@ export interface BeszelSmartDevice { } export interface BeszelSmartAttribute { + /** attribute ID */ id?: number; + /** attribute name */ n: string; + /** current value */ v: number; + /** worst value */ w?: number; + /** threshold */ t?: number; + /** raw value (numeric) */ rv?: number; + /** raw value (string) */ rs?: string; + /** when failed */ wf?: string; } @@ -243,24 +350,40 @@ export interface PocketBaseListResponse { items: T[]; } +/** + * Normalized view model for a Beszel-monitored system. + * Constructed by mapToSystemRow() in request-handler from BeszelSystem + BeszelSystemDetails. + * This is the shape consumed by grid/table widgets — all values are pre-scaled for display. + */ export interface BeszelSystemRow { id: string; name: string; status: BeszelSystemStatus; + /** CPU usage (%) */ cpu: number; + /** memory usage (%) */ memory: number; + /** disk usage (%) */ disk: number; + /** GPU usage (%) */ gpu: number; + /** load average [1m, 5m, 15m] */ loadAvg: [number, number, number] | null; + /** public interface bandwidth (bytes/s). From info.bb or info.b * 1M */ netBytes: number; + /** temperature (°C) */ temp: number | null; + /** battery [level%, charging state] */ battery: [number, number] | null; + /** running systemd services count */ services: number; + /** uptime (seconds) */ uptime: number; agentVersion: string; hostname: string; cpuModel: string; cores: number; + /** total memory (bytes) */ memoryTotal: number; osName: string; } diff --git a/packages/request-handler/src/beszel.ts b/packages/request-handler/src/beszel.ts index ca99ed19d..20ec9f4bd 100644 --- a/packages/request-handler/src/beszel.ts +++ b/packages/request-handler/src/beszel.ts @@ -15,6 +15,15 @@ import { createCachedIntegrationRequestHandler } from "./lib/cached-integration- export type { BeszelSystemRow } from "@homarr/integrations/types"; +/** + * Maps raw Beszel API responses (BeszelSystem + BeszelSystemDetails) into a + * normalized BeszelSystemRow for widget consumption. + * + * BeszelSystemInfo uses short field names (see beszel-types.ts for full docs): + * cpu=CPU%, mp=memory%, dp=disk%, g=GPU%, la=loadAvg, u=uptime(s), v=version, + * bb=bandwidth(bytes/s), b=bandwidth(Mbps legacy), dt=disk temp, bat=battery, + * sv=[running,total] services, h=hostname, m=CPU model, c=cores, ct=threads + */ function mapToSystemRow(system: BeszelSystem, details: BeszelSystemDetails | null): BeszelSystemRow { const info = system.info; return { @@ -26,6 +35,7 @@ function mapToSystemRow(system: BeszelSystem, details: BeszelSystemDetails | nul disk: info.dp, gpu: info.g ?? 0, loadAvg: info.la ?? null, + // bb = bytes/s (newer), b = Mbps (legacy, multiply to get bytes/s) netBytes: info.bb ?? (info.b ?? 0) * 1_000_000, temp: info.dt ?? null, battery: info.bat ?? null, diff --git a/packages/widgets/src/beszel-system-stats/component.tsx b/packages/widgets/src/beszel-system-stats/component.tsx index 92863e418..59fb2b043 100644 --- a/packages/widgets/src/beszel-system-stats/component.tsx +++ b/packages/widgets/src/beszel-system-stats/component.tsx @@ -57,6 +57,8 @@ export default function BeszelSystemStatsWidget({ includeDocker: showDocker, }); + // BeszelSystemStats fields are documented in beszel-types.ts. + // mu/du = bytes, dr/dw = bytes/s, b = public bandwidth [sent,recv] bytes/s, ns/nr = all-interface bytes/s const BYTES_TO_GB = 1024 * 1024 * 1024; const cpuMap = useCallback((s: { cpu: number }) => ({ [t("chart.cpu.series")]: s.cpu }), [t]); @@ -75,6 +77,7 @@ export default function BeszelSystemStatsWidget({ }), [t], ); + // b = public interface bandwidth (preferred), ns/nr = all-interface traffic (fallback) const networkMap = useCallback( (s: { ns: number; nr: number; b?: [number, number] }) => ({ [t("chart.network.sent")]: s.b?.[0] ?? s.ns, diff --git a/packages/widgets/src/beszel/_shared/chart.tsx b/packages/widgets/src/beszel/_shared/chart.tsx index 89a63ea9a..cc2a75898 100644 --- a/packages/widgets/src/beszel/_shared/chart.tsx +++ b/packages/widgets/src/beszel/_shared/chart.tsx @@ -82,6 +82,7 @@ export const useContainerNames = (containerStats: BeszelContainerStatsRecord[] | type ContainerExtractor = (container: BeszelContainerStatsRecord["stats"][number] | undefined) => number; +// c = CPU (%), m = memory (bytes), b = bandwidth [sent,recv] (bytes/s), ns/nr = legacy net (bytes/s) const defaultContainerExtractors: Record = { cpu: (c) => c?.c ?? 0, memory: (c) => c?.m ?? 0,