Add support for GPU and NPU temperatures in the frontend

This commit is contained in:
Nicolas Mowen 2025-12-31 12:21:36 -07:00
parent 194ed61189
commit 08f95e8274
3 changed files with 121 additions and 0 deletions

View File

@ -51,6 +51,7 @@
"gpuMemory": "GPU Memory", "gpuMemory": "GPU Memory",
"gpuEncoder": "GPU Encoder", "gpuEncoder": "GPU Encoder",
"gpuDecoder": "GPU Decoder", "gpuDecoder": "GPU Decoder",
"gpuTemperature": "GPU Temperature",
"gpuInfo": { "gpuInfo": {
"vainfoOutput": { "vainfoOutput": {
"title": "Vainfo Output", "title": "Vainfo Output",
@ -77,6 +78,7 @@
}, },
"npuUsage": "NPU Usage", "npuUsage": "NPU Usage",
"npuMemory": "NPU Memory", "npuMemory": "NPU Memory",
"npuTemperature": "NPU Temperature",
"intelGpuWarning": { "intelGpuWarning": {
"title": "Intel GPU Stats Warning", "title": "Intel GPU Stats Warning",
"message": "GPU stats unavailable", "message": "GPU stats unavailable",

View File

@ -61,11 +61,13 @@ export type GpuStats = {
enc?: string; enc?: string;
dec?: string; dec?: string;
pstate?: string; pstate?: string;
temp?: number;
}; };
export type NpuStats = { export type NpuStats = {
npu: number; npu: number;
mem: string; mem: string;
temp?: number;
}; };
export type GpuInfo = "vainfo" | "nvinfo"; export type GpuInfo = "vainfo" | "nvinfo";

View File

@ -368,6 +368,40 @@ export default function GeneralMetrics({
return Object.keys(series).length > 0 ? Object.values(series) : undefined; return Object.keys(series).length > 0 ? Object.values(series) : undefined;
}, [statsHistory]); }, [statsHistory]);
const gpuTempSeries = useMemo(() => {
if (!statsHistory) {
return [];
}
const series: {
[key: string]: { name: string; data: { x: number; y: number }[] };
} = {};
let hasValidGpu = false;
statsHistory.forEach((stats, statsIdx) => {
if (!stats) {
return;
}
Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => {
if (!(key in series)) {
series[key] = { name: key, data: [] };
}
if (stats.temp !== undefined) {
hasValidGpu = true;
series[key].data.push({ x: statsIdx + 1, y: stats.temp });
}
});
});
if (!hasValidGpu) {
return [];
}
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
}, [statsHistory]);
// Check if Intel GPU has all 0% usage values (known bug) // Check if Intel GPU has all 0% usage values (known bug)
const showIntelGpuWarning = useMemo(() => { const showIntelGpuWarning = useMemo(() => {
if (!statsHistory || statsHistory.length < 3) { if (!statsHistory || statsHistory.length < 3) {
@ -448,6 +482,40 @@ export default function GeneralMetrics({
return Object.keys(series).length > 0 ? Object.values(series) : []; return Object.keys(series).length > 0 ? Object.values(series) : [];
}, [statsHistory]); }, [statsHistory]);
const npuTempSeries = useMemo(() => {
if (!statsHistory) {
return [];
}
const series: {
[key: string]: { name: string; data: { x: number; y: number }[] };
} = {};
let hasValidNpu = false;
statsHistory.forEach((stats, statsIdx) => {
if (!stats) {
return;
}
Object.entries(stats.npu_usages || {}).forEach(([key, stats]) => {
if (!(key in series)) {
series[key] = { name: key, data: [] };
}
if (stats.temp !== undefined) {
hasValidNpu = true;
series[key].data.push({ x: statsIdx + 1, y: stats.temp });
}
});
});
if (!hasValidNpu) {
return [];
}
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
}, [statsHistory]);
// other processes stats // other processes stats
const hardwareType = useMemo(() => { const hardwareType = useMemo(() => {
@ -670,6 +738,7 @@ export default function GeneralMetrics({
className={cn( className={cn(
"mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2", "mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2",
gpuEncSeries?.length && "md:grid-cols-4", gpuEncSeries?.length && "md:grid-cols-4",
gpuTempSeries?.length && "md:grid-cols-3",
)} )}
> >
{statsHistory[0]?.gpu_usages && ( {statsHistory[0]?.gpu_usages && (
@ -804,6 +873,30 @@ export default function GeneralMetrics({
) : ( ) : (
<Skeleton className="aspect-video w-full" /> <Skeleton className="aspect-video w-full" />
)} )}
{statsHistory.length != 0 ? (
<>
{gpuTempSeries && gpuTempSeries?.length != 0 && (
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">
{t("general.hardwareInfo.gpuTemperature")}
</div>
{gpuTempSeries.map((series) => (
<ThresholdBarGraph
key={series.name}
graphId={`${series.name}-temp`}
name={series.name}
unit="°C"
threshold={DetectorTempThreshold}
updateTimes={updateTimes}
data={[series]}
/>
))}
</div>
)}
</>
) : (
<Skeleton className="aspect-video w-full" />
)}
{statsHistory[0]?.npu_usages && ( {statsHistory[0]?.npu_usages && (
<> <>
@ -827,6 +920,30 @@ export default function GeneralMetrics({
) : ( ) : (
<Skeleton className="aspect-video w-full" /> <Skeleton className="aspect-video w-full" />
)} )}
{statsHistory.length != 0 ? (
<>
{npuTempSeries && npuTempSeries?.length != 0 && (
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">
{t("general.hardwareInfo.npuTemperature")}
</div>
{npuTempSeries.map((series) => (
<ThresholdBarGraph
key={series.name}
graphId={`${series.name}-temp`}
name={series.name}
unit="°C"
threshold={DetectorTempThreshold}
updateTimes={updateTimes}
data={[series]}
/>
))}
</div>
)}
</>
) : (
<Skeleton className="aspect-video w-full" />
)}
</> </>
)} )}
</> </>