@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.
- package/dist/{index-bd1cfdb6.js → index-4f7dc0a5.js} +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-7ac9c43d.js → noble-ble-handler-3bd9cd3e.js} +22 -4
- package/dist/noble-ble-handler.d.ts +2 -0
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/dist/types/desktop-api.d.ts +4 -1
- package/dist/types/desktop-api.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/noble-ble-handler.test.ts +8 -0
- package/src/index.ts +1 -1
- package/src/noble-ble-handler.ts +29 -3
- package/src/types/desktop-api.ts +5 -1
|
@@ -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-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 };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
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;
|
|
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;
|
|
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.
|
|
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.
|
|
29
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
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": "
|
|
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');
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -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(
|
|
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 (
|
|
1615
|
-
|
|
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
|
|
package/src/types/desktop-api.ts
CHANGED
|
@@ -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;
|