@onekeyfe/hd-transport-web-device 1.2.0-alpha.183 → 1.2.0-alpha.185
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.
|
@@ -73,8 +73,26 @@ const protocolV2Schema = {
|
|
|
73
73
|
},
|
|
74
74
|
},
|
|
75
75
|
},
|
|
76
|
+
Failure: {
|
|
77
|
+
fields: {
|
|
78
|
+
code: {
|
|
79
|
+
type: 'FailureType',
|
|
80
|
+
id: 1,
|
|
81
|
+
},
|
|
82
|
+
message: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
id: 2,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
FailureType: {
|
|
89
|
+
values: {
|
|
90
|
+
Failure_ProcessError: 5,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
76
93
|
MessageType: {
|
|
77
94
|
values: {
|
|
95
|
+
MessageType_Failure: 3,
|
|
78
96
|
MessageType_ProtocolInfoRequest: 60200,
|
|
79
97
|
MessageType_ProtocolInfo: 60201,
|
|
80
98
|
MessageType_Ping: 60206,
|
|
@@ -461,6 +479,41 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
461
479
|
expect(transport.getProtocolType(device.id)).toBeUndefined();
|
|
462
480
|
});
|
|
463
481
|
|
|
482
|
+
test('surfaces Protocol V2 link disabled while the initial V1 probe is active', async () => {
|
|
483
|
+
const device = { id: 'usb-priority-pro2-id', name: 'OneKey Pro 2' };
|
|
484
|
+
const nobleBle = createNobleBle(device);
|
|
485
|
+
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
486
|
+
|
|
487
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
488
|
+
notificationHandler = handler;
|
|
489
|
+
return jest.fn();
|
|
490
|
+
});
|
|
491
|
+
nobleBle.write.mockImplementation(() => {
|
|
492
|
+
const response = ProtocolV2.encodeFrame(
|
|
493
|
+
schemas,
|
|
494
|
+
'Failure',
|
|
495
|
+
{ code: 5, message: 'link disabled' },
|
|
496
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
497
|
+
);
|
|
498
|
+
const splitAt = 4;
|
|
499
|
+
setTimeout(() => {
|
|
500
|
+
notificationHandler?.(device.id, bytesToHex(response.subarray(0, splitAt)));
|
|
501
|
+
notificationHandler?.(device.id, bytesToHex(response.subarray(splitAt)));
|
|
502
|
+
}, 0);
|
|
503
|
+
return Promise.resolve();
|
|
504
|
+
});
|
|
505
|
+
const transport = configureTransport(nobleBle);
|
|
506
|
+
|
|
507
|
+
await expect(transport.acquire({ uuid: device.id })).rejects.toMatchObject({
|
|
508
|
+
name: 'ProtocolV2LinkDisabledError',
|
|
509
|
+
failureCode: 'Failure_ProcessError',
|
|
510
|
+
firmwareMessage: 'link disabled',
|
|
511
|
+
});
|
|
512
|
+
expect(nobleBle.write).toHaveBeenCalledTimes(1);
|
|
513
|
+
expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
|
|
514
|
+
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
515
|
+
});
|
|
516
|
+
|
|
464
517
|
test('keeps a first expected Protocol V2 probe miss retryable', async () => {
|
|
465
518
|
const device = { id: 'first-v2-id', name: 'OneKey Pro 2' };
|
|
466
519
|
const nobleBle = createNobleBle(device);
|
|
@@ -497,6 +550,40 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
497
550
|
expect(transport.getProtocolType(device.id)).toBeUndefined();
|
|
498
551
|
});
|
|
499
552
|
|
|
553
|
+
test('fails Protocol V2 acquire immediately when subscribe reports insufficient encryption', async () => {
|
|
554
|
+
const device = { id: 'stale-bond-pro2-id', name: 'OneKey Pro 2' };
|
|
555
|
+
const nobleBle = createNobleBle(device);
|
|
556
|
+
nobleBle.subscribe.mockRejectedValue(new Error('Encryption is insufficient'));
|
|
557
|
+
const transport = configureTransport(nobleBle);
|
|
558
|
+
const probe = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
559
|
+
|
|
560
|
+
await expect(
|
|
561
|
+
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
562
|
+
).rejects.toMatchObject({
|
|
563
|
+
errorCode: HardwareErrorCode.BleDeviceBondError,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
expect(probe).not.toHaveBeenCalled();
|
|
567
|
+
expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
|
|
568
|
+
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
test('keeps stale-bond subscribe mapping out of Protocol V1 acquire', async () => {
|
|
572
|
+
const device = { id: 'classic-v1-id', name: 'OneKey Classic' };
|
|
573
|
+
const nobleBle = createNobleBle(device);
|
|
574
|
+
nobleBle.subscribe.mockRejectedValue(new Error('Encryption is insufficient'));
|
|
575
|
+
const transport = configureTransport(nobleBle);
|
|
576
|
+
|
|
577
|
+
try {
|
|
578
|
+
await transport.acquire({ uuid: device.id, expectedProtocol: 'V1' });
|
|
579
|
+
throw new Error('Expected Protocol V1 acquire to fail');
|
|
580
|
+
} catch (error) {
|
|
581
|
+
expect(error).toBeInstanceOf(Error);
|
|
582
|
+
expect((error as Error).message).toBe('Encryption is insufficient');
|
|
583
|
+
expect((error as { errorCode?: unknown }).errorCode).toBeUndefined();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
500
587
|
test('reports a stale bond when a previously confirmed Protocol V2 device stops responding', async () => {
|
|
501
588
|
const device = { id: 'reset-pro2-id', name: 'OneKey Pro 2' };
|
|
502
589
|
const nobleBle = createNobleBle(device);
|
|
@@ -41,6 +41,7 @@ export default class ElectronBleTransport {
|
|
|
41
41
|
private hostDisconnectCleanup?;
|
|
42
42
|
private notificationTokens;
|
|
43
43
|
private nextNotificationToken;
|
|
44
|
+
private toStaleBondError;
|
|
44
45
|
private handleBluetoothError;
|
|
45
46
|
private cleanupDeviceState;
|
|
46
47
|
init(logger: any, emitter?: EventEmitter): void;
|
|
@@ -78,6 +79,7 @@ export default class ElectronBleTransport {
|
|
|
78
79
|
private createMtuSubscription;
|
|
79
80
|
private writeProtocolV2Frame;
|
|
80
81
|
private handleNotification;
|
|
82
|
+
private readProtocolV2LinkDisabledFailure;
|
|
81
83
|
private handleProtocolV2Notification;
|
|
82
84
|
private getProtocolV2FrameQueue;
|
|
83
85
|
private resolveProtocolV2Frame;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -242,6 +242,7 @@ declare class ElectronBleTransport {
|
|
|
242
242
|
private hostDisconnectCleanup?;
|
|
243
243
|
private notificationTokens;
|
|
244
244
|
private nextNotificationToken;
|
|
245
|
+
private toStaleBondError;
|
|
245
246
|
private handleBluetoothError;
|
|
246
247
|
private cleanupDeviceState;
|
|
247
248
|
init(logger: any, emitter?: EventEmitter): void;
|
|
@@ -284,6 +285,7 @@ declare class ElectronBleTransport {
|
|
|
284
285
|
private createMtuSubscription;
|
|
285
286
|
private writeProtocolV2Frame;
|
|
286
287
|
private handleNotification;
|
|
288
|
+
private readProtocolV2LinkDisabledFailure;
|
|
287
289
|
private handleProtocolV2Notification;
|
|
288
290
|
private getProtocolV2FrameQueue;
|
|
289
291
|
private resolveProtocolV2Frame;
|
package/dist/index.js
CHANGED
|
@@ -821,7 +821,7 @@ function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapa
|
|
|
821
821
|
return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
-
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
824
|
+
const { parseConfigure, ProtocolV1, ProtocolV2, check } = transport__default["default"];
|
|
825
825
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
826
826
|
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
827
827
|
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
@@ -871,7 +871,29 @@ class ElectronBleTransport {
|
|
|
871
871
|
this.notificationTokens = new Map();
|
|
872
872
|
this.nextNotificationToken = 1;
|
|
873
873
|
}
|
|
874
|
-
|
|
874
|
+
toStaleBondError(error) {
|
|
875
|
+
var _a;
|
|
876
|
+
if (hdShared.isBleStaleBondHardwareError(error)) {
|
|
877
|
+
return error;
|
|
878
|
+
}
|
|
879
|
+
const errorMessage = error && typeof error === 'object' && 'message' in error
|
|
880
|
+
? String((_a = error.message) !== null && _a !== void 0 ? _a : '')
|
|
881
|
+
: String(error !== null && error !== void 0 ? error : '');
|
|
882
|
+
if (!hdShared.isBleStaleBondErrorText(errorMessage)) {
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
const normalizedErrorMessage = errorMessage.toLowerCase();
|
|
886
|
+
return hdShared.ERRORS.TypedError(normalizedErrorMessage.includes('peer removed pairing information')
|
|
887
|
+
? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
888
|
+
: hdShared.HardwareErrorCode.BleDeviceBondError, errorMessage);
|
|
889
|
+
}
|
|
890
|
+
handleBluetoothError(error, mapProtocolV2StaleBond = false) {
|
|
891
|
+
if (mapProtocolV2StaleBond) {
|
|
892
|
+
const staleBondError = this.toStaleBondError(error);
|
|
893
|
+
if (staleBondError) {
|
|
894
|
+
throw staleBondError;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
875
897
|
if (error && typeof error === 'object') {
|
|
876
898
|
if ('code' in error) {
|
|
877
899
|
if (error.code === hdShared.HardwareErrorCode.BlePoweredOff) {
|
|
@@ -1002,6 +1024,9 @@ class ElectronBleTransport {
|
|
|
1002
1024
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1003
1025
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1004
1026
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
1027
|
+
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
1028
|
+
? expectedProtocol === 'V2'
|
|
1029
|
+
: this.confirmedProtocolV2.has(uuid);
|
|
1005
1030
|
if (!uuid) {
|
|
1006
1031
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
1007
1032
|
}
|
|
@@ -1033,7 +1058,7 @@ class ElectronBleTransport {
|
|
|
1033
1058
|
this.connectedDevices.add(uuid);
|
|
1034
1059
|
}
|
|
1035
1060
|
catch (error) {
|
|
1036
|
-
this.handleBluetoothError(error);
|
|
1061
|
+
this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
|
|
1037
1062
|
}
|
|
1038
1063
|
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
1039
1064
|
if (mtuCleanup) {
|
|
@@ -1041,7 +1066,12 @@ class ElectronBleTransport {
|
|
|
1041
1066
|
}
|
|
1042
1067
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1043
1068
|
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
1044
|
-
|
|
1069
|
+
try {
|
|
1070
|
+
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
1071
|
+
}
|
|
1072
|
+
catch (error) {
|
|
1073
|
+
this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
|
|
1074
|
+
}
|
|
1045
1075
|
yield this.refreshBlePacketCapacity(uuid);
|
|
1046
1076
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
1047
1077
|
this.notificationCleanups.set(uuid, cleanup);
|
|
@@ -1227,6 +1257,9 @@ class ElectronBleTransport {
|
|
|
1227
1257
|
catch (error) {
|
|
1228
1258
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1229
1259
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
|
|
1260
|
+
if (transport.isProtocolV2LinkDisabledError(error)) {
|
|
1261
|
+
throw error;
|
|
1262
|
+
}
|
|
1230
1263
|
return false;
|
|
1231
1264
|
}
|
|
1232
1265
|
});
|
|
@@ -1249,6 +1282,7 @@ class ElectronBleTransport {
|
|
|
1249
1282
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1250
1283
|
this.resetProtocolV2Frames(uuid);
|
|
1251
1284
|
},
|
|
1285
|
+
shouldRethrow: error => hdShared.isBleStaleBondHardwareError(error) || transport.isProtocolV2LinkDisabledError(error),
|
|
1252
1286
|
});
|
|
1253
1287
|
if (!detected) {
|
|
1254
1288
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -1263,7 +1297,16 @@ class ElectronBleTransport {
|
|
|
1263
1297
|
if (!nobleBle) {
|
|
1264
1298
|
throw new Error('Noble BLE API not available');
|
|
1265
1299
|
}
|
|
1266
|
-
|
|
1300
|
+
try {
|
|
1301
|
+
yield nobleBle.write(uuid, hexData, { pacingDelayMs: 0 });
|
|
1302
|
+
}
|
|
1303
|
+
catch (error) {
|
|
1304
|
+
const staleBondError = this.toStaleBondError(error);
|
|
1305
|
+
if (staleBondError) {
|
|
1306
|
+
throw staleBondError;
|
|
1307
|
+
}
|
|
1308
|
+
throw error;
|
|
1309
|
+
}
|
|
1267
1310
|
});
|
|
1268
1311
|
}
|
|
1269
1312
|
refreshBlePacketCapacity(uuid) {
|
|
@@ -1332,8 +1375,39 @@ class ElectronBleTransport {
|
|
|
1332
1375
|
this.handleProtocolV2Notification(deviceId, hexData);
|
|
1333
1376
|
return;
|
|
1334
1377
|
}
|
|
1378
|
+
const linkDisabledError = this.readProtocolV2LinkDisabledFailure(deviceId, hexData);
|
|
1379
|
+
if (linkDisabledError) {
|
|
1380
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
1381
|
+
this.runPromise.reject(linkDisabledError);
|
|
1382
|
+
}
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1335
1385
|
this.handleProtocolV1Notification(deviceId, hexData);
|
|
1336
1386
|
}
|
|
1387
|
+
readProtocolV2LinkDisabledFailure(deviceId, hexData) {
|
|
1388
|
+
var _a, _b;
|
|
1389
|
+
if (!this._messages || !this._messagesV2)
|
|
1390
|
+
return undefined;
|
|
1391
|
+
const assembler = this.v2Assemblers.get(deviceId);
|
|
1392
|
+
if (!assembler)
|
|
1393
|
+
return undefined;
|
|
1394
|
+
try {
|
|
1395
|
+
for (const frame of assembler.drain(transport.hexToBytes(hexData))) {
|
|
1396
|
+
const response = check.call(ProtocolV2.decodeFrame({ protocolV1: this._messages, protocolV2: this._messagesV2 }, frame));
|
|
1397
|
+
if (response.type === 'Failure') {
|
|
1398
|
+
const failureCode = (_a = response.message) === null || _a === void 0 ? void 0 : _a.code;
|
|
1399
|
+
const firmwareMessage = (_b = response.message) === null || _b === void 0 ? void 0 : _b.message;
|
|
1400
|
+
if (transport.isProtocolV2LinkDisabledFailure(failureCode, firmwareMessage)) {
|
|
1401
|
+
return transport.createProtocolV2LinkDisabledError(failureCode, firmwareMessage);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
catch (_c) {
|
|
1407
|
+
assembler.reset();
|
|
1408
|
+
}
|
|
1409
|
+
return undefined;
|
|
1410
|
+
}
|
|
1337
1411
|
handleProtocolV2Notification(deviceId, hexData) {
|
|
1338
1412
|
var _a;
|
|
1339
1413
|
try {
|
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.185",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"lint:fix": "eslint . --fix"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.185",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.185"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.185",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "dba45132513abeef33dc448824b4eaace74900ee"
|
|
33
33
|
}
|
|
@@ -6,7 +6,10 @@ import transport, {
|
|
|
6
6
|
ProtocolV2LinkManager,
|
|
7
7
|
TRANSPORT_EVENT,
|
|
8
8
|
bytesToHex,
|
|
9
|
+
createProtocolV2LinkDisabledError,
|
|
9
10
|
hexToBytes,
|
|
11
|
+
isProtocolV2LinkDisabledError,
|
|
12
|
+
isProtocolV2LinkDisabledFailure,
|
|
10
13
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
11
14
|
writeProtocolV2BleFrame,
|
|
12
15
|
} from '@onekeyfe/hd-transport';
|
|
@@ -16,6 +19,8 @@ import {
|
|
|
16
19
|
HardwareErrorCode,
|
|
17
20
|
HardwareErrorCodeMessage,
|
|
18
21
|
createDeferred,
|
|
22
|
+
isBleStaleBondErrorText,
|
|
23
|
+
isBleStaleBondHardwareError,
|
|
19
24
|
isHeaderChunk,
|
|
20
25
|
} from '@onekeyfe/hd-shared';
|
|
21
26
|
|
|
@@ -31,7 +36,7 @@ import type {
|
|
|
31
36
|
} from '@onekeyfe/hd-transport';
|
|
32
37
|
import type EventEmitter from 'events';
|
|
33
38
|
|
|
34
|
-
const { parseConfigure, ProtocolV1, check } = transport;
|
|
39
|
+
const { parseConfigure, ProtocolV1, ProtocolV2, check } = transport;
|
|
35
40
|
|
|
36
41
|
declare global {
|
|
37
42
|
interface Window {
|
|
@@ -159,7 +164,33 @@ export default class ElectronBleTransport {
|
|
|
159
164
|
|
|
160
165
|
private nextNotificationToken = 1;
|
|
161
166
|
|
|
162
|
-
private
|
|
167
|
+
private toStaleBondError(error: unknown): Error | null {
|
|
168
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
169
|
+
return error as Error;
|
|
170
|
+
}
|
|
171
|
+
const errorMessage =
|
|
172
|
+
error && typeof error === 'object' && 'message' in error
|
|
173
|
+
? String((error as { message?: unknown }).message ?? '')
|
|
174
|
+
: String(error ?? '');
|
|
175
|
+
if (!isBleStaleBondErrorText(errorMessage)) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const normalizedErrorMessage = errorMessage.toLowerCase();
|
|
179
|
+
return ERRORS.TypedError(
|
|
180
|
+
normalizedErrorMessage.includes('peer removed pairing information')
|
|
181
|
+
? HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
182
|
+
: HardwareErrorCode.BleDeviceBondError,
|
|
183
|
+
errorMessage
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private handleBluetoothError(error: any, mapProtocolV2StaleBond = false): never {
|
|
188
|
+
if (mapProtocolV2StaleBond) {
|
|
189
|
+
const staleBondError = this.toStaleBondError(error);
|
|
190
|
+
if (staleBondError) {
|
|
191
|
+
throw staleBondError;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
163
194
|
if (error && typeof error === 'object') {
|
|
164
195
|
if ('code' in error) {
|
|
165
196
|
if (error.code === HardwareErrorCode.BlePoweredOff) {
|
|
@@ -320,6 +351,9 @@ export default class ElectronBleTransport {
|
|
|
320
351
|
|
|
321
352
|
async acquire(input: BleAcquireInput) {
|
|
322
353
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
354
|
+
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
355
|
+
? expectedProtocol === 'V2'
|
|
356
|
+
: this.confirmedProtocolV2.has(uuid);
|
|
323
357
|
|
|
324
358
|
if (!uuid) {
|
|
325
359
|
throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
|
|
@@ -356,7 +390,7 @@ export default class ElectronBleTransport {
|
|
|
356
390
|
await window.desktopApi.nobleBle.connect(uuid);
|
|
357
391
|
this.connectedDevices.add(uuid);
|
|
358
392
|
} catch (error) {
|
|
359
|
-
this.handleBluetoothError(error);
|
|
393
|
+
this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
|
|
360
394
|
}
|
|
361
395
|
|
|
362
396
|
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
@@ -367,7 +401,11 @@ export default class ElectronBleTransport {
|
|
|
367
401
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
368
402
|
this.v2Assemblers.set(uuid, new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
369
403
|
|
|
370
|
-
|
|
404
|
+
try {
|
|
405
|
+
await window.desktopApi.nobleBle.subscribe(uuid);
|
|
406
|
+
} catch (error) {
|
|
407
|
+
this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
|
|
408
|
+
}
|
|
371
409
|
await this.refreshBlePacketCapacity(uuid);
|
|
372
410
|
|
|
373
411
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
@@ -595,6 +633,9 @@ export default class ElectronBleTransport {
|
|
|
595
633
|
} catch (error) {
|
|
596
634
|
this.clearProbeProtocol(uuid, 'V1');
|
|
597
635
|
this.Log?.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
|
|
636
|
+
if (isProtocolV2LinkDisabledError(error)) {
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
598
639
|
return false;
|
|
599
640
|
}
|
|
600
641
|
}
|
|
@@ -615,6 +656,8 @@ export default class ElectronBleTransport {
|
|
|
615
656
|
this.v2Assemblers.get(uuid)?.reset();
|
|
616
657
|
this.resetProtocolV2Frames(uuid);
|
|
617
658
|
},
|
|
659
|
+
shouldRethrow: error =>
|
|
660
|
+
isBleStaleBondHardwareError(error) || isProtocolV2LinkDisabledError(error),
|
|
618
661
|
});
|
|
619
662
|
if (!detected) {
|
|
620
663
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -628,7 +671,15 @@ export default class ElectronBleTransport {
|
|
|
628
671
|
throw new Error('Noble BLE API not available');
|
|
629
672
|
}
|
|
630
673
|
|
|
631
|
-
|
|
674
|
+
try {
|
|
675
|
+
await nobleBle.write(uuid, hexData, { pacingDelayMs: 0 });
|
|
676
|
+
} catch (error) {
|
|
677
|
+
const staleBondError = this.toStaleBondError(error);
|
|
678
|
+
if (staleBondError) {
|
|
679
|
+
throw staleBondError;
|
|
680
|
+
}
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
632
683
|
}
|
|
633
684
|
|
|
634
685
|
private async refreshBlePacketCapacity(uuid: string): Promise<void> {
|
|
@@ -702,9 +753,45 @@ export default class ElectronBleTransport {
|
|
|
702
753
|
this.handleProtocolV2Notification(deviceId, hexData);
|
|
703
754
|
return;
|
|
704
755
|
}
|
|
756
|
+
const linkDisabledError = this.readProtocolV2LinkDisabledFailure(deviceId, hexData);
|
|
757
|
+
if (linkDisabledError) {
|
|
758
|
+
if (this.runPromise && this.runPromiseDeviceId === deviceId) {
|
|
759
|
+
this.runPromise.reject(linkDisabledError);
|
|
760
|
+
}
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
705
763
|
this.handleProtocolV1Notification(deviceId, hexData);
|
|
706
764
|
}
|
|
707
765
|
|
|
766
|
+
private readProtocolV2LinkDisabledFailure(deviceId: string, hexData: string) {
|
|
767
|
+
if (!this._messages || !this._messagesV2) return undefined;
|
|
768
|
+
|
|
769
|
+
const assembler = this.v2Assemblers.get(deviceId);
|
|
770
|
+
if (!assembler) return undefined;
|
|
771
|
+
|
|
772
|
+
try {
|
|
773
|
+
for (const frame of assembler.drain(hexToBytes(hexData))) {
|
|
774
|
+
const response = check.call(
|
|
775
|
+
ProtocolV2.decodeFrame(
|
|
776
|
+
{ protocolV1: this._messages, protocolV2: this._messagesV2 },
|
|
777
|
+
frame
|
|
778
|
+
)
|
|
779
|
+
);
|
|
780
|
+
if (response.type === 'Failure') {
|
|
781
|
+
const failureCode = response.message?.code;
|
|
782
|
+
const firmwareMessage = response.message?.message;
|
|
783
|
+
if (isProtocolV2LinkDisabledFailure(failureCode, firmwareMessage)) {
|
|
784
|
+
return createProtocolV2LinkDisabledError(failureCode, firmwareMessage);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
} catch {
|
|
789
|
+
// A normal Protocol V1 notification is not a Protocol V2 frame.
|
|
790
|
+
assembler.reset();
|
|
791
|
+
}
|
|
792
|
+
return undefined;
|
|
793
|
+
}
|
|
794
|
+
|
|
708
795
|
private handleProtocolV2Notification(deviceId: string, hexData: string): void {
|
|
709
796
|
try {
|
|
710
797
|
const bytes = hexToBytes(hexData);
|