@nebulae/angular-ble
Version:
A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)
1,109 lines (1,101 loc) • 180 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('aes-js'), require('rxjs/operators'), require('rxjs'), require('@angular/common')) :
typeof define === 'function' && define.amd ? define('@nebulae/angular-ble', ['exports', '@angular/core', 'aes-js', 'rxjs/operators', 'rxjs', '@angular/common'], factory) :
(factory((global.nebulae = global.nebulae || {}, global.nebulae['angular-ble'] = {}),global.ng.core,null,global.rxjs.operators,global.rxjs,global.ng.common));
}(this, (function (exports,i0,aes,operators,rxjs,common) { 'use strict';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
var BrowserWebBluetooth = (function () {
function BrowserWebBluetooth() {
this._ble = navigator.bluetooth;
if (!this._ble) {
console.log('error cargando bluetooth');
// bluetoothService.setBluetoothAvailable(false);
// throw new Error('Your browser does not support Smart Bluetooth. See http://caniuse.com/#search=Bluetooth for more details.');
}
}
/**
* @param {?} options
* @return {?}
*/
BrowserWebBluetooth.prototype.requestDevice = /**
* @param {?} options
* @return {?}
*/
function (options) {
return this._ble.requestDevice(options);
};
BrowserWebBluetooth.decorators = [
{ type: i0.Injectable },
];
/** @nocollapse */
BrowserWebBluetooth.ctorParameters = function () { return []; };
return BrowserWebBluetooth;
}());
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b)
if (b.hasOwnProperty(p))
d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
function __values(o) {
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
if (m)
return m.call(o);
return {
next: function () {
if (o && i >= o.length)
o = void 0;
return { value: o && o[i++], done: !o };
}
};
}
function __read(o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m)
return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
ar.push(r.value);
}
catch (error) {
e = { error: error };
}
finally {
try {
if (r && !r.done && (m = i["return"]))
m.call(i);
}
finally {
if (e)
throw e.error;
}
}
return ar;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/** @type {?} */
var GattServices = {
GENERIC_ACCESS: {
SERVICE: 'generic_access',
DEVICE_NAME: 'device_name',
APPEARANCE: 'appearance',
PRIVACY_FLAG: 'privacy_flag',
RECONNECTION_ADDRESS: 'reconnection_address',
PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS: 'peripheral_preferred_connection_parameters'
},
BATTERY: {
SERVICE: 'battery_service',
BATTERY_LEVEL: 'battery_level'
},
DEVICE_INFORMATION: {
SERVICE: 'device_information',
MANUFACTURER_NAME: 'manufacturer_name_string',
MODEL_NUMBER: 'model_number_string',
SERIAL_NUMBER: 'serial_number_string',
HARDWARE_REVISION: 'hardware_revision_string',
FIRMWARE_REVISION: 'firmware_revision_string',
SOFTWARE_REVISION: 'software_revision_string',
SYSTEM_ID: 'system_id',
PNP_ID: 'pnp_id'
}
};
/** @type {?} */
var GATT_SERVICES = Object.keys(GattServices).map(function (key) { return GattServices[key].SERVICE; });
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
var CypherAesService = (function () {
function CypherAesService() {
this.masterKey = [];
this.initialVector = [];
this.encryptMethod = 'CBC';
this.isStaticInitialVector = true;
this.isConfigExecuted = false;
}
/**
* Initial config used to initalice all required params
* @param masterKey key used to encrypt and decrypt
* @param initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
* @param encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
* @param additionalEncryptMethodParams configuration params used by the selected encrypt method.
* Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
* By CTR require the param counter and by CFB require the param segmentSize
* @param isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
*/
/**
* Initial config used to initalice all required params
* @param {?} masterKey key used to encrypt and decrypt
* @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
* @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
* @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
* Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
* By CTR require the param counter and by CFB require the param segmentSize
* @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
* @return {?}
*/
CypherAesService.prototype.config = /**
* Initial config used to initalice all required params
* @param {?} masterKey key used to encrypt and decrypt
* @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
* @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
* @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
* Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
* By CTR require the param counter and by CFB require the param segmentSize
* @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
* @return {?}
*/
function (masterKey, initialVector, encryptMethod, additionalEncryptMethodParams, isStaticInitialVector) {
if (initialVector === void 0) {
initialVector = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}
if (encryptMethod === void 0) {
encryptMethod = 'CBC';
}
if (additionalEncryptMethodParams === void 0) {
additionalEncryptMethodParams = {};
}
if (isStaticInitialVector === void 0) {
isStaticInitialVector = true;
}
this.isConfigExecuted = true;
this.masterKey = masterKey;
this.initialVector = initialVector;
this.encryptMethod = encryptMethod;
this.isStaticInitialVector = isStaticInitialVector;
this.additionalEncryptMethodParams = additionalEncryptMethodParams;
if (!isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
};
/**
* Encrypt the data using the encrypt method previously configured
* @param dataArrayBuffer data to encrypt
*/
/**
* Encrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to encrypt
* @return {?}
*/
CypherAesService.prototype.encrypt = /**
* Encrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to encrypt
* @return {?}
*/
function (dataArrayBuffer) {
if (!this.isConfigExecuted) {
throw new Error('Must configurate cypher-aes before call this method, use the method config()');
}
if (this.encryptMethod === 'CBC' || this.encryptMethod === 'ECB') {
dataArrayBuffer = this.addPadding(dataArrayBuffer);
}
return this.isStaticInitialVector
? this.generateEncryptMethodInstance().encrypt(dataArrayBuffer)
: this.enctrypMethodInstance.encrypt(dataArrayBuffer);
};
/**
* Decrypt the data using the encrypt method previously configured
* @param dataArrayBuffer data to decrypt
*/
/**
* Decrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to decrypt
* @return {?}
*/
CypherAesService.prototype.decrypt = /**
* Decrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to decrypt
* @return {?}
*/
function (dataArrayBuffer) {
if (!this.isConfigExecuted) {
throw new Error('Must configurate cypher-aes before call this method, use the method config()');
}
return this.isStaticInitialVector
? this.generateEncryptMethodInstance().decrypt(dataArrayBuffer)
: this.enctrypMethodInstance.decrypt(dataArrayBuffer);
};
/**
* Change the current initalVector
* @param initialVector new initalVector
*/
/**
* Change the current initalVector
* @param {?} initialVector new initalVector
* @return {?}
*/
CypherAesService.prototype.changeInitialVector = /**
* Change the current initalVector
* @param {?} initialVector new initalVector
* @return {?}
*/
function (initialVector) {
if (!this.isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.initialVector = initialVector;
};
/**
* Change the current encyptMethod
* @param encryptMethod new encryptMethod
*/
/**
* Change the current encyptMethod
* @param {?} encryptMethod new encryptMethod
* @return {?}
*/
CypherAesService.prototype.changeEncryptMethod = /**
* Change the current encyptMethod
* @param {?} encryptMethod new encryptMethod
* @return {?}
*/
function (encryptMethod) {
if (!this.isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.encryptMethod = encryptMethod;
};
/**
* Change the current isStaticInitialVector
* @param isStaticInitialVector new isStaticInitalVector
*/
/**
* Change the current isStaticInitialVector
* @param {?} isStaticInitialVector new isStaticInitalVector
* @return {?}
*/
CypherAesService.prototype.changeStaticInitialVector = /**
* Change the current isStaticInitialVector
* @param {?} isStaticInitialVector new isStaticInitalVector
* @return {?}
*/
function (isStaticInitialVector) {
if (!isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.isStaticInitialVector = isStaticInitialVector;
};
/**
* Change the current masterKey
* @param masterKey new masterKey
*/
/**
* Change the current masterKey
* @param {?} masterKey new masterKey
* @return {?}
*/
CypherAesService.prototype.changeMasterKey = /**
* Change the current masterKey
* @param {?} masterKey new masterKey
* @return {?}
*/
function (masterKey) {
if (!this.isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.masterKey = masterKey;
};
/**
* Add padding to the list
* @param {?} arrayBuffer
* @return {?}
*/
CypherAesService.prototype.addPadding = /**
* Add padding to the list
* @param {?} arrayBuffer
* @return {?}
*/
function (arrayBuffer) {
/** @type {?} */
var paddingLength = Math.ceil(Array.from(arrayBuffer).length / 16) * 16;
/** @type {?} */
var paddingList = new Array(paddingLength - Array.from(arrayBuffer).length).fill(0);
return new Uint8Array(Array.from(arrayBuffer).concat(paddingList));
};
/**
* generate a instance of the encrypt method using the current method configured
* @return {?}
*/
CypherAesService.prototype.generateEncryptMethodInstance = /**
* generate a instance of the encrypt method using the current method configured
* @return {?}
*/
function () {
/** @type {?} */
var enctrypMethodInstance;
switch (this.encryptMethod) {
case 'CBC':
enctrypMethodInstance = new aes.ModeOfOperation.cbc(this.masterKey, this.initialVector);
break;
case 'CTR':
if (!this.additionalEncryptMethodParams.counter) {
throw new Error('additionalEncryptMethodParams.counter is required to use encrypt method CTR');
}
enctrypMethodInstance = new aes.ModeOfOperation.ctr(this.masterKey, this.initialVector, new aes.Counter(this.additionalEncryptMethodParams.counter));
break;
case 'CFB':
if (!this.additionalEncryptMethodParams.segmentSize) {
throw new Error('additionalEncryptMethodParams.segmentSize is required to use encrypt method CFB');
}
enctrypMethodInstance = new aes.ModeOfOperation.cfb(this.masterKey, this.initialVector, this.additionalEncryptMethodParams.segmentSize);
break;
case 'OFB':
enctrypMethodInstance = new aes.ModeOfOperation.ofb(this.masterKey, this.initialVector);
break;
case 'ECB':
enctrypMethodInstance = new aes.ModeOfOperation.ecb(this.masterKey);
break;
}
return enctrypMethodInstance;
};
// #region UTILS
/**
* Convert the text to bytes
* @param text Text to convert
*/
/**
* Convert the text to bytes
* @param {?} text Text to convert
* @return {?}
*/
CypherAesService.prototype.textToBytes = /**
* Convert the text to bytes
* @param {?} text Text to convert
* @return {?}
*/
function (text) {
return aes.utils.utf8.toBytes(text);
};
/**
* Convert the bytes to text
* @param bytes Bytes to convert
*/
/**
* Convert the bytes to text
* @param {?} bytes Bytes to convert
* @return {?}
*/
CypherAesService.prototype.bytesToText = /**
* Convert the bytes to text
* @param {?} bytes Bytes to convert
* @return {?}
*/
function (bytes) {
return aes.utils.utf8.fromBytes(bytes);
};
/**
* Convert the bytes to hex
* @param bytes bytes to convert
*/
/**
* Convert the bytes to hex
* @param {?} bytes bytes to convert
* @return {?}
*/
CypherAesService.prototype.bytesTohex = /**
* Convert the bytes to hex
* @param {?} bytes bytes to convert
* @return {?}
*/
function (bytes) {
return aes.utils.hex.fromBytes(bytes);
};
/**
* Convert the hex to bytes
* @param hex Hex to convert
*/
/**
* Convert the hex to bytes
* @param {?} hex Hex to convert
* @return {?}
*/
CypherAesService.prototype.hexToBytes = /**
* Convert the hex to bytes
* @param {?} hex Hex to convert
* @return {?}
*/
function (hex) {
return aes.utils.hex.toBytes(hex);
};
// #endregion
/**
* @param {?} key
* @return {?}
*/
CypherAesService.prototype.generateSubkeys = /**
* @param {?} key
* @return {?}
*/
function (key) {
/** @type {?} */
var const_Zero = new Uint8Array(16);
/** @type {?} */
var const_Rb = new Buffer('00000000000000000000000000000087', 'hex');
/** @type {?} */
var enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
/** @type {?} */
var lEncrypted = enctrypMethodInstance.encrypt(const_Zero);
/** @type {?} */
var l = new Buffer(this.bytesTohex(lEncrypted), 'hex');
/** @type {?} */
var subkey1 = this.bitShiftLeft(l);
// tslint:disable-next-line:no-bitwise
if (l[0] & 0x80) {
subkey1 = this.xor(subkey1, const_Rb);
}
/** @type {?} */
var subkey2 = this.bitShiftLeft(subkey1);
// tslint:disable-next-line:no-bitwise
if (subkey1[0] & 0x80) {
subkey2 = this.xor(subkey2, const_Rb);
}
return { subkey1: subkey1, subkey2: subkey2 };
};
/**
* @param {?} key
* @param {?} message
* @return {?}
*/
CypherAesService.prototype.aesCmac = /**
* @param {?} key
* @param {?} message
* @return {?}
*/
function (key, message) {
console.log('INICIA CIFRADO!!!!!!!!!!!!!!!!');
/** @type {?} */
var subkeys = this.generateSubkeys(key);
/** @type {?} */
var blockCount = Math.ceil(message.length / 16);
/** @type {?} */
var lastBlockCompleteFlag;
/** @type {?} */
var lastBlock;
/** @type {?} */
var lastBlockIndex;
if (blockCount === 0) {
blockCount = 1;
lastBlockCompleteFlag = false;
}
else {
lastBlockCompleteFlag = message.length % 16 === 0;
}
lastBlockIndex = blockCount - 1;
if (lastBlockCompleteFlag) {
lastBlock = this.xor(this.getMessageBlock(message, lastBlockIndex), subkeys.subkey1);
}
else {
lastBlock = this.xor(this.getPaddedMessageBlock(message, lastBlockIndex), subkeys.subkey2);
}
/** @type {?} */
var x = new Buffer('00000000000000000000000000000000', 'hex');
/** @type {?} */
var y;
/** @type {?} */
var enctrypMethodInstance;
for (var index = 0; index < lastBlockIndex; index++) {
enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
y = this.xor(x, this.getMessageBlock(message, index));
/** @type {?} */
var xEncrypted = enctrypMethodInstance.encrypt(y);
console.log('X normal ===============> ', this.bytesTohex(y));
console.log('X encrypted ==============> ', this.bytesTohex(xEncrypted));
x = new Buffer(this.bytesTohex(xEncrypted), 'hex');
}
y = this.xor(lastBlock, x);
enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
/** @type {?} */
var yEncrypted = enctrypMethodInstance.encrypt(y);
console.log('Y normal ==============> ', this.bytesTohex(y));
console.log('Y encrypted ==============> ', this.bytesTohex(yEncrypted));
return yEncrypted;
};
/**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
CypherAesService.prototype.getMessageBlock = /**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
function (message, blockIndex) {
/** @type {?} */
var block = new Buffer(16);
/** @type {?} */
var start = blockIndex * 16;
/** @type {?} */
var end = start + 16;
/** @type {?} */
var blockI = 0;
for (var i = start; i < end; i++) {
block[blockI] = message[i];
blockI++;
}
return block;
};
/**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
CypherAesService.prototype.getPaddedMessageBlock = /**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
function (message, blockIndex) {
/** @type {?} */
var block = new Buffer(16);
/** @type {?} */
var start = blockIndex * 16;
/** @type {?} */
var end = message.length;
block.fill(0);
/** @type {?} */
var blockI = 0;
for (var i = start; i < end; i++) {
block[blockI] = message[i];
blockI++;
}
block[end - start] = 0x80;
return block;
};
/**
* @param {?} buffer
* @return {?}
*/
CypherAesService.prototype.bitShiftLeft = /**
* @param {?} buffer
* @return {?}
*/
function (buffer) {
/** @type {?} */
var shifted = new Buffer(buffer.length);
/** @type {?} */
var last = buffer.length - 1;
for (var index = 0; index < last; index++) {
// tslint:disable-next-line:no-bitwise
shifted[index] = buffer[index] << 1;
// tslint:disable-next-line:no-bitwise
if (buffer[index + 1] & 0x80) {
shifted[index] += 0x01;
}
}
// tslint:disable-next-line:no-bitwise
shifted[last] = buffer[last] << 1;
return shifted;
};
/**
* @param {?} bufferA
* @param {?} bufferB
* @return {?}
*/
CypherAesService.prototype.xor = /**
* @param {?} bufferA
* @param {?} bufferB
* @return {?}
*/
function (bufferA, bufferB) {
/** @type {?} */
var length = Math.min(bufferA.length, bufferB.length);
/** @type {?} */
var output = new Buffer(length);
for (var index = 0; index < length; index++) {
// tslint:disable-next-line:no-bitwise
output[index] = bufferA[index] ^ bufferB[index];
}
return output;
};
CypherAesService.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */
CypherAesService.ctorParameters = function () { return []; };
/** @nocollapse */ CypherAesService.ngInjectableDef = i0.defineInjectable({ factory: function CypherAesService_Factory() { return new CypherAesService(); }, token: CypherAesService, providedIn: "root" });
return CypherAesService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
var ConsoleLoggerService = (function () {
function ConsoleLoggerService() {
}
/**
* @param {...?} args
* @return {?}
*/
ConsoleLoggerService.prototype.log = /**
* @param {...?} args
* @return {?}
*/
function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
console.log.apply(console, args);
};
/**
* @param {...?} args
* @return {?}
*/
ConsoleLoggerService.prototype.error = /**
* @param {...?} args
* @return {?}
*/
function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
console.error.apply(console, args);
};
/**
* @param {...?} args
* @return {?}
*/
ConsoleLoggerService.prototype.warn = /**
* @param {...?} args
* @return {?}
*/
function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
console.warn.apply(console, args);
};
ConsoleLoggerService.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */ ConsoleLoggerService.ngInjectableDef = i0.defineInjectable({ factory: function ConsoleLoggerService_Factory() { return new ConsoleLoggerService(); }, token: ConsoleLoggerService, providedIn: "root" });
return ConsoleLoggerService;
}());
var NoLoggerService = (function () {
function NoLoggerService() {
}
/**
* @param {...?} args
* @return {?}
*/
NoLoggerService.prototype.log = /**
* @param {...?} args
* @return {?}
*/
function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
};
/**
* @param {...?} args
* @return {?}
*/
NoLoggerService.prototype.error = /**
* @param {...?} args
* @return {?}
*/
function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
};
/**
* @param {...?} args
* @return {?}
*/
NoLoggerService.prototype.warn = /**
* @param {...?} args
* @return {?}
*/
function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
};
NoLoggerService.decorators = [
{ type: i0.Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */ NoLoggerService.ngInjectableDef = i0.defineInjectable({ factory: function NoLoggerService_Factory() { return new NoLoggerService(); }, token: NoLoggerService, providedIn: "root" });
return NoLoggerService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
var BluetoothService = (function (_super) {
__extends(BluetoothService, _super);
function BluetoothService(_webBle, cypherAesService, _console) {
var _this = _super.call(this) || this;
_this._webBle = _webBle;
_this.cypherAesService = cypherAesService;
_this._console = _console;
_this.serviceCharacteristicVsSubscriptionList = {};
_this.notifierSubject = new rxjs.Subject();
_this.notifierStartedSubject = new rxjs.Subject();
_this.bluetoothAvailable = false;
_this._device$ = new i0.EventEmitter();
if (_webBle._ble) {
_this.bluetoothAvailable = true;
}
return _this;
}
/**
* @return {?}
*/
BluetoothService.prototype.isBluetoothAvailable = /**
* @return {?}
*/
function () {
return this.bluetoothAvailable;
};
/**
* get the current device, if the device return null is because the connection has lost
* @returns the current connceted device
*/
/**
* get the current device, if the device return null is because the connection has lost
* @return {?} the current connceted device
*/
BluetoothService.prototype.getDevice$ = /**
* get the current device, if the device return null is because the connection has lost
* @return {?} the current connceted device
*/
function () {
return this._device$;
};
/**
* @return {?}
*/
BluetoothService.prototype.getNotifierStartedSubject$ = /**
* @return {?}
*/
function () {
return this.notifierStartedSubject;
};
/**
* start a stream by notifiers characteristics
* @param service The service to which the characteristic belongs
* @param characteristic The characteristic whose value you want to listen
* @param options object that contains the
* startByte:number (required), stopByte:number (required),
* lengthPosition: { start: number, end: number, lengthPadding: number } (required)
* @returns A DataView than contains the characteristic value
*/
/**
* start a stream by notifiers characteristics
* @param {?} service The service to which the characteristic belongs
* @param {?} characteristic The characteristic whose value you want to listen
* @param {?} options object that contains the
* startByte:number (required), stopByte:number (required),
* lengthPosition: { start: number, end: number, lengthPadding: number } (required)
* @return {?} A DataView than contains the characteristic value
*/
BluetoothService.prototype.startNotifierListener$ = /**
* start a stream by notifiers characteristics
* @param {?} service The service to which the characteristic belongs
* @param {?} characteristic The characteristic whose value you want to listen
* @param {?} options object that contains the
* startByte:number (required), stopByte:number (required),
* lengthPosition: { start: number, end: number, lengthPadding: number } (required)
* @return {?} A DataView than contains the characteristic value
*/
function (service, characteristic, options) {
var _this = this;
return rxjs.defer(function () {
rxjs.of(service)
.pipe(operators.tap(function () { return _this._console.log('Inicia el notifier con la instancia: ', _this.serviceCharacteristicVsSubscriptionList); }), operators.filter(function (_) {
return Object.keys(_this.serviceCharacteristicVsSubscriptionList).indexOf(service + "-" + characteristic) === -1;
}))
.subscribe(function () {
_this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic] =
_this.buildNotifierListener$(service, characteristic, options).subscribe(function (message) {
_this.notifierSubject.next(message);
});
}, function (err) {
_this._console.log('[BLE::Info] Error in notifier: ', err);
}, function () { });
return rxjs.of("notifier as subscribed: service= " + service + ", characteristic= " + characteristic);
});
};
/**
* @param {?} service
* @param {?} characteristic
* @return {?}
*/
BluetoothService.prototype.stopNotifierListener$ = /**
* @param {?} service
* @param {?} characteristic
* @return {?}
*/
function (service, characteristic) {
var _this = this;
return rxjs.defer(function () {
// this.serviceCharacteristicVsSubscriptionList[`${service}-${characteristic}`].unsubscribe();
delete _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic];
return rxjs.of("the notifier of the characteristic " + characteristic + " as been stopped");
});
};
/**
* @param {?} service
* @param {?} characteristic
* @param {?} options
* @return {?}
*/
BluetoothService.prototype.buildNotifierListener$ = /**
* @param {?} service
* @param {?} characteristic
* @param {?} options
* @return {?}
*/
function (service, characteristic, options) {
var _this = this;
return this.getPrimaryService$(service).pipe(operators.tap(function (serviceInstance) { return _this._console.log("toma exitosamente el servicio ================> " + serviceInstance); }), operators.mergeMap(function (primaryService) {
return _this.getCharacteristic$(primaryService, characteristic)
.pipe(operators.tap(function (char) { return _this._console.log("toma exitosamente la caracteristica ================> " + char); }));
}), operators.mergeMap(function (char) {
return rxjs.defer(function () {
// enable the characteristic notifier
return char.startNotifications();
}).pipe(operators.retryWhen(function (error) {
return error.pipe(operators.tap(function () { return _this._console.log('ERROR EN EL startNotifications ================> '); }), operators.delay(1000), operators.take(5));
}), operators.tap(function () {
_this.notifierStartedSubject.next(true);
_this._console.log("incia las notifiaciones de la caracteristica 2 ================> " + characteristic);
}), operators.mergeMap(function (_) {
// start the lister from the even characteristicvaluechanged to get all changes on the specific
// characteristic
return rxjs.fromEvent(char, 'characteristicvaluechanged').pipe(operators.takeUntil(rxjs.fromEvent(char, 'gattserverdisconnected')), operators.map(function (event) {
// get a event from the characteristic and map that
return {
startByteMatches: false,
stopByteMatches: false,
lengthMatches: false,
messageLength: 0,
timestamp: Date.now(),
data: Array.from(new Uint8Array(((((event.target))
.value)).buffer))
};
}), operators.scan(function (acc, value) {
acc.timestamp = acc.timestamp === 0 ? value.timestamp : acc.timestamp;
// if the current accumulator value is a valid message, then is restarted to get the next
// message
if ((acc.lengthMatches &&
acc.startByteMatches &&
acc.stopByteMatches)
|| acc.timestamp + 1000 < Date.now()) {
acc = {
startByteMatches: false,
stopByteMatches: false,
lengthMatches: false,
messageLength: 0,
timestamp: 0,
data: []
};
}
// validate the start byte
if (!acc.startByteMatches &&
value.data[0] === options.startByte) {
// get the message length using the start and end position
acc.messageLength =
new DataView(new Uint8Array(value.data.slice(options.lengthPosition.start, options.lengthPosition.end)).buffer).getInt16(0, false) +
(options.lengthPosition.lengthPadding
? options.lengthPosition.lengthPadding
: 0);
// valid that the initial byte was found
acc.startByteMatches = true;
}
if (!acc.stopByteMatches &&
value.data[value.data.length - 1] === options.stopByte) {
// valid that the end byte was found
acc.stopByteMatches = true;
}
if (acc.startByteMatches) {
// merge the new data bytes to the old bytes
acc.data = acc.data.concat(value.data);
}
acc.lengthMatches =
acc.startByteMatches &&
acc.stopByteMatches &&
acc.messageLength === acc.data.length;
return acc;
}, {
startByteMatches: false,
stopByteMatches: false,
lengthMatches: false,
messageLength: 0,
timestamp: 0,
data: []
}),
// only publish the complete and valid message
operators.filter(function (data) {
return data.lengthMatches &&
data.startByteMatches &&
data.stopByteMatches;
}),
// remove all custom data and publish the message data
operators.map(function (result) { return result.data; }));
}));
}));
};
/**
* Send a request to the device and wait a unique response
* @param message Message to send
* @param service The service to which the characteristic belongs
* @param characteristic The characteristic whose value you want to send the message
* @param responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
* {position: 13, byteToMatch: 0x45}]
* @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
*/
/**
* Send a request to the device and wait a unique response
* @param {?} message Message to send
* @param {?} service The service to which the characteristic belongs
* @param {?} characteristic The characteristic whose value you want to send the message
* @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
* {position: 13, byteToMatch: 0x45}]
* @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
* @return {?}
*/
BluetoothService.prototype.sendAndWaitResponse$ = /**
* Send a request to the device and wait a unique response
* @param {?} message Message to send
* @param {?} service The service to which the characteristic belongs
* @param {?} characteristic The characteristic whose value you want to send the message
* @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
* {position: 13, byteToMatch: 0x45}]
* @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
* @return {?}
*/
function (message, service, characteristic, responseType, cypherMasterKey) {
this._console.log('[BLE::Info] Send message to device: ', this.cypherAesService.bytesTohex(message));
return rxjs.forkJoin(this.subscribeToNotifierListener(responseType, cypherMasterKey).pipe(operators.take(1)), this.sendToNotifier$(message, service, characteristic)).pipe(operators.map(function (_a) {
var _b = __read(_a, 2), messageResp = _b[0], _ = _b[1];
return messageResp;
}), operators.timeout(3000));
};
/**
* Subscribe to the notifiers filtering by byte checking
* @param filterOptions must specific the position and the byte to match Sample:
* [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
* @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
*/
/**
* Subscribe to the notifiers filtering by byte checking
* @param {?} filterOptions must specific the position and the byte to match Sample:
* [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
* @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
* @return {?}
*/
BluetoothService.prototype.subscribeToNotifierListener = /**
* Subscribe to the notifiers filtering by byte checking
* @param {?} filterOptions must specific the position and the byte to match Sample:
* [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
* @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
* @return {?}
*/
function (filterOptions, cypherMasterKey) {
var _this = this;
return this.notifierSubject.pipe(operators.map(function (messageUnformated) {
/** @type {?} */
var messageFormmated = (messageUnformated);
// validate if the message is cyphered
if (cypherMasterKey) {
/** @type {?} */
var datablockLength = new DataView(new Uint8Array(messageFormmated.slice(1, 3)).buffer).getInt16(0, false);
_this.cypherAesService.config(cypherMasterKey);
/** @type {?} */
var datablock = Array.from(_this.cypherAesService.decrypt(((messageUnformated)).slice(3, datablockLength + 3)));
// merge the datablock and the message
messageFormmated = ((messageUnformated))
.slice(0, 3)
.concat(datablock)
.concat(((messageUnformated)).slice(-2));
}
_this._console.log('[BLE::Info] Notification reived from device: ', _this.cypherAesService.bytesTohex(messageFormmated));
return messageFormmated;
}),
// filter the message using the filter options
operators.filter(function (message) {
/** @type {?} */
var availableMessage = false;
try {
for (var filterOptions_1 = __values(filterOptions), filterOptions_1_1 = filterOptions_1.next(); !filterOptions_1_1.done; filterOptions_1_1 = filterOptions_1.next()) {
var option = filterOptions_1_1.value;
if (message[option.position] === option.byteToMatch) {
availableMessage = true;
}
else {
availableMessage = false;
break;
}
}
}
catch (e_1_1) {
e_1 = { error: e_1_1 };
}
finally {
try {
if (filterOptions_1_1 && !filterOptions_1_1.done && (_a = filterOptions_1.return))
_a.call(filterOptions_1);
}
finally {
if (e_1)
throw e_1.error;
}
}
return availableMessage;
var e_1, _a;
}));
};
/**
* Start a request to the browser to list all available bluetooth devices
* @param {?=} options Options to request the devices the structure is:
* acceptAllDevices: true|false
* filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
* optionalServices: [] (services that are going to be used in
* communication with the device, must use the UIID or GATT identfier to list ther services)
* @return {?}
*/
BluetoothService.prototype.discoverDevice$ = /**
* Start a request to the browser to list all available bluetooth devices
* @param {