@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 +1 -6
- package/dist/ble-ops.d.ts.map +1 -1
- package/dist/{index-de36348d.js → index-02fc07fe.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-2e41f27c.js → noble-ble-handler-94c42077.js} +72 -116
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/ble-ops.ts +3 -20
- package/src/noble-ble-handler.ts +94 -159
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
|
-
|
|
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>;
|
package/dist/ble-ops.d.ts.map
CHANGED
|
@@ -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,
|
|
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-
|
|
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
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
85
|
-
const FAST_SCAN_TIMEOUT =
|
|
74
|
+
const DEVICE_SCAN_TIMEOUT = 8000;
|
|
75
|
+
const FAST_SCAN_TIMEOUT = 8000;
|
|
86
76
|
const DEVICE_CHECK_INTERVAL = 500;
|
|
87
|
-
const CONNECTION_TIMEOUT =
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
468
|
+
const serviceUuids = ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [];
|
|
469
|
+
if (!isOneKeyPeripheral(peripheral)) {
|
|
512
470
|
return;
|
|
513
471
|
}
|
|
514
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
723
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE]
|
|
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
|
-
|
|
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
|
-
|
|
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":"
|
|
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.
|
|
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.
|
|
29
|
-
"@onekeyfe/hd-shared": "1.1.27-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport": "1.1.27-alpha.
|
|
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": "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -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 =
|
|
83
|
-
const FAST_SCAN_TIMEOUT =
|
|
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 =
|
|
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
|
-
|
|
98
|
-
const
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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
|
|
691
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
827
|
-
|
|
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 (
|
|
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(
|
|
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
|
|
920
|
+
throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No services found');
|
|
969
921
|
}
|
|
970
922
|
|
|
971
|
-
|
|
972
|
-
|
|
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 (
|
|
938
|
+
// Step 2: Discover ALL characteristics (no filter)
|
|
975
939
|
const characteristics = await new Promise<Characteristic[]>((resolve, reject) => {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
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
|
-
|
|
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
|
|