@onekeyfe/hd-transport-electron 1.1.34-alpha.3 → 1.1.34-alpha.4

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,7 +7,12 @@ export interface SoftRefreshParams {
7
7
  subscriptionOperations: Map<string, 'subscribing' | 'unsubscribing' | 'idle'>;
8
8
  subscribedDevices: Map<string, boolean>;
9
9
  pairedDevices: Set<string>;
10
- onNotificationData: (deviceId: string, data: Buffer) => void;
10
+ notificationCallbacks: Map<string, (hex: string) => void>;
11
+ processNotificationData: (deviceId: string, data: Buffer) => {
12
+ isComplete: boolean;
13
+ completePacket?: string;
14
+ error?: string;
15
+ };
11
16
  logger: Logger | null;
12
17
  }
13
18
  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,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"}
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"}
@@ -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-16791965.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-2e41f27c.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.d.ts CHANGED
@@ -26,10 +26,6 @@ interface Logger {
26
26
  error(message: string, ...args: any[]): void;
27
27
  }
28
28
 
29
- /**
30
- * Desktop API types for Electron preload script
31
- * These types define the core interface for Noble BLE communication
32
- */
33
29
  interface NobleBleAPI {
34
30
  enumerate: () => Promise<{
35
31
  id: string;
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-55699e30.js');
5
+ var index = require('./index-de36348d.js');
6
6
 
7
7
 
8
8
 
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-55699e30.js');
3
+ var index = require('./index-de36348d.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
+ var hdTransport = require('@onekeyfe/hd-transport');
5
6
  var pRetry = require('p-retry');
6
7
 
7
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -19,7 +20,7 @@ function safeLog(logger, level, message, ...args) {
19
20
 
20
21
  function softRefreshSubscription(params) {
21
22
  return index.__awaiter(this, void 0, void 0, function* () {
22
- const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, onNotificationData, logger, } = params;
23
+ const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, notificationCallbacks, processNotificationData, logger, } = params;
23
24
  if (!notifyCharacteristic) {
24
25
  throw new Error(`Notify characteristic not available for device ${deviceId}`);
25
26
  }
@@ -43,7 +44,16 @@ function softRefreshSubscription(params) {
43
44
  pairedDevices.add(deviceId);
44
45
  logger === null || logger === void 0 ? void 0 : logger.info('[BLE-OPS] Device paired successfully', { deviceId });
45
46
  }
46
- onNotificationData(deviceId, data);
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
+ }
47
57
  });
48
58
  subscribedDevices.set(deviceId, true);
49
59
  subscriptionOperations.set(deviceId, 'idle');
@@ -66,15 +76,15 @@ const deviceCharacteristics = new Map();
66
76
  const notificationCallbacks = new Map();
67
77
  const subscribedDevices = new Map();
68
78
  const subscriptionOperations = new Map();
79
+ const devicePacketStates = new Map();
69
80
  const ONEKEY_SERVICE_UUIDS = [hdShared.ONEKEY_SERVICE_UUID];
70
- const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
71
81
  const NORMALIZED_WRITE_UUID = '0002';
72
82
  const NORMALIZED_NOTIFY_UUID = '0003';
73
83
  const BLUETOOTH_INIT_TIMEOUT = 10000;
74
- const DEVICE_SCAN_TIMEOUT = 8000;
75
- const FAST_SCAN_TIMEOUT = 8000;
84
+ const DEVICE_SCAN_TIMEOUT = 5000;
85
+ const FAST_SCAN_TIMEOUT = 1500;
76
86
  const DEVICE_CHECK_INTERVAL = 500;
77
- const CONNECTION_TIMEOUT = 8000;
87
+ const CONNECTION_TIMEOUT = 3000;
78
88
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
79
89
  const BLE_PACKET_SIZE = 192;
80
90
  const UNIFIED_WRITE_DELAY = 5;
@@ -83,35 +93,70 @@ const IS_WINDOWS = process.platform === 'win32';
83
93
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
84
94
  /status:\s*3/i,
85
95
  ];
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));
96
+ const MIN_HEADER_LENGTH = 9;
97
+ function processNotificationData(deviceId, data) {
98
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification', {
99
+ deviceId,
100
+ dataLength: data.length,
104
101
  });
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
+ }
105
154
  }
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
- const appCb = notificationCallbacks.get(deviceId);
113
- if (appCb)
114
- appCb(data.toString('hex'));
155
+ function resetPacketState(packetState) {
156
+ packetState.bufferLength = 0;
157
+ packetState.buffer = [];
158
+ packetState.packetCount = 0;
159
+ packetState.messageId = undefined;
115
160
  }
116
161
  function checkBluetoothAvailability() {
117
162
  return index.__awaiter(this, void 0, void 0, function* () {
@@ -156,6 +201,7 @@ function setupPersistentStateListener() {
156
201
  deviceCharacteristics.clear();
157
202
  subscribedDevices.clear();
158
203
  notificationCallbacks.clear();
204
+ devicePacketStates.clear();
159
205
  subscriptionOperations.clear();
160
206
  pairedDevices.clear();
161
207
  if (noble) {
@@ -275,6 +321,7 @@ function cleanupDevice(deviceId, webContents, options = {}) {
275
321
  connectedDevices.delete(deviceId);
276
322
  deviceCharacteristics.delete(deviceId);
277
323
  notificationCallbacks.delete(deviceId);
324
+ devicePacketStates.delete(deviceId);
278
325
  subscribedDevices.delete(deviceId);
279
326
  subscriptionOperations.delete(deviceId);
280
327
  pairedDevices.delete(deviceId);
@@ -389,7 +436,8 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
389
436
  subscriptionOperations,
390
437
  subscribedDevices,
391
438
  pairedDevices,
392
- onNotificationData: emitRawNotification,
439
+ notificationCallbacks,
440
+ processNotificationData,
393
441
  logger,
394
442
  });
395
443
  logger === null || logger === void 0 ? void 0 : logger.info('[BLE-Write] Subscription refresh completed', { deviceId });
@@ -410,6 +458,11 @@ function transmitHexDataToDevice(deviceId, hexData) {
410
458
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Device ${deviceId} not connected or characteristics not available`);
411
459
  }
412
460
  const toBuffer = Buffer.from(hexData, 'hex');
461
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Writing data:', {
462
+ deviceId,
463
+ dataLength: toBuffer.length,
464
+ firstBytes: toBuffer.subarray(0, 8).toString('hex'),
465
+ });
413
466
  const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
414
467
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
415
468
  const writeCharacteristic = doGetWriteCharacteristic();
@@ -453,19 +506,13 @@ function transmitHexDataToDevice(deviceId, hexData) {
453
506
  });
454
507
  }
455
508
  function handleDeviceDiscovered(peripheral) {
456
- var _a, _b;
457
- if (!isOneKeyPeripheral(peripheral)) {
509
+ var _a;
510
+ const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
511
+ if (!hdShared.isOnekeyDevice(deviceName)) {
458
512
  return;
459
513
  }
460
- const isNewDevice = !discoveredDevices.has(peripheral.id);
514
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered OneKey device:', deviceName);
461
515
  discoveredDevices.set(peripheral.id, peripheral);
462
- if (isNewDevice) {
463
- logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] OneKey BLE device discovered', {
464
- deviceId: peripheral.id,
465
- name: ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device',
466
- serviceUUIDs: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [],
467
- });
468
- }
469
516
  }
470
517
  function ensureDiscoverListener() {
471
518
  if (!noble)
@@ -510,7 +557,7 @@ function performTargetedScan(targetDeviceId) {
510
557
  resolve(null);
511
558
  }, FAST_SCAN_TIMEOUT);
512
559
  nobleInstance.on('discover', onDiscover);
513
- nobleInstance.startScanning([], false, (error) => {
560
+ nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error) => {
514
561
  if (error) {
515
562
  clearTimeout(timeoutId);
516
563
  nobleInstance.removeListener('discover', onDiscover);
@@ -560,13 +607,11 @@ function enumerateDevices() {
560
607
  });
561
608
  };
562
609
  const timeoutId = setTimeout(() => {
563
- checkDevices();
564
610
  cleanup();
565
611
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
566
612
  resolve(devices);
567
613
  }, DEVICE_SCAN_TIMEOUT);
568
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
569
- nobleInstance.startScanning([], false, (error) => {
614
+ nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error) => {
570
615
  if (error) {
571
616
  cleanup();
572
617
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start scanning:', error);
@@ -661,7 +706,7 @@ function discoverServicesAndCharacteristics(peripheral) {
661
706
  });
662
707
  const discoveryPromise = (() => index.__awaiter(this, void 0, void 0, function* () {
663
708
  const services = yield new Promise((resolve, reject) => {
664
- peripheral.discoverServices([], (error, svc) => {
709
+ peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error, svc) => {
665
710
  if (error) {
666
711
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed:', error);
667
712
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, error.message));
@@ -672,30 +717,12 @@ function discoverServicesAndCharacteristics(peripheral) {
672
717
  });
673
718
  });
674
719
  if (!services || services.length === 0) {
675
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
676
- }
677
- logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] services discovered', {
678
- deviceId: peripheral.id,
679
- serviceUUIDs: services.map(service => service.uuid),
680
- });
681
- let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
682
- if (!service) {
683
- logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Known OneKey service UUID not found, trying first vendor service');
684
- service =
685
- services.find(s => PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(getBleUuidKey(s.uuid))) ||
686
- services.find(s => !isGenericBleService(s.uuid)) ||
687
- services[0];
720
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
688
721
  }
689
- if (!service) {
690
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
691
- }
692
- const selectedService = service;
693
- logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] service selected', {
694
- deviceId: peripheral.id,
695
- serviceUuid: selectedService.uuid,
696
- });
722
+ const service = services[0];
723
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Found service:', service.uuid);
697
724
  const characteristics = yield new Promise((resolve, reject) => {
698
- selectedService.discoverCharacteristics([], (error, chars) => {
725
+ service.discoverCharacteristics([hdShared.ONEKEY_WRITE_CHARACTERISTIC_UUID, hdShared.ONEKEY_NOTIFY_CHARACTERISTIC_UUID], (error, chars) => {
699
726
  if (error) {
700
727
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Characteristic discovery failed:', error);
701
728
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, error.message));
@@ -705,6 +732,10 @@ function discoverServicesAndCharacteristics(peripheral) {
705
732
  }
706
733
  });
707
734
  });
735
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered characteristics:', {
736
+ count: (characteristics === null || characteristics === void 0 ? void 0 : characteristics.length) || 0,
737
+ uuids: (characteristics === null || characteristics === void 0 ? void 0 : characteristics.map(c => c.uuid)) || [],
738
+ });
708
739
  let writeCharacteristic = null;
709
740
  let notifyCharacteristic = null;
710
741
  for (const characteristic of characteristics) {
@@ -717,16 +748,14 @@ function discoverServicesAndCharacteristics(peripheral) {
717
748
  notifyCharacteristic = characteristic;
718
749
  }
719
750
  }
751
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Characteristic discovery result:', {
752
+ writeFound: !!writeCharacteristic,
753
+ notifyFound: !!notifyCharacteristic,
754
+ });
720
755
  if (!writeCharacteristic || !notifyCharacteristic) {
721
756
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Missing characteristics - write:', !!writeCharacteristic, 'notify:', !!notifyCharacteristic);
722
757
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, 'Required characteristics not found');
723
758
  }
724
- logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] characteristics selected', {
725
- deviceId: peripheral.id,
726
- serviceUuid: selectedService.uuid,
727
- writeUuid: writeCharacteristic.uuid,
728
- notifyUuid: notifyCharacteristic.uuid,
729
- });
730
759
  return { write: writeCharacteristic, notify: notifyCharacteristic };
731
760
  }))();
732
761
  try {
@@ -915,6 +944,7 @@ function connectDevice(deviceId, webContents) {
915
944
  existingCharacteristics.notify.removeAllListeners('data');
916
945
  }
917
946
  notificationCallbacks.delete(deviceId);
947
+ devicePacketStates.delete(deviceId);
918
948
  subscribedDevices.delete(deviceId);
919
949
  }
920
950
  const characteristics = yield setupConnectionAndDiscoverServices(peripheral, deviceId, webContents);
@@ -997,6 +1027,7 @@ function unsubscribeNotifications(deviceId) {
997
1027
  });
998
1028
  notifyCharacteristic.removeAllListeners('data');
999
1029
  notificationCallbacks.delete(deviceId);
1030
+ devicePacketStates.delete(deviceId);
1000
1031
  subscribedDevices.delete(deviceId);
1001
1032
  }
1002
1033
  finally {
@@ -1036,6 +1067,12 @@ function subscribeNotifications(deviceId, callback) {
1036
1067
  if (subscribedDevices.get(deviceId)) {
1037
1068
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device already subscribed to characteristic, updating callback only');
1038
1069
  notificationCallbacks.set(deviceId, callback);
1070
+ devicePacketStates.set(deviceId, {
1071
+ bufferLength: 0,
1072
+ buffer: [],
1073
+ packetCount: 0,
1074
+ messageId: undefined,
1075
+ });
1039
1076
  subscriptionOperations.set(deviceId, 'idle');
1040
1077
  return Promise.resolve();
1041
1078
  }
@@ -1044,6 +1081,12 @@ function subscribeNotifications(deviceId, callback) {
1044
1081
  }
1045
1082
  notifyCharacteristic.removeAllListeners('data');
1046
1083
  notificationCallbacks.set(deviceId, callback);
1084
+ devicePacketStates.set(deviceId, {
1085
+ bufferLength: 0,
1086
+ buffer: [],
1087
+ packetCount: 0,
1088
+ messageId: undefined,
1089
+ });
1047
1090
  function rebuildAppSubscription(deviceId, notifyCharacteristic) {
1048
1091
  return index.__awaiter(this, void 0, void 0, function* () {
1049
1092
  yield new Promise(resolve => {
@@ -1065,7 +1108,16 @@ function subscribeNotifications(deviceId, callback) {
1065
1108
  pairedDevices.add(deviceId);
1066
1109
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device paired successfully', { deviceId });
1067
1110
  }
1068
- emitRawNotification(deviceId, data);
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
+ }
1069
1121
  });
1070
1122
  });
1071
1123
  }
@@ -1079,17 +1131,19 @@ function subscribeNotifications(deviceId, callback) {
1079
1131
  });
1080
1132
  }
1081
1133
  function setupNobleBleHandlers(webContents) {
1134
+ console.log('[NobleBLE] Attempting to set up Noble BLE handlers.');
1082
1135
  try {
1136
+ console.log('[NobleBLE] NOBLE_VERSION_771');
1083
1137
  logger = require('electron-log');
1138
+ console.log('[NobleBLE] electron-log loaded successfully.');
1084
1139
  const { ipcMain } = require('electron');
1140
+ console.log('[NobleBLE] electron.ipcMain loaded successfully.');
1085
1141
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
1142
+ console.log(`[NobleBLE] Registering handler for: ${hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
1086
1143
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
1087
1144
  try {
1088
1145
  const devices = yield enumerateDevices();
1089
- safeLog(logger, 'debug', 'Enumeration completed', {
1090
- count: devices.length,
1091
- devices: devices.map(device => ({ id: device.id, name: device.name })),
1092
- });
1146
+ safeLog(logger, 'info', 'Enumeration completed, devices:', devices);
1093
1147
  return devices;
1094
1148
  }
1095
1149
  catch (error) {
@@ -1114,6 +1168,7 @@ function setupNobleBleHandlers(webContents) {
1114
1168
  yield disconnectDevice(deviceId);
1115
1169
  }));
1116
1170
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_WRITE, (_event, deviceId, hexData) => index.__awaiter(this, void 0, void 0, function* () {
1171
+ logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length });
1117
1172
  yield transmitHexDataToDevice(deviceId, hexData);
1118
1173
  }));
1119
1174
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_SUBSCRIBE, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
@@ -1 +1 @@
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;AAw8ChE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAiKpE"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.1.34-alpha.3",
3
+ "version": "1.1.34-alpha.4",
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.34-alpha.3",
29
- "@onekeyfe/hd-shared": "1.1.34-alpha.3",
30
- "@onekeyfe/hd-transport": "1.1.34-alpha.3",
28
+ "@onekeyfe/hd-core": "1.1.34-alpha.4",
29
+ "@onekeyfe/hd-shared": "1.1.34-alpha.4",
30
+ "@onekeyfe/hd-transport": "1.1.34-alpha.4",
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": "3688bc8495eeb2ffd5616270d13fa29e8967623a"
39
+ "gitHead": "d546bc55410990351d34009230aefea77725d0d6"
40
40
  }
package/src/ble-ops.ts CHANGED
@@ -7,7 +7,15 @@ export interface SoftRefreshParams {
7
7
  subscriptionOperations: Map<string, 'subscribing' | 'unsubscribing' | 'idle'>;
8
8
  subscribedDevices: Map<string, boolean>;
9
9
  pairedDevices: Set<string>;
10
- onNotificationData: (deviceId: string, data: Buffer) => void;
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
+ };
11
19
  logger: Logger | null;
12
20
  }
13
21
 
@@ -18,7 +26,8 @@ export async function softRefreshSubscription(params: SoftRefreshParams): Promis
18
26
  subscriptionOperations,
19
27
  subscribedDevices,
20
28
  pairedDevices,
21
- onNotificationData,
29
+ notificationCallbacks,
30
+ processNotificationData,
22
31
  logger,
23
32
  } = params;
24
33
 
@@ -51,7 +60,15 @@ export async function softRefreshSubscription(params: SoftRefreshParams): Promis
51
60
  logger?.info('[BLE-OPS] Device paired successfully', { deviceId });
52
61
  }
53
62
 
54
- onNotificationData(deviceId, data);
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
+ }
55
72
  });
56
73
 
57
74
  subscribedDevices.set(deviceId, true);
@@ -9,10 +9,14 @@ import {
9
9
  EOneKeyBleMessageKeys,
10
10
  ERRORS,
11
11
  HardwareErrorCode,
12
+ ONEKEY_NOTIFY_CHARACTERISTIC_UUID,
12
13
  ONEKEY_SERVICE_UUID,
14
+ ONEKEY_WRITE_CHARACTERISTIC_UUID,
15
+ isHeaderChunk,
13
16
  isOnekeyDevice,
14
17
  wait,
15
18
  } from '@onekeyfe/hd-shared';
19
+ import { COMMON_HEADER_SIZE } from '@onekeyfe/hd-transport';
16
20
  import pRetry from 'p-retry';
17
21
 
18
22
  import { safeLog } from './types/noble-extended';
@@ -51,6 +55,15 @@ const subscribedDevices = new Map<string, boolean>(); // Track subscription stat
51
55
  // 🔒 Subscription operation state tracking to prevent race conditions
52
56
  const subscriptionOperations = new Map<string, 'subscribing' | 'unsubscribing' | 'idle'>();
53
57
 
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
+
54
67
  // Windows-only response watchdog state moved to utils/windows-ble-recovery
55
68
 
56
69
  // Pairing-related state removed
@@ -59,7 +72,6 @@ const subscriptionOperations = new Map<string, 'subscribing' | 'unsubscribing' |
59
72
 
60
73
  // Service UUIDs to scan for - using constants from hd-shared
61
74
  const ONEKEY_SERVICE_UUIDS = [ONEKEY_SERVICE_UUID];
62
- const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
63
75
 
64
76
  // Pre-normalized characteristic identifiers for fast comparison
65
77
  const NORMALIZED_WRITE_UUID = '0002';
@@ -67,10 +79,10 @@ const NORMALIZED_NOTIFY_UUID = '0003';
67
79
 
68
80
  // Timeout and interval constants
69
81
  const BLUETOOTH_INIT_TIMEOUT = 10000; // 10 seconds for Bluetooth initialization
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)
82
+ const DEVICE_SCAN_TIMEOUT = 5000; // 5 seconds for device scanning
83
+ const FAST_SCAN_TIMEOUT = 1500; // 1.5 seconds for fast targeted scanning
72
84
  const DEVICE_CHECK_INTERVAL = 500; // 500ms interval for periodic device checks
73
- const CONNECTION_TIMEOUT = 8000; // 8 seconds for device connection (BLE reconnect after release can be slow)
85
+ const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection
74
86
  const SERVICE_DISCOVERY_TIMEOUT = 10000; // 10 seconds for service discovery
75
87
 
76
88
  // Write-related constants
@@ -82,43 +94,101 @@ const ABORTABLE_WRITE_ERROR_PATTERNS = [
82
94
  /status:\s*3/i, // Windows pairing cancelled / GATT write failed
83
95
  ];
84
96
 
85
- function getBleUuidKey(uuid?: string | null) {
86
- const normalized = (uuid ?? '').replace(/-/g, '').toLowerCase();
87
- return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
88
- }
89
-
90
- const NORMALIZED_ONEKEY_SERVICE_UUIDS = new Set([
91
- ...ONEKEY_SERVICE_UUIDS.map(uuid => getBleUuidKey(uuid)),
92
- '0001',
93
- ]);
97
+ // Validation limits
98
+ const MIN_HEADER_LENGTH = 9; // Minimum header chunk length
94
99
 
95
- function isGenericBleService(uuid?: string | null) {
96
- return ['1800', '1801', '180a', '180f'].includes(getBleUuidKey(uuid));
100
+ // Packet processing result types
101
+ interface PacketProcessResult {
102
+ isComplete: boolean;
103
+ completePacket?: string;
104
+ error?: string;
97
105
  }
98
106
 
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
+ // 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,
107
113
  });
108
- }
109
114
 
110
- function isOneKeyPeripheral(peripheral: Peripheral) {
111
- const deviceName = peripheral.advertisement?.localName || null;
112
- return isOnekeyDevice(deviceName, peripheral.id) || hasOneKeyAdvertisementService(peripheral);
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);
175
+
176
+ return { isComplete: true, completePacket: hexString };
177
+ }
178
+
179
+ return { isComplete: false };
180
+ } catch (error) {
181
+ resetPacketState(packetState);
182
+ return { isComplete: false, error: `Packet processing error: ${error}` };
183
+ }
113
184
  }
114
185
 
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
- const appCb = notificationCallbacks.get(deviceId);
121
- if (appCb) appCb(data.toString('hex'));
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;
122
192
  }
123
193
 
124
194
  // Check Bluetooth availability - returns detailed state
@@ -180,6 +250,7 @@ function setupPersistentStateListener(): void {
180
250
  deviceCharacteristics.clear();
181
251
  subscribedDevices.clear();
182
252
  notificationCallbacks.clear();
253
+ devicePacketStates.clear();
183
254
  subscriptionOperations.clear();
184
255
  pairedDevices.clear();
185
256
 
@@ -357,6 +428,7 @@ function cleanupDevice(
357
428
  connectedDevices.delete(deviceId);
358
429
  deviceCharacteristics.delete(deviceId);
359
430
  notificationCallbacks.delete(deviceId);
431
+ devicePacketStates.delete(deviceId);
360
432
  subscribedDevices.delete(deviceId);
361
433
  subscriptionOperations.delete(deviceId);
362
434
  pairedDevices.delete(deviceId);
@@ -518,7 +590,8 @@ async function attemptWindowsWriteUntilPaired(
518
590
  subscriptionOperations,
519
591
  subscribedDevices,
520
592
  pairedDevices,
521
- onNotificationData: emitRawNotification,
593
+ notificationCallbacks,
594
+ processNotificationData,
522
595
  logger,
523
596
  });
524
597
  logger?.info('[BLE-Write] Subscription refresh completed', { deviceId });
@@ -545,6 +618,12 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
545
618
  }
546
619
 
547
620
  const toBuffer = Buffer.from(hexData, 'hex');
621
+ logger?.info('[NobleBLE] Writing data:', {
622
+ deviceId,
623
+ dataLength: toBuffer.length,
624
+ firstBytes: toBuffer.subarray(0, 8).toString('hex'),
625
+ });
626
+
548
627
  const doGetWriteCharacteristic = () => deviceCharacteristics.get(deviceId)?.write;
549
628
 
550
629
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
@@ -606,21 +685,15 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
606
685
 
607
686
  // Handle discovered device (for general enumeration only)
608
687
  function handleDeviceDiscovered(peripheral: Peripheral): void {
609
- // Only process OneKey candidates for general discovery. Avoid logging every
610
- // ambient BLE peripheral; it makes Pro2 debugging hard to read.
611
- if (!isOneKeyPeripheral(peripheral)) {
688
+ const deviceName = peripheral.advertisement?.localName || 'Unknown Device';
689
+
690
+ // Only process OneKey devices for general discovery
691
+ if (!isOnekeyDevice(deviceName)) {
612
692
  return;
613
693
  }
614
694
 
615
- const isNewDevice = !discoveredDevices.has(peripheral.id);
695
+ logger?.info('[NobleBLE] Discovered OneKey device:', deviceName);
616
696
  discoveredDevices.set(peripheral.id, peripheral);
617
- if (isNewDevice) {
618
- logger?.debug('[NobleBLE] OneKey BLE device discovered', {
619
- deviceId: peripheral.id,
620
- name: peripheral.advertisement?.localName || 'Unknown Device',
621
- serviceUUIDs: peripheral.advertisement?.serviceUuids || [],
622
- });
623
- }
624
697
  }
625
698
 
626
699
  // Ensure discover listener is properly set up
@@ -680,8 +753,8 @@ async function performTargetedScan(targetDeviceId: string): Promise<Peripheral |
680
753
  // Add local listener for this scan
681
754
  nobleInstance.on('discover', onDiscover);
682
755
 
683
- // Start scanning — no service UUID filter (Pro2 may use different service UUID)
684
- nobleInstance.startScanning([], false, (error?: Error) => {
756
+ // Start scanning
757
+ nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => {
685
758
  if (error) {
686
759
  clearTimeout(timeoutId);
687
760
  nobleInstance.removeListener('discover', onDiscover);
@@ -743,19 +816,15 @@ async function enumerateDevices(): Promise<DeviceInfo[]> {
743
816
  });
744
817
  };
745
818
 
746
- // Set timeout for scanning — use longer timeout to catch slow-advertising devices like Pro2
819
+ // Set timeout for scanning
747
820
  const timeoutId = setTimeout(() => {
748
- // Final collection before resolving — catches devices discovered near the deadline
749
- checkDevices();
750
821
  cleanup();
751
822
  logger?.info('[NobleBLE] Scan completed, found devices:', devices.length);
752
823
  resolve(devices);
753
824
  }, DEVICE_SCAN_TIMEOUT);
754
825
 
755
- // Start scanning without a service UUID filter so Pro2 advertisements with
756
- // short vendor UUIDs can be found, but only OneKey candidates are logged/returned.
757
- logger?.info('[NobleBLE] Scanning for OneKey BLE devices');
758
- nobleInstance.startScanning([], false, (error?: Error) => {
826
+ // Start scanning for OneKey service UUIDs
827
+ nobleInstance.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => {
759
828
  if (error) {
760
829
  cleanup();
761
830
  logger?.error('[NobleBLE] Failed to start scanning:', error);
@@ -883,9 +952,9 @@ async function discoverServicesAndCharacteristics(
883
952
 
884
953
  // Main discovery logic as async function
885
954
  const discoveryPromise = (async (): Promise<CharacteristicPair> => {
886
- // Step 1: Discover ALL services (no filter — Pro2 may use different service UUID)
955
+ // Step 1: Discover services (promisified)
887
956
  const services = await new Promise<Service[]>((resolve, reject) => {
888
- peripheral.discoverServices([], (error, svc) => {
957
+ peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error, svc) => {
889
958
  if (error) {
890
959
  logger?.error('[NobleBLE] Service discovery failed:', error);
891
960
  reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message));
@@ -896,44 +965,33 @@ async function discoverServicesAndCharacteristics(
896
965
  });
897
966
 
898
967
  if (!services || services.length === 0) {
899
- throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No services found');
968
+ throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
900
969
  }
901
970
 
902
- logger?.debug('[NobleBLE] services discovered', {
903
- deviceId: peripheral.id,
904
- serviceUUIDs: services.map(service => service.uuid),
905
- });
971
+ const service = services[0];
972
+ logger?.info('[NobleBLE] Found service:', service.uuid);
906
973
 
907
- // Find OneKey service Noble may expose 128-bit UUIDs as short UUID keys.
908
- let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
909
- if (!service) {
910
- logger?.info('[NobleBLE] Known OneKey service UUID not found, trying first vendor service');
911
- service =
912
- services.find(s => PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(getBleUuidKey(s.uuid))) ||
913
- services.find(s => !isGenericBleService(s.uuid)) ||
914
- services[0];
915
- }
916
- if (!service) {
917
- throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
918
- }
919
- const selectedService = service;
920
- logger?.debug('[NobleBLE] service selected', {
921
- deviceId: peripheral.id,
922
- serviceUuid: selectedService.uuid,
923
- });
924
- // Step 2: Discover ALL characteristics (no filter)
974
+ // Step 2: Discover characteristics (promisified)
925
975
  const characteristics = await new Promise<Characteristic[]>((resolve, reject) => {
926
- selectedService.discoverCharacteristics([], (error, chars) => {
927
- if (error) {
928
- logger?.error('[NobleBLE] Characteristic discovery failed:', error);
929
- reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message));
930
- } else {
931
- resolve(chars);
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
+ }
932
985
  }
933
- });
986
+ );
934
987
  });
935
988
 
936
989
  // Step 3: Find required characteristics
990
+ logger?.info('[NobleBLE] Discovered characteristics:', {
991
+ count: characteristics?.length || 0,
992
+ uuids: characteristics?.map(c => c.uuid) || [],
993
+ });
994
+
937
995
  let writeCharacteristic: Characteristic | null = null;
938
996
  let notifyCharacteristic: Characteristic | null = null;
939
997
 
@@ -948,6 +1006,11 @@ async function discoverServicesAndCharacteristics(
948
1006
  }
949
1007
  }
950
1008
 
1009
+ logger?.info('[NobleBLE] Characteristic discovery result:', {
1010
+ writeFound: !!writeCharacteristic,
1011
+ notifyFound: !!notifyCharacteristic,
1012
+ });
1013
+
951
1014
  if (!writeCharacteristic || !notifyCharacteristic) {
952
1015
  logger?.error(
953
1016
  '[NobleBLE] Missing characteristics - write:',
@@ -961,13 +1024,6 @@ async function discoverServicesAndCharacteristics(
961
1024
  );
962
1025
  }
963
1026
 
964
- logger?.debug('[NobleBLE] characteristics selected', {
965
- deviceId: peripheral.id,
966
- serviceUuid: selectedService.uuid,
967
- writeUuid: writeCharacteristic.uuid,
968
- notifyUuid: notifyCharacteristic.uuid,
969
- });
970
-
971
1027
  return { write: writeCharacteristic, notify: notifyCharacteristic };
972
1028
  })();
973
1029
 
@@ -1264,6 +1320,7 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1264
1320
  existingCharacteristics.notify.removeAllListeners('data');
1265
1321
  }
1266
1322
  notificationCallbacks.delete(deviceId);
1323
+ devicePacketStates.delete(deviceId);
1267
1324
  subscribedDevices.delete(deviceId);
1268
1325
  // Continue to re-setup the connection properly
1269
1326
  }
@@ -1374,6 +1431,7 @@ async function unsubscribeNotifications(deviceId: string): Promise<void> {
1374
1431
  // Remove all listeners and clear subscription status
1375
1432
  notifyCharacteristic.removeAllListeners('data');
1376
1433
  notificationCallbacks.delete(deviceId);
1434
+ devicePacketStates.delete(deviceId);
1377
1435
  subscribedDevices.delete(deviceId);
1378
1436
  } finally {
1379
1437
  // 🔒 CRITICAL: Always clear operation state (even on error)
@@ -1440,6 +1498,14 @@ async function subscribeNotifications(
1440
1498
  // Just update the callback without re-subscribing
1441
1499
  notificationCallbacks.set(deviceId, callback);
1442
1500
 
1501
+ // Reset packet state for new session
1502
+ devicePacketStates.set(deviceId, {
1503
+ bufferLength: 0,
1504
+ buffer: [],
1505
+ packetCount: 0,
1506
+ messageId: undefined,
1507
+ });
1508
+
1443
1509
  // 🔒 Clear operation state
1444
1510
  subscriptionOperations.set(deviceId, 'idle');
1445
1511
  return Promise.resolve();
@@ -1456,6 +1522,14 @@ async function subscribeNotifications(
1456
1522
  // Store callback for this device
1457
1523
  notificationCallbacks.set(deviceId, callback);
1458
1524
 
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
+
1459
1533
  // Helper: rebuild a clean application-layer subscription
1460
1534
  async function rebuildAppSubscription(
1461
1535
  deviceId: string,
@@ -1484,7 +1558,15 @@ async function subscribeNotifications(
1484
1558
  logger?.info('[NobleBLE] Device paired successfully', { deviceId });
1485
1559
  }
1486
1560
 
1487
- emitRawNotification(deviceId, data);
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
+ }
1488
1570
  });
1489
1571
  }
1490
1572
 
@@ -1499,25 +1581,29 @@ async function subscribeNotifications(
1499
1581
 
1500
1582
  // Setup IPC handlers
1501
1583
  export function setupNobleBleHandlers(webContents: WebContents): void {
1584
+ // Use console.log for initial logging as electron-log might not be available yet.
1585
+ console.log('[NobleBLE] Attempting to set up Noble BLE handlers.');
1502
1586
  try {
1587
+ console.log('[NobleBLE] NOBLE_VERSION_771');
1588
+
1503
1589
  // @ts-ignore – electron-log is only available at runtime
1504
1590
  // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
1505
1591
  logger = require('electron-log') as Logger;
1592
+ console.log('[NobleBLE] electron-log loaded successfully.');
1506
1593
 
1507
1594
  // @ts-ignore – electron is only available at runtime
1508
1595
  // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
1509
1596
  const { ipcMain } = require('electron');
1597
+ console.log('[NobleBLE] electron.ipcMain loaded successfully.');
1510
1598
 
1511
1599
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
1512
1600
 
1513
1601
  // Handle enumerate request
1602
+ console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
1514
1603
  ipcMain.handle(EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, async () => {
1515
1604
  try {
1516
1605
  const devices = await enumerateDevices();
1517
- safeLog(logger, 'debug', 'Enumeration completed', {
1518
- count: devices.length,
1519
- devices: devices.map(device => ({ id: device.id, name: device.name })),
1520
- });
1606
+ safeLog(logger, 'info', 'Enumeration completed, devices:', devices);
1521
1607
  return devices;
1522
1608
  } catch (error) {
1523
1609
  safeLog(logger, 'error', 'Enumeration failed:', error);
@@ -1562,6 +1648,7 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
1562
1648
  ipcMain.handle(
1563
1649
  EOneKeyBleMessageKeys.NOBLE_BLE_WRITE,
1564
1650
  async (_event: IpcMainInvokeEvent, deviceId: string, hexData: string) => {
1651
+ logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length });
1565
1652
  await transmitHexDataToDevice(deviceId, hexData);
1566
1653
  }
1567
1654
  );