@onekeyfe/hd-transport-electron 1.2.2-alpha.13 → 1.2.2-alpha.15
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/{index-ef6298f9.js → index-00297050.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-de8d60e9.js → noble-ble-handler-95c5cf0d.js} +24 -27
- package/dist/noble-ble-handler.d.ts +0 -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/package.json +5 -5
- package/src/__tests__/noble-ble-handler.test.ts +69 -58
- package/src/noble-ble-handler.ts +33 -34
- package/src/noble-ble-timeouts.ts +1 -1
|
@@ -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-95c5cf0d.js'); });
|
|
36
36
|
setupNobleBleHandlers(webContents);
|
|
37
37
|
});
|
|
38
38
|
}
|
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-00297050.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -96,20 +96,10 @@ function softRefreshSubscription(params) {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
|
|
99
|
-
const
|
|
99
|
+
const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
|
|
100
100
|
|
|
101
101
|
let noble = null;
|
|
102
102
|
let logger = null;
|
|
103
|
-
function createNobleBleConnectionError(error, messagePrefix = '') {
|
|
104
|
-
const errorMessage = error.message;
|
|
105
|
-
if (hdShared.isBleStaleBondErrorText(errorMessage)) {
|
|
106
|
-
const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
|
|
107
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
|
|
108
|
-
nativeErrorMessage,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
|
|
112
|
-
}
|
|
113
103
|
const bluetoothState = {
|
|
114
104
|
available: false,
|
|
115
105
|
unsupported: false,
|
|
@@ -934,14 +924,9 @@ function forceReconnectPeripheral(peripheral, deviceId) {
|
|
|
934
924
|
callback();
|
|
935
925
|
}), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
|
|
936
926
|
}
|
|
937
|
-
yield
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
reject(createNobleBleConnectionError(error));
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
resolve();
|
|
944
|
-
});
|
|
927
|
+
yield runBleCallbackOperation(callback => peripheral.connect(callback), {
|
|
928
|
+
timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
929
|
+
timeoutBehavior: 'reject',
|
|
945
930
|
});
|
|
946
931
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
|
|
947
932
|
connectedDevices.set(deviceId, peripheral);
|
|
@@ -964,7 +949,7 @@ function freshScanAndDiscover(deviceId, webContents) {
|
|
|
964
949
|
yield new Promise((resolve, reject) => {
|
|
965
950
|
freshPeripheral.connect((error) => {
|
|
966
951
|
if (error) {
|
|
967
|
-
reject(
|
|
952
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Fresh peripheral connection failed: ${error.message}`));
|
|
968
953
|
}
|
|
969
954
|
else {
|
|
970
955
|
connectedDevices.set(deviceId, freshPeripheral);
|
|
@@ -1025,9 +1010,6 @@ function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
|
|
|
1025
1010
|
yield forceReconnectPeripheral(peripheral, deviceId);
|
|
1026
1011
|
}
|
|
1027
1012
|
catch (resetError) {
|
|
1028
|
-
if (hdShared.isBleStaleBondHardwareError(resetError)) {
|
|
1029
|
-
throw resetError;
|
|
1030
|
-
}
|
|
1031
1013
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
|
|
1032
1014
|
}
|
|
1033
1015
|
setupDisconnectListener(peripheral, deviceId, webContents);
|
|
@@ -1195,11 +1177,27 @@ function connectDevice(deviceId, webContents) {
|
|
|
1195
1177
|
return;
|
|
1196
1178
|
}
|
|
1197
1179
|
return new Promise((resolve, reject) => {
|
|
1180
|
+
let connectionTimedOut = false;
|
|
1181
|
+
const timeout = setTimeout(() => {
|
|
1182
|
+
connectionTimedOut = true;
|
|
1183
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'Connection timeout'));
|
|
1184
|
+
}, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
|
|
1198
1185
|
const connectedPeripheral = peripheral;
|
|
1199
1186
|
connectedPeripheral.connect((error) => index.__awaiter(this, void 0, void 0, function* () {
|
|
1187
|
+
clearTimeout(timeout);
|
|
1188
|
+
if (connectionTimedOut) {
|
|
1189
|
+
if (!error) {
|
|
1190
|
+
try {
|
|
1191
|
+
connectedPeripheral.disconnect(() => undefined);
|
|
1192
|
+
}
|
|
1193
|
+
catch (_a) {
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1200
1198
|
if (error) {
|
|
1201
1199
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection failed:', error);
|
|
1202
|
-
reject(
|
|
1200
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, error.message));
|
|
1203
1201
|
return;
|
|
1204
1202
|
}
|
|
1205
1203
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connected to device:', deviceId);
|
|
@@ -1319,7 +1317,7 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1319
1317
|
timeoutBehavior: 'resolve',
|
|
1320
1318
|
});
|
|
1321
1319
|
yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
|
|
1322
|
-
timeoutMs:
|
|
1320
|
+
timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
1323
1321
|
timeoutBehavior: 'reject',
|
|
1324
1322
|
});
|
|
1325
1323
|
notifyCharacteristic.on('data', (data) => {
|
|
@@ -1471,6 +1469,5 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1471
1469
|
}
|
|
1472
1470
|
}
|
|
1473
1471
|
|
|
1474
|
-
exports.createNobleBleConnectionError = createNobleBleConnectionError;
|
|
1475
1472
|
exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
|
|
1476
1473
|
exports.setupNobleBleHandlers = setupNobleBleHandlers;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { WebContents } from 'electron';
|
|
2
2
|
import type { NobleBleWriteOptions } from './types/desktop-api';
|
|
3
|
-
export declare function createNobleBleConnectionError(error: Error, messagePrefix?: string): import("@onekeyfe/hd-shared").HardwareError;
|
|
4
3
|
export declare function resolveNobleBleWritePacingDelay(options?: NobleBleWriteOptions): number;
|
|
5
4
|
export declare function setupNobleBleHandlers(webContents: WebContents): void;
|
|
6
5
|
//# sourceMappingURL=noble-ble-handler.d.ts.map
|
|
@@ -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":"AAgCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAoFhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAyvDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noble-ble-timeouts.d.ts","sourceRoot":"","sources":["../src/noble-ble-timeouts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,OAAQ,CAAC;AACxD,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"noble-ble-timeouts.d.ts","sourceRoot":"","sources":["../src/noble-ble-timeouts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,OAAQ,CAAC;AACxD,eAAO,MAAM,+BAA+B,QAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-electron",
|
|
3
|
-
"version": "1.2.2-alpha.
|
|
3
|
+
"version": "1.2.2-alpha.15",
|
|
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.2.2-alpha.
|
|
29
|
-
"@onekeyfe/hd-shared": "1.2.2-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport": "1.2.2-alpha.
|
|
28
|
+
"@onekeyfe/hd-core": "1.2.2-alpha.15",
|
|
29
|
+
"@onekeyfe/hd-shared": "1.2.2-alpha.15",
|
|
30
|
+
"@onekeyfe/hd-transport": "1.2.2-alpha.15",
|
|
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": "ea66eff5817385ee2144d8e037c385342e44e21f"
|
|
40
40
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { EOneKeyBleMessageKeys
|
|
2
|
+
import { EOneKeyBleMessageKeys } from '@onekeyfe/hd-shared';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
6
6
|
NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS,
|
|
7
7
|
} from '../noble-ble-timeouts';
|
|
8
8
|
|
|
@@ -26,40 +26,9 @@ describe('Electron Noble BLE device discovery', () => {
|
|
|
26
26
|
jest.clearAllMocks();
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test('
|
|
29
|
+
test('allows enough time for a slow targeted scan and connection', () => {
|
|
30
30
|
expect(NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS).toBe(5_000);
|
|
31
|
-
expect(
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test('maps macOS stale pairing failures without reclassifying generic connection errors', async () => {
|
|
35
|
-
const { createNobleBleConnectionError } = await import('../noble-ble-handler');
|
|
36
|
-
|
|
37
|
-
const staleBondError = createNobleBleConnectionError(
|
|
38
|
-
new Error('CBErrorDomain:14 Peer removed pairing information on the device side')
|
|
39
|
-
);
|
|
40
|
-
expect(staleBondError).toMatchObject({
|
|
41
|
-
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
42
|
-
params: {
|
|
43
|
-
nativeErrorMessage: 'CBErrorDomain:14 Peer removed pairing information on the device side',
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
expect(staleBondError.message).toContain(
|
|
47
|
-
'CBErrorDomain:14 Peer removed pairing information on the device side'
|
|
48
|
-
);
|
|
49
|
-
expect(createNobleBleConnectionError(new Error('Encryption is insufficient'))).toMatchObject({
|
|
50
|
-
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
51
|
-
params: {
|
|
52
|
-
nativeErrorMessage: 'Encryption is insufficient',
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
expect(
|
|
56
|
-
createNobleBleConnectionError(new Error('CBATTErrorDomain:14 localized native message'))
|
|
57
|
-
).toMatchObject({
|
|
58
|
-
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
59
|
-
});
|
|
60
|
-
expect(createNobleBleConnectionError(new Error('connection failed'))).toMatchObject({
|
|
61
|
-
errorCode: HardwareErrorCode.BleConnectedError,
|
|
62
|
-
});
|
|
31
|
+
expect(NOBLE_BLE_CONNECTION_TIMEOUT_MS).toBe(10_000);
|
|
63
32
|
});
|
|
64
33
|
|
|
65
34
|
test('keeps safe pacing by default and allows an explicit high-throughput bypass', async () => {
|
|
@@ -211,33 +180,75 @@ describe('Electron Noble BLE device discovery', () => {
|
|
|
211
180
|
expect(peripheral.connect).toHaveBeenCalledTimes(1);
|
|
212
181
|
});
|
|
213
182
|
|
|
214
|
-
test('
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
183
|
+
test('disconnects a connection callback that arrives after timeout', async () => {
|
|
184
|
+
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
185
|
+
|
|
186
|
+
const handlers = new Map<string, IpcHandler>();
|
|
187
|
+
const ipcMain = {
|
|
188
|
+
handle: jest.fn((channel: string, handler: IpcHandler) => {
|
|
189
|
+
handlers.set(channel, handler);
|
|
190
|
+
}),
|
|
191
|
+
removeHandler: jest.fn((channel: string) => {
|
|
192
|
+
handlers.delete(channel);
|
|
193
|
+
}),
|
|
194
|
+
};
|
|
195
|
+
const noble = new EventEmitter() as EventEmitter & {
|
|
196
|
+
state: string;
|
|
197
|
+
startScanning: jest.Mock;
|
|
198
|
+
stopScanning: jest.Mock;
|
|
199
|
+
};
|
|
200
|
+
let connectCallback: ((error?: Error) => void) | undefined;
|
|
201
|
+
let resolveConnectStarted = () => undefined;
|
|
202
|
+
const connectStarted = new Promise<void>(resolve => {
|
|
203
|
+
resolveConnectStarted = resolve;
|
|
219
204
|
});
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
205
|
+
const peripheral = Object.assign(
|
|
206
|
+
new EventEmitter(),
|
|
207
|
+
createPeripheral('slow-device', 'Pro2 A1B2'),
|
|
208
|
+
{
|
|
209
|
+
connect: jest.fn((callback: (error?: Error) => void) => {
|
|
210
|
+
connectCallback = callback;
|
|
211
|
+
resolveConnectStarted();
|
|
212
|
+
}),
|
|
213
|
+
disconnect: jest.fn((callback: () => void) => callback()),
|
|
214
|
+
}
|
|
230
215
|
);
|
|
231
|
-
|
|
216
|
+
noble.state = 'poweredOn';
|
|
217
|
+
noble.startScanning = jest.fn((_services, _duplicates, callback) => {
|
|
218
|
+
callback?.();
|
|
219
|
+
noble.emit('discover', peripheral);
|
|
220
|
+
});
|
|
221
|
+
noble.stopScanning = jest.fn(callback => callback?.());
|
|
232
222
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
223
|
+
jest.doMock('@stoprocent/noble', () => noble);
|
|
224
|
+
jest.doMock('electron', () => ({ ipcMain }));
|
|
225
|
+
jest.doMock('electron-log', () => ({
|
|
226
|
+
info: jest.fn(),
|
|
227
|
+
debug: jest.fn(),
|
|
228
|
+
error: jest.fn(),
|
|
229
|
+
}));
|
|
238
230
|
|
|
239
|
-
|
|
240
|
-
|
|
231
|
+
const { setupNobleBleHandlers } = await import('../noble-ble-handler');
|
|
232
|
+
setupNobleBleHandlers({
|
|
233
|
+
on: jest.fn(),
|
|
234
|
+
send: jest.fn(),
|
|
235
|
+
} as unknown as WebContents);
|
|
236
|
+
|
|
237
|
+
const connect = handlers.get(EOneKeyBleMessageKeys.NOBLE_BLE_CONNECT);
|
|
238
|
+
if (!connect) {
|
|
239
|
+
throw new Error('Electron Noble BLE connect handler was not registered');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const connectPromise = Promise.resolve(connect(undefined, 'slow-device'));
|
|
243
|
+
await connectStarted;
|
|
244
|
+
expect(peripheral.connect).toHaveBeenCalledTimes(1);
|
|
245
|
+
|
|
246
|
+
jest.advanceTimersByTime(NOBLE_BLE_CONNECTION_TIMEOUT_MS);
|
|
247
|
+
await expect(connectPromise).rejects.toThrow('Connection timeout');
|
|
248
|
+
|
|
249
|
+
connectCallback?.();
|
|
250
|
+
await Promise.resolve();
|
|
251
|
+
expect(peripheral.disconnect).toHaveBeenCalledTimes(1);
|
|
241
252
|
});
|
|
242
253
|
|
|
243
254
|
test('enumerates a Pro2 communication advertisement after Find My changes its name', async () => {
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -10,14 +10,11 @@ import {
|
|
|
10
10
|
EOneKeyBleMessageKeys,
|
|
11
11
|
ERRORS,
|
|
12
12
|
HardwareErrorCode,
|
|
13
|
-
HardwareErrorCodeMessage,
|
|
14
13
|
ONEKEY_NOTIFY_CHARACTERISTIC_UUID,
|
|
15
14
|
ONEKEY_SERVICE_UUID,
|
|
16
15
|
ONEKEY_WRITE_CHARACTERISTIC_UUID,
|
|
17
16
|
createKnownBleUuidAliases,
|
|
18
17
|
hasOnekeyCommunicationService,
|
|
19
|
-
isBleStaleBondErrorText,
|
|
20
|
-
isBleStaleBondHardwareError,
|
|
21
18
|
isOnekeyBluetoothDevice,
|
|
22
19
|
isPro2FamilyBleName,
|
|
23
20
|
matchesKnownBleUuid,
|
|
@@ -29,7 +26,7 @@ import { resolveBlePacketCapacity, resolveNobleAttMtu } from './ble-packet-capac
|
|
|
29
26
|
import { safeLog } from './types/noble-extended';
|
|
30
27
|
import { runBleCallbackOperation, softRefreshSubscription } from './ble-ops';
|
|
31
28
|
import {
|
|
32
|
-
|
|
29
|
+
NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
33
30
|
NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS,
|
|
34
31
|
} from './noble-ble-timeouts';
|
|
35
32
|
|
|
@@ -42,22 +39,6 @@ import type { CharacteristicPair, DeviceInfo, Logger, NobleModule } from './type
|
|
|
42
39
|
let noble: NobleModule | null = null;
|
|
43
40
|
let logger: Logger | null = null;
|
|
44
41
|
|
|
45
|
-
export function createNobleBleConnectionError(error: Error, messagePrefix = '') {
|
|
46
|
-
const errorMessage = error.message;
|
|
47
|
-
if (isBleStaleBondErrorText(errorMessage)) {
|
|
48
|
-
const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
|
|
49
|
-
return ERRORS.TypedError(
|
|
50
|
-
HardwareErrorCode.BleBondInvalid,
|
|
51
|
-
`${HardwareErrorCodeMessage[HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`,
|
|
52
|
-
{
|
|
53
|
-
nativeErrorMessage,
|
|
54
|
-
}
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return ERRORS.TypedError(HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
42
|
// Bluetooth state management
|
|
62
43
|
const bluetoothState: {
|
|
63
44
|
available: boolean;
|
|
@@ -1271,14 +1252,9 @@ async function forceReconnectPeripheral(peripheral: Peripheral, deviceId: string
|
|
|
1271
1252
|
}
|
|
1272
1253
|
|
|
1273
1254
|
// Step 3: Re-establish connection
|
|
1274
|
-
await
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
reject(createNobleBleConnectionError(error));
|
|
1278
|
-
return;
|
|
1279
|
-
}
|
|
1280
|
-
resolve();
|
|
1281
|
-
});
|
|
1255
|
+
await runBleCallbackOperation(callback => peripheral.connect(callback), {
|
|
1256
|
+
timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
1257
|
+
timeoutBehavior: 'reject',
|
|
1282
1258
|
});
|
|
1283
1259
|
logger?.info('[NobleBLE] Force reconnect successful');
|
|
1284
1260
|
connectedDevices.set(deviceId, peripheral);
|
|
@@ -1320,7 +1296,12 @@ async function freshScanAndDiscover(
|
|
|
1320
1296
|
await new Promise<void>((resolve, reject) => {
|
|
1321
1297
|
freshPeripheral.connect((error: Error | undefined) => {
|
|
1322
1298
|
if (error) {
|
|
1323
|
-
reject(
|
|
1299
|
+
reject(
|
|
1300
|
+
ERRORS.TypedError(
|
|
1301
|
+
HardwareErrorCode.BleConnectedError,
|
|
1302
|
+
`Fresh peripheral connection failed: ${error.message}`
|
|
1303
|
+
)
|
|
1304
|
+
);
|
|
1324
1305
|
} else {
|
|
1325
1306
|
connectedDevices.set(deviceId, freshPeripheral);
|
|
1326
1307
|
resolve();
|
|
@@ -1433,9 +1414,6 @@ async function setupConnectionAndDiscoverServices(
|
|
|
1433
1414
|
try {
|
|
1434
1415
|
await forceReconnectPeripheral(peripheral, deviceId);
|
|
1435
1416
|
} catch (resetError) {
|
|
1436
|
-
if (isBleStaleBondHardwareError(resetError)) {
|
|
1437
|
-
throw resetError;
|
|
1438
|
-
}
|
|
1439
1417
|
// A failed reset must not abort the attempt: discovery on the existing
|
|
1440
1418
|
// link, then the fresh-scan fallback, still have a chance to recover.
|
|
1441
1419
|
logger?.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
|
|
@@ -1688,12 +1666,33 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
|
|
|
1688
1666
|
}
|
|
1689
1667
|
|
|
1690
1668
|
return new Promise((resolve, reject) => {
|
|
1669
|
+
let connectionTimedOut = false;
|
|
1670
|
+
const timeout = setTimeout(() => {
|
|
1671
|
+
connectionTimedOut = true;
|
|
1672
|
+
reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, 'Connection timeout'));
|
|
1673
|
+
}, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
|
|
1674
|
+
|
|
1691
1675
|
// TypeScript type assertion - peripheral is guaranteed to be defined at this point
|
|
1692
1676
|
const connectedPeripheral = peripheral as Peripheral;
|
|
1693
1677
|
connectedPeripheral.connect(async (error: Error | undefined) => {
|
|
1678
|
+
clearTimeout(timeout);
|
|
1679
|
+
|
|
1680
|
+
// Noble may invoke the callback after the SDK timed out and released the request.
|
|
1681
|
+
// Ignore it to avoid initializing disposed commands or leaving an orphaned connection.
|
|
1682
|
+
if (connectionTimedOut) {
|
|
1683
|
+
if (!error) {
|
|
1684
|
+
try {
|
|
1685
|
+
connectedPeripheral.disconnect(() => undefined);
|
|
1686
|
+
} catch {
|
|
1687
|
+
// Best-effort cleanup only.
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1694
1693
|
if (error) {
|
|
1695
1694
|
logger?.error('[NobleBLE] Connection failed:', error);
|
|
1696
|
-
reject(
|
|
1695
|
+
reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message));
|
|
1697
1696
|
return;
|
|
1698
1697
|
}
|
|
1699
1698
|
|
|
@@ -1876,7 +1875,7 @@ async function subscribeNotifications(
|
|
|
1876
1875
|
timeoutBehavior: 'resolve',
|
|
1877
1876
|
});
|
|
1878
1877
|
await runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
|
|
1879
|
-
timeoutMs:
|
|
1878
|
+
timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
|
|
1880
1879
|
timeoutBehavior: 'reject',
|
|
1881
1880
|
});
|
|
1882
1881
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5_000;
|
|
2
|
-
export const
|
|
2
|
+
export const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10_000;
|