@onekeyfe/hd-transport-web-device 1.2.0-alpha.1 → 1.2.0-alpha.10
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/__tests__/electron-ble-transport.test.ts +102 -8
- package/dist/electron-ble-transport.d.ts +4 -4
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +116 -123
- package/dist/webusb.d.ts +1 -0
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +102 -113
- package/src/webusb.ts +15 -11
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import transport, { PROTOCOL_V2_CHANNEL_BLE_UART, bytesToHex } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
3
|
|
|
3
4
|
import ElectronBleTransport from '../src/electron-ble-transport';
|
|
4
5
|
|
|
@@ -28,21 +29,25 @@ const protocolV1Schema = {
|
|
|
28
29
|
|
|
29
30
|
const protocolV2Schema = {
|
|
30
31
|
nested: {
|
|
31
|
-
|
|
32
|
+
ProtocolInfoRequest: {
|
|
32
33
|
fields: {},
|
|
33
34
|
},
|
|
34
|
-
|
|
35
|
+
ProtocolInfo: {
|
|
35
36
|
fields: {
|
|
36
|
-
|
|
37
|
+
version: {
|
|
37
38
|
type: 'uint32',
|
|
38
39
|
id: 1,
|
|
39
40
|
},
|
|
40
|
-
|
|
41
|
+
supported_messages: {
|
|
42
|
+
rule: 'repeated',
|
|
41
43
|
type: 'uint32',
|
|
42
44
|
id: 2,
|
|
45
|
+
options: {
|
|
46
|
+
packed: false,
|
|
47
|
+
},
|
|
43
48
|
},
|
|
44
|
-
|
|
45
|
-
type: '
|
|
49
|
+
protobuf_definition: {
|
|
50
|
+
type: 'string',
|
|
46
51
|
id: 3,
|
|
47
52
|
},
|
|
48
53
|
},
|
|
@@ -65,8 +70,8 @@ const protocolV2Schema = {
|
|
|
65
70
|
},
|
|
66
71
|
MessageType: {
|
|
67
72
|
values: {
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
MessageType_ProtocolInfoRequest: 60200,
|
|
74
|
+
MessageType_ProtocolInfo: 60201,
|
|
70
75
|
MessageType_Ping: 60206,
|
|
71
76
|
MessageType_Success: 60207,
|
|
72
77
|
},
|
|
@@ -241,6 +246,95 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
241
246
|
);
|
|
242
247
|
expect(nobleBle.write).toHaveBeenCalledTimes(1);
|
|
243
248
|
expect(transport.getProtocolType(device.id)).toBe('V2');
|
|
249
|
+
await expect(transport.call(device.id, 'Ping', { message: 'after-probe' })).resolves.toEqual({
|
|
250
|
+
type: 'Success',
|
|
251
|
+
message: { message: 'ok' },
|
|
252
|
+
});
|
|
253
|
+
const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
|
|
254
|
+
Number.parseInt(hex.slice(12, 14), 16)
|
|
255
|
+
);
|
|
256
|
+
expect(sentSeqs).toEqual([1, 2]);
|
|
257
|
+
} finally {
|
|
258
|
+
await transport.release(device.id);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('rejects the active Protocol V2 reader when pairing is rejected', async () => {
|
|
263
|
+
const device = { id: 'pairing-rejected-pro2-id', name: 'OneKey Pro 2' };
|
|
264
|
+
const nobleBle = createNobleBle(device);
|
|
265
|
+
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
266
|
+
let pairingRejected = false;
|
|
267
|
+
const probeResponse = ProtocolV2.encodeFrame(
|
|
268
|
+
schemas,
|
|
269
|
+
'Success',
|
|
270
|
+
{ message: 'ok' },
|
|
271
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
275
|
+
notificationHandler = handler;
|
|
276
|
+
return jest.fn();
|
|
277
|
+
});
|
|
278
|
+
nobleBle.write.mockImplementation(() => {
|
|
279
|
+
setTimeout(
|
|
280
|
+
() =>
|
|
281
|
+
notificationHandler?.(
|
|
282
|
+
device.id,
|
|
283
|
+
pairingRejected ? 'PAIRING_REJECTED' : bytesToHex(probeResponse)
|
|
284
|
+
),
|
|
285
|
+
0
|
|
286
|
+
);
|
|
287
|
+
return Promise.resolve();
|
|
288
|
+
});
|
|
289
|
+
const transport = configureTransport(nobleBle);
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
await transport.acquire({ uuid: device.id });
|
|
293
|
+
pairingRejected = true;
|
|
294
|
+
|
|
295
|
+
await expect(
|
|
296
|
+
transport.call(device.id, 'Ping', { message: 'pairing' }, { timeoutMs: 50 })
|
|
297
|
+
).rejects.toMatchObject({ errorCode: HardwareErrorCode.BleDeviceBondedCanceled });
|
|
298
|
+
} finally {
|
|
299
|
+
await transport.release(device.id);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test('rebuilds the active link when Core acquires the same device again', async () => {
|
|
304
|
+
const device = { id: 'repeated-acquire-pro2-id', name: 'OneKey Pro 2' };
|
|
305
|
+
const nobleBle = createNobleBle(device);
|
|
306
|
+
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
307
|
+
const response = ProtocolV2.encodeFrame(
|
|
308
|
+
schemas,
|
|
309
|
+
'Success',
|
|
310
|
+
{ message: 'ok' },
|
|
311
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
315
|
+
notificationHandler = handler;
|
|
316
|
+
return jest.fn();
|
|
317
|
+
});
|
|
318
|
+
nobleBle.write.mockImplementation(() => {
|
|
319
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
|
|
320
|
+
return Promise.resolve();
|
|
321
|
+
});
|
|
322
|
+
const transport = configureTransport(nobleBle);
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
await transport.acquire({ uuid: device.id });
|
|
326
|
+
await transport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
327
|
+
await expect(
|
|
328
|
+
transport.call(device.id, 'Ping', { message: 'after-reacquire' })
|
|
329
|
+
).resolves.toEqual({
|
|
330
|
+
type: 'Success',
|
|
331
|
+
message: { message: 'ok' },
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
|
|
335
|
+
Number.parseInt(hex.slice(12, 14), 16)
|
|
336
|
+
);
|
|
337
|
+
expect(sentSeqs).toEqual([1, 2]);
|
|
244
338
|
} finally {
|
|
245
339
|
await transport.release(device.id);
|
|
246
340
|
}
|
|
@@ -28,8 +28,7 @@ export default class ElectronBleTransport {
|
|
|
28
28
|
private v2Assemblers;
|
|
29
29
|
private v2FrameQueues;
|
|
30
30
|
private v2FramePromises;
|
|
31
|
-
private
|
|
32
|
-
private nextProtocolV2CallToken;
|
|
31
|
+
private protocolV2Links;
|
|
33
32
|
private notificationCleanups;
|
|
34
33
|
private disconnectCleanups;
|
|
35
34
|
private notificationTokens;
|
|
@@ -62,19 +61,20 @@ export default class ElectronBleTransport {
|
|
|
62
61
|
private probeProtocolV1;
|
|
63
62
|
private probeProtocolV2;
|
|
64
63
|
private writeWithChunking;
|
|
65
|
-
private
|
|
64
|
+
private writeOnce;
|
|
66
65
|
private handleNotification;
|
|
67
66
|
private handleProtocolV2Notification;
|
|
68
67
|
private getProtocolV2FrameQueue;
|
|
69
68
|
private resolveProtocolV2Frame;
|
|
70
69
|
private rejectAllProtocolV2Frames;
|
|
71
70
|
private resetProtocolV2Frames;
|
|
72
|
-
private
|
|
71
|
+
private rejectProtocolV2Frames;
|
|
73
72
|
private readProtocolV2Frame;
|
|
74
73
|
private handleProtocolV1Notification;
|
|
75
74
|
call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<import("@onekeyfe/hd-transport").MessageFromOneKey>;
|
|
76
75
|
private callProtocolV1;
|
|
77
76
|
private callProtocolV2;
|
|
77
|
+
private createProtocolV2Adapter;
|
|
78
78
|
private processProtocolV1Notification;
|
|
79
79
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
80
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAevC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;
|
|
1
|
+
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAevC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAqCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAQ7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IA0F9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA+BlB,cAAc;YAuEd,cAAc;IA6B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ declare class WebUsbTransport {
|
|
|
53
53
|
private getTransferInData;
|
|
54
54
|
private toArrayBuffer;
|
|
55
55
|
private transferOutWithRetry;
|
|
56
|
+
private transferOutOnce;
|
|
56
57
|
private transferInWithRetry;
|
|
57
58
|
private resetConnectionAfterProbe;
|
|
58
59
|
private withProtocolReadTimeout;
|
|
@@ -92,8 +93,7 @@ declare class ElectronBleTransport {
|
|
|
92
93
|
private v2Assemblers;
|
|
93
94
|
private v2FrameQueues;
|
|
94
95
|
private v2FramePromises;
|
|
95
|
-
private
|
|
96
|
-
private nextProtocolV2CallToken;
|
|
96
|
+
private protocolV2Links;
|
|
97
97
|
private notificationCleanups;
|
|
98
98
|
private disconnectCleanups;
|
|
99
99
|
private notificationTokens;
|
|
@@ -126,19 +126,20 @@ declare class ElectronBleTransport {
|
|
|
126
126
|
private probeProtocolV1;
|
|
127
127
|
private probeProtocolV2;
|
|
128
128
|
private writeWithChunking;
|
|
129
|
-
private
|
|
129
|
+
private writeOnce;
|
|
130
130
|
private handleNotification;
|
|
131
131
|
private handleProtocolV2Notification;
|
|
132
132
|
private getProtocolV2FrameQueue;
|
|
133
133
|
private resolveProtocolV2Frame;
|
|
134
134
|
private rejectAllProtocolV2Frames;
|
|
135
135
|
private resetProtocolV2Frames;
|
|
136
|
-
private
|
|
136
|
+
private rejectProtocolV2Frames;
|
|
137
137
|
private readProtocolV2Frame;
|
|
138
138
|
private handleProtocolV1Notification;
|
|
139
139
|
call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
|
|
140
140
|
private callProtocolV1;
|
|
141
141
|
private callProtocolV2;
|
|
142
|
+
private createProtocolV2Adapter;
|
|
142
143
|
private processProtocolV1Notification;
|
|
143
144
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
144
145
|
}
|
package/dist/index.js
CHANGED
|
@@ -420,19 +420,11 @@ class WebUsbTransport {
|
|
|
420
420
|
return copied.buffer;
|
|
421
421
|
}
|
|
422
422
|
transferOutWithRetry(path, packet) {
|
|
423
|
-
var _a;
|
|
424
423
|
return __awaiter(this, void 0, void 0, function* () {
|
|
425
424
|
let lastError;
|
|
426
425
|
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt += 1) {
|
|
427
426
|
try {
|
|
428
|
-
|
|
429
|
-
if (!device.opened) {
|
|
430
|
-
yield this.connect(path, false);
|
|
431
|
-
}
|
|
432
|
-
const endpoints = this.deviceEndpoints.get(path);
|
|
433
|
-
const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
434
|
-
const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
435
|
-
yield device.transferOut(endpointOut, transferBuffer);
|
|
427
|
+
yield this.transferOutOnce(path, packet);
|
|
436
428
|
return;
|
|
437
429
|
}
|
|
438
430
|
catch (error) {
|
|
@@ -453,6 +445,19 @@ class WebUsbTransport {
|
|
|
453
445
|
throw lastError;
|
|
454
446
|
});
|
|
455
447
|
}
|
|
448
|
+
transferOutOnce(path, packet) {
|
|
449
|
+
var _a;
|
|
450
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
451
|
+
const device = yield this.findDevice(path);
|
|
452
|
+
if (!device.opened) {
|
|
453
|
+
yield this.connect(path, false);
|
|
454
|
+
}
|
|
455
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
456
|
+
const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
457
|
+
const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
458
|
+
yield device.transferOut(endpointOut, transferBuffer);
|
|
459
|
+
});
|
|
460
|
+
}
|
|
456
461
|
transferInWithRetry(path, length, cancelToken) {
|
|
457
462
|
var _a;
|
|
458
463
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -642,7 +647,7 @@ class WebUsbTransport {
|
|
|
642
647
|
protocolV2: this.messagesV2,
|
|
643
648
|
},
|
|
644
649
|
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
645
|
-
writeFrame: (frame) => this.
|
|
650
|
+
writeFrame: (frame) => this.transferOutOnce(path, frame),
|
|
646
651
|
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
647
652
|
logger: this.Log,
|
|
648
653
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
@@ -761,8 +766,6 @@ function inferProtocolHintFromDeviceName(name) {
|
|
|
761
766
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
762
767
|
const BLE_PACKET_SIZE = 192;
|
|
763
768
|
const BLE_WRITE_DELAY_MS = 5;
|
|
764
|
-
const BLE_WRITE_MAX_RETRIES = 3;
|
|
765
|
-
const BLE_WRITE_RETRY_DELAY_MS = 300;
|
|
766
769
|
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
767
770
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
768
771
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
@@ -778,8 +781,27 @@ class ElectronBleTransport {
|
|
|
778
781
|
this.v2Assemblers = new Map();
|
|
779
782
|
this.v2FrameQueues = new Map();
|
|
780
783
|
this.v2FramePromises = new Map();
|
|
781
|
-
this.
|
|
782
|
-
|
|
784
|
+
this.protocolV2Links = new transport.ProtocolV2LinkManager({
|
|
785
|
+
getSchemas: () => {
|
|
786
|
+
if (!this._messages || !this._messagesV2) {
|
|
787
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
protocolV1: this._messages,
|
|
791
|
+
protocolV2: this._messagesV2,
|
|
792
|
+
};
|
|
793
|
+
},
|
|
794
|
+
classifyError: () => 'link-fatal',
|
|
795
|
+
onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
|
|
796
|
+
var _a, _b;
|
|
797
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
798
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
799
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
800
|
+
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
801
|
+
yield this.release(uuid);
|
|
802
|
+
}
|
|
803
|
+
}),
|
|
804
|
+
});
|
|
783
805
|
this.notificationCleanups = new Map();
|
|
784
806
|
this.disconnectCleanups = new Map();
|
|
785
807
|
this.notificationTokens = new Map();
|
|
@@ -815,15 +837,14 @@ class ElectronBleTransport {
|
|
|
815
837
|
throw error;
|
|
816
838
|
}
|
|
817
839
|
cleanupDeviceState(deviceId) {
|
|
818
|
-
|
|
840
|
+
this.protocolV2Links
|
|
841
|
+
.invalidateLink(deviceId, 'Electron BLE device state cleaned')
|
|
842
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
|
|
819
843
|
this.connectedDevices.delete(deviceId);
|
|
820
844
|
this.deviceProtocol.delete(deviceId);
|
|
821
845
|
this.v1Buffers.delete(deviceId);
|
|
822
846
|
this.v2Assemblers.delete(deviceId);
|
|
823
847
|
this.resetProtocolV2Frames(deviceId);
|
|
824
|
-
if (((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === deviceId) {
|
|
825
|
-
this.activeProtocolV2Call = null;
|
|
826
|
-
}
|
|
827
848
|
this.notificationTokens.delete(deviceId);
|
|
828
849
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
829
850
|
if (notifyCleanup) {
|
|
@@ -852,6 +873,9 @@ class ElectronBleTransport {
|
|
|
852
873
|
configureProtocolV2(signedData) {
|
|
853
874
|
var _a;
|
|
854
875
|
this._messagesV2 = parseConfigure(signedData);
|
|
876
|
+
this.protocolV2Links
|
|
877
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
878
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
855
879
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V2 schema configured');
|
|
856
880
|
}
|
|
857
881
|
listen() {
|
|
@@ -890,12 +914,14 @@ class ElectronBleTransport {
|
|
|
890
914
|
if (!uuid) {
|
|
891
915
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
892
916
|
}
|
|
917
|
+
if (this.connectedDevices.has(uuid)) {
|
|
918
|
+
yield this.release(uuid);
|
|
919
|
+
}
|
|
893
920
|
if (forceCleanRunPromise && this.runPromise) {
|
|
894
921
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
895
922
|
this.runPromise.reject(error);
|
|
896
923
|
this.rejectAllProtocolV2Frames(error);
|
|
897
924
|
this.runPromise = null;
|
|
898
|
-
this.activeProtocolV2Call = null;
|
|
899
925
|
}
|
|
900
926
|
try {
|
|
901
927
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
@@ -963,6 +989,7 @@ class ElectronBleTransport {
|
|
|
963
989
|
var _a, _b;
|
|
964
990
|
return __awaiter(this, void 0, void 0, function* () {
|
|
965
991
|
try {
|
|
992
|
+
yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
966
993
|
if (this.connectedDevices.has(id)) {
|
|
967
994
|
if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
|
|
968
995
|
yield window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -1036,14 +1063,12 @@ class ElectronBleTransport {
|
|
|
1036
1063
|
});
|
|
1037
1064
|
}
|
|
1038
1065
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1039
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
1066
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1040
1067
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1068
|
+
yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
|
|
1041
1069
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1042
1070
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1043
1071
|
this.resetProtocolV2Frames(uuid);
|
|
1044
|
-
if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
|
|
1045
|
-
this.activeProtocolV2Call = null;
|
|
1046
|
-
}
|
|
1047
1072
|
if (this.runPromise) {
|
|
1048
1073
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1049
1074
|
this.runPromise.reject(error);
|
|
@@ -1056,16 +1081,16 @@ class ElectronBleTransport {
|
|
|
1056
1081
|
}
|
|
1057
1082
|
this.notificationTokens.delete(uuid);
|
|
1058
1083
|
try {
|
|
1059
|
-
yield ((
|
|
1084
|
+
yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
|
|
1060
1085
|
}
|
|
1061
1086
|
catch (error) {
|
|
1062
|
-
(
|
|
1087
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1063
1088
|
}
|
|
1064
1089
|
try {
|
|
1065
|
-
yield ((
|
|
1090
|
+
yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.subscribe(uuid));
|
|
1066
1091
|
}
|
|
1067
1092
|
catch (error) {
|
|
1068
|
-
(
|
|
1093
|
+
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1069
1094
|
throw error;
|
|
1070
1095
|
}
|
|
1071
1096
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
@@ -1120,52 +1145,40 @@ class ElectronBleTransport {
|
|
|
1120
1145
|
const totalBytes = hexData.length / 2;
|
|
1121
1146
|
if (totalBytes <= BLE_PACKET_SIZE) {
|
|
1122
1147
|
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1123
|
-
yield this.
|
|
1148
|
+
yield this.writeOnce(uuid, hexData);
|
|
1124
1149
|
return;
|
|
1125
1150
|
}
|
|
1126
1151
|
for (let offset = 0; offset < hexData.length;) {
|
|
1127
1152
|
const chunkHexLen = Math.min(BLE_PACKET_SIZE * 2, hexData.length - offset);
|
|
1128
1153
|
const chunkHex = hexData.substring(offset, offset + chunkHexLen);
|
|
1129
1154
|
offset += chunkHexLen;
|
|
1130
|
-
yield this.
|
|
1155
|
+
yield this.writeOnce(uuid, chunkHex);
|
|
1131
1156
|
if (offset < hexData.length) {
|
|
1132
1157
|
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1133
1158
|
}
|
|
1134
1159
|
}
|
|
1135
1160
|
});
|
|
1136
1161
|
}
|
|
1137
|
-
|
|
1138
|
-
var _a
|
|
1162
|
+
writeOnce(uuid, hexData) {
|
|
1163
|
+
var _a;
|
|
1139
1164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1140
|
-
let lastError;
|
|
1141
1165
|
const nobleBle = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle;
|
|
1142
1166
|
if (!nobleBle) {
|
|
1143
1167
|
throw new Error('Noble BLE API not available');
|
|
1144
1168
|
}
|
|
1145
|
-
|
|
1146
|
-
try {
|
|
1147
|
-
yield nobleBle.write(uuid, hexData);
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
catch (error) {
|
|
1151
|
-
lastError = error;
|
|
1152
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error(`[Electron BLE] write failed (attempt ${attempt}/${BLE_WRITE_MAX_RETRIES}):`, error);
|
|
1153
|
-
if (attempt < BLE_WRITE_MAX_RETRIES) {
|
|
1154
|
-
yield hdShared.wait(BLE_WRITE_RETRY_DELAY_MS);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write failed after ${BLE_WRITE_MAX_RETRIES} attempts: ${(_c = lastError === null || lastError === void 0 ? void 0 : lastError.message) !== null && _c !== void 0 ? _c : lastError}`);
|
|
1169
|
+
yield nobleBle.write(uuid, hexData);
|
|
1159
1170
|
});
|
|
1160
1171
|
}
|
|
1161
1172
|
handleNotification(deviceId, hexData) {
|
|
1162
1173
|
var _a, _b;
|
|
1163
1174
|
if (hexData === 'PAIRING_REJECTED') {
|
|
1164
1175
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
|
|
1165
|
-
|
|
1166
|
-
|
|
1176
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
|
|
1177
|
+
if (this.deviceProtocol.get(deviceId) === 'V2') {
|
|
1178
|
+
this.rejectProtocolV2Frames(deviceId, error);
|
|
1179
|
+
}
|
|
1180
|
+
else if (this.runPromise) {
|
|
1167
1181
|
this.runPromise.reject(error);
|
|
1168
|
-
this.rejectAllProtocolV2Frames(error);
|
|
1169
1182
|
}
|
|
1170
1183
|
return;
|
|
1171
1184
|
}
|
|
@@ -1181,13 +1194,8 @@ class ElectronBleTransport {
|
|
|
1181
1194
|
this.handleProtocolV1Notification(deviceId, hexData);
|
|
1182
1195
|
}
|
|
1183
1196
|
handleProtocolV2Notification(deviceId, hexData) {
|
|
1184
|
-
var _a
|
|
1197
|
+
var _a;
|
|
1185
1198
|
try {
|
|
1186
|
-
if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== deviceId) {
|
|
1187
|
-
(_b = this.v2Assemblers.get(deviceId)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
1188
|
-
this.resetProtocolV2Frames(deviceId);
|
|
1189
|
-
return;
|
|
1190
|
-
}
|
|
1191
1199
|
const bytes = transport.hexToBytes(hexData);
|
|
1192
1200
|
if (bytes.length === 0)
|
|
1193
1201
|
return;
|
|
@@ -1199,12 +1207,9 @@ class ElectronBleTransport {
|
|
|
1199
1207
|
}
|
|
1200
1208
|
}
|
|
1201
1209
|
catch (error) {
|
|
1202
|
-
(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
this.runPromise.reject(notifyError);
|
|
1206
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
1207
|
-
}
|
|
1210
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 notification error:', error);
|
|
1211
|
+
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1212
|
+
this.rejectProtocolV2Frames(deviceId, notifyError);
|
|
1208
1213
|
}
|
|
1209
1214
|
}
|
|
1210
1215
|
getProtocolV2FrameQueue(uuid) {
|
|
@@ -1235,9 +1240,13 @@ class ElectronBleTransport {
|
|
|
1235
1240
|
this.v2FrameQueues.delete(uuid);
|
|
1236
1241
|
this.v2FramePromises.delete(uuid);
|
|
1237
1242
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1243
|
+
rejectProtocolV2Frames(uuid, error) {
|
|
1244
|
+
this.v2FrameQueues.delete(uuid);
|
|
1245
|
+
const framePromise = this.v2FramePromises.get(uuid);
|
|
1246
|
+
if (framePromise) {
|
|
1247
|
+
this.v2FramePromises.delete(uuid);
|
|
1248
|
+
framePromise.reject(error);
|
|
1249
|
+
}
|
|
1241
1250
|
}
|
|
1242
1251
|
readProtocolV2Frame(uuid) {
|
|
1243
1252
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1360,76 +1369,60 @@ class ElectronBleTransport {
|
|
|
1360
1369
|
});
|
|
1361
1370
|
}
|
|
1362
1371
|
callProtocolV2(uuid, name, data, options) {
|
|
1363
|
-
var _a, _b
|
|
1372
|
+
var _a, _b;
|
|
1364
1373
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1365
1374
|
if (!this._messages || !this._messagesV2) {
|
|
1366
1375
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1367
1376
|
}
|
|
1368
|
-
const
|
|
1369
|
-
if (this.runPromise) {
|
|
1370
|
-
if (!forceRun) {
|
|
1371
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1372
|
-
}
|
|
1373
|
-
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1374
|
-
this.runPromise.reject(error);
|
|
1375
|
-
this.rejectAllProtocolV2Frames(error);
|
|
1376
|
-
this.runPromise = null;
|
|
1377
|
-
this.activeProtocolV2Call = null;
|
|
1378
|
-
}
|
|
1379
|
-
const runPromise = hdShared.createDeferred();
|
|
1380
|
-
runPromise.promise.catch(() => undefined);
|
|
1381
|
-
this.runPromise = runPromise;
|
|
1382
|
-
const callToken = this.nextProtocolV2CallToken++;
|
|
1383
|
-
this.activeProtocolV2Call = { uuid, token: callToken };
|
|
1384
|
-
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1385
|
-
this.resetProtocolV2Frames(uuid);
|
|
1386
|
-
let completed = false;
|
|
1387
|
-
const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
|
|
1377
|
+
const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : BLE_RESPONSE_TIMEOUT_MS });
|
|
1388
1378
|
try {
|
|
1389
|
-
|
|
1390
|
-
schemas: {
|
|
1391
|
-
protocolV1: this._messages,
|
|
1392
|
-
protocolV2: this._messagesV2,
|
|
1393
|
-
},
|
|
1394
|
-
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1395
|
-
writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
|
|
1396
|
-
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1397
|
-
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
1398
|
-
if (!(rxFrame instanceof Uint8Array)) {
|
|
1399
|
-
throw new Error('Response is not Uint8Array');
|
|
1400
|
-
}
|
|
1401
|
-
return rxFrame;
|
|
1402
|
-
}),
|
|
1403
|
-
logger: this.Log,
|
|
1404
|
-
logPrefix: 'ProtocolV2 BLE',
|
|
1405
|
-
createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
|
|
1406
|
-
});
|
|
1407
|
-
const result = yield session.call(name, data, callOptions);
|
|
1408
|
-
completed = true;
|
|
1409
|
-
return result;
|
|
1379
|
+
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
1410
1380
|
}
|
|
1411
1381
|
catch (e) {
|
|
1412
|
-
|
|
1413
|
-
(_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
|
|
1414
|
-
this.resetProtocolV2Frames(uuid);
|
|
1415
|
-
}
|
|
1416
|
-
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1382
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1417
1383
|
throw e;
|
|
1418
1384
|
}
|
|
1419
|
-
finally {
|
|
1420
|
-
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
1421
|
-
if (!completed) {
|
|
1422
|
-
(_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
|
|
1423
|
-
}
|
|
1424
|
-
this.resetProtocolV2Frames(uuid);
|
|
1425
|
-
this.activeProtocolV2Call = null;
|
|
1426
|
-
}
|
|
1427
|
-
if (this.runPromise === runPromise) {
|
|
1428
|
-
this.runPromise = null;
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
1385
|
});
|
|
1432
1386
|
}
|
|
1387
|
+
createProtocolV2Adapter(uuid) {
|
|
1388
|
+
var _a;
|
|
1389
|
+
const generation = (_a = this.notificationTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
1390
|
+
const assertCurrentGeneration = () => {
|
|
1391
|
+
if (this.notificationTokens.get(uuid) !== generation) {
|
|
1392
|
+
throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
return {
|
|
1396
|
+
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1397
|
+
generation,
|
|
1398
|
+
prepareCall: () => {
|
|
1399
|
+
var _a;
|
|
1400
|
+
assertCurrentGeneration();
|
|
1401
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1402
|
+
this.resetProtocolV2Frames(uuid);
|
|
1403
|
+
},
|
|
1404
|
+
writeFrame: (frame) => {
|
|
1405
|
+
assertCurrentGeneration();
|
|
1406
|
+
return this.writeWithChunking(uuid, transport.bytesToHex(frame));
|
|
1407
|
+
},
|
|
1408
|
+
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1409
|
+
assertCurrentGeneration();
|
|
1410
|
+
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
1411
|
+
if (!(rxFrame instanceof Uint8Array)) {
|
|
1412
|
+
throw new Error('Response is not Uint8Array');
|
|
1413
|
+
}
|
|
1414
|
+
return rxFrame;
|
|
1415
|
+
}),
|
|
1416
|
+
reset: (reason) => {
|
|
1417
|
+
var _a;
|
|
1418
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1419
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
1420
|
+
},
|
|
1421
|
+
logger: this.Log,
|
|
1422
|
+
logPrefix: 'ProtocolV2 BLE',
|
|
1423
|
+
createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1433
1426
|
processProtocolV1Notification(deviceId, hexData) {
|
|
1434
1427
|
try {
|
|
1435
1428
|
if (typeof hexData !== 'string') {
|
package/dist/webusb.d.ts
CHANGED
package/dist/webusb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqChC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,sBAAsB,CAA8C;IAG5E,OAAO,CAAC,eAAe,CAA2C;IAOlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAY7B,kBAAkB;IAmBlB,SAAS;IASf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAsCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6CtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YA0CpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;
|
|
1
|
+
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqChC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,sBAAsB,CAA8C;IAG5E,OAAO,CAAC,eAAe,CAA2C;IAOlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAY7B,kBAAkB;IAmBlB,SAAS;IASf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAsCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6CtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YA0CpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAyBzB,uBAAuB;YAoCvB,eAAe;YAcf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAkClB,cAAc;YAkCd,cAAc;YA4Cd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAiB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-web-device",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.10",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
23
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.10",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.10"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
27
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.10",
|
|
28
28
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
29
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "db8135cc75ab2d8b87efa14e28517da892c7aa34"
|
|
32
32
|
}
|
|
@@ -3,7 +3,7 @@ import transport, {
|
|
|
3
3
|
PROTOCOL_V1_MESSAGE_HEADER_SIZE,
|
|
4
4
|
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
5
5
|
ProtocolV2FrameAssembler,
|
|
6
|
-
|
|
6
|
+
ProtocolV2LinkManager,
|
|
7
7
|
bytesToHex,
|
|
8
8
|
hexToBytes,
|
|
9
9
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
@@ -72,8 +72,6 @@ const toBleDescriptor = (
|
|
|
72
72
|
|
|
73
73
|
const BLE_PACKET_SIZE = 192;
|
|
74
74
|
const BLE_WRITE_DELAY_MS = 5;
|
|
75
|
-
const BLE_WRITE_MAX_RETRIES = 3;
|
|
76
|
-
const BLE_WRITE_RETRY_DELAY_MS = 300;
|
|
77
75
|
const BLE_RESPONSE_TIMEOUT_MS = 30_000;
|
|
78
76
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
79
77
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
@@ -113,9 +111,26 @@ export default class ElectronBleTransport {
|
|
|
113
111
|
|
|
114
112
|
private v2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
|
|
115
113
|
|
|
116
|
-
private
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
private protocolV2Links = new ProtocolV2LinkManager<string>({
|
|
115
|
+
getSchemas: () => {
|
|
116
|
+
if (!this._messages || !this._messagesV2) {
|
|
117
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
protocolV1: this._messages,
|
|
121
|
+
protocolV2: this._messagesV2,
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
classifyError: () => 'link-fatal',
|
|
125
|
+
onLinkInvalidated: async (uuid, reason) => {
|
|
126
|
+
this.v2Assemblers.get(uuid)?.reset();
|
|
127
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
128
|
+
this.Log?.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
129
|
+
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
130
|
+
await this.release(uuid);
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
});
|
|
119
134
|
|
|
120
135
|
private notificationCleanups: Map<string, () => void> = new Map();
|
|
121
136
|
|
|
@@ -157,6 +172,9 @@ export default class ElectronBleTransport {
|
|
|
157
172
|
}
|
|
158
173
|
|
|
159
174
|
private cleanupDeviceState(deviceId: string): void {
|
|
175
|
+
this.protocolV2Links
|
|
176
|
+
.invalidateLink(deviceId, 'Electron BLE device state cleaned')
|
|
177
|
+
.catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
|
|
160
178
|
this.connectedDevices.delete(deviceId);
|
|
161
179
|
this.deviceProtocol.delete(deviceId);
|
|
162
180
|
// Keep deviceProtocolHints — it's inferred from device name (e.g. "Pro 2" → V2)
|
|
@@ -164,9 +182,6 @@ export default class ElectronBleTransport {
|
|
|
164
182
|
this.v1Buffers.delete(deviceId);
|
|
165
183
|
this.v2Assemblers.delete(deviceId);
|
|
166
184
|
this.resetProtocolV2Frames(deviceId);
|
|
167
|
-
if (this.activeProtocolV2Call?.uuid === deviceId) {
|
|
168
|
-
this.activeProtocolV2Call = null;
|
|
169
|
-
}
|
|
170
185
|
this.notificationTokens.delete(deviceId);
|
|
171
186
|
|
|
172
187
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
@@ -203,6 +218,9 @@ export default class ElectronBleTransport {
|
|
|
203
218
|
|
|
204
219
|
configureProtocolV2(signedData: any) {
|
|
205
220
|
this._messagesV2 = parseConfigure(signedData);
|
|
221
|
+
this.protocolV2Links
|
|
222
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
223
|
+
.catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
|
|
206
224
|
this.Log?.debug('[Electron BLE] Protocol V2 schema configured');
|
|
207
225
|
}
|
|
208
226
|
|
|
@@ -238,12 +256,15 @@ export default class ElectronBleTransport {
|
|
|
238
256
|
throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
|
|
239
257
|
}
|
|
240
258
|
|
|
259
|
+
if (this.connectedDevices.has(uuid)) {
|
|
260
|
+
await this.release(uuid);
|
|
261
|
+
}
|
|
262
|
+
|
|
241
263
|
if (forceCleanRunPromise && this.runPromise) {
|
|
242
264
|
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
243
265
|
this.runPromise.reject(error);
|
|
244
266
|
this.rejectAllProtocolV2Frames(error);
|
|
245
267
|
this.runPromise = null;
|
|
246
|
-
this.activeProtocolV2Call = null;
|
|
247
268
|
}
|
|
248
269
|
|
|
249
270
|
try {
|
|
@@ -320,6 +341,7 @@ export default class ElectronBleTransport {
|
|
|
320
341
|
|
|
321
342
|
async release(id: string) {
|
|
322
343
|
try {
|
|
344
|
+
await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
323
345
|
if (this.connectedDevices.has(id)) {
|
|
324
346
|
if (window.desktopApi?.nobleBle) {
|
|
325
347
|
await window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -417,12 +439,13 @@ export default class ElectronBleTransport {
|
|
|
417
439
|
}
|
|
418
440
|
|
|
419
441
|
private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
|
|
442
|
+
await this.protocolV2Links.invalidateLink(
|
|
443
|
+
uuid,
|
|
444
|
+
`Reset notify state after Protocol ${protocol} probe`
|
|
445
|
+
);
|
|
420
446
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
421
447
|
this.v2Assemblers.get(uuid)?.reset();
|
|
422
448
|
this.resetProtocolV2Frames(uuid);
|
|
423
|
-
if (this.activeProtocolV2Call?.uuid === uuid) {
|
|
424
|
-
this.activeProtocolV2Call = null;
|
|
425
|
-
}
|
|
426
449
|
if (this.runPromise) {
|
|
427
450
|
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
428
451
|
this.runPromise.reject(error);
|
|
@@ -496,7 +519,7 @@ export default class ElectronBleTransport {
|
|
|
496
519
|
|
|
497
520
|
if (totalBytes <= BLE_PACKET_SIZE) {
|
|
498
521
|
await wait(BLE_WRITE_DELAY_MS);
|
|
499
|
-
await this.
|
|
522
|
+
await this.writeOnce(uuid, hexData);
|
|
500
523
|
return;
|
|
501
524
|
}
|
|
502
525
|
|
|
@@ -505,7 +528,7 @@ export default class ElectronBleTransport {
|
|
|
505
528
|
const chunkHex = hexData.substring(offset, offset + chunkHexLen);
|
|
506
529
|
offset += chunkHexLen;
|
|
507
530
|
|
|
508
|
-
await this.
|
|
531
|
+
await this.writeOnce(uuid, chunkHex);
|
|
509
532
|
|
|
510
533
|
if (offset < hexData.length) {
|
|
511
534
|
await wait(BLE_WRITE_DELAY_MS);
|
|
@@ -513,41 +536,23 @@ export default class ElectronBleTransport {
|
|
|
513
536
|
}
|
|
514
537
|
}
|
|
515
538
|
|
|
516
|
-
private async
|
|
517
|
-
let lastError: any;
|
|
539
|
+
private async writeOnce(uuid: string, hexData: string): Promise<void> {
|
|
518
540
|
const nobleBle = window.desktopApi?.nobleBle;
|
|
519
541
|
if (!nobleBle) {
|
|
520
542
|
throw new Error('Noble BLE API not available');
|
|
521
543
|
}
|
|
522
544
|
|
|
523
|
-
|
|
524
|
-
try {
|
|
525
|
-
await nobleBle.write(uuid, hexData);
|
|
526
|
-
return;
|
|
527
|
-
} catch (error) {
|
|
528
|
-
lastError = error;
|
|
529
|
-
this.Log?.error(
|
|
530
|
-
`[Electron BLE] write failed (attempt ${attempt}/${BLE_WRITE_MAX_RETRIES}):`,
|
|
531
|
-
error
|
|
532
|
-
);
|
|
533
|
-
if (attempt < BLE_WRITE_MAX_RETRIES) {
|
|
534
|
-
await wait(BLE_WRITE_RETRY_DELAY_MS);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
throw ERRORS.TypedError(
|
|
539
|
-
HardwareErrorCode.BleWriteCharacteristicError,
|
|
540
|
-
`BLE write failed after ${BLE_WRITE_MAX_RETRIES} attempts: ${lastError?.message ?? lastError}`
|
|
541
|
-
);
|
|
545
|
+
await nobleBle.write(uuid, hexData);
|
|
542
546
|
}
|
|
543
547
|
|
|
544
548
|
private handleNotification(deviceId: string, hexData: string): void {
|
|
545
549
|
if (hexData === 'PAIRING_REJECTED') {
|
|
546
550
|
this.Log?.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
|
|
547
|
-
|
|
548
|
-
|
|
551
|
+
const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
|
|
552
|
+
if (this.deviceProtocol.get(deviceId) === 'V2') {
|
|
553
|
+
this.rejectProtocolV2Frames(deviceId, error);
|
|
554
|
+
} else if (this.runPromise) {
|
|
549
555
|
this.runPromise.reject(error);
|
|
550
|
-
this.rejectAllProtocolV2Frames(error);
|
|
551
556
|
}
|
|
552
557
|
return;
|
|
553
558
|
}
|
|
@@ -566,12 +571,6 @@ export default class ElectronBleTransport {
|
|
|
566
571
|
|
|
567
572
|
private handleProtocolV2Notification(deviceId: string, hexData: string): void {
|
|
568
573
|
try {
|
|
569
|
-
if (!this.runPromise || this.activeProtocolV2Call?.uuid !== deviceId) {
|
|
570
|
-
this.v2Assemblers.get(deviceId)?.reset();
|
|
571
|
-
this.resetProtocolV2Frames(deviceId);
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
574
|
const bytes = hexToBytes(hexData);
|
|
576
575
|
if (bytes.length === 0) return;
|
|
577
576
|
|
|
@@ -583,11 +582,8 @@ export default class ElectronBleTransport {
|
|
|
583
582
|
}
|
|
584
583
|
} catch (error) {
|
|
585
584
|
this.Log?.error('[Electron BLE] Protocol V2 notification error:', error);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
this.runPromise.reject(notifyError);
|
|
589
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
590
|
-
}
|
|
585
|
+
const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
|
|
586
|
+
this.rejectProtocolV2Frames(deviceId, notifyError);
|
|
591
587
|
}
|
|
592
588
|
}
|
|
593
589
|
|
|
@@ -623,8 +619,13 @@ export default class ElectronBleTransport {
|
|
|
623
619
|
this.v2FramePromises.delete(uuid);
|
|
624
620
|
}
|
|
625
621
|
|
|
626
|
-
private
|
|
627
|
-
|
|
622
|
+
private rejectProtocolV2Frames(uuid: string, error: Error) {
|
|
623
|
+
this.v2FrameQueues.delete(uuid);
|
|
624
|
+
const framePromise = this.v2FramePromises.get(uuid);
|
|
625
|
+
if (framePromise) {
|
|
626
|
+
this.v2FramePromises.delete(uuid);
|
|
627
|
+
framePromise.reject(error);
|
|
628
|
+
}
|
|
628
629
|
}
|
|
629
630
|
|
|
630
631
|
private async readProtocolV2Frame(uuid: string) {
|
|
@@ -776,79 +777,67 @@ export default class ElectronBleTransport {
|
|
|
776
777
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
777
778
|
}
|
|
778
779
|
|
|
779
|
-
const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
|
|
780
|
-
if (this.runPromise) {
|
|
781
|
-
if (!forceRun) {
|
|
782
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
|
|
783
|
-
}
|
|
784
|
-
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
785
|
-
this.runPromise.reject(error);
|
|
786
|
-
this.rejectAllProtocolV2Frames(error);
|
|
787
|
-
this.runPromise = null;
|
|
788
|
-
this.activeProtocolV2Call = null;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
const runPromise = createDeferred<Uint8Array | string>();
|
|
792
|
-
runPromise.promise.catch(() => undefined);
|
|
793
|
-
this.runPromise = runPromise;
|
|
794
|
-
const callToken = this.nextProtocolV2CallToken++;
|
|
795
|
-
this.activeProtocolV2Call = { uuid, token: callToken };
|
|
796
|
-
this.v2Assemblers.get(uuid)?.reset();
|
|
797
|
-
this.resetProtocolV2Frames(uuid);
|
|
798
|
-
let completed = false;
|
|
799
780
|
const callOptions = {
|
|
800
781
|
...options,
|
|
801
782
|
timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
|
|
802
783
|
};
|
|
803
784
|
|
|
804
785
|
try {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
readFrame: async () => {
|
|
813
|
-
const rxFrame = await this.readProtocolV2Frame(uuid);
|
|
814
|
-
if (!(rxFrame instanceof Uint8Array)) {
|
|
815
|
-
throw new Error('Response is not Uint8Array');
|
|
816
|
-
}
|
|
817
|
-
return rxFrame;
|
|
818
|
-
},
|
|
819
|
-
logger: this.Log,
|
|
820
|
-
logPrefix: 'ProtocolV2 BLE',
|
|
821
|
-
createTimeoutError: (_messageName: string, timeout: number) =>
|
|
822
|
-
ERRORS.TypedError(
|
|
823
|
-
HardwareErrorCode.BleTimeoutError,
|
|
824
|
-
`BLE response timeout after ${timeout}ms for ${name}`
|
|
825
|
-
),
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
const result = await session.call(name, data, callOptions);
|
|
829
|
-
completed = true;
|
|
830
|
-
return result;
|
|
786
|
+
return await this.protocolV2Links.call(
|
|
787
|
+
uuid,
|
|
788
|
+
() => this.createProtocolV2Adapter(uuid),
|
|
789
|
+
name,
|
|
790
|
+
data,
|
|
791
|
+
callOptions
|
|
792
|
+
);
|
|
831
793
|
} catch (e) {
|
|
832
|
-
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
833
|
-
this.v2Assemblers.get(uuid)?.reset();
|
|
834
|
-
this.resetProtocolV2Frames(uuid);
|
|
835
|
-
}
|
|
836
794
|
this.Log?.error('[Electron BLE] Protocol V2 call error:', e);
|
|
837
795
|
throw e;
|
|
838
|
-
} finally {
|
|
839
|
-
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
840
|
-
if (!completed) {
|
|
841
|
-
this.v2Assemblers.get(uuid)?.reset();
|
|
842
|
-
}
|
|
843
|
-
this.resetProtocolV2Frames(uuid);
|
|
844
|
-
this.activeProtocolV2Call = null;
|
|
845
|
-
}
|
|
846
|
-
if (this.runPromise === runPromise) {
|
|
847
|
-
this.runPromise = null;
|
|
848
|
-
}
|
|
849
796
|
}
|
|
850
797
|
}
|
|
851
798
|
|
|
799
|
+
private createProtocolV2Adapter(uuid: string) {
|
|
800
|
+
const generation = this.notificationTokens.get(uuid) ?? 0;
|
|
801
|
+
const assertCurrentGeneration = () => {
|
|
802
|
+
if (this.notificationTokens.get(uuid) !== generation) {
|
|
803
|
+
throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
return {
|
|
808
|
+
router: PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
809
|
+
generation,
|
|
810
|
+
prepareCall: () => {
|
|
811
|
+
assertCurrentGeneration();
|
|
812
|
+
this.v2Assemblers.get(uuid)?.reset();
|
|
813
|
+
this.resetProtocolV2Frames(uuid);
|
|
814
|
+
},
|
|
815
|
+
writeFrame: (frame: Uint8Array) => {
|
|
816
|
+
assertCurrentGeneration();
|
|
817
|
+
return this.writeWithChunking(uuid, bytesToHex(frame));
|
|
818
|
+
},
|
|
819
|
+
readFrame: async () => {
|
|
820
|
+
assertCurrentGeneration();
|
|
821
|
+
const rxFrame = await this.readProtocolV2Frame(uuid);
|
|
822
|
+
if (!(rxFrame instanceof Uint8Array)) {
|
|
823
|
+
throw new Error('Response is not Uint8Array');
|
|
824
|
+
}
|
|
825
|
+
return rxFrame;
|
|
826
|
+
},
|
|
827
|
+
reset: (reason: string) => {
|
|
828
|
+
this.v2Assemblers.get(uuid)?.reset();
|
|
829
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
830
|
+
},
|
|
831
|
+
logger: this.Log,
|
|
832
|
+
logPrefix: 'ProtocolV2 BLE',
|
|
833
|
+
createTimeoutError: (messageName: string, timeout: number) =>
|
|
834
|
+
ERRORS.TypedError(
|
|
835
|
+
HardwareErrorCode.BleTimeoutError,
|
|
836
|
+
`BLE response timeout after ${timeout}ms for ${messageName}`
|
|
837
|
+
),
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
|
|
852
841
|
private processProtocolV1Notification(deviceId: string, hexData: string): PacketProcessResult {
|
|
853
842
|
try {
|
|
854
843
|
if (typeof hexData !== 'string') {
|
package/src/webusb.ts
CHANGED
|
@@ -571,16 +571,7 @@ export default class WebUsbTransport {
|
|
|
571
571
|
let lastError: unknown;
|
|
572
572
|
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt += 1) {
|
|
573
573
|
try {
|
|
574
|
-
|
|
575
|
-
if (!device.opened) {
|
|
576
|
-
await this.connect(path, false);
|
|
577
|
-
}
|
|
578
|
-
const endpoints = this.deviceEndpoints.get(path);
|
|
579
|
-
const endpointOut = endpoints?.endpointOut ?? this.endpointId;
|
|
580
|
-
const transferBuffer = this.toArrayBuffer(
|
|
581
|
-
packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength)
|
|
582
|
-
);
|
|
583
|
-
await device.transferOut(endpointOut, transferBuffer);
|
|
574
|
+
await this.transferOutOnce(path, packet);
|
|
584
575
|
return;
|
|
585
576
|
} catch (error) {
|
|
586
577
|
lastError = error;
|
|
@@ -603,6 +594,19 @@ export default class WebUsbTransport {
|
|
|
603
594
|
throw lastError;
|
|
604
595
|
}
|
|
605
596
|
|
|
597
|
+
private async transferOutOnce(path: string, packet: Uint8Array) {
|
|
598
|
+
const device = await this.findDevice(path);
|
|
599
|
+
if (!device.opened) {
|
|
600
|
+
await this.connect(path, false);
|
|
601
|
+
}
|
|
602
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
603
|
+
const endpointOut = endpoints?.endpointOut ?? this.endpointId;
|
|
604
|
+
const transferBuffer = this.toArrayBuffer(
|
|
605
|
+
packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength)
|
|
606
|
+
);
|
|
607
|
+
await device.transferOut(endpointOut, transferBuffer);
|
|
608
|
+
}
|
|
609
|
+
|
|
606
610
|
private async transferInWithRetry(
|
|
607
611
|
path: string,
|
|
608
612
|
length: number,
|
|
@@ -835,7 +839,7 @@ export default class WebUsbTransport {
|
|
|
835
839
|
protocolV2: this.messagesV2,
|
|
836
840
|
},
|
|
837
841
|
router: PROTOCOL_V2_CHANNEL_USB,
|
|
838
|
-
writeFrame: (frame: Uint8Array) => this.
|
|
842
|
+
writeFrame: (frame: Uint8Array) => this.transferOutOnce(path, frame),
|
|
839
843
|
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
840
844
|
logger: this.Log,
|
|
841
845
|
logPrefix: 'ProtocolV2 WebUSB',
|