mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 17:59:28 +08:00
feat: add temperatures to dashboard
- Refactor temperature related code and move to standalone function
This commit is contained in:
@@ -178,38 +178,9 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// temperatures (skip if sensors whitelist is set to empty string)
|
// temperatures (skip if sensors whitelist is set to empty string)
|
||||||
if a.sensorsWhitelist != nil && len(a.sensorsWhitelist) == 0 {
|
err = a.updateTemperatures(&systemStats)
|
||||||
slog.Debug("Skipping temperature collection")
|
if err != nil {
|
||||||
} else {
|
slog.Error("Error getting temperatures", "err", err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GPU data
|
// GPU data
|
||||||
@@ -239,6 +210,54 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
return systemStats
|
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
|
// Returns the size of the ZFS ARC memory cache in bytes
|
||||||
func getARCSize() (uint64, error) {
|
func getARCSize() (uint64, error) {
|
||||||
file, err := os.Open("/proc/spl/kstat/zfs/arcstats")
|
file, err := os.Open("/proc/spl/kstat/zfs/arcstats")
|
||||||
|
@@ -75,6 +75,8 @@ type Info struct {
|
|||||||
Bandwidth float64 `json:"b"`
|
Bandwidth float64 `json:"b"`
|
||||||
AgentVersion string `json:"v"`
|
AgentVersion string `json:"v"`
|
||||||
Podman bool `json:"p,omitempty"`
|
Podman bool `json:"p,omitempty"`
|
||||||
|
Gpu float64 `json:"g,omitempty"`
|
||||||
|
HighTemp float64 `json:"ht,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final data structure to return to the hub
|
// Final data structure to return to the hub
|
||||||
|
@@ -66,7 +66,7 @@ import { useStore } from "@nanostores/react"
|
|||||||
import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils"
|
import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils"
|
||||||
import AlertsButton from "../alerts/alert-button"
|
import AlertsButton from "../alerts/alert-button"
|
||||||
import { $router, Link, navigate } from "../router"
|
import { $router, Link, navigate } from "../router"
|
||||||
import { EthernetIcon } from "../ui/icons"
|
import { EthernetIcon, ThermometerIcon } from "../ui/icons"
|
||||||
import { Trans, t } from "@lingui/macro"
|
import { Trans, t } from "@lingui/macro"
|
||||||
import { useLingui } from "@lingui/react"
|
import { useLingui } from "@lingui/react"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
@@ -180,6 +180,30 @@ export default function SystemsTable() {
|
|||||||
icon: HardDriveIcon,
|
icon: HardDriveIcon,
|
||||||
header: ({ column }) => sortableHeader(column),
|
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 (
|
||||||
|
<span
|
||||||
|
className={cn("tabular-nums whitespace-nowrap", {
|
||||||
|
"ps-1": viewMode === "table",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{decimalString(val)} °C
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (originalRow) => originalRow.info.b || 0,
|
accessorFn: (originalRow) => originalRow.info.b || 0,
|
||||||
id: t`Net`,
|
id: t`Net`,
|
||||||
|
4
beszel/site/src/types.d.ts
vendored
4
beszel/site/src/types.d.ts
vendored
@@ -43,6 +43,10 @@ export interface SystemInfo {
|
|||||||
v: string
|
v: string
|
||||||
/** system is using podman */
|
/** system is using podman */
|
||||||
p?: boolean
|
p?: boolean
|
||||||
|
/** highest gpu utilization */
|
||||||
|
g?: number
|
||||||
|
/** highest temperature */
|
||||||
|
ht?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemStats {
|
export interface SystemStats {
|
||||||
|
Reference in New Issue
Block a user