Add CPU temperature chart to System Trends

Queries max core temperature from Prometheus (platform_coretemp_0)
and displays it as a fourth sparkline in red. Grid now 4-column.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-02-03 22:54:12 +01:00
parent d81f39c28d
commit 059d9037b3
3 changed files with 20 additions and 5 deletions

View File

@@ -39,7 +39,7 @@ export async function GET() {
const start = end - 6 * 3600; // 6 hours const start = end - 6 * 3600; // 6 hours
const step = 120; // 2-minute resolution const step = 120; // 2-minute resolution
const [cpu, ram, netRx, netTx] = await Promise.all([ const [cpu, ram, netRx, netTx, temp] = await Promise.all([
// CPU usage percent // CPU usage percent
queryRange( queryRange(
`100 - (avg(rate(node_cpu_seconds_total{mode="idle",instance="${INSTANCE}"}[2m])) * 100)`, `100 - (avg(rate(node_cpu_seconds_total{mode="idle",instance="${INSTANCE}"}[2m])) * 100)`,
@@ -60,16 +60,21 @@ export async function GET() {
`rate(node_network_transmit_bytes_total{instance="${INSTANCE}",device="${NIC}"}[2m])`, `rate(node_network_transmit_bytes_total{instance="${INSTANCE}",device="${NIC}"}[2m])`,
start, end, step start, end, step
), ),
// CPU temperature (max across cores)
queryRange(
`max(node_hwmon_temp_celsius{instance="${INSTANCE}",chip="platform_coretemp_0"})`,
start, end, step
),
]); ]);
return NextResponse.json( return NextResponse.json(
{ cpu, ram, netRx, netTx }, { cpu, ram, netRx, netTx, temp },
{ headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate' } } { headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate' } }
); );
} catch (error) { } catch (error) {
console.error('Metrics error:', error); console.error('Metrics error:', error);
return NextResponse.json( return NextResponse.json(
{ error: 'Failed to fetch metrics', cpu: [], ram: [], netRx: [], netTx: [] }, { error: 'Failed to fetch metrics', cpu: [], ram: [], netRx: [], netTx: [], temp: [] },
{ status: 500 } { status: 500 }
); );
} }

View File

@@ -166,13 +166,14 @@ export function SystemTrends({ uptimeLabel, loadAvg }: SystemTrendsProps) {
{/* Charts */} {/* Charts */}
{loading && !data ? ( {loading && !data ? (
<div className="grid grid-cols-1 md:grid-cols-3 gap-5"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
<ShimmerChart />
<ShimmerChart /> <ShimmerChart />
<ShimmerChart /> <ShimmerChart />
<ShimmerChart /> <ShimmerChart />
</div> </div>
) : data ? ( ) : data ? (
<div className="grid grid-cols-1 md:grid-cols-3 gap-5"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
<SparkChart <SparkChart
label="CPU" label="CPU"
series={data.cpu} series={data.cpu}
@@ -189,6 +190,14 @@ export function SystemTrends({ uptimeLabel, loadAvg }: SystemTrendsProps) {
formatValue={(v) => `${v.toFixed(1)}%`} formatValue={(v) => `${v.toFixed(1)}%`}
domain={[0, 100]} domain={[0, 100]}
/> />
<SparkChart
label="Temp"
series={data.temp}
color="#ef4444"
fillColor="#ef4444"
formatValue={(v) => `${v.toFixed(0)}`}
unit="°C"
/>
<SparkChart <SparkChart
label="Network" label="Network"
series={data.netRx.map(([ts, val], i) => { series={data.netRx.map(([ts, val], i) => {

View File

@@ -47,6 +47,7 @@ export interface MetricsData {
ram: MetricSeries; ram: MetricSeries;
netRx: MetricSeries; netRx: MetricSeries;
netTx: MetricSeries; netTx: MetricSeries;
temp: MetricSeries;
} }
export function formatBytes(bytes: number): string { export function formatBytes(bytes: number): string {