@onekeyfe/hd-transport-web-device 1.2.0-alpha.99 → 1.2.0
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 +289 -32
- package/__tests__/webusb-protocol-cache.test.ts +276 -0
- package/__tests__/webusb-protocol-v2-timeout.test.ts +22 -8
- package/dist/electron-ble-transport.d.ts +6 -1
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +64 -3
- package/dist/index.js +232 -110
- package/dist/webusb.d.ts +11 -1
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +172 -85
- package/src/webusb.ts +201 -33
package/dist/index.js
CHANGED
|
@@ -54,8 +54,22 @@ const PACKET_IO_RETRY_DELAY = 300;
|
|
|
54
54
|
const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
|
|
55
55
|
const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
|
|
56
56
|
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
let activeWebUsbTransport;
|
|
58
|
+
let usbDisconnectListenerAttached = false;
|
|
59
|
+
function registerActiveWebUsbTransport(instance, usb) {
|
|
60
|
+
activeWebUsbTransport = instance;
|
|
61
|
+
if (usbDisconnectListenerAttached)
|
|
62
|
+
return;
|
|
63
|
+
usbDisconnectListenerAttached = true;
|
|
64
|
+
usb.addEventListener('disconnect', event => {
|
|
65
|
+
if (!event.device)
|
|
66
|
+
return;
|
|
67
|
+
const path = hdShared.resolveOneKeyUsbDevicePath(event.device);
|
|
68
|
+
if (!path)
|
|
69
|
+
return;
|
|
70
|
+
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.markProtocolStale(path);
|
|
71
|
+
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.emitDeviceDisconnect(path, event.device);
|
|
72
|
+
});
|
|
59
73
|
}
|
|
60
74
|
class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
61
75
|
constructor() {
|
|
@@ -65,7 +79,11 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
65
79
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
66
80
|
});
|
|
67
81
|
this.deviceProtocol = new Map();
|
|
82
|
+
this.confirmedDeviceProtocols = new Map();
|
|
68
83
|
this.deviceProtocolHints = new Map();
|
|
84
|
+
this.staleProtocolPaths = new Set();
|
|
85
|
+
this.acquiredPaths = new Set();
|
|
86
|
+
this.probedDeviceObjects = new Map();
|
|
69
87
|
this.deviceEndpoints = new Map();
|
|
70
88
|
this.name = 'WebUsbTransport';
|
|
71
89
|
this.stopped = false;
|
|
@@ -75,13 +93,27 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
75
93
|
this.endpointId = ENDPOINT_ID;
|
|
76
94
|
this.interfaceId = INTERFACE_ID;
|
|
77
95
|
}
|
|
78
|
-
init(logger) {
|
|
96
|
+
init(logger, emitter) {
|
|
79
97
|
this.Log = logger;
|
|
98
|
+
this.emitter = emitter;
|
|
80
99
|
const { usb } = navigator;
|
|
81
100
|
if (!usb) {
|
|
82
101
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
|
|
83
102
|
}
|
|
84
103
|
this.usb = usb;
|
|
104
|
+
registerActiveWebUsbTransport(this, usb);
|
|
105
|
+
}
|
|
106
|
+
emitDeviceDisconnect(path, device) {
|
|
107
|
+
var _a, _b;
|
|
108
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
109
|
+
name: (_b = device === null || device === void 0 ? void 0 : device.productName) !== null && _b !== void 0 ? _b : '',
|
|
110
|
+
id: path,
|
|
111
|
+
connectId: path,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
markProtocolStale(path) {
|
|
115
|
+
this.staleProtocolPaths.add(path);
|
|
116
|
+
this.probedDeviceObjects.delete(path);
|
|
85
117
|
}
|
|
86
118
|
configure(signedData) {
|
|
87
119
|
const messages = parseConfigure$1(signedData);
|
|
@@ -129,26 +161,29 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
129
161
|
const devices = yield this.usb.getDevices();
|
|
130
162
|
const onekeyDevices = devices.filter(dev => {
|
|
131
163
|
const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
|
|
132
|
-
|
|
133
|
-
return isOneKey && hasSerialNumber && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
164
|
+
return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
134
165
|
});
|
|
135
|
-
this.deviceList = onekeyDevices.
|
|
136
|
-
const path = device
|
|
137
|
-
|
|
166
|
+
this.deviceList = onekeyDevices.flatMap(device => {
|
|
167
|
+
const path = hdShared.resolveOneKeyUsbDevicePath(device);
|
|
168
|
+
if (!path)
|
|
169
|
+
return [];
|
|
170
|
+
const protocolHint = hdShared.inferProtocolHintFromUsbId(device.vendorId, device.productId);
|
|
138
171
|
if (protocolHint) {
|
|
139
172
|
this.deviceProtocolHints.set(path, protocolHint);
|
|
140
173
|
}
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
174
|
+
return [
|
|
175
|
+
{
|
|
176
|
+
path,
|
|
177
|
+
device,
|
|
178
|
+
commType: 'webusb',
|
|
179
|
+
},
|
|
180
|
+
];
|
|
146
181
|
});
|
|
147
182
|
return this.deviceList;
|
|
148
183
|
});
|
|
149
184
|
}
|
|
150
185
|
acquire(input) {
|
|
151
|
-
var _a, _b, _c, _d;
|
|
186
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
152
187
|
return __awaiter(this, void 0, void 0, function* () {
|
|
153
188
|
if (!input.path)
|
|
154
189
|
return;
|
|
@@ -156,18 +191,63 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
156
191
|
yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
|
|
157
192
|
yield this.closeOpenDevice(input.path);
|
|
158
193
|
yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
194
|
+
if (input.skipProtocolProbe) {
|
|
195
|
+
if (!input.expectedProtocol) {
|
|
196
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires an expected protocol');
|
|
197
|
+
}
|
|
198
|
+
if (this.confirmedDeviceProtocols.get(input.path) !== input.expectedProtocol) {
|
|
199
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires a previously confirmed protocol for this WebUSB endpoint');
|
|
200
|
+
}
|
|
201
|
+
this.staleProtocolPaths.delete(input.path);
|
|
202
|
+
this.deviceProtocol.set(input.path, input.expectedProtocol);
|
|
203
|
+
const currentDevice = (_b = this.deviceList.find(d => d.path === input.path)) === null || _b === void 0 ? void 0 : _b.device;
|
|
204
|
+
if (currentDevice) {
|
|
205
|
+
this.probedDeviceObjects.set(input.path, currentDevice);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (input.forceProtocolDetection) {
|
|
209
|
+
this.staleProtocolPaths.delete(input.path);
|
|
210
|
+
this.deviceProtocol.delete(input.path);
|
|
211
|
+
this.deviceProtocolHints.delete(input.path);
|
|
212
|
+
}
|
|
213
|
+
if (!input.skipProtocolProbe && this.staleProtocolPaths.has(input.path)) {
|
|
214
|
+
this.staleProtocolPaths.delete(input.path);
|
|
215
|
+
this.deviceProtocol.delete(input.path);
|
|
216
|
+
}
|
|
217
|
+
if (!input.skipProtocolProbe && this.deviceProtocol.has(input.path)) {
|
|
218
|
+
const currentDevice = (_c = this.deviceList.find(d => d.path === input.path)) === null || _c === void 0 ? void 0 : _c.device;
|
|
219
|
+
if (!currentDevice || currentDevice !== this.probedDeviceObjects.get(input.path)) {
|
|
220
|
+
this.deviceProtocol.delete(input.path);
|
|
221
|
+
}
|
|
165
222
|
}
|
|
166
|
-
|
|
223
|
+
const cachedProtocol = this.deviceProtocol.get(input.path);
|
|
224
|
+
if (!input.skipProtocolProbe &&
|
|
225
|
+
cachedProtocol &&
|
|
226
|
+
input.expectedProtocol &&
|
|
227
|
+
cachedProtocol !== input.expectedProtocol) {
|
|
228
|
+
this.deviceProtocol.delete(input.path);
|
|
229
|
+
}
|
|
230
|
+
if (!this.deviceProtocol.has(input.path)) {
|
|
231
|
+
const usbDevice = (_d = this.deviceList.find(device => device.path === input.path)) === null || _d === void 0 ? void 0 : _d.device;
|
|
232
|
+
const protocolHint = input.expectedProtocol
|
|
233
|
+
? undefined
|
|
234
|
+
: (_f = (_e = input.protocolHint) !== null && _e !== void 0 ? _e : this.deviceProtocolHints.get(input.path)) !== null && _f !== void 0 ? _f : hdShared.inferProtocolHintFromUsbId(usbDevice === null || usbDevice === void 0 ? void 0 : usbDevice.vendorId, usbDevice === null || usbDevice === void 0 ? void 0 : usbDevice.productId);
|
|
235
|
+
if (protocolHint) {
|
|
236
|
+
this.deviceProtocolHints.set(input.path, protocolHint);
|
|
237
|
+
}
|
|
238
|
+
const detectedProtocol = yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
|
|
239
|
+
this.confirmedDeviceProtocols.set(input.path, detectedProtocol);
|
|
240
|
+
const probedDevice = (_g = this.deviceList.find(d => d.path === input.path)) === null || _g === void 0 ? void 0 : _g.device;
|
|
241
|
+
if (probedDevice) {
|
|
242
|
+
this.probedDeviceObjects.set(input.path, probedDevice);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
this.acquiredPaths.add(input.path);
|
|
167
246
|
return yield Promise.resolve(input.path);
|
|
168
247
|
}
|
|
169
248
|
catch (e) {
|
|
170
249
|
this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
|
|
250
|
+
this.acquiredPaths.delete(input.path);
|
|
171
251
|
yield this.closeOpenDevice(input.path);
|
|
172
252
|
throw e;
|
|
173
253
|
}
|
|
@@ -177,7 +257,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
177
257
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
178
258
|
}
|
|
179
259
|
createProtocolProbeTimeoutError(expected, attempts) {
|
|
180
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.
|
|
260
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInitializeFailed, `Protocol ${expected} probe timeout after ${attempts} attempts`);
|
|
181
261
|
}
|
|
182
262
|
createProtocolDetectionError() {
|
|
183
263
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
@@ -351,8 +431,18 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
351
431
|
}
|
|
352
432
|
});
|
|
353
433
|
}
|
|
434
|
+
assertAcquired(path) {
|
|
435
|
+
if (!this.acquiredPaths.has(path)) {
|
|
436
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device is not acquired for ${path}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
354
439
|
post(session, name, data) {
|
|
355
440
|
return __awaiter(this, void 0, void 0, function* () {
|
|
441
|
+
this.assertAcquired(session);
|
|
442
|
+
if (this.deviceProtocol.get(session) === 'V2') {
|
|
443
|
+
yield this.sendProtocolV2UsbFlowControl(session, name, data);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
356
446
|
yield this.call(session, name, data);
|
|
357
447
|
});
|
|
358
448
|
}
|
|
@@ -381,6 +471,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
381
471
|
var _a;
|
|
382
472
|
return __awaiter(this, void 0, void 0, function* () {
|
|
383
473
|
this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
|
|
474
|
+
this.staleProtocolPaths.add(path);
|
|
384
475
|
yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
385
476
|
try {
|
|
386
477
|
const currentDevice = yield this.findDevice(path);
|
|
@@ -595,6 +686,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
595
686
|
if (this.messages == null) {
|
|
596
687
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
597
688
|
}
|
|
689
|
+
this.assertAcquired(path);
|
|
598
690
|
const device = yield this.findDevice(path);
|
|
599
691
|
if (!device) {
|
|
600
692
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
@@ -603,9 +695,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
603
695
|
if (!protocol) {
|
|
604
696
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
605
697
|
}
|
|
606
|
-
if (!transport.shouldSuppressHighVolumeCallLog(name)) {
|
|
607
|
-
this.Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
|
|
608
|
-
}
|
|
609
698
|
if (protocol === 'V2') {
|
|
610
699
|
return this.callProtocolV2(path, name, data, options);
|
|
611
700
|
}
|
|
@@ -677,10 +766,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
677
766
|
}
|
|
678
767
|
release(path) {
|
|
679
768
|
return __awaiter(this, void 0, void 0, function* () {
|
|
769
|
+
this.acquiredPaths.delete(path);
|
|
680
770
|
yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
|
|
681
771
|
yield this.closeOpenDevice(path);
|
|
682
|
-
this.deviceProtocol.delete(path);
|
|
683
|
-
this.deviceProtocolHints.delete(path);
|
|
684
772
|
this.deviceEndpoints.delete(path);
|
|
685
773
|
});
|
|
686
774
|
}
|
|
@@ -734,14 +822,11 @@ function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapa
|
|
|
734
822
|
}
|
|
735
823
|
|
|
736
824
|
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
737
|
-
function inferProtocolHintFromDeviceName(name) {
|
|
738
|
-
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
739
|
-
}
|
|
740
825
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
741
826
|
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
742
827
|
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
743
828
|
const BLE_WRITE_DELAY_MS = 5;
|
|
744
|
-
const PROTOCOL_PROBE_TIMEOUT_MS =
|
|
829
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
|
|
745
830
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
746
831
|
class ElectronBleTransport {
|
|
747
832
|
constructor() {
|
|
@@ -752,6 +837,7 @@ class ElectronBleTransport {
|
|
|
752
837
|
this.connectedDevices = new Set();
|
|
753
838
|
this.deviceProtocol = new Map();
|
|
754
839
|
this.deviceProtocolHints = new Map();
|
|
840
|
+
this.confirmedProtocolV2 = new Set();
|
|
755
841
|
this.deviceMtus = new Map();
|
|
756
842
|
this.devicePacketCapacities = new Map();
|
|
757
843
|
this.v1Buffers = new Map();
|
|
@@ -782,11 +868,32 @@ class ElectronBleTransport {
|
|
|
782
868
|
this.warnedMissingRelease = false;
|
|
783
869
|
this.notificationCleanups = new Map();
|
|
784
870
|
this.mtuCleanups = new Map();
|
|
785
|
-
this.disconnectCleanups = new Map();
|
|
786
871
|
this.notificationTokens = new Map();
|
|
787
872
|
this.nextNotificationToken = 1;
|
|
788
873
|
}
|
|
789
|
-
|
|
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
|
+
}
|
|
790
897
|
if (error && typeof error === 'object') {
|
|
791
898
|
if ('code' in error) {
|
|
792
899
|
if (error.code === hdShared.HardwareErrorCode.BlePoweredOff) {
|
|
@@ -842,11 +949,6 @@ class ElectronBleTransport {
|
|
|
842
949
|
mtuCleanup();
|
|
843
950
|
this.mtuCleanups.delete(deviceId);
|
|
844
951
|
}
|
|
845
|
-
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
846
|
-
if (disconnectCleanup) {
|
|
847
|
-
disconnectCleanup();
|
|
848
|
-
this.disconnectCleanups.delete(deviceId);
|
|
849
|
-
}
|
|
850
952
|
}
|
|
851
953
|
init(logger, emitter) {
|
|
852
954
|
var _a, _b;
|
|
@@ -855,8 +957,26 @@ class ElectronBleTransport {
|
|
|
855
957
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
856
958
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble BLE API is not available. Please ensure you are running in Electron with Noble support.');
|
|
857
959
|
}
|
|
960
|
+
this.subscribeHostDisconnects();
|
|
858
961
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Transport initialized');
|
|
859
962
|
}
|
|
963
|
+
subscribeHostDisconnects() {
|
|
964
|
+
var _a, _b, _c;
|
|
965
|
+
(_a = this.hostDisconnectCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
966
|
+
this.hostDisconnectCleanup = (_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.onDeviceDisconnected((disconnectedDevice) => {
|
|
967
|
+
var _a, _b, _c;
|
|
968
|
+
const uuid = disconnectedDevice === null || disconnectedDevice === void 0 ? void 0 : disconnectedDevice.id;
|
|
969
|
+
if (!uuid)
|
|
970
|
+
return;
|
|
971
|
+
this.cleanupDeviceState(uuid);
|
|
972
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Device link dropped:', uuid, (_b = disconnectedDevice.reason) !== null && _b !== void 0 ? _b : hdShared.EBleDisconnectReason.DeviceDisconnected);
|
|
973
|
+
(_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
974
|
+
name: disconnectedDevice.name,
|
|
975
|
+
id: uuid,
|
|
976
|
+
connectId: uuid,
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
}
|
|
860
980
|
configure(signedData) {
|
|
861
981
|
this._messages = parseConfigure(signedData);
|
|
862
982
|
this.configured = true;
|
|
@@ -891,10 +1011,6 @@ class ElectronBleTransport {
|
|
|
891
1011
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] enumerate found ${devices.length} device(s):`);
|
|
892
1012
|
for (const dev of devices) {
|
|
893
1013
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] id="${dev.id}" name="${dev.name}"`);
|
|
894
|
-
const protocolHint = inferProtocolHintFromDeviceName(dev.name);
|
|
895
|
-
if (protocolHint) {
|
|
896
|
-
this.deviceProtocolHints.set(dev.id, protocolHint);
|
|
897
|
-
}
|
|
898
1014
|
}
|
|
899
1015
|
return devices.map(device => toBleDescriptor(device));
|
|
900
1016
|
}
|
|
@@ -905,9 +1021,12 @@ class ElectronBleTransport {
|
|
|
905
1021
|
});
|
|
906
1022
|
}
|
|
907
1023
|
acquire(input) {
|
|
908
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
1024
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
909
1025
|
return __awaiter(this, void 0, void 0, function* () {
|
|
910
1026
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
1027
|
+
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
1028
|
+
? expectedProtocol === 'V2'
|
|
1029
|
+
: this.confirmedProtocolV2.has(uuid);
|
|
911
1030
|
if (!uuid) {
|
|
912
1031
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
913
1032
|
}
|
|
@@ -930,7 +1049,7 @@ class ElectronBleTransport {
|
|
|
930
1049
|
}
|
|
931
1050
|
const protocolHint = expectedProtocol
|
|
932
1051
|
? undefined
|
|
933
|
-
: (
|
|
1052
|
+
: (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid);
|
|
934
1053
|
if (protocolHint) {
|
|
935
1054
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
936
1055
|
}
|
|
@@ -939,7 +1058,7 @@ class ElectronBleTransport {
|
|
|
939
1058
|
this.connectedDevices.add(uuid);
|
|
940
1059
|
}
|
|
941
1060
|
catch (error) {
|
|
942
|
-
this.handleBluetoothError(error);
|
|
1061
|
+
this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
|
|
943
1062
|
}
|
|
944
1063
|
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
945
1064
|
if (mtuCleanup) {
|
|
@@ -947,44 +1066,35 @@ class ElectronBleTransport {
|
|
|
947
1066
|
}
|
|
948
1067
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
949
1068
|
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
950
|
-
|
|
1069
|
+
try {
|
|
1070
|
+
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
1071
|
+
}
|
|
1072
|
+
catch (error) {
|
|
1073
|
+
this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
|
|
1074
|
+
}
|
|
951
1075
|
yield this.refreshBlePacketCapacity(uuid);
|
|
952
1076
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
953
1077
|
this.notificationCleanups.set(uuid, cleanup);
|
|
954
|
-
const connectionToken = this.notificationTokens.get(uuid);
|
|
955
1078
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
956
1079
|
if (protocolType === 'V2') {
|
|
957
|
-
(
|
|
1080
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Electron BLE] Protocol V2 write configured', {
|
|
958
1081
|
writeMode: 'withoutResponse',
|
|
959
1082
|
negotiatedMtu: this.deviceMtus.get(uuid),
|
|
960
|
-
packetCapacity: (
|
|
1083
|
+
packetCapacity: (_d = this.devicePacketCapacities.get(uuid)) !== null && _d !== void 0 ? _d : BLE_PACKET_SIZE_FALLBACK,
|
|
961
1084
|
});
|
|
962
1085
|
}
|
|
963
|
-
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
964
|
-
var _a;
|
|
965
|
-
if (disconnectedDevice.id === uuid &&
|
|
966
|
-
this.notificationTokens.get(uuid) === connectionToken) {
|
|
967
|
-
this.cleanupDeviceState(uuid);
|
|
968
|
-
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
969
|
-
name: disconnectedDevice.name,
|
|
970
|
-
id: disconnectedDevice.id,
|
|
971
|
-
connectId: disconnectedDevice.id,
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
});
|
|
975
|
-
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
976
1086
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
977
1087
|
}
|
|
978
1088
|
catch (error) {
|
|
979
|
-
(
|
|
1089
|
+
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.error('[Electron BLE] acquire failed:', error);
|
|
980
1090
|
try {
|
|
981
|
-
if (((
|
|
1091
|
+
if (((_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
982
1092
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
983
1093
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
984
1094
|
}
|
|
985
1095
|
}
|
|
986
1096
|
catch (cleanupError) {
|
|
987
|
-
(
|
|
1097
|
+
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
988
1098
|
}
|
|
989
1099
|
this.cleanupDeviceState(uuid);
|
|
990
1100
|
throw error;
|
|
@@ -1051,8 +1161,9 @@ class ElectronBleTransport {
|
|
|
1051
1161
|
}
|
|
1052
1162
|
});
|
|
1053
1163
|
}
|
|
1054
|
-
createProtocolMismatchError(expected) {
|
|
1055
|
-
|
|
1164
|
+
createProtocolMismatchError(expected, uuid) {
|
|
1165
|
+
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
1166
|
+
return hdShared.ERRORS.TypedError(isStaleV2Bond ? hdShared.HardwareErrorCode.BleDeviceBondError : hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1056
1167
|
}
|
|
1057
1168
|
createProtocolDetectionError() {
|
|
1058
1169
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
@@ -1066,20 +1177,18 @@ class ElectronBleTransport {
|
|
|
1066
1177
|
var _a, _b, _c;
|
|
1067
1178
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1068
1179
|
if (expectedProtocol === 'V1') {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
return 'V1';
|
|
1073
|
-
}
|
|
1074
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1180
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1181
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V1 (expected, no probe)`);
|
|
1182
|
+
return 'V1';
|
|
1075
1183
|
}
|
|
1076
1184
|
if (expectedProtocol === 'V2') {
|
|
1077
1185
|
if (yield this.probeProtocolV2(uuid)) {
|
|
1078
1186
|
this.deviceProtocol.set(uuid, 'V2');
|
|
1187
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1079
1188
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1080
1189
|
return 'V2';
|
|
1081
1190
|
}
|
|
1082
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1191
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1083
1192
|
}
|
|
1084
1193
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1085
1194
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -1090,6 +1199,9 @@ class ElectronBleTransport {
|
|
|
1090
1199
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1091
1200
|
if (detected) {
|
|
1092
1201
|
this.deviceProtocol.set(uuid, protocol);
|
|
1202
|
+
if (protocol === 'V2') {
|
|
1203
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1204
|
+
}
|
|
1093
1205
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
|
|
1094
1206
|
return protocol;
|
|
1095
1207
|
}
|
|
@@ -1113,7 +1225,7 @@ class ElectronBleTransport {
|
|
|
1113
1225
|
});
|
|
1114
1226
|
}
|
|
1115
1227
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1116
|
-
var _a
|
|
1228
|
+
var _a;
|
|
1117
1229
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1118
1230
|
yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
|
|
1119
1231
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
@@ -1125,37 +1237,10 @@ class ElectronBleTransport {
|
|
|
1125
1237
|
this.runPromise = null;
|
|
1126
1238
|
this.runPromiseDeviceId = null;
|
|
1127
1239
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
this.notificationCleanups.delete(uuid);
|
|
1132
|
-
}
|
|
1133
|
-
this.notificationTokens.delete(uuid);
|
|
1134
|
-
try {
|
|
1135
|
-
yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
|
|
1136
|
-
}
|
|
1137
|
-
catch (error) {
|
|
1138
|
-
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1139
|
-
}
|
|
1140
|
-
try {
|
|
1141
|
-
yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.disconnect(uuid));
|
|
1142
|
-
}
|
|
1143
|
-
catch (error) {
|
|
1144
|
-
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] disconnect after Protocol ${protocol} probe failed:`, error);
|
|
1145
|
-
}
|
|
1146
|
-
this.connectedDevices.delete(uuid);
|
|
1147
|
-
try {
|
|
1148
|
-
yield ((_j = (_h = window.desktopApi) === null || _h === void 0 ? void 0 : _h.nobleBle) === null || _j === void 0 ? void 0 : _j.connect(uuid));
|
|
1149
|
-
this.connectedDevices.add(uuid);
|
|
1150
|
-
yield ((_l = (_k = window.desktopApi) === null || _k === void 0 ? void 0 : _k.nobleBle) === null || _l === void 0 ? void 0 : _l.subscribe(uuid));
|
|
1151
|
-
yield this.refreshBlePacketCapacity(uuid);
|
|
1152
|
-
}
|
|
1153
|
-
catch (error) {
|
|
1154
|
-
(_m = this.Log) === null || _m === void 0 ? void 0 : _m.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
|
|
1155
|
-
throw error;
|
|
1240
|
+
if (!this.notificationCleanups.has(uuid)) {
|
|
1241
|
+
const cleanup = this.createNotificationSubscription(uuid);
|
|
1242
|
+
this.notificationCleanups.set(uuid, cleanup);
|
|
1156
1243
|
}
|
|
1157
|
-
const cleanup = this.createNotificationSubscription(uuid);
|
|
1158
|
-
this.notificationCleanups.set(uuid, cleanup);
|
|
1159
1244
|
});
|
|
1160
1245
|
}
|
|
1161
1246
|
probeProtocolV1(uuid) {
|
|
@@ -1172,6 +1257,9 @@ class ElectronBleTransport {
|
|
|
1172
1257
|
catch (error) {
|
|
1173
1258
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1174
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
|
+
}
|
|
1175
1263
|
return false;
|
|
1176
1264
|
}
|
|
1177
1265
|
});
|
|
@@ -1194,6 +1282,7 @@ class ElectronBleTransport {
|
|
|
1194
1282
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1195
1283
|
this.resetProtocolV2Frames(uuid);
|
|
1196
1284
|
},
|
|
1285
|
+
shouldRethrow: error => hdShared.isBleStaleBondHardwareError(error) || transport.isProtocolV2LinkDisabledError(error),
|
|
1197
1286
|
});
|
|
1198
1287
|
if (!detected) {
|
|
1199
1288
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -1208,7 +1297,16 @@ class ElectronBleTransport {
|
|
|
1208
1297
|
if (!nobleBle) {
|
|
1209
1298
|
throw new Error('Noble BLE API not available');
|
|
1210
1299
|
}
|
|
1211
|
-
|
|
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
|
+
}
|
|
1212
1310
|
});
|
|
1213
1311
|
}
|
|
1214
1312
|
refreshBlePacketCapacity(uuid) {
|
|
@@ -1277,8 +1375,27 @@ class ElectronBleTransport {
|
|
|
1277
1375
|
this.handleProtocolV2Notification(deviceId, hexData);
|
|
1278
1376
|
return;
|
|
1279
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
|
+
}
|
|
1280
1385
|
this.handleProtocolV1Notification(deviceId, hexData);
|
|
1281
1386
|
}
|
|
1387
|
+
readProtocolV2LinkDisabledFailure(deviceId, hexData) {
|
|
1388
|
+
if (!this._messages || !this._messagesV2)
|
|
1389
|
+
return undefined;
|
|
1390
|
+
const assembler = this.v2Assemblers.get(deviceId);
|
|
1391
|
+
if (!assembler)
|
|
1392
|
+
return undefined;
|
|
1393
|
+
return transport.detectProtocolV2LinkDisabledError({
|
|
1394
|
+
schemas: { protocolV1: this._messages, protocolV2: this._messagesV2 },
|
|
1395
|
+
assembler,
|
|
1396
|
+
bytes: transport.hexToBytes(hexData),
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1282
1399
|
handleProtocolV2Notification(deviceId, hexData) {
|
|
1283
1400
|
var _a;
|
|
1284
1401
|
try {
|
|
@@ -1362,7 +1479,6 @@ class ElectronBleTransport {
|
|
|
1362
1479
|
}
|
|
1363
1480
|
}
|
|
1364
1481
|
call(uuid, name, data, options) {
|
|
1365
|
-
var _a;
|
|
1366
1482
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1367
1483
|
if (!this._messages) {
|
|
1368
1484
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -1374,15 +1490,21 @@ class ElectronBleTransport {
|
|
|
1374
1490
|
if (!protocol) {
|
|
1375
1491
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1376
1492
|
}
|
|
1377
|
-
if (!transport.shouldSuppressHighVolumeCallLog(name)) {
|
|
1378
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', transport.createTransportCallLog(name, protocol, data));
|
|
1379
|
-
}
|
|
1380
1493
|
if (protocol === 'V2') {
|
|
1381
1494
|
return this.callProtocolV2(uuid, name, data, options);
|
|
1382
1495
|
}
|
|
1383
1496
|
return this.callProtocolV1(uuid, name, data, options);
|
|
1384
1497
|
});
|
|
1385
1498
|
}
|
|
1499
|
+
post(uuid, name, data) {
|
|
1500
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1501
|
+
if (this.deviceProtocol.get(uuid) === 'V2') {
|
|
1502
|
+
yield this.protocolV2Links.sendFlowControl(uuid, () => this.createProtocolV2Adapter(uuid), name, data);
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
yield this.callProtocolV1(uuid, name, data);
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1386
1508
|
callProtocolV1(uuid, name, data, options) {
|
|
1387
1509
|
var _a, _b;
|
|
1388
1510
|
return __awaiter(this, void 0, void 0, function* () {
|