dualsense-ts 6.2.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LINUX_HID.md +85 -0
  2. package/README.md +83 -12
  3. package/dist/dualsense.d.ts +28 -8
  4. package/dist/dualsense.d.ts.map +1 -1
  5. package/dist/dualsense.js +57 -17
  6. package/dist/dualsense.js.map +1 -1
  7. package/dist/hid/bt_checksum.d.ts +7 -0
  8. package/dist/hid/bt_checksum.d.ts.map +1 -1
  9. package/dist/hid/bt_checksum.js +33 -1
  10. package/dist/hid/bt_checksum.js.map +1 -1
  11. package/dist/hid/dualsense_hid.d.ts +77 -0
  12. package/dist/hid/dualsense_hid.d.ts.map +1 -1
  13. package/dist/hid/dualsense_hid.js +193 -0
  14. package/dist/hid/dualsense_hid.js.map +1 -1
  15. package/dist/hid/factory_info.d.ts +53 -0
  16. package/dist/hid/factory_info.d.ts.map +1 -0
  17. package/dist/hid/factory_info.js +166 -0
  18. package/dist/hid/factory_info.js.map +1 -0
  19. package/dist/hid/firmware_info.d.ts +46 -0
  20. package/dist/hid/firmware_info.d.ts.map +1 -0
  21. package/dist/hid/firmware_info.js +109 -0
  22. package/dist/hid/firmware_info.js.map +1 -0
  23. package/dist/hid/hid_provider.d.ts +12 -0
  24. package/dist/hid/hid_provider.d.ts.map +1 -1
  25. package/dist/hid/hid_provider.js +13 -0
  26. package/dist/hid/hid_provider.js.map +1 -1
  27. package/dist/hid/index.d.ts +3 -0
  28. package/dist/hid/index.d.ts.map +1 -1
  29. package/dist/hid/index.js +3 -0
  30. package/dist/hid/index.js.map +1 -1
  31. package/dist/hid/node_hid_provider.d.ts +2 -0
  32. package/dist/hid/node_hid_provider.d.ts.map +1 -1
  33. package/dist/hid/node_hid_provider.js +14 -0
  34. package/dist/hid/node_hid_provider.js.map +1 -1
  35. package/dist/hid/pairing_info.d.ts +9 -0
  36. package/dist/hid/pairing_info.d.ts.map +1 -0
  37. package/dist/hid/pairing_info.js +33 -0
  38. package/dist/hid/pairing_info.js.map +1 -0
  39. package/dist/hid/web_hid_provider.d.ts +14 -0
  40. package/dist/hid/web_hid_provider.d.ts.map +1 -1
  41. package/dist/hid/web_hid_provider.js +79 -8
  42. package/dist/hid/web_hid_provider.js.map +1 -1
  43. package/dist/id.d.ts +4 -0
  44. package/dist/id.d.ts.map +1 -1
  45. package/dist/manager.d.ts +57 -4
  46. package/dist/manager.d.ts.map +1 -1
  47. package/dist/manager.js +248 -66
  48. package/dist/manager.js.map +1 -1
  49. package/nodehid_example/debug.ts +43 -13
  50. package/nodehid_example/single.ts +29 -0
  51. package/package.json +1 -1
  52. package/src/dualsense.ts +73 -23
  53. package/src/hid/bt_checksum.ts +39 -0
  54. package/src/hid/dualsense_hid.ts +230 -0
  55. package/src/hid/factory_info.ts +206 -0
  56. package/src/hid/firmware_info.ts +157 -0
  57. package/src/hid/hid_provider.ts +22 -0
  58. package/src/hid/index.ts +3 -0
  59. package/src/hid/node_hid_provider.ts +14 -0
  60. package/src/hid/pairing_info.ts +33 -0
  61. package/src/hid/web_hid_provider.ts +87 -8
  62. package/src/id.ts +5 -0
  63. package/src/manager.ts +285 -71
  64. package/webhid_example/build/asset-manifest.json +3 -3
  65. package/webhid_example/build/index.html +1 -1
  66. package/webhid_example/build/static/js/main.1c1a2c23.js +3 -0
  67. package/webhid_example/build/static/js/main.1c1a2c23.js.map +1 -0
  68. package/webhid_example/src/App.tsx +7 -1
  69. package/webhid_example/src/hud/AudioIndicator.tsx +116 -0
  70. package/webhid_example/src/hud/BatteryIndicator.tsx +4 -2
  71. package/webhid_example/src/hud/ColorIndicator.tsx +72 -0
  72. package/webhid_example/src/hud/ControllerConnection.tsx +29 -2
  73. package/webhid_example/src/hud/Debugger.tsx +31 -1
  74. package/webhid_example/src/hud/LightbarFadeButtons.tsx +2 -2
  75. package/webhid_example/src/hud/MuteLedControls.tsx +3 -2
  76. package/webhid_example/src/hud/index.tsx +2 -0
  77. package/webhid_example/build/static/js/main.2ac31d24.js +0 -3
  78. package/webhid_example/build/static/js/main.2ac31d24.js.map +0 -1
  79. /package/webhid_example/build/static/js/{main.2ac31d24.js.LICENSE.txt → main.1c1a2c23.js.LICENSE.txt} +0 -0
package/dist/manager.js CHANGED
@@ -72,10 +72,20 @@ class DualsenseManager extends input_1.Input {
72
72
  this.playerPatterns = [...DEFAULT_PLAYER_PATTERNS];
73
73
  /** All controller slots, indexed by slot number */
74
74
  this.slots = [];
75
- /** Map from serial number to slot index, for reconnection matching */
75
+ /** Map from node-hid serial to slot index — best-effort, used as a fallback */
76
76
  this.serialToSlot = new Map();
77
+ /** Map from canonical hardware identity to slot index — preferred when available */
78
+ this.identityToSlot = new Map();
77
79
  /** Whether we're running in a browser environment */
78
80
  this.isBrowser = typeof window !== "undefined";
81
+ // --- Private ---
82
+ /** Previous state snapshot, for deduplication */
83
+ this.lastActive = 0;
84
+ this.lastPlayerCount = 0;
85
+ /** Fingerprint of the last published player set (slot indices + connected flags) */
86
+ this.lastPlayerKey = "";
87
+ /** HIDDevice objects we've already handed to a provider */
88
+ this.knownWebDevices = new WeakSet();
79
89
  this.autoAssignPlayerLeds = params.autoAssignPlayerLeds ?? true;
80
90
  if (this.isBrowser) {
81
91
  this.startWebDiscovery();
@@ -87,21 +97,41 @@ class DualsenseManager extends input_1.Input {
87
97
  get active() {
88
98
  return this.state.active > 0;
89
99
  }
90
- /** All managed controller instances (including disconnected ones awaiting reconnection) */
100
+ /**
101
+ * All managed controller instances (including disconnected ones awaiting
102
+ * reconnection). Excludes provisional slots whose identity is still being
103
+ * resolved — those become visible only after firmware info loads, to
104
+ * avoid surfacing controllers that may be merged into an existing slot.
105
+ */
91
106
  get controllers() {
92
- return this.slots.map((s) => s.controller);
107
+ return this.publicSlots().map((s) => s.controller);
93
108
  }
94
109
  /** Number of managed controllers (including disconnected ones awaiting reconnection) */
95
110
  get count() {
96
- return this.slots.length;
111
+ return this.publicSlots().length;
97
112
  }
98
113
  /** Get a controller by slot index */
99
114
  get(index) {
100
- return this.slots[index]?.controller;
115
+ const slot = this.slots[index];
116
+ if (!slot || slot.provisional)
117
+ return undefined;
118
+ return slot.controller;
101
119
  }
102
120
  /** Iterate over all managed controllers */
103
121
  [Symbol.iterator]() {
104
- return this.slots.map((s) => s.controller).values();
122
+ return this.publicSlots().map((s) => s.controller).values();
123
+ }
124
+ /** Slots that are visible to the consumer (i.e. identity has been resolved) */
125
+ publicSlots() {
126
+ return this.slots.filter((s) => !s.provisional);
127
+ }
128
+ /**
129
+ * True while at least one controller has been discovered but is still
130
+ * waiting for firmware info to load. Useful for showing a "connecting"
131
+ * state in the UI without surfacing the unresolved slot itself.
132
+ */
133
+ get pending() {
134
+ return this.slots.some((s) => s.provisional);
105
135
  }
106
136
  /**
107
137
  * Override the player LED pattern for a given slot index.
@@ -131,16 +161,22 @@ class DualsenseManager extends input_1.Input {
131
161
  return;
132
162
  // Disconnect the HID provider and release the claimed device
133
163
  slot.controller.hid.provider.disconnect();
134
- // Remove serial mapping
164
+ // Remove identity / serial mappings
165
+ if (slot.identity) {
166
+ this.identityToSlot.delete(slot.identity);
167
+ }
135
168
  if (slot.serial) {
136
169
  this.serialToSlot.delete(slot.serial);
137
170
  }
138
171
  // Remove the slot (shift remaining slots down)
139
172
  this.slots.splice(index, 1);
140
- // Re-index serial mappings after splice
173
+ // Re-index identity / serial mappings after splice
141
174
  for (let i = index; i < this.slots.length; i++) {
142
175
  const s = this.slots[i];
143
176
  s.index = i;
177
+ if (s.identity) {
178
+ this.identityToSlot.set(s.identity, i);
179
+ }
144
180
  if (s.serial) {
145
181
  this.serialToSlot.set(s.serial, i);
146
182
  }
@@ -186,51 +222,64 @@ class DualsenseManager extends input_1.Input {
186
222
  }
187
223
  return web_hid_provider_1.WebHIDProvider.getMultiRequest((device) => { this.addWebDevice(device); });
188
224
  }
189
- // --- Private ---
190
225
  /** Build a new state snapshot and push it through InputSet */
191
226
  updateState() {
192
227
  const players = new Map();
228
+ let activeCount = 0;
229
+ let key = "";
193
230
  for (const slot of this.slots) {
231
+ if (slot.provisional)
232
+ continue;
194
233
  players.set(slot.index, slot.controller);
234
+ const connected = slot.controller.connection.active;
235
+ if (connected)
236
+ activeCount += 1;
237
+ key += `${slot.index}:${connected ? "1" : "0"},`;
195
238
  }
196
- const activeCount = this.slots.filter((s) => s.controller.connection.active).length;
197
- // Always creates a new object so BasicComparator detects the change
239
+ // Suppress no-op publishes (e.g. provisional slots churning without
240
+ // changing the visible state) to avoid noisy change events.
241
+ if (activeCount === this.lastActive &&
242
+ players.size === this.lastPlayerCount &&
243
+ key === this.lastPlayerKey) {
244
+ return;
245
+ }
246
+ this.lastActive = activeCount;
247
+ this.lastPlayerCount = players.size;
248
+ this.lastPlayerKey = key;
198
249
  this[input_1.InputSet]({ active: activeCount, players });
199
250
  }
200
- /** Create a Dualsense instance and register it in a slot */
251
+ /**
252
+ * Create a Dualsense instance and register it in a (provisional) slot.
253
+ * The caller is responsible for opening the device on the provider — the
254
+ * manager treats this as the *only* path that opens new devices, so
255
+ * identity matching can run before the slot becomes visible.
256
+ *
257
+ * Note: identity is the sole reconnection key. We do NOT key on node-hid's
258
+ * serialNumber because it's frequently missing or wrong (especially over
259
+ * Bluetooth on Linux). Path is tracked only so we can re-target the same
260
+ * device on transplant.
261
+ */
201
262
  createSlot(provider, serial, path) {
202
- // Check if this serial already has a slot (reconnection)
203
- if (serial) {
204
- const existingIndex = this.serialToSlot.get(serial);
205
- if (existingIndex !== undefined) {
206
- const existingSlot = this.slots[existingIndex];
207
- if (existingSlot && !existingSlot.controller.connection.active) {
208
- // Reconnect to existing slot: update the provider target
209
- if (provider instanceof node_hid_provider_1.NodeHIDProvider) {
210
- const existingProvider = existingSlot.controller.hid
211
- .provider;
212
- existingProvider.targetPath = path;
213
- existingProvider.targetSerial = serial;
214
- }
215
- existingSlot.path = path;
216
- // Release the new provider since we're reusing the existing one
217
- provider.disconnect();
218
- return existingSlot;
219
- }
220
- }
221
- }
222
263
  const hid = new dualsense_hid_1.DualsenseHID(provider);
223
264
  const controller = new dualsense_1.Dualsense({ hid });
224
265
  const index = this.slots.length;
225
- const slot = { controller, serial, path, index };
266
+ const slot = {
267
+ controller,
268
+ serial,
269
+ path,
270
+ index,
271
+ // Hide from public state until firmware info has been read and any
272
+ // identity-based merge has had a chance to run.
273
+ provisional: true,
274
+ };
226
275
  this.slots.push(slot);
227
276
  if (serial) {
228
277
  this.serialToSlot.set(serial, index);
229
278
  }
230
- // Assign player LEDs — set immediately and re-apply on every connect,
231
- // since the controller may not be connected yet at slot creation time.
279
+ // Assign player LEDs — skip for provisional slots (they may get
280
+ // transplanted to a different index). Re-apply on every connect.
232
281
  const applyPlayerLeds = () => {
233
- if (this.autoAssignPlayerLeds) {
282
+ if (this.autoAssignPlayerLeds && !slot.provisional) {
234
283
  controller.playerLeds.set(this.playerPatterns[slot.index] ?? 0);
235
284
  }
236
285
  };
@@ -242,9 +291,140 @@ class DualsenseManager extends input_1.Input {
242
291
  }
243
292
  this.updateState();
244
293
  });
294
+ // Hook firmware-info readiness so we can perform identity-based slot
295
+ // matching once the controller's hardware identity is known. This is
296
+ // far more reliable than node-hid's serial number, which is
297
+ // frequently missing or wrong (especially over Bluetooth on Linux).
298
+ if (!slot.readyHooked) {
299
+ slot.readyHooked = true;
300
+ hid.onReady(() => this.handleSlotReady(slot));
301
+ }
245
302
  this.updateState();
246
303
  return slot;
247
304
  }
305
+ /**
306
+ * Called when a slot's HID layer has finished reading firmware/factory info.
307
+ * If the resolved identity matches a *different* (disconnected) slot, the
308
+ * underlying device is transplanted into that slot's existing provider so
309
+ * the consumer's Dualsense reference is preserved across reconnect.
310
+ */
311
+ handleSlotReady(slot) {
312
+ const identity = slot.controller.hid.identity;
313
+ // No identity at all (firmware read failed completely after retries) —
314
+ // promote the slot anyway so the consumer can still use it. We just
315
+ // won't be able to merge it on reconnect.
316
+ if (!identity) {
317
+ this.promoteSlot(slot);
318
+ return;
319
+ }
320
+ const existingIndex = this.identityToSlot.get(identity);
321
+ // First time we've seen this identity — claim it for this slot.
322
+ if (existingIndex === undefined) {
323
+ slot.identity = identity;
324
+ this.identityToSlot.set(identity, slot.index);
325
+ this.promoteSlot(slot);
326
+ return;
327
+ }
328
+ // We already have a slot for this identity — make sure it's not just us.
329
+ if (existingIndex === slot.index) {
330
+ this.promoteSlot(slot);
331
+ return;
332
+ }
333
+ const existingSlot = this.slots[existingIndex];
334
+ if (!existingSlot) {
335
+ // Stale mapping — overwrite.
336
+ slot.identity = identity;
337
+ this.identityToSlot.set(identity, slot.index);
338
+ this.promoteSlot(slot);
339
+ return;
340
+ }
341
+ // If the existing slot is currently connected, both slots map to the
342
+ // same hardware (shouldn't normally happen). Prefer the older slot
343
+ // and drop the new one without ever publishing it.
344
+ if (existingSlot.controller.connection.active) {
345
+ this.dropSlot(slot);
346
+ return;
347
+ }
348
+ // Existing slot is disconnected — transplant the new device into it.
349
+ // The new (provisional) slot is dropped before any state is published,
350
+ // so the consumer only ever sees the original slot reconnect in place.
351
+ this.transplant(slot, existingSlot);
352
+ }
353
+ /** Mark a slot as visible to consumers and publish state */
354
+ promoteSlot(slot) {
355
+ if (!slot.provisional)
356
+ return;
357
+ slot.provisional = false;
358
+ if (this.autoAssignPlayerLeds) {
359
+ slot.controller.playerLeds.set(this.playerPatterns[slot.index] ?? 0);
360
+ }
361
+ this.updateState();
362
+ }
363
+ /**
364
+ * Move the device handle from `from` into `into`'s existing provider so
365
+ * the existing Dualsense instance reconnects in place. Then remove `from`.
366
+ */
367
+ transplant(from, into) {
368
+ const fromProvider = from.controller.hid.provider;
369
+ const intoProvider = into.controller.hid.provider;
370
+ if (fromProvider instanceof web_hid_provider_1.WebHIDProvider &&
371
+ intoProvider instanceof web_hid_provider_1.WebHIDProvider &&
372
+ fromProvider.device) {
373
+ // Move the open HIDDevice handle from the source provider to the
374
+ // destination. We can't close + reopen here — that would race with
375
+ // the destination's attach() call. Instead we abandon the source
376
+ // provider in place (its slot is about to be dropped) and let the
377
+ // destination take over the same handle. The source's input listener
378
+ // will be GC'd along with the source provider once nothing else
379
+ // references the dropped slot's Dualsense.
380
+ const device = fromProvider.device;
381
+ fromProvider.device = undefined;
382
+ if (fromProvider.deviceId) {
383
+ hid_provider_1.HIDProvider.claimedDevices.delete(fromProvider.deviceId);
384
+ fromProvider.deviceId = undefined;
385
+ }
386
+ intoProvider.replaceDevice(device);
387
+ }
388
+ else if (fromProvider instanceof node_hid_provider_1.NodeHIDProvider &&
389
+ intoProvider instanceof node_hid_provider_1.NodeHIDProvider) {
390
+ // node-hid HID handles can't be moved between providers, so we close
391
+ // the source (releasing its path claim) and re-open the same path on
392
+ // the destination provider — preserving the existing Dualsense
393
+ // instance and its subscribers.
394
+ const newPath = from.path;
395
+ const newSerial = from.serial;
396
+ fromProvider.disconnect();
397
+ intoProvider.targetPath = newPath;
398
+ intoProvider.targetSerial = newSerial;
399
+ into.path = newPath;
400
+ into.serial = newSerial;
401
+ void Promise.resolve(intoProvider.connect()).catch(() => {
402
+ /* errors surface via provider.onError */
403
+ });
404
+ }
405
+ this.dropSlot(from);
406
+ }
407
+ /** Remove a slot without firing the player-LED reshuffle */
408
+ dropSlot(slot) {
409
+ const idx = this.slots.indexOf(slot);
410
+ if (idx === -1)
411
+ return;
412
+ if (slot.identity)
413
+ this.identityToSlot.delete(slot.identity);
414
+ if (slot.serial)
415
+ this.serialToSlot.delete(slot.serial);
416
+ this.slots.splice(idx, 1);
417
+ // Re-index the trailing slots
418
+ for (let i = idx; i < this.slots.length; i++) {
419
+ const s = this.slots[i];
420
+ s.index = i;
421
+ if (s.identity)
422
+ this.identityToSlot.set(s.identity, i);
423
+ if (s.serial)
424
+ this.serialToSlot.set(s.serial, i);
425
+ }
426
+ this.updateState();
427
+ }
248
428
  // --- Node.js discovery ---
249
429
  startNodeDiscovery(intervalMs) {
250
430
  const poll = async () => {
@@ -262,32 +442,27 @@ class DualsenseManager extends input_1.Input {
262
442
  void poll();
263
443
  this.discoveryTimer = setInterval(() => void poll(), intervalMs);
264
444
  }
265
- /** Handle a newly discovered device from enumeration */
445
+ /**
446
+ * Handle a newly discovered device from enumeration. Opens the device on
447
+ * a fresh provider, which adds it to `claimedDevices` so subsequent polls
448
+ * skip it. Identity matching (and any merge into a disconnected slot)
449
+ * happens later, once firmware info has been read.
450
+ */
266
451
  processDiscoveredDevice(device) {
267
452
  if (hid_provider_1.HIDProvider.claimedDevices.has(device.path))
268
453
  return;
269
- // Check if this serial matches a disconnected slot
270
- if (device.serialNumber !== undefined) {
271
- const existingSlotIndex = this.serialToSlot.get(device.serialNumber);
272
- if (existingSlotIndex !== undefined) {
273
- const slot = this.slots[existingSlotIndex];
274
- if (!slot.controller.connection.active) {
275
- // Update the existing provider to reconnect via this path
276
- const provider = slot.controller.hid.provider;
277
- provider.targetPath = device.path;
278
- provider.targetSerial = device.serialNumber;
279
- slot.path = device.path;
280
- // The Dualsense's internal 200ms loop will call provider.connect()
281
- return;
282
- }
283
- }
284
- }
285
- // New device: create a provider and slot
286
454
  const provider = new node_hid_provider_1.NodeHIDProvider({
287
455
  devicePath: device.path,
288
456
  serialNumber: device.serialNumber,
289
457
  });
290
458
  this.createSlot(provider, device.serialNumber, device.path);
459
+ // Drive the connection. The Dualsense instance no longer polls in
460
+ // managed mode, so the manager owns this. claimedDevices is added
461
+ // synchronously inside connect() on success, preventing duplicate
462
+ // discovery on the next poll tick.
463
+ void Promise.resolve(provider.connect()).catch(() => {
464
+ /* errors surface via provider.onError */
465
+ });
291
466
  }
292
467
  // --- WebHID discovery ---
293
468
  startWebDiscovery() {
@@ -296,21 +471,28 @@ class DualsenseManager extends input_1.Input {
296
471
  navigator.hid.addEventListener("connect", ({ device }) => {
297
472
  this.addWebDevice(device);
298
473
  });
299
- // Enumerate already-permitted devices
300
- void web_hid_provider_1.WebHIDProvider.enumerate().then((devices) => {
301
- for (const device of devices) {
302
- this.addWebDevice(device);
303
- }
304
- });
474
+ // Poll for permitted devices. The WebHID connect event only fires
475
+ // for newly-permitted devices, not for already-permitted devices
476
+ // that physically reconnect. Periodic enumeration catches those.
477
+ const poll = () => {
478
+ void web_hid_provider_1.WebHIDProvider.enumerate().then((devices) => {
479
+ for (const device of devices) {
480
+ this.addWebDevice(device);
481
+ }
482
+ });
483
+ };
484
+ poll();
485
+ this.discoveryTimer = setInterval(poll, 2000);
305
486
  }
306
487
  }
307
488
  addWebDevice(device) {
308
- // Check if any existing slot's provider already has this device
309
- for (const slot of this.slots) {
310
- const provider = slot.controller.hid.provider;
311
- if (provider.device === device)
312
- return; // Already managed
313
- }
489
+ // WeakSet tracks object identity enumerate() returns the same objects
490
+ // for still-connected devices, so this deduplicates across polls.
491
+ // On reconnect, the browser provides a fresh HIDDevice object, so it
492
+ // passes this check and creates a new provisional slot.
493
+ if (this.knownWebDevices.has(device))
494
+ return;
495
+ this.knownWebDevices.add(device);
314
496
  const provider = new web_hid_provider_1.WebHIDProvider({ device });
315
497
  this.createSlot(provider, undefined, undefined);
316
498
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { Dualsense } from "../src/dualsense";
2
2
  import { DualsenseManager } from "../src/manager";
3
3
  import { TriggerEffect } from "../src/elements/trigger_feedback";
4
4
  import { ChargeStatus } from "../src/hid/battery_state";
5
+ import { formatFirmwareVersion } from "../src/hid/firmware_info";
5
6
 
6
7
  /** Map a -1..1 stick value to 0..1 */
7
8
  function norm(value: number): number {
@@ -13,21 +14,42 @@ function log(player: number, ...args: unknown[]) {
13
14
  console.log(`[P${player + 1}]`, ...args);
14
15
  }
15
16
 
17
+ /** Log transport-level connection details (available immediately) */
18
+ function logConnection(controller: Dualsense, player: number) {
19
+ const connected = controller.connection.active;
20
+ log(
21
+ player,
22
+ `Connected: ${connected ? "yes" : "no"} - ${
23
+ connected ? (controller.wireless ? "bluetooth" : "usb") : ""
24
+ }`,
25
+ );
26
+ }
27
+
28
+ /** Log firmware/factory identity (available after onReady) */
29
+ function logIdentity(controller: Dualsense, player: number) {
30
+ if (controller.hid.identity) {
31
+ log(player, `Identity: ${controller.hid.identity}`);
32
+ }
33
+ const fw = controller.firmwareInfo;
34
+ log(player, `Firmware: v${formatFirmwareVersion(fw.mainFirmwareVersion)} | HW: ${fw.hardwareInfo} | DSP: ${fw.dspFirmwareVersion} | Built: ${fw.buildDate} ${fw.buildTime}`);
35
+ const fi = controller.factoryInfo;
36
+ log(player, `Factory: ${fi.colorName} | ${fi.boardRevision} | Serial: ${fi.serialNumber}`);
37
+ }
38
+
16
39
  /** Set up all debug bindings for a single controller */
17
40
  function bindController(controller: Dualsense, player: number) {
18
- controller.connection.on("change", ({ state }) => {
19
- log(
20
- player,
21
- `Connected: ${state ? "yes" : "no"} - ${
22
- state ? (controller.wireless ? "bluetooth" : "usb") : ""
23
- }`,
24
- );
25
- if (state) {
26
- log(player, `Device: ${controller.deviceId ?? "unknown"}`);
27
- if (controller.serialNumber) {
28
- log(player, `Serial: ${controller.serialNumber}`);
29
- }
30
- }
41
+ // Log the current connection state immediately.
42
+ logConnection(controller, player);
43
+
44
+ controller.connection.on("change", () => {
45
+ logConnection(controller, player);
46
+ });
47
+
48
+ // Log identity details once firmware/factory info is available. On
49
+ // initial bind onReady fires synchronously (already resolved). On
50
+ // reconnect it fires after the background verification completes.
51
+ controller.hid.onReady(() => {
52
+ logIdentity(controller, player);
31
53
  });
32
54
 
33
55
  // Log battery level on connect and when it changes
@@ -203,6 +225,14 @@ function bindController(controller: Dualsense, player: number) {
203
225
  log(player, `Mute LED: ${state ? "on" : "off"}`);
204
226
  });
205
227
 
228
+ // Audio peripherals
229
+ controller.microphone.on("change", ({ state }) => {
230
+ log(player, `Microphone: ${state ? "connected" : "disconnected"}`);
231
+ });
232
+ controller.headphone.on("change", ({ state }) => {
233
+ log(player, `Headphone: ${state ? "connected" : "disconnected"}`);
234
+ });
235
+
206
236
  // Log stick positions for reference
207
237
  controller.left.analog.on("change", (analog) => {
208
238
  log(
@@ -1,6 +1,7 @@
1
1
  import { Dualsense } from "../src/dualsense";
2
2
  import { TriggerEffect } from "../src/elements/trigger_feedback";
3
3
  import { ChargeStatus } from "../src/hid/battery_state";
4
+ import { formatFirmwareVersion } from "../src/hid/firmware_info";
4
5
 
5
6
  /** Map a -1..1 stick value to 0..1 */
6
7
  function norm(value: number): number {
@@ -18,6 +19,26 @@ function main() {
18
19
  state ? (controller.wireless ? "bluetooth" : "usb") : ""
19
20
  }`,
20
21
  );
22
+ if (state) {
23
+ setTimeout(() => {
24
+ const fw = controller.firmwareInfo;
25
+ if (fw) {
26
+ console.log(
27
+ `Firmware: v${formatFirmwareVersion(fw.mainFirmwareVersion)} | HW: ${fw.hardwareInfo} | DSP: ${fw.dspFirmwareVersion} | Built: ${fw.buildDate} ${fw.buildTime}`,
28
+ );
29
+ } else {
30
+ console.log("Failed to query device firmware info");
31
+ }
32
+ const fi = controller.factoryInfo;
33
+ if (fi) {
34
+ console.log(
35
+ `Factory: ${fi.colorName ?? fi.colorCode} | ${fi.boardRevision ?? "unknown board"} | Serial: ${fi.serialNumber}`,
36
+ );
37
+ } else {
38
+ console.log("Failed to query device factory info");
39
+ }
40
+ }, 2000);
41
+ }
21
42
  });
22
43
 
23
44
  // Battery
@@ -179,6 +200,14 @@ function main() {
179
200
  console.log(`Mute LED: ${state ? "on" : "off"}`);
180
201
  });
181
202
 
203
+ // Audio peripherals
204
+ controller.microphone.on("change", ({ state }) => {
205
+ console.log(`Microphone: ${state ? "connected" : "disconnected"}`);
206
+ });
207
+ controller.headphone.on("change", ({ state }) => {
208
+ console.log(`Headphone: ${state ? "connected" : "disconnected"}`);
209
+ });
210
+
182
211
  // Log stick positions
183
212
  controller.left.analog.on("change", (analog) => {
184
213
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dualsense-ts",
3
- "version": "6.2.0",
3
+ "version": "6.4.0",
4
4
  "description": "A natural interface for your DualSense controller, with Typescript",
5
5
  "keywords": [
6
6
  "dualsense",