@nebulae/angular-ble
Version:
A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)
895 lines (894 loc) • 94.2 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
import * as tslib_1 from "tslib";
import { Injectable, EventEmitter } from '@angular/core';
import { map, mergeMap, concat, mapTo, filter, takeUntil, scan, tap, take, timeout, retryWhen, delay } from 'rxjs/operators';
import { Observable, forkJoin, Subject, defer, fromEvent, of, from } from 'rxjs';
import { GattServices } from './gatt-services';
import { BrowserWebBluetooth } from '../platform/browser';
import { CypherAesService } from '../cypher/cypher-aes.service';
import { ConsoleLoggerService } from '../logger.service';
import * as i0 from "@angular/core";
import * as i1 from "../platform/browser";
import * as i2 from "../cypher/cypher-aes.service";
import * as i3 from "../logger.service";
var BluetoothService = /** @class */ (function (_super) {
tslib_1.__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 Subject();
_this.notifierStartedSubject = new Subject();
_this.bluetoothAvailable = false;
_this._device$ = new 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 defer(function () {
of(service)
.pipe(tap(function () { return _this._console.log('Inicia el notifier con la instancia: ', _this.serviceCharacteristicVsSubscriptionList); }), 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 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 defer(function () {
// this.serviceCharacteristicVsSubscriptionList[`${service}-${characteristic}`].unsubscribe();
delete _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic];
return 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(tap(function (serviceInstance) { return _this._console.log("toma exitosamente el servicio ================> " + serviceInstance); }), mergeMap(function (primaryService) {
return _this.getCharacteristic$(primaryService, characteristic)
.pipe(tap(function (char) { return _this._console.log("toma exitosamente la caracteristica ================> " + char); }));
}), mergeMap(function (char) {
return defer(function () {
// enable the characteristic notifier
return char.startNotifications();
}).pipe(retryWhen(function (error) {
return error.pipe(tap(function () { return _this._console.log('ERROR EN EL startNotifications ================> '); }), delay(1000), take(5));
}), tap(function () {
_this.notifierStartedSubject.next(true);
_this._console.log("incia las notifiaciones de la caracteristica 2 ================> " + characteristic);
}), mergeMap(function (_) {
// start the lister from the even characteristicvaluechanged to get all changes on the specific
// characteristic
return fromEvent(char, 'characteristicvaluechanged').pipe(takeUntil(fromEvent(char, 'gattserverdisconnected')), 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((/** @type {?} */ ((/** @type {?} */ (event.target))
.value)).buffer))
};
}), 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
filter(function (data) {
return data.lengthMatches &&
data.startByteMatches &&
data.stopByteMatches;
}),
// remove all custom data and publish the message data
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 forkJoin(this.subscribeToNotifierListener(responseType, cypherMasterKey).pipe(take(1)), this.sendToNotifier$(message, service, characteristic)).pipe(map(function (_a) {
var _b = tslib_1.__read(_a, 2), messageResp = _b[0], _ = _b[1];
return messageResp;
}), 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(map(function (messageUnformated) {
/** @type {?} */
var messageFormmated = /** @type {?} */ (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((/** @type {?} */ (messageUnformated)).slice(3, datablockLength + 3)));
// merge the datablock and the message
messageFormmated = (/** @type {?} */ (messageUnformated))
.slice(0, 3)
.concat(datablock)
.concat((/** @type {?} */ (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
filter(function (message) {
/** @type {?} */
var availableMessage = false;
try {
for (var filterOptions_1 = tslib_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 {?=} 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 {?}
*/
function (options) {
var _this = this;
if (options === void 0) { options = /** @type {?} */ ({}); }
return defer(function () { return _this._webBle.requestDevice(options); }).pipe(mergeMap(function (device) {
_this.device = device;
_this._device$.emit(device);
return _this.configureDeviceDisconnection$(device).pipe(mapTo(device));
}));
};
/**
* @param {?} device
* @return {?}
*/
BluetoothService.prototype.configureDeviceDisconnection$ = /**
* @param {?} device
* @return {?}
*/
function (device) {
var _this = this;
return Observable.create(function (observer) {
of(device)
.pipe(mergeMap(function (dev) { return fromEvent(device, 'gattserverdisconnected'); }), take(1))
.subscribe(function () {
_this._console.log('Se desconecta disp en OnDevice disconnected!!!!!!!');
_this.device = null;
_this._device$.emit(null);
}, function (err) {
_this._console.log('[BLE::Info] Error in notifier: ', err);
observer.error(err);
}, function () {
});
observer.next("DisconnectionEvent as been register");
});
};
/**
* Discover all available devices and connect to a selected device
* @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)
* @returns the connected device
*/
/**
* Discover all available devices and connect to a selected device
* @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 {?} the connected device
*/
BluetoothService.prototype.connectDevice$ = /**
* Discover all available devices and connect to a selected device
* @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 {?} the connected device
*/
function (options) {
if (!options) {
options = {
acceptAllDevices: true,
optionalServices: []
};
}
else if (!options.optionalServices) {
options.optionalServices = [];
}
options.optionalServices.push(GattServices.GENERIC_ACCESS.SERVICE);
options.optionalServices.push(GattServices.BATTERY.SERVICE);
options.optionalServices.push(GattServices.DEVICE_INFORMATION.SERVICE);
return this.discoverDevice$(options).pipe(mergeMap(function (device) {
return defer(function () { return (/** @type {?} */ (device)).gatt.connect(); });
}));
};
/**
* Disconnect the current device
*/
/**
* Disconnect the current device
* @return {?}
*/
BluetoothService.prototype.disconnectDevice = /**
* Disconnect the current device
* @return {?}
*/
function () {
if (this.device) {
this._console.log('se deconecta dispositivo');
this.device.gatt.disconnect();
}
};
/**
* get a data from the device using the characteristic
* @param service UUID or GATT identifier service
* @param characteristic UUID or GATT identifier characteristic
* @return The characteristic data in a DataView object
*/
/**
* get a data from the device using the characteristic
* @param {?} service UUID or GATT identifier service
* @param {?} characteristic UUID or GATT identifier characteristic
* @return {?} The characteristic data in a DataView object
*/
BluetoothService.prototype.readDeviceValue$ = /**
* get a data from the device using the characteristic
* @param {?} service UUID or GATT identifier service
* @param {?} characteristic UUID or GATT identifier characteristic
* @return {?} The characteristic data in a DataView object
*/
function (service, characteristic) {
var _this = this;
if (!this.device) {
throw new Error('Must start a connection to a device before read the device value');
}
return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
return _this.getCharacteristic$(primaryService, characteristic);
}), mergeMap(function (characteristicValue) {
return _this.readValue$(characteristicValue);
}));
};
/**
* write a value in the selected characteristic
* @param characteristic the characterisitc where you want write the value
* @param value value the value to write
*/
/**
* write a value in the selected characteristic
* @param {?} service
* @param {?} characteristic the characterisitc where you want write the value
* @param {?} value value the value to write
* @return {?}
*/
BluetoothService.prototype.writeDeviceValue$ = /**
* write a value in the selected characteristic
* @param {?} service
* @param {?} characteristic the characterisitc where you want write the value
* @param {?} value value the value to write
* @return {?}
*/
function (service, characteristic, value) {
var _this = this;
if (!this.device) {
throw new Error('Must start a connection to a device before read the device value');
}
return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
return _this.getCharacteristic$(primaryService, characteristic);
}), mergeMap(function (characteristicValue) {
return _this.writeValue$(characteristicValue, value);
}));
};
/**
* get a primary service instance using the service UIID or GATT identifier
* @param service service identifier
* @return service instance
*/
/**
* get a primary service instance using the service UIID or GATT identifier
* @param {?} service service identifier
* @return {?} service instance
*/
BluetoothService.prototype.getPrimaryService$ = /**
* get a primary service instance using the service UIID or GATT identifier
* @param {?} service service identifier
* @return {?} service instance
*/
function (service) {
return of(this.device).pipe(mergeMap(function (device) {
return device.gatt.getPrimaryService(service);
}));
};
/**
* Get a characterisitic instance using the service instance and a characteristic UUID
* @param primaryService service instance
* @param characteristic characterisitic identifier
* @return characteristic instance
*/
/**
* Get a characterisitic instance using the service instance and a characteristic UUID
* @param {?} primaryService service instance
* @param {?} characteristic characterisitic identifier
* @return {?} characteristic instance
*/
BluetoothService.prototype.getCharacteristic$ = /**
* Get a characterisitic instance using the service instance and a characteristic UUID
* @param {?} primaryService service instance
* @param {?} characteristic characterisitic identifier
* @return {?} characteristic instance
*/
function (primaryService, characteristic) {
return defer(function () { return primaryService.getCharacteristic(characteristic); });
};
/**
* read the characteristic value
* @param {?} characteristic characteristic instance
* @return {?} The characteristic data in a DataView object
*/
BluetoothService.prototype.readValue$ = /**
* read the characteristic value
* @param {?} characteristic characteristic instance
* @return {?} The characteristic data in a DataView object
*/
function (characteristic) {
return from(characteristic
.readValue()
.then(function (data) { return Promise.resolve(data); }, function (error) { return Promise.reject("" + error.message); }));
};
/**
* write a value in the selected characteristic
* @param {?} characteristic the characterisitc where you want write the value
* @param {?} value the value to write
* @return {?}
*/
BluetoothService.prototype.writeValue$ = /**
* write a value in the selected characteristic
* @param {?} characteristic the characterisitc where you want write the value
* @param {?} value the value to write
* @return {?}
*/
function (characteristic, value) {
return defer(function () {
return characteristic
.writeValue(value);
});
};
/**
* change the state of the characteristic to enable it
* @param service parent service of the characteristic
* @param characteristic characteristic to change the state
* @param state new state
*/
/**
* change the state of the characteristic to enable it
* @param {?} service parent service of the characteristic
* @param {?} characteristic characteristic to change the state
* @param {?=} state new state
* @return {?}
*/
BluetoothService.prototype.enableCharacteristic$ = /**
* change the state of the characteristic to enable it
* @param {?} service parent service of the characteristic
* @param {?} characteristic characteristic to change the state
* @param {?=} state new state
* @return {?}
*/
function (service, characteristic, state) {
state = state || new Uint8Array([1]);
return this.setCharacteristicState$(service, characteristic, state);
};
/**
* change the state of the characteristic to disable it
* @param service parent service of the characteristic
* @param characteristic characteristic to change the state
* @param state new state
*/
/**
* change the state of the characteristic to disable it
* @param {?} service parent service of the characteristic
* @param {?} characteristic characteristic to change the state
* @param {?=} state new state
* @return {?}
*/
BluetoothService.prototype.disbaleCharacteristic$ = /**
* change the state of the characteristic to disable it
* @param {?} service parent service of the characteristic
* @param {?} characteristic characteristic to change the state
* @param {?=} state new state
* @return {?}
*/
function (service, characteristic, state) {
state = state || new Uint8Array([0]);
return this.setCharacteristicState$(service, characteristic, state);
};
/**
* set a state to an specific characteristic
* @param service parent service of the characteristic
* @param characteristic characteristic to change the state
* @param state new state
* @return
*/
/**
* set a state to an specific characteristic
* @param {?} service parent service of the characteristic
* @param {?} characteristic characteristic to change the state
* @param {?} state new state
* @return {?}
*/
BluetoothService.prototype.setCharacteristicState$ = /**
* set a state to an specific characteristic
* @param {?} service parent service of the characteristic
* @param {?} characteristic characteristic to change the state
* @param {?} state new state
* @return {?}
*/
function (service, characteristic, state) {
var _this = this;
/** @type {?} */
var primaryService = this.getPrimaryService$(service);
return primaryService.pipe(mergeMap(function (_primaryService) {
return _this.getCharacteristic$(_primaryService, characteristic);
}), map(function (_characteristic) {
return _this.writeValue$(_characteristic, state);
}));
};
/**
* Send a message using a notifier characteristic
* @param message message to send
* @param service service to which the characteristic belongs
* @param characteristic feature in which you want to send the notification
*/
/**
* Send a message using a notifier characteristic
* @param {?} message message to send
* @param {?} service service to which the characteristic belongs
* @param {?} characteristic feature in which you want to send the notification
* @return {?}
*/
BluetoothService.prototype.sendToNotifier$ = /**
* Send a message using a notifier characteristic
* @param {?} message message to send
* @param {?} service service to which the characteristic belongs
* @param {?} characteristic feature in which you want to send the notification
* @return {?}
*/
function (message, service, characteristic) {
var _this = this;
return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
return _this.getCharacteristic$(primaryService, characteristic);
}), mergeMap(function (char) {
if (message.length > 16) {
/** @type {?} */
var obsTest = of(undefined);
while (message.length > 16) {
obsTest = obsTest.pipe(concat(_this.writeValue$(char, message.slice(0, 16))));
message = message.slice(16, message.length);
}
if (message.length > 0) {
obsTest = obsTest.pipe(concat(_this.writeValue$(char, message)));
}
return obsTest;
}
else {
return _this.writeValue$(char, message);
}
}));
};
/**
* The Battery Level characteristic is read using the GATT Read Characteristic
* Value sub-procedure and returns the current battery level as a percentage
* from 0% to 100%; 0% represents a battery that is fully discharged, 100%
* represents a battery that is fully charged
*/
/**
* The Battery Level characteristic is read using the GATT Read Characteristic
* Value sub-procedure and returns the current battery level as a percentage
* from 0% to 100%; 0% represents a battery that is fully discharged, 100%
* represents a battery that is fully charged
* @return {?}
*/
BluetoothService.prototype.getBatteryLevel$ = /**
* The Battery Level characteristic is read using the GATT Read Characteristic
* Value sub-procedure and returns the current battery level as a percentage
* from 0% to 100%; 0% represents a battery that is fully discharged, 100%
* represents a battery that is fully charged
* @return {?}
*/
function () {
return this.readDeviceValue$(GattServices.BATTERY.SERVICE, GattServices.BATTERY.BATTERY_LEVEL).pipe(map(function (value) { return value.getUint8(0); }));
};
/**
* This characteristic represents the name of the manufacturer of the device.
*/
/**
* This characteristic represents the name of the manufacturer of the device.
* @return {?}
*/
BluetoothService.prototype.getManufacturerName$ = /**
* This characteristic represents the name of the manufacturer of the device.
* @return {?}
*/
function () {
var _this = this;
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MANUFACTURER_NAME).pipe(map(function (dataView) {
return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
}));
};
/**
* This characteristic represents the model number that is assigned by the device vendor.
*/
/**
* This characteristic represents the model number that is assigned by the device vendor.
* @return {?}
*/
BluetoothService.prototype.getModelNumber$ = /**
* This characteristic represents the model number that is assigned by the device vendor.
* @return {?}
*/
function () {
var _this = this;
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MODEL_NUMBER).pipe(map(function (dataView) {
return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
}));
};
/**
* This characteristic represents the serial number for a particular instance of the device.
*/
/**
* This characteristic represents the serial number for a particular instance of the device.
* @return {?}
*/
BluetoothService.prototype.getSerialNumber$ = /**
* This characteristic represents the serial number for a particular instance of the device.
* @return {?}
*/
function () {
var _this = this;
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SERIAL_NUMBER).pipe(map(function (dataView) {
return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
}));
};
/**
* This characteristic represents the hardware revision for the hardware within the device.
*/
/**
* This characteristic represents the hardware revision for the hardware within the device.
* @return {?}
*/
BluetoothService.prototype.getHardwareRevision$ = /**
* This characteristic represents the hardware revision for the hardware within the device.
* @return {?}
*/
function () {
var _this = this;
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.HARDWARE_REVISION).pipe(map(function (dataView) {
return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
}));
};
/**
* This characteristic represents the firmware revision for the firmware within the device.
*/
/**
* This characteristic represents the firmware revision for the firmware within the device.
* @return {?}
*/
BluetoothService.prototype.getFirmwareRevision$ = /**
* This characteristic represents the firmware revision for the firmware within the device.
* @return {?}
*/
function () {
var _this = this;
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.FIRMWARE_REVISION).pipe(map(function (dataView) {
return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
}));
};
/**
* This characteristic represents the software revision for the software within the device.
*/
/**
* This characteristic represents the software revision for the software within the device.
* @return {?}
*/
BluetoothService.prototype.getSoftwareRevision$ = /**
* This characteristic represents the software revision for the software within the device.
* @return {?}
*/
function () {
var _this = this;
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SOFTWARE_REVISION).pipe(map(function (dataView) {
return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
}));
};
/**
* This characteristic represents a structure containing an Organizationally Unique Identifier
* (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
*/
/**
* This characteristic represents a structure containing an Organizationally Unique Identifier
* (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
* @return {?}
*/
BluetoothService.prototype.getSystemId$ = /**
* This characteristic represents a structure containing an Organizationally Unique Identifier
* (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
* @return {?}
*/
function () {
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SYSTEM_ID);
};
/**
* The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
*/
/**
* The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
* @return {?}
*/
BluetoothService.prototype.getPnpId$ = /**
* The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
* @return {?}
*/
function () {
return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.PNP_ID);
};
BluetoothService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */
BluetoothService.ctorParameters = function () { return [
{ type: BrowserWebBluetooth },
{ type: CypherAesService },
{ type: ConsoleLoggerService }
]; };
/** @nocollapse */ BluetoothService.ngInjectableDef = i0.defineInjectable({ factory: function BluetoothService_Factory() { return new BluetoothService(i0.inject(i1.BrowserWebBluetooth), i0.inject(i2.CypherAesService), i0.inject(i3.ConsoleLoggerService)); }, token: BluetoothService, providedIn: "root" });
return BluetoothService;
}(Subject));
export { BluetoothService };
if (false) {
/** @type {?} */
BluetoothService.prototype._device$;
/** @type {?} */
BluetoothService.prototype.device;
/** @type {?} */
BluetoothService.prototype.serviceCharacteristicVsSubscriptionList;
/** @type {?} */
BluetoothService.prototype.notifierSubject;
/** @type {?} */
BluetoothService.prototype.notifierStartedSubject;
/** @type {?} */
BluetoothService.prototype.bluetoothAvailable;
/** @type {?} */
BluetoothService.prototype._webBle;
/** @type {?} */
BluetoothService.prototype.cypherAesService;
/** @type {?} */
BluetoothService.prototype._console;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmx1ZXRvb3RoLnNlcnZpY2UuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9AbmVidWxhZS9hbmd1bGFyLWJsZS8iLCJzb3VyY2VzIjpbImxpYi9ibHVldG9vdGgvYmx1ZXRvb3RoLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQ0wsR0FBRyxFQUNILFFBQVEsRUFDUixNQUFNLEVBQ04sS0FBSyxFQUNMLE1BQU0sRUFDTixTQUFTLEVBRVQsSUFBSSxFQUNKLEdBQUcsRUFDSCxJQUFJLEVBQ0osT0FBTyxFQUNQLFNBQVMsRUFDVCxLQUFLLEVBQ04sTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQ0wsVUFBVSxFQUNWLFFBQVEsRUFDUixPQUFPLEVBQ1AsS0FBSyxFQUNMLFNBQVMsRUFDVCxFQUFFLEVBQ0YsSUFBSSxFQUVMLE1BQU0sTUFBTSxDQUFDO0FBQ2QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzFELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDOzs7Ozs7SUFLbkIsNENBQXlCO0lBTzdELDBCQUNTLFNBQ0Msa0JBQ0Q7UUFIVCxZQUtFLGlCQUFPLFNBS1I7UUFUUSxhQUFPLEdBQVAsT0FBTztRQUNOLHNCQUFnQixHQUFoQixnQkFBZ0I7UUFDakIsY0FBUSxHQUFSLFFBQVE7d0RBUGlDLEVBQUU7Z0NBQzFCLElBQUksT0FBTyxFQUFFO3VDQUNOLElBQUksT0FBTyxFQUFFO21DQUNqQixLQUFLO1FBT2hDLEtBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxZQUFZLEVBQW1CLENBQUM7UUFDcEQsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDakIsS0FBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksQ0FBQztTQUNoQzs7S0FDRjs7OztJQUVELCtDQUFvQjs7O0lBQXBCO1FBQ0UsTUFBTSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQztLQUNoQztJQUNEOzs7T0FHRzs7Ozs7SUFDSCxxQ0FBVTs7OztJQUFWO1FBQ0UsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7S0FDdEI7Ozs7SUFFRCxxREFBMEI7OztJQUExQjtRQUNFLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUM7S0FDcEM7SUFDRDs7Ozs7Ozs7T0FRRzs7Ozs7Ozs7OztJQUNILGlEQUFzQjs7Ozs7Ozs7O0lBQXRCLFVBQXVCLE9BQU8sRUFBRSxjQUFjLEVBQUUsT0FBTztRQUF2RCxpQkFnQ0M7UUEvQkMsTUFBTSxDQUFDLEtBQUssQ0FBQztZQUNYLEVBQUUsQ0FBQyxPQUFPLENBQUM7aUJBQ1IsSUFBSSxDQUNILEdBQUcsQ0FBQyxjQUFNLE9BQUEsS0FBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsdUNBQXVDLEVBQUUsS0FBSSxDQUFDLHVDQUF1QyxDQUFDLEVBQXhHLENBQXdHLENBQUMsRUFDbkgsTUFBTSxDQUNKLFVBQUEsQ0FBQztnQkFDQyxPQUFBLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSSxDQUFDLHVDQUF1QyxDQUFDLENBQUMsT0FBTyxDQUM1RCxPQUFPLFNBQUksY0FBZ0IsQ0FDL0IsS0FBSyxDQUFDLENBQUM7WUFGUixDQUVRLENBQ1gsQ0FDRjtpQkFDQSxTQUFTLENBQ1I7Z0JBQ0UsS0FBSSxDQUFDLHVDQUF1QyxDQUFJLE9BQU8sU0FBSSxjQUFnQixDQUFDO29CQUMxRSxLQUFJLENBQUMsc0JBQXNCLENBQ3pCLE9BQU8sRUFDUCxjQUFjLEVBQ2QsT0FBTyxDQUNSLENBQUMsU0FBUyxDQUFDLFVBQUEsT0FBTzt3QkFDakIsS0FBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7cUJBQ3BDLENBQUMsQ0FBQzthQUVOLEVBQ0QsVUFBQSxHQUFHO2dCQUNELEtBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLGlDQUFpQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQzNELEVBQ0QsZUFBUyxDQUNaLENBQUM7WUFDRixNQUFNLENBQUMsRUFBRSxDQUFDLHNDQUFvQyxPQUFPLDBCQUFxQixjQUFnQixDQUFDLENBQUM7U0FDN0YsQ0FBQyxDQUFDO0tBRUo7Ozs7OztJQUVELGdEQUFxQjs7Ozs7SUFBckIsVUFBc0IsT0FBTyxFQUFFLGNBQWM7UUFBN0MsaUJBTUM7UUFMQyxNQUFNLENBQUMsS0FBSyxDQUFDOztZQUVYLE9BQU8sS0FBSSxDQUFDLHVDQUF1QyxDQUFJLE9BQU8sU0FBSSxjQUFnQixDQUFDLENBQUM7WUFDcEYsTUFBTSxDQUFDLEVBQUUsQ0FBQyx3Q0FBc0MsY0FBYyxxQkFBa0IsQ0FBQyxDQUFDO1NBQ25GLENBQUMsQ0FBQztLQUNKOzs7Ozs7O0lBRU8saURBQXNCOzs7Ozs7Y0FBQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE9BQU87O1FBQzdELE1BQU0sQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUMxQyxHQUFHLENBQUMsVUFBQSxlQUFlLElBQUksT0FBQSxLQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxxREFBbUQsZUFBaUIsQ0FBQyxFQUF2RixDQUF1RixDQUFDLEVBQy9HLFFBQVEsQ0FBQyxVQUFBLGNBQWM7WUFDckIsT0FBQSxLQUFJLENBQUMsa0JBQWtCLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQztpQkFDdEQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFBLElBQUksSUFBSSxPQUFBLEtBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLDJEQUF5RCxJQUFNLENBQUMsRUFBbEYsQ0FBa0YsQ0FBQyxDQUFDO1FBRHRHLENBQ3NHLENBQ3ZHLEVBQ0QsUUFBUSxDQUFDLFVBQUMsSUFBdUM7WUFDL0MsTUFBTSxDQUFDLEtBQUssQ0FBQzs7Z0JBRVgsTUFBTSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO2FBQ2xDLENBQUMsQ0FBQyxJQUFJLENBQ0wsU0FBUyxDQUFDLFVBQUEsS0FBSztnQkFDYixPQUFBLEtBQUssQ0FBQyxJQUFJLENBQ1IsR0FBRyxDQUFDLGNBQU0sT0FBQSxLQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxtREFBbUQsQ0FBQyxFQUF0RSxDQUFzRSxDQUFDLEVBQ2pGLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFDWCxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQ1I7WUFKRCxDQUlDLENBQ0YsRUFDRCxHQUFHLENBQUM7Z0JBQ0YsS0FBSSxDQUFDLHNCQUFzQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDdkMsS0FBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsc0VBQW9FLGNBQWdCLENBQUMsQ0FBQzthQUN6RyxDQUFDLEVBQ0YsUUFBUSxDQUFDLFVBQUEsQ0FBQzs7O2dCQUdSLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLDRCQUE0QixDQUFDLENBQUMsSUFBSSxDQUN2RCxTQUFTLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSx3QkFBd0IsQ0FBQyxDQUFDLEVBQ3BELEdBQUcsQ0FBQyxVQUFDLEtBQVk7O29CQUVmLE1BQU0sQ0FBQzt3QkFDTCxnQkFBZ0IsRUFBRSxLQUFLO3dCQUN2QixlQUFlLEVBQUUsS0FBSzt3QkFDdEIsYUFBYSxFQUFFLEtBQUs7d0JBQ3BCLGFBQWEsRUFBRSxDQUFDO3dCQUNoQixTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRTt3QkFDckIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQ2QsSUFBSSxVQUFVLENBQ1osbUJBQUMsbUJBQUMsS0FBSyxDQUFDLE1BQTJDLEVBQUM7NkJBQ2pELEtBQWlCLEVBQUMsQ0FBQyxNQUFNLENBQzdCLENBQ0Y7cUJBQ0YsQ0FBQztpQkFDSCxDQUFDLEVBQ0YsSUFBSSxDQUNGLFVBQUMsR0FBRyxFQUFFLEtBQUs7b0JBQ1QsR0FBRyxDQUFDLFNBQVMsR0FBRyxHQUFHLENBQUMsU0FBUyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQzs7O29CQUd0RSxFQUFFLENBQUMsQ0FDRCxDQUFDLEdBQUcsQ0FBQyxhQUFhO3dCQUNoQixHQUFHLENBQUMsZ0JBQWdCO3dCQUNwQixHQUFHLENBQUMsZUFBZSxDQUFDOzJCQUNuQixHQUFHLENBQUMsU0FBUyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxFQUNwQyxDQUFDLENBQUMsQ0FBQzt3QkFDRCxHQUFHLEdBQUc7NEJBQ0osZ0JBQWdCLEVBQUUsS0FBSzs0QkFDdkIsZUFBZSxFQUFFLEtBQUs7NEJBQ3RCLGFBQWEsRUFBRSxLQUFLOzRCQUNwQixhQUFhLEVBQUUsQ0FBQzs0QkFDaEIsU0FBUyxFQUFFLENBQUM7NEJBQ1osSUFBSSxFQUFFLEVBQUU7eUJBQ1QsQ0FBQztxQkFDSDs7b0JBRUQsRUFBRSxDQUFDLENBQ0QsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCO3dCQUNyQixLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLE9BQU8sQ0FBQyxTQUM1QixDQUFDLENBQUMsQ0FBQzs7d0JBRUQsR0FBRyxDQUFDLGFBQWE7NEJBQ2YsSUFBSSxRQUFRLENBQ1YsSUFBSSxVQUFVLENBQ1osS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQ2QsT0FBTyxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQzVCLE9BQU8sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUMzQixDQUNGLENBQUMsTUFBTSxDQUNULENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUM7Z0NBQ3BCLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxhQUFhO29DQUNuQyxDQUFDLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxhQUFhO29DQUN0QyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7O3dCQUVULEdBQUcsQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUM7cUJBQzdCO29CQUNELEVBQUUsQ0FBQyxDQUNELENBQUMsR0FBRyxDQUFDLGVBQWU7d0JBQ3BCLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLEtBQUssT0FBTyxDQUFDLFFBQ2hELENBQUMsQ0FBQyxDQUFDOzt3QkFFRCxHQUFHLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztxQkFDNUI7b0JBQ0QsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQzs7d0JBRXpCLEdBQUcsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO3FCQUN4QztvQkFDRCxHQUFHLENBQUMsYUFBYTt3QkFDZixHQUFHLENBQUMsZ0JBQWdCOzRCQUNwQixHQUFHLENBQUMsZUFBZTs0QkFDbkIsR0FBRyxDQUFDLGFBQWEsS0FBSyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztvQkFDeEMsTUFBTSxDQUFDLEdBQUcsQ0FBQztpQkFDWixFQUNEO29CQUNFLGdCQUFnQixFQUFFLEtBQUs7b0JBQ3ZCLGVBQWUsRUFBRSxLQUFLO29CQUN0QixhQUFhLEVBQUUsS0FBSztvQkFDcEIsYUFBYSxFQUFFLENBQUM7b0JBQ2hCLFNBQVMsRUFBRSxDQUFDO29CQUNaLElBQUksRUFBRSxFQUFFO2lCQUNULENBQ0Y7O2dCQUVELE1BQU0sQ0FDSixVQUFBLElBQUk7b0JBQ0YsT0FBQSxJQUFJLENBQUMsYUFBYTt3QkFDbEIsSUFBSSxDQUFDLGdCQUFnQjt3QkFDckIsSUFBSSxDQUFDLGVBQWU7Z0JBRnBCLENBRW9CLENBQ3ZCOztnQkFFRCxHQUFHLENBQUMsVUFBQSxNQUFNLElBQUksT0FBQSxNQUFNLENBQUMsSUFBSSxFQUFYLENBQVcsQ0FBQyxDQUMzQixDQUFDO2FBQ0gsQ0FBQyxDQUNILENBQUM7U0FDSCxDQUFDLENBQ0gsQ0FBQzs7SUFFSjs7Ozs7Ozs7T0FRRzs7Ozs7Ozs7Ozs7SUFDSCwrQ0FBb0I7Ozs7Ozs7Ozs7SUFBcEIsVUFDRSxPQUFPLEVBQ1AsT0FBTyxFQUNQLGNBQWMsRUFDZCxZQUFZLEVBQ1osZUFBZ0I7UUFFaEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQ2Ysc0NBQXNDLEVBQ3RDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQzFDLENBQUM7UUFDRixNQUFNLENBQUMsUUFBUSxDQUNiLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUMsSUFBSSxDQUNsRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQ1IsRUFDRCxJQUFJLENBQUMsZUFBZSxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsY0FBYyxDQUFDLENBQ3ZELENBQUMsSUFBSSxDQUNKLEdBQUcsQ0FBQyxVQUFDLEVBQWdCO2dCQUFoQiwwQkFBZ0IsRUFBZixtQkFBVyxFQUFFLFNBQUM7WUFBTSxPQUFBLFdBQVc7UUFBWCxDQUFXLENBQUMsRUFDdEMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUNkLENBQUM7S0FDSDtJQUNEOzs7OztPQUtHOzs7Ozs7OztJQUNILHNEQUEyQjs7Ozs7OztJQUEzQixVQUE0QixhQUFhLEVBQUUsZUFBZ0I7UUFBM0QsaUJBNENDO1FBM0NDLE1BQU0sQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FDOUIsR0FBRyxDQUFDLFVBQUEsaUJBQWlCOztZQUVuQixJQUFJLGdCQUFnQixxQkFBRyxpQkFBd0IsRUFBQzs7WUFFaEQsRUFBRSxDQUFDLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQzs7Z0JBRXBCLElBQU0sZUFBZSxHQUFHLElBQUksUUFBUSxDQUNsQyxJQUFJLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUNwRCxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ3JCLEtBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7O2dCQUU5QyxJQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUMxQixLQUFJLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUMzQixtQkFBQyxpQkFBd0IsRUFBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsZUFBZSxHQUFHLENBQUMsQ0FBQyxDQUN6RCxDQUNGLENBQUM7O2dCQUVGLGdCQUFnQixHQUFHLG1CQUFDLGlCQUF3QixFQUFDO3FCQUMxQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztxQkFDWCxNQUFNLENBQUMsU0FBUyxDQUFDO3FCQUNqQixNQUFNLENBQUMsbUJBQUMsaUJBQX