The fleet model
Multi-console support is not a feature of this SDK — it is the shape of the SDK. There is one set of primitives, and they are fleet-shaped. Connecting a single console is simply the case where the fleet has one member. This page explains why that is the model and what it means for your code.
One unified Device record
Section titled “One unified Device record”Discovered, saved, and connected are facets of one Device record, not three
lists to reconcile. A device you just saw in a scan, a device you saved last week,
and a device you are connected to right now are all the same type — the difference
is the value of its presence, saved, and connection fields.
That is why a connect screen renders a single useDevices() array with no
stitching: “nearby but not saved” is a filter over one type
(useDevices({ available: true, saved: false })), not a join across two lists.
The old world had a discovered list and a fleet list and hand-reconciled them on
every render; here there is nothing to reconcile.
Every per-device hook takes an id
Section titled “Every per-device hook takes an id”Because there are N devices, every per-device operation names its device
explicitly by DeviceId:
useLiveMetrics(deviceId); // this device's framesuseConsoleControls(deviceId); // this device's controlsuseDeviceActions(deviceId); // this device's actionsuseConnectDevice().connect(target); // connect a specific deviceThere is no ambient “the console” these operate on. useDevices() returns the
list; you pass a specific id to each per-device hook. This is the whole
multi-console API — there is no second set of hooks for “multi” mode, because
there was never a “single” mode to contrast it with.
No active-device global, no pinning
Section titled “No active-device global, no pinning”The runtime has no “current device” field. There is no active-device global, no pin, no data-driven “switch to whichever console started a workout” heuristic. Those mechanisms existed only to guess which console a single-console API should talk to — and every one of them was a source of bugs (revision-counter hacks, competing streams, pin/unpin gymnastics).
Selecting which device a screen shows is app UI, not SDK state. Your app holds
the selected DeviceId in its own state (route param, local state, wherever it
belongs) and passes it to the per-device hooks. The SDK does not need to know, and
keeping that decision out of the runtime is what makes the absence of an
active-device field structural rather than a convention that erodes.
Why “primary console” was rejected
Section titled “Why “primary console” was rejected”A “primary console” heuristic — auto-selecting one device as the default target — was considered and deliberately rejected. Any such rule (most-recently-connected, strongest-signal, first-in-workout) is right in some flows and wrong in others, and the moment it is wrong the consumer needs an override, which reintroduces the explicit-id API anyway — now alongside a heuristic that fights it. The explicit-id model is the override with no heuristic to fight. Choosing which device is “primary” is a product decision your app makes with its own UI, on top of the unified list.
N=1 is the smallest fleet
Section titled “N=1 is the smallest fleet”A single-console app writes the same code a fleet app writes. It calls
useDevices() (which happens to return one device), passes that device’s id to
the per-device hooks, and renders one set of tiles. Connecting a second console
changes zero lines: the same list now has two entries, the same
session.devices.map(...) renders two tiles, and the same per-device hooks key on
two ids.
The one concession to single-console ergonomics is useDevice(query) — one-line
sugar for useDevices(query)[0] ?? null. It is the N=1 convenience over the
fleet-shaped model, sharing its exact types, state, and lifecycle — not a separate
single-console API.
const rower = useDevice({ modality: 'rower', connected: true }); // Device | nullThat is the entire story: one model, keyed by id, with N=1 as its smallest case.
Reference
Section titled “Reference”Device·DeviceId·DeviceQueryuseDevices·useDevice- Related: Architecture · Connection lifecycle · Connecting devices.