Skip to content

Reactive Watch API ​

Instead of calling a getter on a timer, you can subscribe to a kind of device information and receive a callback whenever the value changes.

Each watch* function delivers the current value immediately, then invokes your callback again on every subsequent change. It returns an async unwatch function that stops watching and removes the listener.

Functions ​

FunctionDeliversWatches for
watchBattery()BatteryInfoCharge level, charging state, health
watchNetwork()NetworkInfoConnectivity changes (Wi-Fi ↔ offline, IP, MAC)
watchStorage()StorageInfoTotal / free space
watchDisplay()DisplayInfoResolution, scale, refresh rate (e.g. plugging in a monitor)
watchDevice()DeviceInfoResponseDevice identity fields (rarely changes)

Signatures ​

typescript
function watchBattery(callback: (info: BatteryInfo) => void, options?: WatchOptions): Promise<UnwatchFn>
function watchNetwork(callback: (info: NetworkInfo) => void, options?: WatchOptions): Promise<UnwatchFn>
function watchStorage(callback: (info: StorageInfo) => void, options?: WatchOptions): Promise<UnwatchFn>
function watchDisplay(callback: (info: DisplayInfo) => void, options?: WatchOptions): Promise<UnwatchFn>
function watchDevice(callback: (info: DeviceInfoResponse) => void, options?: WatchOptions): Promise<UnwatchFn>

Options & return type ​

typescript
interface WatchOptions {
  /**
   * Polling interval in milliseconds, used only for kinds that fall back to
   * polling (native event-driven kinds ignore it). Only the first subscriber
   * for a given kind sets the interval.
   */
  intervalMs?: number;
}

/** Stops a watcher and removes its listener. Returned by every `watch*` function. */
type UnwatchFn = () => Promise<void>;

Basic usage ​

typescript
import { watchBattery } from 'tauri-plugin-device-info-api';

// Called immediately with the current value, then on every change.
const unwatch = await watchBattery((battery) => {
  console.log(`Battery: ${battery.level}% (charging: ${battery.isCharging})`);
});

// Later, when you no longer need updates:
await unwatch();

Cleaning up multiple watchers ​

Always stop watchers when the component/view is torn down, otherwise the backend monitor stays alive.

typescript
import { watchBattery, watchNetwork, watchDisplay } from 'tauri-plugin-device-info-api';
import type { UnwatchFn } from 'tauri-plugin-device-info-api';

const unwatchers: UnwatchFn[] = [];

unwatchers.push(await watchBattery((info) => { /* ... */ }));
unwatchers.push(await watchNetwork((info) => { /* ... */ }));
unwatchers.push(await watchDisplay((info) => { /* ... */ }));

// On teardown:
await Promise.all(unwatchers.map((unwatch) => unwatch()));

How updates are detected ​

The public API and the emitted events are identical across platforms, but the engine behind each kind differs:

  • Where the OS exposes a native, event-driven source, updates are delivered the instant the OS reports a change and the CPU stays idle in between.
  • Otherwise a change-detecting poller reads the value on an interval and emits only when it actually changed.
KindmacOSWindows / Linux
batteryNative — IOKit power notificationsPolled
displayNative — Core Graphics reconfiguration callbackPolled
networkNative — SystemConfiguration reachabilityPolled
storagePolled (no OS change event)Polled
devicePolled (rarely changes)Polled

Polling intervals ​

intervalMs only applies to polled kinds. Defaults and minimums are per kind, because some getters are expensive and some values rarely change:

KindDefaultMinimum
device60000 ms10000 ms
storage10000 ms1000 ms
others (when polled)2000 ms250 ms
typescript
// Poll storage a little more eagerly (still floored at 1000 ms):
const unwatch = await watchStorage((info) => console.log(info.freeSpace), {
  intervalMs: 2000,
});

Reference counting

Subscribers to the same kind share a single monitor. It starts on the first subscriber and is torn down once the last one calls unwatch(). Only the first subscriber's intervalMs takes effect.

Permissions ​

The watch API requires the start-watching and stop-watching commands. device-info:default already includes them; to grant them individually:

json
{
  "permissions": [
    "device-info:allow-start-watching",
    "device-info:allow-stop-watching"
  ]
}

Example output ​

watchBattery delivers the same shape as getBatteryInfo:

json
{
  "level": 85,
  "isCharging": true,
  "health": "Good"
}

Notes ​

  • The current value is always delivered once, immediately, so you don't need a separate getBatteryInfo() call to seed initial state.
  • unwatch() is async — await it if you need the backend monitor fully torn down before continuing.
  • On a kind with a native source (e.g. battery on macOS), intervalMs is ignored.

Released under the MIT License.