system-monitor: CPU temperature in bTop (Temperature card under Memory)
- Backend: readTemps() scans /sys/class/hwmon for all temp sensors and picks the CPU one (k10temp/coretemp/zenpower Tctl/Tdie/Package); added to /stats.temp. - BtopView: a Temperature card at lg:col-start-2 (under Memory, second row) — big CPU °C (color-graded), its sensor label, and the other sensors (GPU, NVMe, wifi…) listed beneath. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { Cpu, MemoryStick, HardDrive, Server } from 'lucide-react';
|
||||
import { Cpu, MemoryStick, HardDrive, Server, Thermometer } from 'lucide-react';
|
||||
import { fmtBytes, fmtDuration } from './shared';
|
||||
|
||||
type Sensor = { name: string; label: string; celsius: number };
|
||||
type Stats = {
|
||||
hostname: string;
|
||||
platform: string;
|
||||
@@ -12,12 +13,14 @@ type Stats = {
|
||||
mem: { totalBytes: number; usedBytes: number; freeBytes: number; usedPct: number; swapTotalBytes: number; swapUsedBytes: number } | null;
|
||||
disks: { mount: string; fsType: string; totalBytes: number; usedBytes: number; usedPct: number }[];
|
||||
processes: { pid: number; user: string; cpuPct: number; memPct: number; command: string }[];
|
||||
temp: { cpuC: number | null; cpuLabel: string | null; sensors: Sensor[] } | null;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
const POLL_MS = 2000;
|
||||
|
||||
const barColor = (pct: number) => (pct >= 85 ? 'bg-red-500' : pct >= 60 ? 'bg-amber-500' : 'bg-emerald-500');
|
||||
const tempColor = (c: number) => (c >= 80 ? 'text-red-500' : c >= 65 ? 'text-amber-500' : 'text-emerald-500');
|
||||
|
||||
const Bar = ({ pct }: { pct: number }) => (
|
||||
<div className="h-2 w-full overflow-hidden rounded-full bg-muted">
|
||||
@@ -25,8 +28,8 @@ const Bar = ({ pct }: { pct: number }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const Card = ({ title, icon: Icon, children }: { title: string; icon: typeof Cpu; children: React.ReactNode }) => (
|
||||
<div className="flex flex-col gap-3 rounded-xl border border-border bg-card p-4">
|
||||
const Card = ({ title, icon: Icon, children, className }: { title: string; icon: typeof Cpu; children: React.ReactNode; className?: string }) => (
|
||||
<div className={`flex flex-col gap-3 rounded-xl border border-border bg-card p-4 ${className ?? ''}`}>
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
|
||||
<Icon size={16} className="text-primary" />
|
||||
{title}
|
||||
@@ -134,6 +137,30 @@ export const BtopView = () => {
|
||||
<p className="text-sm text-muted-foreground">no disks</p>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="Temperature" icon={Thermometer} className="lg:col-start-2">
|
||||
{stats.temp?.cpuC != null ? (
|
||||
<>
|
||||
<div className="flex items-end justify-between">
|
||||
<span className={`text-3xl font-semibold ${tempColor(stats.temp.cpuC)}`}>{stats.temp.cpuC.toFixed(0)}°C</span>
|
||||
<span className="truncate pl-2 text-xs text-muted-foreground">{stats.temp.cpuLabel}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 pt-1">
|
||||
{stats.temp.sensors
|
||||
.filter((s) => `${s.name} · ${s.label}` !== stats.temp!.cpuLabel)
|
||||
.slice(0, 6)
|
||||
.map((s, i) => (
|
||||
<div key={i} className="flex items-center justify-between text-xs">
|
||||
<span className="truncate text-muted-foreground">{s.name} · {s.label}</span>
|
||||
<span className={`font-mono ${tempColor(s.celsius)}`}>{s.celsius.toFixed(0)}°C</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">no sensors</p>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card title="Top processes" icon={Server}>
|
||||
|
||||
Reference in New Issue
Block a user