@onekeyfe/hd-transport-electron 1.1.27-alpha.35 → 1.1.27-alpha.37

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-4f7435c3.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-b01d011d.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-b01d011d.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
74
  const DEVICE_SCAN_TIMEOUT = 8000;
85
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 });
@@ -509,12 +466,18 @@ function handleDeviceDiscovered(peripheral) {
509
466
  var _a, _b;
510
467
  const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
511
468
  const serviceUuids = ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [];
512
- logger === null || logger === void 0 ? void 0 : logger.info(`[NobleBLE] Scan found: name="${deviceName}" id=${peripheral.id} serviceUUIDs=[${serviceUuids.join(', ')}]`);
513
- if (!hdShared.isOnekeyDevice(deviceName)) {
469
+ if (!isOneKeyPeripheral(peripheral)) {
514
470
  return;
515
471
  }
516
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered OneKey device:', deviceName);
472
+ const isNewDevice = !discoveredDevices.has(peripheral.id);
517
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
+ }
518
481
  }
519
482
  function ensureDiscoverListener() {
520
483
  if (!noble)
@@ -614,7 +577,7 @@ function enumerateDevices() {
614
577
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
615
578
  resolve(devices);
616
579
  }, DEVICE_SCAN_TIMEOUT);
617
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for ALL BLE devices (no service UUID filter)');
580
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
618
581
  nobleInstance.startScanning([], false, (error) => {
619
582
  if (error) {
620
583
  cleanup();
@@ -724,17 +687,21 @@ function discoverServicesAndCharacteristics(peripheral) {
724
687
  if (!services || services.length === 0) {
725
688
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
726
689
  }
727
- let service = services.find(s => ONEKEY_SERVICE_UUIDS.includes(s.uuid));
690
+ let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
728
691
  if (!service) {
729
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Known OneKey service UUID not found, trying first non-generic service');
730
- service = services.find(s => !['1800', '1801', '180a'].includes(s.uuid)) || services[0];
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];
731
697
  }
732
698
  if (!service) {
733
699
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
734
700
  }
735
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Using service:', service.uuid);
701
+ const selectedService = service;
702
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Using service:', selectedService.uuid);
736
703
  const characteristics = yield new Promise((resolve, reject) => {
737
- service.discoverCharacteristics([], (error, chars) => {
704
+ selectedService.discoverCharacteristics([], (error, chars) => {
738
705
  if (error) {
739
706
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Characteristic discovery failed:', error);
740
707
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, error.message));
@@ -956,7 +923,6 @@ function connectDevice(deviceId, webContents) {
956
923
  existingCharacteristics.notify.removeAllListeners('data');
957
924
  }
958
925
  notificationCallbacks.delete(deviceId);
959
- devicePacketStates.delete(deviceId);
960
926
  subscribedDevices.delete(deviceId);
961
927
  }
962
928
  const characteristics = yield setupConnectionAndDiscoverServices(peripheral, deviceId, webContents);
@@ -1039,7 +1005,6 @@ function unsubscribeNotifications(deviceId) {
1039
1005
  });
1040
1006
  notifyCharacteristic.removeAllListeners('data');
1041
1007
  notificationCallbacks.delete(deviceId);
1042
- devicePacketStates.delete(deviceId);
1043
1008
  subscribedDevices.delete(deviceId);
1044
1009
  }
1045
1010
  finally {
@@ -1079,12 +1044,6 @@ function subscribeNotifications(deviceId, callback) {
1079
1044
  if (subscribedDevices.get(deviceId)) {
1080
1045
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device already subscribed to characteristic, updating callback only');
1081
1046
  notificationCallbacks.set(deviceId, callback);
1082
- devicePacketStates.set(deviceId, {
1083
- bufferLength: 0,
1084
- buffer: [],
1085
- packetCount: 0,
1086
- messageId: undefined,
1087
- });
1088
1047
  subscriptionOperations.set(deviceId, 'idle');
1089
1048
  return Promise.resolve();
1090
1049
  }
@@ -1093,12 +1052,6 @@ function subscribeNotifications(deviceId, callback) {
1093
1052
  }
1094
1053
  notifyCharacteristic.removeAllListeners('data');
1095
1054
  notificationCallbacks.set(deviceId, callback);
1096
- devicePacketStates.set(deviceId, {
1097
- bufferLength: 0,
1098
- buffer: [],
1099
- packetCount: 0,
1100
- messageId: undefined,
1101
- });
1102
1055
  function rebuildAppSubscription(deviceId, notifyCharacteristic) {
1103
1056
  return index.__awaiter(this, void 0, void 0, function* () {
1104
1057
  yield new Promise(resolve => {
@@ -1120,16 +1073,7 @@ function subscribeNotifications(deviceId, callback) {
1120
1073
  pairedDevices.add(deviceId);
1121
1074
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device paired successfully', { deviceId });
1122
1075
  }
1123
- const result = processNotificationData(deviceId, data);
1124
- if (result.error) {
1125
- logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Packet processing error:', result.error);
1126
- return;
1127
- }
1128
- if (result.isComplete && result.completePacket) {
1129
- const appCb = notificationCallbacks.get(deviceId);
1130
- if (appCb)
1131
- appCb(result.completePacket);
1132
- }
1076
+ emitRawNotification(deviceId, data);
1133
1077
  });
1134
1078
  });
1135
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;AAgjDhE,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.35",
3
+ "version": "1.1.27-alpha.37",
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.35",
29
- "@onekeyfe/hd-shared": "1.1.27-alpha.35",
30
- "@onekeyfe/hd-transport": "1.1.27-alpha.35",
28
+ "@onekeyfe/hd-core": "1.1.27-alpha.37",
29
+ "@onekeyfe/hd-shared": "1.1.27-alpha.37",
30
+ "@onekeyfe/hd-transport": "1.1.27-alpha.37",
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": "3a5eb264b57a6cc2338d1f9534be0ba265c2db35"
39
+ "gitHead": "ff58ceb1249ba3f3f673b39f1c175093fab40a8b"
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';
@@ -82,7 +70,7 @@ const BLUETOOTH_INIT_TIMEOUT = 10000; // 10 seconds for Bluetooth initialization
82
70
  const DEVICE_SCAN_TIMEOUT = 8000; // 8 seconds for device scanning (Pro2 has longer advertising interval)
83
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
- });
90
+ const NORMALIZED_ONEKEY_SERVICE_UUIDS = new Set([
91
+ ...ONEKEY_SERVICE_UUIDS.map(uuid => getBleUuidKey(uuid)),
92
+ '0001',
93
+ ]);
114
94
 
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
- }
129
-
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 });
@@ -688,20 +621,21 @@ function handleDeviceDiscovered(peripheral: Peripheral): void {
688
621
  const deviceName = peripheral.advertisement?.localName || 'Unknown Device';
689
622
  const serviceUuids = peripheral.advertisement?.serviceUuids || [];
690
623
 
691
- // Log ALL discovered devices to help identify Pro2 BLE service UUID
692
- logger?.info(
693
- `[NobleBLE] Scan found: name="${deviceName}" id=${
694
- peripheral.id
695
- } serviceUUIDs=[${serviceUuids.join(', ')}]`
696
- );
697
-
698
- // Only process OneKey devices for general discovery
699
- 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)) {
700
627
  return;
701
628
  }
702
629
 
703
- logger?.info('[NobleBLE] Discovered OneKey device:', deviceName);
630
+ const isNewDevice = !discoveredDevices.has(peripheral.id);
704
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
+ }
705
639
  }
706
640
 
707
641
  // Ensure discover listener is properly set up
@@ -833,9 +767,9 @@ async function enumerateDevices(): Promise<DeviceInfo[]> {
833
767
  resolve(devices);
834
768
  }, DEVICE_SCAN_TIMEOUT);
835
769
 
836
- // Start scanning use empty array to discover ALL BLE devices (Pro2 may use different service UUID)
837
- // TODO: restore ONEKEY_SERVICE_UUIDS filter once Pro2 BLE service UUID is confirmed
838
- logger?.info('[NobleBLE] Scanning for ALL BLE devices (no service UUID filter)');
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');
839
773
  nobleInstance.startScanning([], false, (error?: Error) => {
840
774
  if (error) {
841
775
  cleanup();
@@ -986,23 +920,24 @@ async function discoverServicesAndCharacteristics(
986
920
  throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No services found');
987
921
  }
988
922
 
989
- // Find OneKey service — try known UUID first, fall back to first service
990
- let service = services.find(s => ONEKEY_SERVICE_UUIDS.includes(s.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)));
991
925
  if (!service) {
992
- logger?.info(
993
- '[NobleBLE] Known OneKey service UUID not found, trying first non-generic service'
994
- );
995
- // Skip generic BLE services (1800=GAP, 1801=GATT, 180a=DeviceInfo)
996
- service = services.find(s => !['1800', '1801', '180a'].includes(s.uuid)) || services[0];
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];
997
931
  }
998
932
  if (!service) {
999
933
  throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
1000
934
  }
1001
- logger?.info('[NobleBLE] Using service:', service.uuid);
935
+ const selectedService = service;
936
+ logger?.info('[NobleBLE] Using service:', selectedService.uuid);
1002
937
 
1003
938
  // Step 2: Discover ALL characteristics (no filter)
1004
939
  const characteristics = await new Promise<Characteristic[]>((resolve, reject) => {
1005
- service.discoverCharacteristics([], (error, chars) => {
940
+ selectedService.discoverCharacteristics([], (error, chars) => {
1006
941
  if (error) {
1007
942
  logger?.error('[NobleBLE] Characteristic discovery failed:', error);
1008
943
  reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message));
@@ -1346,7 +1281,6 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1346
1281
  existingCharacteristics.notify.removeAllListeners('data');
1347
1282
  }
1348
1283
  notificationCallbacks.delete(deviceId);
1349
- devicePacketStates.delete(deviceId);
1350
1284
  subscribedDevices.delete(deviceId);
1351
1285
  // Continue to re-setup the connection properly
1352
1286
  }
@@ -1457,7 +1391,6 @@ async function unsubscribeNotifications(deviceId: string): Promise<void> {
1457
1391
  // Remove all listeners and clear subscription status
1458
1392
  notifyCharacteristic.removeAllListeners('data');
1459
1393
  notificationCallbacks.delete(deviceId);
1460
- devicePacketStates.delete(deviceId);
1461
1394
  subscribedDevices.delete(deviceId);
1462
1395
  } finally {
1463
1396
  // 🔒 CRITICAL: Always clear operation state (even on error)
@@ -1524,14 +1457,6 @@ async function subscribeNotifications(
1524
1457
  // Just update the callback without re-subscribing
1525
1458
  notificationCallbacks.set(deviceId, callback);
1526
1459
 
1527
- // Reset packet state for new session
1528
- devicePacketStates.set(deviceId, {
1529
- bufferLength: 0,
1530
- buffer: [],
1531
- packetCount: 0,
1532
- messageId: undefined,
1533
- });
1534
-
1535
1460
  // 🔒 Clear operation state
1536
1461
  subscriptionOperations.set(deviceId, 'idle');
1537
1462
  return Promise.resolve();
@@ -1548,14 +1473,6 @@ async function subscribeNotifications(
1548
1473
  // Store callback for this device
1549
1474
  notificationCallbacks.set(deviceId, callback);
1550
1475
 
1551
- // Reset packet state for new subscription session
1552
- devicePacketStates.set(deviceId, {
1553
- bufferLength: 0,
1554
- buffer: [],
1555
- packetCount: 0,
1556
- messageId: undefined,
1557
- });
1558
-
1559
1476
  // Helper: rebuild a clean application-layer subscription
1560
1477
  async function rebuildAppSubscription(
1561
1478
  deviceId: string,
@@ -1584,15 +1501,7 @@ async function subscribeNotifications(
1584
1501
  logger?.info('[NobleBLE] Device paired successfully', { deviceId });
1585
1502
  }
1586
1503
 
1587
- const result = processNotificationData(deviceId, data);
1588
- if (result.error) {
1589
- logger?.error('[NobleBLE] Packet processing error:', result.error);
1590
- return;
1591
- }
1592
- if (result.isComplete && result.completePacket) {
1593
- const appCb = notificationCallbacks.get(deviceId);
1594
- if (appCb) appCb(result.completePacket);
1595
- }
1504
+ emitRawNotification(deviceId, data);
1596
1505
  });
1597
1506
  }
1598
1507