feat(admin): overview dashboard sections + system temperature/load sensors (0.85.0) (#11)
This commit was merged in pull request #11.
This commit is contained in:
43
frontend/src/lib/admin/temps.test.ts
Normal file
43
frontend/src/lib/admin/temps.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { pickCpuTemp } from './temps';
|
||||
import type { TempStat } from '$lib/api/admin';
|
||||
|
||||
function t(label: string, celsius: number): TempStat {
|
||||
return { label, celsius, max_celsius: celsius, critical_celsius: 100 };
|
||||
}
|
||||
|
||||
describe('pickCpuTemp', () => {
|
||||
it('returns null when there are no sensors', () => {
|
||||
expect(pickCpuTemp([])).toBeNull();
|
||||
});
|
||||
|
||||
it('prefers the Intel package sensor over individual cores', () => {
|
||||
const temps = [
|
||||
t('coretemp Core 0', 50),
|
||||
t('coretemp Core 1', 49),
|
||||
t('coretemp Package id 0', 51),
|
||||
t('acpitz temp1', 28)
|
||||
];
|
||||
expect(pickCpuTemp(temps)?.label).toBe('coretemp Package id 0');
|
||||
});
|
||||
|
||||
it('falls back to the AMD Tctl/Tdie sensor when there is no package', () => {
|
||||
const temps = [t('k10temp Tctl', 55), t('nvme Composite', 41)];
|
||||
expect(pickCpuTemp(temps)?.label).toBe('k10temp Tctl');
|
||||
});
|
||||
|
||||
it('falls back to the hottest core when only per-core sensors exist', () => {
|
||||
const temps = [t('coretemp Core 0', 50), t('coretemp Core 3', 58), t('coretemp Core 1', 49)];
|
||||
expect(pickCpuTemp(temps)?.label).toBe('coretemp Core 3');
|
||||
});
|
||||
|
||||
it('falls back to any cpu/coretemp-labelled sensor as a last resort', () => {
|
||||
const temps = [t('nvme Composite', 41), t('cpu_thermal', 47)];
|
||||
expect(pickCpuTemp(temps)?.label).toBe('cpu_thermal');
|
||||
});
|
||||
|
||||
it('returns null when nothing looks like a CPU sensor', () => {
|
||||
const temps = [t('nvme Composite', 41), t('acpitz temp1', 28)];
|
||||
expect(pickCpuTemp(temps)).toBeNull();
|
||||
});
|
||||
});
|
||||
25
frontend/src/lib/admin/temps.ts
Normal file
25
frontend/src/lib/admin/temps.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { TempStat } from '$lib/api/admin';
|
||||
|
||||
/** Pick the sensor that best represents the CPU die/package temperature
|
||||
* from a heterogeneous component list. Intel exposes
|
||||
* `coretemp Package id 0`; AMD exposes `k10temp Tctl`/`Tdie`. When neither
|
||||
* is present we fall back to the hottest individual core, then to any
|
||||
* CPU-ish sensor, and finally `null` (nothing CPU-related — e.g. only NVMe
|
||||
* / chipset sensors are exposed). Used to keep the System tab focused on
|
||||
* the one reading operators care about, with the rest tucked away. */
|
||||
export function pickCpuTemp(temps: TempStat[]): TempStat | null {
|
||||
const byLabel = (re: RegExp) => temps.find((t) => re.test(t.label)) ?? null;
|
||||
return (
|
||||
byLabel(/package/i) ??
|
||||
byLabel(/tctl|tdie|k10temp/i) ??
|
||||
hottestCore(temps) ??
|
||||
byLabel(/cpu|coretemp/i) ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function hottestCore(temps: TempStat[]): TempStat | null {
|
||||
const cores = temps.filter((t) => /core\s*\d+/i.test(t.label));
|
||||
if (cores.length === 0) return null;
|
||||
return cores.reduce((a, b) => (b.celsius > a.celsius ? b : a));
|
||||
}
|
||||
Reference in New Issue
Block a user