@onekeyfe/hd-transport-electron 1.2.0-alpha.7 → 1.2.0-alpha.71

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-02fc07fe.js');
3
+ var index = require('./index-bd1cfdb6.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -8,6 +8,14 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
8
8
 
9
9
  var pRetry__default = /*#__PURE__*/_interopDefaultLegacy(pRetry);
10
10
 
11
+ const BLE_ATT_HEADER_BYTES = 3;
12
+ function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
13
+ if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
14
+ return fallbackPacketCapacity;
15
+ }
16
+ return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
17
+ }
18
+
11
19
  function safeLog(logger, level, message, ...args) {
12
20
  if (logger) {
13
21
  logger[level](message, ...args);
@@ -17,6 +25,37 @@ function safeLog(logger, level, message, ...args) {
17
25
  }
18
26
  }
19
27
 
28
+ function runBleCallbackOperation(operation, options) {
29
+ return new Promise((resolve, reject) => {
30
+ let settled = false;
31
+ const settle = (error) => {
32
+ if (settled)
33
+ return;
34
+ settled = true;
35
+ clearTimeout(timeout);
36
+ if (error) {
37
+ reject(error);
38
+ }
39
+ else {
40
+ resolve();
41
+ }
42
+ };
43
+ const timeout = setTimeout(() => {
44
+ if (options.timeoutBehavior === 'reject') {
45
+ settle(new Error(`BLE operation timed out after ${options.timeoutMs}ms`));
46
+ }
47
+ else {
48
+ settle();
49
+ }
50
+ }, options.timeoutMs);
51
+ try {
52
+ operation(settle);
53
+ }
54
+ catch (error) {
55
+ settle(error instanceof Error ? error : new Error(String(error)));
56
+ }
57
+ });
58
+ }
20
59
  function softRefreshSubscription(params) {
21
60
  return index.__awaiter(this, void 0, void 0, function* () {
22
61
  const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, onNotificationData, logger, } = params;
@@ -25,17 +64,13 @@ function softRefreshSubscription(params) {
25
64
  }
26
65
  logger === null || logger === void 0 ? void 0 : logger.info('[BLE-OPS] Starting subscription refresh', { deviceId });
27
66
  subscriptionOperations.set(deviceId, 'subscribing');
28
- yield new Promise(resolve => {
29
- notifyCharacteristic.unsubscribe(() => resolve());
67
+ yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
68
+ timeoutMs: 250,
69
+ timeoutBehavior: 'resolve',
30
70
  });
31
- yield new Promise((resolve, reject) => {
32
- notifyCharacteristic.subscribe((error) => {
33
- if (error) {
34
- reject(error);
35
- return;
36
- }
37
- resolve();
38
- });
71
+ yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
72
+ timeoutMs: 8000,
73
+ timeoutBehavior: 'reject',
39
74
  });
40
75
  notifyCharacteristic.removeAllListeners('data');
41
76
  notifyCharacteristic.on('data', (data) => {
@@ -51,6 +86,9 @@ function softRefreshSubscription(params) {
51
86
  });
52
87
  }
53
88
 
89
+ const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
90
+ const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
91
+
54
92
  let noble = null;
55
93
  let logger = null;
56
94
  const bluetoothState = {
@@ -59,6 +97,7 @@ const bluetoothState = {
59
97
  initialized: false,
60
98
  };
61
99
  let persistentStateListener = null;
100
+ let persistentDiscoverListener = null;
62
101
  const discoveredDevices = new Map();
63
102
  const connectedDevices = new Map();
64
103
  const pairedDevices = new Set();
@@ -66,54 +105,39 @@ const deviceCharacteristics = new Map();
66
105
  const notificationCallbacks = new Map();
67
106
  const subscribedDevices = new Map();
68
107
  const subscriptionOperations = new Map();
108
+ const deviceDisconnectListeners = new Map();
109
+ const deviceMtuListeners = new Map();
69
110
  const ONEKEY_SERVICE_UUIDS = [hdShared.ONEKEY_SERVICE_UUID];
70
- const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
71
- const NORMALIZED_WRITE_UUID = '0002';
72
- const NORMALIZED_NOTIFY_UUID = '0003';
111
+ const ONEKEY_SERVICE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_SERVICE_UUID);
112
+ const ONEKEY_WRITE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_WRITE_CHARACTERISTIC_UUID);
113
+ const ONEKEY_NOTIFY_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_NOTIFY_CHARACTERISTIC_UUID);
73
114
  const BLUETOOTH_INIT_TIMEOUT = 10000;
74
- const DEVICE_SCAN_TIMEOUT = 8000;
75
- const FAST_SCAN_TIMEOUT = 8000;
115
+ const DEVICE_SCAN_TIMEOUT = 5000;
76
116
  const DEVICE_CHECK_INTERVAL = 500;
77
- const CONNECTION_TIMEOUT = 8000;
78
117
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
79
- const BLE_PACKET_SIZE = 192;
80
- const UNIFIED_WRITE_DELAY = 5;
118
+ const BLE_CLEANUP_TIMEOUT = 250;
119
+ const BLE_PACKET_SIZE_FALLBACK = 192;
120
+ const BLE_PACKET_SIZE_MAXIMUM = 244;
81
121
  const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 };
82
122
  const IS_WINDOWS = process.platform === 'win32';
83
123
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
84
124
  /status:\s*3/i,
85
125
  ];
86
- function getBleUuidKey(uuid) {
87
- const normalized = (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
88
- return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
89
- }
90
- const NORMALIZED_ONEKEY_SERVICE_UUIDS = new Set([
91
- ...ONEKEY_SERVICE_UUIDS.map(uuid => getBleUuidKey(uuid)),
92
- '0001',
93
- ]);
94
- function isGenericBleService(uuid) {
95
- return ['1800', '1801', '180a', '180f'].includes(getBleUuidKey(uuid));
96
- }
97
- function hasOneKeyAdvertisementService(peripheral) {
98
- var _a, _b;
99
- const serviceUuids = (_b = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids) !== null && _b !== void 0 ? _b : [];
100
- return serviceUuids.some(uuid => {
101
- const uuidKey = getBleUuidKey(uuid);
102
- return (NORMALIZED_ONEKEY_SERVICE_UUIDS.has(uuidKey) ||
103
- PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(uuidKey));
104
- });
105
- }
106
126
  function isOneKeyPeripheral(peripheral) {
107
- var _a;
108
- const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || null;
109
- return hdShared.isOnekeyDevice(deviceName, peripheral.id) || hasOneKeyAdvertisementService(peripheral);
127
+ var _a, _b;
128
+ const serviceUuids = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids;
129
+ const localName = (_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName;
130
+ if (!(localName === null || localName === void 0 ? void 0 : localName.trim()) || hdShared.isPro2FindMyAdvertisementName(localName)) {
131
+ return false;
132
+ }
133
+ return (hdShared.hasOnekeyCommunicationService(serviceUuids) &&
134
+ hdShared.isOnekeyBluetoothDevice({
135
+ id: peripheral.id,
136
+ localName,
137
+ serviceUuids,
138
+ }));
110
139
  }
111
140
  function emitRawNotification(deviceId, data) {
112
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Raw notification', {
113
- deviceId,
114
- dataLength: data.length,
115
- firstBytes: data.subarray(0, 8).toString('hex'),
116
- });
117
141
  const appCb = notificationCallbacks.get(deviceId);
118
142
  if (appCb)
119
143
  appCb(data.toString('hex'));
@@ -250,9 +274,12 @@ function initializeNoble() {
250
274
  };
251
275
  noble.on('stateChange', onStateChange);
252
276
  });
253
- noble.on('discover', (peripheral) => {
254
- handleDeviceDiscovered(peripheral);
255
- });
277
+ if (!persistentDiscoverListener) {
278
+ persistentDiscoverListener = (peripheral) => {
279
+ handleDeviceDiscovered(peripheral);
280
+ };
281
+ noble.on('discover', persistentDiscoverListener);
282
+ }
256
283
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble initialized successfully');
257
284
  }
258
285
  catch (error) {
@@ -277,6 +304,16 @@ function cleanupDevice(deviceId, webContents, options = {}) {
277
304
  const peripheral = connectedDevices.get(deviceId);
278
305
  const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
279
306
  if (cleanupConnection) {
307
+ const disconnectEntry = deviceDisconnectListeners.get(deviceId);
308
+ if (disconnectEntry) {
309
+ disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
310
+ deviceDisconnectListeners.delete(deviceId);
311
+ }
312
+ const mtuEntry = deviceMtuListeners.get(deviceId);
313
+ if (mtuEntry) {
314
+ mtuEntry.peripheral.removeListener('mtu', mtuEntry.listener);
315
+ deviceMtuListeners.delete(deviceId);
316
+ }
280
317
  connectedDevices.delete(deviceId);
281
318
  deviceCharacteristics.delete(deviceId);
282
319
  notificationCallbacks.delete(deviceId);
@@ -311,12 +348,34 @@ function handleDeviceDisconnect(deviceId, webContents) {
311
348
  });
312
349
  }
313
350
  function setupDisconnectListener(peripheral, deviceId, webContents) {
314
- peripheral.removeAllListeners('disconnect');
315
- peripheral.on('disconnect', () => {
351
+ const existing = deviceDisconnectListeners.get(deviceId);
352
+ if (existing) {
353
+ existing.peripheral.removeListener('disconnect', existing.listener);
354
+ }
355
+ const listener = () => {
316
356
  handleDeviceDisconnect(deviceId, webContents);
317
- });
357
+ };
358
+ deviceDisconnectListeners.set(deviceId, { peripheral, listener });
359
+ peripheral.on('disconnect', listener);
360
+ setupMtuListener(peripheral, deviceId, webContents);
361
+ }
362
+ function setupMtuListener(peripheral, deviceId, webContents) {
363
+ const existing = deviceMtuListeners.get(deviceId);
364
+ if (existing) {
365
+ existing.peripheral.removeListener('mtu', existing.listener);
366
+ }
367
+ const listener = (mtu) => {
368
+ if (!Number.isFinite(mtu) || mtu <= 0)
369
+ return;
370
+ webContents.send(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_MTU_CHANGED, {
371
+ id: deviceId,
372
+ mtu,
373
+ });
374
+ };
375
+ deviceMtuListeners.set(deviceId, { peripheral, listener });
376
+ peripheral.on('mtu', listener);
318
377
  }
319
- function writeCharacteristicWithAck(deviceId, writeCharacteristic, buffer) {
378
+ function writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, buffer) {
320
379
  return index.__awaiter(this, void 0, void 0, function* () {
321
380
  return new Promise((resolve, reject) => {
322
381
  writeCharacteristic.write(buffer, true, (error) => {
@@ -348,7 +407,7 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
348
407
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Write characteristic not available for ${deviceId}`);
349
408
  }
350
409
  try {
351
- yield writeCharacteristicWithAck(deviceId, latestWrite, payload);
410
+ yield writeCharacteristicWithoutResponse(deviceId, latestWrite, payload);
352
411
  }
353
412
  catch (e) {
354
413
  const errorMessage = e instanceof Error ? e.message : String(e);
@@ -415,84 +474,78 @@ function transmitHexDataToDevice(deviceId, hexData) {
415
474
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Device ${deviceId} not connected or characteristics not available`);
416
475
  }
417
476
  const toBuffer = Buffer.from(hexData, 'hex');
418
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Writing data:', {
419
- deviceId,
420
- dataLength: toBuffer.length,
421
- firstBytes: toBuffer.subarray(0, 8).toString('hex'),
422
- });
423
477
  const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
478
+ const packetCapacity = resolveBlePacketCapacity(peripheral.mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
424
479
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
425
480
  const writeCharacteristic = doGetWriteCharacteristic();
426
481
  if (!writeCharacteristic) {
427
482
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Write characteristic not available for ${deviceId}`);
428
483
  }
429
- if (toBuffer.length <= BLE_PACKET_SIZE) {
430
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
431
- yield writeCharacteristicWithAck(deviceId, writeCharacteristic, toBuffer);
484
+ if (toBuffer.length <= packetCapacity) {
485
+ yield writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, toBuffer);
432
486
  return;
433
487
  }
434
488
  for (let offset = 0; offset < toBuffer.length;) {
435
- const chunkSize = Math.min(BLE_PACKET_SIZE, toBuffer.length - offset);
489
+ const chunkSize = Math.min(packetCapacity, toBuffer.length - offset);
436
490
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
437
491
  offset += chunkSize;
438
492
  const latest = doGetWriteCharacteristic();
439
493
  if (!latest) {
440
494
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Write characteristic not available for ${deviceId}`);
441
495
  }
442
- yield writeCharacteristicWithAck(deviceId, latest, chunk);
443
- if (offset < toBuffer.length) {
444
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
445
- }
496
+ yield writeCharacteristicWithoutResponse(deviceId, latest, chunk);
446
497
  }
447
498
  return;
448
499
  }
449
- if (toBuffer.length <= BLE_PACKET_SIZE) {
450
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
500
+ if (toBuffer.length <= packetCapacity) {
451
501
  yield attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, toBuffer, 'single');
452
502
  return;
453
503
  }
454
504
  for (let offset = 0, idx = 0; offset < toBuffer.length; idx++) {
455
- const chunkSize = Math.min(BLE_PACKET_SIZE, toBuffer.length - offset);
505
+ const chunkSize = Math.min(packetCapacity, toBuffer.length - offset);
456
506
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
457
507
  offset += chunkSize;
458
508
  yield attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, chunk, `chunk-${idx + 1}`);
459
- if (offset < toBuffer.length) {
460
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
461
- }
462
509
  }
463
510
  });
464
511
  }
465
512
  function handleDeviceDiscovered(peripheral) {
466
513
  var _a, _b;
467
- const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
468
- const serviceUuids = ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [];
469
514
  if (!isOneKeyPeripheral(peripheral)) {
470
515
  return;
471
516
  }
472
517
  const isNewDevice = !discoveredDevices.has(peripheral.id);
473
518
  discoveredDevices.set(peripheral.id, peripheral);
474
519
  if (isNewDevice) {
475
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered OneKey BLE device:', {
476
- name: deviceName,
477
- id: peripheral.id,
478
- serviceUUIDs: serviceUuids,
520
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] OneKey BLE device discovered', {
521
+ deviceId: peripheral.id,
522
+ name: ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device',
523
+ serviceUUIDs: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [],
479
524
  });
480
525
  }
481
526
  }
482
527
  function ensureDiscoverListener() {
483
528
  if (!noble)
484
529
  return;
485
- const listenerCount = noble.listenerCount('discover');
486
- if (listenerCount === 0) {
530
+ if (!persistentDiscoverListener) {
487
531
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discover listener missing, re-adding it');
488
- noble.on('discover', (peripheral) => {
532
+ persistentDiscoverListener = (peripheral) => {
489
533
  handleDeviceDiscovered(peripheral);
490
- });
534
+ };
535
+ noble.on('discover', persistentDiscoverListener);
491
536
  }
492
537
  else {
493
- logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] Discover listener already exists, count:', listenerCount);
538
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] Discover listener already registered');
494
539
  }
495
540
  }
541
+ function waitForNobleScanStop(nobleInstance) {
542
+ return index.__awaiter(this, void 0, void 0, function* () {
543
+ yield runBleCallbackOperation(callback => nobleInstance.stopScanning(() => callback()), {
544
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
545
+ timeoutBehavior: 'resolve',
546
+ });
547
+ });
548
+ }
496
549
  function performTargetedScan(targetDeviceId) {
497
550
  return index.__awaiter(this, void 0, void 0, function* () {
498
551
  if (!noble) {
@@ -501,33 +554,43 @@ function performTargetedScan(targetDeviceId) {
501
554
  const nobleInstance = noble;
502
555
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting targeted scan for device:', targetDeviceId);
503
556
  return new Promise((resolve, reject) => {
557
+ let settled = false;
558
+ const finish = (peripheral, error) => index.__awaiter(this, void 0, void 0, function* () {
559
+ if (settled)
560
+ return;
561
+ settled = true;
562
+ if (timeoutId)
563
+ clearTimeout(timeoutId);
564
+ nobleInstance.removeListener('discover', onDiscover);
565
+ yield waitForNobleScanStop(nobleInstance);
566
+ if (error) {
567
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start targeted scan:', error);
568
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
569
+ return;
570
+ }
571
+ if (peripheral) {
572
+ discoveredDevices.set(peripheral.id, peripheral);
573
+ }
574
+ resolve(peripheral);
575
+ });
504
576
  const onDiscover = (peripheral) => {
505
577
  var _a;
506
- if (peripheral.id === targetDeviceId) {
578
+ if (peripheral.id === targetDeviceId && isOneKeyPeripheral(peripheral)) {
507
579
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Target device found during targeted scan:', {
508
580
  id: peripheral.id,
509
581
  name: (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName,
510
582
  });
511
- clearTimeout(timeoutId);
512
- nobleInstance.removeListener('discover', onDiscover);
513
- nobleInstance.stopScanning();
514
- discoveredDevices.set(peripheral.id, peripheral);
515
- resolve(peripheral);
583
+ finish(peripheral).catch(reject);
516
584
  }
517
585
  };
518
586
  const timeoutId = setTimeout(() => {
519
- nobleInstance.removeListener('discover', onDiscover);
520
- nobleInstance.stopScanning();
521
587
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId);
522
- resolve(null);
523
- }, FAST_SCAN_TIMEOUT);
588
+ finish(null).catch(reject);
589
+ }, NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS);
524
590
  nobleInstance.on('discover', onDiscover);
525
591
  nobleInstance.startScanning([], false, (error) => {
526
592
  if (error) {
527
- clearTimeout(timeoutId);
528
- nobleInstance.removeListener('discover', onDiscover);
529
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start targeted scan:', error);
530
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
593
+ finish(null, error).catch(reject);
531
594
  return;
532
595
  }
533
596
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan started for device:', targetDeviceId);
@@ -550,12 +613,12 @@ function enumerateDevices() {
550
613
  return new Promise((resolve, reject) => {
551
614
  const devices = [];
552
615
  let intervalId;
553
- const cleanup = () => {
616
+ const cleanup = () => index.__awaiter(this, void 0, void 0, function* () {
554
617
  clearTimeout(timeoutId);
555
618
  if (intervalId)
556
619
  clearInterval(intervalId);
557
- nobleInstance.stopScanning();
558
- };
620
+ yield waitForNobleScanStop(nobleInstance);
621
+ });
559
622
  const checkDevices = () => {
560
623
  discoveredDevices.forEach((peripheral, id) => {
561
624
  var _a;
@@ -571,23 +634,23 @@ function enumerateDevices() {
571
634
  }
572
635
  });
573
636
  };
574
- const timeoutId = setTimeout(() => {
637
+ const timeoutId = setTimeout(() => index.__awaiter(this, void 0, void 0, function* () {
575
638
  checkDevices();
576
- cleanup();
639
+ yield cleanup();
577
640
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
578
641
  resolve(devices);
579
- }, DEVICE_SCAN_TIMEOUT);
642
+ }), DEVICE_SCAN_TIMEOUT);
580
643
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
581
- nobleInstance.startScanning([], false, (error) => {
644
+ nobleInstance.startScanning([], false, (error) => index.__awaiter(this, void 0, void 0, function* () {
582
645
  if (error) {
583
- cleanup();
646
+ yield cleanup();
584
647
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start scanning:', error);
585
648
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
586
649
  return;
587
650
  }
588
651
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning started for OneKey devices');
589
652
  intervalId = setInterval(checkDevices, DEVICE_CHECK_INTERVAL);
590
- });
653
+ }));
591
654
  });
592
655
  });
593
656
  }
@@ -595,25 +658,20 @@ function stopScanning() {
595
658
  return index.__awaiter(this, void 0, void 0, function* () {
596
659
  if (!noble)
597
660
  return;
598
- return new Promise(resolve => {
599
- if (!noble) {
600
- resolve();
601
- return;
602
- }
603
- noble.stopScanning(() => {
604
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning stopped');
605
- resolve();
606
- });
607
- });
661
+ const nobleInstance = noble;
662
+ yield waitForNobleScanStop(nobleInstance);
663
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning stopped');
608
664
  });
609
665
  }
610
666
  function cleanupNobleListeners() {
611
667
  if (!noble)
612
668
  return;
613
669
  try {
614
- noble.removeAllListeners('discover');
615
- noble.removeAllListeners('stateChange');
616
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] All Noble listeners cleaned up');
670
+ if (persistentDiscoverListener) {
671
+ noble.removeListener('discover', persistentDiscoverListener);
672
+ persistentDiscoverListener = null;
673
+ }
674
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Owned Noble listeners cleaned up');
617
675
  }
618
676
  catch (error) {
619
677
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to clean up some listeners:', error);
@@ -624,22 +682,12 @@ function getDevice(deviceId) {
624
682
  const peripheral = discoveredDevices.get(deviceId);
625
683
  if (peripheral) {
626
684
  const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
627
- return {
628
- commType: 'electron-ble',
629
- id: peripheral.id,
630
- name: deviceName,
631
- state: peripheral.state || 'disconnected',
632
- };
685
+ return Object.assign({ commType: 'electron-ble', id: peripheral.id, name: deviceName, state: peripheral.state || 'disconnected' }, (typeof peripheral.mtu === 'number' ? { mtu: peripheral.mtu } : {}));
633
686
  }
634
687
  const connectedPeripheral = connectedDevices.get(deviceId);
635
688
  if (connectedPeripheral) {
636
689
  const deviceName = ((_b = connectedPeripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName) || 'Unknown Device';
637
- return {
638
- commType: 'electron-ble',
639
- id: connectedPeripheral.id,
640
- name: deviceName,
641
- state: connectedPeripheral.state || 'connected',
642
- };
690
+ return Object.assign({ commType: 'electron-ble', id: connectedPeripheral.id, name: deviceName, state: connectedPeripheral.state || 'connected' }, (typeof connectedPeripheral.mtu === 'number' ? { mtu: connectedPeripheral.mtu } : {}));
643
691
  }
644
692
  return {
645
693
  commType: 'electron-ble',
@@ -683,23 +731,22 @@ function discoverServicesAndCharacteristics(peripheral) {
683
731
  }
684
732
  });
685
733
  });
686
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] All services:', services === null || services === void 0 ? void 0 : services.map(s => s.uuid));
687
734
  if (!services || services.length === 0) {
688
735
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
689
736
  }
690
- let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
691
- if (!service) {
692
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Known OneKey service UUID not found, trying first vendor service');
693
- service =
694
- services.find(s => PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(getBleUuidKey(s.uuid))) ||
695
- services.find(s => !isGenericBleService(s.uuid)) ||
696
- services[0];
697
- }
737
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] services discovered', {
738
+ deviceId: peripheral.id,
739
+ serviceUUIDs: services.map(service => service.uuid),
740
+ });
741
+ const service = services.find(s => hdShared.matchesKnownBleUuid(s.uuid, ONEKEY_SERVICE_UUID_ALIASES));
698
742
  if (!service) {
699
743
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
700
744
  }
701
745
  const selectedService = service;
702
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Using service:', selectedService.uuid);
746
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] service selected', {
747
+ deviceId: peripheral.id,
748
+ serviceUuid: selectedService.uuid,
749
+ });
703
750
  const characteristics = yield new Promise((resolve, reject) => {
704
751
  selectedService.discoverCharacteristics([], (error, chars) => {
705
752
  if (error) {
@@ -711,30 +758,26 @@ function discoverServicesAndCharacteristics(peripheral) {
711
758
  }
712
759
  });
713
760
  });
714
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered characteristics:', {
715
- count: (characteristics === null || characteristics === void 0 ? void 0 : characteristics.length) || 0,
716
- uuids: (characteristics === null || characteristics === void 0 ? void 0 : characteristics.map(c => c.uuid)) || [],
717
- });
718
761
  let writeCharacteristic = null;
719
762
  let notifyCharacteristic = null;
720
763
  for (const characteristic of characteristics) {
721
- const uuid = characteristic.uuid.replace(/-/g, '').toLowerCase();
722
- const uuidKey = uuid.length >= 8 ? uuid.substring(4, 8) : uuid;
723
- if (uuidKey === NORMALIZED_WRITE_UUID) {
764
+ if (hdShared.matchesKnownBleUuid(characteristic.uuid, ONEKEY_WRITE_UUID_ALIASES)) {
724
765
  writeCharacteristic = characteristic;
725
766
  }
726
- else if (uuidKey === NORMALIZED_NOTIFY_UUID) {
767
+ else if (hdShared.matchesKnownBleUuid(characteristic.uuid, ONEKEY_NOTIFY_UUID_ALIASES)) {
727
768
  notifyCharacteristic = characteristic;
728
769
  }
729
770
  }
730
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Characteristic discovery result:', {
731
- writeFound: !!writeCharacteristic,
732
- notifyFound: !!notifyCharacteristic,
733
- });
734
771
  if (!writeCharacteristic || !notifyCharacteristic) {
735
772
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Missing characteristics - write:', !!writeCharacteristic, 'notify:', !!notifyCharacteristic);
736
773
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, 'Required characteristics not found');
737
774
  }
775
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] characteristics selected', {
776
+ deviceId: peripheral.id,
777
+ serviceUuid: selectedService.uuid,
778
+ writeUuid: writeCharacteristic.uuid,
779
+ notifyUuid: notifyCharacteristic.uuid,
780
+ });
738
781
  return { write: writeCharacteristic, notify: notifyCharacteristic };
739
782
  }))();
740
783
  try {
@@ -755,27 +798,17 @@ function forceReconnectPeripheral(peripheral, deviceId) {
755
798
  reason: 'force-reconnect',
756
799
  });
757
800
  if (peripheral.state === 'connected') {
758
- peripheral.removeAllListeners('disconnect');
759
- yield new Promise(resolve => {
760
- peripheral.disconnect(() => {
761
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force disconnect completed');
762
- resolve();
763
- });
764
- });
801
+ yield runBleCallbackOperation(callback => peripheral.disconnect(() => {
802
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force disconnect completed');
803
+ callback();
804
+ }), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
765
805
  }
766
- yield new Promise((resolve, reject) => {
767
- peripheral.connect((error) => {
768
- if (error) {
769
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Force reconnect failed:', error);
770
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
771
- }
772
- else {
773
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
774
- connectedDevices.set(deviceId, peripheral);
775
- resolve();
776
- }
777
- });
806
+ yield runBleCallbackOperation(callback => peripheral.connect(callback), {
807
+ timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
808
+ timeoutBehavior: 'reject',
778
809
  });
810
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
811
+ connectedDevices.set(deviceId, peripheral);
779
812
  yield hdShared.wait(500);
780
813
  });
781
814
  }
@@ -852,13 +885,23 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
852
885
  }
853
886
  function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
854
887
  return index.__awaiter(this, void 0, void 0, function* () {
888
+ setupDisconnectListener(peripheral, deviceId, webContents);
889
+ try {
890
+ return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
891
+ }
892
+ catch (initialError) {
893
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct service discovery failed, retrying after reconnect', {
894
+ deviceId,
895
+ error: initialError,
896
+ });
897
+ }
855
898
  yield forceReconnectPeripheral(peripheral, deviceId);
856
899
  setupDisconnectListener(peripheral, deviceId, webContents);
857
900
  try {
858
901
  return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
859
902
  }
860
- catch (error) {
861
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed, attempting fresh scan...', error);
903
+ catch (reconnectError) {
904
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed after reconnect, attempting fresh scan...', reconnectError);
862
905
  return freshScanAndDiscover(deviceId, webContents);
863
906
  }
864
907
  });
@@ -931,12 +974,24 @@ function connectDevice(deviceId, webContents) {
931
974
  return;
932
975
  }
933
976
  return new Promise((resolve, reject) => {
977
+ let connectionTimedOut = false;
934
978
  const timeout = setTimeout(() => {
979
+ connectionTimedOut = true;
935
980
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'Connection timeout'));
936
- }, CONNECTION_TIMEOUT);
981
+ }, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
937
982
  const connectedPeripheral = peripheral;
938
983
  connectedPeripheral.connect((error) => index.__awaiter(this, void 0, void 0, function* () {
939
984
  clearTimeout(timeout);
985
+ if (connectionTimedOut) {
986
+ if (!error) {
987
+ try {
988
+ connectedPeripheral.disconnect(() => undefined);
989
+ }
990
+ catch (_a) {
991
+ }
992
+ }
993
+ return;
994
+ }
940
995
  if (error) {
941
996
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection failed:', error);
942
997
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
@@ -966,17 +1021,20 @@ function disconnectDevice(deviceId) {
966
1021
  if (!peripheral) {
967
1022
  return;
968
1023
  }
969
- return new Promise(resolve => {
970
- peripheral.removeAllListeners('disconnect');
971
- peripheral.disconnect(() => {
972
- cleanupDevice(deviceId, undefined, {
973
- cleanupConnection: true,
974
- sendDisconnectEvent: false,
975
- cancelOperations: true,
976
- reason: 'manual-disconnect',
977
- });
978
- resolve();
979
- });
1024
+ const disconnectEntry = deviceDisconnectListeners.get(deviceId);
1025
+ if (disconnectEntry) {
1026
+ disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
1027
+ deviceDisconnectListeners.delete(deviceId);
1028
+ }
1029
+ yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1030
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1031
+ timeoutBehavior: 'resolve',
1032
+ });
1033
+ cleanupDevice(deviceId, undefined, {
1034
+ cleanupConnection: true,
1035
+ sendDisconnectEvent: false,
1036
+ cancelOperations: true,
1037
+ reason: 'manual-disconnect',
980
1038
  });
981
1039
  });
982
1040
  }
@@ -991,18 +1049,11 @@ function unsubscribeNotifications(deviceId) {
991
1049
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Unsubscribing from notifications for device:', deviceId);
992
1050
  subscriptionOperations.set(deviceId, 'unsubscribing');
993
1051
  try {
994
- yield new Promise((resolve, reject) => {
995
- notifyCharacteristic.unsubscribe((error) => {
996
- if (error) {
997
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Notification unsubscription failed:', error);
998
- reject(error);
999
- }
1000
- else {
1001
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification unsubscription successful');
1002
- resolve();
1003
- }
1004
- });
1052
+ yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
1053
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1054
+ timeoutBehavior: 'resolve',
1005
1055
  });
1056
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification unsubscription completed');
1006
1057
  notifyCharacteristic.removeAllListeners('data');
1007
1058
  notificationCallbacks.delete(deviceId);
1008
1059
  subscribedDevices.delete(deviceId);
@@ -1054,19 +1105,13 @@ function subscribeNotifications(deviceId, callback) {
1054
1105
  notificationCallbacks.set(deviceId, callback);
1055
1106
  function rebuildAppSubscription(deviceId, notifyCharacteristic) {
1056
1107
  return index.__awaiter(this, void 0, void 0, function* () {
1057
- yield new Promise(resolve => {
1058
- notifyCharacteristic.unsubscribe(() => {
1059
- resolve();
1060
- });
1108
+ yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
1109
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1110
+ timeoutBehavior: 'resolve',
1061
1111
  });
1062
- yield new Promise((resolve, reject) => {
1063
- notifyCharacteristic.subscribe((error) => {
1064
- if (error) {
1065
- reject(error);
1066
- return;
1067
- }
1068
- resolve();
1069
- });
1112
+ yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
1113
+ timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
1114
+ timeoutBehavior: 'reject',
1070
1115
  });
1071
1116
  notifyCharacteristic.on('data', (data) => {
1072
1117
  if (!pairedDevices.has(deviceId)) {
@@ -1087,19 +1132,17 @@ function subscribeNotifications(deviceId, callback) {
1087
1132
  });
1088
1133
  }
1089
1134
  function setupNobleBleHandlers(webContents) {
1090
- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.');
1091
1135
  try {
1092
- console.log('[NobleBLE] NOBLE_VERSION_771');
1093
1136
  logger = require('electron-log');
1094
- console.log('[NobleBLE] electron-log loaded successfully.');
1095
1137
  const { ipcMain } = require('electron');
1096
- console.log('[NobleBLE] electron.ipcMain loaded successfully.');
1097
1138
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
1098
- console.log(`[NobleBLE] Registering handler for: ${hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
1099
1139
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
1100
1140
  try {
1101
1141
  const devices = yield enumerateDevices();
1102
- safeLog(logger, 'info', 'Enumeration completed, devices:', devices);
1142
+ safeLog(logger, 'debug', 'Enumeration completed', {
1143
+ count: devices.length,
1144
+ devices: devices.map(device => ({ id: device.id, name: device.name })),
1145
+ });
1103
1146
  return devices;
1104
1147
  }
1105
1148
  catch (error) {
@@ -1124,7 +1167,6 @@ function setupNobleBleHandlers(webContents) {
1124
1167
  yield disconnectDevice(deviceId);
1125
1168
  }));
1126
1169
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_WRITE, (_event, deviceId, hexData) => index.__awaiter(this, void 0, void 0, function* () {
1127
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length });
1128
1170
  yield transmitHexDataToDevice(deviceId, hexData);
1129
1171
  }));
1130
1172
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_SUBSCRIBE, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
@@ -1169,23 +1211,23 @@ function setupNobleBleHandlers(webContents) {
1169
1211
  }));
1170
1212
  webContents.on('destroyed', () => {
1171
1213
  safeLog(logger, 'info', 'Cleaning up Noble BLE handlers');
1172
- const deviceIds = Array.from(connectedDevices.keys());
1173
- deviceIds.forEach(deviceId => {
1174
- cleanupDevice(deviceId, undefined, {
1175
- cleanupConnection: true,
1176
- sendDisconnectEvent: false,
1177
- cancelOperations: true,
1178
- reason: 'app-quit',
1179
- });
1214
+ (() => index.__awaiter(this, void 0, void 0, function* () {
1215
+ const deviceIds = Array.from(connectedDevices.keys());
1216
+ for (const deviceId of deviceIds) {
1217
+ yield unsubscribeNotifications(deviceId).catch(() => undefined);
1218
+ yield disconnectDevice(deviceId).catch(() => undefined);
1219
+ }
1220
+ yield stopScanning().catch(() => undefined);
1221
+ if (noble && persistentStateListener) {
1222
+ noble.removeListener('stateChange', persistentStateListener);
1223
+ persistentStateListener = null;
1224
+ }
1225
+ cleanupNobleListeners();
1226
+ discoveredDevices.clear();
1227
+ safeLog(logger, 'info', 'Noble BLE cleanup completed');
1228
+ }))().catch(error => {
1229
+ safeLog(logger, 'error', 'Noble BLE cleanup failed', error);
1180
1230
  });
1181
- stopScanning();
1182
- if (noble && persistentStateListener) {
1183
- noble.removeListener('stateChange', persistentStateListener);
1184
- persistentStateListener = null;
1185
- }
1186
- cleanupNobleListeners();
1187
- discoveredDevices.clear();
1188
- safeLog(logger, 'info', 'Noble BLE cleanup completed');
1189
1231
  });
1190
1232
  safeLog(logger, 'info', 'Noble BLE IPC handlers setup completed');
1191
1233
  }