@onekeyfe/hd-transport-electron 1.1.27-alpha.35 → 1.1.27-alpha.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ble-ops.d.ts +1 -6
- package/dist/ble-ops.d.ts.map +1 -1
- package/dist/{index-b01d011d.js → index-c4cb52d7.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{noble-ble-handler-4f7435c3.js → noble-ble-handler-a1af4c83.js} +56 -112
- package/dist/noble-ble-handler.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/ble-ops.ts +3 -20
- package/src/noble-ble-handler.ts +63 -154
package/dist/ble-ops.d.ts
CHANGED
|
@@ -7,12 +7,7 @@ export interface SoftRefreshParams {
|
|
|
7
7
|
subscriptionOperations: Map<string, 'subscribing' | 'unsubscribing' | 'idle'>;
|
|
8
8
|
subscribedDevices: Map<string, boolean>;
|
|
9
9
|
pairedDevices: Set<string>;
|
|
10
|
-
|
|
11
|
-
processNotificationData: (deviceId: string, data: Buffer) => {
|
|
12
|
-
isComplete: boolean;
|
|
13
|
-
completePacket?: string;
|
|
14
|
-
error?: string;
|
|
15
|
-
};
|
|
10
|
+
onNotificationData: (deviceId: string, data: Buffer) => void;
|
|
16
11
|
logger: Logger | null;
|
|
17
12
|
}
|
|
18
13
|
export declare function softRefreshSubscription(params: SoftRefreshParams): Promise<void>;
|
package/dist/ble-ops.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ble-ops.d.ts","sourceRoot":"","sources":["../src/ble-ops.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;IACxD,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;IAC9E,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,
|
|
1
|
+
{"version":3,"file":"ble-ops.d.ts","sourceRoot":"","sources":["../src/ble-ops.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;IACxD,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;IAC9E,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8CtF"}
|
|
@@ -32,7 +32,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
32
32
|
|
|
33
33
|
function initNobleBleSupport(webContents) {
|
|
34
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
-
const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-
|
|
35
|
+
const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-a1af4c83.js'); });
|
|
36
36
|
setupNobleBleHandlers(webContents);
|
|
37
37
|
});
|
|
38
38
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-c4cb52d7.js');
|
|
4
4
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
|
-
var hdTransport = require('@onekeyfe/hd-transport');
|
|
6
5
|
var pRetry = require('p-retry');
|
|
7
6
|
|
|
8
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
@@ -20,7 +19,7 @@ function safeLog(logger, level, message, ...args) {
|
|
|
20
19
|
|
|
21
20
|
function softRefreshSubscription(params) {
|
|
22
21
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
23
|
-
const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices,
|
|
22
|
+
const { deviceId, notifyCharacteristic, subscriptionOperations, subscribedDevices, pairedDevices, onNotificationData, logger, } = params;
|
|
24
23
|
if (!notifyCharacteristic) {
|
|
25
24
|
throw new Error(`Notify characteristic not available for device ${deviceId}`);
|
|
26
25
|
}
|
|
@@ -44,16 +43,7 @@ function softRefreshSubscription(params) {
|
|
|
44
43
|
pairedDevices.add(deviceId);
|
|
45
44
|
logger === null || logger === void 0 ? void 0 : logger.info('[BLE-OPS] Device paired successfully', { deviceId });
|
|
46
45
|
}
|
|
47
|
-
|
|
48
|
-
if (result.error) {
|
|
49
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[BLE-OPS] Packet processing error:', result.error);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
if (result.isComplete && result.completePacket) {
|
|
53
|
-
const appCb = notificationCallbacks.get(deviceId);
|
|
54
|
-
if (appCb)
|
|
55
|
-
appCb(result.completePacket);
|
|
56
|
-
}
|
|
46
|
+
onNotificationData(deviceId, data);
|
|
57
47
|
});
|
|
58
48
|
subscribedDevices.set(deviceId, true);
|
|
59
49
|
subscriptionOperations.set(deviceId, 'idle');
|
|
@@ -76,8 +66,8 @@ const deviceCharacteristics = new Map();
|
|
|
76
66
|
const notificationCallbacks = new Map();
|
|
77
67
|
const subscribedDevices = new Map();
|
|
78
68
|
const subscriptionOperations = new Map();
|
|
79
|
-
const devicePacketStates = new Map();
|
|
80
69
|
const ONEKEY_SERVICE_UUIDS = [hdShared.ONEKEY_SERVICE_UUID];
|
|
70
|
+
const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
|
|
81
71
|
const NORMALIZED_WRITE_UUID = '0002';
|
|
82
72
|
const NORMALIZED_NOTIFY_UUID = '0003';
|
|
83
73
|
const BLUETOOTH_INIT_TIMEOUT = 10000;
|
|
@@ -93,70 +83,40 @@ const IS_WINDOWS = process.platform === 'win32';
|
|
|
93
83
|
const ABORTABLE_WRITE_ERROR_PATTERNS = [
|
|
94
84
|
/status:\s*3/i,
|
|
95
85
|
];
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
86
|
+
function getBleUuidKey(uuid) {
|
|
87
|
+
const normalized = (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
|
|
88
|
+
return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
|
|
89
|
+
}
|
|
90
|
+
const NORMALIZED_ONEKEY_SERVICE_UUIDS = new Set([
|
|
91
|
+
...ONEKEY_SERVICE_UUIDS.map(uuid => getBleUuidKey(uuid)),
|
|
92
|
+
'0001',
|
|
93
|
+
]);
|
|
94
|
+
function isGenericBleService(uuid) {
|
|
95
|
+
return ['1800', '1801', '180a', '180f'].includes(getBleUuidKey(uuid));
|
|
96
|
+
}
|
|
97
|
+
function hasOneKeyAdvertisementService(peripheral) {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
const serviceUuids = (_b = (_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.serviceUuids) !== null && _b !== void 0 ? _b : [];
|
|
100
|
+
return serviceUuids.some(uuid => {
|
|
101
|
+
const uuidKey = getBleUuidKey(uuid);
|
|
102
|
+
return (NORMALIZED_ONEKEY_SERVICE_UUIDS.has(uuidKey) ||
|
|
103
|
+
PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(uuidKey));
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function isOneKeyPeripheral(peripheral) {
|
|
107
|
+
var _a;
|
|
108
|
+
const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || null;
|
|
109
|
+
return hdShared.isOnekeyDevice(deviceName, peripheral.id) || hasOneKeyAdvertisementService(peripheral);
|
|
110
|
+
}
|
|
111
|
+
function emitRawNotification(deviceId, data) {
|
|
112
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Raw notification', {
|
|
99
113
|
deviceId,
|
|
100
114
|
dataLength: data.length,
|
|
115
|
+
firstBytes: data.subarray(0, 8).toString('hex'),
|
|
101
116
|
});
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
devicePacketStates.set(deviceId, packetState);
|
|
106
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Initialized new packet state for device:', deviceId);
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
if (hdShared.isHeaderChunk(data)) {
|
|
110
|
-
if (data.length < MIN_HEADER_LENGTH) {
|
|
111
|
-
return { isComplete: false, error: 'Invalid header chunk: too short' };
|
|
112
|
-
}
|
|
113
|
-
const messageId = `${deviceId}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
114
|
-
packetState.bufferLength = data.readInt32BE(5);
|
|
115
|
-
packetState.buffer = [...data.subarray(3)];
|
|
116
|
-
packetState.packetCount = 1;
|
|
117
|
-
packetState.messageId = messageId;
|
|
118
|
-
if (packetState.bufferLength < 0) {
|
|
119
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Invalid negative packet length detected:', {
|
|
120
|
-
length: packetState.bufferLength,
|
|
121
|
-
dataLength: data.length,
|
|
122
|
-
rawHeader: data.subarray(0, Math.min(16, data.length)).toString('hex'),
|
|
123
|
-
lengthBytes: data.subarray(5, 9).toString('hex'),
|
|
124
|
-
});
|
|
125
|
-
resetPacketState(packetState);
|
|
126
|
-
return { isComplete: false, error: 'Invalid packet length in header' };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
if (packetState.bufferLength === 0) {
|
|
131
|
-
return { isComplete: false, error: 'Received data chunk without header' };
|
|
132
|
-
}
|
|
133
|
-
packetState.packetCount += 1;
|
|
134
|
-
packetState.buffer = packetState.buffer.concat([...data]);
|
|
135
|
-
}
|
|
136
|
-
if (packetState.buffer.length - hdTransport.COMMON_HEADER_SIZE >= packetState.bufferLength) {
|
|
137
|
-
const completeBuffer = Buffer.from(packetState.buffer);
|
|
138
|
-
const hexString = completeBuffer.toString('hex');
|
|
139
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Packet assembled', {
|
|
140
|
-
deviceId,
|
|
141
|
-
totalPackets: packetState.packetCount,
|
|
142
|
-
expectedLength: packetState.bufferLength,
|
|
143
|
-
actualLength: packetState.buffer.length - hdTransport.COMMON_HEADER_SIZE,
|
|
144
|
-
});
|
|
145
|
-
resetPacketState(packetState);
|
|
146
|
-
return { isComplete: true, completePacket: hexString };
|
|
147
|
-
}
|
|
148
|
-
return { isComplete: false };
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
resetPacketState(packetState);
|
|
152
|
-
return { isComplete: false, error: `Packet processing error: ${error}` };
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function resetPacketState(packetState) {
|
|
156
|
-
packetState.bufferLength = 0;
|
|
157
|
-
packetState.buffer = [];
|
|
158
|
-
packetState.packetCount = 0;
|
|
159
|
-
packetState.messageId = undefined;
|
|
117
|
+
const appCb = notificationCallbacks.get(deviceId);
|
|
118
|
+
if (appCb)
|
|
119
|
+
appCb(data.toString('hex'));
|
|
160
120
|
}
|
|
161
121
|
function checkBluetoothAvailability() {
|
|
162
122
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -201,7 +161,6 @@ function setupPersistentStateListener() {
|
|
|
201
161
|
deviceCharacteristics.clear();
|
|
202
162
|
subscribedDevices.clear();
|
|
203
163
|
notificationCallbacks.clear();
|
|
204
|
-
devicePacketStates.clear();
|
|
205
164
|
subscriptionOperations.clear();
|
|
206
165
|
pairedDevices.clear();
|
|
207
166
|
if (noble) {
|
|
@@ -321,7 +280,6 @@ function cleanupDevice(deviceId, webContents, options = {}) {
|
|
|
321
280
|
connectedDevices.delete(deviceId);
|
|
322
281
|
deviceCharacteristics.delete(deviceId);
|
|
323
282
|
notificationCallbacks.delete(deviceId);
|
|
324
|
-
devicePacketStates.delete(deviceId);
|
|
325
283
|
subscribedDevices.delete(deviceId);
|
|
326
284
|
subscriptionOperations.delete(deviceId);
|
|
327
285
|
pairedDevices.delete(deviceId);
|
|
@@ -436,8 +394,7 @@ function attemptWindowsWriteUntilPaired(deviceId, doGetWriteCharacteristic, payl
|
|
|
436
394
|
subscriptionOperations,
|
|
437
395
|
subscribedDevices,
|
|
438
396
|
pairedDevices,
|
|
439
|
-
|
|
440
|
-
processNotificationData,
|
|
397
|
+
onNotificationData: emitRawNotification,
|
|
441
398
|
logger,
|
|
442
399
|
});
|
|
443
400
|
logger === null || logger === void 0 ? void 0 : logger.info('[BLE-Write] Subscription refresh completed', { deviceId });
|
|
@@ -509,12 +466,18 @@ function handleDeviceDiscovered(peripheral) {
|
|
|
509
466
|
var _a, _b;
|
|
510
467
|
const deviceName = ((_a = peripheral.advertisement) === null || _a === void 0 ? void 0 : _a.localName) || 'Unknown Device';
|
|
511
468
|
const serviceUuids = ((_b = peripheral.advertisement) === null || _b === void 0 ? void 0 : _b.serviceUuids) || [];
|
|
512
|
-
|
|
513
|
-
if (!hdShared.isOnekeyDevice(deviceName)) {
|
|
469
|
+
if (!isOneKeyPeripheral(peripheral)) {
|
|
514
470
|
return;
|
|
515
471
|
}
|
|
516
|
-
|
|
472
|
+
const isNewDevice = !discoveredDevices.has(peripheral.id);
|
|
517
473
|
discoveredDevices.set(peripheral.id, peripheral);
|
|
474
|
+
if (isNewDevice) {
|
|
475
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Discovered OneKey BLE device:', {
|
|
476
|
+
name: deviceName,
|
|
477
|
+
id: peripheral.id,
|
|
478
|
+
serviceUUIDs: serviceUuids,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
518
481
|
}
|
|
519
482
|
function ensureDiscoverListener() {
|
|
520
483
|
if (!noble)
|
|
@@ -614,7 +577,7 @@ function enumerateDevices() {
|
|
|
614
577
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scan completed, found devices:', devices.length);
|
|
615
578
|
resolve(devices);
|
|
616
579
|
}, DEVICE_SCAN_TIMEOUT);
|
|
617
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for
|
|
580
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Scanning for OneKey BLE devices');
|
|
618
581
|
nobleInstance.startScanning([], false, (error) => {
|
|
619
582
|
if (error) {
|
|
620
583
|
cleanup();
|
|
@@ -724,17 +687,21 @@ function discoverServicesAndCharacteristics(peripheral) {
|
|
|
724
687
|
if (!services || services.length === 0) {
|
|
725
688
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound, 'No services found');
|
|
726
689
|
}
|
|
727
|
-
let service = services.find(s =>
|
|
690
|
+
let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
|
|
728
691
|
if (!service) {
|
|
729
|
-
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Known OneKey service UUID not found, trying first
|
|
730
|
-
service =
|
|
692
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Known OneKey service UUID not found, trying first vendor service');
|
|
693
|
+
service =
|
|
694
|
+
services.find(s => PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(getBleUuidKey(s.uuid))) ||
|
|
695
|
+
services.find(s => !isGenericBleService(s.uuid)) ||
|
|
696
|
+
services[0];
|
|
731
697
|
}
|
|
732
698
|
if (!service) {
|
|
733
699
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
734
700
|
}
|
|
735
|
-
|
|
701
|
+
const selectedService = service;
|
|
702
|
+
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Using service:', selectedService.uuid);
|
|
736
703
|
const characteristics = yield new Promise((resolve, reject) => {
|
|
737
|
-
|
|
704
|
+
selectedService.discoverCharacteristics([], (error, chars) => {
|
|
738
705
|
if (error) {
|
|
739
706
|
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Characteristic discovery failed:', error);
|
|
740
707
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound, error.message));
|
|
@@ -956,7 +923,6 @@ function connectDevice(deviceId, webContents) {
|
|
|
956
923
|
existingCharacteristics.notify.removeAllListeners('data');
|
|
957
924
|
}
|
|
958
925
|
notificationCallbacks.delete(deviceId);
|
|
959
|
-
devicePacketStates.delete(deviceId);
|
|
960
926
|
subscribedDevices.delete(deviceId);
|
|
961
927
|
}
|
|
962
928
|
const characteristics = yield setupConnectionAndDiscoverServices(peripheral, deviceId, webContents);
|
|
@@ -1039,7 +1005,6 @@ function unsubscribeNotifications(deviceId) {
|
|
|
1039
1005
|
});
|
|
1040
1006
|
notifyCharacteristic.removeAllListeners('data');
|
|
1041
1007
|
notificationCallbacks.delete(deviceId);
|
|
1042
|
-
devicePacketStates.delete(deviceId);
|
|
1043
1008
|
subscribedDevices.delete(deviceId);
|
|
1044
1009
|
}
|
|
1045
1010
|
finally {
|
|
@@ -1079,12 +1044,6 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1079
1044
|
if (subscribedDevices.get(deviceId)) {
|
|
1080
1045
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device already subscribed to characteristic, updating callback only');
|
|
1081
1046
|
notificationCallbacks.set(deviceId, callback);
|
|
1082
|
-
devicePacketStates.set(deviceId, {
|
|
1083
|
-
bufferLength: 0,
|
|
1084
|
-
buffer: [],
|
|
1085
|
-
packetCount: 0,
|
|
1086
|
-
messageId: undefined,
|
|
1087
|
-
});
|
|
1088
1047
|
subscriptionOperations.set(deviceId, 'idle');
|
|
1089
1048
|
return Promise.resolve();
|
|
1090
1049
|
}
|
|
@@ -1093,12 +1052,6 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1093
1052
|
}
|
|
1094
1053
|
notifyCharacteristic.removeAllListeners('data');
|
|
1095
1054
|
notificationCallbacks.set(deviceId, callback);
|
|
1096
|
-
devicePacketStates.set(deviceId, {
|
|
1097
|
-
bufferLength: 0,
|
|
1098
|
-
buffer: [],
|
|
1099
|
-
packetCount: 0,
|
|
1100
|
-
messageId: undefined,
|
|
1101
|
-
});
|
|
1102
1055
|
function rebuildAppSubscription(deviceId, notifyCharacteristic) {
|
|
1103
1056
|
return index.__awaiter(this, void 0, void 0, function* () {
|
|
1104
1057
|
yield new Promise(resolve => {
|
|
@@ -1120,16 +1073,7 @@ function subscribeNotifications(deviceId, callback) {
|
|
|
1120
1073
|
pairedDevices.add(deviceId);
|
|
1121
1074
|
logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Device paired successfully', { deviceId });
|
|
1122
1075
|
}
|
|
1123
|
-
|
|
1124
|
-
if (result.error) {
|
|
1125
|
-
logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Packet processing error:', result.error);
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
if (result.isComplete && result.completePacket) {
|
|
1129
|
-
const appCb = notificationCallbacks.get(deviceId);
|
|
1130
|
-
if (appCb)
|
|
1131
|
-
appCb(result.completePacket);
|
|
1132
|
-
}
|
|
1076
|
+
emitRawNotification(deviceId, data);
|
|
1133
1077
|
});
|
|
1134
1078
|
});
|
|
1135
1079
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,UAAU,CAAC;AAy9ChE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAsKpE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-electron",
|
|
3
|
-
"version": "1.1.27-alpha.
|
|
3
|
+
"version": "1.1.27-alpha.36",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"electron-log": ">=4.0.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-core": "1.1.27-alpha.
|
|
29
|
-
"@onekeyfe/hd-shared": "1.1.27-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport": "1.1.27-alpha.
|
|
28
|
+
"@onekeyfe/hd-core": "1.1.27-alpha.36",
|
|
29
|
+
"@onekeyfe/hd-shared": "1.1.27-alpha.36",
|
|
30
|
+
"@onekeyfe/hd-transport": "1.1.27-alpha.36",
|
|
31
31
|
"@stoprocent/noble": "2.3.16",
|
|
32
32
|
"p-retry": "^4.6.2"
|
|
33
33
|
},
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"electron": "^25.0.0",
|
|
37
37
|
"typescript": "^5.3.3"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "1aaff42d7dd10933009ba7e0c0b7324e91d8e993"
|
|
40
40
|
}
|
package/src/ble-ops.ts
CHANGED
|
@@ -7,15 +7,7 @@ export interface SoftRefreshParams {
|
|
|
7
7
|
subscriptionOperations: Map<string, 'subscribing' | 'unsubscribing' | 'idle'>;
|
|
8
8
|
subscribedDevices: Map<string, boolean>;
|
|
9
9
|
pairedDevices: Set<string>;
|
|
10
|
-
|
|
11
|
-
processNotificationData: (
|
|
12
|
-
deviceId: string,
|
|
13
|
-
data: Buffer
|
|
14
|
-
) => {
|
|
15
|
-
isComplete: boolean;
|
|
16
|
-
completePacket?: string;
|
|
17
|
-
error?: string;
|
|
18
|
-
};
|
|
10
|
+
onNotificationData: (deviceId: string, data: Buffer) => void;
|
|
19
11
|
logger: Logger | null;
|
|
20
12
|
}
|
|
21
13
|
|
|
@@ -26,8 +18,7 @@ export async function softRefreshSubscription(params: SoftRefreshParams): Promis
|
|
|
26
18
|
subscriptionOperations,
|
|
27
19
|
subscribedDevices,
|
|
28
20
|
pairedDevices,
|
|
29
|
-
|
|
30
|
-
processNotificationData,
|
|
21
|
+
onNotificationData,
|
|
31
22
|
logger,
|
|
32
23
|
} = params;
|
|
33
24
|
|
|
@@ -60,15 +51,7 @@ export async function softRefreshSubscription(params: SoftRefreshParams): Promis
|
|
|
60
51
|
logger?.info('[BLE-OPS] Device paired successfully', { deviceId });
|
|
61
52
|
}
|
|
62
53
|
|
|
63
|
-
|
|
64
|
-
if (result.error) {
|
|
65
|
-
logger?.error('[BLE-OPS] Packet processing error:', result.error);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (result.isComplete && result.completePacket) {
|
|
69
|
-
const appCb = notificationCallbacks.get(deviceId);
|
|
70
|
-
if (appCb) appCb(result.completePacket);
|
|
71
|
-
}
|
|
54
|
+
onNotificationData(deviceId, data);
|
|
72
55
|
});
|
|
73
56
|
|
|
74
57
|
subscribedDevices.set(deviceId, true);
|
package/src/noble-ble-handler.ts
CHANGED
|
@@ -9,14 +9,10 @@ import {
|
|
|
9
9
|
EOneKeyBleMessageKeys,
|
|
10
10
|
ERRORS,
|
|
11
11
|
HardwareErrorCode,
|
|
12
|
-
ONEKEY_NOTIFY_CHARACTERISTIC_UUID,
|
|
13
12
|
ONEKEY_SERVICE_UUID,
|
|
14
|
-
ONEKEY_WRITE_CHARACTERISTIC_UUID,
|
|
15
|
-
isHeaderChunk,
|
|
16
13
|
isOnekeyDevice,
|
|
17
14
|
wait,
|
|
18
15
|
} from '@onekeyfe/hd-shared';
|
|
19
|
-
import { COMMON_HEADER_SIZE } from '@onekeyfe/hd-transport';
|
|
20
16
|
import pRetry from 'p-retry';
|
|
21
17
|
|
|
22
18
|
import { safeLog } from './types/noble-extended';
|
|
@@ -55,15 +51,6 @@ const subscribedDevices = new Map<string, boolean>(); // Track subscription stat
|
|
|
55
51
|
// 🔒 Subscription operation state tracking to prevent race conditions
|
|
56
52
|
const subscriptionOperations = new Map<string, 'subscribing' | 'unsubscribing' | 'idle'>();
|
|
57
53
|
|
|
58
|
-
// Packet reassembly state for each device
|
|
59
|
-
interface PacketAssemblyState {
|
|
60
|
-
bufferLength: number;
|
|
61
|
-
buffer: number[];
|
|
62
|
-
packetCount: number;
|
|
63
|
-
messageId?: string; // Add message ID to track concurrent requests
|
|
64
|
-
}
|
|
65
|
-
const devicePacketStates = new Map<string, PacketAssemblyState>();
|
|
66
|
-
|
|
67
54
|
// Windows-only response watchdog state moved to utils/windows-ble-recovery
|
|
68
55
|
|
|
69
56
|
// Pairing-related state removed
|
|
@@ -72,6 +59,7 @@ const devicePacketStates = new Map<string, PacketAssemblyState>();
|
|
|
72
59
|
|
|
73
60
|
// Service UUIDs to scan for - using constants from hd-shared
|
|
74
61
|
const ONEKEY_SERVICE_UUIDS = [ONEKEY_SERVICE_UUID];
|
|
62
|
+
const PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS = new Set(['fffd']);
|
|
75
63
|
|
|
76
64
|
// Pre-normalized characteristic identifiers for fast comparison
|
|
77
65
|
const NORMALIZED_WRITE_UUID = '0002';
|
|
@@ -94,101 +82,49 @@ const ABORTABLE_WRITE_ERROR_PATTERNS = [
|
|
|
94
82
|
/status:\s*3/i, // Windows pairing cancelled / GATT write failed
|
|
95
83
|
];
|
|
96
84
|
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
// Packet processing result types
|
|
101
|
-
interface PacketProcessResult {
|
|
102
|
-
isComplete: boolean;
|
|
103
|
-
completePacket?: string;
|
|
104
|
-
error?: string;
|
|
85
|
+
function getBleUuidKey(uuid?: string | null) {
|
|
86
|
+
const normalized = (uuid ?? '').replace(/-/g, '').toLowerCase();
|
|
87
|
+
return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
|
|
105
88
|
}
|
|
106
89
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
deviceId,
|
|
112
|
-
dataLength: data.length,
|
|
113
|
-
});
|
|
90
|
+
const NORMALIZED_ONEKEY_SERVICE_UUIDS = new Set([
|
|
91
|
+
...ONEKEY_SERVICE_UUIDS.map(uuid => getBleUuidKey(uuid)),
|
|
92
|
+
'0001',
|
|
93
|
+
]);
|
|
114
94
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
packetState = { bufferLength: 0, buffer: [], packetCount: 0 };
|
|
119
|
-
devicePacketStates.set(deviceId, packetState);
|
|
120
|
-
logger?.info('[NobleBLE] Initialized new packet state for device:', deviceId);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
if (isHeaderChunk(data)) {
|
|
125
|
-
// Validate header chunk
|
|
126
|
-
if (data.length < MIN_HEADER_LENGTH) {
|
|
127
|
-
return { isComplete: false, error: 'Invalid header chunk: too short' };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Generate message ID for this packet sequence
|
|
131
|
-
const messageId = `${deviceId}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
132
|
-
|
|
133
|
-
// Reset packet state for new message
|
|
134
|
-
packetState.bufferLength = data.readInt32BE(5);
|
|
135
|
-
packetState.buffer = [...data.subarray(3)];
|
|
136
|
-
packetState.packetCount = 1;
|
|
137
|
-
packetState.messageId = messageId;
|
|
138
|
-
|
|
139
|
-
// Only validate for negative lengths (which would be invalid)
|
|
140
|
-
if (packetState.bufferLength < 0) {
|
|
141
|
-
logger?.error('[NobleBLE] Invalid negative packet length detected:', {
|
|
142
|
-
length: packetState.bufferLength,
|
|
143
|
-
dataLength: data.length,
|
|
144
|
-
rawHeader: data.subarray(0, Math.min(16, data.length)).toString('hex'),
|
|
145
|
-
lengthBytes: data.subarray(5, 9).toString('hex'),
|
|
146
|
-
});
|
|
147
|
-
resetPacketState(packetState);
|
|
148
|
-
return { isComplete: false, error: 'Invalid packet length in header' };
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
// Validate we have an active packet session
|
|
152
|
-
if (packetState.bufferLength === 0) {
|
|
153
|
-
return { isComplete: false, error: 'Received data chunk without header' };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Increment packet counter and append data
|
|
157
|
-
packetState.packetCount += 1;
|
|
158
|
-
packetState.buffer = packetState.buffer.concat([...data]);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Check if packet is complete
|
|
162
|
-
if (packetState.buffer.length - COMMON_HEADER_SIZE >= packetState.bufferLength) {
|
|
163
|
-
const completeBuffer = Buffer.from(packetState.buffer);
|
|
164
|
-
const hexString = completeBuffer.toString('hex');
|
|
165
|
-
|
|
166
|
-
logger?.info('[NobleBLE] Packet assembled', {
|
|
167
|
-
deviceId,
|
|
168
|
-
totalPackets: packetState.packetCount,
|
|
169
|
-
expectedLength: packetState.bufferLength,
|
|
170
|
-
actualLength: packetState.buffer.length - COMMON_HEADER_SIZE,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Reset packet state for next message
|
|
174
|
-
resetPacketState(packetState);
|
|
95
|
+
function isGenericBleService(uuid?: string | null) {
|
|
96
|
+
return ['1800', '1801', '180a', '180f'].includes(getBleUuidKey(uuid));
|
|
97
|
+
}
|
|
175
98
|
|
|
176
|
-
|
|
177
|
-
|
|
99
|
+
function hasOneKeyAdvertisementService(peripheral: Peripheral) {
|
|
100
|
+
const serviceUuids = peripheral.advertisement?.serviceUuids ?? [];
|
|
101
|
+
return serviceUuids.some(uuid => {
|
|
102
|
+
const uuidKey = getBleUuidKey(uuid);
|
|
103
|
+
return (
|
|
104
|
+
NORMALIZED_ONEKEY_SERVICE_UUIDS.has(uuidKey) ||
|
|
105
|
+
PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(uuidKey)
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
178
109
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return { isComplete: false, error: `Packet processing error: ${error}` };
|
|
183
|
-
}
|
|
110
|
+
function isOneKeyPeripheral(peripheral: Peripheral) {
|
|
111
|
+
const deviceName = peripheral.advertisement?.localName || null;
|
|
112
|
+
return isOnekeyDevice(deviceName, peripheral.id) || hasOneKeyAdvertisementService(peripheral);
|
|
184
113
|
}
|
|
185
114
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Forward a single BLE notification chunk (not an assembled packet) to the
|
|
117
|
+
* renderer-side transport. Packet reassembly is handled by ElectronBleTransport.
|
|
118
|
+
*/
|
|
119
|
+
function emitRawNotification(deviceId: string, data: Buffer): void {
|
|
120
|
+
logger?.info('[NobleBLE] Raw notification', {
|
|
121
|
+
deviceId,
|
|
122
|
+
dataLength: data.length,
|
|
123
|
+
firstBytes: data.subarray(0, 8).toString('hex'),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const appCb = notificationCallbacks.get(deviceId);
|
|
127
|
+
if (appCb) appCb(data.toString('hex'));
|
|
192
128
|
}
|
|
193
129
|
|
|
194
130
|
// Check Bluetooth availability - returns detailed state
|
|
@@ -250,7 +186,6 @@ function setupPersistentStateListener(): void {
|
|
|
250
186
|
deviceCharacteristics.clear();
|
|
251
187
|
subscribedDevices.clear();
|
|
252
188
|
notificationCallbacks.clear();
|
|
253
|
-
devicePacketStates.clear();
|
|
254
189
|
subscriptionOperations.clear();
|
|
255
190
|
pairedDevices.clear();
|
|
256
191
|
|
|
@@ -428,7 +363,6 @@ function cleanupDevice(
|
|
|
428
363
|
connectedDevices.delete(deviceId);
|
|
429
364
|
deviceCharacteristics.delete(deviceId);
|
|
430
365
|
notificationCallbacks.delete(deviceId);
|
|
431
|
-
devicePacketStates.delete(deviceId);
|
|
432
366
|
subscribedDevices.delete(deviceId);
|
|
433
367
|
subscriptionOperations.delete(deviceId);
|
|
434
368
|
pairedDevices.delete(deviceId);
|
|
@@ -590,8 +524,7 @@ async function attemptWindowsWriteUntilPaired(
|
|
|
590
524
|
subscriptionOperations,
|
|
591
525
|
subscribedDevices,
|
|
592
526
|
pairedDevices,
|
|
593
|
-
|
|
594
|
-
processNotificationData,
|
|
527
|
+
onNotificationData: emitRawNotification,
|
|
595
528
|
logger,
|
|
596
529
|
});
|
|
597
530
|
logger?.info('[BLE-Write] Subscription refresh completed', { deviceId });
|
|
@@ -688,20 +621,21 @@ function handleDeviceDiscovered(peripheral: Peripheral): void {
|
|
|
688
621
|
const deviceName = peripheral.advertisement?.localName || 'Unknown Device';
|
|
689
622
|
const serviceUuids = peripheral.advertisement?.serviceUuids || [];
|
|
690
623
|
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
peripheral.id
|
|
695
|
-
} serviceUUIDs=[${serviceUuids.join(', ')}]`
|
|
696
|
-
);
|
|
697
|
-
|
|
698
|
-
// Only process OneKey devices for general discovery
|
|
699
|
-
if (!isOnekeyDevice(deviceName)) {
|
|
624
|
+
// Only process OneKey candidates for general discovery. Avoid logging every
|
|
625
|
+
// ambient BLE peripheral; it makes Pro2 debugging hard to read.
|
|
626
|
+
if (!isOneKeyPeripheral(peripheral)) {
|
|
700
627
|
return;
|
|
701
628
|
}
|
|
702
629
|
|
|
703
|
-
|
|
630
|
+
const isNewDevice = !discoveredDevices.has(peripheral.id);
|
|
704
631
|
discoveredDevices.set(peripheral.id, peripheral);
|
|
632
|
+
if (isNewDevice) {
|
|
633
|
+
logger?.info('[NobleBLE] Discovered OneKey BLE device:', {
|
|
634
|
+
name: deviceName,
|
|
635
|
+
id: peripheral.id,
|
|
636
|
+
serviceUUIDs: serviceUuids,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
705
639
|
}
|
|
706
640
|
|
|
707
641
|
// Ensure discover listener is properly set up
|
|
@@ -833,9 +767,9 @@ async function enumerateDevices(): Promise<DeviceInfo[]> {
|
|
|
833
767
|
resolve(devices);
|
|
834
768
|
}, DEVICE_SCAN_TIMEOUT);
|
|
835
769
|
|
|
836
|
-
// Start scanning
|
|
837
|
-
//
|
|
838
|
-
logger?.info('[NobleBLE] Scanning for
|
|
770
|
+
// Start scanning without a service UUID filter so Pro2 advertisements with
|
|
771
|
+
// short vendor UUIDs can be found, but only OneKey candidates are logged/returned.
|
|
772
|
+
logger?.info('[NobleBLE] Scanning for OneKey BLE devices');
|
|
839
773
|
nobleInstance.startScanning([], false, (error?: Error) => {
|
|
840
774
|
if (error) {
|
|
841
775
|
cleanup();
|
|
@@ -986,23 +920,24 @@ async function discoverServicesAndCharacteristics(
|
|
|
986
920
|
throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, 'No services found');
|
|
987
921
|
}
|
|
988
922
|
|
|
989
|
-
// Find OneKey service —
|
|
990
|
-
let service = services.find(s =>
|
|
923
|
+
// Find OneKey service — Noble may expose 128-bit UUIDs as short UUID keys.
|
|
924
|
+
let service = services.find(s => NORMALIZED_ONEKEY_SERVICE_UUIDS.has(getBleUuidKey(s.uuid)));
|
|
991
925
|
if (!service) {
|
|
992
|
-
logger?.info(
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
926
|
+
logger?.info('[NobleBLE] Known OneKey service UUID not found, trying first vendor service');
|
|
927
|
+
service =
|
|
928
|
+
services.find(s => PRO2_ADVERTISEMENT_SERVICE_UUID_KEYS.has(getBleUuidKey(s.uuid))) ||
|
|
929
|
+
services.find(s => !isGenericBleService(s.uuid)) ||
|
|
930
|
+
services[0];
|
|
997
931
|
}
|
|
998
932
|
if (!service) {
|
|
999
933
|
throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
|
|
1000
934
|
}
|
|
1001
|
-
|
|
935
|
+
const selectedService = service;
|
|
936
|
+
logger?.info('[NobleBLE] Using service:', selectedService.uuid);
|
|
1002
937
|
|
|
1003
938
|
// Step 2: Discover ALL characteristics (no filter)
|
|
1004
939
|
const characteristics = await new Promise<Characteristic[]>((resolve, reject) => {
|
|
1005
|
-
|
|
940
|
+
selectedService.discoverCharacteristics([], (error, chars) => {
|
|
1006
941
|
if (error) {
|
|
1007
942
|
logger?.error('[NobleBLE] Characteristic discovery failed:', error);
|
|
1008
943
|
reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message));
|
|
@@ -1346,7 +1281,6 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
|
|
|
1346
1281
|
existingCharacteristics.notify.removeAllListeners('data');
|
|
1347
1282
|
}
|
|
1348
1283
|
notificationCallbacks.delete(deviceId);
|
|
1349
|
-
devicePacketStates.delete(deviceId);
|
|
1350
1284
|
subscribedDevices.delete(deviceId);
|
|
1351
1285
|
// Continue to re-setup the connection properly
|
|
1352
1286
|
}
|
|
@@ -1457,7 +1391,6 @@ async function unsubscribeNotifications(deviceId: string): Promise<void> {
|
|
|
1457
1391
|
// Remove all listeners and clear subscription status
|
|
1458
1392
|
notifyCharacteristic.removeAllListeners('data');
|
|
1459
1393
|
notificationCallbacks.delete(deviceId);
|
|
1460
|
-
devicePacketStates.delete(deviceId);
|
|
1461
1394
|
subscribedDevices.delete(deviceId);
|
|
1462
1395
|
} finally {
|
|
1463
1396
|
// 🔒 CRITICAL: Always clear operation state (even on error)
|
|
@@ -1524,14 +1457,6 @@ async function subscribeNotifications(
|
|
|
1524
1457
|
// Just update the callback without re-subscribing
|
|
1525
1458
|
notificationCallbacks.set(deviceId, callback);
|
|
1526
1459
|
|
|
1527
|
-
// Reset packet state for new session
|
|
1528
|
-
devicePacketStates.set(deviceId, {
|
|
1529
|
-
bufferLength: 0,
|
|
1530
|
-
buffer: [],
|
|
1531
|
-
packetCount: 0,
|
|
1532
|
-
messageId: undefined,
|
|
1533
|
-
});
|
|
1534
|
-
|
|
1535
1460
|
// 🔒 Clear operation state
|
|
1536
1461
|
subscriptionOperations.set(deviceId, 'idle');
|
|
1537
1462
|
return Promise.resolve();
|
|
@@ -1548,14 +1473,6 @@ async function subscribeNotifications(
|
|
|
1548
1473
|
// Store callback for this device
|
|
1549
1474
|
notificationCallbacks.set(deviceId, callback);
|
|
1550
1475
|
|
|
1551
|
-
// Reset packet state for new subscription session
|
|
1552
|
-
devicePacketStates.set(deviceId, {
|
|
1553
|
-
bufferLength: 0,
|
|
1554
|
-
buffer: [],
|
|
1555
|
-
packetCount: 0,
|
|
1556
|
-
messageId: undefined,
|
|
1557
|
-
});
|
|
1558
|
-
|
|
1559
1476
|
// Helper: rebuild a clean application-layer subscription
|
|
1560
1477
|
async function rebuildAppSubscription(
|
|
1561
1478
|
deviceId: string,
|
|
@@ -1584,15 +1501,7 @@ async function subscribeNotifications(
|
|
|
1584
1501
|
logger?.info('[NobleBLE] Device paired successfully', { deviceId });
|
|
1585
1502
|
}
|
|
1586
1503
|
|
|
1587
|
-
|
|
1588
|
-
if (result.error) {
|
|
1589
|
-
logger?.error('[NobleBLE] Packet processing error:', result.error);
|
|
1590
|
-
return;
|
|
1591
|
-
}
|
|
1592
|
-
if (result.isComplete && result.completePacket) {
|
|
1593
|
-
const appCb = notificationCallbacks.get(deviceId);
|
|
1594
|
-
if (appCb) appCb(result.completePacket);
|
|
1595
|
-
}
|
|
1504
|
+
emitRawNotification(deviceId, data);
|
|
1596
1505
|
});
|
|
1597
1506
|
}
|
|
1598
1507
|
|