@onekeyfe/hd-transport-electron 1.1.27-alpha.4 → 1.1.27-alpha.40

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.
package/dist/ble-ops.d.ts CHANGED
@@ -7,12 +7,7 @@ export interface SoftRefreshParams {
7
7
  subscriptionOperations: Map<string, 'subscribing' | 'unsubscribing' | 'idle'>;
8
8
  subscribedDevices: Map<string, boolean>;
9
9
  pairedDevices: Set<string>;
10
- notificationCallbacks: Map<string, (hex: string) => void>;
11
- processNotificationData: (deviceId: string, data: Buffer) => {
12
- isComplete: boolean;
13
- completePacket?: string;
14
- error?: string;
15
- };
10
+ onNotificationData: (deviceId: string, data: Buffer) => void;
16
11
  logger: Logger | null;
17
12
  }
18
13
  export declare function softRefreshSubscription(params: SoftRefreshParams): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"ble-ops.d.ts","sourceRoot":"","sources":["../src/ble-ops.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;IACxD,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;IAC9E,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;IAC1D,uBAAuB,EAAE,CACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,KACT;QACH,UAAU,EAAE,OAAO,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDtF"}
1
+ {"version":3,"file":"ble-ops.d.ts","sourceRoot":"","sources":["../src/ble-ops.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;IACxD,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;IAC9E,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8CtF"}
@@ -32,7 +32,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
32
32
 
33
33
  function initNobleBleSupport(webContents) {
34
34
  return __awaiter(this, void 0, void 0, function* () {
35
- const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-2e41f27c.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-94c42077.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-de36348d.js');
5
+ var index = require('./index-02fc07fe.js');
6
6
 
7
7
 
8
8
 
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-de36348d.js');
3
+ var index = require('./index-02fc07fe.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
- var hdTransport = require('@onekeyfe/hd-transport');
6
5
  var pRetry = require('p-retry');
7
6
 
8
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -20,7 +19,7 @@ function safeLog(logger, level, message, ...args) {
20
19
 
21
20
  function softRefreshSubscription(params) {
22
21
  return index.__awaiter(this, void 0, void 0, function* () {
23
- const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, notificationCallbacks, processNotificationData, logger, } = params;
22
+ const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, onNotificationData, logger, } = params;
24
23
  if (!notifyCharacteristic) {
25
24
  throw new Error(`Notify characteristic not available for device ${deviceId}`);
26
25
  }
@@ -44,16 +43,7 @@ function softRefreshSubscription(params) {
44
43
  pairedDevices.add(deviceId);
45
44
  logger === null || logger === void 0 ? void 0 : logger.info('[BLE-OPS] Device paired successfully', { deviceId });
46
45
  }
47
- const result = processNotificationData(deviceId, data);
48
- if (result.error) {
49
- logger === null || logger === void 0 ? void 0 : logger.error('[BLE-OPS] Packet processing error:', result.error);
50
- return;
51
- }
52
- if (result.isComplete && result.completePacket) {
53
- const appCb = notificationCallbacks.get(deviceId);
54
- if (appCb)
55
- appCb(result.completePacket);
56
- }
46
+ onNotificationData(deviceId, data);
57
47
  });
58
48
  subscribedDevices.set(deviceId, true);
59
49
  subscriptionOperations.set(deviceId, 'idle');
@@ -76,15 +66,15 @@ const deviceCharacteristics = new Map();
76
66
  const notificationCallbacks = new Map();
77
67
  const subscribedDevices = new Map();
78
68
  const subscriptionOperations = new Map();
79
- const devicePacketStates = new Map();
80
69
  const ONEKEY_SERVICE_UUIDS = [hdShared.ONEKEY_SERVICE_UUID];
70
+ const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
81
71
  const NORMALIZED_WRITE_UUID = '0002';
82
72
  const NORMALIZED_NOTIFY_UUID = '0003';
83
73
  const BLUETOOTH_INIT_TIMEOUT = 10000;
84
- const DEVICE_SCAN_TIMEOUT = 5000;
85
- const FAST_SCAN_TIMEOUT = 1500;
74
+ const DEVICE_SCAN_TIMEOUT = 8000;
75
+ const FAST_SCAN_TIMEOUT = 8000;
86
76
  const DEVICE_CHECK_INTERVAL = 500;
87
- const CONNECTION_TIMEOUT = 3000;
77
+ const CONNECTION_TIMEOUT = 8000;
88
78
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
89
79
  const BLE_PACKET_SIZE = 192;
90
80
  const UNIFIED_WRITE_DELAY = 5;
@@ -93,70 +83,40 @@ const IS_WINDOWS = process.platform === 'win32';
93
83
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
94
84
  /status:\s*3/i,
95
85
  ];
96
- const MIN_HEADER_LENGTH = 9;
97
- function processNotificationData(deviceId, data) {
98
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification', {
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
+ 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);
110
+ }
111
+ function emitRawNotification(deviceId, data) {
112
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Raw notification', {
99
113
  deviceId,
100
114
  dataLength: data.length,
115
+ firstBytes: data.subarray(0, 8).toString('hex'),
101
116
  });
102
- let packetState = devicePacketStates.get(deviceId);
103
- if (!packetState) {
104
- packetState = { bufferLength: 0, buffer: [], packetCount: 0 };
105
- devicePacketStates.set(deviceId, packetState);
106
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Initialized new packet state for device:', deviceId);
107
- }
108
- try {
109
- if (hdShared.isHeaderChunk(data)) {
110
- if (data.length < MIN_HEADER_LENGTH) {
111
- return { isComplete: false, error: 'Invalid header chunk: too short' };
112
- }
113
- const messageId = `${deviceId}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
114
- packetState.bufferLength = data.readInt32BE(5);
115
- packetState.buffer = [...data.subarray(3)];
116
- packetState.packetCount = 1;
117
- packetState.messageId = messageId;
118
- if (packetState.bufferLength < 0) {
119
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Invalid negative packet length detected:', {
120
- length: packetState.bufferLength,
121
- dataLength: data.length,
122
- rawHeader: data.subarray(0, Math.min(16, data.length)).toString('hex'),
123
- lengthBytes: data.subarray(5, 9).toString('hex'),
124
- });
125
- resetPacketState(packetState);
126
- return { isComplete: false, error: 'Invalid packet length in header' };
127
- }
128
- }
129
- else {
130
- if (packetState.bufferLength === 0) {
131
- return { isComplete: false, error: 'Received data chunk without header' };
132
- }
133
- packetState.packetCount += 1;
134
- packetState.buffer = packetState.buffer.concat([...data]);
135
- }
136
- if (packetState.buffer.length - hdTransport.COMMON_HEADER_SIZE >= packetState.bufferLength) {
137
- const completeBuffer = Buffer.from(packetState.buffer);
138
- const hexString = completeBuffer.toString('hex');
139
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Packet assembled', {
140
- deviceId,
141
- totalPackets: packetState.packetCount,
142
- expectedLength: packetState.bufferLength,
143
- actualLength: packetState.buffer.length - hdTransport.COMMON_HEADER_SIZE,
144
- });
145
- resetPacketState(packetState);
146
- return { isComplete: true, completePacket: hexString };
147
- }
148
- return { isComplete: false };
149
- }
150
- catch (error) {
151
- resetPacketState(packetState);
152
- return { isComplete: false, error: `Packet processing error: ${error}` };
153
- }
154
- }
155
- function resetPacketState(packetState) {
156
- packetState.bufferLength = 0;
157
- packetState.buffer = [];
158
- packetState.packetCount = 0;
159
- packetState.messageId = undefined;
117
+ const appCb = notificationCallbacks.get(deviceId);
118
+ if (appCb)
119
+ appCb(data.toString('hex'));
160
120
  }
161
121
  function checkBluetoothAvailability() {
162
122
  return index.__awaiter(this, void 0, void 0, function* () {
@@ -201,7 +161,6 @@ function setupPersistentStateListener() {
201
161
  deviceCharacteristics.clear();
202
162
  subscribedDevices.clear();
203
163
  notificationCallbacks.clear();
204
- devicePacketStates.clear();
205
164
  subscriptionOperations.clear();
206
165
  pairedDevices.clear();
207
166
  if (noble) {
@@ -321,7 +280,6 @@ function cleanupDevice(deviceId, webContents, options = {}) {
321
280
  connectedDevices.delete(deviceId);
322
281
  deviceCharacteristics.delete(deviceId);
323
282
  notificationCallbacks.delete(deviceId);
324
- devicePacketStates.delete(deviceId);
325
283
  subscribedDevices.delete(deviceId);
326
284
  subscriptionOperations.delete(deviceId);
327
285
  pairedDevices.delete(deviceId);
@@ -436,8 +394,7 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
436
394
  subscriptionOperations,
437
395
  subscribedDevices,
438
396
  pairedDevices,
439
- notificationCallbacks,
440
- processNotificationData,
397
+ onNotificationData: emitRawNotification,
441
398
  logger,
442
399
  });
443
400
  logger === null || logger === void 0 ? void 0 : logger.info('[BLE-Write] Subscription refresh completed', { deviceId });
@@ -506,13 +463,21 @@ function transmitHexDataToDevice(deviceId, hexData) {
506
463
  });
507
464
  }
508
465
  function handleDeviceDiscovered(peripheral) {
509
- var _a;
466
+ var _a, _b;
510
467
  const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
511
- if (!hdShared.isOnekeyDevice(deviceName)) {
468
+ const serviceUuids = ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [];
469
+ if (!isOneKeyPeripheral(peripheral)) {
512
470
  return;
513
471
  }
514
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered OneKey device:', deviceName);
472
+ const isNewDevice = !discoveredDevices.has(peripheral.id);
515
473
  discoveredDevices.set(peripheral.id, peripheral);
474
+ 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,
479
+ });
480
+ }
516
481
  }
517
482
  function ensureDiscoverListener() {
518
483
  if (!noble)
@@ -557,7 +522,7 @@ function performTargetedScan(targetDeviceId) {
557
522
  resolve(null);
558
523
  }, FAST_SCAN_TIMEOUT);
559
524
  nobleInstance.on('discover', onDiscover);
560
- nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error) => {
525
+ nobleInstance.startScanning([], false, (error) => {
561
526
  if (error) {
562
527
  clearTimeout(timeoutId);
563
528
  nobleInstance.removeListener('discover', onDiscover);
@@ -607,11 +572,13 @@ function enumerateDevices() {
607
572
  });
608
573
  };
609
574
  const timeoutId = setTimeout(() => {
575
+ checkDevices();
610
576
  cleanup();
611
577
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
612
578
  resolve(devices);
613
579
  }, DEVICE_SCAN_TIMEOUT);
614
- nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error) => {
580
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
581
+ nobleInstance.startScanning([], false, (error) => {
615
582
  if (error) {
616
583
  cleanup();
617
584
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start scanning:', error);
@@ -706,7 +673,7 @@ function discoverServicesAndCharacteristics(peripheral) {
706
673
  });
707
674
  const discoveryPromise = (() => index.__awaiter(this, void 0, void 0, function* () {
708
675
  const services = yield new Promise((resolve, reject) => {
709
- peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error, svc) => {
676
+ peripheral.discoverServices([], (error, svc) => {
710
677
  if (error) {
711
678
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed:', error);
712
679
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, error.message));
@@ -716,13 +683,25 @@ function discoverServicesAndCharacteristics(peripheral) {
716
683
  }
717
684
  });
718
685
  });
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));
719
687
  if (!services || services.length === 0) {
720
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
688
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
689
+ }
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
+ }
698
+ if (!service) {
699
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
721
700
  }
722
- const service = services[0];
723
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Found service:', service.uuid);
701
+ const selectedService = service;
702
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Using service:', selectedService.uuid);
724
703
  const characteristics = yield new Promise((resolve, reject) => {
725
- service.discoverCharacteristics([hdShared.ONEKEY_WRITE_CHARACTERISTIC_UUID, hdShared.ONEKEY_NOTIFY_CHARACTERISTIC_UUID], (error, chars) => {
704
+ selectedService.discoverCharacteristics([], (error, chars) => {
726
705
  if (error) {
727
706
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Characteristic discovery failed:', error);
728
707
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, error.message));
@@ -944,7 +923,6 @@ function connectDevice(deviceId, webContents) {
944
923
  existingCharacteristics.notify.removeAllListeners('data');
945
924
  }
946
925
  notificationCallbacks.delete(deviceId);
947
- devicePacketStates.delete(deviceId);
948
926
  subscribedDevices.delete(deviceId);
949
927
  }
950
928
  const characteristics = yield setupConnectionAndDiscoverServices(peripheral, deviceId, webContents);
@@ -1027,7 +1005,6 @@ function unsubscribeNotifications(deviceId) {
1027
1005
  });
1028
1006
  notifyCharacteristic.removeAllListeners('data');
1029
1007
  notificationCallbacks.delete(deviceId);
1030
- devicePacketStates.delete(deviceId);
1031
1008
  subscribedDevices.delete(deviceId);
1032
1009
  }
1033
1010
  finally {
@@ -1067,12 +1044,6 @@ function subscribeNotifications(deviceId, callback) {
1067
1044
  if (subscribedDevices.get(deviceId)) {
1068
1045
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device already subscribed to characteristic, updating callback only');
1069
1046
  notificationCallbacks.set(deviceId, callback);
1070
- devicePacketStates.set(deviceId, {
1071
- bufferLength: 0,
1072
- buffer: [],
1073
- packetCount: 0,
1074
- messageId: undefined,
1075
- });
1076
1047
  subscriptionOperations.set(deviceId, 'idle');
1077
1048
  return Promise.resolve();
1078
1049
  }
@@ -1081,12 +1052,6 @@ function subscribeNotifications(deviceId, callback) {
1081
1052
  }
1082
1053
  notifyCharacteristic.removeAllListeners('data');
1083
1054
  notificationCallbacks.set(deviceId, callback);
1084
- devicePacketStates.set(deviceId, {
1085
- bufferLength: 0,
1086
- buffer: [],
1087
- packetCount: 0,
1088
- messageId: undefined,
1089
- });
1090
1055
  function rebuildAppSubscription(deviceId, notifyCharacteristic) {
1091
1056
  return index.__awaiter(this, void 0, void 0, function* () {
1092
1057
  yield new Promise(resolve => {
@@ -1108,16 +1073,7 @@ function subscribeNotifications(deviceId, callback) {
1108
1073
  pairedDevices.add(deviceId);
1109
1074
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device paired successfully', { deviceId });
1110
1075
  }
1111
- const result = processNotificationData(deviceId, data);
1112
- if (result.error) {
1113
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Packet processing error:', result.error);
1114
- return;
1115
- }
1116
- if (result.isComplete && result.completePacket) {
1117
- const appCb = notificationCallbacks.get(deviceId);
1118
- if (appCb)
1119
- appCb(result.completePacket);
1120
- }
1076
+ emitRawNotification(deviceId, data);
1121
1077
  });
1122
1078
  });
1123
1079
  }
@@ -1 +1 @@
1
- {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AAshDhE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAsKpE"}
1
+ {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AAy9ChE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAsKpE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.1.27-alpha.4",
3
+ "version": "1.1.27-alpha.40",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -25,9 +25,9 @@
25
25
  "electron-log": ">=4.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-core": "1.1.27-alpha.4",
29
- "@onekeyfe/hd-shared": "1.1.27-alpha.4",
30
- "@onekeyfe/hd-transport": "1.1.27-alpha.4",
28
+ "@onekeyfe/hd-core": "1.1.27-alpha.40",
29
+ "@onekeyfe/hd-shared": "1.1.27-alpha.40",
30
+ "@onekeyfe/hd-transport": "1.1.27-alpha.40",
31
31
  "@stoprocent/noble": "2.3.16",
32
32
  "p-retry": "^4.6.2"
33
33
  },
@@ -36,5 +36,5 @@
36
36
  "electron": "^25.0.0",
37
37
  "typescript": "^5.3.3"
38
38
  },
39
- "gitHead": "93d0e7734f8c3a6380be10f74de203f0ba785bb8"
39
+ "gitHead": "484f6253dd8c776fc4037f5da171c8f2226baea6"
40
40
  }
package/src/ble-ops.ts CHANGED
@@ -7,15 +7,7 @@ export interface SoftRefreshParams {
7
7
  subscriptionOperations: Map<string, 'subscribing' | 'unsubscribing' | 'idle'>;
8
8
  subscribedDevices: Map<string, boolean>;
9
9
  pairedDevices: Set<string>;
10
- notificationCallbacks: Map<string, (hex: string) => void>;
11
- processNotificationData: (
12
- deviceId: string,
13
- data: Buffer
14
- ) => {
15
- isComplete: boolean;
16
- completePacket?: string;
17
- error?: string;
18
- };
10
+ onNotificationData: (deviceId: string, data: Buffer) => void;
19
11
  logger: Logger | null;
20
12
  }
21
13
 
@@ -26,8 +18,7 @@ export async function softRefreshSubscription(params: SoftRefreshParams): Promis
26
18
  subscriptionOperations,
27
19
  subscribedDevices,
28
20
  pairedDevices,
29
- notificationCallbacks,
30
- processNotificationData,
21
+ onNotificationData,
31
22
  logger,
32
23
  } = params;
33
24
 
@@ -60,15 +51,7 @@ export async function softRefreshSubscription(params: SoftRefreshParams): Promis
60
51
  logger?.info('[BLE-OPS] Device paired successfully', { deviceId });
61
52
  }
62
53
 
63
- const result = processNotificationData(deviceId, data);
64
- if (result.error) {
65
- logger?.error('[BLE-OPS] Packet processing error:', result.error);
66
- return;
67
- }
68
- if (result.isComplete && result.completePacket) {
69
- const appCb = notificationCallbacks.get(deviceId);
70
- if (appCb) appCb(result.completePacket);
71
- }
54
+ onNotificationData(deviceId, data);
72
55
  });
73
56
 
74
57
  subscribedDevices.set(deviceId, true);
@@ -9,14 +9,10 @@ import {
9
9
  EOneKeyBleMessageKeys,
10
10
  ERRORS,
11
11
  HardwareErrorCode,
12
- ONEKEY_NOTIFY_CHARACTERISTIC_UUID,
13
12
  ONEKEY_SERVICE_UUID,
14
- ONEKEY_WRITE_CHARACTERISTIC_UUID,
15
- isHeaderChunk,
16
13
  isOnekeyDevice,
17
14
  wait,
18
15
  } from '@onekeyfe/hd-shared';
19
- import { COMMON_HEADER_SIZE } from '@onekeyfe/hd-transport';
20
16
  import pRetry from 'p-retry';
21
17
 
22
18
  import { safeLog } from './types/noble-extended';
@@ -55,15 +51,6 @@ const subscribedDevices = new Map<string, boolean>(); // Track subscription stat
55
51
  // 🔒 Subscription operation state tracking to prevent race conditions
56
52
  const subscriptionOperations = new Map<string, 'subscribing' | 'unsubscribing' | 'idle'>();
57
53
 
58
- // Packet reassembly state for each device
59
- interface PacketAssemblyState {
60
- bufferLength: number;
61
- buffer: number[];
62
- packetCount: number;
63
- messageId?: string; // Add message ID to track concurrent requests
64
- }
65
- const devicePacketStates = new Map<string, PacketAssemblyState>();
66
-
67
54
  // Windows-only response watchdog state moved to utils/windows-ble-recovery
68
55
 
69
56
  // Pairing-related state removed
@@ -72,6 +59,7 @@ const devicePacketStates = new Map<string, PacketAssemblyState>();
72
59
 
73
60
  // Service UUIDs to scan for - using constants from hd-shared
74
61
  const ONEKEY_SERVICE_UUIDS = [ONEKEY_SERVICE_UUID];
62
+ const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
75
63
 
76
64
  // Pre-normalized characteristic identifiers for fast comparison
77
65
  const NORMALIZED_WRITE_UUID = '0002';
@@ -79,10 +67,10 @@ const NORMALIZED_NOTIFY_UUID = '0003';
79
67
 
80
68
  // Timeout and interval constants
81
69
  const BLUETOOTH_INIT_TIMEOUT = 10000; // 10 seconds for Bluetooth initialization
82
- const DEVICE_SCAN_TIMEOUT = 5000; // 5 seconds for device scanning
83
- const FAST_SCAN_TIMEOUT = 1500; // 1.5 seconds for fast targeted scanning
70
+ const DEVICE_SCAN_TIMEOUT = 8000; // 8 seconds for device scanning (Pro2 has longer advertising interval)
71
+ const FAST_SCAN_TIMEOUT = 8000; // 8 seconds for targeted scanning (Pro2 has longer advertising interval)
84
72
  const DEVICE_CHECK_INTERVAL = 500; // 500ms interval for periodic device checks
85
- const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection
73
+ const CONNECTION_TIMEOUT = 8000; // 8 seconds for device connection (BLE reconnect after release can be slow)
86
74
  const SERVICE_DISCOVERY_TIMEOUT = 10000; // 10 seconds for service discovery
87
75
 
88
76
  // Write-related constants
@@ -94,101 +82,49 @@ const ABORTABLE_WRITE_ERROR_PATTERNS = [
94
82
  /status:\s*3/i, // Windows pairing cancelled / GATT write failed
95
83
  ];
96
84
 
97
- // Validation limits
98
- const MIN_HEADER_LENGTH = 9; // Minimum header chunk length
99
-
100
- // Packet processing result types
101
- interface PacketProcessResult {
102
- isComplete: boolean;
103
- completePacket?: string;
104
- error?: string;
85
+ function getBleUuidKey(uuid?: string | null) {
86
+ const normalized = (uuid ?? '').replace(/-/g, '').toLowerCase();
87
+ return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
105
88
  }
106
89
 
107
- // Process incoming BLE notification data with proper packet reassembly
108
- function processNotificationData(deviceId: string, data: Buffer): PacketProcessResult {
109
- // notification telemetry
110
- logger?.info('[NobleBLE] Notification', {
111
- deviceId,
112
- dataLength: data.length,
113
- });
114
-
115
- // Get or initialize packet state for this device
116
- let packetState = devicePacketStates.get(deviceId);
117
- if (!packetState) {
118
- packetState = { bufferLength: 0, buffer: [], packetCount: 0 };
119
- devicePacketStates.set(deviceId, packetState);
120
- logger?.info('[NobleBLE] Initialized new packet state for device:', deviceId);
121
- }
122
-
123
- try {
124
- if (isHeaderChunk(data)) {
125
- // Validate header chunk
126
- if (data.length < MIN_HEADER_LENGTH) {
127
- return { isComplete: false, error: 'Invalid header chunk: too short' };
128
- }
90
+ const NORMALIZED_ONEKEY_SERVICE_UUIDS = new Set([
91
+ ...ONEKEY_SERVICE_UUIDS.map(uuid => getBleUuidKey(uuid)),
92
+ '0001',
93
+ ]);
129
94
 
130
- // Generate message ID for this packet sequence
131
- const messageId = `${deviceId}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
132
-
133
- // Reset packet state for new message
134
- packetState.bufferLength = data.readInt32BE(5);
135
- packetState.buffer = [...data.subarray(3)];
136
- packetState.packetCount = 1;
137
- packetState.messageId = messageId;
138
-
139
- // Only validate for negative lengths (which would be invalid)
140
- if (packetState.bufferLength < 0) {
141
- logger?.error('[NobleBLE] Invalid negative packet length detected:', {
142
- length: packetState.bufferLength,
143
- dataLength: data.length,
144
- rawHeader: data.subarray(0, Math.min(16, data.length)).toString('hex'),
145
- lengthBytes: data.subarray(5, 9).toString('hex'),
146
- });
147
- resetPacketState(packetState);
148
- return { isComplete: false, error: 'Invalid packet length in header' };
149
- }
150
- } else {
151
- // Validate we have an active packet session
152
- if (packetState.bufferLength === 0) {
153
- return { isComplete: false, error: 'Received data chunk without header' };
154
- }
155
-
156
- // Increment packet counter and append data
157
- packetState.packetCount += 1;
158
- packetState.buffer = packetState.buffer.concat([...data]);
159
- }
160
-
161
- // Check if packet is complete
162
- if (packetState.buffer.length - COMMON_HEADER_SIZE >= packetState.bufferLength) {
163
- const completeBuffer = Buffer.from(packetState.buffer);
164
- const hexString = completeBuffer.toString('hex');
165
-
166
- logger?.info('[NobleBLE] Packet assembled', {
167
- deviceId,
168
- totalPackets: packetState.packetCount,
169
- expectedLength: packetState.bufferLength,
170
- actualLength: packetState.buffer.length - COMMON_HEADER_SIZE,
171
- });
172
-
173
- // Reset packet state for next message
174
- resetPacketState(packetState);
95
+ function isGenericBleService(uuid?: string | null) {
96
+ return ['1800', '1801', '180a', '180f'].includes(getBleUuidKey(uuid));
97
+ }
175
98
 
176
- return { isComplete: true, completePacket: hexString };
177
- }
99
+ function hasOneKeyAdvertisementService(peripheral: Peripheral) {
100
+ const serviceUuids = peripheral.advertisement?.serviceUuids ?? [];
101
+ return serviceUuids.some(uuid => {
102
+ const uuidKey = getBleUuidKey(uuid);
103
+ return (
104
+ NORMALIZED_ONEKEY_SERVICE_UUIDS.has(uuidKey) ||
105
+ PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(uuidKey)
106
+ );
107
+ });
108
+ }
178
109
 
179
- return { isComplete: false };
180
- } catch (error) {
181
- resetPacketState(packetState);
182
- return { isComplete: false, error: `Packet processing error: ${error}` };
183
- }
110
+ function isOneKeyPeripheral(peripheral: Peripheral) {
111
+ const deviceName = peripheral.advertisement?.localName || null;
112
+ return isOnekeyDevice(deviceName, peripheral.id) || hasOneKeyAdvertisementService(peripheral);
184
113
  }
185
114
 
186
- // Reset packet state to clean state
187
- function resetPacketState(packetState: PacketAssemblyState): void {
188
- packetState.bufferLength = 0;
189
- packetState.buffer = [];
190
- packetState.packetCount = 0;
191
- packetState.messageId = undefined;
115
+ /**
116
+ * Forward a single BLE notification chunk (not an assembled packet) to the
117
+ * renderer-side transport. Packet reassembly is handled by ElectronBleTransport.
118
+ */
119
+ function emitRawNotification(deviceId: string, data: Buffer): void {
120
+ logger?.info('[NobleBLE] Raw notification', {
121
+ deviceId,
122
+ dataLength: data.length,
123
+ firstBytes: data.subarray(0, 8).toString('hex'),
124
+ });
125
+
126
+ const appCb = notificationCallbacks.get(deviceId);
127
+ if (appCb) appCb(data.toString('hex'));
192
128
  }
193
129
 
194
130
  // Check Bluetooth availability - returns detailed state
@@ -250,7 +186,6 @@ function setupPersistentStateListener(): void {
250
186
  deviceCharacteristics.clear();
251
187
  subscribedDevices.clear();
252
188
  notificationCallbacks.clear();
253
- devicePacketStates.clear();
254
189
  subscriptionOperations.clear();
255
190
  pairedDevices.clear();
256
191
 
@@ -428,7 +363,6 @@ function cleanupDevice(
428
363
  connectedDevices.delete(deviceId);
429
364
  deviceCharacteristics.delete(deviceId);
430
365
  notificationCallbacks.delete(deviceId);
431
- devicePacketStates.delete(deviceId);
432
366
  subscribedDevices.delete(deviceId);
433
367
  subscriptionOperations.delete(deviceId);
434
368
  pairedDevices.delete(deviceId);
@@ -590,8 +524,7 @@ async function attemptWindowsWriteUntilPaired(
590
524
  subscriptionOperations,
591
525
  subscribedDevices,
592
526
  pairedDevices,
593
- notificationCallbacks,
594
- processNotificationData,
527
+ onNotificationData: emitRawNotification,
595
528
  logger,
596
529
  });
597
530
  logger?.info('[BLE-Write] Subscription refresh completed', { deviceId });
@@ -686,14 +619,23 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
686
619
  // Handle discovered device (for general enumeration only)
687
620
  function handleDeviceDiscovered(peripheral: Peripheral): void {
688
621
  const deviceName = peripheral.advertisement?.localName || 'Unknown Device';
622
+ const serviceUuids = peripheral.advertisement?.serviceUuids || [];
689
623
 
690
- // Only process OneKey devices for general discovery
691
- if (!isOnekeyDevice(deviceName)) {
624
+ // Only process OneKey candidates for general discovery. Avoid logging every
625
+ // ambient BLE peripheral; it makes Pro2 debugging hard to read.
626
+ if (!isOneKeyPeripheral(peripheral)) {
692
627
  return;
693
628
  }
694
629
 
695
- logger?.info('[NobleBLE] Discovered OneKey device:', deviceName);
630
+ const isNewDevice = !discoveredDevices.has(peripheral.id);
696
631
  discoveredDevices.set(peripheral.id, peripheral);
632
+ if (isNewDevice) {
633
+ logger?.info('[NobleBLE] Discovered OneKey BLE device:', {
634
+ name: deviceName,
635
+ id: peripheral.id,
636
+ serviceUUIDs: serviceUuids,
637
+ });
638
+ }
697
639
  }
698
640
 
699
641
  // Ensure discover listener is properly set up
@@ -753,8 +695,8 @@ async function performTargetedScan(targetDeviceId: string): Promise<Peripheral |
753
695
  // Add local listener for this scan
754
696
  nobleInstance.on('discover', onDiscover);
755
697
 
756
- // Start scanning
757
- nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => {
698
+ // Start scanning — no service UUID filter (Pro2 may use different service UUID)
699
+ nobleInstance.startScanning([], false, (error?: Error) => {
758
700
  if (error) {
759
701
  clearTimeout(timeoutId);
760
702
  nobleInstance.removeListener('discover', onDiscover);
@@ -816,15 +758,19 @@ async function enumerateDevices(): Promise<DeviceInfo[]> {
816
758
  });
817
759
  };
818
760
 
819
- // Set timeout for scanning
761
+ // Set timeout for scanning — use longer timeout to catch slow-advertising devices like Pro2
820
762
  const timeoutId = setTimeout(() => {
763
+ // Final collection before resolving — catches devices discovered near the deadline
764
+ checkDevices();
821
765
  cleanup();
822
766
  logger?.info('[NobleBLE] Scan completed, found devices:', devices.length);
823
767
  resolve(devices);
824
768
  }, DEVICE_SCAN_TIMEOUT);
825
769
 
826
- // Start scanning for OneKey service UUIDs
827
- nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => {
770
+ // Start scanning without a service UUID filter so Pro2 advertisements with
771
+ // short vendor UUIDs can be found, but only OneKey candidates are logged/returned.
772
+ logger?.info('[NobleBLE] Scanning for OneKey BLE devices');
773
+ nobleInstance.startScanning([], false, (error?: Error) => {
828
774
  if (error) {
829
775
  cleanup();
830
776
  logger?.error('[NobleBLE] Failed to start scanning:', error);
@@ -952,9 +898,9 @@ async function discoverServicesAndCharacteristics(
952
898
 
953
899
  // Main discovery logic as async function
954
900
  const discoveryPromise = (async (): Promise<CharacteristicPair> => {
955
- // Step 1: Discover services (promisified)
901
+ // Step 1: Discover ALL services (no filter — Pro2 may use different service UUID)
956
902
  const services = await new Promise<Service[]>((resolve, reject) => {
957
- peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error, svc) => {
903
+ peripheral.discoverServices([], (error, svc) => {
958
904
  if (error) {
959
905
  logger?.error('[NobleBLE] Service discovery failed:', error);
960
906
  reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message));
@@ -964,26 +910,41 @@ async function discoverServicesAndCharacteristics(
964
910
  });
965
911
  });
966
912
 
913
+ // Log all discovered services
914
+ logger?.info(
915
+ '[NobleBLE] All services:',
916
+ services?.map(s => s.uuid)
917
+ );
918
+
967
919
  if (!services || services.length === 0) {
968
- throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
920
+ throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No services found');
969
921
  }
970
922
 
971
- const service = services[0];
972
- logger?.info('[NobleBLE] Found service:', service.uuid);
923
+ // Find OneKey service Noble may expose 128-bit UUIDs as short UUID keys.
924
+ let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
925
+ if (!service) {
926
+ logger?.info('[NobleBLE] Known OneKey service UUID not found, trying first vendor service');
927
+ service =
928
+ services.find(s => PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(getBleUuidKey(s.uuid))) ||
929
+ services.find(s => !isGenericBleService(s.uuid)) ||
930
+ services[0];
931
+ }
932
+ if (!service) {
933
+ throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
934
+ }
935
+ const selectedService = service;
936
+ logger?.info('[NobleBLE] Using service:', selectedService.uuid);
973
937
 
974
- // Step 2: Discover characteristics (promisified)
938
+ // Step 2: Discover ALL characteristics (no filter)
975
939
  const characteristics = await new Promise<Characteristic[]>((resolve, reject) => {
976
- service.discoverCharacteristics(
977
- [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID],
978
- (error, chars) => {
979
- if (error) {
980
- logger?.error('[NobleBLE] Characteristic discovery failed:', error);
981
- reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message));
982
- } else {
983
- resolve(chars);
984
- }
940
+ selectedService.discoverCharacteristics([], (error, chars) => {
941
+ if (error) {
942
+ logger?.error('[NobleBLE] Characteristic discovery failed:', error);
943
+ reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message));
944
+ } else {
945
+ resolve(chars);
985
946
  }
986
- );
947
+ });
987
948
  });
988
949
 
989
950
  // Step 3: Find required characteristics
@@ -1320,7 +1281,6 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1320
1281
  existingCharacteristics.notify.removeAllListeners('data');
1321
1282
  }
1322
1283
  notificationCallbacks.delete(deviceId);
1323
- devicePacketStates.delete(deviceId);
1324
1284
  subscribedDevices.delete(deviceId);
1325
1285
  // Continue to re-setup the connection properly
1326
1286
  }
@@ -1431,7 +1391,6 @@ async function unsubscribeNotifications(deviceId: string): Promise<void> {
1431
1391
  // Remove all listeners and clear subscription status
1432
1392
  notifyCharacteristic.removeAllListeners('data');
1433
1393
  notificationCallbacks.delete(deviceId);
1434
- devicePacketStates.delete(deviceId);
1435
1394
  subscribedDevices.delete(deviceId);
1436
1395
  } finally {
1437
1396
  // 🔒 CRITICAL: Always clear operation state (even on error)
@@ -1498,14 +1457,6 @@ async function subscribeNotifications(
1498
1457
  // Just update the callback without re-subscribing
1499
1458
  notificationCallbacks.set(deviceId, callback);
1500
1459
 
1501
- // Reset packet state for new session
1502
- devicePacketStates.set(deviceId, {
1503
- bufferLength: 0,
1504
- buffer: [],
1505
- packetCount: 0,
1506
- messageId: undefined,
1507
- });
1508
-
1509
1460
  // 🔒 Clear operation state
1510
1461
  subscriptionOperations.set(deviceId, 'idle');
1511
1462
  return Promise.resolve();
@@ -1522,14 +1473,6 @@ async function subscribeNotifications(
1522
1473
  // Store callback for this device
1523
1474
  notificationCallbacks.set(deviceId, callback);
1524
1475
 
1525
- // Reset packet state for new subscription session
1526
- devicePacketStates.set(deviceId, {
1527
- bufferLength: 0,
1528
- buffer: [],
1529
- packetCount: 0,
1530
- messageId: undefined,
1531
- });
1532
-
1533
1476
  // Helper: rebuild a clean application-layer subscription
1534
1477
  async function rebuildAppSubscription(
1535
1478
  deviceId: string,
@@ -1558,15 +1501,7 @@ async function subscribeNotifications(
1558
1501
  logger?.info('[NobleBLE] Device paired successfully', { deviceId });
1559
1502
  }
1560
1503
 
1561
- const result = processNotificationData(deviceId, data);
1562
- if (result.error) {
1563
- logger?.error('[NobleBLE] Packet processing error:', result.error);
1564
- return;
1565
- }
1566
- if (result.isComplete && result.completePacket) {
1567
- const appCb = notificationCallbacks.get(deviceId);
1568
- if (appCb) appCb(result.completePacket);
1569
- }
1504
+ emitRawNotification(deviceId, data);
1570
1505
  });
1571
1506
  }
1572
1507