@onekeyfe/hd-transport-lowlevel 1.1.27-alpha.31 → 1.1.27-alpha.32
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/.eslintignore +2 -0
- package/__tests__/protocol-v2.test.js +63 -9
- package/dist/index.d.ts +4 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -18
- package/jest.config.js +6 -0
- package/package.json +5 -4
- package/src/index.ts +58 -14
package/.eslintignore
CHANGED
|
@@ -48,10 +48,28 @@ const protocolV2Schema = {
|
|
|
48
48
|
},
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
|
+
Ping: {
|
|
52
|
+
fields: {
|
|
53
|
+
message: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
id: 1,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
Success: {
|
|
60
|
+
fields: {
|
|
61
|
+
message: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
id: 1,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
51
67
|
MessageType: {
|
|
52
68
|
values: {
|
|
53
69
|
MessageType_GetProtoVersion: 60200,
|
|
54
70
|
MessageType_ProtoVersion: 60201,
|
|
71
|
+
MessageType_Ping: 60206,
|
|
72
|
+
MessageType_Success: 60207,
|
|
55
73
|
},
|
|
56
74
|
},
|
|
57
75
|
},
|
|
@@ -119,11 +137,9 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
119
137
|
test('detects Protocol V2 devices and reassembles split Protocol V2 notifications', async () => {
|
|
120
138
|
const probeResponse = ProtocolV2.encodeFrame(
|
|
121
139
|
schemas,
|
|
122
|
-
'
|
|
140
|
+
'Success',
|
|
123
141
|
{
|
|
124
|
-
|
|
125
|
-
minor_version: 0,
|
|
126
|
-
patch_version: 0,
|
|
142
|
+
message: 'probe',
|
|
127
143
|
},
|
|
128
144
|
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
129
145
|
);
|
|
@@ -144,7 +160,7 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
144
160
|
const lowlevel = configureTransport(plugin);
|
|
145
161
|
|
|
146
162
|
await expect(lowlevel.enumerate()).resolves.toEqual([
|
|
147
|
-
{ id: 'pro2-id', name: 'OneKey Pro 2', commType: 'ble'
|
|
163
|
+
{ id: 'pro2-id', name: 'OneKey Pro 2', commType: 'ble' },
|
|
148
164
|
]);
|
|
149
165
|
await expect(lowlevel.acquire({ uuid: 'pro2-id' })).resolves.toEqual({
|
|
150
166
|
uuid: 'pro2-id',
|
|
@@ -164,11 +180,9 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
164
180
|
test('falls back to Protocol V2 probe for unnamed Protocol V2 devices', async () => {
|
|
165
181
|
const probeResponse = ProtocolV2.encodeFrame(
|
|
166
182
|
schemas,
|
|
167
|
-
'
|
|
183
|
+
'Success',
|
|
168
184
|
{
|
|
169
|
-
|
|
170
|
-
minor_version: 0,
|
|
171
|
-
patch_version: 0,
|
|
185
|
+
message: 'probe',
|
|
172
186
|
},
|
|
173
187
|
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
174
188
|
);
|
|
@@ -185,6 +199,46 @@ describe('LowlevelTransport protocol framing', () => {
|
|
|
185
199
|
expect(lowlevel.getProtocolType('unknown-pro2-id')).toBe('V2');
|
|
186
200
|
});
|
|
187
201
|
|
|
202
|
+
test('resets the lowlevel connection before probing Protocol V2 after a V1 timeout', async () => {
|
|
203
|
+
const probeResponse = ProtocolV2.encodeFrame(
|
|
204
|
+
schemas,
|
|
205
|
+
'Success',
|
|
206
|
+
{
|
|
207
|
+
message: 'probe',
|
|
208
|
+
},
|
|
209
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
210
|
+
);
|
|
211
|
+
let staleReceivePending = false;
|
|
212
|
+
let resetAfterTimeout = false;
|
|
213
|
+
const plugin = createPlugin({
|
|
214
|
+
devices: [{ id: 'slow-v2-id', name: 'Unknown BLE Device', commType: 'ble' }],
|
|
215
|
+
responses: [],
|
|
216
|
+
});
|
|
217
|
+
plugin.disconnect.mockImplementation(() => {
|
|
218
|
+
staleReceivePending = false;
|
|
219
|
+
resetAfterTimeout = true;
|
|
220
|
+
return Promise.resolve();
|
|
221
|
+
});
|
|
222
|
+
plugin.receive.mockImplementation(() => {
|
|
223
|
+
if (!staleReceivePending && !resetAfterTimeout) {
|
|
224
|
+
staleReceivePending = true;
|
|
225
|
+
return new Promise(() => {});
|
|
226
|
+
}
|
|
227
|
+
if (staleReceivePending) {
|
|
228
|
+
return Promise.reject(new Error('stale receive still pending'));
|
|
229
|
+
}
|
|
230
|
+
return Promise.resolve(bytesToHex(probeResponse));
|
|
231
|
+
});
|
|
232
|
+
const lowlevel = configureTransport(plugin);
|
|
233
|
+
|
|
234
|
+
await expect(lowlevel.acquire({ uuid: 'slow-v2-id' })).resolves.toEqual({
|
|
235
|
+
uuid: 'slow-v2-id',
|
|
236
|
+
protocolType: 'V2',
|
|
237
|
+
});
|
|
238
|
+
expect(plugin.disconnect).toHaveBeenCalledWith('slow-v2-id');
|
|
239
|
+
expect(plugin.connect).toHaveBeenCalledTimes(2);
|
|
240
|
+
});
|
|
241
|
+
|
|
188
242
|
test('verifies expected Protocol V1 instead of trusting the requested protocol', async () => {
|
|
189
243
|
const plugin = createPlugin({
|
|
190
244
|
devices: [{ id: 'v2-id', name: 'Unknown BLE Device', commType: 'ble' }],
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as transport from '@onekeyfe/hd-transport';
|
|
2
|
-
import transport__default, { ProtocolType, LowlevelTransportSharedPlugin, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
2
|
+
import transport__default, { ProtocolType, LowlevelTransportSharedPlugin, LowLevelDevice, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
3
3
|
import EventEmitter from 'events';
|
|
4
4
|
|
|
5
5
|
type LowLevelAcquireInput = {
|
|
@@ -15,18 +15,14 @@ declare class LowlevelTransport {
|
|
|
15
15
|
emitter?: EventEmitter;
|
|
16
16
|
plugin: LowlevelTransportSharedPlugin;
|
|
17
17
|
private deviceProtocol;
|
|
18
|
+
private deviceProtocolHints;
|
|
18
19
|
private protocolV2Assemblers;
|
|
19
20
|
getProtocolType(path: string): ProtocolType;
|
|
20
21
|
init(logger: any, emitter: EventEmitter, plugin: LowlevelTransportSharedPlugin): void;
|
|
21
22
|
configure(signedData: any): void;
|
|
22
23
|
configureProtocolV2(signedData: any): void;
|
|
23
24
|
listen(): void;
|
|
24
|
-
enumerate(): Promise<
|
|
25
|
-
protocolType?: ProtocolType | undefined;
|
|
26
|
-
commType: transport.OneKeyDeviceCommType;
|
|
27
|
-
id: string;
|
|
28
|
-
name: string;
|
|
29
|
-
}[]>;
|
|
25
|
+
enumerate(): Promise<LowLevelDevice[]>;
|
|
30
26
|
acquire(input: LowLevelAcquireInput): Promise<{
|
|
31
27
|
uuid: string;
|
|
32
28
|
protocolType: ProtocolType;
|
|
@@ -37,6 +33,7 @@ declare class LowlevelTransport {
|
|
|
37
33
|
private createProtocolTimeoutError;
|
|
38
34
|
private createProtocolMismatchError;
|
|
39
35
|
private detectProtocol;
|
|
36
|
+
private resetConnectionAfterProbe;
|
|
40
37
|
private probeProtocolV1;
|
|
41
38
|
private probeProtocolV2;
|
|
42
39
|
private receiveHex;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAqBpD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,MAAM,EAAE,6BAA6B,CAAuC;IAE5E,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IAI3C,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B;IAO9E,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAInC,MAAM;IAIA,SAAS;IAWT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IAuBnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAapB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA6BlB,cAAc;IAuC5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;YAOrB,cAAc;YAiDd,yBAAyB;YA0BzB,eAAe;YAef,eAAe;YAoBf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAOpB,cAAc;IAsC5B,MAAM;CAGP"}
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
|
42
42
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
43
43
|
const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30000;
|
|
44
44
|
const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
|
|
45
|
-
function
|
|
45
|
+
function inferProtocolHintFromDeviceName(name) {
|
|
46
46
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
47
47
|
}
|
|
48
48
|
function isProtocolV1TransportChunk(data) {
|
|
@@ -56,6 +56,7 @@ class LowlevelTransport {
|
|
|
56
56
|
this.configured = false;
|
|
57
57
|
this.plugin = {};
|
|
58
58
|
this.deviceProtocol = new Map();
|
|
59
|
+
this.deviceProtocolHints = new Map();
|
|
59
60
|
this.protocolV2Assemblers = new Map();
|
|
60
61
|
}
|
|
61
62
|
getProtocolType(path) {
|
|
@@ -82,16 +83,16 @@ class LowlevelTransport {
|
|
|
82
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
83
84
|
const devices = yield this.plugin.enumerate();
|
|
84
85
|
return devices.map((device) => {
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
this.
|
|
86
|
+
const protocolHint = inferProtocolHintFromDeviceName(device.name);
|
|
87
|
+
if (protocolHint) {
|
|
88
|
+
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
88
89
|
}
|
|
89
|
-
return
|
|
90
|
+
return device;
|
|
90
91
|
});
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
94
|
acquire(input) {
|
|
94
|
-
var _a
|
|
95
|
+
var _a;
|
|
95
96
|
return __awaiter(this, void 0, void 0, function* () {
|
|
96
97
|
try {
|
|
97
98
|
yield this.plugin.connect(input.uuid);
|
|
@@ -101,8 +102,10 @@ class LowlevelTransport {
|
|
|
101
102
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.LowlevelTrasnportConnectError, (_a = error.message) !== null && _a !== void 0 ? _a : error);
|
|
102
103
|
}
|
|
103
104
|
this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler());
|
|
104
|
-
const
|
|
105
|
-
|
|
105
|
+
const protocolHint = input.expectedProtocol
|
|
106
|
+
? undefined
|
|
107
|
+
: this.deviceProtocolHints.get(input.uuid);
|
|
108
|
+
const protocolType = yield this.detectProtocol(input.uuid, input.expectedProtocol, protocolHint);
|
|
106
109
|
return { uuid: input.uuid, protocolType };
|
|
107
110
|
});
|
|
108
111
|
}
|
|
@@ -111,6 +114,7 @@ class LowlevelTransport {
|
|
|
111
114
|
try {
|
|
112
115
|
yield this.plugin.disconnect(uuid);
|
|
113
116
|
this.deviceProtocol.delete(uuid);
|
|
117
|
+
this.deviceProtocolHints.delete(uuid);
|
|
114
118
|
this.protocolV2Assemblers.delete(uuid);
|
|
115
119
|
return true;
|
|
116
120
|
}
|
|
@@ -179,8 +183,8 @@ class LowlevelTransport {
|
|
|
179
183
|
createProtocolMismatchError(expected) {
|
|
180
184
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
181
185
|
}
|
|
182
|
-
detectProtocol(uuid, expectedProtocol) {
|
|
183
|
-
var _a, _b, _c, _d;
|
|
186
|
+
detectProtocol(uuid, expectedProtocol, protocolHint) {
|
|
187
|
+
var _a, _b, _c, _d, _e;
|
|
184
188
|
return __awaiter(this, void 0, void 0, function* () {
|
|
185
189
|
if (expectedProtocol === 'V2') {
|
|
186
190
|
if (yield this.probeProtocolV2(uuid)) {
|
|
@@ -198,21 +202,49 @@ class LowlevelTransport {
|
|
|
198
202
|
}
|
|
199
203
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
200
204
|
}
|
|
205
|
+
if (protocolHint === 'V2' && (yield this.probeProtocolV2(uuid))) {
|
|
206
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
207
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
|
|
208
|
+
return 'V2';
|
|
209
|
+
}
|
|
201
210
|
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
202
211
|
if (cachedProtocol === 'V2' && (yield this.probeProtocolV2(uuid))) {
|
|
203
212
|
this.deviceProtocol.set(uuid, 'V2');
|
|
204
|
-
(
|
|
213
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
|
|
205
214
|
return 'V2';
|
|
206
215
|
}
|
|
207
216
|
let protocol = 'V1';
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
const protocolV1Detected = yield this.probeProtocolV1(uuid);
|
|
218
|
+
if (!protocolV1Detected) {
|
|
219
|
+
yield this.resetConnectionAfterProbe(uuid, 'V1');
|
|
220
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
221
|
+
protocol = 'V2';
|
|
222
|
+
}
|
|
210
223
|
}
|
|
211
224
|
this.deviceProtocol.set(uuid, protocol);
|
|
212
|
-
(
|
|
225
|
+
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
213
226
|
return protocol;
|
|
214
227
|
});
|
|
215
228
|
}
|
|
229
|
+
resetConnectionAfterProbe(uuid, protocol) {
|
|
230
|
+
var _a, _b, _c, _d;
|
|
231
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
232
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
233
|
+
try {
|
|
234
|
+
yield this.plugin.disconnect(uuid);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[LowlevelTransport] disconnect after Protocol ${protocol} probe failed:`, error);
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
yield this.plugin.connect(uuid);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] reconnect after Protocol ${protocol} probe failed:`, error);
|
|
244
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.LowlevelTrasnportConnectError, (_d = error.message) !== null && _d !== void 0 ? _d : error);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
216
248
|
probeProtocolV1(uuid) {
|
|
217
249
|
var _a;
|
|
218
250
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -243,10 +275,11 @@ class LowlevelTransport {
|
|
|
243
275
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
244
276
|
logger: this.Log,
|
|
245
277
|
logPrefix: 'ProtocolV2 Lowlevel-BLE',
|
|
246
|
-
onProbeFailed: () => {
|
|
247
|
-
var
|
|
248
|
-
(
|
|
249
|
-
|
|
278
|
+
onProbeFailed: () => __awaiter(this, void 0, void 0, function* () {
|
|
279
|
+
var _b;
|
|
280
|
+
(_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
281
|
+
yield this.resetConnectionAfterProbe(uuid, 'V2');
|
|
282
|
+
}),
|
|
250
283
|
});
|
|
251
284
|
});
|
|
252
285
|
}
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-lowlevel",
|
|
3
|
-
"version": "1.1.27-alpha.
|
|
3
|
+
"version": "1.1.27-alpha.32",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
"scripts": {
|
|
16
16
|
"dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
|
|
17
17
|
"build": "rimraf dist && rollup -c ../../build/rollup.config.js",
|
|
18
|
+
"test": "jest",
|
|
18
19
|
"lint": "eslint .",
|
|
19
20
|
"lint:fix": "eslint . --fix"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@onekeyfe/hd-shared": "1.1.27-alpha.
|
|
23
|
-
"@onekeyfe/hd-transport": "1.1.27-alpha.
|
|
23
|
+
"@onekeyfe/hd-shared": "1.1.27-alpha.32",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.1.27-alpha.32"
|
|
24
25
|
},
|
|
25
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "04f92052a18dcf17e4a347f3003b4c4bbf062681"
|
|
26
27
|
}
|
package/src/index.ts
CHANGED
|
@@ -28,7 +28,7 @@ const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
|
28
28
|
const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30_000;
|
|
29
29
|
const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
|
|
30
30
|
|
|
31
|
-
function
|
|
31
|
+
function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
|
|
32
32
|
return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -55,6 +55,8 @@ export default class LowlevelTransport {
|
|
|
55
55
|
|
|
56
56
|
private deviceProtocol: Map<string, ProtocolType> = new Map();
|
|
57
57
|
|
|
58
|
+
private deviceProtocolHints: Map<string, ProtocolType> = new Map();
|
|
59
|
+
|
|
58
60
|
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
59
61
|
|
|
60
62
|
getProtocolType(path: string): ProtocolType {
|
|
@@ -85,14 +87,11 @@ export default class LowlevelTransport {
|
|
|
85
87
|
async enumerate() {
|
|
86
88
|
const devices = await this.plugin.enumerate();
|
|
87
89
|
return devices.map((device: LowLevelDevice) => {
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
this.
|
|
90
|
+
const protocolHint = inferProtocolHintFromDeviceName(device.name);
|
|
91
|
+
if (protocolHint) {
|
|
92
|
+
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
91
93
|
}
|
|
92
|
-
return
|
|
93
|
-
...device,
|
|
94
|
-
...(protocolType ? { protocolType } : {}),
|
|
95
|
-
};
|
|
94
|
+
return device;
|
|
96
95
|
});
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -108,8 +107,14 @@ export default class LowlevelTransport {
|
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
this.protocolV2Assemblers.set(input.uuid, new ProtocolV2FrameAssembler());
|
|
111
|
-
const
|
|
112
|
-
|
|
110
|
+
const protocolHint = input.expectedProtocol
|
|
111
|
+
? undefined
|
|
112
|
+
: this.deviceProtocolHints.get(input.uuid);
|
|
113
|
+
const protocolType = await this.detectProtocol(
|
|
114
|
+
input.uuid,
|
|
115
|
+
input.expectedProtocol,
|
|
116
|
+
protocolHint
|
|
117
|
+
);
|
|
113
118
|
return { uuid: input.uuid, protocolType };
|
|
114
119
|
}
|
|
115
120
|
|
|
@@ -117,6 +122,7 @@ export default class LowlevelTransport {
|
|
|
117
122
|
try {
|
|
118
123
|
await this.plugin.disconnect(uuid);
|
|
119
124
|
this.deviceProtocol.delete(uuid);
|
|
125
|
+
this.deviceProtocolHints.delete(uuid);
|
|
120
126
|
this.protocolV2Assemblers.delete(uuid);
|
|
121
127
|
return true;
|
|
122
128
|
} catch (error) {
|
|
@@ -213,7 +219,8 @@ export default class LowlevelTransport {
|
|
|
213
219
|
|
|
214
220
|
private async detectProtocol(
|
|
215
221
|
uuid: string,
|
|
216
|
-
expectedProtocol?: ProtocolType
|
|
222
|
+
expectedProtocol?: ProtocolType,
|
|
223
|
+
protocolHint?: ProtocolType
|
|
217
224
|
): Promise<ProtocolType> {
|
|
218
225
|
if (expectedProtocol === 'V2') {
|
|
219
226
|
if (await this.probeProtocolV2(uuid)) {
|
|
@@ -233,6 +240,12 @@ export default class LowlevelTransport {
|
|
|
233
240
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
234
241
|
}
|
|
235
242
|
|
|
243
|
+
if (protocolHint === 'V2' && (await this.probeProtocolV2(uuid))) {
|
|
244
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
245
|
+
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
|
|
246
|
+
return 'V2';
|
|
247
|
+
}
|
|
248
|
+
|
|
236
249
|
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
237
250
|
if (cachedProtocol === 'V2' && (await this.probeProtocolV2(uuid))) {
|
|
238
251
|
this.deviceProtocol.set(uuid, 'V2');
|
|
@@ -241,14 +254,44 @@ export default class LowlevelTransport {
|
|
|
241
254
|
}
|
|
242
255
|
|
|
243
256
|
let protocol: ProtocolType = 'V1';
|
|
244
|
-
|
|
245
|
-
|
|
257
|
+
const protocolV1Detected = await this.probeProtocolV1(uuid);
|
|
258
|
+
if (!protocolV1Detected) {
|
|
259
|
+
await this.resetConnectionAfterProbe(uuid, 'V1');
|
|
260
|
+
if (await this.probeProtocolV2(uuid)) {
|
|
261
|
+
protocol = 'V2';
|
|
262
|
+
}
|
|
246
263
|
}
|
|
247
264
|
this.deviceProtocol.set(uuid, protocol);
|
|
248
265
|
this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
249
266
|
return protocol;
|
|
250
267
|
}
|
|
251
268
|
|
|
269
|
+
private async resetConnectionAfterProbe(uuid: string, protocol: ProtocolType) {
|
|
270
|
+
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
await this.plugin.disconnect(uuid);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
this.Log?.debug(
|
|
276
|
+
`[LowlevelTransport] disconnect after Protocol ${protocol} probe failed:`,
|
|
277
|
+
error
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
await this.plugin.connect(uuid);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
this.Log?.debug(
|
|
285
|
+
`[LowlevelTransport] reconnect after Protocol ${protocol} probe failed:`,
|
|
286
|
+
error
|
|
287
|
+
);
|
|
288
|
+
throw ERRORS.TypedError(
|
|
289
|
+
HardwareErrorCode.LowlevelTrasnportConnectError,
|
|
290
|
+
error.message ?? error
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
252
295
|
private async probeProtocolV1(uuid: string) {
|
|
253
296
|
if (!this._messages) {
|
|
254
297
|
return false;
|
|
@@ -277,8 +320,9 @@ export default class LowlevelTransport {
|
|
|
277
320
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
278
321
|
logger: this.Log,
|
|
279
322
|
logPrefix: 'ProtocolV2 Lowlevel-BLE',
|
|
280
|
-
onProbeFailed: () => {
|
|
323
|
+
onProbeFailed: async () => {
|
|
281
324
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
325
|
+
await this.resetConnectionAfterProbe(uuid, 'V2');
|
|
282
326
|
},
|
|
283
327
|
});
|
|
284
328
|
}
|