gatt-ip-js
Version:
GATT-IP library is A simple javascript protocol to connect any Bluetooth Smart (GATT) Device to the IP network
286 lines (255 loc) • 10.8 kB
JavaScript
var C = require('./lib/constants').C;
var helper = require('./lib/message-helper');
var Peripheral = require('./peripheral').Peripheral;
var Stream = require('./stream').Stream;
var InternalError = require('./errors').InternalError;
var ApplicationError = require('./errors').ApplicationError;
var ee = require("./lib/event-emitter");
var GatewayError = require("./errors").GatewayError;
function Gateway(gattip, scanFilters) {
ee.instantiateEmitter(this);
var self = this;
var peripherals = {};
var objectsByObjectId = {};
this.isScanning = false;
this.isPoweredOn = function () {
return self.state == C.kPoweredOn;
};
// REQUESTS =================================================
this._authenticate = function (callback, token, version) {
var params = {};
params[C.kDeviceAccessToken] = token;
params[C.kGetVersionInfo] = version;
gattip.request(C.kOpen, params, callback, function (params) {
if (params && params.isAuthenticated === false) {
gattip.reject(callback, new GatewayError("Authentication failed"))
} else {
gattip.fulfill(callback, self);
}
});
};
this.scan = function (callback, scanOptions) {
var params = {};
if (scanOptions) {
if ('boolean' == typeof scanOptions.scanDuplicates) {
params[C.kScanOptionAllowDuplicatesKey] = scanOptions.scanDuplicates;
}
if ('object' == typeof scanOptions.services) {
params[C.kServiceUUIDs] = scanOptions.services;
}
}
gattip.request(C.kScanForPeripherals, params, callback, function (params) {
self.isScanning = true;
gattip.fulfill(callback, self);
});
};
// TODO: Unregister all on scan event handlers
this.stopScan = function (callback) {
gattip.request(C.kStopScanning, {}, callback, function (params) {
self.isScanning = false;
gattip.fulfill(callback, self);
});
};
this.centralState = function (callback) {
var params = {};
gattip.request(C.kCentralState, {}, callback, function (params) {
self.state = params[C.kState];
gattip.fulfill(callback, self);
});
};
this.configure = function (callback, pwrAlert, centralID) {
var params = {};
if (typeof pwrAlert != 'undefined') {
params[C.kShowPowerAlert] = pwrAlert;
}
if (typeof centralID != 'undefined') {
params[C.kIdentifierKey] = centralID;
}
gattip.request(C.kConfigure, {}, callback, function (params) {
gattip.fulfill(callback, self);
});
};
this.openStream = function (callback, streamPath, options) {
var params = {};
if (!options) {
options = {};
}
if (typeof options.speed == 'number') {
params[C.kSpeed] = options.speed;
}
if (typeof options.force == 'boolean') {
params[C.kForce] = options.force;
}
var streamObjectId = params[C.kObjectId] = streamPath;
helper.requireFields('objectId', params, [C.kObjectId]);
gattip.request(C.kOpenStream, params, callback, function (params) {
if (typeof params[C.kObjectId] == "string") {
// if the response contains a new object ID, replace it
streamObjectId = params[C.kObjectId];
}
var stream = new Stream(this, streamObjectId);
objectsByObjectId[streamObjectId] = stream;
gattip.fulfill(callback, stream);
});
};
//
this.closeStream = function (callback, objectId) {
var params = {};
var streamObjectId = params[C.kObjectId] = objectId;
helper.requireFields('objectId', params, [C.kObjectId]);
gattip.request(C.kCloseStream, params, callback, function (params) {
var stream = objectsByObjectId[streamObjectId];
delete objectsByObjectId[streamObjectId];
gattip.fulfill(callback, stream);
});
};
//
this.handleScanIndication = function(params) {
var peripheralUUID = params[C.kPeripheralUUID];
if (!peripheralUUID) {
throw new InternalError('Peripheral UUID is not availabvle');
}
if (scanFilters && scanFilters.uuids) {
for (var i = 0; i < scanFilters.uuids.length; i++) {
var uuid = scanFilters.uuids[i];
if (uuid && uuid.length) {
if (uuid != peripheralUUID) {
return;
}
}
}
}
var peripheral = self.getPeripheral(peripheralUUID);
if (!peripheral) {
peripheral = self.addPeripheral(new Peripheral(
gattip,
peripheralUUID,
params[C.kPeripheralName],
params[C.kRSSIkey],
params[C.kCBAdvertisementDataTxPowerLevel],
params[C.kCBAdvertisementDataIsConnectable],
params[C.kCBAdvertisementDataServiceUUIDsKey],
params[C.kCBAdvertisementDataManufacturerDataKey],
params[C.kCBAdvertisementDataServiceDataKey],
params[C.kAdvertisementDataKey],
params[C.kScanRecord])
);
} else {
peripheral._updateFromScanData(
params[C.kPeripheralName],
params[C.kRSSIkey],
params[C.kCBAdvertisementDataTxPowerLevel],
params[C.kCBAdvertisementDataIsConnectable],
params[C.kCBAdvertisementDataServiceUUIDsKey],
params[C.kCBAdvertisementDataManufacturerDataKey],
params[C.kCBAdvertisementDataServiceDataKey],
params[C.kAdvertisementDataKey],
params[C.kScanRecord]
);
}
self.emit('scan', peripheral);
};
// PERIPHERAL MANAGEMENT ETC. ======================================
this.addPeripheralWithValues = function (uuid, name, RSSI, txPwr, serviceUUIDs, mfrData, scvData, connectable) {
if (!uuid) {
throw new InternalError('Attempting to add an empty peripheral');
}
var peripheral = self.addPeripheral(new Peripheral(gattip, uuid, name, RSSI, txPwr, connectable, serviceUUIDs, mfrData, scvData));
peripherals[uuid] = peripheral;
return peripheral;
};
this.addPeripheral = function (peripheral) {
if (!peripheral || !peripheral.uuid) {
throw new InternalError('Attempting to add an empty peripheral');
}
peripherals[peripheral.uuid] = peripheral;
return peripheral;
};
this.addStream = function (objectId) {
var stream =new Stream(this, objectId);
objectsByObjectId[objectId] = stream;
return stream;
};
this.removePeripheral = function (peripheral) {
if (!peripheral || !peripheral.uuid) {
throw new InternalError('Attempting to remove an empty peripheral');
}
delete peripherals[peripheral.uuid];
};
this.getPeripheral = function (peripheralUUID) {
return peripherals[peripheralUUID];
};
this.getObject = function (objectId) {
return objectsByObjectId[objectId];
};
this.removeObject = function (objectId) {
delete objectsByObjectId[objectId];
};
this.getPeripheralOrDie = function (peripheralUUID) {
var peripheral = peripherals[peripheralUUID];
if (!peripheral) {
throw new InternalError('Unable to find peripheral with UUID ' + peripheralUUID);
}
return peripheral;
};
this.getObjects = function(type, peripheralUUID, serviceUUID, characteristicUUID, descriptorUUID) {
var resultObj = {};
resultObj.peripheral = peripherals[peripheralUUID];
if (resultObj.peripheral) {
if (type === 'p') {
return resultObj;
}
resultObj.service = resultObj.peripheral.findService(serviceUUID);
if (resultObj.service) {
if (type === 's') {
return resultObj;
}
resultObj.characteristic = resultObj.service.findCharacteristic(characteristicUUID);
if (resultObj.characteristic) {
if (type === 'c') {
return resultObj;
}
resultObj.descriptor = resultObj.characteristic.findDescriptor(descriptorUUID);
if (resultObj.descriptor) {
if (type === 'd') {
return resultObj;
} else {
throw new InternalError('_getObjects: Argument "type" is required');
}
} else {
throw new ApplicationError('Descriptor "'+ descriptorUUID + '" not found in the service table');
}
} else {
throw new ApplicationError('Characteristic "'+ characteristicUUID + '" not found in the service table');
}
} else {
throw new ApplicationError('Service "'+ serviceUUID + '" not found in the service table');
}
} else {
throw new ApplicationError('Peripheral with id '+ peripheralUUID + ' not found');
}
};
this.getObjectsFromMessage = function(type, params) {
if (!params) {
throw new InternalError("Message parameters are missing");
}
try {
return self.getObjects(type, params[C.kPeripheralUUID], params[C.kServiceUUID], params[C.kCharacteristicUUID], params[C.kDescriptorUUID] );
} catch (error) {
throw new InternalError(error.message, error.detail);
}
};
this.close = function () {
for (var pIdx in Object.keys(peripherals)) {
if (peripherals.hasOwnProperty(pIdx)) {
var p = peripherals[pIdx];
p.removeAllListeners();
p.removeAllChildListenersAndFlush();
}
}
peripherals = {};
self.removeAllListeners();
};
}
ee.makeEmitter(Gateway);
module.exports.Gateway = Gateway;