Add selectable host temperature sensors (#1307)

This commit is contained in:
ZacharyZcR
2026-08-24 01:08:39 +08:00
committed by GitHub
parent 5e76aec2bf
commit 433ede0b0d
6 changed files with 115 additions and 5 deletions
@@ -3,6 +3,11 @@ import { useTranslation } from "react-i18next";
import type { ServerMetrics } from "@/main-axios";
import { StatRow } from "@/components/charts";
import { MetricCard } from "./MetricCard";
import { useEffect, useState } from "react";
import {
selectTemperatureSensor,
temperaturePreferenceKey,
} from "../temperature-preference";
function formatTemperature(value: number | null | undefined): string {
return typeof value === "number" && Number.isFinite(value)
@@ -12,12 +17,37 @@ function formatTemperature(value: number | null | undefined): string {
export function TemperatureCard({
metrics,
hostId,
}: {
metrics: ServerMetrics | null;
hostId: number | null;
}) {
const { t } = useTranslation();
const temperature = metrics?.temperature;
const sensors = temperature?.sensors ?? [];
const [preferredLabel, setPreferredLabel] = useState("");
useEffect(() => {
setPreferredLabel(
hostId === null
? ""
: (localStorage.getItem(temperaturePreferenceKey(hostId)) ?? ""),
);
}, [hostId]);
const preferredSensor = selectTemperatureSensor(sensors, preferredLabel);
const displayedTemperature =
preferredSensor?.celsius ?? temperature?.highestCelsius;
const displayedLabel =
preferredSensor?.label ?? t("hostMetrics.highestTemperature");
const chooseSensor = (label: string) => {
setPreferredLabel(label);
if (hostId === null) return;
const key = temperaturePreferenceKey(hostId);
if (label) localStorage.setItem(key, label);
else localStorage.removeItem(key);
};
return (
<MetricCard
@@ -29,13 +59,31 @@ export function TemperatureCard({
<div className="flex flex-col gap-3">
<div>
<div className="text-3xl font-semibold tabular-nums">
{formatTemperature(temperature?.highestCelsius)}
{formatTemperature(displayedTemperature)}
</div>
<div className="mt-1 text-xs text-muted-foreground">
{t("hostMetrics.highestTemperature")}
{displayedLabel}
</div>
</div>
{sensors.length > 1 && (
<label className="flex flex-col gap-1 text-xs text-muted-foreground">
<span>{t("hostMetrics.primaryTemperatureSensor")}</span>
<select
value={preferredSensor?.label ?? ""}
onChange={(event) => chooseSensor(event.target.value)}
className="h-8 border border-border bg-background px-2 text-xs text-foreground outline-none focus:ring-1 focus:ring-ring"
>
<option value="">{t("hostMetrics.highestTemperature")}</option>
{sensors.map((sensor) => (
<option key={sensor.label} value={sensor.label}>
{sensor.label}
</option>
))}
</select>
</label>
)}
{sensors.length === 0 ? (
<span className="text-xs text-muted-foreground">N/A</span>
) : (
@@ -0,0 +1,12 @@
import type { TemperatureSensor } from "@/types/stats-widgets";
export function selectTemperatureSensor(
sensors: TemperatureSensor[],
preferredLabel: string,
) {
return sensors.find((sensor) => sensor.label === preferredLabel) ?? null;
}
export function temperaturePreferenceKey(hostId: number) {
return `termix-host-metrics:${hostId}:temperature-sensor`;
}
+1
View File
@@ -2511,6 +2511,7 @@
"selectFilesystem": "Select filesystem",
"temperature": "Temperature",
"highestTemperature": "Highest temperature",
"primaryTemperatureSensor": "Primary sensor",
"failedToFetchHostConfig": "Failed to fetch host configuration",
"serverOffline": "Server Offline",
"cannotFetchMetrics": "Cannot fetch metrics from offline server",
@@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";
import {
selectTemperatureSensor,
temperaturePreferenceKey,
} from "../../../features/host-metrics/temperature-preference";
const sensors = [
{ label: "k10temp: Tctl", celsius: 55 },
{ label: "nvme-pci-0100: Composite", celsius: 70 },
];
describe("temperature sensor preference", () => {
it("selects the configured sensor by its complete label", () => {
expect(selectTemperatureSensor(sensors, "k10temp: Tctl")).toEqual(
sensors[0],
);
});
it("falls back when a sensor disappears", () => {
expect(selectTemperatureSensor(sensors, "missing")).toBeNull();
});
it("scopes the preference to a host", () => {
expect(temperaturePreferenceKey(42)).toBe(
"termix-host-metrics:42:temperature-sensor",
);
});
});