obniz
Version:
obniz sdk for javascript
390 lines (389 loc) • 17.3 kB
JavaScript
"use strict";
/**
* @packageDocumentation
*
* @ignore
*/
// var debug = require('debug')('bindings');
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NobleBindings = void 0;
const eventemitter3_1 = __importDefault(require("eventemitter3"));
const ObnizError_1 = require("../../../../../ObnizError");
const bleHelper_1 = __importDefault(require("../../bleHelper"));
const acl_stream_1 = require("./acl-stream");
const gap_1 = require("./gap");
const gatt_1 = require("./gatt");
const signaling_1 = require("./signaling");
/**
* @ignore
*/
class NobleBindings extends eventemitter3_1.default {
constructor(hciProtocol) {
super();
this._scanServiceUuids = null;
this.debugHandler = () => {
// do nothing.
};
this._hci = hciProtocol;
this._gap = new gap_1.Gap(this._hci);
this._state = null;
this._addresses = {};
this._addresseTypes = {};
this._connectable = {};
this._handles = {};
this._gatts = {};
this._aclStreams = {};
this._signalings = {};
this._connectPromises = [];
this._hci.on('stateChange', this.onStateChange.bind(this));
this._hci.on('disconnComplete', this.onDisconnComplete.bind(this));
this._hci.on('aclDataPkt', this.onAclDataPkt.bind(this));
this._hci.on('updatePhy', this.onPhy.bind(this));
this._gap.on('discover', this.onDiscover.bind(this));
}
/**
* @ignore
* @private
*/
_reset() {
this._state = null;
this._addresses = {};
this._addresseTypes = {};
this._connectable = {};
this._handles = {};
this._gatts = {};
this._aclStreams = {};
this._signalings = {};
this._gap._reset();
// TODO: It must be canceled.
this._connectPromises = [];
}
addPeripheralData(uuid, addressType) {
if (!this._addresses[uuid]) {
const address = bleHelper_1.default.addColon(uuid);
this._addresses[uuid] = address;
this._addresseTypes[uuid] = addressType;
this._connectable[uuid] = true;
}
}
async startExtendedScanningWait(serviceUuids, allowDuplicates, activeScan, usePhy1m, usePhyCoded) {
if (!usePhy1m && !usePhyCoded) {
throw new ObnizError_1.ObnizBleInvalidParameterError('Please make either true', `usePhy1M:${usePhy1m} usePhyCoded:${usePhyCoded}`);
}
this._scanServiceUuids = serviceUuids !== null && serviceUuids !== void 0 ? serviceUuids : null;
await this._gap.startExtendedScanningWait(allowDuplicates, activeScan, usePhy1m, usePhyCoded);
}
async startScanningWait(serviceUuids, allowDuplicates, activeScan) {
this._scanServiceUuids = serviceUuids !== null && serviceUuids !== void 0 ? serviceUuids : null;
await this._gap.startScanningWait(allowDuplicates, activeScan);
}
async stopScanningWait() {
await this._gap.stopScanningWait();
}
async stopExtendedScanningWait() {
await this._gap.stopExtendedScanningWait();
}
async connectWait(peripheralUuid, mtu, onConnectCallback) {
const address = this._addresses[peripheralUuid];
const addressType = this._addresseTypes[peripheralUuid];
if (!address) {
throw new ObnizError_1.ObnizBleUnknownPeripheralError(peripheralUuid);
}
// Block parall connection ongoing for ESP32 bug.
const doPromise = Promise.all(this._connectPromises)
.catch((error) => {
// nothing
})
.then(async () => {
const conResult = await this._hci.createLeConnWait(address, addressType, 90 * 1000, (result) => {
// on connect success
this.onLeConnComplete(result.status, result.handle, result.role, result.addressType, result.address, result.interval, result.latency, result.supervisionTimeout, result.masterClockAccuracy);
if (onConnectCallback && typeof onConnectCallback === 'function') {
onConnectCallback();
}
}); // connection timeout for 90 secs.
return await this._gatts[conResult.handle].exchangeMtuWait(mtu);
})
.then(() => {
this._connectPromises = this._connectPromises.filter((e) => e === doPromise);
return Promise.resolve();
}, (error) => {
this._connectPromises = this._connectPromises.filter((e) => e === doPromise);
return Promise.reject(error);
});
this._connectPromises.push(doPromise);
return doPromise;
}
async setDefaultPhyWait(usePhy1m, usePhy2m, usePhyCoded) {
if (!usePhy1m && !usePhyCoded && !usePhy2m) {
throw new ObnizError_1.ObnizBleInvalidParameterError('Please make either true', `usePhy1M:${usePhy1m} usePhy2M:${usePhy2m} usePhyCoded:${usePhyCoded}`);
}
const booleanToNumber = (flg) => (flg ? 1 : 0);
const setPhy = booleanToNumber(usePhy1m) +
booleanToNumber(usePhy2m) * 2 +
booleanToNumber(usePhyCoded) * 4;
await this._hci.leSetDefaultPhyCommandWait(0, setPhy, setPhy);
}
async readPhyWait(address) {
return await this._hci.leReadPhyCommandWait(this._handles[address]);
}
async setPhyWait(address, usePhy1m, usePhy2m, usePhyCoded, useCodedModeS8, useCodedModeS2) {
if (!usePhy1m && !usePhyCoded && !usePhy2m) {
throw new ObnizError_1.ObnizBleInvalidParameterError('Please make either true', `usePhy1M:${usePhy1m} usePhy2M:${usePhy2m} usePhyCoded:${usePhyCoded}`);
}
if (usePhyCoded && !useCodedModeS8 && !useCodedModeS2) {
throw new ObnizError_1.ObnizBleInvalidParameterError('Please make either true', `useCodedModeS8:${useCodedModeS8} useCodedModeS2:${useCodedModeS2}`);
}
const booleanToNumber = (flg) => (flg ? 1 : 0);
const setPhy = booleanToNumber(usePhy1m) +
booleanToNumber(usePhy2m) * 2 +
booleanToNumber(usePhyCoded) * 4;
await this._hci.leSetPhyCommandWait(this._handles[address], 0, setPhy, setPhy, booleanToNumber(useCodedModeS8) * 2 + booleanToNumber(useCodedModeS2));
}
onPhy(handler, txPhy, rxPhy) {
this.emit('updatePhy', handler, txPhy, rxPhy);
}
async connectExtendedWait(peripheralUuid, mtu, onConnectCallback, usePhy1m = true, usePhy2m = true, usePhyCoded = true) {
if (!usePhy1m && !usePhyCoded && !usePhy2m) {
throw new ObnizError_1.ObnizBleInvalidParameterError('Please make either true', `usePhy1M:${usePhy1m} usePhy2M:${usePhy2m} usePhyCoded:${usePhyCoded}`);
}
const address = this._addresses[peripheralUuid];
const addressType = this._addresseTypes[peripheralUuid];
if (!address) {
throw new ObnizError_1.ObnizBleUnknownPeripheralError(peripheralUuid);
}
// Block parall connection ongoing for ESP32 bug.
const doPromise = Promise.all(this._connectPromises)
.catch((error) => {
// nothing
})
.then(async () => {
const conResult = await this._hci.createLeExtendedConnWait(address, addressType, 90 * 1000, (result) => {
// on connect success
this.onLeConnComplete(result.status, result.handle, result.role, result.addressType, result.address, result.interval, result.latency, result.supervisionTimeout, result.masterClockAccuracy);
if (onConnectCallback && typeof onConnectCallback === 'function') {
onConnectCallback();
}
}, usePhy1m, usePhy2m, usePhyCoded); // connection timeout for 90 secs.
return await this._gatts[conResult.handle].exchangeMtuWait(mtu);
})
.then(() => {
this._connectPromises = this._connectPromises.filter((e) => e === doPromise);
return Promise.resolve();
}, (error) => {
this._connectPromises = this._connectPromises.filter((e) => e === doPromise);
return Promise.reject(error);
});
this._connectPromises.push(doPromise);
return doPromise;
}
disconnect(peripheralUuid) {
this._hci.disconnect(this._handles[peripheralUuid]);
}
async updateRssiWait(peripheralUuid) {
const rssi = await this._hci.readRssiWait(this._handles[peripheralUuid]);
return rssi;
}
onStateChange(state) {
if (this._state === state) {
return;
}
this._state = state;
this.emit('stateChange', state);
}
onDiscover(status, address, addressType, connectable, advertisement, rssi, primaryPhy, secondaryPhy) {
if (this._scanServiceUuids === null) {
// scan not started ?
return;
}
let serviceUuids = advertisement.serviceUuids || [];
const serviceData = advertisement.serviceData || [];
let hasScanServiceUuids = this._scanServiceUuids.length === 0;
if (!hasScanServiceUuids) {
serviceUuids = serviceUuids.slice();
for (const i in serviceData) {
serviceUuids.push(serviceData[i].uuid);
}
for (const i in serviceUuids) {
hasScanServiceUuids =
this._scanServiceUuids.indexOf(serviceUuids[i]) !== -1;
if (hasScanServiceUuids) {
break;
}
}
}
if (hasScanServiceUuids) {
const uuid = address.split(':').join('');
this._addresses[uuid] = address;
this._addresseTypes[uuid] = addressType;
this._connectable[uuid] = connectable;
this.emit('discover', uuid, address, addressType, connectable, advertisement, rssi, primaryPhy, secondaryPhy);
}
}
onLeConnComplete(status, handle, role, addressType, address, interval, latency, supervisionTimeout, masterClockAccuracy) {
if (role !== 0) {
// not master, ignore
return;
}
if (status !== 0) {
throw new ObnizError_1.ObnizBleHciStateError(status);
}
const uuid = address.split(':').join('').toLowerCase();
const aclStream = new acl_stream_1.AclStream(this._hci, handle, this._hci.addressType, this._hci.address, addressType, address);
aclStream.debugHandler = (text) => {
this.debug(text);
};
const gatt = new gatt_1.GattCentral(address, aclStream);
const signaling = new signaling_1.Signaling(handle, aclStream);
this._gatts[uuid] = this._gatts[handle] = gatt;
this._signalings[uuid] = this._signalings[handle] = signaling;
this._aclStreams[handle] = aclStream;
this._handles[uuid] = handle;
this._handles[handle] = uuid;
this._gatts[handle].on('notification', this.onNotification.bind(this));
this._gatts[handle].on('handleNotify', this.onHandleNotify.bind(this));
this._signalings[handle].on('connectionParameterUpdateRequest', this.onConnectionParameterUpdateWait.bind(this));
// public onMtu(address: any, mtu?: any) {}
}
onDisconnComplete(handle, reason) {
const uuid = this._handles[handle];
if (uuid) {
const error = new ObnizError_1.ObnizBleHciStateError(reason, {
peripheralAddress: uuid,
});
this._gatts[handle].onEnd(error);
this._gatts[handle].removeAllListeners();
this._signalings[handle].removeAllListeners();
delete this._gatts[uuid];
delete this._gatts[handle];
delete this._signalings[uuid];
delete this._signalings[handle];
delete this._aclStreams[handle];
delete this._handles[uuid];
delete this._handles[handle];
this.emit('disconnect', uuid, error); // TODO: handle reason?
}
else {
// maybe disconnect as peripheral
// console.warn(
// 'noble warning: unknown handle ' + handle + ' disconnected!'
// );
}
}
onAclDataPkt(handle, cid, data) {
const aclStream = this._aclStreams[handle];
if (aclStream) {
aclStream.push(cid, data);
}
}
async discoverServicesWait(peripheralUuid, uuids) {
const gatt = this.getGatt(peripheralUuid);
const services = await gatt.discoverServicesWait(uuids || []);
return services;
}
async discoverIncludedServicesWait(peripheralUuid, serviceUuid, serviceUuids) {
const gatt = this.getGatt(peripheralUuid);
const services = gatt.discoverIncludedServicesWait(serviceUuid, serviceUuids || []);
return services;
}
async discoverCharacteristicsWait(peripheralUuid, serviceUuid, characteristicUuids) {
const gatt = this.getGatt(peripheralUuid);
const chars = await gatt.discoverCharacteristicsWait(serviceUuid, characteristicUuids || []);
return chars;
}
async readWait(peripheralUuid, serviceUuid, characteristicUuid) {
const gatt = this.getGatt(peripheralUuid);
const data = await gatt.readWait(serviceUuid, characteristicUuid);
return data;
}
async writeWait(peripheralUuid, serviceUuid, characteristicUuid, data, withoutResponse) {
const gatt = this.getGatt(peripheralUuid);
await gatt.writeWait(serviceUuid, characteristicUuid, data, withoutResponse);
}
async broadcastWait(peripheralUuid, serviceUuid, characteristicUuid, broadcast) {
const gatt = this.getGatt(peripheralUuid);
await gatt.broadcastWait(serviceUuid, characteristicUuid, broadcast);
}
async notifyWait(peripheralUuid, serviceUuid, characteristicUuid, notify) {
const gatt = this.getGatt(peripheralUuid);
await gatt.notifyWait(serviceUuid, characteristicUuid, notify);
}
onNotification(address, serviceUuid, characteristicUuid, data) {
const uuid = address.split(':').join('').toLowerCase();
this.emit('notification', uuid, serviceUuid, characteristicUuid, data, true, true);
}
async discoverDescriptorsWait(peripheralUuid, serviceUuid, characteristicUuid) {
const gatt = this.getGatt(peripheralUuid);
return await gatt.discoverDescriptorsWait(serviceUuid, characteristicUuid);
}
async readValueWait(peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid) {
const gatt = this.getGatt(peripheralUuid);
return await gatt.readValueWait(serviceUuid, characteristicUuid, descriptorUuid);
}
async writeValueWait(peripheralUuid, serviceUuid, characteristicUuid, descriptorUuid, data) {
const gatt = this.getGatt(peripheralUuid);
await gatt.writeValueWait(serviceUuid, characteristicUuid, descriptorUuid, data);
}
async readHandleWait(peripheralUuid, attHandle) {
const gatt = this.getGatt(peripheralUuid);
const data = await gatt.readHandleWait(attHandle);
return data;
}
async writeHandleWait(peripheralUuid, attHandle, data, withoutResponse) {
const gatt = this.getGatt(peripheralUuid);
await gatt.writeHandleWait(attHandle, data, withoutResponse);
}
onHandleNotify(address, handle, data) {
const uuid = address.split(':').join('').toLowerCase();
this.emit('handleNotify', uuid, handle, data);
}
onConnectionParameterUpdateWait(handle, minInterval, maxInterval, latency, supervisionTimeout) {
this._hci
.connUpdateLeWait(handle, minInterval, maxInterval, latency, supervisionTimeout)
.then(() => {
// do nothing.
})
.catch((e) => {
// TODO:
// This must passed to Obniz class.
// console.error(e);
});
// this.onLeConnUpdateComplete(); is nop
}
async isPairingFinishedWait(peripheralUuid) {
const gatt = this.getGatt(peripheralUuid);
const result = gatt.hasEncryptKeys();
return result;
}
async getPairingKeysWait(peripheralUuid) {
const gatt = this.getGatt(peripheralUuid);
const result = gatt.getEncryptKeys();
return result;
}
async pairingWait(peripheralUuid, options) {
options = options || {};
const gatt = this.getGatt(peripheralUuid);
const result = await gatt.encryptWait(options);
return result;
}
setPairingOption(peripheralUuid, options) {
options = options || {};
const gatt = this.getGatt(peripheralUuid);
gatt.setEncryptOption(options);
}
getGatt(peripheralUuid) {
const handle = this._handles[peripheralUuid];
const gatt = this._gatts[handle];
if (!gatt) {
throw new ObnizError_1.ObnizBleUnknownPeripheralError(peripheralUuid);
}
return gatt;
}
debug(text) {
this.debugHandler(`${text}`);
}
}
exports.NobleBindings = NobleBindings;