-
-
Save robwormald/cc93a6c74f1c837b4d98 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('FITapp.deviceManager', []) | |
.controller('DeviceManagerController',function($scope,FITBluetoothLEManager){ | |
$scope.deviceManager = FITBluetoothLEManager; | |
}) | |
.controller('DeviceController',function($scope,$state,FITBluetoothLEManager,$ionicSideMenuDelegate){ | |
$scope.fitDevice = FITBluetoothLEManager.devices.find(function(dvice){ | |
return dvice.address == $state.params.deviceAddress; | |
}); | |
$scope.leftSideMenu = $ionicSideMenuDelegate | |
$scope.leftSideMenu.canDragContent(false) | |
}) | |
.factory('FITError',function($timeout){ | |
function FITError(message) { | |
this.name = "FITError"; | |
this.message = message || "Unknown FIT Error!"; | |
} | |
FITError.prototype = new Error(); | |
FITError.prototype.constructor = MyError; | |
return FITError; | |
}) | |
.factory('FITBluetoothDevice',function($q,$timeout,FITBluetoothProfiles,FITKeyPressSensor,FITAccelerometer){ | |
function FITBluetoothDevice(manager,discoveryData){ | |
Object.defineProperty(this,'manager',{ | |
enumerable: false, | |
value: manager | |
}) | |
this.address = discoveryData.address; | |
this.deviceName = discoveryData.name; | |
this.signalStrength = discoveryData.rssi; | |
this.connected = false; | |
this.connecting = false; | |
this.Profiles = { | |
KeyService: new FITKeyPressSensor(this,FITBluetoothProfiles.SIMPLEKEY_UUID_SERVICE,FITBluetoothProfiles.SIMPLEKEY_UUID_DATA), | |
Accelerometer: new FITAccelerometer(this,FITBluetoothProfiles.ACCELEROMETER_UUID_SERVICE,FITBluetoothProfiles.ACCELEROMETER_UUID_DATA,FITBluetoothProfiles.ACCELEROMETER_UUID_CONF,FITBluetoothProfiles.ACCELEROMETER_UUID_PERIOD) | |
}; | |
} | |
FITBluetoothDevice.prototype.connect = function(){ | |
this.connecting = true; | |
var self = this; | |
return self.manager.connectToDevice(self.address).then(function(connectionInfo){ | |
$timeout(function(){ | |
self.onConnect(connectionInfo); | |
},0) | |
},self.onConnectFailure,self.onConnectionStateChange); | |
} | |
FITBluetoothDevice.prototype.readRSSI = function(){ | |
var self = this; | |
return self.manager.readRSSI(self).then(function(rssi){ | |
return rssi; | |
}); | |
} | |
FITBluetoothDevice.prototype.init = function(){ | |
var self = this; | |
for(var profileKey in self.Profiles){ | |
console.log(profileKey) | |
var profile = self.Profiles[profileKey]; | |
var service = self.services.find(function(svc){ | |
console.log(svc) | |
if(typeof profile.UUID_SERVICE.test != 'undefined'){ | |
return profile.UUID_SERVICE.test(svc.uuid); | |
} | |
return svc.uuid == profile.UUID_SERVICE; | |
}) | |
if(service){ | |
profile.init(service); | |
} | |
} | |
self.initialized = true; | |
} | |
FITBluetoothDevice.prototype.readServices = function(){ | |
var self = this; | |
return self.manager.readServicesFromDevice(self).then(function(services){ | |
var serviceChars = $q.all(services.map(function(svcInfo){ | |
return self.manager.readServiceCharacteristics(self,svcInfo).then(function(chars){ | |
svcInfo.characteristics = chars; | |
return $q.all(svcInfo.characteristics.map(function(char){ | |
return self.manager.readCharacteristicDescriptors(self,svcInfo,char).then(function(charDetails){ | |
char.descriptors = charDetails; | |
return char; | |
}); | |
})).then(function(info){ | |
return services; | |
}) | |
}) | |
})); | |
return serviceChars.then(function(){ | |
self.services = services; | |
self.init(); | |
console.log('got all device service info',self); | |
return services; | |
}) | |
}); | |
} | |
FITBluetoothDevice.prototype.updateServices = function(discoveredServices){ | |
var self = this; | |
var svcs = discoveredServices.map(function(serviceInfo){ | |
return self.readService(serviceInfo.handle); | |
}) | |
this.services = svcs; | |
} | |
FITBluetoothDevice.prototype.readService = function(serviceHandle){ | |
var self = this; | |
} | |
FITBluetoothDevice.prototype.onConnect = function(connectionInfo){ | |
console.log('BLE :: successfully connected',connectionInfo); | |
this.connected = true; | |
this.connecting = false; | |
this.handle = connectionInfo.deviceHandle; | |
return connectionInfo; | |
} | |
FITBluetoothDevice.prototype.onConnectFailure = function(connectionInfo){ | |
console.log('BLE :: connection failure',connectionInfo); | |
this.error = connectionInfo; | |
this.connecting = false; | |
this.connected = false; | |
} | |
FITBluetoothDevice.prototype.onConnectionStateChange = function(connectionInfo){ | |
console.log('BLE :: connection state changed',connectionInfo); | |
} | |
return FITBluetoothDevice; | |
}) | |
.factory('FITKeyPressSensor',function($q,$timeout,FITBluetoothProfiles,FITBluetoothBaseProfile){ | |
var SimpleKey = function (fitDevice) { | |
FITBluetoothBaseProfile.call(this, | |
"SimpleKey", | |
fitDevice, | |
FITBluetoothProfiles.SIMPLEKEY_UUID_SERVICE, | |
FITBluetoothProfiles.SIMPLEKEY_UUID_DATA); | |
}; | |
SimpleKey.Keys = { | |
RIGHT: 0x01, | |
LEFT: 0x02, | |
CENTER: 0x04 | |
}; | |
SimpleKey.prototype = new FITBluetoothBaseProfile(); | |
SimpleKey.prototype.constructor = SimpleKey; | |
// Remove unsupported prototype functions | |
delete SimpleKey.prototype.enable; | |
delete SimpleKey.prototype.disable; | |
return SimpleKey; | |
}) | |
.constant('FITBluetoothProfiles',{ | |
GATT_CLIENT_CHAR_CFG_UUID: /^(0000)?2902(-0000-1000-8000-00805f9b34fb)?$/, | |
ACCELEROMETER_UUID_SERVICE: "f000aa10-0451-4000-b000-000000000000", | |
ACCELEROMETER_UUID_DATA: /^f000aa11-0451-4000-b000-000000000000$/, | |
ACCELEROMETER_UUID_CONF: "f000aa12-0451-4000-b000-000000000000", | |
ACCELEROMETER_UUID_PERIOD: "f000aa13-0451-4000-b000-000000000000", | |
BAROMETRICPRESSURE_UUID_SERVICE: "f000aa40-0451-4000-b000-000000000000", | |
BAROMETRICPRESSURE_UUID_DATA: /^f000aa41-0451-4000-b000-000000000000$/, | |
BAROMETRICPRESSURE_UUID_CONF: "f000aa42-0451-4000-b000-000000000000", | |
BAROMETRICPRESSURE_UUID_PERIOD: "f000aa44-0451-4000-b000-000000000000", | |
BAROMETRICPRESSURE_UUID_CALIBRATION: /^f000aa43-0451-4000-b000-000000000000$/, | |
GYROSCOPE_UUID_SERVICE: "f000aa50-0451-4000-b000-000000000000", | |
GYROSCOPE_UUID_DATA: /^f000aa51-0451-4000-b000-000000000000$/, | |
GYROSCOPE_UUID_CONF: "f000aa52-0451-4000-b000-000000000000", | |
GYROSCOPE_UUID_PERIOD: "f000aa53-0451-4000-b000-000000000000", | |
HUMIDITY_UUID_SERVICE: "f000aa20-0451-4000-b000-000000000000", | |
HUMIDITY_UUID_DATA: /^f000aa21-0451-4000-b000-000000000000$/, | |
HUMIDITY_UUID_CONF: "f000aa22-0451-4000-b000-000000000000", | |
HUMIDITY_UUID_PERIOD: "f000aa23-0451-4000-b000-000000000000", | |
IRTEMPERATURE_UUID_SERVICE: "f000aa00-0451-4000-b000-000000000000", | |
IRTEMPERATURE_UUID_DATA: /^f000aa01-0451-4000-b000-000000000000$/, | |
IRTEMPERATURE_UUID_CONF: "f000aa02-0451-4000-b000-000000000000", | |
IRTEMPERATURE_UUID_PERIOD: "f000aa03-0451-4000-b000-000000000000", | |
MAGNETOMETER_UUID_SERVICE: "f000aa30-0451-4000-b000-000000000000", | |
MAGNETOMETER_UUID_DATA: /^f000aa31-0451-4000-b000-000000000000$/, | |
MAGNETOMETER_UUID_CONF: "f000aa32-0451-4000-b000-000000000000", | |
MAGNETOMETER_UUID_PERIOD: "f000aa33-0451-4000-b000-000000000000", | |
SIMPLEKEY_UUID_SERVICE: /^(0000)?ffe0(-0000-1000-8000-00805f9b34fb)?$/, | |
SIMPLEKEY_UUID_DATA: /^(0000)?ffe1(-0000-1000-8000-00805f9b34fb)?$/, | |
GUID_PATTERN: /([a-f0-9]{8})-?([a-f0-9]{4})-?([a-f0-9]{4})-?([a-f0-9]{4})-?([a-f0-9]{12})/, | |
GUID_REPLACEMENT: '$1-$2-$3-$4-$5' | |
}) | |
.factory('FITBluetoothBaseProfile',['FITBluetoothProfiles','$timeout','$q',function(Constants,$timeout,$q){ | |
/** | |
* The base class representing each sensor | |
* @param {string} name The name of the sensor | |
* @param {FITBluetoothDevice} fitDevice The fitDevice object this sensor belongs to | |
* @param {uuid} UUID_DATA The UUID of the data characteristic | |
* @param {uuid} UUID_CONF The UUID of the configuration characteristic | |
* @param {uuid} UUID_PERIOD The UUID of the period characteristic | |
*/ | |
var FITBluetoothBaseProfile = function (name, fitDevice, UUID_SERVICE, UUID_DATA, UUID_CONF, UUID_PERIOD) { | |
this.identifier = name; | |
this.device = fitDevice; | |
this.UUID_SERVICE = UUID_SERVICE; | |
this.UUID_DATA = UUID_DATA; | |
this.UUID_CONF = UUID_CONF; | |
this.UUID_PERIOD = UUID_PERIOD; | |
this.enabled = false; | |
this.characteristics = { | |
config: null, | |
period: null, | |
data: null, | |
}; | |
this.descriptors = { | |
notification: null | |
}; | |
this._listeners = []; | |
}; | |
/** | |
* Initializes the sensor with the FITBluetoothBaseProfile service information | |
* @param {service} service The SensorTag service object | |
*/ | |
FITBluetoothBaseProfile.prototype.init = function (service) { | |
for (var ci in service.characteristics) { | |
var characteristic = service.characteristics[ci], | |
cGuid = characteristic.uuid.replace(Constants.GUID_PATTERN, Constants.GUID_REPLACEMENT); | |
switch (cGuid) { | |
case this.UUID_CONF: | |
this.characteristics.config = characteristic; | |
break; | |
case this.UUID_PERIOD: | |
this.characteristics.period = characteristic; | |
break; | |
} | |
if(this.UUID_DATA.test(cGuid)) { | |
this.characteristics.data = characteristic; | |
for (var di in characteristic.descriptors) { | |
var descriptor = characteristic.descriptors[di], | |
dGuid = descriptor.uuid.replace(Constants.GUID_PATTERN, Constants.GUID_REPLACEMENT); | |
if (Constants.GATT_CLIENT_CHAR_CFG_UUID.test(dGuid)) { | |
this.descriptors.notification = descriptor; | |
} | |
} | |
} | |
} | |
$timeout(function(){ | |
this.initialized = true; | |
},0) | |
console.log("Initialized"); | |
}; | |
/** | |
* Logs a message on behalf of the sensor | |
* @param {string} message The message to log | |
*/ | |
FITBluetoothBaseProfile.prototype.log = function (message) { | |
this.log(this.identifier + ': ' + message); | |
}; | |
/** | |
* Enables the sensor | |
* @param {Integer} [value] The value to initialize the sensor with | |
*/ | |
FITBluetoothBaseProfile.prototype.enable = function (value) { | |
var ON = 1, | |
self = this; | |
return self.device.manager.writeCharacteristic( | |
self.device, | |
self.characteristics.config, | |
new Uint8Array([value || ON]), | |
function () { | |
self.enabled = true; | |
self.log("Enabled sensor..."); | |
} | |
) | |
}; | |
/** | |
* Disables the sensor | |
*/ | |
FITBluetoothBaseProfile.prototype.disable = function (value) { | |
var OFF = 0, | |
self = this; | |
self.device.manager.writeCharacteristic( | |
self.characteristics.config, | |
new Uint8Array([OFF]), | |
function () { | |
self.enabled = false; | |
self.log("Disabled"); | |
}, | |
function (errorCode) { | |
self.log("disable error: " + errorCode); | |
} | |
); | |
}; | |
/** | |
* Toggle sensor status | |
*/ | |
FITBluetoothBaseProfile.prototype.toggleStatus = function (newVal) { | |
self = this | |
console.log(newVal) | |
if(newVal == true){ | |
self.enableNotification() | |
self.enable(newVal) | |
} | |
else if(newVal == false){ | |
self.disableNotification() | |
self.disable(newVal) | |
} | |
}; | |
/** | |
* Enables notifications for the sensor | |
*/ | |
FITBluetoothBaseProfile.prototype.enableNotification = function () { | |
var ENABLE_NOTIFICATIONS = [1, 0], | |
self = this; | |
function writeEnableDescriptor(){ | |
if(!self.descriptors.notification){ | |
return $q.when(true); | |
} | |
return self.device.manager.writeDescriptor( | |
self.device, | |
self.descriptors.notification, | |
new Uint8Array(ENABLE_NOTIFICATIONS) | |
).then(function(){ | |
return true; | |
console.log('wrote notification enable success') | |
},function(){ | |
console.log('wrote notification enable error') | |
}) | |
} | |
writeEnableDescriptor().then(function(ready){ | |
return self.device.manager.enableNotification( | |
self.device, | |
self.characteristics.data, | |
function() { | |
self.onDataNotify.apply(self, arguments); | |
}).then(function(){ | |
console.log('notifications enabled'); | |
return true; | |
},function(err){ | |
console.log('error subscribing to notifications'); | |
return false; | |
}) | |
}) | |
}; | |
/** | |
* Disables notifications for the sensor | |
*/ | |
FITBluetoothBaseProfile.prototype.disableNotification = function () { | |
var DISABLE_NOTIFICATIONS = [0, 0], | |
self = this; | |
function writeDisableDescriptor(){ | |
if(!self.descriptors.notification){ | |
return $q.when(true); | |
} | |
return self.device.manager.writeDescriptor( | |
self.device, | |
self.descriptors.notification, | |
new Uint8Array(DISABLE_NOTIFICATIONS), | |
function () { | |
self.log("Notifications disabled"); | |
}, | |
function (errorCode) { | |
self.log("disableNotification write error: " + errorCode); | |
} | |
); | |
} | |
writeDisableDescriptor().then(function(ready){ | |
return self.device.manager.disableNotification( | |
self.device, | |
self.characteristics.data, | |
function() { | |
// Do nothing | |
}, | |
function (errorCode) { | |
self.log("disableNotification subscribe error: " + errorCode); | |
} | |
); | |
}) | |
}; | |
/** | |
* Adds a notification listener | |
* @param {callback} callback The callback to receive updates from the sensor | |
* @returns {handle} The callback handle | |
*/ | |
FITBluetoothBaseProfile.prototype.addListener = function (callback) { | |
this._listeners.push(callback); | |
// Return a handle | |
return this._listeners.length - 1; | |
}; | |
/** | |
* Removes a notification listener | |
* @param {handle} handle The handle for the callback | |
*/ | |
FITBluetoothBaseProfile.prototype.removeListener = function (handle) { | |
this._listeners.splice(handle, 1); | |
}; | |
/** | |
* Notifies all the listeners | |
*/ | |
FITBluetoothBaseProfile.prototype.onDataNotify = function () { | |
console.log('data:',arguments); | |
for (var li in this._listeners) { | |
var listener = this._listeners[li]; | |
listener.apply(this, arguments); | |
} | |
}; | |
return FITBluetoothBaseProfile; | |
}]) | |
.factory('FITBluetoothLEManager',function($timeout,$q,$cacheFactory,$log,FITBluetoothDevice){ | |
var exec = cordova.require('cordova/exec'); | |
var connectionState = { | |
'STATE_DISCONNECTED': 0, | |
'STATE_CONNECTING': 1, | |
'STATE_CONNECTED': 2, | |
'STATE_DISCONNECTING': 4 | |
}; | |
function FITBluetoothLEManager(){ | |
this.devices = []; | |
this.deviceCache = $cacheFactory('FIT_DEVICE_CACHE'); | |
} | |
FITBluetoothLEManager.prototype.startScanning = function(){ | |
var self = this; | |
self.scanning = true; | |
console.log('starting scan for devices...'); | |
exec(function(deviceInfo){ | |
$timeout(function(){ | |
self.didDiscoverDevice(deviceInfo); | |
},0); | |
},function(err){ | |
$timeout(function(){ | |
self.scanDidFailWithError(err); | |
},0); | |
},'BLE','startScan',[]); | |
} | |
FITBluetoothLEManager.prototype.stopScanning = function(){ | |
this.scanning = false; | |
exec(null,null,'BLE','stopScan',[]); | |
} | |
FITBluetoothLEManager.prototype.scanForDevices = function(options){ | |
var self = this | |
self.startScanning() | |
$timeout(function(){ | |
self.stopScanning() | |
},2000) | |
} | |
FITBluetoothLEManager.prototype.scanForDevice = function(deviceAddress,options){ | |
} | |
FITBluetoothLEManager.prototype.didDiscoverDevice = function(deviceInfo){ | |
var self = this; | |
console.log('discovered device ',deviceInfo); | |
var deviceInstance = self.deviceCache.get(deviceInfo.address); | |
if(!deviceInstance){ | |
deviceInstance = new FITBluetoothDevice(self,deviceInfo); | |
self.deviceCache.put(deviceInfo.address,deviceInstance); | |
} | |
if(!self.devices.find(function(dvc){ | |
return dvc.address == deviceInstance.address; | |
})){ | |
self.devices.push(deviceInstance); | |
} | |
} | |
FITBluetoothLEManager.prototype.scanDidFailWithError = function(deviceError){ | |
this.scanning = false; | |
} | |
FITBluetoothLEManager.prototype.readRSSI = function(btDevice){ | |
var qPing = $q.defer(); | |
exec(function(ping){ | |
$timeout(function(){ | |
return qPing.resolve(ping); | |
},0) | |
},function(err){ | |
qPing.reject(err); | |
},'BLE','rssi',[btDevice.handle]); | |
return qPing.promise; | |
} | |
FITBluetoothLEManager.prototype.readServicesFromDevice = function(btDevice){ | |
var self = this; | |
var qServices = $q.defer(); | |
exec(function(discoveredServices){ | |
console.log('services:',discoveredServices); | |
$timeout(function(){ | |
self.services = discoveredServices; | |
qServices.resolve(discoveredServices); | |
},0) | |
},function(err){ | |
console.log(err); | |
$timeout(function(){ | |
qServices.reject(err); | |
},0) | |
},'BLE','services',[btDevice.handle]); | |
return qServices.promise; | |
} | |
FITBluetoothLEManager.prototype.readServiceCharacteristics = function(btDevice,btDeviceService){ | |
var self = this; | |
var qServiceChars = $q.defer(); | |
exec(function(discoveredCharacteristics){ | |
console.log('characteristics:',discoveredCharacteristics); | |
$timeout(function(){ | |
btDeviceService.characteristics = discoveredCharacteristics; | |
qServiceChars.resolve(btDeviceService.characteristics); | |
},0) | |
},function(err){ | |
console.log(err); | |
$timeout(function(){ | |
qServiceChars.reject(err); | |
},0) | |
},'BLE','characteristics',[btDevice.handle,btDeviceService.handle]); | |
return qServiceChars.promise; | |
} | |
FITBluetoothLEManager.prototype.readCharacteristicDescriptors = function(btDevice,btDeviceService,btDeviceServiceCharacteristic){ | |
var self = this; | |
var qServiceCharDescriptors = $q.defer(); | |
exec(function(discoveredDescriptors){ | |
console.log('descriptors:',discoveredDescriptors); | |
$timeout(function(){ | |
btDeviceServiceCharacteristic.descriptors = discoveredDescriptors; | |
qServiceCharDescriptors.resolve(btDeviceServiceCharacteristic.descriptors); | |
},0) | |
},function(err){ | |
console.log(err); | |
$timeout(function(){ | |
qServiceCharDescriptors.reject(err); | |
},0) | |
},'BLE','characteristics',[btDevice.handle,btDeviceService.handle,btDeviceServiceCharacteristic.handle]); | |
return qServiceCharDescriptors.promise; | |
} | |
FITBluetoothLEManager.prototype.writeDescriptor = function(btDevice,btDeviceServiceDescriptor,data){ | |
var self = this; | |
var qWriteResult = $q.defer(); | |
exec(function(writeSuccess){ | |
console.log('write success:',writeSuccess); | |
$timeout(function(){ | |
qWriteResult.resolve(writeSuccess); | |
},0) | |
},function(err){ | |
console.log(err); | |
$timeout(function(){ | |
qWriteResult.reject(err); | |
},0) | |
},'BLE','writeDescriptor',[btDevice.handle,btDeviceServiceDescriptor.handle,data.buffer]); | |
return qWriteResult.promise; | |
} | |
FITBluetoothLEManager.prototype.writeCharacteristic = function(btDevice,btDeviceCharacteristic,data){ | |
var self = this; | |
var qWriteResult = $q.defer(); | |
exec(function(writeSuccess){ | |
console.log('write success:',writeSuccess); | |
$timeout(function(){ | |
qWriteResult.resolve(writeSuccess); | |
},0) | |
},function(err){ | |
console.log(err); | |
$timeout(function(){ | |
qWriteResult.reject(err); | |
},0) | |
},'BLE','writeCharacteristic',[btDevice.handle,btDeviceCharacteristic.handle,data.buffer]); | |
return qWriteResult.promise; | |
} | |
FITBluetoothLEManager.prototype.enableNotification = function(btDevice,btDeviceCharacteristic,listener){ | |
var self = this; | |
var timer = $timeout(function(){ | |
return true; | |
},3000); | |
exec(function(data){ | |
listener.call(this,data); | |
},function(err){ | |
console.log(err); | |
},'BLE','enableNotification',[btDevice.handle,btDeviceCharacteristic.handle]); | |
return timer; | |
} | |
FITBluetoothLEManager.prototype.connectToDevice = function(deviceAddress){ | |
this.stopScanning(); | |
var connection = $q.defer(); | |
exec(function(connectionEvent){ | |
if(connectionEvent.state == connectionState.STATE_CONNECTING){ | |
return connection.notify(connectionEvent); | |
}; | |
if(connectionEvent.state == connectionState.STATE_CONNECTED){ | |
return connection.resolve(connectionEvent); | |
} | |
},function(err){ | |
connection.reject(err); | |
},'BLE','connect',[deviceAddress]); | |
return connection.promise; | |
} | |
var manager = new FITBluetoothLEManager(); | |
return manager; | |
}) | |
.factory('FITDeviceCache',function(){ | |
}) | |
.factory('FITDevices',function(FitDeviceConfig){ | |
function FITDeviceManager(){ | |
} | |
FITDeviceManager.scanForDevices = function(){ | |
} | |
return FITDeviceManager; | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment