From e6054058b9d23993207ece3dd2d5c1002757be5b Mon Sep 17 00:00:00 2001 From: henrygd Date: Fri, 7 Feb 2025 21:27:15 -0500 Subject: [PATCH] feat: add temperatures to dashboard - Refactor temperature related code and move to standalone function --- beszel/internal/agent/system.go | 83 ++++++++++++------- beszel/internal/entities/system/system.go | 2 + .../systems-table/systems-table.tsx | 26 +++++- beszel/site/src/types.d.ts | 4 + 4 files changed, 82 insertions(+), 33 deletions(-) diff --git a/beszel/internal/agent/system.go b/beszel/internal/agent/system.go index ed7a208..baa189c 100644 --- a/beszel/internal/agent/system.go +++ b/beszel/internal/agent/system.go @@ -178,38 +178,9 @@ func (a *Agent) getSystemStats() system.Stats { } // temperatures (skip if sensors whitelist is set to empty string) - if a.sensorsWhitelist != nil && len(a.sensorsWhitelist) == 0 { - slog.Debug("Skipping temperature collection") - } else { - temps, err := sensors.TemperaturesWithContext(a.sensorsContext) - if err != nil { - slog.Debug("Sensor error", "err", err) - } - slog.Debug("Temperature", "sensors", temps) - if len(temps) > 0 { - systemStats.Temperatures = make(map[string]float64, len(temps)) - for i, sensor := range temps { - // skip if temperature is 0 - if sensor.Temperature <= 0 || sensor.Temperature >= 200 { - continue - } - if _, ok := systemStats.Temperatures[sensor.SensorKey]; ok { - // if key already exists, append int to key - systemStats.Temperatures[sensor.SensorKey+"_"+strconv.Itoa(i)] = twoDecimals(sensor.Temperature) - } else { - systemStats.Temperatures[sensor.SensorKey] = twoDecimals(sensor.Temperature) - } - } - // remove sensors from systemStats if whitelist exists and sensor is not in whitelist - // (do this here instead of in initial loop so we have correct keys if int was appended) - if a.sensorsWhitelist != nil { - for key := range systemStats.Temperatures { - if _, nameInWhitelist := a.sensorsWhitelist[key]; !nameInWhitelist { - delete(systemStats.Temperatures, key) - } - } - } - } + err = a.updateTemperatures(&systemStats) + if err != nil { + slog.Error("Error getting temperatures", "err", err) } // GPU data @@ -239,6 +210,54 @@ func (a *Agent) getSystemStats() system.Stats { return systemStats } +func (a *Agent) updateTemperatures(systemStats *system.Stats) error { + // skip if sensors whitelist is set to empty string + if a.sensorsWhitelist != nil && len(a.sensorsWhitelist) == 0 { + slog.Debug("Skipping temperature collection") + return nil + } + + // reset high temp + a.systemInfo.HighTemp = 0 + + // get sensor data + temps, err := sensors.TemperaturesWithContext(a.sensorsContext) + if err != nil { + return err + } + slog.Debug("Temperature", "sensors", temps) + + // return if no sensors + if len(temps) == 0 { + return nil + } + + systemStats.Temperatures = make(map[string]float64, len(temps)) + for i, sensor := range temps { + // skip if temperature is unreasonable + if sensor.Temperature <= 0 || sensor.Temperature >= 200 { + continue + } + sensorName := sensor.SensorKey + if _, ok := systemStats.Temperatures[sensorName]; ok { + // if key already exists, append int to key + sensorName = sensorName + "_" + strconv.Itoa(i) + } + // skip if not in whitelist + if a.sensorsWhitelist != nil { + if _, nameInWhitelist := a.sensorsWhitelist[sensorName]; !nameInWhitelist { + continue + } + } + // assign high temperature if sensor temp is higher than a.systemInfo.HighTemp + if sensor.Temperature > a.systemInfo.HighTemp { + a.systemInfo.HighTemp = sensor.Temperature + } + systemStats.Temperatures[sensorName] = twoDecimals(sensor.Temperature) + } + return nil +} + // Returns the size of the ZFS ARC memory cache in bytes func getARCSize() (uint64, error) { file, err := os.Open("/proc/spl/kstat/zfs/arcstats") diff --git a/beszel/internal/entities/system/system.go b/beszel/internal/entities/system/system.go index 3cabcce..e1ef29e 100644 --- a/beszel/internal/entities/system/system.go +++ b/beszel/internal/entities/system/system.go @@ -75,6 +75,8 @@ type Info struct { Bandwidth float64 `json:"b"` AgentVersion string `json:"v"` Podman bool `json:"p,omitempty"` + Gpu float64 `json:"g,omitempty"` + HighTemp float64 `json:"ht,omitempty"` } // Final data structure to return to the hub diff --git a/beszel/site/src/components/systems-table/systems-table.tsx b/beszel/site/src/components/systems-table/systems-table.tsx index 9ee2d95..28f883b 100644 --- a/beszel/site/src/components/systems-table/systems-table.tsx +++ b/beszel/site/src/components/systems-table/systems-table.tsx @@ -66,7 +66,7 @@ import { useStore } from "@nanostores/react" import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils" import AlertsButton from "../alerts/alert-button" import { $router, Link, navigate } from "../router" -import { EthernetIcon } from "../ui/icons" +import { EthernetIcon, ThermometerIcon } from "../ui/icons" import { Trans, t } from "@lingui/macro" import { useLingui } from "@lingui/react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card" @@ -180,6 +180,30 @@ export default function SystemsTable() { icon: HardDriveIcon, header: ({ column }) => sortableHeader(column), }, + { + accessorKey: "info.ht", + id: t`Temp`, + invertSorting: true, + sortUndefined: 0, + size: 50, + icon: ThermometerIcon, + header: ({ column }) => sortableHeader(column), + cell(info) { + const val = info.getValue() as number + if (!val) { + return null + } + return ( + + {decimalString(val)} °C + + ) + }, + }, { accessorFn: (originalRow) => originalRow.info.b || 0, id: t`Net`, diff --git a/beszel/site/src/types.d.ts b/beszel/site/src/types.d.ts index 0c6648e..a6f9ad3 100644 --- a/beszel/site/src/types.d.ts +++ b/beszel/site/src/types.d.ts @@ -43,6 +43,10 @@ export interface SystemInfo { v: string /** system is using podman */ p?: boolean + /** highest gpu utilization */ + g?: number + /** highest temperature */ + ht?: number } export interface SystemStats {