diff --git a/com.bryce.llamawatch.sdPlugin/ui/gpu.html b/com.bryce.llamawatch.sdPlugin/ui/gpu.html index 8cb8e29..4b65c31 100644 --- a/com.bryce.llamawatch.sdPlugin/ui/gpu.html +++ b/com.bryce.llamawatch.sdPlugin/ui/gpu.html @@ -14,6 +14,9 @@ + + + diff --git a/src/actions/gpu-graph.ts b/src/actions/gpu-graph.ts index 44eb9de..17c4369 100644 --- a/src/actions/gpu-graph.ts +++ b/src/actions/gpu-graph.ts @@ -8,6 +8,7 @@ import streamDeck, { type WillDisappearEvent, } from "@elgato/streamdeck"; import { type GpuMetricKind } from "../lib/metrics-parser"; +import { combineSeries } from "../lib/metrics-poller"; import { renderGpuGraph, svgDataUrl } from "../lib/render"; import { runtime } from "../lib/runtime"; import { cfgFromSettings, type CfgSettings } from "../lib/util"; @@ -15,6 +16,7 @@ import { cfgFromSettings, type CfgSettings } from "../lib/util"; type GpuSettings = CfgSettings & { gpuId?: string; metric?: GpuMetricKind; + gpuCombo?: string; }; type GpuState = { @@ -71,12 +73,28 @@ export class GpuGraph extends SingletonAction { const action = state.action; if (!action) return; const poller = runtime.pollerInstance; - const gpuId = state.settings.gpuId ?? "all"; const metric = state.settings.metric ?? "util_percent"; - const value = poller?.getValue(gpuId, metric); - const history = poller?.getHistory(gpuId, metric) ?? []; + const combo = parseCombo(state.settings.gpuCombo); const offline = !poller || poller.isOffline(); - const gpuName = gpuId === "all" ? "ALL GPUS" : this.gpuLabel(gpuId); + + let value: number | undefined; + let history: number[] = []; + let gpuName: string; + if (combo) { + const known = new Set((poller?.gpus() ?? []).map((g) => g.id)); + const ids = combo.filter((id) => known.has(id)); + const histories = ids.map((id) => poller?.getHistory(id, metric) ?? []); + const combined = combineSeries(histories, metric); + value = combined.value; + history = combined.history; + gpuName = `GPU ${ids.join("+")}`; + } else { + const gpuId = state.settings.gpuId ?? "all"; + value = poller?.getValue(gpuId, metric); + history = poller?.getHistory(gpuId, metric) ?? []; + gpuName = gpuId === "all" ? "ALL GPUS" : this.gpuLabel(gpuId); + } + const svg = renderGpuGraph({ gpuName, metric, value, history, offline }); if (svg === state.lastSvg) return; state.lastSvg = svg; @@ -88,3 +106,11 @@ export class GpuGraph extends SingletonAction { return gpu && gpu.name ? `GPU ${gpuId} · ${gpu.name}` : `GPU ${gpuId}`; } } + +function parseCombo(raw: string | undefined): string[] | undefined { + const ids = (raw ?? "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + return ids.length > 0 ? ids : undefined; +}