@onekeyfe/hd-transport-electron 1.2.0-alpha.157 → 1.2.0-alpha.158
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-fe6d3919.js → index-2ec67577.js} +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-c88e98df.js → noble-ble-handler-7c14645d.js} +21 -10
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/dist/types/desktop-api.d.ts +2 -0
- package/dist/types/desktop-api.d.ts.map +1 -1
- package/dist/types/noble-extended.d.ts +1 -0
- package/dist/types/noble-extended.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/noble-ble-handler.ts +62 -10
- package/src/types/desktop-api.ts +11 -1
- package/src/types/noble-extended.ts +1 -0
|
@@ -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-7c14645d.js'); });
|
|
36
36
|
setupNobleBleHandlers(webContents);
|
|
37
37
|
});
|
|
38
38
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WebContents } from 'electron';
|
|
2
2
|
import { Characteristic, Peripheral } from '@stoprocent/noble';
|
|
3
3
|
import { OneKeyDeviceInfoBase } from '@onekeyfe/hd-transport';
|
|
4
|
+
import { EBleDisconnectReason } from '@onekeyfe/hd-shared';
|
|
4
5
|
|
|
5
6
|
interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
6
7
|
id: string;
|
|
@@ -24,6 +25,7 @@ interface NobleModule {
|
|
|
24
25
|
interface Logger {
|
|
25
26
|
info(message: string, ...args: any[]): void;
|
|
26
27
|
debug(message: string, ...args: any[]): void;
|
|
28
|
+
warn(message: string, ...args: any[]): void;
|
|
27
29
|
error(message: string, ...args: any[]): void;
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -31,6 +33,7 @@ interface Logger {
|
|
|
31
33
|
* Desktop API types for Electron preload script
|
|
32
34
|
* These types define the core interface for Noble BLE communication
|
|
33
35
|
*/
|
|
36
|
+
|
|
34
37
|
interface NobleBleWriteOptions {
|
|
35
38
|
pacingDelayMs?: number;
|
|
36
39
|
}
|
|
@@ -55,9 +58,16 @@ interface NobleBleAPI {
|
|
|
55
58
|
id: string;
|
|
56
59
|
mtu: number;
|
|
57
60
|
}) => void) => () => void;
|
|
61
|
+
/**
|
|
62
|
+
* Fires whenever a BLE link drops. `reason` distinguishes a real peripheral
|
|
63
|
+
* drop from the main process freeing an idle link on its keep-alive timer;
|
|
64
|
+
* it is optional so an older host bridge that omits it still type-checks
|
|
65
|
+
* (absent is treated as a real drop by consumers).
|
|
66
|
+
*/
|
|
58
67
|
onDeviceDisconnected: (callback: (device: {
|
|
59
68
|
id: string;
|
|
60
69
|
name: string;
|
|
70
|
+
reason?: EBleDisconnectReason;
|
|
61
71
|
}) => void) => () => void;
|
|
62
72
|
checkAvailability: () => Promise<{
|
|
63
73
|
available: boolean;
|
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-2ec67577.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -116,7 +116,8 @@ const DEVICE_SCAN_TIMEOUT = 5000;
|
|
|
116
116
|
const DEVICE_CHECK_INTERVAL = 500;
|
|
117
117
|
const SERVICE_DISCOVERY_TIMEOUT = 10000;
|
|
118
118
|
const BLE_CLEANUP_TIMEOUT = 250;
|
|
119
|
-
const
|
|
119
|
+
const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
|
|
120
|
+
const BLE_IDLE_DISCONNECT_MS = 20000;
|
|
120
121
|
const BLE_BUSY_BACKSTOP_MS = 10 * 60000;
|
|
121
122
|
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
122
123
|
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
@@ -242,6 +243,7 @@ function initializeNoble() {
|
|
|
242
243
|
try {
|
|
243
244
|
noble = require('@stoprocent/noble');
|
|
244
245
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble library loaded');
|
|
246
|
+
setupPersistentStateListener();
|
|
245
247
|
yield new Promise((resolve, reject) => {
|
|
246
248
|
if (!noble) {
|
|
247
249
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not initialized'));
|
|
@@ -251,7 +253,6 @@ function initializeNoble() {
|
|
|
251
253
|
resolve();
|
|
252
254
|
return;
|
|
253
255
|
}
|
|
254
|
-
setupPersistentStateListener();
|
|
255
256
|
const timeout = setTimeout(() => {
|
|
256
257
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout'));
|
|
257
258
|
}, BLUETOOTH_INIT_TIMEOUT);
|
|
@@ -336,11 +337,19 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
|
|
|
336
337
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
|
|
337
338
|
const peripheral = connectedDevices.get(deviceId);
|
|
338
339
|
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 =
|
|
340
|
+
const pending = unsubscribeNotifications(deviceId)
|
|
341
|
+
.catch(error => {
|
|
342
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
|
|
343
|
+
deviceId,
|
|
344
|
+
error: error instanceof Error ? error.message : String(error),
|
|
345
|
+
});
|
|
346
|
+
})
|
|
347
|
+
.then(() => disconnectDevice(deviceId))
|
|
340
348
|
.then(() => {
|
|
341
349
|
broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
|
|
342
350
|
id: deviceId,
|
|
343
351
|
name: deviceName,
|
|
352
|
+
reason: hdShared.EBleDisconnectReason.IdleKeepAlive,
|
|
344
353
|
});
|
|
345
354
|
})
|
|
346
355
|
.catch(error => {
|
|
@@ -356,7 +365,7 @@ function armIdleDisconnect(deviceId, ms = BLE_IDLE_DISCONNECT_MS, reason = 'idle
|
|
|
356
365
|
}
|
|
357
366
|
function cleanupDevice(deviceId, webContents, options = {}) {
|
|
358
367
|
var _a;
|
|
359
|
-
const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', } = options;
|
|
368
|
+
const { cleanupConnection = true, cleanupDiscoveredCache = false, sendDisconnectEvent = false, cancelOperations = true, reason = 'unknown', disconnectReason = hdShared.EBleDisconnectReason.DeviceDisconnected, } = options;
|
|
360
369
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Starting device cleanup', {
|
|
361
370
|
deviceId,
|
|
362
371
|
reason,
|
|
@@ -393,6 +402,7 @@ function cleanupDevice(deviceId, webContents, options = {}) {
|
|
|
393
402
|
broadcastToAllWebContents(hdShared.EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
|
|
394
403
|
id: deviceId,
|
|
395
404
|
name: deviceName,
|
|
405
|
+
reason: disconnectReason,
|
|
396
406
|
});
|
|
397
407
|
}
|
|
398
408
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device cleanup completed', { deviceId, reason });
|
|
@@ -407,6 +417,7 @@ function handleDeviceDisconnect(deviceId, webContents) {
|
|
|
407
417
|
});
|
|
408
418
|
cleanupDevice(deviceId, webContents, {
|
|
409
419
|
cleanupConnection: true,
|
|
420
|
+
cleanupDiscoveredCache: true,
|
|
410
421
|
sendDisconnectEvent: true,
|
|
411
422
|
cancelOperations: true,
|
|
412
423
|
reason: 'auto-disconnect',
|
|
@@ -1174,11 +1185,15 @@ function disconnectDevice(deviceId) {
|
|
|
1174
1185
|
deviceDisconnectListeners.delete(deviceId);
|
|
1175
1186
|
}
|
|
1176
1187
|
yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1177
|
-
timeoutMs:
|
|
1188
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1178
1189
|
timeoutBehavior: 'resolve',
|
|
1179
1190
|
});
|
|
1191
|
+
if (peripheral.state === 'connected') {
|
|
1192
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
|
|
1193
|
+
}
|
|
1180
1194
|
cleanupDevice(deviceId, undefined, {
|
|
1181
1195
|
cleanupConnection: true,
|
|
1196
|
+
cleanupDiscoveredCache: true,
|
|
1182
1197
|
sendDisconnectEvent: false,
|
|
1183
1198
|
cancelOperations: true,
|
|
1184
1199
|
reason: 'manual-disconnect',
|
|
@@ -1389,10 +1404,6 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1389
1404
|
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1390
1405
|
}
|
|
1391
1406
|
yield stopScanning().catch(() => undefined);
|
|
1392
|
-
if (noble && persistentStateListener) {
|
|
1393
|
-
noble.removeListener('stateChange', persistentStateListener);
|
|
1394
|
-
persistentStateListener = null;
|
|
1395
|
-
}
|
|
1396
1407
|
cleanupNobleListeners();
|
|
1397
1408
|
discoveredDevices.clear();
|
|
1398
1409
|
safeLog(logger, 'info', 'Noble BLE cleanup completed');
|
|
@@ -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":"AA+BA,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;AAqoDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EBleDisconnectReason } from '@onekeyfe/hd-shared';
|
|
1
2
|
export interface NobleBleWriteOptions {
|
|
2
3
|
pacingDelayMs?: number;
|
|
3
4
|
}
|
|
@@ -25,6 +26,7 @@ export interface NobleBleAPI {
|
|
|
25
26
|
onDeviceDisconnected: (callback: (device: {
|
|
26
27
|
id: string;
|
|
27
28
|
name: string;
|
|
29
|
+
reason?: EBleDisconnectReason;
|
|
28
30
|
}) => void) => () => void;
|
|
29
31
|
checkAvailability: () => Promise<{
|
|
30
32
|
available: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IACzD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAOvF,oBAAoB,EAAE,CACpB,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,oBAAoB,CAAA;KAAE,KAAK,IAAI,KACpF,MAAM,IAAI,CAAC;IAChB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QAC/B,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB"}
|
|
@@ -22,6 +22,7 @@ export interface NobleModule {
|
|
|
22
22
|
export interface Logger {
|
|
23
23
|
info(message: string, ...args: any[]): void;
|
|
24
24
|
debug(message: string, ...args: any[]): void;
|
|
25
|
+
warn(message: string, ...args: any[]): void;
|
|
25
26
|
error(message: string, ...args: any[]): void;
|
|
26
27
|
}
|
|
27
28
|
export declare function safeLog(logger: Logger | null, level: 'info' | 'debug' | 'error', message: string, ...args: any[]): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noble-extended.d.ts","sourceRoot":"","sources":["../../src/types/noble-extended.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGnE,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAGD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CACX,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GACjC,IAAI,CAAC;IACR,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAClE,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IACxE,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9E,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;CACrF;AAGD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC9C;AAGD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,EACjC,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,GAAG,EAAE,GACb,IAAI,CAMN"}
|
|
1
|
+
{"version":3,"file":"noble-extended.d.ts","sourceRoot":"","sources":["../../src/types/noble-extended.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAGnE,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;CACxB;AAGD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CACX,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GACjC,IAAI,CAAC;IACR,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAClE,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IACxE,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9E,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;CACrF;AAGD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC9C;AAGD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,EACjC,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,GAAG,EAAE,GACb,IAAI,CAMN"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-electron",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.158",
|
|
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.0-alpha.
|
|
29
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.158",
|
|
29
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.158",
|
|
30
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.158",
|
|
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": "4eff5659b79164afa0c2d070833bec4f2a477f0b"
|
|
40
40
|
}
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
/* eslint-disable @typescript-eslint/no-var-requires, import/no-extraneous-dependencies */
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
+
EBleDisconnectReason,
|
|
9
10
|
EOneKeyBleMessageKeys,
|
|
10
11
|
ERRORS,
|
|
11
12
|
HardwareErrorCode,
|
|
@@ -89,8 +90,18 @@ const DEVICE_SCAN_TIMEOUT = 5000; // 5 seconds for device scanning
|
|
|
89
90
|
const DEVICE_CHECK_INTERVAL = 500; // 500ms interval for periodic device checks
|
|
90
91
|
const SERVICE_DISCOVERY_TIMEOUT = 10000; // 10 seconds for service discovery
|
|
91
92
|
const BLE_CLEANUP_TIMEOUT = 250;
|
|
93
|
+
// A physical teardown must actually reach the OS before the device can return
|
|
94
|
+
// to a clean state; 250ms routinely declared success while CoreBluetooth was
|
|
95
|
+
// still disconnecting, leaving no trace when the teardown never completed.
|
|
96
|
+
const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
|
|
92
97
|
// Renderer release is logical only; this timer physically frees the device.
|
|
93
|
-
|
|
98
|
+
// Keep it SHORT: the Classic 1S goes protocol-deaf when a link is dropped
|
|
99
|
+
// after sitting idle for minutes (field logs 2026-08-19: every deaf window
|
|
100
|
+
// followed a 3-minute-idle disconnect, while disconnects right after traffic
|
|
101
|
+
// have never produced one across 6.5.0's per-call teardown history). A hot
|
|
102
|
+
// disconnect ~20s after the last operation stays inside the proven-safe
|
|
103
|
+
// pattern and also shrinks the window in which phones cannot see the device.
|
|
104
|
+
const BLE_IDLE_DISCONNECT_MS = 20_000;
|
|
94
105
|
// Ceiling while a call is in flight: no outstanding write, but not forever.
|
|
95
106
|
const BLE_BUSY_BACKSTOP_MS = 10 * 60_000;
|
|
96
107
|
|
|
@@ -256,6 +267,11 @@ async function initializeNoble(): Promise<void> {
|
|
|
256
267
|
noble = require('@stoprocent/noble') as NobleModule;
|
|
257
268
|
logger?.info('[NobleBLE] Noble library loaded');
|
|
258
269
|
|
|
270
|
+
// Register the process-lifetime state listener before any early return:
|
|
271
|
+
// the poweredOn fast path below would otherwise skip it for the whole
|
|
272
|
+
// session, leaving poweredOff cache/state reconciliation dead.
|
|
273
|
+
setupPersistentStateListener();
|
|
274
|
+
|
|
259
275
|
// Wait for Bluetooth to be ready
|
|
260
276
|
await new Promise<void>((resolve, reject) => {
|
|
261
277
|
if (!noble) {
|
|
@@ -268,9 +284,6 @@ async function initializeNoble(): Promise<void> {
|
|
|
268
284
|
return;
|
|
269
285
|
}
|
|
270
286
|
|
|
271
|
-
// Setup persistent state listener before initialization
|
|
272
|
-
setupPersistentStateListener();
|
|
273
|
-
|
|
274
287
|
const timeout = setTimeout(() => {
|
|
275
288
|
reject(
|
|
276
289
|
ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout')
|
|
@@ -336,6 +349,12 @@ interface DeviceCleanupOptions {
|
|
|
336
349
|
cleanupDiscoveredCache?: boolean;
|
|
337
350
|
/** Whether to send disconnect event */
|
|
338
351
|
sendDisconnectEvent?: boolean;
|
|
352
|
+
/**
|
|
353
|
+
* Why the link went down, forwarded to renderers on BLE_DEVICE_DISCONNECTED.
|
|
354
|
+
* Defaults to a real peripheral drop; the keep-alive path overrides it so
|
|
355
|
+
* consumers can tell an internal idle release from a device that left.
|
|
356
|
+
*/
|
|
357
|
+
disconnectReason?: EBleDisconnectReason;
|
|
339
358
|
/** Whether to cancel ongoing operations */
|
|
340
359
|
cancelOperations?: boolean;
|
|
341
360
|
/** Cleanup reason (for logging) */
|
|
@@ -391,12 +410,31 @@ function armIdleDisconnect(
|
|
|
391
410
|
logger?.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
|
|
392
411
|
const peripheral = connectedDevices.get(deviceId);
|
|
393
412
|
const deviceName = peripheral?.advertisement?.localName || 'Unknown Device';
|
|
394
|
-
|
|
413
|
+
// Unsubscribe CCCD before dropping the link, matching every other
|
|
414
|
+
// teardown path: the 1S leaves its notify session half-open when the
|
|
415
|
+
// link drops without an unsubscribe and then ignores application
|
|
416
|
+
// protocol traffic on NEW links for tens of minutes (field log
|
|
417
|
+
// 2026-08-19: the lone unsubscribe-skipping idle teardown caused a
|
|
418
|
+
// 6-attempt reconnect loop; unsubscribe-first teardowns reconnected
|
|
419
|
+
// instantly).
|
|
420
|
+
const pending = unsubscribeNotifications(deviceId)
|
|
421
|
+
.catch(error => {
|
|
422
|
+
logger?.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
|
|
423
|
+
deviceId,
|
|
424
|
+
error: error instanceof Error ? error.message : String(error),
|
|
425
|
+
});
|
|
426
|
+
})
|
|
427
|
+
.then(() => disconnectDevice(deviceId))
|
|
395
428
|
.then(() => {
|
|
396
429
|
// A call is still in flight here; it must reject, not hang.
|
|
430
|
+
// Both 'idle' and 'busy-backstop' report IdleKeepAlive on purpose:
|
|
431
|
+
// we reclaimed the link ourselves and have no evidence the device
|
|
432
|
+
// left, so consumers must not mark it disconnected. The in-flight
|
|
433
|
+
// call still fails, via the renderer dropping its device state.
|
|
397
434
|
broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
|
|
398
435
|
id: deviceId,
|
|
399
436
|
name: deviceName,
|
|
437
|
+
reason: EBleDisconnectReason.IdleKeepAlive,
|
|
400
438
|
});
|
|
401
439
|
})
|
|
402
440
|
.catch(error => {
|
|
@@ -426,6 +464,7 @@ function cleanupDevice(
|
|
|
426
464
|
sendDisconnectEvent = false,
|
|
427
465
|
cancelOperations = true,
|
|
428
466
|
reason = 'unknown',
|
|
467
|
+
disconnectReason = EBleDisconnectReason.DeviceDisconnected,
|
|
429
468
|
} = options;
|
|
430
469
|
|
|
431
470
|
logger?.info('[NobleBLE] Starting device cleanup', {
|
|
@@ -475,6 +514,7 @@ function cleanupDevice(
|
|
|
475
514
|
broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
|
|
476
515
|
id: deviceId,
|
|
477
516
|
name: deviceName,
|
|
517
|
+
reason: disconnectReason,
|
|
478
518
|
});
|
|
479
519
|
}
|
|
480
520
|
|
|
@@ -494,6 +534,9 @@ function handleDeviceDisconnect(deviceId: string, webContents: WebContents): voi
|
|
|
494
534
|
|
|
495
535
|
cleanupDevice(deviceId, webContents, {
|
|
496
536
|
cleanupConnection: true,
|
|
537
|
+
// Same stale-object hazard as manual disconnect: an externally dropped
|
|
538
|
+
// link invalidates the cached peripheral for the next reconnect.
|
|
539
|
+
cleanupDiscoveredCache: true,
|
|
497
540
|
sendDisconnectEvent: true,
|
|
498
541
|
cancelOperations: true,
|
|
499
542
|
reason: 'auto-disconnect',
|
|
@@ -1583,11 +1626,18 @@ async function disconnectDevice(deviceId: string): Promise<void> {
|
|
|
1583
1626
|
}
|
|
1584
1627
|
|
|
1585
1628
|
await runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1586
|
-
timeoutMs:
|
|
1629
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1587
1630
|
timeoutBehavior: 'resolve',
|
|
1588
1631
|
});
|
|
1632
|
+
if (peripheral.state === 'connected') {
|
|
1633
|
+
logger?.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
|
|
1634
|
+
}
|
|
1589
1635
|
cleanupDevice(deviceId, undefined, {
|
|
1590
1636
|
cleanupConnection: true,
|
|
1637
|
+
// macOS returns zero GATT services when a previously-disconnected
|
|
1638
|
+
// peripheral object is reconnected; drop the discovery cache so the next
|
|
1639
|
+
// connect resolves a fresh peripheral (direct connect by id, or scan).
|
|
1640
|
+
cleanupDiscoveredCache: true,
|
|
1591
1641
|
sendDisconnectEvent: false,
|
|
1592
1642
|
cancelOperations: true,
|
|
1593
1643
|
reason: 'manual-disconnect',
|
|
@@ -1913,10 +1963,12 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
|
|
|
1913
1963
|
}
|
|
1914
1964
|
|
|
1915
1965
|
await stopScanning().catch(() => undefined);
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1966
|
+
// persistentStateListener is process-lifetime, NOT per-window: this
|
|
1967
|
+
// destroy handler also fires on a renderer soft restart, and removing
|
|
1968
|
+
// the listener here permanently killed poweredOff/poweredOn
|
|
1969
|
+
// reconciliation for the rest of the process (nothing re-registers it:
|
|
1970
|
+
// initializeNoble early-returns once noble is loaded). The null-guard
|
|
1971
|
+
// in setupPersistentStateListener keeps it deduped to one instance.
|
|
1920
1972
|
cleanupNobleListeners();
|
|
1921
1973
|
discoveredDevices.clear();
|
|
1922
1974
|
safeLog(logger, 'info', 'Noble BLE cleanup completed');
|
package/src/types/desktop-api.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* These types define the core interface for Noble BLE communication
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { EBleDisconnectReason } from '@onekeyfe/hd-shared';
|
|
7
|
+
|
|
6
8
|
// Noble BLE API interface - core BLE functionality
|
|
7
9
|
export interface NobleBleWriteOptions {
|
|
8
10
|
pacingDelayMs?: number;
|
|
@@ -21,7 +23,15 @@ export interface NobleBleAPI {
|
|
|
21
23
|
write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
|
|
22
24
|
onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
|
|
23
25
|
onMtuChanged?: (callback: (device: { id: string; mtu: number }) => void) => () => void;
|
|
24
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Fires whenever a BLE link drops. `reason` distinguishes a real peripheral
|
|
28
|
+
* drop from the main process freeing an idle link on its keep-alive timer;
|
|
29
|
+
* it is optional so an older host bridge that omits it still type-checks
|
|
30
|
+
* (absent is treated as a real drop by consumers).
|
|
31
|
+
*/
|
|
32
|
+
onDeviceDisconnected: (
|
|
33
|
+
callback: (device: { id: string; name: string; reason?: EBleDisconnectReason }) => void
|
|
34
|
+
) => () => void;
|
|
25
35
|
checkAvailability: () => Promise<{
|
|
26
36
|
available: boolean;
|
|
27
37
|
state: string;
|