@onekeyfe/hd-transport-electron 1.2.0-alpha.4 → 1.2.0-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 +6 -0
- package/dist/ble-ops.d.ts.map +1 -1
- package/dist/{index-02fc07fe.js → index-5f728113.js} +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-94c42077.js → noble-ble-handler-d58af248.js} +173 -177
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/ble-ops.test.ts +21 -0
- package/src/ble-ops.ts +40 -10
- package/src/noble-ble-handler.ts +157 -210
package/dist/ble-ops.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Characteristic } from '@stoprocent/noble';
|
|
3
3
|
import type { Logger } from './types/noble-extended';
|
|
4
|
+
type BleCallback = (error?: Error) => void;
|
|
5
|
+
export declare function runBleCallbackOperation(operation: (callback: BleCallback) => void, options: {
|
|
6
|
+
timeoutMs: number;
|
|
7
|
+
timeoutBehavior: 'resolve' | 'reject';
|
|
8
|
+
}): Promise<void>;
|
|
4
9
|
export interface SoftRefreshParams {
|
|
5
10
|
deviceId: string;
|
|
6
11
|
notifyCharacteristic: Characteristic | null | undefined;
|
|
@@ -11,4 +16,5 @@ export interface SoftRefreshParams {
|
|
|
11
16
|
logger: Logger | null;
|
|
12
17
|
}
|
|
13
18
|
export declare function softRefreshSubscription(params: SoftRefreshParams): Promise<void>;
|
|
19
|
+
export {};
|
|
14
20
|
//# sourceMappingURL=ble-ops.d.ts.map
|
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,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,
|
|
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,KAAK,WAAW,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;AAE3C,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,CAAC,QAAQ,EAAE,WAAW,KAAK,IAAI,EAC1C,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,GACpE,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED,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,CA0CtF"}
|
|
@@ -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-d58af248.js'); });
|
|
36
36
|
setupNobleBleHandlers(webContents);
|
|
37
37
|
});
|
|
38
38
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ 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
|
+
*/
|
|
29
33
|
interface NobleBleAPI {
|
|
30
34
|
enumerate: () => Promise<{
|
|
31
35
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-5f728113.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -17,6 +17,37 @@ function safeLog(logger, level, message, ...args) {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function runBleCallbackOperation(operation, options) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
let settled = false;
|
|
23
|
+
const settle = (error) => {
|
|
24
|
+
if (settled)
|
|
25
|
+
return;
|
|
26
|
+
settled = true;
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
if (error) {
|
|
29
|
+
reject(error);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
resolve();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const timeout = setTimeout(() => {
|
|
36
|
+
if (options.timeoutBehavior === 'reject') {
|
|
37
|
+
settle(new Error(`BLE operation timed out after ${options.timeoutMs}ms`));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
settle();
|
|
41
|
+
}
|
|
42
|
+
}, options.timeoutMs);
|
|
43
|
+
try {
|
|
44
|
+
operation(settle);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
settle(error instanceof Error ? error : new Error(String(error)));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
20
51
|
function softRefreshSubscription(params) {
|
|
21
52
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
22
53
|
const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, onNotificationData, logger, } = params;
|
|
@@ -25,17 +56,13 @@ function softRefreshSubscription(params) {
|
|
|
25
56
|
}
|
|
26
57
|
logger === null || logger === void 0 ? void 0 : logger.info('[BLE-OPS] Starting subscription refresh', { deviceId });
|
|
27
58
|
subscriptionOperations.set(deviceId, 'subscribing');
|
|
28
|
-
yield
|
|
29
|
-
|
|
59
|
+
yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
|
|
60
|
+
timeoutMs: 250,
|
|
61
|
+
timeoutBehavior: 'resolve',
|
|
30
62
|
});
|
|
31
|
-
yield
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
reject(error);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
resolve();
|
|
38
|
-
});
|
|
63
|
+
yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
|
|
64
|
+
timeoutMs: 8000,
|
|
65
|
+
timeoutBehavior: 'reject',
|
|
39
66
|
});
|
|
40
67
|
notifyCharacteristic.removeAllListeners('data');
|
|
41
68
|
notifyCharacteristic.on('data', (data) => {
|
|
@@ -59,6 +86,7 @@ const bluetoothState = {
|
|
|
59
86
|
initialized: false,
|
|
60
87
|
};
|
|
61
88
|
let persistentStateListener = null;
|
|
89
|
+
let persistentDiscoverListener = null;
|
|
62
90
|
const discoveredDevices = new Map();
|
|
63
91
|
const connectedDevices = new Map();
|
|
64
92
|
const pairedDevices = new Set();
|
|
@@ -66,16 +94,18 @@ const deviceCharacteristics = new Map();
|
|
|
66
94
|
const notificationCallbacks = new Map();
|
|
67
95
|
const subscribedDevices = new Map();
|
|
68
96
|
const subscriptionOperations = new Map();
|
|
97
|
+
const deviceDisconnectListeners = new Map();
|
|
69
98
|
const ONEKEY_SERVICE_UUIDS = [hdShared.ONEKEY_SERVICE_UUID];
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
99
|
+
const ONEKEY_SERVICE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_SERVICE_UUID);
|
|
100
|
+
const ONEKEY_WRITE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_WRITE_CHARACTERISTIC_UUID);
|
|
101
|
+
const ONEKEY_NOTIFY_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_NOTIFY_CHARACTERISTIC_UUID);
|
|
73
102
|
const BLUETOOTH_INIT_TIMEOUT = 10000;
|
|
74
103
|
const DEVICE_SCAN_TIMEOUT = 8000;
|
|
75
104
|
const FAST_SCAN_TIMEOUT = 8000;
|
|
76
105
|
const DEVICE_CHECK_INTERVAL = 500;
|
|
77
106
|
const CONNECTION_TIMEOUT = 8000;
|
|
78
107
|
const SERVICE_DISCOVERY_TIMEOUT = 10000;
|
|
108
|
+
const BLE_CLEANUP_TIMEOUT = 250;
|
|
79
109
|
const BLE_PACKET_SIZE = 192;
|
|
80
110
|
const UNIFIED_WRITE_DELAY = 5;
|
|
81
111
|
const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 };
|
|
@@ -83,37 +113,17 @@ const IS_WINDOWS = process.platform === 'win32';
|
|
|
83
113
|
const ABORTABLE_WRITE_ERROR_PATTERNS = [
|
|
84
114
|
/status:\s*3/i,
|
|
85
115
|
];
|
|
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
116
|
function isOneKeyPeripheral(peripheral) {
|
|
107
|
-
var _a;
|
|
108
|
-
const
|
|
109
|
-
return hdShared.
|
|
117
|
+
var _a, _b;
|
|
118
|
+
const serviceUuids = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids;
|
|
119
|
+
return (hdShared.hasOnekeyCommunicationService(serviceUuids) &&
|
|
120
|
+
hdShared.isOnekeyBluetoothDevice({
|
|
121
|
+
id: peripheral.id,
|
|
122
|
+
localName: (_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName,
|
|
123
|
+
serviceUuids,
|
|
124
|
+
}));
|
|
110
125
|
}
|
|
111
126
|
function emitRawNotification(deviceId, data) {
|
|
112
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Raw notification', {
|
|
113
|
-
deviceId,
|
|
114
|
-
dataLength: data.length,
|
|
115
|
-
firstBytes: data.subarray(0, 8).toString('hex'),
|
|
116
|
-
});
|
|
117
127
|
const appCb = notificationCallbacks.get(deviceId);
|
|
118
128
|
if (appCb)
|
|
119
129
|
appCb(data.toString('hex'));
|
|
@@ -250,9 +260,12 @@ function initializeNoble() {
|
|
|
250
260
|
};
|
|
251
261
|
noble.on('stateChange', onStateChange);
|
|
252
262
|
});
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
if (!persistentDiscoverListener) {
|
|
264
|
+
persistentDiscoverListener = (peripheral) => {
|
|
265
|
+
handleDeviceDiscovered(peripheral);
|
|
266
|
+
};
|
|
267
|
+
noble.on('discover', persistentDiscoverListener);
|
|
268
|
+
}
|
|
256
269
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble initialized successfully');
|
|
257
270
|
}
|
|
258
271
|
catch (error) {
|
|
@@ -277,6 +290,11 @@ function cleanupDevice(deviceId, webContents, options = {}) {
|
|
|
277
290
|
const peripheral = connectedDevices.get(deviceId);
|
|
278
291
|
const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
279
292
|
if (cleanupConnection) {
|
|
293
|
+
const disconnectEntry = deviceDisconnectListeners.get(deviceId);
|
|
294
|
+
if (disconnectEntry) {
|
|
295
|
+
disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
|
|
296
|
+
deviceDisconnectListeners.delete(deviceId);
|
|
297
|
+
}
|
|
280
298
|
connectedDevices.delete(deviceId);
|
|
281
299
|
deviceCharacteristics.delete(deviceId);
|
|
282
300
|
notificationCallbacks.delete(deviceId);
|
|
@@ -311,10 +329,15 @@ function handleDeviceDisconnect(deviceId, webContents) {
|
|
|
311
329
|
});
|
|
312
330
|
}
|
|
313
331
|
function setupDisconnectListener(peripheral, deviceId, webContents) {
|
|
314
|
-
|
|
315
|
-
|
|
332
|
+
const existing = deviceDisconnectListeners.get(deviceId);
|
|
333
|
+
if (existing) {
|
|
334
|
+
existing.peripheral.removeListener('disconnect', existing.listener);
|
|
335
|
+
}
|
|
336
|
+
const listener = () => {
|
|
316
337
|
handleDeviceDisconnect(deviceId, webContents);
|
|
317
|
-
}
|
|
338
|
+
};
|
|
339
|
+
deviceDisconnectListeners.set(deviceId, { peripheral, listener });
|
|
340
|
+
peripheral.on('disconnect', listener);
|
|
318
341
|
}
|
|
319
342
|
function writeCharacteristicWithAck(deviceId, writeCharacteristic, buffer) {
|
|
320
343
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -415,11 +438,6 @@ function transmitHexDataToDevice(deviceId, hexData) {
|
|
|
415
438
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Device ${deviceId} not connected or characteristics not available`);
|
|
416
439
|
}
|
|
417
440
|
const toBuffer = Buffer.from(hexData, 'hex');
|
|
418
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Writing data:', {
|
|
419
|
-
deviceId,
|
|
420
|
-
dataLength: toBuffer.length,
|
|
421
|
-
firstBytes: toBuffer.subarray(0, 8).toString('hex'),
|
|
422
|
-
});
|
|
423
441
|
const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
|
|
424
442
|
if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
|
|
425
443
|
const writeCharacteristic = doGetWriteCharacteristic();
|
|
@@ -464,33 +482,31 @@ function transmitHexDataToDevice(deviceId, hexData) {
|
|
|
464
482
|
}
|
|
465
483
|
function handleDeviceDiscovered(peripheral) {
|
|
466
484
|
var _a, _b;
|
|
467
|
-
const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
468
|
-
const serviceUuids = ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [];
|
|
469
485
|
if (!isOneKeyPeripheral(peripheral)) {
|
|
470
486
|
return;
|
|
471
487
|
}
|
|
472
488
|
const isNewDevice = !discoveredDevices.has(peripheral.id);
|
|
473
489
|
discoveredDevices.set(peripheral.id, peripheral);
|
|
474
490
|
if (isNewDevice) {
|
|
475
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
serviceUUIDs: serviceUuids,
|
|
491
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] OneKey BLE device discovered', {
|
|
492
|
+
deviceId: peripheral.id,
|
|
493
|
+
name: ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device',
|
|
494
|
+
serviceUUIDs: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [],
|
|
479
495
|
});
|
|
480
496
|
}
|
|
481
497
|
}
|
|
482
498
|
function ensureDiscoverListener() {
|
|
483
499
|
if (!noble)
|
|
484
500
|
return;
|
|
485
|
-
|
|
486
|
-
if (listenerCount === 0) {
|
|
501
|
+
if (!persistentDiscoverListener) {
|
|
487
502
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discover listener missing, re-adding it');
|
|
488
|
-
|
|
503
|
+
persistentDiscoverListener = (peripheral) => {
|
|
489
504
|
handleDeviceDiscovered(peripheral);
|
|
490
|
-
}
|
|
505
|
+
};
|
|
506
|
+
noble.on('discover', persistentDiscoverListener);
|
|
491
507
|
}
|
|
492
508
|
else {
|
|
493
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] Discover listener already
|
|
509
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] Discover listener already registered');
|
|
494
510
|
}
|
|
495
511
|
}
|
|
496
512
|
function performTargetedScan(targetDeviceId) {
|
|
@@ -503,7 +519,7 @@ function performTargetedScan(targetDeviceId) {
|
|
|
503
519
|
return new Promise((resolve, reject) => {
|
|
504
520
|
const onDiscover = (peripheral) => {
|
|
505
521
|
var _a;
|
|
506
|
-
if (peripheral.id === targetDeviceId) {
|
|
522
|
+
if (peripheral.id === targetDeviceId && isOneKeyPeripheral(peripheral)) {
|
|
507
523
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Target device found during targeted scan:', {
|
|
508
524
|
id: peripheral.id,
|
|
509
525
|
name: (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName,
|
|
@@ -595,25 +611,23 @@ function stopScanning() {
|
|
|
595
611
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
596
612
|
if (!noble)
|
|
597
613
|
return;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
noble.stopScanning(() => {
|
|
604
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning stopped');
|
|
605
|
-
resolve();
|
|
606
|
-
});
|
|
614
|
+
const nobleInstance = noble;
|
|
615
|
+
yield runBleCallbackOperation(callback => nobleInstance.stopScanning(() => callback()), {
|
|
616
|
+
timeoutMs: BLE_CLEANUP_TIMEOUT,
|
|
617
|
+
timeoutBehavior: 'resolve',
|
|
607
618
|
});
|
|
619
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning stopped');
|
|
608
620
|
});
|
|
609
621
|
}
|
|
610
622
|
function cleanupNobleListeners() {
|
|
611
623
|
if (!noble)
|
|
612
624
|
return;
|
|
613
625
|
try {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
626
|
+
if (persistentDiscoverListener) {
|
|
627
|
+
noble.removeListener('discover', persistentDiscoverListener);
|
|
628
|
+
persistentDiscoverListener = null;
|
|
629
|
+
}
|
|
630
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Owned Noble listeners cleaned up');
|
|
617
631
|
}
|
|
618
632
|
catch (error) {
|
|
619
633
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to clean up some listeners:', error);
|
|
@@ -683,23 +697,22 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
683
697
|
}
|
|
684
698
|
});
|
|
685
699
|
});
|
|
686
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] All services:', services === null || services === void 0 ? void 0 : services.map(s => s.uuid));
|
|
687
700
|
if (!services || services.length === 0) {
|
|
688
701
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
|
|
689
702
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
services.find(s => !isGenericBleService(s.uuid)) ||
|
|
696
|
-
services[0];
|
|
697
|
-
}
|
|
703
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] services discovered', {
|
|
704
|
+
deviceId: peripheral.id,
|
|
705
|
+
serviceUUIDs: services.map(service => service.uuid),
|
|
706
|
+
});
|
|
707
|
+
const service = services.find(s => hdShared.matchesKnownBleUuid(s.uuid, ONEKEY_SERVICE_UUID_ALIASES));
|
|
698
708
|
if (!service) {
|
|
699
709
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
700
710
|
}
|
|
701
711
|
const selectedService = service;
|
|
702
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
712
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] service selected', {
|
|
713
|
+
deviceId: peripheral.id,
|
|
714
|
+
serviceUuid: selectedService.uuid,
|
|
715
|
+
});
|
|
703
716
|
const characteristics = yield new Promise((resolve, reject) => {
|
|
704
717
|
selectedService.discoverCharacteristics([], (error, chars) => {
|
|
705
718
|
if (error) {
|
|
@@ -711,30 +724,26 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
711
724
|
}
|
|
712
725
|
});
|
|
713
726
|
});
|
|
714
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered characteristics:', {
|
|
715
|
-
count: (characteristics === null || characteristics === void 0 ? void 0 : characteristics.length) || 0,
|
|
716
|
-
uuids: (characteristics === null || characteristics === void 0 ? void 0 : characteristics.map(c => c.uuid)) || [],
|
|
717
|
-
});
|
|
718
727
|
let writeCharacteristic = null;
|
|
719
728
|
let notifyCharacteristic = null;
|
|
720
729
|
for (const characteristic of characteristics) {
|
|
721
|
-
|
|
722
|
-
const uuidKey = uuid.length >= 8 ? uuid.substring(4, 8) : uuid;
|
|
723
|
-
if (uuidKey === NORMALIZED_WRITE_UUID) {
|
|
730
|
+
if (hdShared.matchesKnownBleUuid(characteristic.uuid, ONEKEY_WRITE_UUID_ALIASES)) {
|
|
724
731
|
writeCharacteristic = characteristic;
|
|
725
732
|
}
|
|
726
|
-
else if (
|
|
733
|
+
else if (hdShared.matchesKnownBleUuid(characteristic.uuid, ONEKEY_NOTIFY_UUID_ALIASES)) {
|
|
727
734
|
notifyCharacteristic = characteristic;
|
|
728
735
|
}
|
|
729
736
|
}
|
|
730
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Characteristic discovery result:', {
|
|
731
|
-
writeFound: !!writeCharacteristic,
|
|
732
|
-
notifyFound: !!notifyCharacteristic,
|
|
733
|
-
});
|
|
734
737
|
if (!writeCharacteristic || !notifyCharacteristic) {
|
|
735
738
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Missing characteristics - write:', !!writeCharacteristic, 'notify:', !!notifyCharacteristic);
|
|
736
739
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, 'Required characteristics not found');
|
|
737
740
|
}
|
|
741
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] characteristics selected', {
|
|
742
|
+
deviceId: peripheral.id,
|
|
743
|
+
serviceUuid: selectedService.uuid,
|
|
744
|
+
writeUuid: writeCharacteristic.uuid,
|
|
745
|
+
notifyUuid: notifyCharacteristic.uuid,
|
|
746
|
+
});
|
|
738
747
|
return { write: writeCharacteristic, notify: notifyCharacteristic };
|
|
739
748
|
}))();
|
|
740
749
|
try {
|
|
@@ -755,27 +764,17 @@ function forceReconnectPeripheral(peripheral, deviceId) {
|
|
|
755
764
|
reason: 'force-reconnect',
|
|
756
765
|
});
|
|
757
766
|
if (peripheral.state === 'connected') {
|
|
758
|
-
peripheral.
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
resolve();
|
|
763
|
-
});
|
|
764
|
-
});
|
|
767
|
+
yield runBleCallbackOperation(callback => peripheral.disconnect(() => {
|
|
768
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force disconnect completed');
|
|
769
|
+
callback();
|
|
770
|
+
}), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
|
|
765
771
|
}
|
|
766
|
-
yield
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Force reconnect failed:', error);
|
|
770
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
|
|
771
|
-
}
|
|
772
|
-
else {
|
|
773
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
|
|
774
|
-
connectedDevices.set(deviceId, peripheral);
|
|
775
|
-
resolve();
|
|
776
|
-
}
|
|
777
|
-
});
|
|
772
|
+
yield runBleCallbackOperation(callback => peripheral.connect(callback), {
|
|
773
|
+
timeoutMs: CONNECTION_TIMEOUT,
|
|
774
|
+
timeoutBehavior: 'reject',
|
|
778
775
|
});
|
|
776
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
|
|
777
|
+
connectedDevices.set(deviceId, peripheral);
|
|
779
778
|
yield hdShared.wait(500);
|
|
780
779
|
});
|
|
781
780
|
}
|
|
@@ -852,13 +851,23 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
|
|
|
852
851
|
}
|
|
853
852
|
function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
|
|
854
853
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
854
|
+
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
855
|
+
try {
|
|
856
|
+
return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
|
|
857
|
+
}
|
|
858
|
+
catch (initialError) {
|
|
859
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct service discovery failed, retrying after reconnect', {
|
|
860
|
+
deviceId,
|
|
861
|
+
error: initialError,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
855
864
|
yield forceReconnectPeripheral(peripheral, deviceId);
|
|
856
865
|
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
857
866
|
try {
|
|
858
867
|
return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
|
|
859
868
|
}
|
|
860
|
-
catch (
|
|
861
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed, attempting fresh scan...',
|
|
869
|
+
catch (reconnectError) {
|
|
870
|
+
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed after reconnect, attempting fresh scan...', reconnectError);
|
|
862
871
|
return freshScanAndDiscover(deviceId, webContents);
|
|
863
872
|
}
|
|
864
873
|
});
|
|
@@ -966,17 +975,20 @@ function disconnectDevice(deviceId) {
|
|
|
966
975
|
if (!peripheral) {
|
|
967
976
|
return;
|
|
968
977
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
peripheral.disconnect
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
978
|
+
const disconnectEntry = deviceDisconnectListeners.get(deviceId);
|
|
979
|
+
if (disconnectEntry) {
|
|
980
|
+
disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
|
|
981
|
+
deviceDisconnectListeners.delete(deviceId);
|
|
982
|
+
}
|
|
983
|
+
yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
984
|
+
timeoutMs: BLE_CLEANUP_TIMEOUT,
|
|
985
|
+
timeoutBehavior: 'resolve',
|
|
986
|
+
});
|
|
987
|
+
cleanupDevice(deviceId, undefined, {
|
|
988
|
+
cleanupConnection: true,
|
|
989
|
+
sendDisconnectEvent: false,
|
|
990
|
+
cancelOperations: true,
|
|
991
|
+
reason: 'manual-disconnect',
|
|
980
992
|
});
|
|
981
993
|
});
|
|
982
994
|
}
|
|
@@ -991,18 +1003,11 @@ function unsubscribeNotifications(deviceId) {
|
|
|
991
1003
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Unsubscribing from notifications for device:', deviceId);
|
|
992
1004
|
subscriptionOperations.set(deviceId, 'unsubscribing');
|
|
993
1005
|
try {
|
|
994
|
-
yield
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Notification unsubscription failed:', error);
|
|
998
|
-
reject(error);
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification unsubscription successful');
|
|
1002
|
-
resolve();
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1006
|
+
yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
|
|
1007
|
+
timeoutMs: BLE_CLEANUP_TIMEOUT,
|
|
1008
|
+
timeoutBehavior: 'resolve',
|
|
1005
1009
|
});
|
|
1010
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification unsubscription completed');
|
|
1006
1011
|
notifyCharacteristic.removeAllListeners('data');
|
|
1007
1012
|
notificationCallbacks.delete(deviceId);
|
|
1008
1013
|
subscribedDevices.delete(deviceId);
|
|
@@ -1054,19 +1059,13 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1054
1059
|
notificationCallbacks.set(deviceId, callback);
|
|
1055
1060
|
function rebuildAppSubscription(deviceId, notifyCharacteristic) {
|
|
1056
1061
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1057
|
-
yield
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
});
|
|
1062
|
+
yield runBleCallbackOperation(callback => notifyCharacteristic.unsubscribe(callback), {
|
|
1063
|
+
timeoutMs: BLE_CLEANUP_TIMEOUT,
|
|
1064
|
+
timeoutBehavior: 'resolve',
|
|
1061
1065
|
});
|
|
1062
|
-
yield
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
reject(error);
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
resolve();
|
|
1069
|
-
});
|
|
1066
|
+
yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
|
|
1067
|
+
timeoutMs: CONNECTION_TIMEOUT,
|
|
1068
|
+
timeoutBehavior: 'reject',
|
|
1070
1069
|
});
|
|
1071
1070
|
notifyCharacteristic.on('data', (data) => {
|
|
1072
1071
|
if (!pairedDevices.has(deviceId)) {
|
|
@@ -1087,19 +1086,17 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1087
1086
|
});
|
|
1088
1087
|
}
|
|
1089
1088
|
function setupNobleBleHandlers(webContents) {
|
|
1090
|
-
console.log('[NobleBLE] Attempting to set up Noble BLE handlers.');
|
|
1091
1089
|
try {
|
|
1092
|
-
console.log('[NobleBLE] NOBLE_VERSION_771');
|
|
1093
1090
|
logger = require('electron-log');
|
|
1094
|
-
console.log('[NobleBLE] electron-log loaded successfully.');
|
|
1095
1091
|
const { ipcMain } = require('electron');
|
|
1096
|
-
console.log('[NobleBLE] electron.ipcMain loaded successfully.');
|
|
1097
1092
|
safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
|
|
1098
|
-
console.log(`[NobleBLE] Registering handler for: ${hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
|
|
1099
1093
|
ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
|
|
1100
1094
|
try {
|
|
1101
1095
|
const devices = yield enumerateDevices();
|
|
1102
|
-
safeLog(logger, '
|
|
1096
|
+
safeLog(logger, 'debug', 'Enumeration completed', {
|
|
1097
|
+
count: devices.length,
|
|
1098
|
+
devices: devices.map(device => ({ id: device.id, name: device.name })),
|
|
1099
|
+
});
|
|
1103
1100
|
return devices;
|
|
1104
1101
|
}
|
|
1105
1102
|
catch (error) {
|
|
@@ -1124,7 +1121,6 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1124
1121
|
yield disconnectDevice(deviceId);
|
|
1125
1122
|
}));
|
|
1126
1123
|
ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_WRITE, (_event, deviceId, hexData) => index.__awaiter(this, void 0, void 0, function* () {
|
|
1127
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length });
|
|
1128
1124
|
yield transmitHexDataToDevice(deviceId, hexData);
|
|
1129
1125
|
}));
|
|
1130
1126
|
ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_SUBSCRIBE, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -1169,23 +1165,23 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1169
1165
|
}));
|
|
1170
1166
|
webContents.on('destroyed', () => {
|
|
1171
1167
|
safeLog(logger, 'info', 'Cleaning up Noble BLE handlers');
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1168
|
+
(() => index.__awaiter(this, void 0, void 0, function* () {
|
|
1169
|
+
const deviceIds = Array.from(connectedDevices.keys());
|
|
1170
|
+
for (const deviceId of deviceIds) {
|
|
1171
|
+
yield unsubscribeNotifications(deviceId).catch(() => undefined);
|
|
1172
|
+
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1173
|
+
}
|
|
1174
|
+
yield stopScanning().catch(() => undefined);
|
|
1175
|
+
if (noble && persistentStateListener) {
|
|
1176
|
+
noble.removeListener('stateChange', persistentStateListener);
|
|
1177
|
+
persistentStateListener = null;
|
|
1178
|
+
}
|
|
1179
|
+
cleanupNobleListeners();
|
|
1180
|
+
discoveredDevices.clear();
|
|
1181
|
+
safeLog(logger, 'info', 'Noble BLE cleanup completed');
|
|
1182
|
+
}))().catch(error => {
|
|
1183
|
+
safeLog(logger, 'error', 'Noble BLE cleanup failed', error);
|
|
1180
1184
|
});
|
|
1181
|
-
stopScanning();
|
|
1182
|
-
if (noble && persistentStateListener) {
|
|
1183
|
-
noble.removeListener('stateChange', persistentStateListener);
|
|
1184
|
-
persistentStateListener = null;
|
|
1185
|
-
}
|
|
1186
|
-
cleanupNobleListeners();
|
|
1187
|
-
discoveredDevices.clear();
|
|
1188
|
-
safeLog(logger, 'info', 'Noble BLE cleanup completed');
|
|
1189
1185
|
});
|
|
1190
1186
|
safeLog(logger, 'info', 'Noble BLE IPC handlers setup completed');
|
|
1191
1187
|
}
|
|
@@ -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":"AAyBA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AA46ChE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAyJpE"}
|