@onekeyfe/hd-transport-electron 1.2.2-alpha.12 → 1.2.2-alpha.14

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.
@@ -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-e4eaea92.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-de8d60e9.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-eea100c1.js');
5
+ var index = require('./index-ef6298f9.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-eea100c1.js');
3
+ var index = require('./index-ef6298f9.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -96,22 +96,19 @@ function softRefreshSubscription(params) {
96
96
  }
97
97
 
98
98
  const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
99
- const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
99
+ const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10000;
100
100
 
101
101
  let noble = null;
102
102
  let logger = null;
103
103
  function createNobleBleConnectionError(error, messagePrefix = '') {
104
104
  const errorMessage = error.message;
105
- const normalizedErrorMessage = errorMessage.toLowerCase();
106
- let errorCode = hdShared.HardwareErrorCode.BleConnectedError;
107
105
  if (hdShared.isBleStaleBondErrorText(errorMessage)) {
108
- errorCode =
109
- normalizedErrorMessage.includes('peer removed pairing information') ||
110
- normalizedErrorMessage.includes('cberrordomain:14')
111
- ? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
112
- : hdShared.HardwareErrorCode.BleDeviceBondError;
106
+ const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
107
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
108
+ nativeErrorMessage,
109
+ });
113
110
  }
114
- return hdShared.ERRORS.TypedError(errorCode, `${messagePrefix}${errorMessage}`);
111
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
115
112
  }
116
113
  const bluetoothState = {
117
114
  available: false,
@@ -937,9 +934,14 @@ function forceReconnectPeripheral(peripheral, deviceId) {
937
934
  callback();
938
935
  }), { timeoutMs: BLE_CLEANUP_TIMEOUT, timeoutBehavior: 'resolve' });
939
936
  }
940
- yield runBleCallbackOperation(callback => peripheral.connect(callback), {
941
- timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
942
- timeoutBehavior: 'reject',
937
+ yield new Promise((resolve, reject) => {
938
+ peripheral.connect(error => {
939
+ if (error) {
940
+ reject(createNobleBleConnectionError(error));
941
+ return;
942
+ }
943
+ resolve();
944
+ });
943
945
  });
944
946
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Force reconnect successful');
945
947
  connectedDevices.set(deviceId, peripheral);
@@ -1023,6 +1025,9 @@ function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
1023
1025
  yield forceReconnectPeripheral(peripheral, deviceId);
1024
1026
  }
1025
1027
  catch (resetError) {
1028
+ if (hdShared.isBleStaleBondHardwareError(resetError)) {
1029
+ throw resetError;
1030
+ }
1026
1031
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
1027
1032
  }
1028
1033
  setupDisconnectListener(peripheral, deviceId, webContents);
@@ -1190,24 +1195,8 @@ function connectDevice(deviceId, webContents) {
1190
1195
  return;
1191
1196
  }
1192
1197
  return new Promise((resolve, reject) => {
1193
- let connectionTimedOut = false;
1194
- const timeout = setTimeout(() => {
1195
- connectionTimedOut = true;
1196
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'Connection timeout'));
1197
- }, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
1198
1198
  const connectedPeripheral = peripheral;
1199
1199
  connectedPeripheral.connect((error) => index.__awaiter(this, void 0, void 0, function* () {
1200
- clearTimeout(timeout);
1201
- if (connectionTimedOut) {
1202
- if (!error) {
1203
- try {
1204
- connectedPeripheral.disconnect(() => undefined);
1205
- }
1206
- catch (_a) {
1207
- }
1208
- }
1209
- return;
1210
- }
1211
1200
  if (error) {
1212
1201
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection failed:', error);
1213
1202
  reject(createNobleBleConnectionError(error));
@@ -1330,7 +1319,7 @@ function subscribeNotifications(deviceId, callback) {
1330
1319
  timeoutBehavior: 'resolve',
1331
1320
  });
1332
1321
  yield runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
1333
- timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
1322
+ timeoutMs: NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
1334
1323
  timeoutBehavior: 'reject',
1335
1324
  });
1336
1325
  notifyCharacteristic.on('data', (data) => {
@@ -1 +1 @@
1
- {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,SAAK,+CAa7E;AA+ED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAovDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
1
+ {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,SAAK,+CAc7E;AA+ED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAuuDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
@@ -1,3 +1,3 @@
1
1
  export declare const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5000;
2
- export declare const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10000;
2
+ export declare const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10000;
3
3
  //# sourceMappingURL=noble-ble-timeouts.d.ts.map
@@ -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,+BAA+B,QAAS,CAAC"}
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,8BAA8B,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.12",
3
+ "version": "1.2.2-alpha.14",
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.12",
29
- "@onekeyfe/hd-shared": "1.2.2-alpha.12",
30
- "@onekeyfe/hd-transport": "1.2.2-alpha.12",
28
+ "@onekeyfe/hd-core": "1.2.2-alpha.14",
29
+ "@onekeyfe/hd-shared": "1.2.2-alpha.14",
30
+ "@onekeyfe/hd-transport": "1.2.2-alpha.14",
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": "9b5a45bc55819d22ed752899b4c40aad70de4527"
39
+ "gitHead": "1b3bf0576a53bedbc5f4a82308c48e11ffc00b16"
40
40
  }
@@ -2,7 +2,7 @@ import { EventEmitter } from 'events';
2
2
  import { EOneKeyBleMessageKeys, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
3
 
4
4
  import {
5
- NOBLE_BLE_CONNECTION_TIMEOUT_MS,
5
+ NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
6
6
  NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS,
7
7
  } from '../noble-ble-timeouts';
8
8
 
@@ -26,23 +26,36 @@ describe('Electron Noble BLE device discovery', () => {
26
26
  jest.clearAllMocks();
27
27
  });
28
28
 
29
- test('allows enough time for a slow targeted scan and connection', () => {
29
+ test('bounds targeted scans and subscription callbacks independently', () => {
30
30
  expect(NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS).toBe(5_000);
31
- expect(NOBLE_BLE_CONNECTION_TIMEOUT_MS).toBe(10_000);
31
+ expect(NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS).toBe(10_000);
32
32
  });
33
33
 
34
34
  test('maps macOS stale pairing failures without reclassifying generic connection errors', async () => {
35
35
  const { createNobleBleConnectionError } = await import('../noble-ble-handler');
36
36
 
37
- expect(
38
- createNobleBleConnectionError(
39
- new Error('CBErrorDomain:14 Peer removed pairing information on the device side')
40
- )
41
- ).toMatchObject({
42
- errorCode: HardwareErrorCode.BlePeerRemovedPairingInformation,
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
+ },
43
45
  });
46
+ expect(staleBondError.message).toContain(
47
+ 'CBErrorDomain:14 Peer removed pairing information on the device side'
48
+ );
44
49
  expect(createNobleBleConnectionError(new Error('Encryption is insufficient'))).toMatchObject({
45
- errorCode: HardwareErrorCode.BleDeviceBondError,
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,
46
59
  });
47
60
  expect(createNobleBleConnectionError(new Error('connection failed'))).toMatchObject({
48
61
  errorCode: HardwareErrorCode.BleConnectedError,
@@ -198,75 +211,33 @@ describe('Electron Noble BLE device discovery', () => {
198
211
  expect(peripheral.connect).toHaveBeenCalledTimes(1);
199
212
  });
200
213
 
201
- test('disconnects a connection callback that arrives after timeout', async () => {
202
- jest.useFakeTimers({ doNotFake: ['performance'] });
203
-
204
- const handlers = new Map<string, IpcHandler>();
205
- const ipcMain = {
206
- handle: jest.fn((channel: string, handler: IpcHandler) => {
207
- handlers.set(channel, handler);
208
- }),
209
- removeHandler: jest.fn((channel: string) => {
210
- handlers.delete(channel);
211
- }),
212
- };
213
- const noble = new EventEmitter() as EventEmitter & {
214
- state: string;
215
- startScanning: jest.Mock;
216
- stopScanning: jest.Mock;
217
- };
218
- let connectCallback: ((error?: Error) => void) | undefined;
219
- let resolveConnectStarted = () => undefined;
220
- const connectStarted = new Promise<void>(resolve => {
221
- resolveConnectStarted = resolve;
214
+ test('settles a pending Noble connect with the native disconnect error', () => {
215
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
216
+ const Noble = require('@stoprocent/noble/lib/noble');
217
+ const bindings = Object.assign(new EventEmitter(), {
218
+ connect: jest.fn(),
222
219
  });
223
- const peripheral = Object.assign(
224
- new EventEmitter(),
225
- createPeripheral('slow-device', 'Pro2 A1B2'),
226
- {
227
- connect: jest.fn((callback: (error?: Error) => void) => {
228
- connectCallback = callback;
229
- resolveConnectStarted();
230
- }),
231
- disconnect: jest.fn((callback: () => void) => callback()),
232
- }
220
+ const noble = new Noble(bindings);
221
+ noble._registerListeners();
222
+ const peripheral = noble._createPeripheral(
223
+ 'aabbccdd',
224
+ '',
225
+ 'unknown',
226
+ true,
227
+ { localName: 'Pro2 A1B2', serviceUuids: ['0001'] },
228
+ -50,
229
+ false
233
230
  );
234
- noble.state = 'poweredOn';
235
- noble.startScanning = jest.fn((_services, _duplicates, callback) => {
236
- callback?.();
237
- noble.emit('discover', peripheral);
238
- });
239
- noble.stopScanning = jest.fn(callback => callback?.());
231
+ const connectCallback = jest.fn();
240
232
 
241
- jest.doMock('@stoprocent/noble', () => noble);
242
- jest.doMock('electron', () => ({ ipcMain }));
243
- jest.doMock('electron-log', () => ({
244
- info: jest.fn(),
245
- debug: jest.fn(),
246
- error: jest.fn(),
247
- }));
248
-
249
- const { setupNobleBleHandlers } = await import('../noble-ble-handler');
250
- setupNobleBleHandlers({
251
- on: jest.fn(),
252
- send: jest.fn(),
253
- } as unknown as WebContents);
254
-
255
- const connect = handlers.get(EOneKeyBleMessageKeys.NOBLE_BLE_CONNECT);
256
- if (!connect) {
257
- throw new Error('Electron Noble BLE connect handler was not registered');
258
- }
259
-
260
- const connectPromise = Promise.resolve(connect(undefined, 'slow-device'));
261
- await connectStarted;
262
- expect(peripheral.connect).toHaveBeenCalledTimes(1);
263
-
264
- jest.advanceTimersByTime(NOBLE_BLE_CONNECTION_TIMEOUT_MS);
265
- await expect(connectPromise).rejects.toThrow('Connection timeout');
233
+ peripheral.connect(connectCallback);
234
+ const nativeError = new Error(
235
+ 'CBATTErrorDomain:14 Peer removed pairing information on the device side'
236
+ );
237
+ bindings.emit('disconnect', 'aabbccdd', nativeError);
266
238
 
267
- connectCallback?.();
268
- await Promise.resolve();
269
- expect(peripheral.disconnect).toHaveBeenCalledTimes(1);
239
+ expect(connectCallback).toHaveBeenCalledWith(nativeError);
240
+ expect(peripheral.state).toBe('disconnected');
270
241
  });
271
242
 
272
243
  test('enumerates a Pro2 communication advertisement after Find My changes its name', async () => {
@@ -10,12 +10,14 @@ import {
10
10
  EOneKeyBleMessageKeys,
11
11
  ERRORS,
12
12
  HardwareErrorCode,
13
+ HardwareErrorCodeMessage,
13
14
  ONEKEY_NOTIFY_CHARACTERISTIC_UUID,
14
15
  ONEKEY_SERVICE_UUID,
15
16
  ONEKEY_WRITE_CHARACTERISTIC_UUID,
16
17
  createKnownBleUuidAliases,
17
18
  hasOnekeyCommunicationService,
18
19
  isBleStaleBondErrorText,
20
+ isBleStaleBondHardwareError,
19
21
  isOnekeyBluetoothDevice,
20
22
  isPro2FamilyBleName,
21
23
  matchesKnownBleUuid,
@@ -27,7 +29,7 @@ import { resolveBlePacketCapacity, resolveNobleAttMtu } from './ble-packet-capac
27
29
  import { safeLog } from './types/noble-extended';
28
30
  import { runBleCallbackOperation, softRefreshSubscription } from './ble-ops';
29
31
  import {
30
- NOBLE_BLE_CONNECTION_TIMEOUT_MS,
32
+ NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
31
33
  NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS,
32
34
  } from './noble-ble-timeouts';
33
35
 
@@ -42,17 +44,18 @@ let logger: Logger | null = null;
42
44
 
43
45
  export function createNobleBleConnectionError(error: Error, messagePrefix = '') {
44
46
  const errorMessage = error.message;
45
- const normalizedErrorMessage = errorMessage.toLowerCase();
46
- let errorCode = HardwareErrorCode.BleConnectedError;
47
47
  if (isBleStaleBondErrorText(errorMessage)) {
48
- errorCode =
49
- normalizedErrorMessage.includes('peer removed pairing information') ||
50
- normalizedErrorMessage.includes('cberrordomain:14')
51
- ? HardwareErrorCode.BlePeerRemovedPairingInformation
52
- : HardwareErrorCode.BleDeviceBondError;
48
+ const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
49
+ return ERRORS.TypedError(
50
+ HardwareErrorCode.BleBondInvalid,
51
+ `${HardwareErrorCodeMessage[HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`,
52
+ {
53
+ nativeErrorMessage,
54
+ }
55
+ );
53
56
  }
54
57
 
55
- return ERRORS.TypedError(errorCode, `${messagePrefix}${errorMessage}`);
58
+ return ERRORS.TypedError(HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
56
59
  }
57
60
 
58
61
  // Bluetooth state management
@@ -1268,9 +1271,14 @@ async function forceReconnectPeripheral(peripheral: Peripheral, deviceId: string
1268
1271
  }
1269
1272
 
1270
1273
  // Step 3: Re-establish connection
1271
- await runBleCallbackOperation(callback => peripheral.connect(callback), {
1272
- timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
1273
- timeoutBehavior: 'reject',
1274
+ await new Promise<void>((resolve, reject) => {
1275
+ peripheral.connect(error => {
1276
+ if (error) {
1277
+ reject(createNobleBleConnectionError(error));
1278
+ return;
1279
+ }
1280
+ resolve();
1281
+ });
1274
1282
  });
1275
1283
  logger?.info('[NobleBLE] Force reconnect successful');
1276
1284
  connectedDevices.set(deviceId, peripheral);
@@ -1425,6 +1433,9 @@ async function setupConnectionAndDiscoverServices(
1425
1433
  try {
1426
1434
  await forceReconnectPeripheral(peripheral, deviceId);
1427
1435
  } catch (resetError) {
1436
+ if (isBleStaleBondHardwareError(resetError)) {
1437
+ throw resetError;
1438
+ }
1428
1439
  // A failed reset must not abort the attempt: discovery on the existing
1429
1440
  // link, then the fresh-scan fallback, still have a chance to recover.
1430
1441
  logger?.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
@@ -1677,30 +1688,9 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1677
1688
  }
1678
1689
 
1679
1690
  return new Promise((resolve, reject) => {
1680
- let connectionTimedOut = false;
1681
- const timeout = setTimeout(() => {
1682
- connectionTimedOut = true;
1683
- reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, 'Connection timeout'));
1684
- }, NOBLE_BLE_CONNECTION_TIMEOUT_MS);
1685
-
1686
1691
  // TypeScript type assertion - peripheral is guaranteed to be defined at this point
1687
1692
  const connectedPeripheral = peripheral as Peripheral;
1688
1693
  connectedPeripheral.connect(async (error: Error | undefined) => {
1689
- clearTimeout(timeout);
1690
-
1691
- // Noble may invoke the callback after the SDK timed out and released the request.
1692
- // Ignore it to avoid initializing disposed commands or leaving an orphaned connection.
1693
- if (connectionTimedOut) {
1694
- if (!error) {
1695
- try {
1696
- connectedPeripheral.disconnect(() => undefined);
1697
- } catch {
1698
- // Best-effort cleanup only.
1699
- }
1700
- }
1701
- return;
1702
- }
1703
-
1704
1694
  if (error) {
1705
1695
  logger?.error('[NobleBLE] Connection failed:', error);
1706
1696
  reject(createNobleBleConnectionError(error));
@@ -1886,7 +1876,7 @@ async function subscribeNotifications(
1886
1876
  timeoutBehavior: 'resolve',
1887
1877
  });
1888
1878
  await runBleCallbackOperation(callback => notifyCharacteristic.subscribe(callback), {
1889
- timeoutMs: NOBLE_BLE_CONNECTION_TIMEOUT_MS,
1879
+ timeoutMs: NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS,
1890
1880
  timeoutBehavior: 'reject',
1891
1881
  });
1892
1882
 
@@ -1,2 +1,2 @@
1
1
  export const NOBLE_BLE_TARGETED_SCAN_TIMEOUT_MS = 5_000;
2
- export const NOBLE_BLE_CONNECTION_TIMEOUT_MS = 10_000;
2
+ export const NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS = 10_000;