feat: add GPU monitoring support via Glances integration (#5255)

* feat: add GPU monitoring support via Glances integration

- Add GPU fields to SystemHealthMonitoring interface (gpuId, name,
  memoryUtilization, processorUtilization, temperature, fanSpeed)
- Extend Glances integration to parse GPU data from /api/4/all endpoint
  (supports NVIDIA, AMD, and Intel GPUs via Glances)
- Add GPU utilization ring to Health Monitoring widget with tooltip
  showing VRAM usage, temperature, and fan speed
- Add GPU time-series chart to System Resources widget (grape color)
- Add GPU toggle option to both widgets
- Add English translations for GPU labels
- Add gpu: [] to all other integrations (dashdot, truenas, unraid,
  openmediavault) for backward compatibility
- Add GPU mock data for development/testing

Closes community request from PR #5102 for GPU monitoring support.

* fix: implement GPU monitoring for dashdot integration and clarify OMV comment

- Add GPU load fetching via dashdot /load/gpu endpoint
- Add GPU info parsing from dashdot /info endpoint
- Add gpuLoadApi and gpu field to internalServerInfoApi schemas
- Add getCurrentGpuLoadAsync method to DashDotIntegration
- Map GPU load data to SystemHealthMonitoring gpu array
- Add explanatory comment for OMV's empty gpu array

Addresses review comments from manuel-rw on PR #5255

* Delete manifest.json
This commit is contained in:
Daniel authored and GitHub committed 2026-03-27 20:22:41 +01:00
1 parent d7f0596db8
commit 003dac4744
15 files changed
+231 -2

No files matched your search

@@ -33,6 +33,7 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
const memoryLoad = await this.getCurrentMemoryLoadAsync();
const storageLoad = await this.getCurrentStorageLoadAsync();
const networkLoad = await this.getCurrentNetworkLoadAsync();
const gpuLoad = await this.getCurrentGpuLoadAsync();
const channel = this.getChannel();
const history = await channel.getSliceUntilTimeAsync(dayjs().subtract(15, "minutes").toDate());
@@ -56,6 +57,14 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
availablePkgUpdates: 0,
rebootRequired: false,
smart: [], // API endpoint does not provide S.M.A.R.T data.
gpu: gpuLoad.map((gpu, index) => ({
gpuId: `gpu-${index}`,
name: info.gpuNames[index] ?? `GPU ${index}`,
memoryUtilization: gpu.memory ?? 0,
processorUtilization: gpu.load ?? 0,
temperature: null,
fanSpeed: null,
})),
uptime: info.uptime,
version: `${info.operatingSystemVersion}`,
loadAverage: {
@@ -76,6 +85,7 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
cpuModel: serverInfo.cpu.model,
operatingSystemVersion: `${serverInfo.os.distro} ${serverInfo.os.release} (${serverInfo.os.kernel})`,
uptime: serverInfo.os.uptime,
gpuNames: serverInfo.gpu.map((gpu) => gpu.brand),
};
}
@@ -150,6 +160,17 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
return await networkLoadApi.parseAsync(JSON.parse(result));
}
private async getCurrentGpuLoadAsync() {
const response = await fetchWithTrustedCertificatesAsync(this.url("/load/gpu"));
const result = await response.text();
// response is either valid json or empty if gpu widget is disabled.
if (result.length === 0) return [];
const data = await gpuLoadApi.parseAsync(JSON.parse(result));
return data.layout;
}
private getChannel() {
return createChannelEventHistoryOld<z.infer<typeof cpuLoadPerCoreApiList>>(
`integration:${this.integration.id}:history:cpu`,
@@ -198,6 +219,23 @@ const internalServerInfoApi = z.object({
),
}),
),
gpu: z
.array(
z.object({
brand: z.string(),
model: z.string().optional(),
}),
)
.default([]),
});
const cpuLoadPerCoreApiList = z.array(cpuLoadPerCoreApi);
const gpuLoadApi = z.object({
layout: z.array(
z.object({
load: z.number().min(0).optional(),
memory: z.number().min(0).optional(),
}),
),
});