System and power
Reference for dd.system status, vitals, volume, audio devices and Wi-Fi, and for dd.power capabilities and actions.
dd.system
Reads need the system permission; the write surface (setVolume,
setMuted, the microphone verbs, setRadio, the audio device and app
volume verbs, the Bluetooth verbs, the brightness verbs, the Wi-Fi verbs,
openSettings) additionally needs systemControls.
dd.system.status()
dd.system.status(): Promise<SystemVitals | null>The latest vitals snapshot, a cheap cache read (null until the sampler's
first pass). Seed your UI with this, then follow onVitals.
dd.system.onVitals()
dd.system.onVitals(cb: (vitals: SystemVitals) => void): () => void
interface SystemVitals {
/** CPU busy fraction 0..1 (null on the very first sample). */
cpu: number | null;
ram: { usedMb: number; totalMb: number; percent: number } | null;
/** GPU busy fraction 0..1: the busiest engine (3D, copy, video, compute)
* of ONE adapter, the figure Task Manager shows. The host measures the
* hardware adapter with the most dedicated video memory unless the user
* picked another in Settings > General. App 0.3.8+; earlier apps summed
* the 3D engines of every adapter. */
gpu: number | null;
/** Disk busy fraction 0..1 (physical disks total). */
disk: number | null;
/** Fixed drives (letters), resampled about every 30s inside the 1Hz
* stream. `percent` is used/total 0..1; `label` is the volume name or
* null; null when enumeration fails, empty when no fixed drives exist. */
drives:
| { mount: string; label: string | null; usedMb: number; totalMb: number; percent: number }[]
| null;
net: { rxBps: number; txBps: number } | null;
battery: { percent: number | null; charging: boolean } | null;
wifi: { ssid: string; signal: number } | null;
volume: { level: number; muted: boolean } | null;
/** The default microphone; null with no microphone. App 0.4.2+. */
mic: { level: number; muted: boolean } | null;
/** Radio state as Windows Quick Settings shows it; null while Windows
* cannot list the radios. App 0.4.2+. */
radios: { wifi: RadioState; bluetooth: RadioState; cellular: RadioState } | null;
}
/** "disabled": off by hardware switch or airplane mode. null: no such radio. */
type RadioState = "on" | "off" | "disabled" | null;A 1 Hz stream. Every field fails independently: a machine with no GPU
counters, no Wi-Fi adapter, no battery, or no audio device reports null for
that field while the rest keep flowing. Render nulls as absent, not as zero.
cpu, gpu, disk, volume.level, mic.level and ram.percent are 0..1 fractions;
wifi.signal is 0..100. drives lists fixed volumes only and refreshes about
every 30 seconds inside the stream (app 0.2.5+); sizes are MiB, like ram.
net sums the up hardware interfaces only, so a VPN tunnel or a virtual
switch port never counts the same bytes again; a machine with no hardware
interface up (a VM guest) sums every up interface, and the first tick after
that rule flips is null (app 0.3.7 or newer; earlier apps summed every up
interface).
gpu is the busiest engine of one graphics adapter, the figure Task Manager
shows for that card: the hardware adapter with the most dedicated video
memory, or the one the user picked in Settings > General (app 0.3.8 or newer;
earlier apps summed the 3D engines of every adapter, integrated graphics
included).
dd.system.setVolume()
dd.system.setVolume(level: number): Promise<void> // 0..1Master volume. 120 calls per rolling 10 seconds; throttle slider drags client-side to roughly one call per 100 ms.
dd.system.setMuted()
dd.system.setMuted(muted: boolean): Promise<void>20 calls per rolling 10 seconds.
dd.system.setMicVolume()
dd.system.setMicVolume(level: number): Promise<void> // 0..1The default microphone's level, the one the mic vitals field reads. 120
calls per rolling 10 seconds; throttle slider drags like setVolume. A
machine with no microphone rejects with INTERNAL. App 0.4.2 or newer.
dd.system.setMicMuted()
dd.system.setMicMuted(muted: boolean): Promise<void>Mutes or unmutes the default microphone. 20 calls per rolling 10 seconds. App 0.4.2 or newer.
dd.system.setRadio()
dd.system.setRadio(
kind: "wifi" | "bluetooth" | "cellular",
on: boolean,
): Promise<RadioSetResult>
interface RadioSetResult {
access: "allowed" | "deniedByUser" | "deniedBySystem" | "unspecified";
state: RadioState;
}Turns every radio of a kind on or off, like the tiles in Windows Quick Settings. Call it from a click: the host refuses it unless the cursor is inside your widget, its pane, or your bar, and to a desktop widget in edit mode, because Wi-Fi off cuts the network.
Windows has its own privacy switch, "Let apps control device radios". When
it is off, the call resolves with access: "deniedByUser" instead of
rejecting; show that and offer openSettings("privacy-radios"). state is
the radio's state right after the switch, and the radios vitals field
confirms it within a second. A machine with no radio of that kind rejects
with NOT_FOUND. 5 calls per rolling 10 seconds. App 0.4.2 or newer.
dd.system.audioDevices()
dd.system.audioDevices(): Promise<AudioDevices>
interface AudioDevices {
outputs: AudioDevice[];
inputs: AudioDevice[];
}
interface AudioDevice {
/** Opaque token for setAudioDevice, valid for this app run only. */
id: string;
/** The name Windows shows, e.g. "Speakers (Realtek Audio)". */
name: string;
/** The Windows default for its direction. */
isDefault: boolean;
}The active audio outputs and inputs, each list with the default first, then
by name. The hardware id never reaches your widget: id is a token the host
issues per app run, so list again after a restart. 20 calls per rolling 10
seconds. App 0.4.2 or newer.
dd.system.setAudioDevice()
dd.system.setAudioDevice(id: string): Promise<AudioDevices>Makes a listed device the Windows default for its direction, in every role
(console, multimedia, communications), as the Windows sound menu does. It
resolves with the fresh list as Windows now reports it: render that, not a
guess. A stale id rejects NOT_FOUND (list again). The volume calls and
the audio spectrum follow the new default by themselves. 5 calls per
rolling 10 seconds. App 0.4.2 or newer.
dd.system.onAudioDevicesChanged()
dd.system.onAudioDevicesChanged(cb: () => void): () => voidA data-free ping when a device comes, goes, is enabled or disabled, or
becomes the default (your own setAudioDevice included), at most once a
second: call audioDevices() again. Needs system and systemControls.
App 0.4.2 or newer. For a ready-made list, use
<dd-audio-devices>.
dd.system.audioApps()
dd.system.audioApps(): Promise<{ apps: AudioApp[] }>
interface AudioApp {
/** Opaque token, valid for this app run. */
id: string;
/** "Spotify"; "System sounds" for the system session. */
name: string;
/** The app's icon as a data: URI, or null. */
icon: string | null;
/** The app's own level 0..1, a fraction of the master volume. */
level: number;
muted: boolean;
/** Playing sound right now. */
active: boolean;
/** The Windows system sounds session. */
system: boolean;
}The apps with sound on the default output device, as the Windows volume
mixer shows them: one row per app, playing apps first, then by name, and the
system sounds row last. Running app names and icons say more than the vitals
do, so this read needs systemControls too. There is no change event: poll
while your mixer is visible, or use
<dd-audio-mixer>, which does.
20 calls per rolling 10 seconds. App 0.4.2 or newer.
dd.system.setAppVolume()
dd.system.setAppVolume(id: string, level: number): Promise<void> // 0..1Sets one app's level, every stream it has. 120 calls per rolling 10
seconds; throttle slider drags like setVolume. A stale id rejects with
NOT_FOUND (list again). App 0.4.2 or newer.
dd.system.setAppMuted()
dd.system.setAppMuted(id: string, muted: boolean): Promise<void>Mutes or unmutes one app. 20 calls per rolling 10 seconds. App 0.4.2 or newer.
dd.system.bluetoothDevices()
dd.system.bluetoothDevices(): Promise<{ devices: BluetoothDevice[] }>
interface BluetoothDevice {
/** Opaque token, valid for this app run. */
id: string;
/** The name Windows shows. */
name: string;
connected: boolean;
/** "audio": headsets and speakers; the rest from the device class. */
kind: "audio" | "input" | "phone" | "computer" | "other";
/** The finer type, or null. App 0.4.5+. */
type:
| "headphones" | "headset" | "earbuds" | "speaker" | "gamepad" | "keyboard"
| "mouse" | "pen" | "watch" | "phone" | "tablet" | "computer" | null;
/** bluetoothConnect / bluetoothDisconnect work on this device. */
canConnect: boolean;
/** 0..100, or null. Always null while disconnected. App 0.4.5+. */
battery: number | null;
/** null when the source does not say. App 0.4.5+. */
charging: boolean | null;
/** Earbuds that report each part (AirPods, Beats), else null. App 0.4.5+. */
parts: {
left: BatteryPart | null;
right: BatteryPart | null;
case: BatteryPart | null;
} | null;
}
interface BatteryPart {
/** 0..100. */
level: number;
charging: boolean;
}The paired Bluetooth devices, classic and Low Energy (a dual-mode device is
one row), connected first. It reads what Windows already knows: no scan, and
no pairing. Send users to openSettings("bluetooth") to add a device. The
list stays readable while the radio is off. Paired device names say more
than the vitals do, so this read needs systemControls too. List again on
onBluetoothDevicesChanged (app 0.4.5
or newer; on older apps, poll while your list is visible), or use
<dd-bluetooth-devices>,
which does both. 20 calls per rolling 10 seconds. App 0.4.2 or newer.
Type. type is the finer device type when the device class names one,
else null. It comes from the classic minor device class or the LE
appearance, so an Xbox controller is kind: "input" and type: "gamepad".
Earbuds that report parts are "earbuds", whatever their class says. Pick
an icon from type first and fall back to kind. App 0.4.5 or newer.
Battery. battery is the level Windows Settings shows for the device.
When Windows has none, the host reads the device's Bluetooth LE Battery
Service directly. A disconnected device always reads null, because Windows
keeps the last level after the link drops. charging is null when the
source does not say, and neither Windows nor the Battery Service does.
AirPods and Beats also report each earbud and the case in parts, where a
part they do not report is null. A device with no level anywhere reads
null: some headsets send their level only to their maker's app. Once a
minute is often enough for a battery display. The three battery fields need
app 0.4.5 or newer; older apps leave them out.
dd.system.onBluetoothDevicesChanged()
dd.system.onBluetoothDevicesChanged(cb: () => void): () => voidA data-free ping when a paired device connects, disconnects, or is paired or
removed, at most once a second: call bluetoothDevices() again. Battery
levels change without a ping, and Windows sets a level a few seconds after a
device connects, so keep a slow poll (once a minute is plenty) for them.
Needs system and systemControls. App 0.4.5 or newer.
dd.system.bluetoothConnect() and bluetoothDisconnect()
dd.system.bluetoothConnect(id: string): Promise<void>
dd.system.bluetoothDisconnect(id: string): Promise<void>Connect or disconnect a paired audio device (canConnect: true), like the
Connect button in Windows Quick Settings; disconnecting does not unpair.
Call them from a click: the host refuses them unless the cursor is inside
your widget, its pane, or your bar, and to a desktop widget in edit mode. They resolve once
Windows has the request, and the link follows within a few seconds, so list
again. A device with no audio connection rejects with INVALID_PARAMS, a
stale id with NOT_FOUND. 5 calls per rolling 10 seconds each. App 0.4.2 or
newer.
dd.system.brightness()
dd.system.brightness(): Promise<{ displays: DisplayBrightness[] }>
interface DisplayBrightness {
/** Opaque token, valid for this app run. */
id: string;
/** "LG ULTRAGEAR"; "Built-in display" for a nameless laptop panel. */
name: string;
/** 0..1 */
level: number;
primary: boolean;
/** The display showing your widget, bar or pane. */
here: boolean;
}The displays whose brightness can be set: a laptop's built-in panel, and
external monitors that answer DDC/CI over the cable. A monitor without
DDC/CI, or with it turned off in its own menu, is left out, so a desktop
often gets an empty list; render nothing then. There is no change event:
poll while visible, or use
<dd-brightness>, which does.
Monitor reads are cached for 15 seconds, so polling faster shows nothing
new. 20 calls per rolling 10 seconds. App 0.4.2 or newer.
dd.system.setBrightness()
dd.system.setBrightness(id: string, level: number): Promise<void> // 0..1Sets one display's brightness. The host serves every display from one
queue: sets for the same display collapse to the newest, and writes to an
external monitor stay 200 ms apart, because many monitors store each write.
Throttle slider drags to roughly one call per 150 ms. A stale id rejects
with NOT_FOUND (list again). 120 calls per rolling 10 seconds. App 0.4.2
or newer.
dd.system.wifiNetworks()
dd.system.wifiNetworks(): Promise<{ networks: WifiNetwork[] }>
interface WifiNetwork {
ssid: string;
/** Signal quality 0..100. */
signal: number;
secured: boolean;
connected: boolean;
/** Windows has a saved profile: wifiConnect only works for these. */
known: boolean;
}Available networks, deduped by SSID (strongest kept), connected first. The host triggers a background scan at most every 30 seconds. 6 calls per rolling 10 seconds.
dd.system.wifiConnect()
dd.system.wifiConnect(ssid: string): Promise<void>Connects only to networks with a saved Windows profile (known: true);
passwords never cross the bridge. No profile rejects NOT_FOUND; send users
to openSettings("wifi") for new networks. 3 calls per rolling 10 seconds.
dd.system.wifiDisconnect()
dd.system.wifiDisconnect(): Promise<void>3 calls per rolling 10 seconds.
dd.system.openSettings()
dd.system.openSettings(panel: SettingsPanel): Promise<void>
type SettingsPanel =
| "sound"
| "power"
| "wifi"
| "battery"
| "bluetooth" // app 0.4.2+
| "display" // app 0.4.2+
| "apps-volume" // app 0.4.2+: the per-app volume mixer page
| "privacy-radios" // app 0.4.2+: where Windows lets apps control the radiosOpens a Windows settings page. panel is a whitelist key mapped host-side;
widgets can never launch arbitrary settings targets. An app older than 0.4.2
rejects the newer keys with INVALID_PARAMS. 3 calls per rolling 10
seconds.
dd.power
Needs the power permission, which is deliberately separate from
systemControls: granting a volume slider is not granting the power to turn
the machine off.
dd.power.capabilities()
dd.power.capabilities(): Promise<{ sleep: boolean; hibernate: boolean }>What this machine can actually do, read from its firmware. Check it before
drawing a Sleep button: many desktops have no sleep state at all, and a
button that fails every press reads as a broken widget. An unreadable answer
reports true for both, so a machine that would have worked never loses its
button. No gesture gate (a widget needs this before any interaction). 30
calls per rolling 10 seconds.
dd.power.run()
dd.power.run(action: "shutdown" | "restart" | "restartAdvanced" | "sleep" | "lock" | "signOut"): Promise<{ ran: boolean }>Ends or suspends the Windows session. Only these six actions exist; nothing else (no flags, no timeouts, no targets) reaches the OS. Refused unless the real cursor is inside the widget, and refused to a desktop widget in edit mode (to a bar too before app 0.5.1), so a background timer can never trigger it. From a bar, the test is the bar window or its pane (app 0.3.7 or newer; older apps refuse bars outright).
restartAdvanced restarts into the advanced startup menu (recovery,
firmware settings, safe mode), what shutdown /r /o does. App 0.3.7 or
newer; an older app rejects it INVALID_PARAMS with "unknown power action",
so catch that to feature-detect.
shutdown, restart, restartAdvanced and signOut first raise a host-drawn confirmation the
widget cannot draw, style, or auto-answer; Cancel holds the initial focus,
and Escape, closing the window, or a timeout all answer no. ran: false
means the user declined, which is the moment to re-enable your button:
const { ran } = await dd.power.run("shutdown");
if (!ran) button.disabled = false;sleep and lock skip the confirmation, being instantly reversible. Nothing
here forces applications to close: an app with unsaved work still gets
Windows' own "this app is preventing shutdown" screen. On actions that
proceed, the session usually ends before the promise settles. 6 calls per
rolling 10 seconds.