@onekeyfe/hd-transport-electron 1.2.2-alpha.100 → 1.2.2-alpha.102
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-packet-capacity.d.ts +2 -0
- package/dist/ble-packet-capacity.d.ts.map +1 -1
- package/dist/{index-b37d926c.js → index-8e6acfc1.js} +33 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/{noble-ble-handler-e8b87173.js → noble-ble-handler-3a1d131d.js} +377 -121
- package/dist/noble-ble-handler.d.ts +11 -1
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/dist/noble-ble-timeouts.d.ts +1 -1
- package/dist/noble-ble-timeouts.d.ts.map +1 -1
- package/dist/types/desktop-api.d.ts +13 -0
- package/dist/types/desktop-api.d.ts.map +1 -1
- package/dist/types/noble-extended.d.ts +2 -0
- package/dist/types/noble-extended.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/ble-packet-capacity.test.ts +8 -1
- package/src/__tests__/noble-ble-handler.test.ts +509 -18
- package/src/ble-packet-capacity.ts +14 -0
- package/src/index.ts +14 -1
- package/src/noble-ble-handler.ts +546 -149
- package/src/noble-ble-timeouts.ts +1 -1
- package/src/types/desktop-api.ts +44 -0
- package/src/types/noble-extended.ts +2 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-8e6acfc1.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -9,6 +9,15 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
9
9
|
var pRetry__default = /*#__PURE__*/_interopDefaultLegacy(pRetry);
|
|
10
10
|
|
|
11
11
|
const BLE_ATT_HEADER_BYTES = 3;
|
|
12
|
+
function resolveNobleAttMtu(reportedMtu, platform = process.platform) {
|
|
13
|
+
if (typeof reportedMtu !== 'number' || !Number.isFinite(reportedMtu) || reportedMtu <= 0) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const normalizedMtu = Math.floor(reportedMtu);
|
|
17
|
+
return platform === 'darwin' || platform === 'win32'
|
|
18
|
+
? normalizedMtu + BLE_ATT_HEADER_BYTES
|
|
19
|
+
: normalizedMtu;
|
|
20
|
+
}
|
|
12
21
|
function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
|
|
13
22
|
if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
|
|
14
23
|
return fallbackPacketCapacity;
|
|
@@ -87,10 +96,54 @@ function softRefreshSubscription(params) {
|
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
|
|
90
|
-
const
|
|
99
|
+
const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10000;
|
|
91
100
|
|
|
92
101
|
let noble = null;
|
|
93
102
|
let logger = null;
|
|
103
|
+
let disposing = false;
|
|
104
|
+
let disposePromise;
|
|
105
|
+
let removeIpcHandlers;
|
|
106
|
+
const pendingCancellations = new Set();
|
|
107
|
+
function assertBleActive() {
|
|
108
|
+
if (disposing) {
|
|
109
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, 'Noble BLE is shutting down');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function createNobleBleConnectionError(error, messagePrefix = '') {
|
|
113
|
+
const errorMessage = error.message;
|
|
114
|
+
const isInvalidMacOsBond = (error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain') ||
|
|
115
|
+
(error.nativeErrorCode === 15 && error.nativeErrorDomain === 'CBATTErrorDomain');
|
|
116
|
+
if (isInvalidMacOsBond) {
|
|
117
|
+
const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
|
|
118
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
|
|
119
|
+
nativeErrorMessage,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
|
|
123
|
+
}
|
|
124
|
+
function createNobleBleIpcErrorResponse(error) {
|
|
125
|
+
const candidate = error;
|
|
126
|
+
const errorCode = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.errorCode) === 'number' ? candidate.errorCode : hdShared.HardwareErrorCode.UnknownError;
|
|
127
|
+
const message = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.message) === 'string' ? candidate.message : String(error !== null && error !== void 0 ? error : 'Unknown error');
|
|
128
|
+
const name = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.name) === 'string' ? candidate.name : 'Error';
|
|
129
|
+
let params;
|
|
130
|
+
if ((candidate === null || candidate === void 0 ? void 0 : candidate.params) !== undefined) {
|
|
131
|
+
try {
|
|
132
|
+
const serializedParams = JSON.stringify(candidate.params);
|
|
133
|
+
params = serializedParams === undefined ? undefined : JSON.parse(serializedParams);
|
|
134
|
+
}
|
|
135
|
+
catch (_a) {
|
|
136
|
+
params = undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
type: 'NobleBleIpcError',
|
|
141
|
+
success: false,
|
|
142
|
+
error: Object.assign({ name,
|
|
143
|
+
message,
|
|
144
|
+
errorCode }, (params !== undefined ? { params } : {})),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
94
147
|
const bluetoothState = {
|
|
95
148
|
available: false,
|
|
96
149
|
unsupported: false,
|
|
@@ -100,6 +153,57 @@ let persistentStateListener = null;
|
|
|
100
153
|
let persistentDiscoverListener = null;
|
|
101
154
|
const discoveredDevices = new Map();
|
|
102
155
|
const connectedDevices = new Map();
|
|
156
|
+
const connectingDevices = new Map();
|
|
157
|
+
let nextConnectionGeneration = 0;
|
|
158
|
+
function connectPeripheralWithCancellation(peripheral, deviceId, messagePrefix = '') {
|
|
159
|
+
assertBleActive();
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
var _a;
|
|
162
|
+
const generation = ++nextConnectionGeneration;
|
|
163
|
+
let settled = false;
|
|
164
|
+
const settle = (settler) => {
|
|
165
|
+
var _a;
|
|
166
|
+
if (settled)
|
|
167
|
+
return false;
|
|
168
|
+
settled = true;
|
|
169
|
+
if (((_a = connectingDevices.get(deviceId)) === null || _a === void 0 ? void 0 : _a.generation) === generation) {
|
|
170
|
+
connectingDevices.delete(deviceId);
|
|
171
|
+
}
|
|
172
|
+
settler();
|
|
173
|
+
return true;
|
|
174
|
+
};
|
|
175
|
+
const pendingConnection = {
|
|
176
|
+
peripheral,
|
|
177
|
+
generation,
|
|
178
|
+
cancel: error => {
|
|
179
|
+
settle(() => reject(error));
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
(_a = connectingDevices
|
|
183
|
+
.get(deviceId)) === null || _a === void 0 ? void 0 : _a.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, `Device ${deviceId} connect attempt was superseded`));
|
|
184
|
+
connectingDevices.set(deviceId, pendingConnection);
|
|
185
|
+
peripheral.connect(error => {
|
|
186
|
+
if (error) {
|
|
187
|
+
settle(() => reject(createNobleBleConnectionError(error, messagePrefix)));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!settle(() => {
|
|
191
|
+
connectedDevices.set(deviceId, peripheral);
|
|
192
|
+
resolve();
|
|
193
|
+
})) {
|
|
194
|
+
const activeConnection = connectingDevices.get(deviceId);
|
|
195
|
+
if (!activeConnection || activeConnection.peripheral !== peripheral) {
|
|
196
|
+
try {
|
|
197
|
+
peripheral.removeAllListeners('disconnect');
|
|
198
|
+
peripheral.disconnect(() => undefined);
|
|
199
|
+
}
|
|
200
|
+
catch (_a) {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
103
207
|
const pairedDevices = new Set();
|
|
104
208
|
const deviceCharacteristics = new Map();
|
|
105
209
|
const notificationCallbacks = new Map();
|
|
@@ -116,7 +220,8 @@ const DEVICE_SCAN_TIMEOUT = 5000;
|
|
|
116
220
|
const DEVICE_CHECK_INTERVAL = 500;
|
|
117
221
|
const SERVICE_DISCOVERY_TIMEOUT = 10000;
|
|
118
222
|
const BLE_CLEANUP_TIMEOUT = 250;
|
|
119
|
-
const
|
|
223
|
+
const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
|
|
224
|
+
const BLE_IDLE_DISCONNECT_MS = 20000;
|
|
120
225
|
const BLE_BUSY_BACKSTOP_MS = 10 * 60000;
|
|
121
226
|
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
122
227
|
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
@@ -237,11 +342,13 @@ function updateBluetoothState(state) {
|
|
|
237
342
|
}
|
|
238
343
|
function initializeNoble() {
|
|
239
344
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
345
|
+
assertBleActive();
|
|
240
346
|
if (noble)
|
|
241
347
|
return;
|
|
242
348
|
try {
|
|
243
349
|
noble = require('@stoprocent/noble');
|
|
244
350
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble library loaded');
|
|
351
|
+
setupPersistentStateListener();
|
|
245
352
|
yield new Promise((resolve, reject) => {
|
|
246
353
|
if (!noble) {
|
|
247
354
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not initialized'));
|
|
@@ -251,11 +358,12 @@ function initializeNoble() {
|
|
|
251
358
|
resolve();
|
|
252
359
|
return;
|
|
253
360
|
}
|
|
254
|
-
setupPersistentStateListener();
|
|
255
361
|
const timeout = setTimeout(() => {
|
|
362
|
+
cleanup();
|
|
256
363
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout'));
|
|
257
364
|
}, BLUETOOTH_INIT_TIMEOUT);
|
|
258
365
|
const cleanup = () => {
|
|
366
|
+
pendingCancellations.delete(cancel);
|
|
259
367
|
clearTimeout(timeout);
|
|
260
368
|
if (noble) {
|
|
261
369
|
noble.removeListener('stateChange', onStateChange);
|
|
@@ -280,8 +388,14 @@ function initializeNoble() {
|
|
|
280
388
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
281
389
|
}
|
|
282
390
|
};
|
|
391
|
+
const cancel = () => {
|
|
392
|
+
cleanup();
|
|
393
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, 'Noble BLE is shutting down'));
|
|
394
|
+
};
|
|
395
|
+
pendingCancellations.add(cancel);
|
|
283
396
|
noble.on('stateChange', onStateChange);
|
|
284
397
|
});
|
|
398
|
+
assertBleActive();
|
|
285
399
|
if (!persistentDiscoverListener) {
|
|
286
400
|
persistentDiscoverListener = (peripheral) => {
|
|
287
401
|
handleDeviceDiscovered(peripheral);
|
|
@@ -328,6 +442,8 @@ function broadcastToAllWebContents(channel, payload) {
|
|
|
328
442
|
}
|
|
329
443
|
function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle') {
|
|
330
444
|
clearIdleDisconnect(deviceId);
|
|
445
|
+
if (disposing)
|
|
446
|
+
return;
|
|
331
447
|
idleDisconnectTimers.set(deviceId, setTimeout(() => {
|
|
332
448
|
var _a;
|
|
333
449
|
idleDisconnectTimers.delete(deviceId);
|
|
@@ -336,7 +452,14 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
|
|
|
336
452
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
|
|
337
453
|
const peripheral = connectedDevices.get(deviceId);
|
|
338
454
|
const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
339
|
-
const pending =
|
|
455
|
+
const pending = unsubscribeNotifications(deviceId)
|
|
456
|
+
.catch(error => {
|
|
457
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
|
|
458
|
+
deviceId,
|
|
459
|
+
error: error instanceof Error ? error.message : String(error),
|
|
460
|
+
});
|
|
461
|
+
})
|
|
462
|
+
.then(() => disconnectDevice(deviceId))
|
|
340
463
|
.then(() => {
|
|
341
464
|
broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
|
|
342
465
|
id: deviceId,
|
|
@@ -356,7 +479,7 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
|
|
|
356
479
|
}, ms));
|
|
357
480
|
}
|
|
358
481
|
function cleanupDevice(deviceId, webContents, options = {}) {
|
|
359
|
-
var _a;
|
|
482
|
+
var _a, _b;
|
|
360
483
|
const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', disconnectReason = hdShared.EBleDisconnectReason.DeviceDisconnected, } = options;
|
|
361
484
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device cleanup', {
|
|
362
485
|
deviceId,
|
|
@@ -370,6 +493,8 @@ function cleanupDevice(deviceId, webContents, options = {}) {
|
|
|
370
493
|
const peripheral = connectedDevices.get(deviceId);
|
|
371
494
|
const deviceName = ((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
372
495
|
if (cleanupConnection) {
|
|
496
|
+
(_b = connectingDevices
|
|
497
|
+
.get(deviceId)) === null || _b === void 0 ? void 0 : _b.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, `Device ${deviceId} disconnected while connecting`));
|
|
373
498
|
const disconnectEntry = deviceDisconnectListeners.get(deviceId);
|
|
374
499
|
if (disconnectEntry) {
|
|
375
500
|
disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
|
|
@@ -409,6 +534,7 @@ function handleDeviceDisconnect(deviceId, webContents) {
|
|
|
409
534
|
});
|
|
410
535
|
cleanupDevice(deviceId, webContents, {
|
|
411
536
|
cleanupConnection: true,
|
|
537
|
+
cleanupDiscoveredCache: true,
|
|
412
538
|
sendDisconnectEvent: true,
|
|
413
539
|
cancelOperations: true,
|
|
414
540
|
reason: 'auto-disconnect',
|
|
@@ -432,13 +558,14 @@ function setupMtuListener(peripheral, deviceId, webContents) {
|
|
|
432
558
|
existing.peripheral.removeListener('mtu', existing.listener);
|
|
433
559
|
}
|
|
434
560
|
const listener = (mtu) => {
|
|
435
|
-
|
|
561
|
+
const normalizedMtu = resolveNobleAttMtu(mtu);
|
|
562
|
+
if (normalizedMtu === undefined)
|
|
436
563
|
return;
|
|
437
564
|
if (webContents.isDestroyed())
|
|
438
565
|
return;
|
|
439
566
|
webContents.send(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_MTU_CHANGED, {
|
|
440
567
|
id: deviceId,
|
|
441
|
-
mtu,
|
|
568
|
+
mtu: normalizedMtu,
|
|
442
569
|
});
|
|
443
570
|
};
|
|
444
571
|
deviceMtuListeners.set(deviceId, { peripheral, listener });
|
|
@@ -446,6 +573,7 @@ function setupMtuListener(peripheral, deviceId, webContents) {
|
|
|
446
573
|
}
|
|
447
574
|
function writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, buffer) {
|
|
448
575
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
576
|
+
assertBleActive();
|
|
449
577
|
return new Promise((resolve, reject) => {
|
|
450
578
|
writeCharacteristic.write(buffer, true, (error) => {
|
|
451
579
|
if (error) {
|
|
@@ -545,7 +673,7 @@ function transmitHexDataToDevice(deviceId, hexData, options) {
|
|
|
545
673
|
armIdleDisconnect(deviceId, BLE_BUSY_BACKSTOP_MS, 'busy-backstop');
|
|
546
674
|
const toBuffer = Buffer.from(hexData, 'hex');
|
|
547
675
|
const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
|
|
548
|
-
const packetCapacity = resolveBlePacketCapacity(peripheral.mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
|
|
676
|
+
const packetCapacity = resolveBlePacketCapacity(resolveNobleAttMtu(peripheral.mtu), BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
|
|
549
677
|
const pacingDelayMs = resolveNobleBleWritePacingDelay(options);
|
|
550
678
|
if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
|
|
551
679
|
const writeCharacteristic = doGetWriteCharacteristic();
|
|
@@ -590,18 +718,37 @@ function transmitHexDataToDevice(deviceId, hexData, options) {
|
|
|
590
718
|
}
|
|
591
719
|
});
|
|
592
720
|
}
|
|
721
|
+
const BLE_COLD_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
722
|
+
const SERVICE_DISCOVERY_MAX_ATTEMPTS = 2;
|
|
723
|
+
const bleNamesById = new Map();
|
|
724
|
+
function rememberBleName(deviceId, name) {
|
|
725
|
+
const trimmed = name === null || name === void 0 ? void 0 : name.trim();
|
|
726
|
+
if (trimmed) {
|
|
727
|
+
bleNamesById.set(deviceId, trimmed);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function resolveBleName(deviceId) {
|
|
731
|
+
var _a, _b, _c, _d, _e, _f;
|
|
732
|
+
const live = ((_c = (_b = (_a = connectedDevices.get(deviceId)) === null || _a === void 0 ? void 0 : _a.advertisement) === null || _b === void 0 ? void 0 : _b.localName) === null || _c === void 0 ? void 0 : _c.trim()) ||
|
|
733
|
+
((_f = (_e = (_d = discoveredDevices.get(deviceId)) === null || _d === void 0 ? void 0 : _d.advertisement) === null || _e === void 0 ? void 0 : _e.localName) === null || _f === void 0 ? void 0 : _f.trim());
|
|
734
|
+
return live || bleNamesById.get(deviceId);
|
|
735
|
+
}
|
|
736
|
+
function shouldConnectByIdFirst(deviceId) {
|
|
737
|
+
return hdShared.isPro2FamilyBleName(resolveBleName(deviceId));
|
|
738
|
+
}
|
|
593
739
|
function handleDeviceDiscovered(peripheral) {
|
|
594
|
-
var _a, _b;
|
|
740
|
+
var _a, _b, _c;
|
|
595
741
|
if (!isOneKeyPeripheral(peripheral)) {
|
|
596
742
|
return;
|
|
597
743
|
}
|
|
598
744
|
const isNewDevice = !discoveredDevices.has(peripheral.id);
|
|
599
745
|
discoveredDevices.set(peripheral.id, peripheral);
|
|
746
|
+
rememberBleName(peripheral.id, (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName);
|
|
600
747
|
if (isNewDevice) {
|
|
601
748
|
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] OneKey BLE device discovered', {
|
|
602
749
|
deviceId: peripheral.id,
|
|
603
|
-
name: ((
|
|
604
|
-
serviceUUIDs: ((
|
|
750
|
+
name: ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName) || 'Unknown Device',
|
|
751
|
+
serviceUUIDs: ((_c = peripheral.advertisement) === null || _c === void 0 ? void 0 : _c.serviceUuids) || [],
|
|
605
752
|
});
|
|
606
753
|
}
|
|
607
754
|
}
|
|
@@ -627,8 +774,9 @@ function waitForNobleScanStop(nobleInstance) {
|
|
|
627
774
|
});
|
|
628
775
|
});
|
|
629
776
|
}
|
|
630
|
-
function performTargetedScan(targetDeviceId) {
|
|
777
|
+
function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS) {
|
|
631
778
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
779
|
+
assertBleActive();
|
|
632
780
|
if (!noble) {
|
|
633
781
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
|
|
634
782
|
}
|
|
@@ -640,6 +788,7 @@ function performTargetedScan(targetDeviceId) {
|
|
|
640
788
|
if (settled)
|
|
641
789
|
return;
|
|
642
790
|
settled = true;
|
|
791
|
+
pendingCancellations.delete(cancel);
|
|
643
792
|
if (timeoutId)
|
|
644
793
|
clearTimeout(timeoutId);
|
|
645
794
|
nobleInstance.removeListener('discover', onDiscover);
|
|
@@ -649,6 +798,7 @@ function performTargetedScan(targetDeviceId) {
|
|
|
649
798
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, error.message));
|
|
650
799
|
return;
|
|
651
800
|
}
|
|
801
|
+
assertBleActive();
|
|
652
802
|
if (peripheral) {
|
|
653
803
|
discoveredDevices.set(peripheral.id, peripheral);
|
|
654
804
|
}
|
|
@@ -667,7 +817,11 @@ function performTargetedScan(targetDeviceId) {
|
|
|
667
817
|
const timeoutId = setTimeout(() => {
|
|
668
818
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId);
|
|
669
819
|
finish(null).catch(reject);
|
|
670
|
-
},
|
|
820
|
+
}, timeoutMs);
|
|
821
|
+
const cancel = () => {
|
|
822
|
+
finish(null, new Error('Noble BLE is shutting down')).catch(reject);
|
|
823
|
+
};
|
|
824
|
+
pendingCancellations.add(cancel);
|
|
671
825
|
nobleInstance.on('discover', onDiscover);
|
|
672
826
|
nobleInstance.startScanning([], true, (error) => {
|
|
673
827
|
if (error) {
|
|
@@ -687,14 +841,18 @@ function enumerateDevices() {
|
|
|
687
841
|
if (!noble) {
|
|
688
842
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
|
|
689
843
|
}
|
|
844
|
+
assertBleActive();
|
|
690
845
|
const nobleInstance = noble;
|
|
691
846
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device enumeration');
|
|
692
847
|
discoveredDevices.clear();
|
|
693
848
|
ensureDiscoverListener();
|
|
694
849
|
return new Promise((resolve, reject) => {
|
|
695
850
|
const devices = [];
|
|
851
|
+
let settled = false;
|
|
696
852
|
let intervalId;
|
|
697
853
|
const cleanup = () => index.__awaiter(this, void 0, void 0, function* () {
|
|
854
|
+
settled = true;
|
|
855
|
+
pendingCancellations.delete(cancel);
|
|
698
856
|
clearTimeout(timeoutId);
|
|
699
857
|
if (intervalId)
|
|
700
858
|
clearInterval(intervalId);
|
|
@@ -716,13 +874,21 @@ function enumerateDevices() {
|
|
|
716
874
|
connectedDevices.forEach(pushDevice);
|
|
717
875
|
};
|
|
718
876
|
const timeoutId = setTimeout(() => index.__awaiter(this, void 0, void 0, function* () {
|
|
877
|
+
if (settled)
|
|
878
|
+
return;
|
|
719
879
|
checkDevices();
|
|
720
880
|
yield cleanup();
|
|
721
881
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
|
|
722
882
|
resolve(devices);
|
|
723
883
|
}), DEVICE_SCAN_TIMEOUT);
|
|
884
|
+
const cancel = () => {
|
|
885
|
+
cleanup().then(() => resolve([]), reject);
|
|
886
|
+
};
|
|
887
|
+
pendingCancellations.add(cancel);
|
|
724
888
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
|
|
725
889
|
nobleInstance.startScanning([], true, (error) => index.__awaiter(this, void 0, void 0, function* () {
|
|
890
|
+
if (settled)
|
|
891
|
+
return;
|
|
726
892
|
if (error) {
|
|
727
893
|
yield cleanup();
|
|
728
894
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to start scanning:', error);
|
|
@@ -763,12 +929,14 @@ function getDevice(deviceId) {
|
|
|
763
929
|
const peripheral = discoveredDevices.get(deviceId);
|
|
764
930
|
if (peripheral) {
|
|
765
931
|
const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
766
|
-
|
|
932
|
+
const mtu = resolveNobleAttMtu(peripheral.mtu);
|
|
933
|
+
return Object.assign({ commType: 'electron-ble', id: peripheral.id, name: deviceName, state: peripheral.state || 'disconnected' }, (mtu === undefined ? {} : { mtu }));
|
|
767
934
|
}
|
|
768
935
|
const connectedPeripheral = connectedDevices.get(deviceId);
|
|
769
936
|
if (connectedPeripheral) {
|
|
770
937
|
const deviceName = ((_b = connectedPeripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName) || 'Unknown Device';
|
|
771
|
-
|
|
938
|
+
const mtu = resolveNobleAttMtu(connectedPeripheral.mtu);
|
|
939
|
+
return Object.assign({ commType: 'electron-ble', id: connectedPeripheral.id, name: deviceName, state: connectedPeripheral.state || 'connected' }, (mtu === undefined ? {} : { mtu }));
|
|
772
940
|
}
|
|
773
941
|
return {
|
|
774
942
|
commType: 'electron-ble',
|
|
@@ -779,13 +947,16 @@ function getDevice(deviceId) {
|
|
|
779
947
|
}
|
|
780
948
|
function discoverServicesAndCharacteristics(peripheral) {
|
|
781
949
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
950
|
+
assertBleActive();
|
|
782
951
|
let timeoutId;
|
|
783
952
|
let onDisconnect;
|
|
784
953
|
const cleanup = () => {
|
|
785
954
|
if (timeoutId)
|
|
786
955
|
clearTimeout(timeoutId);
|
|
787
|
-
if (onDisconnect)
|
|
956
|
+
if (onDisconnect) {
|
|
788
957
|
peripheral.removeListener('disconnect', onDisconnect);
|
|
958
|
+
pendingCancellations.delete(onDisconnect);
|
|
959
|
+
}
|
|
789
960
|
};
|
|
790
961
|
const timeoutPromise = new Promise((_, reject) => {
|
|
791
962
|
timeoutId = setTimeout(() => {
|
|
@@ -798,11 +969,12 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
798
969
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Device disconnected during service discovery');
|
|
799
970
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'Device disconnected during service discovery'));
|
|
800
971
|
};
|
|
972
|
+
pendingCancellations.add(onDisconnect);
|
|
801
973
|
peripheral.once('disconnect', onDisconnect);
|
|
802
974
|
});
|
|
803
975
|
const discoveryPromise = (() => index.__awaiter(this, void 0, void 0, function* () {
|
|
804
976
|
const services = yield new Promise((resolve, reject) => {
|
|
805
|
-
peripheral.discoverServices(
|
|
977
|
+
peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error, svc) => {
|
|
806
978
|
if (error) {
|
|
807
979
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed:', error);
|
|
808
980
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, error.message));
|
|
@@ -812,8 +984,9 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
812
984
|
}
|
|
813
985
|
});
|
|
814
986
|
});
|
|
987
|
+
assertBleActive();
|
|
815
988
|
if (!services || services.length === 0) {
|
|
816
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
|
|
989
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No OneKey services found');
|
|
817
990
|
}
|
|
818
991
|
logger === null || logger === void 0 ? void 0 : logger.debug('[NobleBLE] services discovered', {
|
|
819
992
|
deviceId: peripheral.id,
|
|
@@ -884,12 +1057,8 @@ function forceReconnectPeripheral(peripheral, deviceId) {
|
|
|
884
1057
|
callback();
|
|
885
1058
|
}), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
|
|
886
1059
|
}
|
|
887
|
-
yield
|
|
888
|
-
timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
889
|
-
timeoutBehavior: 'reject',
|
|
890
|
-
});
|
|
1060
|
+
yield connectPeripheralWithCancellation(peripheral, deviceId);
|
|
891
1061
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
|
|
892
|
-
connectedDevices.set(deviceId, peripheral);
|
|
893
1062
|
yield hdShared.wait(500);
|
|
894
1063
|
});
|
|
895
1064
|
}
|
|
@@ -906,17 +1075,7 @@ function freshScanAndDiscover(deviceId, webContents) {
|
|
|
906
1075
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${deviceId} not found in fresh scan`);
|
|
907
1076
|
}
|
|
908
1077
|
discoveredDevices.set(deviceId, freshPeripheral);
|
|
909
|
-
yield
|
|
910
|
-
freshPeripheral.connect((error) => {
|
|
911
|
-
if (error) {
|
|
912
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Fresh peripheral connection failed: ${error.message}`));
|
|
913
|
-
}
|
|
914
|
-
else {
|
|
915
|
-
connectedDevices.set(deviceId, freshPeripheral);
|
|
916
|
-
resolve();
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
});
|
|
1078
|
+
yield connectPeripheralWithCancellation(freshPeripheral, deviceId, 'Fresh peripheral connection failed: ');
|
|
920
1079
|
setupDisconnectListener(freshPeripheral, deviceId, webContents);
|
|
921
1080
|
yield hdShared.wait(500);
|
|
922
1081
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Attempting service discovery with fresh peripheral');
|
|
@@ -930,11 +1089,11 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
|
|
|
930
1089
|
deviceId,
|
|
931
1090
|
peripheralState: peripheral.state,
|
|
932
1091
|
attempt: attemptNumber,
|
|
933
|
-
maxRetries:
|
|
1092
|
+
maxRetries: SERVICE_DISCOVERY_MAX_ATTEMPTS,
|
|
934
1093
|
targetUUIDs: ONEKEY_SERVICE_UUIDS,
|
|
935
1094
|
});
|
|
936
1095
|
if (attemptNumber > 1) {
|
|
937
|
-
logger === null || logger === void 0 ? void 0 : logger.info(`[NobleBLE] Service discovery retry attempt ${attemptNumber}
|
|
1096
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[NobleBLE] Service discovery retry attempt ${attemptNumber}/${SERVICE_DISCOVERY_MAX_ATTEMPTS}`);
|
|
938
1097
|
}
|
|
939
1098
|
if (peripheral.state !== 'connected') {
|
|
940
1099
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Device not connected: ${peripheral.state}`);
|
|
@@ -943,18 +1102,19 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
|
|
|
943
1102
|
return yield discoverServicesAndCharacteristics(peripheral);
|
|
944
1103
|
}
|
|
945
1104
|
catch (error) {
|
|
946
|
-
logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] No services found (attempt ${attemptNumber}
|
|
947
|
-
if (attemptNumber <
|
|
948
|
-
logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Will retry service discovery (attempt ${attemptNumber + 1}
|
|
1105
|
+
logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] No services found (attempt ${attemptNumber}/${SERVICE_DISCOVERY_MAX_ATTEMPTS})`);
|
|
1106
|
+
if (attemptNumber < SERVICE_DISCOVERY_MAX_ATTEMPTS) {
|
|
1107
|
+
logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Will retry service discovery (attempt ${attemptNumber + 1}/${SERVICE_DISCOVERY_MAX_ATTEMPTS})`);
|
|
949
1108
|
}
|
|
950
1109
|
throw error;
|
|
951
1110
|
}
|
|
952
1111
|
}), {
|
|
953
|
-
retries:
|
|
1112
|
+
retries: SERVICE_DISCOVERY_MAX_ATTEMPTS - 1,
|
|
954
1113
|
factor: 1.5,
|
|
955
|
-
minTimeout:
|
|
1114
|
+
minTimeout: 500,
|
|
956
1115
|
maxTimeout: 3000,
|
|
957
1116
|
onFailedAttempt: error => {
|
|
1117
|
+
assertBleActive();
|
|
958
1118
|
logger === null || logger === void 0 ? void 0 : logger.error(`[NobleBLE] Service discovery attempt ${error.attemptNumber} failed:`, {
|
|
959
1119
|
message: error.message,
|
|
960
1120
|
retriesLeft: error.retriesLeft,
|
|
@@ -966,23 +1126,29 @@ function discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId) {
|
|
|
966
1126
|
}
|
|
967
1127
|
function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
|
|
968
1128
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
969
|
-
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
970
1129
|
try {
|
|
971
|
-
|
|
1130
|
+
yield forceReconnectPeripheral(peripheral, deviceId);
|
|
972
1131
|
}
|
|
973
|
-
catch (
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
}
|
|
1132
|
+
catch (resetError) {
|
|
1133
|
+
if (hdShared.isBleStaleBondHardwareError(resetError) ||
|
|
1134
|
+
(resetError === null || resetError === void 0 ? void 0 : resetError.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
|
|
1135
|
+
throw resetError;
|
|
1136
|
+
}
|
|
1137
|
+
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
|
|
978
1138
|
}
|
|
979
|
-
|
|
1139
|
+
assertBleActive();
|
|
980
1140
|
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
981
1141
|
try {
|
|
982
1142
|
return yield discoverServicesAndCharacteristicsWithRetry(peripheral, deviceId);
|
|
983
1143
|
}
|
|
984
|
-
catch (
|
|
985
|
-
|
|
1144
|
+
catch (discoveryError) {
|
|
1145
|
+
if (peripheral.state === 'connected') {
|
|
1146
|
+
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed on a live link, dropping it before the fresh scan', discoveryError);
|
|
1147
|
+
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Service discovery failed, attempting fresh scan...', discoveryError);
|
|
1151
|
+
}
|
|
986
1152
|
return freshScanAndDiscover(deviceId, webContents);
|
|
987
1153
|
}
|
|
988
1154
|
});
|
|
@@ -993,21 +1159,26 @@ const directConnectCooldownUntil = new Map();
|
|
|
993
1159
|
function tryDirectConnectById(deviceId) {
|
|
994
1160
|
var _a;
|
|
995
1161
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1162
|
+
assertBleActive();
|
|
996
1163
|
const nobleInstance = noble;
|
|
997
1164
|
if (!(nobleInstance === null || nobleInstance === void 0 ? void 0 : nobleInstance.connectAsync))
|
|
998
1165
|
return undefined;
|
|
999
1166
|
const cooldownUntil = (_a = directConnectCooldownUntil.get(deviceId)) !== null && _a !== void 0 ? _a : 0;
|
|
1000
1167
|
if (Date.now() < cooldownUntil)
|
|
1001
1168
|
return undefined;
|
|
1169
|
+
let timer;
|
|
1170
|
+
let cancel;
|
|
1002
1171
|
try {
|
|
1003
1172
|
const directPromise = nobleInstance.connectAsync(deviceId);
|
|
1004
1173
|
const raced = yield Promise.race([
|
|
1005
1174
|
directPromise,
|
|
1006
1175
|
new Promise(resolve => {
|
|
1007
|
-
|
|
1176
|
+
cancel = () => resolve('timeout');
|
|
1177
|
+
pendingCancellations.add(cancel);
|
|
1178
|
+
timer = setTimeout(cancel, DIRECT_CONNECT_TIMEOUT_MS);
|
|
1008
1179
|
}),
|
|
1009
1180
|
]);
|
|
1010
|
-
if (raced === 'timeout') {
|
|
1181
|
+
if (raced === 'timeout' || disposing) {
|
|
1011
1182
|
directConnectCooldownUntil.set(deviceId, Date.now() + DIRECT_CONNECT_COOLDOWN_MS);
|
|
1012
1183
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Direct connect-by-id timed out, falling back to scan', {
|
|
1013
1184
|
deviceId,
|
|
@@ -1039,10 +1210,15 @@ function tryDirectConnectById(deviceId) {
|
|
|
1039
1210
|
});
|
|
1040
1211
|
return undefined;
|
|
1041
1212
|
}
|
|
1213
|
+
finally {
|
|
1214
|
+
clearTimeout(timer);
|
|
1215
|
+
if (cancel)
|
|
1216
|
+
pendingCancellations.delete(cancel);
|
|
1217
|
+
}
|
|
1042
1218
|
});
|
|
1043
1219
|
}
|
|
1044
1220
|
function connectDevice(deviceId, webContents) {
|
|
1045
|
-
var _a;
|
|
1221
|
+
var _a, _b, _c;
|
|
1046
1222
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1047
1223
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connect device request:', {
|
|
1048
1224
|
deviceId,
|
|
@@ -1054,40 +1230,55 @@ function connectDevice(deviceId, webContents) {
|
|
|
1054
1230
|
});
|
|
1055
1231
|
let peripheral = (_a = discoveredDevices.get(deviceId)) !== null && _a !== void 0 ? _a : connectedDevices.get(deviceId);
|
|
1056
1232
|
if (!peripheral) {
|
|
1057
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device not discovered, attempting targeted scan for:', deviceId);
|
|
1058
1233
|
if (!noble) {
|
|
1059
1234
|
yield initializeNoble();
|
|
1060
1235
|
}
|
|
1061
1236
|
if (!noble) {
|
|
1062
1237
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
|
|
1063
1238
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1239
|
+
const byIdFirst = shouldConnectByIdFirst(deviceId);
|
|
1240
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Resolving device for cold connect', {
|
|
1241
|
+
deviceId,
|
|
1242
|
+
name: (_b = resolveBleName(deviceId)) !== null && _b !== void 0 ? _b : 'unknown',
|
|
1243
|
+
strategy: byIdFirst ? 'connect-by-id-first' : 'scan-first',
|
|
1244
|
+
});
|
|
1245
|
+
const scanForPeripheral = () => index.__awaiter(this, void 0, void 0, function* () {
|
|
1246
|
+
var _d;
|
|
1247
|
+
try {
|
|
1248
|
+
return (_d = (yield performTargetedScan(deviceId, BLE_COLD_CONNECT_SCAN_TIMEOUT_MS))) !== null && _d !== void 0 ? _d : undefined;
|
|
1074
1249
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1250
|
+
catch (scanError) {
|
|
1251
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Targeted scan failed', {
|
|
1252
|
+
deviceId,
|
|
1253
|
+
error: scanError instanceof Error ? scanError.message : String(scanError),
|
|
1254
|
+
});
|
|
1255
|
+
return undefined;
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
const connectById = () => index.__awaiter(this, void 0, void 0, function* () {
|
|
1259
|
+
const found = yield tryDirectConnectById(deviceId);
|
|
1260
|
+
if (found) {
|
|
1261
|
+
discoveredDevices.set(deviceId, found);
|
|
1262
|
+
}
|
|
1263
|
+
return found;
|
|
1264
|
+
});
|
|
1265
|
+
peripheral = byIdFirst ? yield connectById() : yield scanForPeripheral();
|
|
1266
|
+
if (!peripheral) {
|
|
1267
|
+
peripheral = byIdFirst ? yield scanForPeripheral() : yield connectById();
|
|
1080
1268
|
}
|
|
1081
1269
|
}
|
|
1270
|
+
assertBleActive();
|
|
1082
1271
|
if (!peripheral) {
|
|
1083
1272
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${deviceId} not found`);
|
|
1084
1273
|
}
|
|
1274
|
+
rememberBleName(deviceId, (_c = peripheral.advertisement) === null || _c === void 0 ? void 0 : _c.localName);
|
|
1085
1275
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connecting to device:', deviceId);
|
|
1086
1276
|
if (peripheral.state === 'connected') {
|
|
1087
1277
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device already connected, skipping connection step');
|
|
1088
1278
|
if (!connectedDevices.has(deviceId)) {
|
|
1089
1279
|
connectedDevices.set(deviceId, peripheral);
|
|
1090
1280
|
}
|
|
1281
|
+
assertBleActive();
|
|
1091
1282
|
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
1092
1283
|
if (deviceCharacteristics.has(deviceId)) {
|
|
1093
1284
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device characteristics already available');
|
|
@@ -1112,6 +1303,7 @@ function connectDevice(deviceId, webContents) {
|
|
|
1112
1303
|
}
|
|
1113
1304
|
try {
|
|
1114
1305
|
const characteristics = yield setupConnectionAndDiscoverServices(peripheral, deviceId, webContents);
|
|
1306
|
+
assertBleActive();
|
|
1115
1307
|
deviceCharacteristics.set(deviceId, characteristics);
|
|
1116
1308
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device ready for communication:', deviceId);
|
|
1117
1309
|
}
|
|
@@ -1122,65 +1314,46 @@ function connectDevice(deviceId, webContents) {
|
|
|
1122
1314
|
}
|
|
1123
1315
|
return;
|
|
1124
1316
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
catch (_a) {
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1144
|
-
if (error) {
|
|
1145
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection failed:', error);
|
|
1146
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
|
|
1147
|
-
return;
|
|
1148
|
-
}
|
|
1149
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connected to device:', deviceId);
|
|
1150
|
-
connectedDevices.set(deviceId, connectedPeripheral);
|
|
1151
|
-
try {
|
|
1152
|
-
const characteristics = yield setupConnectionAndDiscoverServices(connectedPeripheral, deviceId, webContents);
|
|
1153
|
-
deviceCharacteristics.set(deviceId, characteristics);
|
|
1154
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device ready for communication:', deviceId);
|
|
1155
|
-
resolve();
|
|
1156
|
-
}
|
|
1157
|
-
catch (setupError) {
|
|
1158
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection setup failed:', setupError);
|
|
1159
|
-
connectedPeripheral.disconnect(() => {
|
|
1160
|
-
reject(setupError);
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
}));
|
|
1164
|
-
});
|
|
1317
|
+
const connectedPeripheral = peripheral;
|
|
1318
|
+
yield connectPeripheralWithCancellation(connectedPeripheral, deviceId);
|
|
1319
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connected to device:', deviceId);
|
|
1320
|
+
try {
|
|
1321
|
+
const characteristics = yield setupConnectionAndDiscoverServices(connectedPeripheral, deviceId, webContents);
|
|
1322
|
+
assertBleActive();
|
|
1323
|
+
deviceCharacteristics.set(deviceId, characteristics);
|
|
1324
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device ready for communication:', deviceId);
|
|
1325
|
+
}
|
|
1326
|
+
catch (setupError) {
|
|
1327
|
+
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection setup failed:', setupError);
|
|
1328
|
+
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1329
|
+
throw setupError;
|
|
1330
|
+
}
|
|
1165
1331
|
});
|
|
1166
1332
|
}
|
|
1167
1333
|
function disconnectDevice(deviceId) {
|
|
1334
|
+
var _a;
|
|
1168
1335
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1169
|
-
const
|
|
1336
|
+
const pendingConnection = connectingDevices.get(deviceId);
|
|
1337
|
+
const peripheral = (_a = connectedDevices.get(deviceId)) !== null && _a !== void 0 ? _a : pendingConnection === null || pendingConnection === void 0 ? void 0 : pendingConnection.peripheral;
|
|
1170
1338
|
if (!peripheral) {
|
|
1171
1339
|
return;
|
|
1172
1340
|
}
|
|
1341
|
+
pendingConnection === null || pendingConnection === void 0 ? void 0 : pendingConnection.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, `Device ${deviceId} connection cancelled`));
|
|
1173
1342
|
const disconnectEntry = deviceDisconnectListeners.get(deviceId);
|
|
1174
1343
|
if (disconnectEntry) {
|
|
1175
1344
|
disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
|
|
1176
1345
|
deviceDisconnectListeners.delete(deviceId);
|
|
1177
1346
|
}
|
|
1178
1347
|
yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1179
|
-
timeoutMs:
|
|
1348
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1180
1349
|
timeoutBehavior: 'resolve',
|
|
1181
1350
|
});
|
|
1351
|
+
if (peripheral.state === 'connected') {
|
|
1352
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
|
|
1353
|
+
}
|
|
1182
1354
|
cleanupDevice(deviceId, undefined, {
|
|
1183
1355
|
cleanupConnection: true,
|
|
1356
|
+
cleanupDiscoveredCache: true,
|
|
1184
1357
|
sendDisconnectEvent: false,
|
|
1185
1358
|
cancelOperations: true,
|
|
1186
1359
|
reason: 'manual-disconnect',
|
|
@@ -1208,7 +1381,8 @@ function unsubscribeNotifications(deviceId) {
|
|
|
1208
1381
|
subscribedDevices.delete(deviceId);
|
|
1209
1382
|
}
|
|
1210
1383
|
finally {
|
|
1211
|
-
|
|
1384
|
+
if (!disposing)
|
|
1385
|
+
subscriptionOperations.set(deviceId, 'idle');
|
|
1212
1386
|
}
|
|
1213
1387
|
});
|
|
1214
1388
|
}
|
|
@@ -1258,10 +1432,12 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1258
1432
|
timeoutMs: BLE_CLEANUP_TIMEOUT,
|
|
1259
1433
|
timeoutBehavior: 'resolve',
|
|
1260
1434
|
});
|
|
1435
|
+
assertBleActive();
|
|
1261
1436
|
yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
|
|
1262
|
-
timeoutMs:
|
|
1437
|
+
timeoutMs: NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
|
|
1263
1438
|
timeoutBehavior: 'reject',
|
|
1264
1439
|
});
|
|
1440
|
+
assertBleActive();
|
|
1265
1441
|
notifyCharacteristic.on('data', (data) => {
|
|
1266
1442
|
if (!pairedDevices.has(deviceId)) {
|
|
1267
1443
|
pairedDevices.add(deviceId);
|
|
@@ -1271,22 +1447,45 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1271
1447
|
});
|
|
1272
1448
|
});
|
|
1273
1449
|
}
|
|
1450
|
+
const subscribeStartedAt = Date.now();
|
|
1274
1451
|
try {
|
|
1275
1452
|
yield rebuildAppSubscription(deviceId, notifyCharacteristic);
|
|
1453
|
+
assertBleActive();
|
|
1276
1454
|
subscribedDevices.set(deviceId, true);
|
|
1455
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Notification subscription active', {
|
|
1456
|
+
deviceId,
|
|
1457
|
+
ms: Date.now() - subscribeStartedAt,
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
catch (error) {
|
|
1461
|
+
throw createNobleBleConnectionError(error, 'Notification subscription failed: ');
|
|
1277
1462
|
}
|
|
1278
1463
|
finally {
|
|
1279
|
-
|
|
1464
|
+
if (!disposing)
|
|
1465
|
+
subscriptionOperations.set(deviceId, 'idle');
|
|
1280
1466
|
}
|
|
1281
1467
|
});
|
|
1282
1468
|
}
|
|
1283
1469
|
function setupNobleBleHandlers(webContents) {
|
|
1470
|
+
if (disposing)
|
|
1471
|
+
return;
|
|
1284
1472
|
try {
|
|
1285
1473
|
logger = require('electron-log');
|
|
1286
1474
|
const { ipcMain } = require('electron');
|
|
1475
|
+
const channels = new Set();
|
|
1476
|
+
removeIpcHandlers = () => channels.forEach(channel => ipcMain.removeHandler(channel));
|
|
1287
1477
|
const handle = (channel, listener) => {
|
|
1478
|
+
channels.add(channel);
|
|
1288
1479
|
ipcMain.removeHandler(channel);
|
|
1289
|
-
ipcMain.handle(channel,
|
|
1480
|
+
ipcMain.handle(channel, (...args) => index.__awaiter(this, void 0, void 0, function* () {
|
|
1481
|
+
try {
|
|
1482
|
+
assertBleActive();
|
|
1483
|
+
return yield Promise.resolve(listener(...args));
|
|
1484
|
+
}
|
|
1485
|
+
catch (error) {
|
|
1486
|
+
return createNobleBleIpcErrorResponse(error);
|
|
1487
|
+
}
|
|
1488
|
+
}));
|
|
1290
1489
|
};
|
|
1291
1490
|
safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
|
|
1292
1491
|
handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -1383,6 +1582,8 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1383
1582
|
}
|
|
1384
1583
|
}));
|
|
1385
1584
|
webContents.on('destroyed', () => {
|
|
1585
|
+
if (disposing)
|
|
1586
|
+
return;
|
|
1386
1587
|
safeLog(logger, 'info', 'Cleaning up Noble BLE handlers');
|
|
1387
1588
|
(() => index.__awaiter(this, void 0, void 0, function* () {
|
|
1388
1589
|
const deviceIds = Array.from(connectedDevices.keys());
|
|
@@ -1391,10 +1592,6 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1391
1592
|
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1392
1593
|
}
|
|
1393
1594
|
yield stopScanning().catch(() => undefined);
|
|
1394
|
-
if (noble && persistentStateListener) {
|
|
1395
|
-
noble.removeListener('stateChange', persistentStateListener);
|
|
1396
|
-
persistentStateListener = null;
|
|
1397
|
-
}
|
|
1398
1595
|
cleanupNobleListeners();
|
|
1399
1596
|
discoveredDevices.clear();
|
|
1400
1597
|
safeLog(logger, 'info', 'Noble BLE cleanup completed');
|
|
@@ -1409,6 +1606,65 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1409
1606
|
throw error;
|
|
1410
1607
|
}
|
|
1411
1608
|
}
|
|
1609
|
+
function disposeNobleBleSupport(releaseNoble = instance => instance.stop()) {
|
|
1610
|
+
if (disposePromise)
|
|
1611
|
+
return disposePromise;
|
|
1612
|
+
disposing = true;
|
|
1613
|
+
removeIpcHandlers === null || removeIpcHandlers === void 0 ? void 0 : removeIpcHandlers();
|
|
1614
|
+
const instance = noble;
|
|
1615
|
+
const deviceIds = new Set([...connectedDevices.keys(), ...connectingDevices.keys()]);
|
|
1616
|
+
const pendingPeripherals = Array.from(connectingDevices.values(), pending => pending.peripheral);
|
|
1617
|
+
for (const cancel of pendingCancellations)
|
|
1618
|
+
cancel();
|
|
1619
|
+
for (const id of idleDisconnectTimers.keys())
|
|
1620
|
+
clearIdleDisconnect(id);
|
|
1621
|
+
for (const pending of connectingDevices.values()) {
|
|
1622
|
+
pending.cancel(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, 'Noble BLE is shutting down'));
|
|
1623
|
+
}
|
|
1624
|
+
let timeout;
|
|
1625
|
+
disposePromise = (() => index.__awaiter(this, void 0, void 0, function* () {
|
|
1626
|
+
try {
|
|
1627
|
+
yield Promise.race([
|
|
1628
|
+
Promise.allSettled([
|
|
1629
|
+
stopScanning(),
|
|
1630
|
+
...pendingPeripherals.map(peripheral => runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1631
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1632
|
+
timeoutBehavior: 'resolve',
|
|
1633
|
+
})),
|
|
1634
|
+
...Array.from(deviceIds, (id) => index.__awaiter(this, void 0, void 0, function* () {
|
|
1635
|
+
yield unsubscribeNotifications(id).catch(() => undefined);
|
|
1636
|
+
yield disconnectDevice(id).catch(() => undefined);
|
|
1637
|
+
})),
|
|
1638
|
+
]),
|
|
1639
|
+
new Promise(resolve => {
|
|
1640
|
+
timeout = setTimeout(() => {
|
|
1641
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Process dispose timed out; releasing native manager');
|
|
1642
|
+
resolve();
|
|
1643
|
+
}, 3500);
|
|
1644
|
+
}),
|
|
1645
|
+
]);
|
|
1646
|
+
}
|
|
1647
|
+
finally {
|
|
1648
|
+
clearTimeout(timeout);
|
|
1649
|
+
cleanupNobleListeners();
|
|
1650
|
+
if (instance && persistentStateListener) {
|
|
1651
|
+
instance.removeListener('stateChange', persistentStateListener);
|
|
1652
|
+
persistentStateListener = null;
|
|
1653
|
+
}
|
|
1654
|
+
for (const id of deviceIds)
|
|
1655
|
+
cleanupDevice(id);
|
|
1656
|
+
discoveredDevices.clear();
|
|
1657
|
+
directConnectCooldownUntil.clear();
|
|
1658
|
+
if (instance)
|
|
1659
|
+
releaseNoble(instance);
|
|
1660
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Process dispose completed');
|
|
1661
|
+
}
|
|
1662
|
+
}))();
|
|
1663
|
+
return disposePromise;
|
|
1664
|
+
}
|
|
1412
1665
|
|
|
1666
|
+
exports.createNobleBleConnectionError = createNobleBleConnectionError;
|
|
1667
|
+
exports.createNobleBleIpcErrorResponse = createNobleBleIpcErrorResponse;
|
|
1668
|
+
exports.disposeNobleBleSupport = disposeNobleBleSupport;
|
|
1413
1669
|
exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
|
|
1414
1670
|
exports.setupNobleBleHandlers = setupNobleBleHandlers;
|