@ukeyfe/hardware-transport-react-native 1.1.13
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/README.md +29 -0
- package/dist/BleManager.d.ts +9 -0
- package/dist/BleManager.d.ts.map +1 -0
- package/dist/BleTransport.d.ts +15 -0
- package/dist/BleTransport.d.ts.map +1 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +724 -0
- package/dist/subscribeBleOn.d.ts +3 -0
- package/dist/subscribeBleOn.d.ts.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/timer.d.ts +5 -0
- package/dist/utils/timer.d.ts.map +1 -0
- package/dist/utils/validateNotify.d.ts +3 -0
- package/dist/utils/validateNotify.d.ts.map +1 -0
- package/package.json +27 -0
- package/src/BleManager.ts +54 -0
- package/src/BleTransport.ts +71 -0
- package/src/constants.ts +43 -0
- package/src/index.ts +672 -0
- package/src/subscribeBleOn.ts +26 -0
- package/src/types.ts +10 -0
- package/src/utils/timer.ts +24 -0
- package/src/utils/validateNotify.ts +16 -0
- package/tsconfig.json +7 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactNative = require('react-native');
|
|
4
|
+
var buffer = require('buffer');
|
|
5
|
+
var reactNativeBlePlx = require('react-native-ble-plx');
|
|
6
|
+
var ByteBuffer = require('bytebuffer');
|
|
7
|
+
var transport = require('@ukeyfe/hardware-transport');
|
|
8
|
+
var hardwareShared = require('@ukeyfe/hardware-shared');
|
|
9
|
+
var BleUtils = require('@ukeyfe/react-native-ble-utils');
|
|
10
|
+
var hardwareCore = require('@ukeyfe/hardware-core');
|
|
11
|
+
|
|
12
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
13
|
+
|
|
14
|
+
var ByteBuffer__default = /*#__PURE__*/_interopDefaultLegacy(ByteBuffer);
|
|
15
|
+
var transport__default = /*#__PURE__*/_interopDefaultLegacy(transport);
|
|
16
|
+
var BleUtils__default = /*#__PURE__*/_interopDefaultLegacy(BleUtils);
|
|
17
|
+
|
|
18
|
+
/******************************************************************************
|
|
19
|
+
Copyright (c) Microsoft Corporation.
|
|
20
|
+
|
|
21
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
22
|
+
purpose with or without fee is hereby granted.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
25
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
26
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
27
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
28
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
29
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
30
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
31
|
+
***************************************************************************** */
|
|
32
|
+
|
|
33
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
34
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
35
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
36
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
37
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
38
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
39
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
44
|
+
var e = new Error(message);
|
|
45
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const Logger = hardwareCore.getLogger(hardwareCore.LoggerNames.HdBleTransport);
|
|
49
|
+
const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
|
|
50
|
+
const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
|
|
51
|
+
const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
52
|
+
let timeout;
|
|
53
|
+
const cleanup = (cleanupListener) => {
|
|
54
|
+
if (timeout) {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
}
|
|
57
|
+
if (cleanupListener)
|
|
58
|
+
cleanupListener();
|
|
59
|
+
};
|
|
60
|
+
const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
|
|
61
|
+
var _a;
|
|
62
|
+
if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const { bondState } = peripheral;
|
|
66
|
+
if (bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING') {
|
|
67
|
+
timeout = setTimeout(() => {
|
|
68
|
+
cleanup(cleanupListener);
|
|
69
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
|
|
70
|
+
}, 60 * 1000);
|
|
71
|
+
}
|
|
72
|
+
const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
|
|
73
|
+
const hasCanceled = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
|
|
74
|
+
Logger.debug('onDeviceBondState bondState:', bondState);
|
|
75
|
+
if (hasBonded) {
|
|
76
|
+
cleanup(cleanupListener);
|
|
77
|
+
resolve(peripheral);
|
|
78
|
+
}
|
|
79
|
+
else if (hasCanceled) {
|
|
80
|
+
cleanup(cleanupListener);
|
|
81
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleDeviceBondedCanceled, 'bonding canceled'));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const timer = process.env.NODE_ENV === 'development'
|
|
87
|
+
? {
|
|
88
|
+
timeout: (fn, ms) => {
|
|
89
|
+
const startTime = Date.now();
|
|
90
|
+
const interval = setInterval(() => {
|
|
91
|
+
if (Date.now() - startTime >= ms) {
|
|
92
|
+
clearInterval(interval);
|
|
93
|
+
fn();
|
|
94
|
+
}
|
|
95
|
+
}, 100);
|
|
96
|
+
return () => {
|
|
97
|
+
clearInterval(interval);
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
: {
|
|
102
|
+
timeout: (fn, ms) => {
|
|
103
|
+
const timeout = setTimeout(fn, ms);
|
|
104
|
+
return () => clearTimeout(timeout);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject) => {
|
|
109
|
+
let done = false;
|
|
110
|
+
const subscription = bleManager.onStateChange(state => {
|
|
111
|
+
console.log('ble state -> ', state);
|
|
112
|
+
if (state === 'PoweredOn') {
|
|
113
|
+
if (done)
|
|
114
|
+
return;
|
|
115
|
+
clearTimeout();
|
|
116
|
+
done = true;
|
|
117
|
+
subscription.remove();
|
|
118
|
+
resolve();
|
|
119
|
+
}
|
|
120
|
+
}, true);
|
|
121
|
+
const clearTimeout = timer.timeout(() => {
|
|
122
|
+
if (done)
|
|
123
|
+
return;
|
|
124
|
+
subscription.remove();
|
|
125
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BlePermissionError));
|
|
126
|
+
}, ms);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const IOS_PACKET_LENGTH = 128;
|
|
130
|
+
const ANDROID_PACKET_LENGTH = 192;
|
|
131
|
+
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
132
|
+
const UKeyServices = {
|
|
133
|
+
classic: {
|
|
134
|
+
[ClassicServiceUUID]: {
|
|
135
|
+
serviceUuid: ClassicServiceUUID,
|
|
136
|
+
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
137
|
+
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
const bluetoothServices = [];
|
|
142
|
+
for (const deviceType of Object.keys(UKeyServices)) {
|
|
143
|
+
const services = UKeyServices[deviceType];
|
|
144
|
+
bluetoothServices.push(...Object.keys(services));
|
|
145
|
+
}
|
|
146
|
+
const getBluetoothServiceUuids = () => bluetoothServices;
|
|
147
|
+
const getInfosForServiceUuid = (serviceUuid, deviceType) => {
|
|
148
|
+
const services = UKeyServices[deviceType];
|
|
149
|
+
if (!services) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const service = services[serviceUuid];
|
|
153
|
+
if (!service) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return service;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const isHeaderChunk = (chunk) => {
|
|
160
|
+
if (chunk.length < 9)
|
|
161
|
+
return false;
|
|
162
|
+
const [MagicQuestionMark, sharp1, sharp2] = chunk;
|
|
163
|
+
if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.MESSAGE_TOP_CHAR) &&
|
|
164
|
+
String.fromCharCode(sharp1) === String.fromCharCode(transport.MESSAGE_HEADER_BYTE) &&
|
|
165
|
+
String.fromCharCode(sharp2) === String.fromCharCode(transport.MESSAGE_HEADER_BYTE)) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const Log = hardwareCore.getLogger(hardwareCore.LoggerNames.HdBleTransport);
|
|
172
|
+
class BleTransport {
|
|
173
|
+
constructor(device, writeCharacteristic, notifyCharacteristic) {
|
|
174
|
+
this.name = 'ReactNativeBleTransport';
|
|
175
|
+
this.mtuSize = 20;
|
|
176
|
+
this.id = device.id;
|
|
177
|
+
this.device = device;
|
|
178
|
+
this.writeCharacteristic = writeCharacteristic;
|
|
179
|
+
this.notifyCharacteristic = notifyCharacteristic;
|
|
180
|
+
console.log(`BleTransport(${String(this.id)}) new instance`);
|
|
181
|
+
}
|
|
182
|
+
writeWithRetry(data, retryCount = BleTransport.MAX_RETRIES) {
|
|
183
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
184
|
+
try {
|
|
185
|
+
yield this.writeCharacteristic.writeWithoutResponse(data);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
|
|
189
|
+
if (retryCount > 0) {
|
|
190
|
+
yield hardwareCore.wait(BleTransport.RETRY_DELAY);
|
|
191
|
+
if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
|
|
192
|
+
error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
|
|
193
|
+
try {
|
|
194
|
+
yield this.device.connect();
|
|
195
|
+
yield this.device.discoverAllServicesAndCharacteristics();
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`Connect or discoverAllServicesAndCharacteristics error: ${e}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`writeCharacteristic error: ${error}`);
|
|
203
|
+
}
|
|
204
|
+
return this.writeWithRetry(data, retryCount - 1);
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
BleTransport.MAX_RETRIES = 5;
|
|
212
|
+
BleTransport.RETRY_DELAY = 2000;
|
|
213
|
+
|
|
214
|
+
const { check, buildBuffers, receiveOne, parseConfigure } = transport__default["default"];
|
|
215
|
+
const transportCache = {};
|
|
216
|
+
let connectOptions = {
|
|
217
|
+
requestMTU: 256,
|
|
218
|
+
timeout: 3000,
|
|
219
|
+
refreshGatt: 'OnConnected',
|
|
220
|
+
};
|
|
221
|
+
const tryToGetConfiguration = (device) => {
|
|
222
|
+
if (!device || !device.serviceUUIDs)
|
|
223
|
+
return null;
|
|
224
|
+
const [serviceUUID] = device.serviceUUIDs;
|
|
225
|
+
const infos = getInfosForServiceUuid(serviceUUID, 'classic');
|
|
226
|
+
if (!infos)
|
|
227
|
+
return null;
|
|
228
|
+
return infos;
|
|
229
|
+
};
|
|
230
|
+
function remapError(error) {
|
|
231
|
+
var _a;
|
|
232
|
+
if (error instanceof reactNativeBlePlx.BleError) {
|
|
233
|
+
if (error.iosErrorCode === reactNativeBlePlx.BleATTErrorCode.UnlikelyError ||
|
|
234
|
+
error.reason === 'Peer removed pairing information') {
|
|
235
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
236
|
+
}
|
|
237
|
+
if ((error === null || error === void 0 ? void 0 : error.attErrorCode) === 22) {
|
|
238
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleDeviceBondError);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (error instanceof Error &&
|
|
242
|
+
error.message &&
|
|
243
|
+
(error.message.includes('was disconnected') || error.message.includes('not found'))) {
|
|
244
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
245
|
+
}
|
|
246
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleConnectedError, (_a = error.reason) !== null && _a !== void 0 ? _a : error);
|
|
247
|
+
}
|
|
248
|
+
class ReactNativeBleTransport {
|
|
249
|
+
constructor(options) {
|
|
250
|
+
var _a;
|
|
251
|
+
this.name = 'ReactNativeBleTransport';
|
|
252
|
+
this.configured = false;
|
|
253
|
+
this.stopped = false;
|
|
254
|
+
this.scanTimeout = 3000;
|
|
255
|
+
this.runPromise = null;
|
|
256
|
+
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : 3000;
|
|
257
|
+
}
|
|
258
|
+
init(logger, emitter) {
|
|
259
|
+
this.Log = logger;
|
|
260
|
+
this.emitter = emitter;
|
|
261
|
+
}
|
|
262
|
+
configure(signedData) {
|
|
263
|
+
const messages = parseConfigure(signedData);
|
|
264
|
+
this.configured = true;
|
|
265
|
+
this._messages = messages;
|
|
266
|
+
}
|
|
267
|
+
listen() {
|
|
268
|
+
}
|
|
269
|
+
getPlxManager() {
|
|
270
|
+
if (this.blePlxManager)
|
|
271
|
+
return Promise.resolve(this.blePlxManager);
|
|
272
|
+
this.blePlxManager = new reactNativeBlePlx.BleManager();
|
|
273
|
+
return Promise.resolve(this.blePlxManager);
|
|
274
|
+
}
|
|
275
|
+
enumerate() {
|
|
276
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
277
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
const deviceList = [];
|
|
279
|
+
const blePlxManager = yield this.getPlxManager();
|
|
280
|
+
try {
|
|
281
|
+
yield subscribeBleOn(blePlxManager);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
this.Log.debug('subscribeBleOn error: ', error);
|
|
285
|
+
reject(error);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (reactNative.Platform.OS === 'android' && reactNative.Platform.Version >= 31) {
|
|
289
|
+
this.Log.debug('requesting permissions, please wait...');
|
|
290
|
+
const resultConnect = yield reactNative.PermissionsAndroid.requestMultiple([
|
|
291
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
|
|
292
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
|
|
293
|
+
]);
|
|
294
|
+
this.Log.debug('requesting permissions, result: ', resultConnect);
|
|
295
|
+
if (resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] !== 'granted' ||
|
|
296
|
+
resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] !== 'granted') {
|
|
297
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BlePermissionError));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
blePlxManager.startDeviceScan(null, {
|
|
302
|
+
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
303
|
+
}, (error, device) => {
|
|
304
|
+
var _a, _b;
|
|
305
|
+
if (error) {
|
|
306
|
+
this.Log.debug('ble scan manager: ', blePlxManager);
|
|
307
|
+
this.Log.debug('ble scan error: ', error);
|
|
308
|
+
if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
|
|
309
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BlePermissionError));
|
|
310
|
+
}
|
|
311
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothUnauthorized) {
|
|
312
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleLocationError));
|
|
313
|
+
}
|
|
314
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.LocationServicesDisabled) {
|
|
315
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleLocationServicesDisabled));
|
|
316
|
+
}
|
|
317
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) {
|
|
318
|
+
timer.timeout(() => { }, this.scanTimeout);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleScanError, (_a = error.reason) !== null && _a !== void 0 ? _a : ''));
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (hardwareShared.isUkeyDevice((_b = device === null || device === void 0 ? void 0 : device.name) !== null && _b !== void 0 ? _b : null, device === null || device === void 0 ? void 0 : device.id)) {
|
|
326
|
+
this.Log.debug('search device start ======================');
|
|
327
|
+
const { name, localName, id } = device !== null && device !== void 0 ? device : {};
|
|
328
|
+
this.Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}`);
|
|
329
|
+
addDevice(device);
|
|
330
|
+
this.Log.debug('search device end ======================\n');
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
|
|
334
|
+
for (const device of devices) {
|
|
335
|
+
this.Log.debug('search connected peripheral: ', device.id);
|
|
336
|
+
addDevice(device);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
const addDevice = (device) => {
|
|
340
|
+
if (deviceList.every(d => d.id !== device.id)) {
|
|
341
|
+
deviceList.push(device);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
timer.timeout(() => {
|
|
345
|
+
blePlxManager.stopDeviceScan();
|
|
346
|
+
resolve(deviceList);
|
|
347
|
+
}, this.scanTimeout);
|
|
348
|
+
}));
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
acquire(input) {
|
|
352
|
+
var _a;
|
|
353
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
354
|
+
const { uuid, forceCleanRunPromise } = input;
|
|
355
|
+
if (!uuid) {
|
|
356
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleRequiredUUID);
|
|
357
|
+
}
|
|
358
|
+
let device = null;
|
|
359
|
+
if (transportCache[uuid]) {
|
|
360
|
+
this.Log.debug('transport not be released, will release: ', uuid);
|
|
361
|
+
yield this.release(uuid);
|
|
362
|
+
}
|
|
363
|
+
if (forceCleanRunPromise && this.runPromise) {
|
|
364
|
+
this.runPromise.reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleForceCleanRunPromise));
|
|
365
|
+
this.Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
366
|
+
}
|
|
367
|
+
const blePlxManager = yield this.getPlxManager();
|
|
368
|
+
try {
|
|
369
|
+
yield subscribeBleOn(blePlxManager);
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
this.Log.debug('subscribeBleOn error: ', error);
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
if (reactNative.Platform.OS === 'android') {
|
|
376
|
+
const bondState = yield pairDevice(uuid);
|
|
377
|
+
if (bondState.bonding) {
|
|
378
|
+
yield onDeviceBondState(uuid);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (!device) {
|
|
382
|
+
const devices = yield blePlxManager.devices([uuid]);
|
|
383
|
+
[device] = devices;
|
|
384
|
+
}
|
|
385
|
+
if (!device) {
|
|
386
|
+
const connectedDevice = yield blePlxManager.connectedDevices(getBluetoothServiceUuids());
|
|
387
|
+
const deviceFilter = connectedDevice.filter(device => device.id === uuid);
|
|
388
|
+
this.Log.debug(`found connected device count: ${deviceFilter.length}`);
|
|
389
|
+
[device] = deviceFilter;
|
|
390
|
+
}
|
|
391
|
+
if (!device) {
|
|
392
|
+
this.Log.debug('try to connect to device: ', uuid);
|
|
393
|
+
try {
|
|
394
|
+
device = yield blePlxManager.connectToDevice(uuid, connectOptions);
|
|
395
|
+
}
|
|
396
|
+
catch (e) {
|
|
397
|
+
this.Log.debug('try to connect to device has error: ', e);
|
|
398
|
+
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
399
|
+
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
400
|
+
connectOptions = {};
|
|
401
|
+
this.Log.debug('first try to reconnect without params');
|
|
402
|
+
device = yield blePlxManager.connectToDevice(uuid);
|
|
403
|
+
}
|
|
404
|
+
else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
405
|
+
this.Log.debug('device already connected');
|
|
406
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleAlreadyConnected);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
remapError(e);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (!device) {
|
|
414
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
|
|
415
|
+
}
|
|
416
|
+
if (!(yield device.isConnected())) {
|
|
417
|
+
this.Log.debug('not connected, try to connect to device: ', uuid);
|
|
418
|
+
try {
|
|
419
|
+
yield device.connect(connectOptions);
|
|
420
|
+
}
|
|
421
|
+
catch (e) {
|
|
422
|
+
this.Log.debug('not connected, try to connect to device has error: ', e);
|
|
423
|
+
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
424
|
+
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
425
|
+
connectOptions = {};
|
|
426
|
+
this.Log.debug('second try to reconnect without params');
|
|
427
|
+
try {
|
|
428
|
+
yield device.connect();
|
|
429
|
+
}
|
|
430
|
+
catch (e) {
|
|
431
|
+
this.Log.debug('last try to reconnect error: ', e);
|
|
432
|
+
if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
433
|
+
this.Log.debug('last try to reconnect');
|
|
434
|
+
yield device.cancelConnection();
|
|
435
|
+
yield device.connect();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
remapError(e);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
yield device.discoverAllServicesAndCharacteristics();
|
|
445
|
+
let infos = tryToGetConfiguration(device);
|
|
446
|
+
let characteristics;
|
|
447
|
+
if (!infos) {
|
|
448
|
+
for (const serviceUuid of getBluetoothServiceUuids()) {
|
|
449
|
+
try {
|
|
450
|
+
characteristics = yield device.characteristicsForService(serviceUuid);
|
|
451
|
+
infos = getInfosForServiceUuid(serviceUuid, 'classic');
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
catch (e) {
|
|
455
|
+
this.Log.error(e);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (!infos) {
|
|
460
|
+
try {
|
|
461
|
+
this.Log.debug('cancel connection when service not found');
|
|
462
|
+
yield device.cancelConnection();
|
|
463
|
+
}
|
|
464
|
+
catch (e) {
|
|
465
|
+
this.Log.debug('cancel connection error when service not found: ', e.message || e.reason);
|
|
466
|
+
}
|
|
467
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleServiceNotFound);
|
|
468
|
+
}
|
|
469
|
+
const { serviceUuid, writeUuid, notifyUuid } = infos;
|
|
470
|
+
if (!characteristics) {
|
|
471
|
+
characteristics = yield device.characteristicsForService(serviceUuid);
|
|
472
|
+
}
|
|
473
|
+
if (!characteristics) {
|
|
474
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleCharacteristicNotFound);
|
|
475
|
+
}
|
|
476
|
+
let writeCharacteristic;
|
|
477
|
+
let notifyCharacteristic;
|
|
478
|
+
for (const c of characteristics) {
|
|
479
|
+
if (c.uuid === writeUuid) {
|
|
480
|
+
writeCharacteristic = c;
|
|
481
|
+
}
|
|
482
|
+
else if (c.uuid === notifyUuid) {
|
|
483
|
+
notifyCharacteristic = c;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (!writeCharacteristic) {
|
|
487
|
+
throw hardwareShared.ERRORS.TypedError('BLECharacteristicNotFound: write characteristic not found');
|
|
488
|
+
}
|
|
489
|
+
if (!notifyCharacteristic) {
|
|
490
|
+
throw hardwareShared.ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
|
|
491
|
+
}
|
|
492
|
+
if (!writeCharacteristic.isWritableWithResponse) {
|
|
493
|
+
throw hardwareShared.ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
|
|
494
|
+
}
|
|
495
|
+
if (!notifyCharacteristic.isNotifiable) {
|
|
496
|
+
throw hardwareShared.ERRORS.TypedError('BLECharacteristicNotNotifiable: notify characteristic not notifiable');
|
|
497
|
+
}
|
|
498
|
+
yield this.release(uuid);
|
|
499
|
+
const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
500
|
+
transport.nofitySubscription = this._monitorCharacteristic(transport.notifyCharacteristic);
|
|
501
|
+
transportCache[uuid] = transport;
|
|
502
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-connect', {
|
|
503
|
+
name: device.name,
|
|
504
|
+
id: device.id,
|
|
505
|
+
connectId: device.id,
|
|
506
|
+
});
|
|
507
|
+
const disconnectSubscription = device.onDisconnected(() => {
|
|
508
|
+
var _a;
|
|
509
|
+
try {
|
|
510
|
+
this.Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
|
|
511
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
|
|
512
|
+
name: device === null || device === void 0 ? void 0 : device.name,
|
|
513
|
+
id: device === null || device === void 0 ? void 0 : device.id,
|
|
514
|
+
connectId: device === null || device === void 0 ? void 0 : device.id,
|
|
515
|
+
});
|
|
516
|
+
if (this.runPromise) {
|
|
517
|
+
this.runPromise.reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleConnectedError));
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
catch (e) {
|
|
521
|
+
this.Log.debug('device disconnect error: ', e);
|
|
522
|
+
}
|
|
523
|
+
finally {
|
|
524
|
+
this.release(uuid);
|
|
525
|
+
disconnectSubscription === null || disconnectSubscription === void 0 ? void 0 : disconnectSubscription.remove();
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
return { uuid };
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
_monitorCharacteristic(characteristic) {
|
|
532
|
+
let bufferLength = 0;
|
|
533
|
+
let buffer$1 = [];
|
|
534
|
+
const subscription = characteristic.monitor((error, c) => {
|
|
535
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
536
|
+
if (error) {
|
|
537
|
+
this.Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
538
|
+
if (this.runPromise) {
|
|
539
|
+
let ERROR = hardwareShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
540
|
+
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
541
|
+
ERROR = hardwareShared.HardwareErrorCode.BleTimeoutError;
|
|
542
|
+
}
|
|
543
|
+
if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
|
|
544
|
+
ERROR = hardwareShared.HardwareErrorCode.BleDeviceBondError;
|
|
545
|
+
}
|
|
546
|
+
if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
|
|
547
|
+
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
|
|
548
|
+
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
|
|
549
|
+
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
|
|
550
|
+
((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
|
|
551
|
+
this.runPromise.reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure));
|
|
552
|
+
this.Log.debug(`${hardwareShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
this.runPromise.reject(hardwareShared.ERRORS.TypedError(ERROR));
|
|
556
|
+
this.Log.debug(': monitor notify error, and has unreleased Promise', Error);
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (!c) {
|
|
561
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleMonitorError);
|
|
562
|
+
}
|
|
563
|
+
try {
|
|
564
|
+
const data = buffer.Buffer.from(c.value, 'base64');
|
|
565
|
+
if (isHeaderChunk(data)) {
|
|
566
|
+
bufferLength = data.readInt32BE(5);
|
|
567
|
+
buffer$1 = [...data.subarray(3)];
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
buffer$1 = buffer$1.concat([...data]);
|
|
571
|
+
}
|
|
572
|
+
if (buffer$1.length - transport.COMMON_HEADER_SIZE >= bufferLength) {
|
|
573
|
+
const value = buffer.Buffer.from(buffer$1);
|
|
574
|
+
bufferLength = 0;
|
|
575
|
+
buffer$1 = [];
|
|
576
|
+
(_h = this.runPromise) === null || _h === void 0 ? void 0 : _h.resolve(value.toString('hex'));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
this.Log.debug('monitor data error: ', error);
|
|
581
|
+
(_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleWriteCharacteristicError));
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
return () => {
|
|
585
|
+
this.Log.debug('remove characteristic monitor: ', characteristic.uuid);
|
|
586
|
+
subscription.remove();
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
release(uuid) {
|
|
590
|
+
var _a;
|
|
591
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
592
|
+
const transport = transportCache[uuid];
|
|
593
|
+
if (transport) {
|
|
594
|
+
delete transportCache[uuid];
|
|
595
|
+
(_a = transport.nofitySubscription) === null || _a === void 0 ? void 0 : _a.call(transport);
|
|
596
|
+
if (reactNative.Platform.OS === 'android') ;
|
|
597
|
+
}
|
|
598
|
+
return Promise.resolve(true);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
post(session, name, data) {
|
|
602
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
603
|
+
yield this.call(session, name, data);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
call(uuid, name, data) {
|
|
607
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
608
|
+
if (this.stopped) {
|
|
609
|
+
return Promise.reject(hardwareShared.ERRORS.TypedError('Transport stopped.'));
|
|
610
|
+
}
|
|
611
|
+
if (this._messages == null) {
|
|
612
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.TransportNotConfigured);
|
|
613
|
+
}
|
|
614
|
+
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
615
|
+
this.Log.debug('transport-react-native call this.runPromise', this.runPromise);
|
|
616
|
+
if (this.runPromise && !forceRun) {
|
|
617
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.TransportCallInProgress);
|
|
618
|
+
}
|
|
619
|
+
const transport$1 = transportCache[uuid];
|
|
620
|
+
if (!transport$1) {
|
|
621
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.TransportNotFound);
|
|
622
|
+
}
|
|
623
|
+
this.runPromise = hardwareShared.createDeferred();
|
|
624
|
+
const messages = this._messages;
|
|
625
|
+
if (name === 'ResourceUpdate' || name === 'ResourceAck') {
|
|
626
|
+
this.Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
|
|
627
|
+
file_name: data === null || data === void 0 ? void 0 : data.file_name,
|
|
628
|
+
hash: data === null || data === void 0 ? void 0 : data.hash,
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
else if (transport.LogBlockCommand.has(name)) {
|
|
632
|
+
this.Log.debug('transport-react-native', 'call-', ' name: ', name);
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
this.Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
|
|
636
|
+
}
|
|
637
|
+
const buffers = buildBuffers(messages, name, data);
|
|
638
|
+
function writeChunkedData(buffers, writeFunction, onError) {
|
|
639
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
640
|
+
const packetCapacity = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_PACKET_LENGTH;
|
|
641
|
+
let index = 0;
|
|
642
|
+
let chunk = ByteBuffer__default["default"].allocate(packetCapacity);
|
|
643
|
+
while (index < buffers.length) {
|
|
644
|
+
const buffer = buffers[index].toBuffer();
|
|
645
|
+
chunk.append(buffer);
|
|
646
|
+
index += 1;
|
|
647
|
+
if (chunk.offset === packetCapacity || index >= buffers.length) {
|
|
648
|
+
chunk.reset();
|
|
649
|
+
try {
|
|
650
|
+
yield writeFunction(chunk.toString('base64'));
|
|
651
|
+
chunk = ByteBuffer__default["default"].allocate(packetCapacity);
|
|
652
|
+
}
|
|
653
|
+
catch (e) {
|
|
654
|
+
onError(e);
|
|
655
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
if (name === 'EmmcFileWrite') {
|
|
662
|
+
yield writeChunkedData(buffers, data => transport$1.writeWithRetry(data), e => {
|
|
663
|
+
this.runPromise = null;
|
|
664
|
+
this.Log.error('writeCharacteristic write error: ', e);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
else if (name === 'FirmwareUpload') {
|
|
668
|
+
yield writeChunkedData(buffers, (data) => __awaiter(this, void 0, void 0, function* () {
|
|
669
|
+
yield transport$1.writeCharacteristic.writeWithoutResponse(data);
|
|
670
|
+
}), e => {
|
|
671
|
+
this.runPromise = null;
|
|
672
|
+
this.Log.error('writeCharacteristic write error: ', e);
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
for (const o of buffers) {
|
|
677
|
+
const outData = o.toString('base64');
|
|
678
|
+
try {
|
|
679
|
+
yield transport$1.writeCharacteristic.writeWithoutResponse(outData);
|
|
680
|
+
}
|
|
681
|
+
catch (e) {
|
|
682
|
+
this.Log.debug('writeCharacteristic write error: ', e);
|
|
683
|
+
this.runPromise = null;
|
|
684
|
+
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
|
|
685
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleDeviceNotBonded);
|
|
686
|
+
}
|
|
687
|
+
else if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationStartFailed) {
|
|
688
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleWriteCharacteristicError, e.reason);
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
try {
|
|
697
|
+
const response = yield this.runPromise.promise;
|
|
698
|
+
if (typeof response !== 'string') {
|
|
699
|
+
throw new Error('Returning data is not string.');
|
|
700
|
+
}
|
|
701
|
+
this.Log.debug('receive data: ', response);
|
|
702
|
+
const jsonData = receiveOne(messages, response);
|
|
703
|
+
return check.call(jsonData);
|
|
704
|
+
}
|
|
705
|
+
catch (e) {
|
|
706
|
+
this.Log.error('call error: ', e);
|
|
707
|
+
throw e;
|
|
708
|
+
}
|
|
709
|
+
finally {
|
|
710
|
+
this.runPromise = null;
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
stop() {
|
|
715
|
+
this.stopped = true;
|
|
716
|
+
}
|
|
717
|
+
cancel() {
|
|
718
|
+
this.Log.debug('transport-react-native transport cancel');
|
|
719
|
+
if (this.runPromise) ;
|
|
720
|
+
this.runPromise = null;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
module.exports = ReactNativeBleTransport;
|