dualsense-ts
Version:
The natural interface for your DualSense and DualSense Access controllers, with Typescript
140 lines • 6.31 kB
TypeScript
import { Dualsense } from "./dualsense";
import { Input, InputParams } from "./input";
/** The state exposed by a DualsenseManager via the Input system */
export interface DualsenseManagerState {
/** Number of currently connected controllers */
active: number;
/** All managed controllers, keyed by slot index (player number) */
players: ReadonlyMap<number, Dualsense>;
}
/** Settings for the DualsenseManager */
export interface DualsenseManagerParams extends InputParams {
/** Polling interval (ms) for discovering new devices in Node.js. Default: 2000 */
discoveryInterval?: number;
/** Whether to automatically assign player LED patterns. Default: true */
autoAssignPlayerLeds?: boolean;
}
/**
* Manages multiple Dualsense controllers. Automatically discovers devices,
* assigns player LEDs, and provides indexed access to each controller.
*
* Extends Input so that events from all managed controllers bubble up:
* - `change` fires when the controller count changes or any controller input changes
* - `press` / `release` bubble from any managed controller (including connection state)
*/
export declare class DualsenseManager extends Input<DualsenseManagerState> {
/** Current manager state: active count and player map */
state: DualsenseManagerState;
/** Whether to auto-assign player LED patterns on connection */
autoAssignPlayerLeds: boolean;
/** Player LED bitmask patterns indexed by slot number (0–30) */
private readonly playerPatterns;
/** All controller slots, indexed by slot number */
private readonly slots;
/** Map from node-hid serial to slot index — best-effort, used as a fallback */
private readonly serialToSlot;
/** Map from canonical hardware identity to slot index — preferred when available */
private readonly identityToSlot;
/** Discovery polling timer (Node.js only) */
private discoveryTimer?;
/** Whether we're running in a browser environment */
private readonly isBrowser;
constructor(params?: DualsenseManagerParams);
get active(): boolean;
/**
* All managed controller instances (including disconnected ones awaiting
* reconnection). Excludes provisional slots whose identity is still being
* resolved — those become visible only after firmware info loads, to
* avoid surfacing controllers that may be merged into an existing slot.
*/
get controllers(): readonly Dualsense[];
/** Number of managed controllers (including disconnected ones awaiting reconnection) */
get count(): number;
/** Get a controller by slot index */
get(index: number): Dualsense | undefined;
/** Iterate over all managed controllers */
[](): IterableIterator<Dualsense>;
/** Slots that are visible to the consumer (i.e. identity has been resolved) */
private publicSlots;
/**
* True while at least one controller has been discovered but is still
* waiting for firmware info to load. Useful for showing a "connecting"
* state in the UI without surfacing the unresolved slot itself.
*/
get pending(): boolean;
/**
* Override the player LED pattern for a given slot index.
* @param index Slot index (0-based)
* @param bitmask 5-bit LED bitmask (0x00–0x1f)
*/
setPlayerPattern(index: number, bitmask: number): void;
/** Get the player LED pattern for a given slot index */
getPlayerPattern(index: number): number;
/**
* Release a controller slot, freeing it for reuse.
* If the controller is still connected, it will be disconnected.
* @param index Slot index to release
*/
release(index: number): void;
/**
* Release all disconnected controller slots.
* Connected controllers are not affected.
*/
releaseDisconnected(): void;
/**
* Stop discovery and disconnect all controllers.
*/
dispose(): void;
/**
* For WebHID: returns a click handler that opens the device picker,
* allowing the user to select multiple controllers at once.
*/
getRequest(): () => Promise<void>;
/** Previous state snapshot, for deduplication */
private lastActive;
private lastPlayerCount;
/** Fingerprint of the last published player set (slot indices + connected flags) */
private lastPlayerKey;
/** Build a new state snapshot and push it through InputSet */
private updateState;
/**
* Create a Dualsense instance and register it in a (provisional) slot.
* The caller is responsible for opening the device on the provider — the
* manager treats this as the *only* path that opens new devices, so
* identity matching can run before the slot becomes visible.
*
* Note: identity is the sole reconnection key. We do NOT key on node-hid's
* serialNumber because it can be missing or wrong. Path is tracked only
* so we can re-target the same device on transplant.
*/
private createSlot;
/**
* Called when a slot's HID layer has finished reading firmware/factory info.
* If the resolved identity matches a *different* (disconnected) slot, the
* underlying device is transplanted into that slot's existing provider so
* the consumer's Dualsense reference is preserved across reconnect.
*/
private handleSlotReady;
/** Mark a slot as visible to consumers and publish state */
private promoteSlot;
/**
* Move the device handle from `from` into `into`'s existing provider so
* the existing Dualsense instance reconnects in place. Then remove `from`.
*/
private transplant;
/** Remove a slot without firing the player-LED reshuffle */
private dropSlot;
private startNodeDiscovery;
/**
* Handle a newly discovered device from enumeration. Opens the device on
* a fresh provider, which adds it to `claimedDevices` so subsequent polls
* skip it. Identity matching (and any merge into a disconnected slot)
* happens later, once firmware info has been read.
*/
private processDiscoveredDevice;
private startWebDiscovery;
/** HIDDevice objects we've already handed to a provider */
private readonly knownWebDevices;
private addWebDevice;
}
//# sourceMappingURL=manager.d.ts.map