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

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-84fea839.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -17,6 +17,37 @@ function safeLog(logger, level, message, ...args) {
17
17
  }
18
18
  }
19
19
 
20
+ function runBleCallbackOperation(operation, options) {
21
+ return new Promise((resolve, reject) => {
22
+ let settled = false;
23
+ const settle = (error) => {
24
+ if (settled)
25
+ return;
26
+ settled = true;
27
+ clearTimeout(timeout);
28
+ if (error) {
29
+ reject(error);
30
+ }
31
+ else {
32
+ resolve();
33
+ }
34
+ };
35
+ const timeout = setTimeout(() => {
36
+ if (options.timeoutBehavior === 'reject') {
37
+ settle(new Error(`BLE operation timed out after ${options.timeoutMs}ms`));
38
+ }
39
+ else {
40
+ settle();
41
+ }
42
+ }, options.timeoutMs);
43
+ try {
44
+ operation(settle);
45
+ }
46
+ catch (error) {
47
+ settle(error instanceof Error ? error : new Error(String(error)));
48
+ }
49
+ });
50
+ }
20
51
  function softRefreshSubscription(params) {
21
52
  return index.__awaiter(this, void 0, void 0, function* () {
22
53
  const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, onNotificationData, logger, } = params;
@@ -25,17 +56,13 @@ function softRefreshSubscription(params) {
25
56
  }
26
57
  logger === null || logger === void 0 ? void 0 : logger.info('[BLE-OPS] Starting subscription refresh', { deviceId });
27
58
  subscriptionOperations.set(deviceId, 'subscribing');
28
- yield new Promise(resolve => {
29
- notifyCharacteristic.unsubscribe(() => resolve());
59
+ yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
60
+ timeoutMs: 250,
61
+ timeoutBehavior: 'resolve',
30
62
  });
31
- yield new Promise((resolve, reject) => {
32
- notifyCharacteristic.subscribe((error) => {
33
- if (error) {
34
- reject(error);
35
- return;
36
- }
37
- resolve();
38
- });
63
+ yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
64
+ timeoutMs: 8000,
65
+ timeoutBehavior: 'reject',
39
66
  });
40
67
  notifyCharacteristic.removeAllListeners('data');
41
68
  notifyCharacteristic.on('data', (data) => {
@@ -51,6 +78,9 @@ function softRefreshSubscription(params) {
51
78
  });
52
79
  }
53
80
 
81
+ const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
82
+ const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
83
+
54
84
  let noble = null;
55
85
  let logger = null;
56
86
  const bluetoothState = {
@@ -59,6 +89,7 @@ const bluetoothState = {
59
89
  initialized: false,
60
90
  };
61
91
  let persistentStateListener = null;
92
+ let persistentDiscoverListener = null;
62
93
  const discoveredDevices = new Map();
63
94
  const connectedDevices = new Map();
64
95
  const pairedDevices = new Set();
@@ -66,16 +97,16 @@ const deviceCharacteristics = new Map();
66
97
  const notificationCallbacks = new Map();
67
98
  const subscribedDevices = new Map();
68
99
  const subscriptionOperations = new Map();
100
+ const deviceDisconnectListeners = new Map();
69
101
  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';
102
+ const ONEKEY_SERVICE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_SERVICE_UUID);
103
+ const ONEKEY_WRITE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_WRITE_CHARACTERISTIC_UUID);
104
+ const ONEKEY_NOTIFY_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_NOTIFY_CHARACTERISTIC_UUID);
73
105
  const BLUETOOTH_INIT_TIMEOUT = 10000;
74
- const DEVICE_SCAN_TIMEOUT = 8000;
75
- const FAST_SCAN_TIMEOUT = 8000;
106
+ const DEVICE_SCAN_TIMEOUT = 5000;
76
107
  const DEVICE_CHECK_INTERVAL = 500;
77
- const CONNECTION_TIMEOUT = 8000;
78
108
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
109
+ const BLE_CLEANUP_TIMEOUT = 250;
79
110
  const BLE_PACKET_SIZE = 192;
80
111
  const UNIFIED_WRITE_DELAY = 5;
81
112
  const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 };
@@ -83,37 +114,21 @@ const IS_WINDOWS = process.platform === 'win32';
83
114
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
84
115
  /status:\s*3/i,
85
116
  ];
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
117
  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);
118
+ var _a, _b;
119
+ const serviceUuids = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids;
120
+ const localName = (_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName;
121
+ if (!(localName === null || localName === void 0 ? void 0 : localName.trim()) || hdShared.isPro2FindMyAdvertisementName(localName)) {
122
+ return false;
123
+ }
124
+ return (hdShared.hasOnekeyCommunicationService(serviceUuids) &&
125
+ hdShared.isOnekeyBluetoothDevice({
126
+ id: peripheral.id,
127
+ localName,
128
+ serviceUuids,
129
+ }));
110
130
  }
111
131
  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
132
  const appCb = notificationCallbacks.get(deviceId);
118
133
  if (appCb)
119
134
  appCb(data.toString('hex'));
@@ -250,9 +265,12 @@ function initializeNoble() {
250
265
  };
251
266
  noble.on('stateChange', onStateChange);
252
267
  });
253
- noble.on('discover', (peripheral) => {
254
- handleDeviceDiscovered(peripheral);
255
- });
268
+ if (!persistentDiscoverListener) {
269
+ persistentDiscoverListener = (peripheral) => {
270
+ handleDeviceDiscovered(peripheral);
271
+ };
272
+ noble.on('discover', persistentDiscoverListener);
273
+ }
256
274
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble initialized successfully');
257
275
  }
258
276
  catch (error) {
@@ -277,6 +295,11 @@ function cleanupDevice(deviceId, webContents, options = {}) {
277
295
  const peripheral = connectedDevices.get(deviceId);
278
296
  const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
279
297
  if (cleanupConnection) {
298
+ const disconnectEntry = deviceDisconnectListeners.get(deviceId);
299
+ if (disconnectEntry) {
300
+ disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
301
+ deviceDisconnectListeners.delete(deviceId);
302
+ }
280
303
  connectedDevices.delete(deviceId);
281
304
  deviceCharacteristics.delete(deviceId);
282
305
  notificationCallbacks.delete(deviceId);
@@ -311,10 +334,15 @@ function handleDeviceDisconnect(deviceId, webContents) {
311
334
  });
312
335
  }
313
336
  function setupDisconnectListener(peripheral, deviceId, webContents) {
314
- peripheral.removeAllListeners('disconnect');
315
- peripheral.on('disconnect', () => {
337
+ const existing = deviceDisconnectListeners.get(deviceId);
338
+ if (existing) {
339
+ existing.peripheral.removeListener('disconnect', existing.listener);
340
+ }
341
+ const listener = () => {
316
342
  handleDeviceDisconnect(deviceId, webContents);
317
- });
343
+ };
344
+ deviceDisconnectListeners.set(deviceId, { peripheral, listener });
345
+ peripheral.on('disconnect', listener);
318
346
  }
319
347
  function writeCharacteristicWithAck(deviceId, writeCharacteristic, buffer) {
320
348
  return index.__awaiter(this, void 0, void 0, function* () {
@@ -415,11 +443,6 @@ function transmitHexDataToDevice(deviceId, hexData) {
415
443
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Device ${deviceId} not connected or characteristics not available`);
416
444
  }
417
445
  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
446
  const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
424
447
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
425
448
  const writeCharacteristic = doGetWriteCharacteristic();
@@ -464,35 +487,41 @@ function transmitHexDataToDevice(deviceId, hexData) {
464
487
  }
465
488
  function handleDeviceDiscovered(peripheral) {
466
489
  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
490
  if (!isOneKeyPeripheral(peripheral)) {
470
491
  return;
471
492
  }
472
493
  const isNewDevice = !discoveredDevices.has(peripheral.id);
473
494
  discoveredDevices.set(peripheral.id, peripheral);
474
495
  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,
496
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] OneKey BLE device discovered', {
497
+ deviceId: peripheral.id,
498
+ name: ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device',
499
+ serviceUUIDs: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [],
479
500
  });
480
501
  }
481
502
  }
482
503
  function ensureDiscoverListener() {
483
504
  if (!noble)
484
505
  return;
485
- const listenerCount = noble.listenerCount('discover');
486
- if (listenerCount === 0) {
506
+ if (!persistentDiscoverListener) {
487
507
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discover listener missing, re-adding it');
488
- noble.on('discover', (peripheral) => {
508
+ persistentDiscoverListener = (peripheral) => {
489
509
  handleDeviceDiscovered(peripheral);
490
- });
510
+ };
511
+ noble.on('discover', persistentDiscoverListener);
491
512
  }
492
513
  else {
493
- logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] Discover listener already exists, count:', listenerCount);
514
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] Discover listener already registered');
494
515
  }
495
516
  }
517
+ function waitForNobleScanStop(nobleInstance) {
518
+ return index.__awaiter(this, void 0, void 0, function* () {
519
+ yield runBleCallbackOperation(callback => nobleInstance.stopScanning(() => callback()), {
520
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
521
+ timeoutBehavior: 'resolve',
522
+ });
523
+ });
524
+ }
496
525
  function performTargetedScan(targetDeviceId) {
497
526
  return index.__awaiter(this, void 0, void 0, function* () {
498
527
  if (!noble) {
@@ -501,33 +530,43 @@ function performTargetedScan(targetDeviceId) {
501
530
  const nobleInstance = noble;
502
531
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting targeted scan for device:', targetDeviceId);
503
532
  return new Promise((resolve, reject) => {
533
+ let settled = false;
534
+ const finish = (peripheral, error) => index.__awaiter(this, void 0, void 0, function* () {
535
+ if (settled)
536
+ return;
537
+ settled = true;
538
+ if (timeoutId)
539
+ clearTimeout(timeoutId);
540
+ nobleInstance.removeListener('discover', onDiscover);
541
+ yield waitForNobleScanStop(nobleInstance);
542
+ if (error) {
543
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start targeted scan:', error);
544
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
545
+ return;
546
+ }
547
+ if (peripheral) {
548
+ discoveredDevices.set(peripheral.id, peripheral);
549
+ }
550
+ resolve(peripheral);
551
+ });
504
552
  const onDiscover = (peripheral) => {
505
553
  var _a;
506
- if (peripheral.id === targetDeviceId) {
554
+ if (peripheral.id === targetDeviceId && isOneKeyPeripheral(peripheral)) {
507
555
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Target device found during targeted scan:', {
508
556
  id: peripheral.id,
509
557
  name: (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName,
510
558
  });
511
- clearTimeout(timeoutId);
512
- nobleInstance.removeListener('discover', onDiscover);
513
- nobleInstance.stopScanning();
514
- discoveredDevices.set(peripheral.id, peripheral);
515
- resolve(peripheral);
559
+ finish(peripheral).catch(reject);
516
560
  }
517
561
  };
518
562
  const timeoutId = setTimeout(() => {
519
- nobleInstance.removeListener('discover', onDiscover);
520
- nobleInstance.stopScanning();
521
563
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId);
522
- resolve(null);
523
- }, FAST_SCAN_TIMEOUT);
564
+ finish(null).catch(reject);
565
+ }, NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS);
524
566
  nobleInstance.on('discover', onDiscover);
525
567
  nobleInstance.startScanning([], false, (error) => {
526
568
  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));
569
+ finish(null, error).catch(reject);
531
570
  return;
532
571
  }
533
572
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan started for device:', targetDeviceId);
@@ -550,12 +589,12 @@ function enumerateDevices() {
550
589
  return new Promise((resolve, reject) => {
551
590
  const devices = [];
552
591
  let intervalId;
553
- const cleanup = () => {
592
+ const cleanup = () => index.__awaiter(this, void 0, void 0, function* () {
554
593
  clearTimeout(timeoutId);
555
594
  if (intervalId)
556
595
  clearInterval(intervalId);
557
- nobleInstance.stopScanning();
558
- };
596
+ yield waitForNobleScanStop(nobleInstance);
597
+ });
559
598
  const checkDevices = () => {
560
599
  discoveredDevices.forEach((peripheral, id) => {
561
600
  var _a;
@@ -571,23 +610,23 @@ function enumerateDevices() {
571
610
  }
572
611
  });
573
612
  };
574
- const timeoutId = setTimeout(() => {
613
+ const timeoutId = setTimeout(() => index.__awaiter(this, void 0, void 0, function* () {
575
614
  checkDevices();
576
- cleanup();
615
+ yield cleanup();
577
616
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
578
617
  resolve(devices);
579
- }, DEVICE_SCAN_TIMEOUT);
618
+ }), DEVICE_SCAN_TIMEOUT);
580
619
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
581
- nobleInstance.startScanning([], false, (error) => {
620
+ nobleInstance.startScanning([], false, (error) => index.__awaiter(this, void 0, void 0, function* () {
582
621
  if (error) {
583
- cleanup();
622
+ yield cleanup();
584
623
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start scanning:', error);
585
624
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
586
625
  return;
587
626
  }
588
627
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning started for OneKey devices');
589
628
  intervalId = setInterval(checkDevices, DEVICE_CHECK_INTERVAL);
590
- });
629
+ }));
591
630
  });
592
631
  });
593
632
  }
@@ -595,25 +634,20 @@ function stopScanning() {
595
634
  return index.__awaiter(this, void 0, void 0, function* () {
596
635
  if (!noble)
597
636
  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
- });
637
+ const nobleInstance = noble;
638
+ yield waitForNobleScanStop(nobleInstance);
639
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning stopped');
608
640
  });
609
641
  }
610
642
  function cleanupNobleListeners() {
611
643
  if (!noble)
612
644
  return;
613
645
  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');
646
+ if (persistentDiscoverListener) {
647
+ noble.removeListener('discover', persistentDiscoverListener);
648
+ persistentDiscoverListener = null;
649
+ }
650
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Owned Noble listeners cleaned up');
617
651
  }
618
652
  catch (error) {
619
653
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to clean up some listeners:', error);
@@ -683,23 +717,22 @@ function discoverServicesAndCharacteristics(peripheral) {
683
717
  }
684
718
  });
685
719
  });
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
720
  if (!services || services.length === 0) {
688
721
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
689
722
  }
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
- }
723
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] services discovered', {
724
+ deviceId: peripheral.id,
725
+ serviceUUIDs: services.map(service => service.uuid),
726
+ });
727
+ const service = services.find(s => hdShared.matchesKnownBleUuid(s.uuid, ONEKEY_SERVICE_UUID_ALIASES));
698
728
  if (!service) {
699
729
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
700
730
  }
701
731
  const selectedService = service;
702
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Using service:', selectedService.uuid);
732
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] service selected', {
733
+ deviceId: peripheral.id,
734
+ serviceUuid: selectedService.uuid,
735
+ });
703
736
  const characteristics = yield new Promise((resolve, reject) => {
704
737
  selectedService.discoverCharacteristics([], (error, chars) => {
705
738
  if (error) {
@@ -711,30 +744,26 @@ function discoverServicesAndCharacteristics(peripheral) {
711
744
  }
712
745
  });
713
746
  });
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
747
  let writeCharacteristic = null;
719
748
  let notifyCharacteristic = null;
720
749
  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) {
750
+ if (hdShared.matchesKnownBleUuid(characteristic.uuid, ONEKEY_WRITE_UUID_ALIASES)) {
724
751
  writeCharacteristic = characteristic;
725
752
  }
726
- else if (uuidKey === NORMALIZED_NOTIFY_UUID) {
753
+ else if (hdShared.matchesKnownBleUuid(characteristic.uuid, ONEKEY_NOTIFY_UUID_ALIASES)) {
727
754
  notifyCharacteristic = characteristic;
728
755
  }
729
756
  }
730
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Characteristic discovery result:', {
731
- writeFound: !!writeCharacteristic,
732
- notifyFound: !!notifyCharacteristic,
733
- });
734
757
  if (!writeCharacteristic || !notifyCharacteristic) {
735
758
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Missing characteristics - write:', !!writeCharacteristic, 'notify:', !!notifyCharacteristic);
736
759
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, 'Required characteristics not found');
737
760
  }
761
+ logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] characteristics selected', {
762
+ deviceId: peripheral.id,
763
+ serviceUuid: selectedService.uuid,
764
+ writeUuid: writeCharacteristic.uuid,
765
+ notifyUuid: notifyCharacteristic.uuid,
766
+ });
738
767
  return { write: writeCharacteristic, notify: notifyCharacteristic };
739
768
  }))();
740
769
  try {
@@ -755,27 +784,17 @@ function forceReconnectPeripheral(peripheral, deviceId) {
755
784
  reason: 'force-reconnect',
756
785
  });
757
786
  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
- });
787
+ yield runBleCallbackOperation(callback => peripheral.disconnect(() => {
788
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force disconnect completed');
789
+ callback();
790
+ }), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
765
791
  }
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
- });
792
+ yield runBleCallbackOperation(callback => peripheral.connect(callback), {
793
+ timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
794
+ timeoutBehavior: 'reject',
778
795
  });
796
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
797
+ connectedDevices.set(deviceId, peripheral);
779
798
  yield hdShared.wait(500);
780
799
  });
781
800
  }
@@ -852,13 +871,23 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
852
871
  }
853
872
  function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
854
873
  return index.__awaiter(this, void 0, void 0, function* () {
874
+ setupDisconnectListener(peripheral, deviceId, webContents);
875
+ try {
876
+ return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
877
+ }
878
+ catch (initialError) {
879
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct service discovery failed, retrying after reconnect', {
880
+ deviceId,
881
+ error: initialError,
882
+ });
883
+ }
855
884
  yield forceReconnectPeripheral(peripheral, deviceId);
856
885
  setupDisconnectListener(peripheral, deviceId, webContents);
857
886
  try {
858
887
  return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
859
888
  }
860
- catch (error) {
861
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed, attempting fresh scan...', error);
889
+ catch (reconnectError) {
890
+ logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed after reconnect, attempting fresh scan...', reconnectError);
862
891
  return freshScanAndDiscover(deviceId, webContents);
863
892
  }
864
893
  });
@@ -931,12 +960,24 @@ function connectDevice(deviceId, webContents) {
931
960
  return;
932
961
  }
933
962
  return new Promise((resolve, reject) => {
963
+ let connectionTimedOut = false;
934
964
  const timeout = setTimeout(() => {
965
+ connectionTimedOut = true;
935
966
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'Connection timeout'));
936
- }, CONNECTION_TIMEOUT);
967
+ }, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
937
968
  const connectedPeripheral = peripheral;
938
969
  connectedPeripheral.connect((error) => index.__awaiter(this, void 0, void 0, function* () {
939
970
  clearTimeout(timeout);
971
+ if (connectionTimedOut) {
972
+ if (!error) {
973
+ try {
974
+ connectedPeripheral.disconnect(() => undefined);
975
+ }
976
+ catch (_a) {
977
+ }
978
+ }
979
+ return;
980
+ }
940
981
  if (error) {
941
982
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection failed:', error);
942
983
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
@@ -966,17 +1007,20 @@ function disconnectDevice(deviceId) {
966
1007
  if (!peripheral) {
967
1008
  return;
968
1009
  }
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
- });
1010
+ const disconnectEntry = deviceDisconnectListeners.get(deviceId);
1011
+ if (disconnectEntry) {
1012
+ disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
1013
+ deviceDisconnectListeners.delete(deviceId);
1014
+ }
1015
+ yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
1016
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1017
+ timeoutBehavior: 'resolve',
1018
+ });
1019
+ cleanupDevice(deviceId, undefined, {
1020
+ cleanupConnection: true,
1021
+ sendDisconnectEvent: false,
1022
+ cancelOperations: true,
1023
+ reason: 'manual-disconnect',
980
1024
  });
981
1025
  });
982
1026
  }
@@ -991,18 +1035,11 @@ function unsubscribeNotifications(deviceId) {
991
1035
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Unsubscribing from notifications for device:', deviceId);
992
1036
  subscriptionOperations.set(deviceId, 'unsubscribing');
993
1037
  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
- });
1038
+ yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
1039
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1040
+ timeoutBehavior: 'resolve',
1005
1041
  });
1042
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification unsubscription completed');
1006
1043
  notifyCharacteristic.removeAllListeners('data');
1007
1044
  notificationCallbacks.delete(deviceId);
1008
1045
  subscribedDevices.delete(deviceId);
@@ -1054,19 +1091,13 @@ function subscribeNotifications(deviceId, callback) {
1054
1091
  notificationCallbacks.set(deviceId, callback);
1055
1092
  function rebuildAppSubscription(deviceId, notifyCharacteristic) {
1056
1093
  return index.__awaiter(this, void 0, void 0, function* () {
1057
- yield new Promise(resolve => {
1058
- notifyCharacteristic.unsubscribe(() => {
1059
- resolve();
1060
- });
1094
+ yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
1095
+ timeoutMs: BLE_CLEANUP_TIMEOUT,
1096
+ timeoutBehavior: 'resolve',
1061
1097
  });
1062
- yield new Promise((resolve, reject) => {
1063
- notifyCharacteristic.subscribe((error) => {
1064
- if (error) {
1065
- reject(error);
1066
- return;
1067
- }
1068
- resolve();
1069
- });
1098
+ yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
1099
+ timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
1100
+ timeoutBehavior: 'reject',
1070
1101
  });
1071
1102
  notifyCharacteristic.on('data', (data) => {
1072
1103
  if (!pairedDevices.has(deviceId)) {
@@ -1087,19 +1118,17 @@ function subscribeNotifications(deviceId, callback) {
1087
1118
  });
1088
1119
  }
1089
1120
  function setupNobleBleHandlers(webContents) {
1090
- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.');
1091
1121
  try {
1092
- console.log('[NobleBLE] NOBLE_VERSION_771');
1093
1122
  logger = require('electron-log');
1094
- console.log('[NobleBLE] electron-log loaded successfully.');
1095
1123
  const { ipcMain } = require('electron');
1096
- console.log('[NobleBLE] electron.ipcMain loaded successfully.');
1097
1124
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
1098
- console.log(`[NobleBLE] Registering handler for: ${hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
1099
1125
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
1100
1126
  try {
1101
1127
  const devices = yield enumerateDevices();
1102
- safeLog(logger, 'info', 'Enumeration completed, devices:', devices);
1128
+ safeLog(logger, 'debug', 'Enumeration completed', {
1129
+ count: devices.length,
1130
+ devices: devices.map(device => ({ id: device.id, name: device.name })),
1131
+ });
1103
1132
  return devices;
1104
1133
  }
1105
1134
  catch (error) {
@@ -1124,7 +1153,6 @@ function setupNobleBleHandlers(webContents) {
1124
1153
  yield disconnectDevice(deviceId);
1125
1154
  }));
1126
1155
  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
1156
  yield transmitHexDataToDevice(deviceId, hexData);
1129
1157
  }));
1130
1158
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_SUBSCRIBE, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
@@ -1169,23 +1197,23 @@ function setupNobleBleHandlers(webContents) {
1169
1197
  }));
1170
1198
  webContents.on('destroyed', () => {
1171
1199
  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
- });
1200
+ (() => index.__awaiter(this, void 0, void 0, function* () {
1201
+ const deviceIds = Array.from(connectedDevices.keys());
1202
+ for (const deviceId of deviceIds) {
1203
+ yield unsubscribeNotifications(deviceId).catch(() => undefined);
1204
+ yield disconnectDevice(deviceId).catch(() => undefined);
1205
+ }
1206
+ yield stopScanning().catch(() => undefined);
1207
+ if (noble && persistentStateListener) {
1208
+ noble.removeListener('stateChange', persistentStateListener);
1209
+ persistentStateListener = null;
1210
+ }
1211
+ cleanupNobleListeners();
1212
+ discoveredDevices.clear();
1213
+ safeLog(logger, 'info', 'Noble BLE cleanup completed');
1214
+ }))().catch(error => {
1215
+ safeLog(logger, 'error', 'Noble BLE cleanup failed', error);
1180
1216
  });
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
1217
  });
1190
1218
  safeLog(logger, 'info', 'Noble BLE IPC handlers setup completed');
1191
1219
  }