Documentation
¶
Overview ¶
Package devices is the shared vocabulary for the jarvisfriends devices-* family (devices-cpu, devices-gpu, devices-memory, …). Each category repo implements Collector; everything else here — the Engine, the cli, tui, and web packages — works against that one interface, so a new category gets all four consumption surfaces (library, CLI, TUI, web component) for free.
The model splits along two axes:
- Kind: Static values are one-time lookups (model numbers, serials, specs) that the Engine caches after the first read. Dynamic values change over time (temperatures, usage, clocks, power) and are re-collected on every request.
- Level: Basic is the cheap common set every platform can produce. Detailed and Full add values that cost more to retrieve — extra subprocesses, WMI queries, privileged files — so callers opt into the latency instead of paying it by default.
Index ¶
- Constants
- func Every(seconds float64) time.Duration
- func FormatBytes(bytes uint64) string
- func FormatValue(f Field) string
- type Collector
- type Device
- type Engine
- func (e *Engine) Collector() Collector
- func (e *Engine) Dynamic(ctx context.Context, level Level) (Snapshot, error)
- func (e *Engine) InvalidateStatic()
- func (e *Engine) Request(ctx context.Context, level Level) <-chan Result
- func (e *Engine) Snapshot(ctx context.Context, level Level) (Snapshot, error)
- func (e *Engine) Static(ctx context.Context, level Level) (Snapshot, error)
- func (e *Engine) Stream(ctx context.Context, level Level, interval time.Duration) <-chan Result
- type Field
- func Bool(key, name string, value bool, kind Kind, level Level) Field
- func Bytes(key, name string, value uint64, kind Kind, level Level) Field
- func Int(key, name string, value int64, unit string, kind Kind, level Level) Field
- func Num(key, name string, value float64, unit string, kind Kind, level Level) Field
- func Pct(key, name string, value float64, kind Kind, level Level) Field
- func Str(key, name, value string, kind Kind, level Level) Field
- type Gap
- type Kind
- type Level
- type Option
- type Result
- type Snapshot
Constants ¶
const CGOEnabled = true
CGOEnabled reports whether the binary was built with cgo. Collectors use it to advertise (via Snapshot.CGO and gaps) why cgo-only fields are absent from a pure-Go build rather than leaving them silently missing.
Variables ¶
This section is empty.
Functions ¶
func Every ¶
Every converts a decimal number of seconds into a Duration, so "0.25" from a flag or query parameter maps directly onto an interval.
func FormatBytes ¶
FormatBytes renders a byte count in binary units (KiB-scale, single letter), matching how the rest of the jarvisfriends repos print sizes.
func FormatValue ¶
FormatValue renders one field's value with its unit, the same way on every surface: byte counts humanize, floats keep one decimal, everything else prints as-is.
Types ¶
type Collector ¶
type Collector interface {
// Category is the short lowercase name: "cpu", "gpu", "drive", ….
Category() string
Static(ctx context.Context, level Level) (Snapshot, error)
Dynamic(ctx context.Context, level Level) (Snapshot, error)
}
Collector is what each devices-* repo implements. Static and Dynamic are separate so the Engine can cache one and re-collect the other; both honor the level by not collecting values above it (Trim exists for the cheap cases where gating isn't worth the branching).
Collectors must be safe for concurrent use: the Engine may serve an on-demand request while a stream is mid-collection.
type Device ¶
type Device struct {
// ID is stable across snapshots of the same boot ("nvme0n1", "gpu0",
// "DIMM_A1") so streams can be joined over time.
ID string `json:"id"`
Name string `json:"name,omitempty"`
Fields []Field `json:"fields,omitempty"`
}
Device is one physical instance in a category: one GPU, one DIMM, one disk, one connected monitor. Category-wide values live on the Snapshot itself; per-instance values live here.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine wraps a Collector with the three request shapes every surface needs: blocking on-demand (Snapshot), async one-shot (Request), and a periodic stream (Stream). It also owns the static cache, so serial numbers and model names are read once per process, not once per refresh.
func (*Engine) InvalidateStatic ¶
func (e *Engine) InvalidateStatic()
InvalidateStatic drops the static cache, forcing the next read to hit the collector again.
func (*Engine) Request ¶
Request is the async form of Snapshot: it returns immediately and delivers exactly one Result on the channel, then closes it.
func (*Engine) Snapshot ¶
Snapshot returns static (cached) and dynamic values merged into one snapshot. A dynamic failure fails the call; a static failure is reported as a gap so a permissions problem on serial numbers doesn't take the live readings down with it.
func (*Engine) Stream ¶
Stream emits a merged snapshot immediately and then every interval until ctx is canceled, closing the channel on the way out. Intervals accept fractions of a second (Every(0.25)). Sends block, so a slow consumer slows the stream rather than piling snapshots up; if collection itself takes longer than the interval, ticks are dropped, not queued.
type Field ¶
type Field struct {
// Key is the stable machine-readable identity ("usage_percent",
// "serial_number"). Consumers select and chart by Key; Name is what
// humans see.
Key string `json:"key"`
Name string `json:"name"`
Value any `json:"value"`
// Unit is the display unit: "%", "°C", "MHz", "W", "B", "B/s". A Unit of
// "B" tells renderers to humanize (GiB/MiB); empty means unitless.
Unit string `json:"unit,omitempty"`
Kind Kind `json:"kind"`
Level Level `json:"level"`
// Min and Max bound gauge-like values so gradients and bars can be drawn
// without per-field knowledge. Both zero means no range is known.
Min float64 `json:"min,omitempty"`
Max float64 `json:"max,omitempty"`
}
Field is one reported value. Value is one of: string, bool, int64, uint64, float64 — nothing richer, so every field survives a JSON round trip and any renderer (table cell, gauge, chart point) knows what it is holding.
type Gap ¶
Gap records a value that could not be read and why — permissions, missing vendor tool, unsupported platform. Reported instead of silently dropped so an empty list is distinguishable from an unreadable one.
type Kind ¶
type Kind uint8
Kind says whether a field is a one-time lookup or a live reading.
const ( // Static values do not change while the machine is up: model numbers, // serial numbers, capacities, firmware versions. The Engine caches them. Static Kind = iota // Dynamic values move over time: utilization, temperature, clock speed, // power draw. They are re-collected on every request. Dynamic )
func (Kind) MarshalText ¶
MarshalText makes Kind render as its name in JSON output.
func (*Kind) UnmarshalText ¶
UnmarshalText accepts "static" or "dynamic".
type Level ¶
type Level uint8
Level selects how much a collection is allowed to cost. Levels are cumulative: Full includes everything Detailed does, which includes everything Basic does.
const ( // Basic is the default: the common values every platform can produce // quickly, with no subprocesses and no privileged reads. Basic Level = iota // Detailed adds values that take noticeably longer or need extra // sources — vendor CLIs, WMI queries, wider sysfs walks. Detailed // Full adds everything else we know how to read, however slow: SMART // polls, per-process counters, exhaustive enumeration. Full )
func ParseLevel ¶
ParseLevel maps a level name to its Level. It exists for flag and query parameter parsing, so unknown names are an error rather than a default.
func (Level) MarshalText ¶
MarshalText makes Level render as its name in JSON output.
func (*Level) UnmarshalText ¶
UnmarshalText parses a level name.
type Option ¶
type Option func(*Engine)
Option configures an Engine.
func WithStaticTTL ¶
WithStaticTTL re-reads static values after d instead of caching them forever. Useful for long-lived daemons where hot-pluggable hardware (monitors, drives) can change under them.
type Snapshot ¶
type Snapshot struct {
// Category is the collector's name: "cpu", "gpu", "memory", ….
Category string `json:"category"`
Taken time.Time `json:"taken"`
Level Level `json:"level"`
// CGO reports whether the binary was built with cgo, which some
// categories use to unlock extra fields.
CGO bool `json:"cgo"`
Fields []Field `json:"fields,omitempty"`
Devices []Device `json:"devices,omitempty"`
Gaps []Gap `json:"gaps,omitempty"`
}
Snapshot is one collection result: category-wide fields, per-instance devices, and the gaps hit along the way.
func Merge ¶
Merge combines a static and a dynamic snapshot: category-wide fields concatenate (static first) and devices join by ID, so a GPU's model number and its live temperature end up on the same Device entry.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cli is the shared command line for every devices-* tool.
|
Package cli is the shared command line for every devices-* tool. |
|
Package tui renders any devices.Collector as a live full-screen dashboard built from the snap component set.
|
Package tui renders any devices.Collector as a live full-screen dashboard built from the snap component set. |
|
Package web serves device snapshots over HTTP three ways: a JSON API for programs, a Server-Sent Events stream for live consumers, and an embeddable web component (<jf-devices>) that any site can drop in to get live tables and gauges.
|
Package web serves device snapshots over HTTP three ways: a JSON API for programs, a Server-Sent Events stream for live consumers, and an embeddable web component (<jf-devices>) that any site can drop in to get live tables and gauges. |