Hardware: a Motherboard card

The board, the chassis and the firmware on it, which the System card was
reducing to two lines. Maker, model, version, serial and asset tag; chassis
and where the board sits in it; the firmware's vendor, version, revision,
date, ROM size and whether the machine boots UEFI — that last one is a line
in SMBIOS's characteristics list rather than a field, so the parser now keeps
those lists too.

Expansion slots come with it, with the device found at each occupied slot's
PCI address rather than the address alone. They are only shown when the
firmware tracks occupancy at all: core's NucBox lists five PCIe x1 slots it
does not physically have, all "Available", copied out of a reference design.
grid's board reports real addresses and real occupancy, which is the case
worth printing.

A machine with no SMBIOS gets a card that says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-25 22:21:27 -07:00
co-authored by Claude Opus 5
parent 38039d1adc
commit 03e0761f3e
3 changed files with 189 additions and 31 deletions
+57 -6
View File
@@ -2,7 +2,7 @@
// their slots, the drives and the graphics. None of it moves, so the page is a read rather
// than a monitor: what is live (temperature, usage, SMART) belongs to Dashboard and Disks.
import { useCallback, useEffect, useState } from 'react'
import { api, Hardware as HW, HWDrive, HWGPU, HWModule } from './api'
import { api, Hardware as HW, HWBoard, HWDrive, HWGPU, HWModule } from './api'
import { fmtBytes } from './fmt'
import { Btn, Loading } from './ui'
@@ -87,6 +87,58 @@ function Gpu({ g }: { g: HWGPU }) {
)
}
// The board and the firmware on it. Everything here is SMBIOS, so a machine without one
// — a Mac, a Pi — gets a card that says as much instead of a column of blanks.
function Motherboard({ b }: { b: HWBoard }) {
const named = b.maker || b.product || b.bios
// A board that reports every slot "Available" is not telling us anything — mini PCs
// copy the slot table out of a reference design and list slots the case has no room
// for. Only worth showing when the firmware actually tracks what is plugged in.
const slots = (b.slots ?? []).some((s) => s.usage === 'In Use') ? (b.slots ?? []) : []
return (
<section className="card">
<h2>Motherboard</h2>
{!named && <div className="empty">This machine has no SMBIOS table, so its firmware doesn't say what board it is.</div>}
{named && (
<div className="props">
<P k="Maker" v={b.maker} />
<P k="Model" v={b.product} />
<P k="Version" v={b.version} />
<P k="Kind" v={b.type} />
<P k="Chassis" v={[b.chassis_maker, b.chassis].filter(Boolean).join(' ')} />
<P k="In chassis" v={b.location} />
<P k="Serial" v={b.serial} mono />
<P k="Asset tag" v={b.asset_tag} mono />
</div>
)}
{(b.bios || b.bios_date) && (
<div className="props" style={{ marginTop: 8 }}>
<P k="Firmware" v={b.bios} />
<P k="Revision" v={b.bios_revision} />
<P k="Dated" v={b.bios_date} />
<P k="ROM" v={b.rom_size} />
<P k="Boots" v={b.uefi ? 'UEFI' : ''} />
</div>
)}
{slots.length > 0 && (
<div style={{ marginTop: 8 }}>
<div className="sub-head"><span className="dname">Expansion slots</span><span className="spacer" /><span className="muted small">as the firmware lists them</span></div>
<table className="dtable">
<thead><tr><th>Slot</th><th>Type</th><th>What is in it</th></tr></thead>
<tbody>{slots.map((s, i) => (
<tr key={s.designation + i}>
<td className="mono small">{s.designation}</td>
<td className="mono small">{[s.type, s.length && s.length !== 'Other' ? s.length.toLowerCase() : ''].filter(Boolean).join(' · ')}</td>
<td className="small" title={s.bus}>{s.usage === 'In Use' ? (s.device ?? s.bus ?? 'in use') : <span className="muted">{(s.usage ?? '').toLowerCase() || '—'}</span>}</td>
</tr>
))}</tbody>
</table>
</div>
)}
</section>
)
}
export default function Hardware() {
const [hw, setHw] = useState<HW | null>(null)
const [err, setErr] = useState('')
@@ -100,7 +152,7 @@ export default function Hardware() {
if (err) return <div className="dash"><div className="notice">{err === 'not found' ? "This machine's Ham Lab is older than the Hardware tab. Update it from Machines." : err}</div></div>
if (!hw) return <div className="dash"><Loading label="Reading the machine…" /></div>
const { system: sys, cpu, memory: mem } = hw
const { system: sys, board, cpu, memory: mem } = hw
const drives = hw.drives ?? []
const gpus = hw.gpus ?? []
const capacity = drives.reduce((n, d) => n + d.size, 0)
@@ -191,14 +243,13 @@ export default function Hardware() {
{gpus.length === 0 && <div className="empty">No display adapter found.</div>}
</section>
<Motherboard b={board} />
<section className="card">
<h2>System</h2>
<div className="props">
<P k="Model" v={sys.model} />
<P k="Board" v={sys.board} />
<P k="Chassis" v={sys.chassis} />
<P k="Firmware" v={sys.bios} />
<P k="Dated" v={sys.bios_date} />
<P k="Family" v={sys.family} />
<P k="Serial" v={sys.serial} mono />
<P k="Kernel" v={sys.kernel} mono />
<P k="Arch" v={sys.arch} />