@onekeyfe/hd-transport-electron 1.2.0-alpha.152 → 1.2.0-alpha.153
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-c28cc24a.js} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-c88e98df.js → noble-ble-handler-78adadea.js} +17 -8
- package/dist/noble-ble-handler.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 +41 -9
- 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-78adadea.js'); });
|
|
36
36
|
setupNobleBleHandlers(webContents);
|
|
37
37
|
});
|
|
38
38
|
}
|
package/dist/index.d.ts
CHANGED
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-c28cc24a.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
5
|
var pRetry = require('p-retry');
|
|
6
6
|
|
|
@@ -116,6 +116,7 @@ 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 BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
|
|
119
120
|
const BLE_IDLE_DISCONNECT_MS = 3 * 60000;
|
|
120
121
|
const BLE_BUSY_BACKSTOP_MS = 10 * 60000;
|
|
121
122
|
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
@@ -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,7 +337,14 @@ 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,
|
|
@@ -407,6 +415,7 @@ function handleDeviceDisconnect(deviceId, webContents) {
|
|
|
407
415
|
});
|
|
408
416
|
cleanupDevice(deviceId, webContents, {
|
|
409
417
|
cleanupConnection: true,
|
|
418
|
+
cleanupDiscoveredCache: true,
|
|
410
419
|
sendDisconnectEvent: true,
|
|
411
420
|
cancelOperations: true,
|
|
412
421
|
reason: 'auto-disconnect',
|
|
@@ -1174,11 +1183,15 @@ function disconnectDevice(deviceId) {
|
|
|
1174
1183
|
deviceDisconnectListeners.delete(deviceId);
|
|
1175
1184
|
}
|
|
1176
1185
|
yield runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1177
|
-
timeoutMs:
|
|
1186
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1178
1187
|
timeoutBehavior: 'resolve',
|
|
1179
1188
|
});
|
|
1189
|
+
if (peripheral.state === 'connected') {
|
|
1190
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
|
|
1191
|
+
}
|
|
1180
1192
|
cleanupDevice(deviceId, undefined, {
|
|
1181
1193
|
cleanupConnection: true,
|
|
1194
|
+
cleanupDiscoveredCache: true,
|
|
1182
1195
|
sendDisconnectEvent: false,
|
|
1183
1196
|
cancelOperations: true,
|
|
1184
1197
|
reason: 'manual-disconnect',
|
|
@@ -1389,10 +1402,6 @@ function setupNobleBleHandlers(webContents) {
|
|
|
1389
1402
|
yield disconnectDevice(deviceId).catch(() => undefined);
|
|
1390
1403
|
}
|
|
1391
1404
|
yield stopScanning().catch(() => undefined);
|
|
1392
|
-
if (noble && persistentStateListener) {
|
|
1393
|
-
noble.removeListener('stateChange', persistentStateListener);
|
|
1394
|
-
persistentStateListener = null;
|
|
1395
|
-
}
|
|
1396
1405
|
cleanupNobleListeners();
|
|
1397
1406
|
discoveredDevices.clear();
|
|
1398
1407
|
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":"AA8BA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AA8EhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAwnDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
|
|
@@ -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.153",
|
|
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.153",
|
|
29
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.153",
|
|
30
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.153",
|
|
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": "2544b0bde0835515911c2c1b8c8b631788557300"
|
|
40
40
|
}
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -89,6 +89,10 @@ const DEVICE_SCAN_TIMEOUT = 5000; // 5 seconds for device scanning
|
|
|
89
89
|
const DEVICE_CHECK_INTERVAL = 500; // 500ms interval for periodic device checks
|
|
90
90
|
const SERVICE_DISCOVERY_TIMEOUT = 10000; // 10 seconds for service discovery
|
|
91
91
|
const BLE_CLEANUP_TIMEOUT = 250;
|
|
92
|
+
// A physical teardown must actually reach the OS before the device can return
|
|
93
|
+
// to a clean state; 250ms routinely declared success while CoreBluetooth was
|
|
94
|
+
// still disconnecting, leaving no trace when the teardown never completed.
|
|
95
|
+
const BLE_DISCONNECT_CONFIRM_TIMEOUT_MS = 3000;
|
|
92
96
|
// Renderer release is logical only; this timer physically frees the device.
|
|
93
97
|
const BLE_IDLE_DISCONNECT_MS = 3 * 60_000;
|
|
94
98
|
// Ceiling while a call is in flight: no outstanding write, but not forever.
|
|
@@ -256,6 +260,11 @@ async function initializeNoble(): Promise<void> {
|
|
|
256
260
|
noble = require('@stoprocent/noble') as NobleModule;
|
|
257
261
|
logger?.info('[NobleBLE] Noble library loaded');
|
|
258
262
|
|
|
263
|
+
// Register the process-lifetime state listener before any early return:
|
|
264
|
+
// the poweredOn fast path below would otherwise skip it for the whole
|
|
265
|
+
// session, leaving poweredOff cache/state reconciliation dead.
|
|
266
|
+
setupPersistentStateListener();
|
|
267
|
+
|
|
259
268
|
// Wait for Bluetooth to be ready
|
|
260
269
|
await new Promise<void>((resolve, reject) => {
|
|
261
270
|
if (!noble) {
|
|
@@ -268,9 +277,6 @@ async function initializeNoble(): Promise<void> {
|
|
|
268
277
|
return;
|
|
269
278
|
}
|
|
270
279
|
|
|
271
|
-
// Setup persistent state listener before initialization
|
|
272
|
-
setupPersistentStateListener();
|
|
273
|
-
|
|
274
280
|
const timeout = setTimeout(() => {
|
|
275
281
|
reject(
|
|
276
282
|
ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Bluetooth initialization timeout')
|
|
@@ -391,7 +397,21 @@ function armIdleDisconnect(
|
|
|
391
397
|
logger?.info('[NobleBLE] Keep-alive timeout, disconnecting device', { deviceId, reason });
|
|
392
398
|
const peripheral = connectedDevices.get(deviceId);
|
|
393
399
|
const deviceName = peripheral?.advertisement?.localName || 'Unknown Device';
|
|
394
|
-
|
|
400
|
+
// Unsubscribe CCCD before dropping the link, matching every other
|
|
401
|
+
// teardown path: the 1S leaves its notify session half-open when the
|
|
402
|
+
// link drops without an unsubscribe and then ignores application
|
|
403
|
+
// protocol traffic on NEW links for tens of minutes (field log
|
|
404
|
+
// 2026-08-19: the lone unsubscribe-skipping idle teardown caused a
|
|
405
|
+
// 6-attempt reconnect loop; unsubscribe-first teardowns reconnected
|
|
406
|
+
// instantly).
|
|
407
|
+
const pending = unsubscribeNotifications(deviceId)
|
|
408
|
+
.catch(error => {
|
|
409
|
+
logger?.warn('[NobleBLE] Keep-alive unsubscribe failed, disconnecting anyway', {
|
|
410
|
+
deviceId,
|
|
411
|
+
error: error instanceof Error ? error.message : String(error),
|
|
412
|
+
});
|
|
413
|
+
})
|
|
414
|
+
.then(() => disconnectDevice(deviceId))
|
|
395
415
|
.then(() => {
|
|
396
416
|
// A call is still in flight here; it must reject, not hang.
|
|
397
417
|
broadcastToAllWebContents(EOneKeyBleMessageKeys.BLE_DEVICE_DISCONNECTED, {
|
|
@@ -494,6 +514,9 @@ function handleDeviceDisconnect(deviceId: string, webContents: WebContents): voi
|
|
|
494
514
|
|
|
495
515
|
cleanupDevice(deviceId, webContents, {
|
|
496
516
|
cleanupConnection: true,
|
|
517
|
+
// Same stale-object hazard as manual disconnect: an externally dropped
|
|
518
|
+
// link invalidates the cached peripheral for the next reconnect.
|
|
519
|
+
cleanupDiscoveredCache: true,
|
|
497
520
|
sendDisconnectEvent: true,
|
|
498
521
|
cancelOperations: true,
|
|
499
522
|
reason: 'auto-disconnect',
|
|
@@ -1583,11 +1606,18 @@ async function disconnectDevice(deviceId: string): Promise<void> {
|
|
|
1583
1606
|
}
|
|
1584
1607
|
|
|
1585
1608
|
await runBleCallbackOperation(callback => peripheral.disconnect(() => callback()), {
|
|
1586
|
-
timeoutMs:
|
|
1609
|
+
timeoutMs: BLE_DISCONNECT_CONFIRM_TIMEOUT_MS,
|
|
1587
1610
|
timeoutBehavior: 'resolve',
|
|
1588
1611
|
});
|
|
1612
|
+
if (peripheral.state === 'connected') {
|
|
1613
|
+
logger?.warn('[NobleBLE] Physical disconnect did not confirm in time', { deviceId });
|
|
1614
|
+
}
|
|
1589
1615
|
cleanupDevice(deviceId, undefined, {
|
|
1590
1616
|
cleanupConnection: true,
|
|
1617
|
+
// macOS returns zero GATT services when a previously-disconnected
|
|
1618
|
+
// peripheral object is reconnected; drop the discovery cache so the next
|
|
1619
|
+
// connect resolves a fresh peripheral (direct connect by id, or scan).
|
|
1620
|
+
cleanupDiscoveredCache: true,
|
|
1591
1621
|
sendDisconnectEvent: false,
|
|
1592
1622
|
cancelOperations: true,
|
|
1593
1623
|
reason: 'manual-disconnect',
|
|
@@ -1913,10 +1943,12 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
|
|
|
1913
1943
|
}
|
|
1914
1944
|
|
|
1915
1945
|
await stopScanning().catch(() => undefined);
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1946
|
+
// persistentStateListener is process-lifetime, NOT per-window: this
|
|
1947
|
+
// destroy handler also fires on a renderer soft restart, and removing
|
|
1948
|
+
// the listener here permanently killed poweredOff/poweredOn
|
|
1949
|
+
// reconciliation for the rest of the process (nothing re-registers it:
|
|
1950
|
+
// initializeNoble early-returns once noble is loaded). The null-guard
|
|
1951
|
+
// in setupPersistentStateListener keeps it deduped to one instance.
|
|
1920
1952
|
cleanupNobleListeners();
|
|
1921
1953
|
discoveredDevices.clear();
|
|
1922
1954
|
safeLog(logger, 'info', 'Noble BLE cleanup completed');
|