@onekeyfe/hd-transport-electron 1.2.0-alpha.71 → 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.
@@ -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-7ac9c43d.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
@@ -31,6 +31,9 @@ interface Logger {
31
31
  * Desktop API types for Electron preload script
32
32
  * These types define the core interface for Noble BLE communication
33
33
  */
34
+ interface NobleBleWriteOptions {
35
+ pacingDelayMs?: number;
36
+ }
34
37
  interface NobleBleAPI {
35
38
  enumerate: () => Promise<{
36
39
  id: string;
@@ -45,7 +48,7 @@ interface NobleBleAPI {
45
48
  disconnect: (uuid: string) => Promise<void>;
46
49
  subscribe: (uuid: string) => Promise<void>;
47
50
  unsubscribe: (uuid: string) => Promise<void>;
48
- write: (uuid: string, data: string) => Promise<void>;
51
+ write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
49
52
  onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
50
53
  onMtuChanged?: (callback: (device: {
51
54
  id: string;
@@ -68,4 +71,4 @@ interface DesktopAPI {
68
71
 
69
72
  declare function initNobleBleSupport(webContents: WebContents): Promise<void>;
70
73
 
71
- 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-bd1cfdb6.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-bd1cfdb6.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
 
@@ -118,11 +118,17 @@ const SERVICE_DISCOVERY_TIMEOUT = 10000;
118
118
  const BLE_CLEANUP_TIMEOUT = 250;
119
119
  const BLE_PACKET_SIZE_FALLBACK = 192;
120
120
  const BLE_PACKET_SIZE_MAXIMUM = 244;
121
+ const DEFAULT_WRITE_PACING_DELAY_MS = 5;
121
122
  const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 };
122
123
  const IS_WINDOWS = process.platform === 'win32';
123
124
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
124
125
  /status:\s*3/i,
125
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
+ }
126
132
  function isOneKeyPeripheral(peripheral) {
127
133
  var _a, _b;
128
134
  const serviceUuids = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids;
@@ -466,7 +472,7 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
466
472
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `No response observed after ${RETRY_CONFIG.MAX_ATTEMPTS} writes: ${deviceId}`);
467
473
  });
468
474
  }
469
- function transmitHexDataToDevice(deviceId, hexData) {
475
+ function transmitHexDataToDevice(deviceId, hexData, options) {
470
476
  return index.__awaiter(this, void 0, void 0, function* () {
471
477
  const characteristics = deviceCharacteristics.get(deviceId);
472
478
  const peripheral = connectedDevices.get(deviceId);
@@ -476,12 +482,15 @@ function transmitHexDataToDevice(deviceId, hexData) {
476
482
  const toBuffer = Buffer.from(hexData, 'hex');
477
483
  const doGetWriteCharacteristic = () => { var _a; return (_a = deviceCharacteristics.get(deviceId)) === null || _a === void 0 ? void 0 : _a.write; };
478
484
  const packetCapacity = resolveBlePacketCapacity(peripheral.mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
485
+ const pacingDelayMs = resolveNobleBleWritePacingDelay(options);
479
486
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
480
487
  const writeCharacteristic = doGetWriteCharacteristic();
481
488
  if (!writeCharacteristic) {
482
489
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Write characteristic not available for ${deviceId}`);
483
490
  }
484
491
  if (toBuffer.length <= packetCapacity) {
492
+ if (pacingDelayMs > 0)
493
+ yield hdShared.wait(pacingDelayMs);
485
494
  yield writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, toBuffer);
486
495
  return;
487
496
  }
@@ -494,10 +503,15 @@ function transmitHexDataToDevice(deviceId, hexData) {
494
503
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, `Write characteristic not available for ${deviceId}`);
495
504
  }
496
505
  yield writeCharacteristicWithoutResponse(deviceId, latest, chunk);
506
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
507
+ yield hdShared.wait(pacingDelayMs);
508
+ }
497
509
  }
498
510
  return;
499
511
  }
500
512
  if (toBuffer.length <= packetCapacity) {
513
+ if (pacingDelayMs > 0)
514
+ yield hdShared.wait(pacingDelayMs);
501
515
  yield attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, toBuffer, 'single');
502
516
  return;
503
517
  }
@@ -506,6 +520,9 @@ function transmitHexDataToDevice(deviceId, hexData) {
506
520
  const chunk = toBuffer.subarray(offset, offset + chunkSize);
507
521
  offset += chunkSize;
508
522
  yield attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, chunk, `chunk-${idx + 1}`);
523
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
524
+ yield hdShared.wait(pacingDelayMs);
525
+ }
509
526
  }
510
527
  });
511
528
  }
@@ -1166,8 +1183,8 @@ function setupNobleBleHandlers(webContents) {
1166
1183
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_DISCONNECT, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
1167
1184
  yield disconnectDevice(deviceId);
1168
1185
  }));
1169
- ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_WRITE, (_event, deviceId, hexData) => index.__awaiter(this, void 0, void 0, function* () {
1170
- 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);
1171
1188
  }));
1172
1189
  ipcMain.handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_SUBSCRIBE, (_event, deviceId) => index.__awaiter(this, void 0, void 0, function* () {
1173
1190
  yield subscribeNotifications(deviceId, (data) => {
@@ -1237,4 +1254,5 @@ function setupNobleBleHandlers(webContents) {
1237
1254
  }
1238
1255
  }
1239
1256
 
1257
+ exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
1240
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":"AA+BA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AA++ChE,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;
@@ -12,7 +15,7 @@ export interface NobleBleAPI {
12
15
  disconnect: (uuid: string) => Promise<void>;
13
16
  subscribe: (uuid: string) => Promise<void>;
14
17
  unsubscribe: (uuid: string) => Promise<void>;
15
- write: (uuid: string, data: string) => Promise<void>;
18
+ write: (uuid: string, data: string, options?: NobleBleWriteOptions) => Promise<void>;
16
19
  onNotification: (callback: (deviceId: string, data: string) => void) => () => void;
17
20
  onMtuChanged?: (callback: (device: {
18
21
  id: 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,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,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,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"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.2.0-alpha.71",
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.71",
29
- "@onekeyfe/hd-shared": "1.2.0-alpha.71",
30
- "@onekeyfe/hd-transport": "1.2.0-alpha.71",
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": "c58f39f2e5c54a98faa8844cd8b6b0453a17d80c"
39
+ "gitHead": "76311ccd3753eca8e492b5324f93542bd38525ba"
40
40
  }
@@ -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
 
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');
@@ -31,6 +31,7 @@ import {
31
31
 
32
32
  import type { IpcMainInvokeEvent, WebContents } from 'electron';
33
33
  import type { Characteristic, Peripheral, Service } from '@stoprocent/noble';
34
+ import type { NobleBleWriteOptions } from './types/desktop-api';
34
35
  import type { CharacteristicPair, DeviceInfo, Logger, NobleModule } from './types/noble-extended';
35
36
 
36
37
  // Noble will be dynamically imported to avoid bundling issues
@@ -93,12 +94,19 @@ const BLE_CLEANUP_TIMEOUT = 250;
93
94
  // Write-related constants
94
95
  const BLE_PACKET_SIZE_FALLBACK = 192;
95
96
  const BLE_PACKET_SIZE_MAXIMUM = 244;
97
+ const DEFAULT_WRITE_PACING_DELAY_MS = 5;
96
98
  const RETRY_CONFIG = { MAX_ATTEMPTS: 15, WRITE_TIMEOUT: 2000 } as const;
97
99
  const IS_WINDOWS = process.platform === 'win32';
98
100
  const ABORTABLE_WRITE_ERROR_PATTERNS = [
99
101
  /status:\s*3/i, // Windows pairing cancelled / GATT write failed
100
102
  ];
101
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
+
102
110
  function isOneKeyPeripheral(peripheral: Peripheral) {
103
111
  const serviceUuids = peripheral.advertisement?.serviceUuids;
104
112
  const localName = peripheral.advertisement?.localName;
@@ -579,7 +587,11 @@ async function attemptWindowsWriteUntilPaired(
579
587
  );
580
588
  }
581
589
 
582
- 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> {
583
595
  const characteristics = deviceCharacteristics.get(deviceId);
584
596
  const peripheral = connectedDevices.get(deviceId);
585
597
  if (!peripheral || !characteristics) {
@@ -596,6 +608,7 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
596
608
  BLE_PACKET_SIZE_MAXIMUM,
597
609
  BLE_PACKET_SIZE_FALLBACK
598
610
  );
611
+ const pacingDelayMs = resolveNobleBleWritePacingDelay(options);
599
612
 
600
613
  if (!IS_WINDOWS || pairedDevices.has(deviceId)) {
601
614
  // macOS / Linux or already paired on Windows: direct write
@@ -607,6 +620,7 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
607
620
  );
608
621
  }
609
622
  if (toBuffer.length <= packetCapacity) {
623
+ if (pacingDelayMs > 0) await wait(pacingDelayMs);
610
624
  await writeCharacteristicWithoutResponse(deviceId, writeCharacteristic, toBuffer);
611
625
  return;
612
626
  }
@@ -623,12 +637,16 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
623
637
  );
624
638
  }
625
639
  await writeCharacteristicWithoutResponse(deviceId, latest, chunk);
640
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
641
+ await wait(pacingDelayMs);
642
+ }
626
643
  }
627
644
  return;
628
645
  }
629
646
 
630
647
  // Windows unpaired path: use loop
631
648
  if (toBuffer.length <= packetCapacity) {
649
+ if (pacingDelayMs > 0) await wait(pacingDelayMs);
632
650
  await attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, toBuffer, 'single');
633
651
  return;
634
652
  }
@@ -643,6 +661,9 @@ async function transmitHexDataToDevice(deviceId: string, hexData: string): Promi
643
661
  chunk,
644
662
  `chunk-${idx + 1}`
645
663
  );
664
+ if (offset < toBuffer.length && pacingDelayMs > 0) {
665
+ await wait(pacingDelayMs);
666
+ }
646
667
  }
647
668
  }
648
669
 
@@ -1611,8 +1632,13 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
1611
1632
  // Handle write request
1612
1633
  ipcMain.handle(
1613
1634
  EOneKeyBleMessageKeys.NOBLE_BLE_WRITE,
1614
- async (_event: IpcMainInvokeEvent, deviceId: string, hexData: string) => {
1615
- 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);
1616
1642
  }
1617
1643
  );
1618
1644
 
@@ -4,6 +4,10 @@
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
13
  getDevice: (uuid: string) => Promise<{ id: string; name: string; mtu?: number } | null>;
@@ -11,7 +15,7 @@ export interface NobleBleAPI {
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;
16
20
  onMtuChanged?: (callback: (device: { id: string; mtu: number }) => void) => () => void;
17
21
  onDeviceDisconnected: (callback: (device: { id: string; name: string }) => void) => () => void;