obniz
Version:
obniz sdk for javascript
537 lines (536 loc) • 19 kB
JavaScript
"use strict";
/**
* Obniz BLE are switches automatically. <br/>
* obnizOS ver >= 3.0.0 : [[ObnizCore.Components.Ble.Hci | Hci]] <br/>
* obnizOS ver < 3.0.0 : Not Supported <br/>
*
* @packageDocumentation
* @module ObnizCore.Components.Ble.Hci
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObnizBLE = void 0;
const hci_1 = require("./hci");
const bindings_1 = require("./protocol/central/bindings");
const hci_2 = require("./protocol/hci");
const bindings_2 = require("./protocol/peripheral/bindings");
const semver_1 = __importDefault(require("semver"));
const ObnizError_1 = require("../../../ObnizError");
const ComponentAbstact_1 = require("../../ComponentAbstact");
const bleAdvertisement_1 = require("./bleAdvertisement");
const bleCharacteristic_1 = require("./bleCharacteristic");
const bleDescriptor_1 = require("./bleDescriptor");
const blePeripheral_1 = require("./blePeripheral");
const bleRemotePeripheral_1 = require("./bleRemotePeripheral");
const bleScan_1 = require("./bleScan");
const bleService_1 = require("./bleService");
const bleExtendedAdvertisement_1 = require("./bleExtendedAdvertisement");
/**
* Use a obniz device as a BLE device.
* Peripheral and Central mode are supported
*/
class ObnizBLE extends ComponentAbstact_1.ComponentAbstract {
constructor(obniz, info) {
super(obniz);
this.remotePeripherals = [];
/**
* @ignore
*/
this._initialized = false;
// eslint-disable-next-line
this.debugHandler = (text) => {
};
const extended = info.extended;
this.hci = new hci_1.ObnizBLEHci(obniz, extended);
this.service = bleService_1.BleService;
this.characteristic = bleCharacteristic_1.BleCharacteristic;
this.descriptor = bleDescriptor_1.BleDescriptor;
// this.on("/response/ble/hci/read", (obj) => {
// if (obj.hci) {
// this.hci.notified(obj.hci);
// }
// });
// this.on("/response/ble/error", (obj) => {
// if (obj.error) {
// const error = obj.error;
// let msg = "BLE error: " + error.message;
// msg += " (";
// msg += "error_code: " + error.error_code;
// msg += ", ";
// msg += "module_error_code: " + error.module_error_code;
// msg += ", ";
// msg += "function_code: " + error.function_code;
// msg += ", ";
// msg += "address: " + error.address;
// msg += ", ";
// msg += "service_uuid: " + error.service_uuid;
// msg += ", ";
// msg += "characteristic_uuid: " + error.characteristic_uuid;
// msg += ", ";
// msg += "descriptor_uuid: " + error.descriptor_uuid;
// msg += ")";
//
// this.Obniz.error({ alert: "error", message: msg });
// }
// });
this._reset();
}
// public security!: BleSecurity;
/**
* Initialized status.
*
* ```javascript
* // Javascript Example
* obniz.ble.isInitialized; // => false
* await obniz.ble.initWait();
* obniz.ble.isInitialized; // => true
* ```
*/
get isInitialized() {
return this._initialized;
}
/**
* @ignore
*
* @param data
* @param reverse
* @private
*/
static _dataArray2uuidHex(data, reverse) {
let uuid = [];
for (let i = 0; i < data.length; i++) {
uuid.push(('00' + data[i].toString(16).toLowerCase()).slice(-2));
}
if (reverse) {
uuid = uuid.reverse();
}
let str = uuid.join('');
if (uuid.length >= 16) {
str =
str.slice(0, 8) +
'-' +
str.slice(8, 12) +
'-' +
str.slice(12, 16) +
'-' +
str.slice(16, 20) +
'-' +
str.slice(20);
}
return str;
}
notifyFromObniz(json) {
if (json.hci) {
this.hci.notified(json.hci);
}
if (json.error) {
const error = json.error;
let msg = 'BLE error: ' + error.message;
msg += ' (';
msg += 'error_code: ' + error.error_code;
msg += ', ';
msg += 'module_error_code: ' + error.module_error_code;
msg += ', ';
msg += 'function_code: ' + error.function_code;
msg += ', ';
msg += 'address: ' + error.address;
msg += ', ';
msg += 'service_uuid: ' + error.service_uuid;
msg += ', ';
msg += 'characteristic_uuid: ' + error.characteristic_uuid;
msg += ', ';
msg += 'descriptor_uuid: ' + error.descriptor_uuid;
msg += ')';
this.Obniz.error({ alert: 'error', message: msg });
}
}
/**
* ESP32 C3 or ESP32 S3 only
*
* Sets the PHY to use by default
*
* ```javascript
* // Javascript Example
* await obniz.ble.setDefaultPhyWait(false,false,true);//coded only
* ```
*/
async setDefaultPhyWait(usePhy1m, usePhy2m, usePhyCoded) {
await this.centralBindings.setDefaultPhyWait(usePhy1m, usePhy2m, usePhyCoded);
}
_onUpdatePhy(handler, txPhy, rxPhy) {
if (this.onUpdatePhy) {
this.onUpdatePhy(this.phyToStr(txPhy), this.phyToStr(rxPhy), handler);
}
}
/**
* Initialize BLE module. You need call this first everything before.
* This throws if device is not supported device.
*
* esp32 C3 or esp32 S3 Put true in the argument
* when not using the BLE5.0 extended advertise
*
* ```javascript
* // Javascript Example
* await obniz.ble.initWait();
* ```
*/
async initWait(supportType = {}) {
if (this.hci._extended &&
supportType &&
typeof supportType.extended === 'boolean') {
this.hci._extended = supportType.extended;
this._reset(true);
}
if (!this._initialized) {
const MinHCIAvailableOS = '3.0.0';
if (semver_1.default.lt(this.Obniz.firmware_ver, MinHCIAvailableOS)) {
throw new ObnizError_1.ObnizBleUnSupportedOSVersionError(this.Obniz.firmware_ver, MinHCIAvailableOS);
}
// force initialize on obnizOS < 3.2.0
if (semver_1.default.lt(semver_1.default.coerce(this.Obniz.firmware_ver), '3.2.0')) {
this.hci.init();
this.hci.end(); // disable once
this.hci.init();
}
try {
await this.hciProtocol.initWait();
}
catch (e) {
if (e instanceof ObnizError_1.ObnizBleUnsupportedHciError) {
this.Obniz.reboot();
}
throw e;
}
this._initialized = true;
}
}
/**
* Reset Target Device and current SDK status without rebooting. If error occured while reset, then target device will reboot.
*
* ```javascript
* // Javascript Example
* await obniz.ble.resetWait();
* ```
*/
async resetWait() {
try {
if (this._initialized) {
this._reset();
await this.hciProtocol.resetWait();
this._initialized = true;
}
}
catch (e) {
if (e instanceof ObnizError_1.ObnizBleUnsupportedHciError) {
this.Obniz.reboot();
}
throw e;
}
}
/**
* @ignore
* @private
*/
_reset(keepExtended = false) {
// reset state at first
this._initialized = false;
this._initializeWarning = true;
// clear all found peripherals.
for (const p of this.remotePeripherals) {
if (p.connected) {
p.notifyFromServer('statusupdate', {
status: 'disconnected',
reason: new ObnizError_1.ObnizOfflineError(),
});
}
}
this.remotePeripherals = [];
// instantiate
if (!this.peripheral) {
this.peripheral = new blePeripheral_1.BlePeripheral(this);
}
if (!this.scan) {
this.scan = new bleScan_1.BleScan(this);
}
else {
this.scan.notifyFromServer('obnizClose', {});
}
if (!this.advertisement) {
this.advertisement = new bleAdvertisement_1.BleAdvertisement(this);
}
if (!this.extendedAdvertisement && this.hci._extended) {
this.extendedAdvertisement = new bleExtendedAdvertisement_1.BleExtendedAdvertisement(this);
}
if (!this.hci._extended) {
this.extendedAdvertisement = undefined;
}
// reset all submodules.
this.peripheral._reset();
this.scan._reset();
this.advertisement._reset();
if (this.extendedAdvertisement) {
this.extendedAdvertisement._reset();
}
// clear scanning
this.hci._reset(keepExtended);
if (!this.hciProtocol) {
this.hciProtocol = new hci_2.Hci(this.hci);
this.hciProtocol.debugHandler = (text) => {
this.debug(`BLE-HCI: ${text}`);
};
}
else {
this.hciProtocol._reset();
}
if (!this.centralBindings) {
this.centralBindings = new bindings_1.NobleBindings(this.hciProtocol);
this.centralBindings.debugHandler = (text) => {
this.debug(`BLE: ${text}`);
};
this.centralBindings.on('stateChange', this.onStateChange.bind(this));
this.centralBindings.on('discover', this.onDiscover.bind(this));
this.centralBindings.on('disconnect', this.onDisconnect.bind(this));
this.centralBindings.on('notification', this.onNotification.bind(this));
this.centralBindings.on('updatePhy', this._onUpdatePhy.bind(this));
}
else {
this.centralBindings._reset();
}
if (!this.peripheralBindings) {
this.peripheralBindings = new bindings_2.BlenoBindings(this.hciProtocol);
this.peripheralBindings.on('stateChange', this.onPeripheralStateChange.bind(this));
this.peripheralBindings.on('accept', this.onPeripheralAccept.bind(this));
this.peripheralBindings.on('mtuChange', this.onPeripheralMtuChange.bind(this));
this.peripheralBindings.on('disconnect', this.onPeripheralDisconnect.bind(this));
}
else {
this.peripheralBindings._reset();
}
}
/**
* Connect to peripheral without scanning.
* Returns a peripheral instance, but the advertisement information such as localName is null because it has not been scanned.
*
* ```javascript
* // Javascript Example
*
* await obniz.ble.initWait();
* var peripheral = obniz.ble.directConnect("e4b9efb29218","random");
* peripheral.onconnect = ()=>{
* console.log("connected");
* }
* ```
*
* @param address peripheral device address
* @param addressType "random" or "public"
*
* @deprecated replaced by {@link #directConnectWait()}
*/
directConnect(address, addressType, connectionSetting) {
// noinspection JSIgnoredPromiseFromCall
this.directConnectWait(address, addressType, connectionSetting).catch((e) => {
// background
this.Obniz.error(e);
});
const peripheral = this.findPeripheral(address);
return peripheral;
}
/**
* Connect to peripheral without scanning, and wait to finish connecting.
*
* It throws when connection establish failed.
* Returns a peripheral instance, but the advertisement information such as localName is null because it has not been scanned.
*
* ```javascript
* // Javascript Example
* await obniz.ble.initWait();
* try {
* var peripheral = await obniz.ble.directConnectWait("e4b9efb29218","random");
* console.log("connected");
* } catch(e) {
* console.log("can't connect");
* }
* ```
*
* @param address peripheral device address
* @param addressType "random" or "public"
*/
async directConnectWait(address, addressType, connectionSetting) {
let peripheral = this.findPeripheral(address);
if (!peripheral) {
peripheral = new bleRemotePeripheral_1.BleRemotePeripheral(this, address);
this.remotePeripherals.push(peripheral);
}
this.centralBindings.addPeripheralData(address, addressType);
await peripheral.connectWait(connectionSetting);
return peripheral;
}
/**
* Return connected peripherals.
*
* ```javascript
* // Javascript Example
* await obniz.ble.initWait();
* let target = {
* localName: "Blank"
* };
* var peripheral = await obniz.ble.scan.startOneWait(target);
* if(peripheral) {
* try {
* await peripheral.connectWait();
* } catch(e) {
* console.error(e);
* }
* }
* console.log(obniz.ble.getConnectedPeripherals());
* ```
*
* @returns connected peripherals
*/
getConnectedPeripherals() {
const connectedPeripherals = [];
for (const elm of this.remotePeripherals) {
if (elm.connected) {
connectedPeripherals.push(elm);
}
}
return connectedPeripherals;
}
/**
* @ignore
*/
warningIfNotInitialize() {
if (this.Obniz.connectionState !== 'connected') {
throw new ObnizError_1.ObnizOfflineError();
}
if (!this._initialized && this._initializeWarning) {
this._initializeWarning = true;
this.Obniz.warning({
alert: 'warning',
message: `BLE is not initialized. Please call 'await obniz.ble.initWait()'`,
});
}
}
schemaBasePath() {
return 'ble';
}
onStateChange() {
// do nothing.
}
findPeripheral(address) {
for (const key in this.remotePeripherals) {
if (this.remotePeripherals[key].address === address) {
return this.remotePeripherals[key];
}
}
return null;
}
onDiscover(uuid, address, addressType, connectable, advertisement, rssi, primaryPhy, secondaryPhy) {
let val = this.findPeripheral(uuid);
if (!val) {
val = new bleRemotePeripheral_1.BleRemotePeripheral(this, uuid);
this.remotePeripherals.push(val);
}
val.discoverdOnRemote = true;
const peripheralData = {
device_type: 'ble',
address_type: addressType,
ble_event_type: connectable
? 'connectable_advertisemnt'
: 'non_connectable_advertising',
rssi,
adv_data: advertisement.advertisementRaw,
scan_resp: advertisement.scanResponseRaw,
service_data: advertisement.serviceData,
primary_phy: primaryPhy !== null && primaryPhy !== void 0 ? primaryPhy : null,
secondary_phy: secondaryPhy !== null && secondaryPhy !== void 0 ? secondaryPhy : null,
};
val.setParams(peripheralData);
val.setExtendFlg(this.hci._extended);
this.scan.notifyFromServer('onfind', val);
}
onDisconnect(peripheralUuid, reason) {
const peripheral = this.findPeripheral(peripheralUuid);
peripheral.notifyFromServer('statusupdate', {
status: 'disconnected',
reason,
});
}
//
// protected onServicesDiscover(peripheralUuid: any, serviceUuids?: any) {
// const peripheral: any = this.findPeripheral(peripheralUuid);
// for (const serviceUuid of serviceUuids) {
// peripheral.notifyFromServer("discover", { service_uuid: serviceUuid });
// }
// peripheral.notifyFromServer("discoverfinished", {});
// }
// protected onIncludedServicesDiscover(peripheralUuid: any, serviceUuid?: any, includedServiceUuids?: any) {}
// protected onCharacteristicsDiscover(peripheralUuid: any, serviceUuid?: any, characteristics?: any) {
// const peripheral: any = this.findPeripheral(peripheralUuid);
// const service: any = peripheral.findService({ service_uuid: serviceUuid });
// for (const char of characteristics) {
// const obj: any = {
// properties: char.properties.map((e: any) => BleHelper.toSnakeCase(e)),
// characteristic_uuid: char.uuid,
// };
// service.notifyFromServer("discover", obj);
// }
// service.notifyFromServer("discoverfinished", {});
// }
onNotification(peripheralUuid, serviceUuid, characteristicUuid, data, isNotification, isSuccess) {
const peripheral = this.findPeripheral(peripheralUuid);
const characteristic = peripheral.findCharacteristic({
service_uuid: serviceUuid,
characteristic_uuid: characteristicUuid,
});
if (isNotification) {
const obj = {
data: Array.from(data),
};
characteristic.notifyFromServer('onnotify', obj);
}
}
onPeripheralStateChange(state) {
// console.error("onPeripheralStateChange")
}
onPeripheralAccept(clientAddress) {
this.peripheral.currentConnectedDeviceAddress = clientAddress;
if (this.peripheral.onconnectionupdates) {
this.peripheral.onconnectionupdates({
address: clientAddress,
status: 'connected',
});
}
}
onPeripheralMtuChange(mtu) {
// console.error("onPeripheralMtuChange")
}
onPeripheralDisconnect(clientAddress, reason) {
this.peripheral.currentConnectedDeviceAddress = null;
if (this.peripheral.onconnectionupdates) {
this.peripheral.onconnectionupdates({
address: clientAddress,
status: 'disconnected',
reason,
});
}
}
debug(text) {
this.debugHandler(text);
}
phyToStr(phy) {
switch (phy) {
case 1:
return '1m';
case 2:
return '2m';
case 3:
return 'coded';
default:
throw new Error('decode Phy Error');
}
}
}
exports.ObnizBLE = ObnizBLE;