@onekeyfe/hd-transport-electron 1.2.0-alpha.70 → 1.2.0-alpha.72

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.
@@ -0,0 +1,2 @@
1
+ export declare function resolveBlePacketCapacity(mtu: number | null | undefined, maximumPacketCapacity: number, fallbackPacketCapacity: number): number;
2
+ //# sourceMappingURL=ble-packet-capacity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ble-packet-capacity.d.ts","sourceRoot":"","sources":["../src/ble-packet-capacity.ts"],"names":[],"mappings":"AAEA,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,qBAAqB,EAAE,MAAM,EAC7B,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAMR"}
@@ -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-a0b2f06d.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-3bd9cd3e.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ interface DeviceInfo extends OneKeyDeviceInfoBase {
6
6
  id: string;
7
7
  name: string;
8
8
  state: string;
9
+ mtu?: number;
9
10
  }
10
11
  interface CharacteristicPair {
11
12
  write: Characteristic;
@@ -30,6 +31,9 @@ interface Logger {
30
31
  * Desktop API types for Electron preload script
31
32
  * These types define the core interface for Noble BLE communication
32
33
  */
34
+ interface NobleBleWriteOptions {
35
+ pacingDelayMs?: number;
36
+ }
33
37
  interface NobleBleAPI {
34
38
  enumerate: () => Promise<{
35
39
  id: string;
@@ -38,13 +42,18 @@ interface NobleBleAPI {
38
42
  getDevice: (uuid: string) => Promise<{
39
43
  id: string;
40
44
  name: string;
45
+ mtu?: number;
41
46
  } | null>;
42
47
  connect: (uuid: string) => Promise<void>;
43
48
  disconnect: (uuid: string) => Promise<void>;
44
49
  subscribe: (uuid: string) => Promise<void>;
45
50
  unsubscribe: (uuid: string) => Promise<void>;
46
- write: (uuid: string, data: string) => Promise<void>;
51
+ write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
47
52
  onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
53
+ onMtuChanged?: (callback: (device: {
54
+ id: string;
55
+ mtu: number;
56
+ }) => void) => () => void;
48
57
  onDeviceDisconnected: (callback: (device: {
49
58
  id: string;
50
59
  name: string;
@@ -62,4 +71,4 @@ interface DesktopAPI {
62
71
 
63
72
  declare function initNobleBleSupport(webContents: WebContents): Promise<void>;
64
73
 
65
- export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleModule, initNobleBleSupport };
74
+ export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleWriteOptions, NobleModule, initNobleBleSupport };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,cAAc,SAAS,CAAC;AAGxB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEnE,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,cAAc,SAAS,CAAC;AAGxB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE"}
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-84fea839.js');
5
+ var index = require('./index-4f7dc0a5.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-84fea839.js');
3
+ var index = require('./index-4f7dc0a5.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -8,6 +8,14 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
8
8
 
9
9
  var pRetry__default = /*#__PURE__*/_interopDefaultLegacy(pRetry);
10
10
 
11
+ const BLE_ATT_HEADER_BYTES = 3;
12
+ function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
13
+ if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
14
+ return fallbackPacketCapacity;
15
+ }
16
+ return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
17
+ }
18
+
11
19
  function safeLog(logger, level, message, ...args) {
12
20
  if (logger) {
13
21
  logger[level](message, ...args);
@@ -98,6 +106,7 @@ const notificationCallbacks = new Map();
98
106
  const subscribedDevices = new Map();
99
107
  const subscriptionOperations = new Map();
100
108
  const deviceDisconnectListeners = new Map();
109
+ const deviceMtuListeners = new Map();
101
110
  const ONEKEY_SERVICE_UUIDS = [hdShared.ONEKEY_SERVICE_UUID];
102
111
  const ONEKEY_SERVICE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_SERVICE_UUID);
103
112
  const ONEKEY_WRITE_UUID_ALIASES = hdShared.createKnownBleUuidAliases(hdShared.ONEKEY_WRITE_CHARACTERISTIC_UUID);
@@ -107,13 +116,19 @@ const DEVICE_SCAN_TIMEOUT = 5000;
107
116
  const DEVICE_CHECK_INTERVAL = 500;
108
117
  const SERVICE_DISCOVERY_TIMEOUT = 10000;
109
118
  const BLE_CLEANUP_TIMEOUT = 250;
110
- const BLE_PACKET_SIZE = 192;
111
- const UNIFIED_WRITE_DELAY = 5;
119
+ const BLE_PACKET_SIZE_FALLBACK = 192;
120
+ const BLE_PACKET_SIZE_MAXIMUM = 244;
121
+ const DEFAULT_WRITE_PACING_DELAY_MS = 5;
112
122
  const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 };
113
123
  const IS_WINDOWS = process.platform === 'win32';
114
124
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
115
125
  /status:\s*3/i,
116
126
  ];
127
+ function resolveNobleBleWritePacingDelay(options) {
128
+ return typeof (options === null || options === void 0 ? void 0 : options.pacingDelayMs) === 'number' && Number.isFinite(options.pacingDelayMs)
129
+ ? Math.min(Math.max(Math.floor(options.pacingDelayMs), 0), 1000)
130
+ : DEFAULT_WRITE_PACING_DELAY_MS;
131
+ }
117
132
  function isOneKeyPeripheral(peripheral) {
118
133
  var _a, _b;
119
134
  const serviceUuids = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids;
@@ -300,6 +315,11 @@ function cleanupDevice(deviceId, webContents, options = {}) {
300
315
  disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
301
316
  deviceDisconnectListeners.delete(deviceId);
302
317
  }
318
+ const mtuEntry = deviceMtuListeners.get(deviceId);
319
+ if (mtuEntry) {
320
+ mtuEntry.peripheral.removeListener('mtu', mtuEntry.listener);
321
+ deviceMtuListeners.delete(deviceId);
322
+ }
303
323
  connectedDevices.delete(deviceId);
304
324
  deviceCharacteristics.delete(deviceId);
305
325
  notificationCallbacks.delete(deviceId);
@@ -343,8 +363,25 @@ function setupDisconnectListener(peripheral, deviceId, webContents) {
343
363
  };
344
364
  deviceDisconnectListeners.set(deviceId, { peripheral, listener });
345
365
  peripheral.on('disconnect', listener);
366
+ setupMtuListener(peripheral, deviceId, webContents);
346
367
  }
347
- function writeCharacteristicWithAck(deviceId, writeCharacteristic, buffer) {
368
+ function setupMtuListener(peripheral, deviceId, webContents) {
369
+ const existing = deviceMtuListeners.get(deviceId);
370
+ if (existing) {
371
+ existing.peripheral.removeListener('mtu', existing.listener);
372
+ }
373
+ const listener = (mtu) => {
374
+ if (!Number.isFinite(mtu) || mtu <= 0)
375
+ return;
376
+ webContents.send(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_MTU_CHANGED, {
377
+ id: deviceId,
378
+ mtu,
379
+ });
380
+ };
381
+ deviceMtuListeners.set(deviceId, { peripheral, listener });
382
+ peripheral.on('mtu', listener);
383
+ }
384
+ function writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, buffer) {
348
385
  return index.__awaiter(this, void 0, void 0, function* () {
349
386
  return new Promise((resolve, reject) => {
350
387
  writeCharacteristic.write(buffer, true, (error) => {
@@ -376,7 +413,7 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
376
413
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Write characteristic not available for ${deviceId}`);
377
414
  }
378
415
  try {
379
- yield writeCharacteristicWithAck(deviceId, latestWrite, payload);
416
+ yield writeCharacteristicWithoutResponse(deviceId, latestWrite, payload);
380
417
  }
381
418
  catch (e) {
382
419
  const errorMessage = e instanceof Error ? e.message : String(e);
@@ -435,7 +472,7 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
435
472
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `No response observed after ${RETRY_CONFIG.MAX_ATTEMPTS} writes: ${deviceId}`);
436
473
  });
437
474
  }
438
- function transmitHexDataToDevice(deviceId, hexData) {
475
+ function transmitHexDataToDevice(deviceId, hexData, options) {
439
476
  return index.__awaiter(this, void 0, void 0, function* () {
440
477
  const characteristics = deviceCharacteristics.get(deviceId);
441
478
  const peripheral = connectedDevices.get(deviceId);
@@ -444,43 +481,47 @@ function transmitHexDataToDevice(deviceId, hexData) {
444
481
  }
445
482
  const toBuffer = Buffer.from(hexData, 'hex');
446
483
  const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
484
+ const packetCapacity = resolveBlePacketCapacity(peripheral.mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
485
+ const pacingDelayMs = resolveNobleBleWritePacingDelay(options);
447
486
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
448
487
  const writeCharacteristic = doGetWriteCharacteristic();
449
488
  if (!writeCharacteristic) {
450
489
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Write characteristic not available for ${deviceId}`);
451
490
  }
452
- if (toBuffer.length <= BLE_PACKET_SIZE) {
453
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
454
- yield writeCharacteristicWithAck(deviceId, writeCharacteristic, toBuffer);
491
+ if (toBuffer.length <= packetCapacity) {
492
+ if (pacingDelayMs > 0)
493
+ yield hdShared.wait(pacingDelayMs);
494
+ yield writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, toBuffer);
455
495
  return;
456
496
  }
457
497
  for (let offset = 0; offset < toBuffer.length;) {
458
- const chunkSize = Math.min(BLE_PACKET_SIZE, toBuffer.length - offset);
498
+ const chunkSize = Math.min(packetCapacity, toBuffer.length - offset);
459
499
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
460
500
  offset += chunkSize;
461
501
  const latest = doGetWriteCharacteristic();
462
502
  if (!latest) {
463
503
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Write characteristic not available for ${deviceId}`);
464
504
  }
465
- yield writeCharacteristicWithAck(deviceId, latest, chunk);
466
- if (offset < toBuffer.length) {
467
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
505
+ yield writeCharacteristicWithoutResponse(deviceId, latest, chunk);
506
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
507
+ yield hdShared.wait(pacingDelayMs);
468
508
  }
469
509
  }
470
510
  return;
471
511
  }
472
- if (toBuffer.length <= BLE_PACKET_SIZE) {
473
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
512
+ if (toBuffer.length <= packetCapacity) {
513
+ if (pacingDelayMs > 0)
514
+ yield hdShared.wait(pacingDelayMs);
474
515
  yield attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, toBuffer, 'single');
475
516
  return;
476
517
  }
477
518
  for (let offset = 0, idx = 0; offset < toBuffer.length; idx++) {
478
- const chunkSize = Math.min(BLE_PACKET_SIZE, toBuffer.length - offset);
519
+ const chunkSize = Math.min(packetCapacity, toBuffer.length - offset);
479
520
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
480
521
  offset += chunkSize;
481
522
  yield attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, chunk, `chunk-${idx + 1}`);
482
- if (offset < toBuffer.length) {
483
- yield hdShared.wait(UNIFIED_WRITE_DELAY);
523
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
524
+ yield hdShared.wait(pacingDelayMs);
484
525
  }
485
526
  }
486
527
  });
@@ -658,22 +699,12 @@ function getDevice(deviceId) {
658
699
  const peripheral = discoveredDevices.get(deviceId);
659
700
  if (peripheral) {
660
701
  const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
661
- return {
662
- commType: 'electron-ble',
663
- id: peripheral.id,
664
- name: deviceName,
665
- state: peripheral.state || 'disconnected',
666
- };
702
+ return Object.assign({ commType: 'electron-ble', id: peripheral.id, name: deviceName, state: peripheral.state || 'disconnected' }, (typeof peripheral.mtu === 'number' ? { mtu: peripheral.mtu } : {}));
667
703
  }
668
704
  const connectedPeripheral = connectedDevices.get(deviceId);
669
705
  if (connectedPeripheral) {
670
706
  const deviceName = ((_b = connectedPeripheral.advertisement) === null || _b === void 0 ? void 0 : _b.localName) || 'Unknown Device';
671
- return {
672
- commType: 'electron-ble',
673
- id: connectedPeripheral.id,
674
- name: deviceName,
675
- state: connectedPeripheral.state || 'connected',
676
- };
707
+ return Object.assign({ commType: 'electron-ble', id: connectedPeripheral.id, name: deviceName, state: connectedPeripheral.state || 'connected' }, (typeof connectedPeripheral.mtu === 'number' ? { mtu: connectedPeripheral.mtu } : {}));
677
708
  }
678
709
  return {
679
710
  commType: 'electron-ble',
@@ -1152,8 +1183,8 @@ function setupNobleBleHandlers(webContents) {
1152
1183
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_DISCONNECT, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
1153
1184
  yield disconnectDevice(deviceId);
1154
1185
  }));
1155
- ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_WRITE, (_event, deviceId, hexData) => index.__awaiter(this, void 0, void 0, function* () {
1156
- yield transmitHexDataToDevice(deviceId, hexData);
1186
+ ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_WRITE, (_event, deviceId, hexData, options) => index.__awaiter(this, void 0, void 0, function* () {
1187
+ yield transmitHexDataToDevice(deviceId, hexData, options);
1157
1188
  }));
1158
1189
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_SUBSCRIBE, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
1159
1190
  yield subscribeNotifications(deviceId, (data) => {
@@ -1223,4 +1254,5 @@ function setupNobleBleHandlers(webContents) {
1223
1254
  }
1224
1255
  }
1225
1256
 
1257
+ exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
1226
1258
  exports.setupNobleBleHandlers = setupNobleBleHandlers;
@@ -1,3 +1,5 @@
1
1
  import type { WebContents } from 'electron';
2
+ import type { NobleBleWriteOptions } from './types/desktop-api';
3
+ export declare function resolveNobleBleWritePacingDelay(options?: NobleBleWriteOptions): number;
2
4
  export declare function setupNobleBleHandlers(webContents: WebContents): void;
3
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":"AA8BA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AAi9ChE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAyJpE"}
1
+ {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AAEhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAsEhE,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAw7CD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CA8JpE"}
@@ -1,3 +1,6 @@
1
+ export interface NobleBleWriteOptions {
2
+ pacingDelayMs?: number;
3
+ }
1
4
  export interface NobleBleAPI {
2
5
  enumerate: () => Promise<{
3
6
  id: string;
@@ -6,13 +9,18 @@ export interface NobleBleAPI {
6
9
  getDevice: (uuid: string) => Promise<{
7
10
  id: string;
8
11
  name: string;
12
+ mtu?: number;
9
13
  } | null>;
10
14
  connect: (uuid: string) => Promise<void>;
11
15
  disconnect: (uuid: string) => Promise<void>;
12
16
  subscribe: (uuid: string) => Promise<void>;
13
17
  unsubscribe: (uuid: string) => Promise<void>;
14
- write: (uuid: string, data: string) => Promise<void>;
18
+ write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
15
19
  onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
20
+ onMtuChanged?: (callback: (device: {
21
+ id: string;
22
+ mtu: number;
23
+ }) => void) => () => void;
16
24
  onDeviceDisconnected: (callback: (device: {
17
25
  id: string;
18
26
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAMA,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,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC1E,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,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,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnF,oBAAoB,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC/F,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"}
1
+ {"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAMA,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;IACzC,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;IACvF,oBAAoB,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC/F,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"}
@@ -4,6 +4,7 @@ export interface DeviceInfo extends OneKeyDeviceInfoBase {
4
4
  id: string;
5
5
  name: string;
6
6
  state: string;
7
+ mtu?: number;
7
8
  }
8
9
  export interface CharacteristicPair {
9
10
  write: Characteristic;
@@ -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;CACf;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,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.70",
3
+ "version": "1.2.0-alpha.72",
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.70",
29
- "@onekeyfe/hd-shared": "1.2.0-alpha.70",
30
- "@onekeyfe/hd-transport": "1.2.0-alpha.70",
28
+ "@onekeyfe/hd-core": "1.2.0-alpha.72",
29
+ "@onekeyfe/hd-shared": "1.2.0-alpha.72",
30
+ "@onekeyfe/hd-transport": "1.2.0-alpha.72",
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": "d86b033cf970f97e060c49fe4cf618f38b9ce666"
39
+ "gitHead": "76311ccd3753eca8e492b5324f93542bd38525ba"
40
40
  }
@@ -0,0 +1,13 @@
1
+ import { resolveBlePacketCapacity } from '../ble-packet-capacity';
2
+
3
+ describe('resolveBlePacketCapacity', () => {
4
+ test('uses ATT MTU payload capacity with an upper bound', () => {
5
+ expect(resolveBlePacketCapacity(247, 244, 192)).toBe(244);
6
+ expect(resolveBlePacketCapacity(185, 244, 192)).toBe(182);
7
+ });
8
+
9
+ test('preserves the compatibility fallback when MTU is unavailable', () => {
10
+ expect(resolveBlePacketCapacity(null, 244, 192)).toBe(192);
11
+ expect(resolveBlePacketCapacity(undefined, 244, 192)).toBe(192);
12
+ });
13
+ });
@@ -31,6 +31,14 @@ describe('Electron Noble BLE device discovery', () => {
31
31
  expect(NOBLE_BLE_CONNECTION_TIMEOUT_MS).toBe(10_000);
32
32
  });
33
33
 
34
+ test('keeps safe pacing by default and allows an explicit high-throughput bypass', async () => {
35
+ const { resolveNobleBleWritePacingDelay } = await import('../noble-ble-handler');
36
+
37
+ expect(resolveNobleBleWritePacingDelay()).toBe(5);
38
+ expect(resolveNobleBleWritePacingDelay({ pacingDelayMs: 0 })).toBe(0);
39
+ expect(resolveNobleBleWritePacingDelay({ pacingDelayMs: 3.8 })).toBe(3);
40
+ });
41
+
34
42
  test('waits for Noble to stop scanning before enumeration resolves', async () => {
35
43
  jest.useFakeTimers({ doNotFake: ['performance'] });
36
44
 
@@ -0,0 +1,13 @@
1
+ const BLE_ATT_HEADER_BYTES = 3;
2
+
3
+ export function resolveBlePacketCapacity(
4
+ mtu: number | null | undefined,
5
+ maximumPacketCapacity: number,
6
+ fallbackPacketCapacity: number
7
+ ): number {
8
+ if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
9
+ return fallbackPacketCapacity;
10
+ }
11
+
12
+ return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
13
+ }
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@ import type { WebContents } from 'electron';
3
3
  export * from './types';
4
4
 
5
5
  // Export Desktop API types for other packages to reuse
6
- export type { DesktopAPI, NobleBleAPI } from './types/desktop-api';
6
+ export type { DesktopAPI, NobleBleAPI, NobleBleWriteOptions } from './types/desktop-api';
7
7
 
8
8
  export async function initNobleBleSupport(webContents: WebContents) {
9
9
  const { setupNobleBleHandlers } = await import('./noble-ble-handler');
@@ -21,6 +21,7 @@ import {
21
21
  } from '@onekeyfe/hd-shared';
22
22
  import pRetry from 'p-retry';
23
23
 
24
+ import { resolveBlePacketCapacity } from './ble-packet-capacity';
24
25
  import { safeLog } from './types/noble-extended';
25
26
  import { runBleCallbackOperation, softRefreshSubscription } from './ble-ops';
26
27
  import {
@@ -30,6 +31,7 @@ import {
30
31
 
31
32
  import type { IpcMainInvokeEvent, WebContents } from 'electron';
32
33
  import type { Characteristic, Peripheral, Service } from '@stoprocent/noble';
34
+ import type { NobleBleWriteOptions } from './types/desktop-api';
33
35
  import type { CharacteristicPair, DeviceInfo, Logger, NobleModule } from './types/noble-extended';
34
36
 
35
37
  // Noble will be dynamically imported to avoid bundling issues
@@ -65,6 +67,10 @@ const deviceDisconnectListeners = new Map<
65
67
  string,
66
68
  { peripheral: Peripheral; listener: () => void }
67
69
  >();
70
+ const deviceMtuListeners = new Map<
71
+ string,
72
+ { peripheral: Peripheral; listener: (mtu: number) => void }
73
+ >();
68
74
 
69
75
  // Windows-only response watchdog state moved to utils/windows-ble-recovery
70
76
 
@@ -86,14 +92,21 @@ const SERVICE_DISCOVERY_TIMEOUT = 10000; // 10 seconds for service discovery
86
92
  const BLE_CLEANUP_TIMEOUT = 250;
87
93
 
88
94
  // Write-related constants
89
- const BLE_PACKET_SIZE = 192;
90
- const UNIFIED_WRITE_DELAY = 5;
95
+ const BLE_PACKET_SIZE_FALLBACK = 192;
96
+ const BLE_PACKET_SIZE_MAXIMUM = 244;
97
+ const DEFAULT_WRITE_PACING_DELAY_MS = 5;
91
98
  const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 } as const;
92
99
  const IS_WINDOWS = process.platform === 'win32';
93
100
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
94
101
  /status:\s*3/i, // Windows pairing cancelled / GATT write failed
95
102
  ];
96
103
 
104
+ export function resolveNobleBleWritePacingDelay(options?: NobleBleWriteOptions) {
105
+ return typeof options?.pacingDelayMs === 'number' && Number.isFinite(options.pacingDelayMs)
106
+ ? Math.min(Math.max(Math.floor(options.pacingDelayMs), 0), 1000)
107
+ : DEFAULT_WRITE_PACING_DELAY_MS;
108
+ }
109
+
97
110
  function isOneKeyPeripheral(peripheral: Peripheral) {
98
111
  const serviceUuids = peripheral.advertisement?.serviceUuids;
99
112
  const localName = peripheral.advertisement?.localName;
@@ -364,6 +377,11 @@ function cleanupDevice(
364
377
  disconnectEntry.peripheral.removeListener('disconnect', disconnectEntry.listener);
365
378
  deviceDisconnectListeners.delete(deviceId);
366
379
  }
380
+ const mtuEntry = deviceMtuListeners.get(deviceId);
381
+ if (mtuEntry) {
382
+ mtuEntry.peripheral.removeListener('mtu', mtuEntry.listener);
383
+ deviceMtuListeners.delete(deviceId);
384
+ }
367
385
  connectedDevices.delete(deviceId);
368
386
  deviceCharacteristics.delete(deviceId);
369
387
  notificationCallbacks.delete(deviceId);
@@ -423,11 +441,33 @@ function setupDisconnectListener(
423
441
  };
424
442
  deviceDisconnectListeners.set(deviceId, { peripheral, listener });
425
443
  peripheral.on('disconnect', listener);
444
+ setupMtuListener(peripheral, deviceId, webContents);
445
+ }
446
+
447
+ function setupMtuListener(
448
+ peripheral: Peripheral,
449
+ deviceId: string,
450
+ webContents: WebContents
451
+ ): void {
452
+ const existing = deviceMtuListeners.get(deviceId);
453
+ if (existing) {
454
+ existing.peripheral.removeListener('mtu', existing.listener);
455
+ }
456
+
457
+ const listener = (mtu: number) => {
458
+ if (!Number.isFinite(mtu) || mtu <= 0) return;
459
+ webContents.send(EOneKeyBleMessageKeys.NOBLE_BLE_MTU_CHANGED, {
460
+ id: deviceId,
461
+ mtu,
462
+ });
463
+ };
464
+ deviceMtuListeners.set(deviceId, { peripheral, listener });
465
+ peripheral.on('mtu', listener);
426
466
  }
427
467
 
428
468
  // ===== Write helpers (inline) =====
429
469
 
430
- async function writeCharacteristicWithAck(
470
+ async function writeCharacteristicWithoutResponse(
431
471
  deviceId: string,
432
472
  writeCharacteristic: Characteristic,
433
473
  buffer: Buffer
@@ -475,7 +515,7 @@ async function attemptWindowsWriteUntilPaired(
475
515
  }
476
516
 
477
517
  try {
478
- await writeCharacteristicWithAck(deviceId, latestWrite, payload);
518
+ await writeCharacteristicWithoutResponse(deviceId, latestWrite, payload);
479
519
  } catch (e) {
480
520
  const errorMessage = e instanceof Error ? e.message : String(e);
481
521
  logger?.error('[BLE-Write] Windows write error', {
@@ -547,7 +587,11 @@ async function attemptWindowsWriteUntilPaired(
547
587
  );
548
588
  }
549
589
 
550
- async function transmitHexDataToDevice(deviceId: string, hexData: string): Promise<void> {
590
+ async function transmitHexDataToDevice(
591
+ deviceId: string,
592
+ hexData: string,
593
+ options?: NobleBleWriteOptions
594
+ ): Promise<void> {
551
595
  const characteristics = deviceCharacteristics.get(deviceId);
552
596
  const peripheral = connectedDevices.get(deviceId);
553
597
  if (!peripheral || !characteristics) {
@@ -559,6 +603,12 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
559
603
 
560
604
  const toBuffer = Buffer.from(hexData, 'hex');
561
605
  const doGetWriteCharacteristic = () => deviceCharacteristics.get(deviceId)?.write;
606
+ const packetCapacity = resolveBlePacketCapacity(
607
+ peripheral.mtu,
608
+ BLE_PACKET_SIZE_MAXIMUM,
609
+ BLE_PACKET_SIZE_FALLBACK
610
+ );
611
+ const pacingDelayMs = resolveNobleBleWritePacingDelay(options);
562
612
 
563
613
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
564
614
  // macOS / Linux or already paired on Windows: direct write
@@ -569,14 +619,14 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
569
619
  `Write characteristic not available for ${deviceId}`
570
620
  );
571
621
  }
572
- if (toBuffer.length <= BLE_PACKET_SIZE) {
573
- await wait(UNIFIED_WRITE_DELAY);
574
- await writeCharacteristicWithAck(deviceId, writeCharacteristic, toBuffer);
622
+ if (toBuffer.length <= packetCapacity) {
623
+ if (pacingDelayMs > 0) await wait(pacingDelayMs);
624
+ await writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, toBuffer);
575
625
  return;
576
626
  }
577
627
  // chunked
578
628
  for (let offset = 0; offset < toBuffer.length; ) {
579
- const chunkSize = Math.min(BLE_PACKET_SIZE, toBuffer.length - offset);
629
+ const chunkSize = Math.min(packetCapacity, toBuffer.length - offset);
580
630
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
581
631
  offset += chunkSize;
582
632
  const latest = doGetWriteCharacteristic();
@@ -586,23 +636,23 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
586
636
  `Write characteristic not available for ${deviceId}`
587
637
  );
588
638
  }
589
- await writeCharacteristicWithAck(deviceId, latest, chunk);
590
- if (offset < toBuffer.length) {
591
- await wait(UNIFIED_WRITE_DELAY);
639
+ await writeCharacteristicWithoutResponse(deviceId, latest, chunk);
640
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
641
+ await wait(pacingDelayMs);
592
642
  }
593
643
  }
594
644
  return;
595
645
  }
596
646
 
597
647
  // Windows unpaired path: use loop
598
- if (toBuffer.length <= BLE_PACKET_SIZE) {
599
- await wait(UNIFIED_WRITE_DELAY);
648
+ if (toBuffer.length <= packetCapacity) {
649
+ if (pacingDelayMs > 0) await wait(pacingDelayMs);
600
650
  await attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, toBuffer, 'single');
601
651
  return;
602
652
  }
603
653
  // chunked loop
604
654
  for (let offset = 0, idx = 0; offset < toBuffer.length; idx++) {
605
- const chunkSize = Math.min(BLE_PACKET_SIZE, toBuffer.length - offset);
655
+ const chunkSize = Math.min(packetCapacity, toBuffer.length - offset);
606
656
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
607
657
  offset += chunkSize;
608
658
  await attemptWindowsWriteUntilPaired(
@@ -611,8 +661,8 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
611
661
  chunk,
612
662
  `chunk-${idx + 1}`
613
663
  );
614
- if (offset < toBuffer.length) {
615
- await wait(UNIFIED_WRITE_DELAY);
664
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
665
+ await wait(pacingDelayMs);
616
666
  }
617
667
  }
618
668
  }
@@ -835,6 +885,7 @@ function getDevice(deviceId: string): DeviceInfo | null {
835
885
  id: peripheral.id,
836
886
  name: deviceName,
837
887
  state: peripheral.state || 'disconnected',
888
+ ...(typeof peripheral.mtu === 'number' ? { mtu: peripheral.mtu } : {}),
838
889
  };
839
890
  }
840
891
 
@@ -847,6 +898,7 @@ function getDevice(deviceId: string): DeviceInfo | null {
847
898
  id: connectedPeripheral.id,
848
899
  name: deviceName,
849
900
  state: connectedPeripheral.state || 'connected',
901
+ ...(typeof connectedPeripheral.mtu === 'number' ? { mtu: connectedPeripheral.mtu } : {}),
850
902
  };
851
903
  }
852
904
 
@@ -1580,8 +1632,13 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
1580
1632
  // Handle write request
1581
1633
  ipcMain.handle(
1582
1634
  EOneKeyBleMessageKeys.NOBLE_BLE_WRITE,
1583
- async (_event: IpcMainInvokeEvent, deviceId: string, hexData: string) => {
1584
- await transmitHexDataToDevice(deviceId, hexData);
1635
+ async (
1636
+ _event: IpcMainInvokeEvent,
1637
+ deviceId: string,
1638
+ hexData: string,
1639
+ options?: NobleBleWriteOptions
1640
+ ) => {
1641
+ await transmitHexDataToDevice(deviceId, hexData, options);
1585
1642
  }
1586
1643
  );
1587
1644
 
@@ -4,15 +4,20 @@
4
4
  */
5
5
 
6
6
  // Noble BLE API interface - core BLE functionality
7
+ export interface NobleBleWriteOptions {
8
+ pacingDelayMs?: number;
9
+ }
10
+
7
11
  export interface NobleBleAPI {
8
12
  enumerate: () => Promise<{ id: string; name: string }[]>;
9
- getDevice: (uuid: string) => Promise<{ id: string; name: string } | null>;
13
+ getDevice: (uuid: string) => Promise<{ id: string; name: string; mtu?: number } | null>;
10
14
  connect: (uuid: string) => Promise<void>;
11
15
  disconnect: (uuid: string) => Promise<void>;
12
16
  subscribe: (uuid: string) => Promise<void>;
13
17
  unsubscribe: (uuid: string) => Promise<void>;
14
- write: (uuid: string, data: string) => Promise<void>;
18
+ write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
15
19
  onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
20
+ onMtuChanged?: (callback: (device: { id: string; mtu: number }) => void) => () => void;
16
21
  onDeviceDisconnected: (callback: (device: { id: string; name: string }) => void) => () => void;
17
22
  checkAvailability: () => Promise<{
18
23
  available: boolean;
@@ -11,6 +11,7 @@ export interface DeviceInfo extends OneKeyDeviceInfoBase {
11
11
  id: string;
12
12
  name: string;
13
13
  state: string;
14
+ mtu?: number;
14
15
  }
15
16
 
16
17
  // Characteristic pair interface