├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── central-manager-delegate.js ├── central-manager.js ├── characteristic.js ├── descriptor.js ├── local-address.js ├── mutable-characteristic.js ├── mutable-descriptor.js ├── mutable-service.js ├── peripheral-delegate.js ├── peripheral-manager-delegate.js ├── peripheral-manager.js ├── peripheral.js ├── service.js ├── util.js └── uuid-to-address.js ├── package.json ├── test-central.js └── test-peripheral.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sandeep Mistry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-core-bluetooth 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CentralManager: require('./lib/central-manager'), 3 | PeripheralManager: require('./lib/peripheral-manager'), 4 | MutableService: require('./lib/mutable-service'), 5 | MutableCharacteristic: require('./lib/mutable-characteristic'), 6 | MutableDescriptor: require('./lib/mutable-descriptor') 7 | }; 8 | -------------------------------------------------------------------------------- /lib/central-manager-delegate.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | var debug = require('debug')('central-manager-delegate'); 6 | 7 | var Peripheral = require('./peripheral'); 8 | var $util = require('./util'); 9 | 10 | function CentralManagerDelegate() { 11 | this.$ = CBCentralManagerDelegate('alloc')('init'); 12 | 13 | delegates[this.$] = this; 14 | 15 | this._peripheral = {}; 16 | } 17 | 18 | util.inherits(CentralManagerDelegate, events.EventEmitter); 19 | 20 | module.exports = CentralManagerDelegate; 21 | 22 | var delegates = {}; 23 | var peripherals = {}; 24 | 25 | function mapDelegate(self) { 26 | return delegates[self]; 27 | } 28 | 29 | function mapPeripheral(identifier) { 30 | return peripherals[identifier]; 31 | } 32 | 33 | var CBCentralManagerDelegate = $.NSObject.extend('CBCentralManagerDelegate'); 34 | 35 | CBCentralManagerDelegate.addMethod('centralManagerDidUpdateState:', 'v@:@', function ($self, $_cmd, $centralManager) { 36 | var state = $centralManager('state'); 37 | debug('centralManagerDidUpdateState:%d', state); 38 | 39 | process.nextTick(function(state) { 40 | var STATE_MAPPER = ['unknown', 'resetting', 'unsupported', 'unauthorized', 'poweredOff', 'poweredOn']; 41 | 42 | this.emit('stateUpdate', STATE_MAPPER[state]); 43 | }.bind(mapDelegate($self), state)); 44 | }); 45 | 46 | CBCentralManagerDelegate.addMethod('centralManager:didDiscoverPeripheral:advertisementData:RSSI:', 47 | 'v@:@@@@', function ($self, $_cmd, $centralManager, $peripheral, $advertisementData, $RSSI) { 48 | var identifier = $util.identifierFor$Peripheral($peripheral); 49 | 50 | var $localName = $util.objectForKey($advertisementData, 'kCBAdvDataLocalName'); 51 | var $manufacturerData = $util.objectForKey($advertisementData, 'kCBAdvDataManufacturerData'); 52 | var $serviceData = $util.objectForKey($advertisementData, 'kCBAdvDataServiceData'); 53 | var $serviceUuids = $util.objectForKey($advertisementData, 'kCBAdvDataServiceUUIDs'); 54 | var $txPowerLevel = $util.objectForKey($advertisementData, 'kCBAdvDataTxPowerLevel'); 55 | var $connectable = $util.objectForKey($advertisementData, 'kCBAdvDataIsConnectable'); 56 | 57 | var advertisementData = {}; 58 | 59 | if ($localName !== null) { 60 | advertisementData.localName = $util.toString($localName); 61 | } 62 | 63 | if ($manufacturerData !== null) { 64 | advertisementData.manufacturerData = $util.toBuffer($manufacturerData); 65 | } 66 | 67 | if ($serviceData !== null) { 68 | advertisementData.serviceData = {}; 69 | 70 | $util.$dictionaryForEach($serviceData, function($key, $object) { 71 | advertisementData.serviceData[$util.toUuidString($key)] = $util.toBuffer($object); 72 | }); 73 | } 74 | 75 | if ($serviceUuids !== null) { 76 | advertisementData.serviceUuids = []; 77 | 78 | $util.$arrayForEach($serviceUuids, function($object) { 79 | advertisementData.serviceUuids.push($util.toUuidString($object)); 80 | }); 81 | } 82 | 83 | if ($txPowerLevel) { 84 | advertisementData.txPowerLevel = $util.toInt($txPowerLevel); 85 | } 86 | 87 | if ($connectable !== null) { 88 | advertisementData.connectable = $util.toBool($connectable); 89 | } 90 | 91 | var rssi = $util.toInt($RSSI); 92 | 93 | if (advertisementData.connectable !== undefined && Object.keys(advertisementData).length === 1) { 94 | return; 95 | } 96 | 97 | debug('centralManagerDidDiscoverPeripheral:%s advertisement:%j RSSI:%d', identifier, advertisementData, rssi); 98 | 99 | if (peripherals[identifier] === undefined) { 100 | peripherals[identifier] = new Peripheral($peripheral); 101 | } 102 | 103 | process.nextTick(function(peripheral, advertisementData, rssi) { 104 | this.emit('peripheralDiscover', peripheral, advertisementData, rssi); 105 | }.bind(mapDelegate($self), mapPeripheral(identifier), advertisementData, rssi)); 106 | }); 107 | 108 | CBCentralManagerDelegate.addMethod('centralManager:didConnectPeripheral:', 109 | 'v@:@@', function ($self, $_cmd, $centralManager, $peripheral) { 110 | var identifier = $util.identifierFor$Peripheral($peripheral); 111 | 112 | debug('centralManagerDidConnectPeripheral:%s', identifier); 113 | 114 | process.nextTick(function(peripheral) { 115 | this.emit('peripheralConnect', peripheral); 116 | 117 | peripheral.emit('connect'); 118 | }.bind(mapDelegate($self), mapPeripheral(identifier))); 119 | }); 120 | 121 | CBCentralManagerDelegate.addMethod('centralManager:didDisconnectPeripheral:error:', 122 | 'v@:@@@', function ($self, $_cmd, $centralManager, $peripheral, $error) { 123 | var identifier = $util.identifierFor$Peripheral($peripheral); 124 | var error = $util.toError($error); 125 | 126 | debug('centralManagerDidDisconnectPeripheral:%s %s', identifier, error); 127 | 128 | process.nextTick(function(peripheral, error) { 129 | this.emit('peripheralDisconnect', peripheral, error); 130 | 131 | peripheral.emit('disconnect', error); 132 | }.bind(mapDelegate($self), mapPeripheral(identifier), error)); 133 | }); 134 | 135 | CBCentralManagerDelegate.addMethod('centralManager:didFailToConnectPeripheral:error:', 136 | 'v@:@@@', function ($self, $_cmd, $centralManager, $peripheral, $error) { 137 | var identifier = $util.identifierFor$Peripheral($peripheral); 138 | var error = $util.toError($error); 139 | 140 | debug('centralManagerDidFailToConnectPeripheral:%s %s', identifier, error); 141 | 142 | process.nextTick(function(peripheral, error) { 143 | this.emit('peripheralConnectFail', peripheral, error); 144 | 145 | peripheral.emit('connectFail', error); 146 | }.bind(mapDelegate($self), mapPeripheral(identifier), error)); 147 | }); 148 | -------------------------------------------------------------------------------- /lib/central-manager.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var ffi = require('ffi'); 5 | var $ = require('NodObjC'); 6 | var reemit = require('re-emitter'); 7 | 8 | var CentralManagerDelegate = require('./central-manager-delegate'); 9 | var $util = require('./util'); 10 | 11 | var localAddress = require('./local-address'); 12 | 13 | $.import('Foundation'); 14 | $.import('CoreBluetooth'); 15 | 16 | var libdispatch = ffi.Library('/usr/lib/system/libdispatch', { 17 | 'dispatch_queue_create': ['pointer', ['string', 'int']] 18 | }); 19 | 20 | function CentralManager() { 21 | this.delegate = new CentralManagerDelegate(); 22 | 23 | this.delegate.on('peripheralDiscover', this._onPeripheralDiscover.bind(this)); 24 | 25 | reemit(this.delegate, this, [ 26 | 'stateUpdate', 27 | 'peripheralDiscover', 28 | 'peripheralConnect', 29 | 'peripheralDisconnect', 30 | 'peripheralConnectFail' 31 | ]); 32 | 33 | localAddress(function(address) { 34 | if (address) { 35 | this.emit('address', address); 36 | } 37 | 38 | this._dispatchQueue = libdispatch.dispatch_queue_create('node-core-bluetooth', 0); 39 | this.$ = $.CBCentralManager('alloc')('initWithDelegate', this.delegate.$, 'queue', this._dispatchQueue); 40 | }.bind(this)); 41 | } 42 | 43 | util.inherits(CentralManager, events.EventEmitter); 44 | 45 | CentralManager.prototype.scanForPeripherals = function(services, allowDuplicates) { 46 | var $services = $util.to$Uuids(services); 47 | var $allowDuplicates = $util.boolTo$Number(allowDuplicates ? true : false); 48 | var $options = $.NSMutableDictionary('alloc')('init'); 49 | 50 | $util.$setObjectForKey($options, $allowDuplicates, 'kCBScanOptionAllowDuplicates'); 51 | 52 | this.$('scanForPeripheralsWithServices', $services, 'options', $options); 53 | }; 54 | 55 | CentralManager.prototype.stopScan = function() { 56 | this.$('stopScan'); 57 | }; 58 | 59 | CentralManager.prototype.connect = function(peripheral) { 60 | this.$('connectPeripheral', peripheral.$, 'options', null); 61 | }; 62 | 63 | CentralManager.prototype.cancelConnection = function(peripheral) { 64 | this.$('cancelPeripheralConnection', peripheral.$); 65 | }; 66 | 67 | CentralManager.prototype._onPeripheralDiscover = function(peripheral, advertisementData, rssi) { 68 | peripheral.centralManager = this; 69 | }; 70 | 71 | module.exports = CentralManager; 72 | -------------------------------------------------------------------------------- /lib/characteristic.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | 6 | var Descriptor = require('./descriptor'); 7 | var $util = require('./util'); 8 | 9 | function Characteristic($characteristic) { 10 | this.$ = $characteristic('retain'); 11 | 12 | this.identifier = $util.identifierFor$Characteristic(this.$); 13 | this.uuid = $util.toUuidString(this.$('UUID')); 14 | 15 | this.properties = this.$('properties'); 16 | } 17 | 18 | util.inherits(Characteristic, events.EventEmitter); 19 | 20 | Characteristic.prototype.toString = function() { 21 | return JSON.stringify({ 22 | uuid: this.uuid, 23 | properties: this.properties, 24 | descriptors: this.descriptors 25 | }); 26 | }; 27 | 28 | Characteristic.prototype.discoverDescriptors = function() { 29 | this.peripheral.discoverDescriptorsForCharacteristic(this); 30 | }; 31 | 32 | Characteristic.prototype.readValue = function() { 33 | this.peripheral.readValueForCharacteristic(this); 34 | }; 35 | 36 | Characteristic.prototype.setBroadcastValue = function(value) { 37 | this.peripheral.setBroadcastValueForCharacteristic(value, this); 38 | }; 39 | 40 | Characteristic.prototype.setNotifyValue = function(value) { 41 | this.peripheral.setNotifyValueForCharacteristic(value, this); 42 | }; 43 | 44 | Characteristic.prototype.writeValue = function(value, type) { 45 | this.peripheral.writeValueForCharacteristic(value, this, type ? 1 : 0); 46 | }; 47 | 48 | Characteristic.prototype.onDescriptorsDiscover = function(error) { 49 | this.descriptors = undefined; 50 | 51 | if (error === undefined) { 52 | var $descriptors = this.$('descriptors'); 53 | 54 | this.descriptors = []; 55 | 56 | $util.$arrayForEach($descriptors, function($descriptor) { 57 | var descriptor = new Descriptor($descriptor); 58 | 59 | descriptor.characteristic = this; 60 | descriptor.peripheral = this.peripheral; 61 | 62 | this.descriptors.push(descriptor); 63 | }.bind(this)); 64 | } 65 | 66 | this.emit('descriptorsDiscover', this.descriptors, error); 67 | }; 68 | 69 | Characteristic.prototype.onValueUpdate = function(error) { 70 | var value; 71 | 72 | if (error === undefined) { 73 | var $data = this.$('value'); 74 | 75 | value = $util.toBuffer($data); 76 | } 77 | 78 | this.emit('valueUpdate', value, error); 79 | }; 80 | 81 | Characteristic.prototype.onValueWrite = function(error) { 82 | this.emit('valueWrite', error); 83 | }; 84 | 85 | Characteristic.prototype.onNotificationStateUpdate = function(error) { 86 | var isNotifying; 87 | 88 | if (error === undefined) { 89 | isNotifying = this.$('isNotifying') ? true : false; 90 | } 91 | 92 | this.emit('notificationStateUpdate', isNotifying, error); 93 | }; 94 | 95 | module.exports = Characteristic; 96 | -------------------------------------------------------------------------------- /lib/descriptor.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | 6 | var $util = require('./util'); 7 | 8 | function Descriptor($descriptor) { 9 | this.$ = $descriptor('retain'); 10 | 11 | this.identifier = $util.identifierFor$Descriptor(this.$); 12 | this.uuid = $util.toUuidString(this.$('UUID')); 13 | } 14 | 15 | util.inherits(Descriptor, events.EventEmitter); 16 | 17 | Descriptor.prototype.toString = function() { 18 | return JSON.stringify({ 19 | uuid: this.uuid 20 | }); 21 | }; 22 | 23 | Descriptor.prototype.readValue = function() { 24 | this.peripheral.readValueForDescriptor(this); 25 | }; 26 | 27 | Descriptor.prototype.writeValue = function(value) { 28 | this.peripheral.writeValueForDescriptor(value, this); 29 | }; 30 | 31 | Descriptor.prototype.onValueUpdate = function(error) { 32 | var value; 33 | 34 | if (error === undefined) { 35 | var $data = this.$('value'); 36 | 37 | value = $util.toBuffer($data); 38 | } 39 | 40 | this.emit('valueUpdate', value, error); 41 | }; 42 | 43 | Descriptor.prototype.onValueWrite = function(error) { 44 | this.emit('valueWrite', error); 45 | }; 46 | 47 | module.exports = Descriptor; 48 | -------------------------------------------------------------------------------- /lib/local-address.js: -------------------------------------------------------------------------------- 1 | var child_process = require('child_process'); 2 | 3 | function localAddress(callback) { 4 | child_process.exec('system_profiler SPBluetoothDataType', {}, function(error, stdout, stderr) { 5 | var address = null; 6 | 7 | if (!error) { 8 | var found = stdout.match(/\s+Address: (.*)/); 9 | if (found) { 10 | address = found[1].toLowerCase().replace(/-/g, ':'); 11 | } 12 | } 13 | 14 | callback(address); 15 | }); 16 | } 17 | 18 | module.exports = localAddress; 19 | -------------------------------------------------------------------------------- /lib/mutable-characteristic.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | var debug = require('debug')('mutable-characteristic'); 6 | 7 | var $util = require('./util'); 8 | 9 | $.import('Foundation'); 10 | 11 | function MutableCharacteristic(uuid, properties, value, permissions, descriptors) { 12 | this.uuid = uuid; 13 | this.properties = properties; 14 | this.value = value; 15 | this.descriptors = descriptors; 16 | 17 | var $uuid = $util.to$Uuid(uuid); 18 | var $properties = properties; 19 | var $value = $util.bufferTo$Data(value); 20 | var $permissions = permissions; 21 | 22 | this.$ = $.CBMutableCharacteristic('alloc')('initWithType', $uuid, 23 | 'properties', $properties, 24 | 'value', $value, 25 | 'permissions', $permissions); 26 | 27 | if (descriptors) { 28 | var $descriptors = $.NSMutableArray('alloc')('init'); 29 | 30 | descriptors.forEach(function(descriptor) { 31 | $descriptors('addObject', descriptor.$); 32 | }); 33 | 34 | this.$('setDescriptors', $descriptors); 35 | } 36 | } 37 | 38 | util.inherits(MutableCharacteristic, events.EventEmitter); 39 | 40 | MutableCharacteristic.prototype.toString = function() { 41 | return JSON.stringify({ 42 | uuid: this.uuid, 43 | properties: this.properties, 44 | value: this.value, 45 | descriptors: this.descriptors 46 | }); 47 | }; 48 | 49 | MutableCharacteristic.prototype.onAdded = function() { 50 | this.identifier = $util.identifierFor$MutableAttribute(this.$); 51 | 52 | if (this.descriptors) { 53 | this.descriptors.forEach(function(descriptor) { 54 | descriptor.onAdded(); 55 | }); 56 | } 57 | 58 | this.emit('added'); 59 | }; 60 | 61 | /* 62 | typedef enum { 63 | CBATTErrorSuccess = 0x00, 64 | CBATTErrorInvalidHandle = 0x01, 65 | CBATTErrorReadNotPermitted = 0x02, 66 | CBATTErrorWriteNotPermitted = 0x03, 67 | CBATTErrorInvalidPdu = 0x04, 68 | CBATTErrorInsufficientAuthentication = 0x05, 69 | CBATTErrorRequestNotSupported = 0x06, 70 | CBATTErrorInvalidOffset = 0x07, 71 | CBATTErrorInsufficientAuthorization = 0x08, 72 | CBATTErrorPrepareQueueFull = 0x09, 73 | CBATTErrorAttributeNotFound = 0x0A, 74 | CBATTErrorAttributeNotLong = 0x0B, 75 | CBATTErrorInsufficientEncryptionKeySize = 0x0C, 76 | CBATTErrorInvalidAttributeValueLength = 0x0D, 77 | CBATTErrorUnlikelyError = 0x0E, 78 | CBATTErrorInsufficientEncryption = 0x0F, 79 | CBATTErrorUnsupportedGroupType = 0x10, 80 | CBATTErrorInsufficientResources = 0x11, 81 | } CBATTError; 82 | */ 83 | 84 | MutableCharacteristic.prototype.onReadRequest = function(transactionId, offset) { 85 | debug('onReadRequest: %d', offset); 86 | 87 | this.peripheralManager.respondToRequest(transactionId, 0x0e); 88 | }; 89 | 90 | MutableCharacteristic.prototype.onWriteRequest = function(transactionId, offset, value, ignoreResponse) { 91 | debug('onWriteRequest: %d %s %d', offset, value.toString('hex'), ignoreResponse); 92 | 93 | this.peripheralManager.respondToRequest(transactionId, 0x0e); 94 | }; 95 | 96 | MutableCharacteristic.prototype.onSubscribe = function(centralIdentifier, maximumUpdateValueLength) { 97 | debug('onSubscribe: %s %d', centralIdentifier, maximumUpdateValueLength); 98 | }; 99 | 100 | MutableCharacteristic.prototype.onUnsubscribe = function(centralIdentifier) { 101 | debug('onUnsubscribe: %s', centralIdentifier); 102 | }; 103 | 104 | MutableCharacteristic.prototype.onValueUpdate = function() { 105 | debug('onValueUpdate'); 106 | }; 107 | 108 | module.exports = MutableCharacteristic; 109 | -------------------------------------------------------------------------------- /lib/mutable-descriptor.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | var debug = require('debug')('mutable-descriptor'); 6 | 7 | var $util = require('./util'); 8 | 9 | $.import('Foundation'); 10 | 11 | function MutableDescriptor(uuid, value) { 12 | this.uuid = uuid; 13 | this.value = value; 14 | 15 | var $uuid = $util.to$Uuid(uuid); 16 | var $value = (uuid === '2901') ? $(value) : $util.bufferTo$Data(value); 17 | 18 | this.$ = $.CBMutableDescriptor('alloc')('initWithType', $uuid, 'value', $value); 19 | } 20 | 21 | util.inherits(MutableDescriptor, events.EventEmitter); 22 | 23 | MutableDescriptor.prototype.toString = function() { 24 | return JSON.stringify({ 25 | uuid: this.uuid, 26 | value: this.value 27 | }); 28 | }; 29 | 30 | MutableDescriptor.prototype.onAdded = function() { 31 | this.identifier = $util.identifierFor$MutableAttribute(this.$); 32 | 33 | this.emit('added'); 34 | }; 35 | 36 | module.exports = MutableDescriptor; 37 | -------------------------------------------------------------------------------- /lib/mutable-service.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | var debug = require('debug')('mutable-service'); 6 | 7 | var $util = require('./util'); 8 | 9 | $.import('Foundation'); 10 | 11 | function MutableService(uuid, primary, includedServices, characteristics) { 12 | this.uuid = uuid; 13 | this.primary = primary; 14 | this.includedServices = includedServices; 15 | this.characteristics = characteristics; 16 | 17 | var $uuid = $util.to$Uuid(uuid); 18 | 19 | this.$ = $.CBMutableService('alloc')('initWithType', $uuid, 'primary', primary ? true : false); 20 | 21 | if (includedServices) { 22 | var $includedServices = $.NSMutableArray('alloc')('init'); 23 | 24 | includedServices.forEach(function(includedService) { 25 | $includedServices('addObject', includedService.$); 26 | }); 27 | 28 | this.$('setIncludedServices', $includedServices); 29 | } 30 | 31 | if (characteristics) { 32 | var $characteristics = $.NSMutableArray('alloc')('init'); 33 | 34 | characteristics.forEach(function(characteristic) { 35 | $characteristics('addObject', characteristic.$); 36 | }); 37 | 38 | this.$('setCharacteristics', $characteristics); 39 | } 40 | } 41 | 42 | util.inherits(MutableService, events.EventEmitter); 43 | 44 | MutableService.prototype.toString = function() { 45 | return JSON.stringify({ 46 | uuid: this.uuid, 47 | includedServices: this.includedServices, 48 | characteristics: this.characteristics 49 | }); 50 | }; 51 | 52 | MutableService.prototype.onAdded = function(error) { 53 | if (error === undefined) { 54 | this.identifier = $util.identifierFor$MutableAttribute(this.$); 55 | 56 | if (this.includedServices) { 57 | this.includedServices.forEach(function(includedService) { 58 | includedService.onAdded(); 59 | }); 60 | } 61 | 62 | if (this.characteristics) { 63 | this.characteristics.forEach(function(characteristic) { 64 | characteristic.onAdded(); 65 | }); 66 | } 67 | } 68 | 69 | this.emit('added', error); 70 | }; 71 | 72 | module.exports = MutableService; 73 | -------------------------------------------------------------------------------- /lib/peripheral-delegate.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | var debug = require('debug')('peripheral-delegate'); 6 | 7 | var $util = require('./util'); 8 | 9 | $.import('Foundation'); 10 | 11 | function PeripheralDelegate() { 12 | this.$ = CBPeripheralDelegate('alloc')('init'); 13 | 14 | delegates[this.$] = this; 15 | } 16 | 17 | util.inherits(PeripheralDelegate, events.EventEmitter); 18 | 19 | module.exports = PeripheralDelegate; 20 | 21 | var delegates = {}; 22 | 23 | function mapDelegate(self) { 24 | return delegates[self]; 25 | } 26 | 27 | var CBPeripheralDelegate = $.NSObject.extend('CBPeripheralDelegate'); 28 | 29 | CBPeripheralDelegate.addMethod('peripheralDidUpdateRSSI:error:', 30 | 'v@:@@', function ($self, $_cmd, $peripheral, $error) { 31 | var identifier = $util.identifierFor$Peripheral($peripheral); 32 | var error = $util.toError($error); 33 | 34 | debug('peripheralDidUpdateRSSI:%s %s', identifier, error); 35 | 36 | process.nextTick(function(error) { 37 | this.emit('rssiUpdate', error); 38 | }.bind(mapDelegate($self), error)); 39 | }); 40 | 41 | CBPeripheralDelegate.addMethod('peripheral:didDiscoverServices:', 42 | 'v@:@@', function ($self, $_cmd, $peripheral, $error) { 43 | var identifier = $util.identifierFor$Peripheral($peripheral); 44 | var error = $util.toError($error); 45 | 46 | debug('peripheralDidDiscoverServices:%s %s', identifier, error); 47 | 48 | process.nextTick(function(error) { 49 | this.emit('servicesDiscover', error); 50 | }.bind(mapDelegate($self), error)); 51 | }); 52 | 53 | CBPeripheralDelegate.addMethod('peripheral:didDiscoverIncludedServicesForService:error:', 54 | 'v@:@@@', function ($self, $_cmd, $peripheral, $service, $error) { 55 | var identifier = $util.identifierFor$Peripheral($peripheral); 56 | var serviceIdentifier = $util.identifierFor$Service($service); 57 | var error = $util.toError($error); 58 | 59 | debug('peripheralDidDiscoverIncludedServicesForService:%s %s %s', identifier, serviceIdentifier, error); 60 | 61 | process.nextTick(function(serviceIdentifier, error) { 62 | this.emit('serviceIncludedServicesDiscover', serviceIdentifier, error); 63 | }.bind(mapDelegate($self), serviceIdentifier, error)); 64 | }); 65 | 66 | CBPeripheralDelegate.addMethod('peripheral:didDiscoverCharacteristicsForService:error:', 67 | 'v@:@@@', function ($self, $_cmd, $peripheral, $service, $error) { 68 | var identifier = $util.identifierFor$Peripheral($peripheral); 69 | var serviceIdentifier = $util.identifierFor$Service($service); 70 | var error = $util.toError($error); 71 | 72 | debug('peripheralDidDiscoverCharacteristicsForService:%s %s %s', identifier, serviceIdentifier, error); 73 | 74 | process.nextTick(function(serviceIdentifier, error) { 75 | this.emit('serviceCharacteristicsDiscover', serviceIdentifier, error); 76 | }.bind(mapDelegate($self), serviceIdentifier, error)); 77 | }); 78 | 79 | CBPeripheralDelegate.addMethod('peripheral:didDiscoverDescriptorsForCharacteristic:error:', 80 | 'v@:@@@', function ($self, $_cmd, $peripheral, $characteristic, $error) { 81 | var identifier = $util.identifierFor$Peripheral($peripheral); 82 | var characteristicIdentifier = $util.identifierFor$Characteristic($characteristic); 83 | var error = $util.toError($error); 84 | 85 | debug('peripheralDidDiscoverDescriptorsForCharacteristic:%s %s %s', identifier, characteristicIdentifier, error); 86 | 87 | process.nextTick(function(characteristicIdentifier, error) { 88 | this.emit('characteristicDescriptorsDiscover', characteristicIdentifier, error); 89 | }.bind(mapDelegate($self), characteristicIdentifier, error)); 90 | }); 91 | 92 | CBPeripheralDelegate.addMethod('peripheral:didUpdateValueForCharacteristic:error:', 93 | 'v@:@@@', function ($self, $_cmd, $peripheral, $characteristic, $error) { 94 | var identifier = $util.identifierFor$Peripheral($peripheral); 95 | var characteristicIdentifier = $util.identifierFor$Characteristic($characteristic); 96 | var error = $util.toError($error); 97 | 98 | debug('peripheralDidUpdateValueForCharacteristic:%s %s %s', identifier, characteristicIdentifier, error); 99 | 100 | process.nextTick(function(characteristicIdentifier, error) { 101 | this.emit('characteristicValueUpdate', characteristicIdentifier, error); 102 | }.bind(mapDelegate($self), characteristicIdentifier, error)); 103 | }); 104 | 105 | CBPeripheralDelegate.addMethod('peripheral:didUpdateNotificationStateForCharacteristic:error:', 106 | 'v@:@@@', function ($self, $_cmd, $peripheral, $characteristic, $error) { 107 | var identifier = $util.identifierFor$Peripheral($peripheral); 108 | var characteristicIdentifier = $util.identifierFor$Characteristic($characteristic); 109 | var error = $util.toError($error); 110 | 111 | debug('peripheralDidUpdateNotificationStateForCharacteristic:%s %s %s', identifier, characteristicIdentifier, error); 112 | 113 | process.nextTick(function(characteristicIdentifier, error) { 114 | this.emit('characteristicNotificationStateUpdate', characteristicIdentifier, error); 115 | }.bind(mapDelegate($self), characteristicIdentifier, error)); 116 | }); 117 | 118 | CBPeripheralDelegate.addMethod('peripheral:didWriteValueForCharacteristic:error:', 119 | 'v@:@@@', function ($self, $_cmd, $peripheral, $characteristic, $error) { 120 | var identifier = $util.identifierFor$Peripheral($peripheral); 121 | var characteristicIdentifier = $util.identifierFor$Characteristic($characteristic); 122 | var error = $util.toError($error); 123 | 124 | debug('peripheralDidWriteValueForCharacteristic:%s %s %s', identifier, characteristicIdentifier, error); 125 | 126 | process.nextTick(function(characteristicIdentifier, error) { 127 | this.emit('characteristicValueWrite', characteristicIdentifier, error); 128 | }.bind(mapDelegate($self), characteristicIdentifier, error)); 129 | }); 130 | 131 | CBPeripheralDelegate.addMethod('peripheral:didUpdateValueForDescriptor:error:', 132 | 'v@:@@@', function ($self, $_cmd, $peripheral, $descriptor, $error) { 133 | var identifier = $util.identifierFor$Peripheral($peripheral); 134 | var descriptorIdentifier = $util.identifierFor$Descriptor($descriptor); 135 | var error = $util.toError($error); 136 | 137 | debug('peripheralDidUpdateValueForDescriptor:%s %s %s', identifier, descriptorIdentifier, error); 138 | 139 | process.nextTick(function(descriptorIdentifier, error) { 140 | this.emit('descriptorValueUpdate', descriptorIdentifier, error); 141 | }.bind(mapDelegate($self), descriptorIdentifier, error)); 142 | }); 143 | 144 | CBPeripheralDelegate.addMethod('peripheral:didWriteValueForDescriptor:error:', 145 | 'v@:@@@', function ($self, $_cmd, $peripheral, $descriptor, $error) { 146 | var identifier = $util.identifierFor$Peripheral($peripheral); 147 | var descriptorIdentifier = $util.identifierFor$Descriptor($descriptor); 148 | var error = $util.toError($error); 149 | 150 | debug('peripheralDidWriteValueForDescriptor:%s %s %s', identifier, descriptorIdentifier, error); 151 | 152 | process.nextTick(function(descriptorIdentifier, error) { 153 | this.emit('descriptorValueWrite', descriptorIdentifier, error); 154 | }.bind(mapDelegate($self), descriptorIdentifier, error)); 155 | }); 156 | -------------------------------------------------------------------------------- /lib/peripheral-manager-delegate.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | var debug = require('debug')('peripheral-manager-delegate'); 6 | 7 | var $util = require('./util'); 8 | 9 | function PeripheralManagerDelegate() { 10 | this.$ = CBPeripheralManagerDelegate('alloc')('init'); 11 | 12 | delegates[this.$] = this; 13 | 14 | this._peripheral = {}; 15 | } 16 | 17 | util.inherits(PeripheralManagerDelegate, events.EventEmitter); 18 | 19 | module.exports = PeripheralManagerDelegate; 20 | 21 | var delegates = {}; 22 | 23 | function mapDelegate(self) { 24 | return delegates[self]; 25 | } 26 | 27 | var CBPeripheralManagerDelegate = $.NSObject.extend('CBPeripheralManagerDelegate'); 28 | 29 | CBPeripheralManagerDelegate.addMethod('peripheralManagerDidUpdateState:', 'v@:@', function ($self, $_cmd, $peripheralManager) { 30 | var state = $peripheralManager('state'); 31 | debug('peripheralManagerDidUpdateState:%d', state); 32 | 33 | process.nextTick(function(state) { 34 | var STATE_MAPPER = ['unknown', 'resetting', 'unsupported', 'unauthorized', 'poweredOff', 'poweredOn']; 35 | 36 | this.emit('stateUpdate', STATE_MAPPER[state]); 37 | }.bind(mapDelegate($self), state)); 38 | }); 39 | 40 | CBPeripheralManagerDelegate.addMethod('peripheralManagerDidStartAdvertising:error:', 41 | 'v@:@@', function ($self, $_cmd, $peripheralManager, $error) { 42 | var error = $util.toError($error); 43 | 44 | debug('peripheralManagerDidStartAdvertising:%s', error); 45 | 46 | process.nextTick(function(error) { 47 | this.emit('advertisingStart', error); 48 | }.bind(mapDelegate($self), error)); 49 | }); 50 | 51 | CBPeripheralManagerDelegate.addMethod('peripheralManager:didAddService:error:', 52 | 'v@:@@@', function ($self, $_cmd, $peripheralManager, $service, $error) { 53 | var error = $util.toError($error); 54 | var serviceIdentifier = $util.identifierFor$MutableAttribute($service); 55 | 56 | debug('peripheralManagerDidAddService:%s %s', error, serviceIdentifier); 57 | 58 | process.nextTick(function(serviceIdentifier, error) { 59 | this.emit('serviceAdded', serviceIdentifier, error); 60 | }.bind(mapDelegate($self), serviceIdentifier, error)); 61 | }); 62 | 63 | CBPeripheralManagerDelegate.addMethod('peripheralManager:central:didExchangeMTU:', 64 | 'v@:@@@', function ($self, $_cmd, $peripheralManager, $deviceUUID, $mtu) { 65 | var centralIdentifier = $util.toUuidString($deviceUUID); 66 | var mtu = $util.toInt($mtu); 67 | 68 | debug('peripheralManagercentralDidExchangeMTU:%s %d', centralIdentifier, mtu); 69 | 70 | process.nextTick(function(centralIdentifier, mtu) { 71 | this.emit('mtuChange', centralIdentifier, mtu); 72 | }.bind(mapDelegate($self), centralIdentifier, mtu)); 73 | }); 74 | 75 | CBPeripheralManagerDelegate.addMethod('peripheralManager:central:didSubscribeToCharacteristic:', 76 | 'v@:@@@', function ($self, $_cmd, $peripheralManager, $central, $characteristic) { 77 | var centralIdentifier = $util.identifierFor$Central($central); 78 | var maximumUpdateValueLength = $central('maximumUpdateValueLength'); 79 | var characteristicIdentifier = $util.identifierFor$MutableAttribute($characteristic); 80 | 81 | debug('peripheralManagerCentralDidSubscribeToCharacteristic:%s %d %s', centralIdentifier, maximumUpdateValueLength, characteristicIdentifier); 82 | 83 | process.nextTick(function(centralIdentifier, maximumUpdateValueLength, characteristicIdentifier) { 84 | this.emit('subscribe', centralIdentifier, maximumUpdateValueLength, characteristicIdentifier); 85 | }.bind(mapDelegate($self), centralIdentifier, maximumUpdateValueLength, characteristicIdentifier)); 86 | }); 87 | 88 | CBPeripheralManagerDelegate.addMethod('peripheralManager:central:didUnsubscribeFromCharacteristic:', 89 | 'v@:@@@', function ($self, $_cmd, $peripheralManager, $central, $characteristic) { 90 | var centralIdentifier = $util.identifierFor$Central($central); 91 | var characteristicIdentifier = $util.identifierFor$MutableAttribute($characteristic); 92 | 93 | debug('peripheralManagerCentralDidUnsubscribeFromCharacteristic:%s %s', centralIdentifier, characteristicIdentifier); 94 | 95 | process.nextTick(function(centralIdentifier, characteristicIdentifier) { 96 | this.emit('unsubscribe', centralIdentifier, characteristicIdentifier); 97 | }.bind(mapDelegate($self), centralIdentifier, characteristicIdentifier)); 98 | }); 99 | 100 | // - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral 101 | 102 | CBPeripheralManagerDelegate.addMethod('peripheralManager:didUpdateValueForCharacteristic:', 103 | 'v@:@@', function ($self, $_cmd, $peripheralManager, $attributeID) { 104 | var characteristicIdentifier = $util.toInt($attributeID); 105 | 106 | debug('peripheralManagerDidUpdateValueForCharacteristic:%s %s', characteristicIdentifier); 107 | 108 | process.nextTick(function(characteristicIdentifier) { 109 | this.emit('valueUpdate', characteristicIdentifier); 110 | }.bind(mapDelegate($self), characteristicIdentifier)); 111 | }); 112 | 113 | CBPeripheralManagerDelegate.addMethod('peripheralManager:didReceiveReadRequest:', 114 | 'v@:@@', function ($self, $_cmd, $peripheralManager, $request) { 115 | debug('peripheralManagerDidReceiveReadRequest:%s', $request); 116 | 117 | $request('retain'); 118 | 119 | process.nextTick(function($request) { 120 | this.emit('readRequest', $request); 121 | }.bind(mapDelegate($self), $request)); 122 | }); 123 | 124 | CBPeripheralManagerDelegate.addMethod('peripheralManager:didReceiveWriteRequests:', 125 | 'v@:@@', function ($self, $_cmd, $peripheralManager, $requests) { 126 | debug('peripheralManagerDidReceiveWriteRequests:%s', $requests); 127 | 128 | $requests('retain'); 129 | 130 | $util.$arrayForEach($requests, function($request) { 131 | $request('retain'); 132 | }); 133 | 134 | process.nextTick(function($requests) { 135 | this.emit('writeRequests', $requests); 136 | }.bind(mapDelegate($self), $requests)); 137 | }); 138 | -------------------------------------------------------------------------------- /lib/peripheral-manager.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var ffi = require('ffi'); 5 | var $ = require('NodObjC'); 6 | var reemit = require('re-emitter'); 7 | 8 | var PeripheralManagerDelegate = require('./peripheral-manager-delegate'); 9 | var $util = require('./util'); 10 | 11 | var localAddress = require('./local-address'); 12 | var uuidToAddress = require('./uuid-to-address'); 13 | 14 | 15 | $.import('Foundation'); 16 | $.import('CoreBluetooth'); 17 | 18 | var libdispatch = ffi.Library('/usr/lib/system/libdispatch', { 19 | 'dispatch_queue_create': ['pointer', ['string', 'int']] 20 | }); 21 | 22 | $.CBPeripheralManager.addMethod('xpcConnection:didReceiveMsg:argsSwizzled:', { retval: 'v', args: [ '@', ':', '@', 'i', '@'] }, function($self, $_cmd, $arg1, $arg2, $arg3) { 23 | $self('xpcConnection', $arg1, 'didReceiveMsg', $arg2, 'argsSwizzled', $arg3); 24 | 25 | if ($arg2 === 53) { 26 | var $deviceUUID = $util.objectForKey($arg3, 'kCBMsgArgDeviceUUID'); 27 | var $mtu = $util.objectForKey($arg3, 'kCBMsgArgATTMTU'); 28 | 29 | process.nextTick(function($deviceUUID, $mtu) { 30 | $self('delegate')('peripheralManager', $self, 'central', $deviceUUID, 'didExchangeMTU', $mtu); 31 | }.bind(this, $deviceUUID, $mtu)); 32 | } else if ($arg2 === 23) { 33 | var $attributeID = $util.objectForKey($arg3, 'kCBMsgArgAttributeID'); 34 | 35 | process.nextTick(function($attributeID) { 36 | $self('delegate')('peripheralManager', $self, 'didUpdateValueForCharacteristic', $attributeID); 37 | }.bind(this, $attributeID)); 38 | } 39 | }); 40 | 41 | $.CBPeripheralManager.getInstanceMethod('xpcConnection:didReceiveMsg:args:').exchangeImplementations($.CBPeripheralManager.getInstanceMethod('xpcConnection:didReceiveMsg:argsSwizzled:')); 42 | 43 | function PeripheralManager() { 44 | this.delegate = new PeripheralManagerDelegate(); 45 | this._services = []; 46 | this._pendingRequests = {}; 47 | 48 | reemit(this.delegate, this, [ 49 | 'stateUpdate', 50 | 'advertisingStart' 51 | ]); 52 | 53 | this.delegate.on('serviceAdded', this._onServiceAdded.bind(this)); 54 | this.delegate.on('readRequest', this._onReadRequest.bind(this)); 55 | this.delegate.on('writeRequests', this._onWriteRequests.bind(this)); 56 | this.delegate.on('mtuChange', this._onMtuChange.bind(this)); 57 | this.delegate.on('subscribe', this._onSubscribe.bind(this)); 58 | this.delegate.on('unsubscribe', this._onUnsubscribe.bind(this)); 59 | this.delegate.on('valueUpdate', this._onValueUpdate.bind(this)); 60 | 61 | localAddress(function(address) { 62 | if (address) { 63 | this.emit('address', address); 64 | } 65 | 66 | this._dispatchQueue = libdispatch.dispatch_queue_create('node-core-bluetooth', 0); 67 | this.$ = $.CBPeripheralManager('alloc')('initWithDelegate', this.delegate.$, 'queue', this._dispatchQueue); 68 | }.bind(this)); 69 | } 70 | 71 | util.inherits(PeripheralManager, events.EventEmitter); 72 | 73 | PeripheralManager.prototype.startAdvertising = function(advertisementData) { 74 | var $advertisementData = $.NSMutableDictionary('alloc')('init'); 75 | 76 | if (advertisementData.localName) { 77 | var $localName = $(advertisementData.localName); 78 | 79 | $util.$setObjectForKey($advertisementData, $localName, 'kCBAdvDataLocalName'); 80 | } 81 | 82 | if (advertisementData.serviceUuids) { 83 | var $serviceUuids = $util.to$Uuids(advertisementData.serviceUuids); 84 | 85 | $util.$setObjectForKey($advertisementData, $serviceUuids, 'kCBAdvDataServiceUUIDs'); 86 | } 87 | 88 | if (advertisementData.appleBeacon) { 89 | var $appleBeacon = $util.bufferTo$Data(advertisementData.appleBeacon); 90 | 91 | $util.$setObjectForKey($advertisementData, $appleBeacon, 'kCBAdvDataAppleBeaconKey'); 92 | } 93 | 94 | if (advertisementData.appleMfgData) { 95 | var $appleMfgData = $util.bufferTo$Data(advertisementData.appleMfgData); 96 | 97 | $util.$setObjectForKey($advertisementData, $appleMfgData, 'kCBAdvDataAppleMfgData'); 98 | } 99 | 100 | this.$('startAdvertising', $advertisementData); 101 | }; 102 | 103 | PeripheralManager.prototype.stopAdvertising = function() { 104 | this.$('stopAdvertising'); 105 | }; 106 | 107 | PeripheralManager.prototype.addService = function(mutableService) { 108 | this.$('addService', mutableService.$); 109 | 110 | mutableService.identifier = $util.identifierFor$MutableAttribute(mutableService.$); 111 | 112 | this._services.push(mutableService); 113 | }; 114 | 115 | PeripheralManager.prototype.removeService = function(mutableService) { 116 | this.$('removeService', mutableService); 117 | }; 118 | 119 | PeripheralManager.prototype.removeAllServices = function() { 120 | this.$('removeAllServices'); 121 | }; 122 | 123 | PeripheralManager.prototype.respondToRequest = function(transactionId, result, value) { 124 | var $request = this._pendingRequests[transactionId]; 125 | var $value = $util.bufferTo$Data(value); 126 | 127 | $request('setValue', $value); 128 | 129 | $request('release'); 130 | 131 | delete this._pendingRequests[transactionId]; 132 | 133 | this.$('respondToRequest', $request, 'withResult', result); 134 | }; 135 | 136 | // - (void)setDesiredConnectionLatency:(CBPeripheralManagerConnectionLatency)latency forCentral:(CBCentral *)central 137 | 138 | PeripheralManager.prototype.updateValueForCharacteristic = function(characteristic, value) { 139 | var $value = $util.bufferTo$Data(value); 140 | 141 | return this.$('updateValue', $value, 'forCharacteristic', characteristic.$, 'onSubscribedCentrals', null); 142 | }; 143 | 144 | PeripheralManager.prototype._onServiceAdded = function(serviceIdentifier, error) { 145 | this._forServiceIdentifier(serviceIdentifier, function(service) { 146 | this.emit('serviceAdded', service, error); 147 | 148 | service.onAdded(error); 149 | }.bind(this)); 150 | }; 151 | 152 | PeripheralManager.prototype._onReadRequest = function($request) { 153 | var transactionId = $util.toInt($request('transactionID')); // private API 154 | var characteristicIdentifier = $util.identifierFor$MutableAttribute($request('characteristic')); 155 | var offset = $request('offset'); 156 | 157 | this._pendingRequests[transactionId] = $request; 158 | 159 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 160 | characteristic.peripheralManager = this; 161 | characteristic.onReadRequest(transactionId, offset); 162 | }.bind(this)); 163 | }; 164 | 165 | PeripheralManager.prototype._onWriteRequests = function($requests) { 166 | $util.$arrayForEach($requests, function($request) { 167 | // TODO: do all $requests have the same transaction id? 168 | var transactionId = $util.toInt($request('transactionID')); // private API 169 | var characteristicIdentifier = $util.identifierFor$MutableAttribute($request('characteristic')); 170 | var offset = $request('offset'); 171 | var value = $util.toBuffer($request('value')); 172 | var ignoreResponse = $request('ignoreResponse'); 173 | 174 | this._pendingRequests[transactionId] = $request; 175 | 176 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 177 | characteristic.peripheralManager = this; 178 | characteristic.onWriteRequest(transactionId, offset, value, ignoreResponse); 179 | }.bind(this)); 180 | }.bind(this)); 181 | 182 | $requests('release'); 183 | }; 184 | 185 | PeripheralManager.prototype._onMtuChange = function(centralIdentifier, mtu) { 186 | var address = uuidToAddress(centralIdentifier); 187 | 188 | this.emit('accept', centralIdentifier, address); 189 | this.emit('mtuChange', mtu); 190 | }; 191 | 192 | PeripheralManager.prototype._onSubscribe = function(centralIdentifier, maximumUpdateValueLength, characteristicIdentifier) { 193 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 194 | characteristic.peripheralManager = this; 195 | characteristic.onSubscribe(centralIdentifier, maximumUpdateValueLength); 196 | }.bind(this)); 197 | }; 198 | 199 | PeripheralManager.prototype._onUnsubscribe = function(centralIdentifier, characteristicIdentifier) { 200 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 201 | characteristic.onUnsubscribe(centralIdentifier); 202 | }.bind(this)); 203 | }; 204 | 205 | PeripheralManager.prototype._onValueUpdate = function(characteristicIdentifier) { 206 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 207 | characteristic.onValueUpdate(); 208 | }.bind(this)); 209 | }; 210 | 211 | PeripheralManager.prototype._forServiceIdentifier = function(serviceIdentifier, callback) { 212 | this._services.forEach(function(service) { 213 | if (service.identifier === serviceIdentifier) { 214 | callback(service); 215 | } 216 | }); 217 | }; 218 | 219 | PeripheralManager.prototype._forCharacteristicIdentifier = function(characteristicIdentifier, callback) { 220 | this._services.forEach(function(service) { 221 | if (service.characteristics) { 222 | service.characteristics.forEach(function(characteristic) { 223 | if (characteristic.identifier === characteristicIdentifier) { 224 | callback(characteristic); 225 | } 226 | }); 227 | } 228 | }); 229 | }; 230 | 231 | module.exports = PeripheralManager; 232 | -------------------------------------------------------------------------------- /lib/peripheral.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | 6 | var PeripheralDelegate = require('./peripheral-delegate'); 7 | var Service = require('./service'); 8 | var $util = require('./util'); 9 | var uuidToAddress = require('./uuid-to-address'); 10 | 11 | function Peripheral($peripheral) { 12 | this.$ = $peripheral('retain'); 13 | 14 | this.identifier = $util.identifierFor$Peripheral(this.$); 15 | this.address = uuidToAddress(this.identifier); 16 | this.delegate = new PeripheralDelegate(); 17 | 18 | this._attributes = {}; 19 | 20 | this.delegate.on('rssiUpdate', this._onRssiUpdate.bind(this)); 21 | this.delegate.on('servicesDiscover', this._onServicesDiscover.bind(this)); 22 | this.delegate.on('serviceIncludedServicesDiscover', this._onServiceIncludedServicesDiscover.bind(this)); 23 | this.delegate.on('serviceCharacteristicsDiscover', this._onServiceCharacteristicsDiscover.bind(this)); 24 | this.delegate.on('characteristicDescriptorsDiscover', this._onCharacteristicDescriptorsDiscover.bind(this)); 25 | this.delegate.on('characteristicValueUpdate', this._onCharacteristicValueUpdate.bind(this)); 26 | this.delegate.on('characteristicNotificationStateUpdate', this._onCharacteristicNotificationStateUpdate.bind(this)); 27 | this.delegate.on('characteristicValueWrite', this._onCharacteristicValueWrite.bind(this)); 28 | this.delegate.on('descriptorValueUpdate', this._onDescriptorValueUpdate.bind(this)); 29 | this.delegate.on('descriptorValueWrite', this._onDescriptorValueWrite.bind(this)); 30 | 31 | this.$('setDelegate', this.delegate.$); 32 | } 33 | 34 | util.inherits(Peripheral, events.EventEmitter); 35 | 36 | Peripheral.prototype.toString = function() { 37 | return JSON.stringify({ 38 | identifier: this.identifier, 39 | services: this.services 40 | }); 41 | }; 42 | 43 | Peripheral.prototype.connect = function() { 44 | this.centralManager.connect(this); 45 | }; 46 | 47 | Peripheral.prototype.cancelConnection = function() { 48 | this.centralManager.cancelConnection(this); 49 | }; 50 | 51 | Peripheral.prototype.readRSSI = function() { 52 | this.$('readRSSI'); 53 | }; 54 | 55 | Peripheral.prototype.discoverServices = function(serviceUUIDs) { 56 | this.$('discoverServices', $util.to$Uuids(serviceUUIDs)); 57 | }; 58 | 59 | Peripheral.prototype.discoverIncludedServicesForService = function(includedServiceUUIDs, service) { 60 | this.$('discoverIncludedServices', $util.to$Uuids(includedServiceUUIDs), 'forService', service.$); 61 | }; 62 | 63 | Peripheral.prototype.discoverCharacteristicsForService = function(characteristicUUIDs, service) { 64 | this.$('discoverCharacteristics', $util.to$Uuids(characteristicUUIDs), 'forService', service.$); 65 | }; 66 | 67 | Peripheral.prototype.discoverDescriptorsForCharacteristic = function(characteristic) { 68 | this.$('discoverDescriptorsForCharacteristic', characteristic.$); 69 | }; 70 | 71 | Peripheral.prototype.readValueForCharacteristic = function(characteristic) { 72 | this.$('readValueForCharacteristic', characteristic.$); 73 | }; 74 | 75 | Peripheral.prototype.setBroadcastValueForCharacteristic = function(value, characteristic) { 76 | this.$('setBroadcastValue', value, 'forCharacteristic', characteristic.$); 77 | }; 78 | 79 | Peripheral.prototype.setNotifyValueForCharacteristic = function(value, characteristic) { 80 | this.$('setNotifyValue', value, 'forCharacteristic', characteristic.$); 81 | }; 82 | 83 | Peripheral.prototype.writeValueForCharacteristic = function(value, characteristic, type) { 84 | this.$('writeValue', $util.bufferTo$Data(value), 'forCharacteristic', characteristic.$, 'type', type); 85 | }; 86 | 87 | Peripheral.prototype.readValueForDescriptor = function(descriptor) { 88 | this.$('readValueForDescriptor', descriptor.$); 89 | }; 90 | 91 | Peripheral.prototype.writeValueForDescriptor = function(value, descriptor) { 92 | console.log('writeValueForDescriptor ', $util.bufferTo$Data(value), descriptor.$); 93 | this.$('writeValue', $util.bufferTo$Data(value), 'forDescriptor', descriptor.$); 94 | }; 95 | 96 | Peripheral.prototype._onRssiUpdate = function(error) { 97 | var rssi; 98 | 99 | if (error === undefined) { 100 | rssi = $util.toInt(this.$('RSSI')); 101 | } 102 | 103 | this.emit('rssiUpdate', rssi, error); 104 | }; 105 | 106 | Peripheral.prototype._onServicesDiscover = function(error) { 107 | this.services = undefined; 108 | 109 | if (error === undefined) { 110 | var $services = this.$('services'); 111 | 112 | this.services = []; 113 | 114 | $util.$arrayForEach($services, function($service) { 115 | var service = new Service($service); 116 | 117 | service.peripheral = this; 118 | 119 | this.services.push(service); 120 | }.bind(this)); 121 | } 122 | 123 | this.emit('servicesDiscover', this.services, error); 124 | }; 125 | 126 | Peripheral.prototype._onServiceIncludedServicesDiscover = function(serviceIdentifier, error) { 127 | this.services.forEach(function(service) { 128 | if (service.identifier === serviceIdentifier) { 129 | service.onIncludedServicesDiscover(error); 130 | } 131 | }); 132 | }; 133 | 134 | Peripheral.prototype._onServiceCharacteristicsDiscover = function(serviceIdentifier, error) { 135 | this.services.forEach(function(service) { 136 | if (service.identifier === serviceIdentifier) { 137 | service.onCharacteristicsDiscover(error); 138 | } 139 | }); 140 | }; 141 | 142 | Peripheral.prototype._onCharacteristicDescriptorsDiscover = function(characteristicIdentifier, error) { 143 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 144 | characteristic.onDescriptorsDiscover(error); 145 | }); 146 | }; 147 | 148 | Peripheral.prototype._onCharacteristicValueUpdate = function(characteristicIdentifier, error) { 149 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 150 | characteristic.onValueUpdate(error); 151 | }); 152 | }; 153 | 154 | Peripheral.prototype._onCharacteristicValueWrite = function(characteristicIdentifier, error) { 155 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 156 | characteristic.onValueWrite(error); 157 | }); 158 | }; 159 | 160 | Peripheral.prototype._onCharacteristicNotificationStateUpdate = function(characteristicIdentifier, error) { 161 | this._forCharacteristicIdentifier(characteristicIdentifier, function(characteristic) { 162 | characteristic.onNotificationStateUpdate(error); 163 | }); 164 | }; 165 | 166 | Peripheral.prototype._onDescriptorValueUpdate = function(descriptorIdentifier, error) { 167 | this._forDescriptorIdentifier(descriptorIdentifier, function(descriptor) { 168 | descriptor.onValueUpdate(error); 169 | }); 170 | }; 171 | 172 | Peripheral.prototype._onDescriptorValueWrite = function(descriptorIdentifier, error) { 173 | this._forDescriptorIdentifier(descriptorIdentifier, function(descriptor) { 174 | descriptor.onValueWrite(error); 175 | }); 176 | }; 177 | 178 | Peripheral.prototype._forCharacteristicIdentifier = function(characteristicIdentifier, callback) { 179 | this.services.forEach(function(service) { 180 | if (service.characteristics) { 181 | service.characteristics.forEach(function(characteristic) { 182 | if (characteristic.identifier === characteristicIdentifier) { 183 | callback(characteristic); 184 | } 185 | }); 186 | } 187 | }); 188 | }; 189 | 190 | Peripheral.prototype._forDescriptorIdentifier = function(descriptorIdentifier, callback) { 191 | this.services.forEach(function(service) { 192 | if (service.characteristics) { 193 | service.characteristics.forEach(function(characteristic) { 194 | if (characteristic.descriptors) { 195 | characteristic.descriptors.forEach(function(descriptor) { 196 | if (descriptor.identifier === descriptorIdentifier) { 197 | callback(descriptor); 198 | } 199 | }); 200 | } 201 | }); 202 | } 203 | }); 204 | }; 205 | 206 | module.exports = Peripheral; 207 | -------------------------------------------------------------------------------- /lib/service.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | var $ = require('NodObjC'); 5 | 6 | var Characteristic = require('./characteristic'); 7 | var $util = require('./util'); 8 | 9 | function Serivce($service) { 10 | this.$ = $service('retain'); 11 | 12 | this.identifier = $util.identifierFor$Service(this.$); 13 | this.uuid = $util.toUuidString(this.$('UUID')); 14 | this.isPrimary = $util.toBool(this.$('isPrimary')); 15 | } 16 | 17 | util.inherits(Serivce, events.EventEmitter); 18 | 19 | Serivce.prototype.toString = function() { 20 | return JSON.stringify({ 21 | uuid: this.uuid, 22 | includedServices: this.includedServices, 23 | characteristics: this.characteristics 24 | }); 25 | }; 26 | 27 | Serivce.prototype.discoverIncludedServices = function(includedServiceUUIDs) { 28 | this.peripheral.discoverIncludedServicesForService(includedServiceUUIDs, this); 29 | }; 30 | 31 | Serivce.prototype.discoverCharacteristics = function(characteristicUUIDs) { 32 | this.peripheral.discoverCharacteristicsForService(characteristicUUIDs, this); 33 | }; 34 | 35 | Serivce.prototype.onIncludedServicesDiscover = function(error) { 36 | this.includedServices = undefined; 37 | 38 | if (error === undefined) { 39 | var $includedServices = this.$('includedServices'); 40 | 41 | this.includedServices = []; 42 | 43 | $util.$arrayForEach($includedServices, function($includedService) { 44 | var includedService = new Serivce($includedService); 45 | 46 | includedService.service = this; 47 | includedService.peripheral = this.peripheral; 48 | 49 | this.includedServices.push(includedService); 50 | }.bind(this)); 51 | } 52 | 53 | this.emit('includedServicesDiscover', this.includedServices, error); 54 | }; 55 | 56 | Serivce.prototype.onCharacteristicsDiscover = function(error) { 57 | this.characteristics = undefined; 58 | 59 | if (error === undefined) { 60 | var $characteristics = this.$('characteristics'); 61 | 62 | this.characteristics = []; 63 | 64 | $util.$arrayForEach($characteristics, function($characteristic) { 65 | var characteristic = new Characteristic($characteristic); 66 | 67 | characteristic.service = this; 68 | characteristic.peripheral = this.peripheral; 69 | 70 | this.characteristics.push(characteristic); 71 | }.bind(this)); 72 | } 73 | 74 | this.emit('characteristicsDiscover', this.characteristics, error); 75 | }; 76 | 77 | module.exports = Serivce; 78 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | var $ = require('NodObjC'); 2 | var uuid = require('uuid'); 3 | 4 | $.import('Foundation'); 5 | $.import('CoreBluetooth'); 6 | 7 | module.exports = { 8 | identifierFor$Peripheral: function($peripheral) { 9 | return $peripheral('identifier')('UUIDString').toString(); 10 | }, 11 | 12 | identifierFor$Central: function($central) { 13 | return $central('identifier')('UUIDString').toString(); 14 | }, 15 | 16 | objectForKey: function($dictionary, key) { 17 | return $dictionary('objectForKey', $(key)); 18 | }, 19 | 20 | toBool: function($number) { 21 | return $number ? $number('boolValue') : undefined; 22 | }, 23 | 24 | toInt: function($number) { 25 | return $number ? $number('intValue') : undefined; 26 | }, 27 | 28 | toString: function($string) { 29 | return $string ? $string.toString() : undefined; 30 | }, 31 | 32 | toBuffer: function($data) { 33 | var base64Data = $data('base64EncodedStringWithOptions', 0).toString(); 34 | 35 | return new Buffer(base64Data, 'base64'); 36 | }, 37 | 38 | toError: function($error) { 39 | return $error ? new Error($error('localizedDescription').toString()) : undefined; 40 | }, 41 | 42 | toUuidString: function($uuid) { 43 | return $uuid('UUIDString').toString(); 44 | }, 45 | 46 | boolTo$Number: function(bool) { 47 | return $.NSNumber('alloc')('initWithBool', bool); 48 | }, 49 | 50 | bufferTo$Data: function(buffer) { 51 | var $data = null; 52 | 53 | if (buffer) { 54 | var base64String = buffer.toString('base64'); 55 | var $base64String = $(base64String); 56 | 57 | $data = $.NSData('alloc')('initWithBase64EncodedString', $base64String, 'options', 0); 58 | } 59 | 60 | return $data; 61 | }, 62 | 63 | $arrayForEach: function($array, callback) { 64 | var $objectEnumerator = $array('objectEnumerator'); 65 | var $object; 66 | 67 | while (($object = $objectEnumerator('nextObject'))) { 68 | callback($object); 69 | } 70 | }, 71 | 72 | $dictionaryForEach: function($dictionary, callback) { 73 | var $keyEnumerator = $dictionary('keyEnumerator'); 74 | var $key; 75 | 76 | while(($key = $keyEnumerator('nextObject'))) { 77 | var $object = $dictionary('objectForKey', $key); 78 | 79 | callback($key, $object); 80 | } 81 | }, 82 | 83 | to$Uuid: function(uuid_) { 84 | uuid_ = uuid_.replace(/-/g, ''); 85 | if (uuid_.length > 4) { 86 | uuid_ = uuid.unparse(new Buffer(uuid_, 'hex')); 87 | } 88 | 89 | return $.CBUUID('alloc')('initWithString', $(uuid_)); // private API 90 | }, 91 | 92 | to$Uuids: function(uuids) { 93 | var $uuids = $.NSMutableArray('alloc')('init'); 94 | 95 | uuids = uuids || []; 96 | uuids.forEach(function(uuid_) { 97 | $uuid = this.to$Uuid(uuid_); 98 | 99 | $uuids('addObject', $uuid); 100 | }.bind(this)); 101 | 102 | return $uuids; 103 | }, 104 | 105 | $setObjectForKey: function($dictionary, $object, key) { 106 | $dictionary('setObject', $object, 'forKey', $(key)); 107 | }, 108 | 109 | identifierFor$Service: function($service) { 110 | var startHandle = this.toInt($service('startHandle')); // private API 111 | var endHandle = this.toInt($service('endHandle')); // private API 112 | 113 | return (startHandle + '-' + endHandle); 114 | }, 115 | 116 | identifierFor$Characteristic: function($characteristic) { 117 | var handle = this.toInt($characteristic('handle')); // private API 118 | var valueHandle = this.toInt($characteristic('valueHandle')); // private API 119 | 120 | return (handle + '-' + valueHandle); 121 | }, 122 | 123 | identifierFor$Descriptor: function($descriptor) { 124 | var handle = this.toInt($descriptor('handle')); // private API 125 | 126 | return (handle + ''); 127 | }, 128 | 129 | identifierFor$MutableAttribute: function($mutableAttribute) { 130 | return this.toInt($mutableAttribute('ID')); 131 | } 132 | }; 133 | -------------------------------------------------------------------------------- /lib/uuid-to-address.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var bplist = require('bplist-parser'); 4 | 5 | var PLIST_FILE = '/Library/Preferences/com.apple.Bluetooth.plist'; 6 | 7 | var coreBluetoothCache = {}; 8 | 9 | function processPlist() { 10 | bplist.parseFile(PLIST_FILE, function (err, obj) { 11 | if (err) { 12 | return; 13 | } 14 | 15 | coreBluetoothCache = obj[0].CoreBluetoothCache || {}; 16 | }); 17 | } 18 | 19 | function uuidToAddress(uuid) { 20 | var coreBluetoothCacheEntry = coreBluetoothCache[uuid]; 21 | var address = coreBluetoothCacheEntry ? coreBluetoothCacheEntry.DeviceAddress.replace(/-/g, ':') : undefined; 22 | 23 | return address; 24 | } 25 | 26 | processPlist(); 27 | fs.watch(PLIST_FILE, processPlist); 28 | 29 | module.exports = uuidToAddress; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "core-bluetooth", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jshint *.js lib/." 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/sandeepmistry/node-core-bluetooth.git" 12 | }, 13 | "keywords": [ 14 | "Core", 15 | "Bluetooth", 16 | "OS", 17 | "X" 18 | ], 19 | "author": "Sandeep Mistry ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/sandeepmistry/node-core-bluetooth/issues" 23 | }, 24 | "homepage": "https://github.com/sandeepmistry/node-core-bluetooth", 25 | "dependencies": { 26 | "bplist-parser": "^0.1.1", 27 | "debug": "^2.2.0", 28 | "ffi": "^2.0.0", 29 | "nodobjc": "^2.1.0", 30 | "re-emitter": "^1.1.1", 31 | "uuid": "^2.0.1" 32 | }, 33 | "devDependencies": { 34 | "jshint": "^2.8.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test-central.js: -------------------------------------------------------------------------------- 1 | var CentralManager = require('./index').CentralManager; 2 | var centralManager = new CentralManager(); 3 | 4 | centralManager.on('stateUpdate', function(state) { 5 | console.log('\tstateUpdate => ', state); 6 | 7 | console.log('scanForPeripherals'); 8 | centralManager.scanForPeripherals(); 9 | }); 10 | 11 | centralManager.on('peripheralDiscover', function(peripheral, advertisementData, rssi) { 12 | console.log('\tperipheralDiscover => ', peripheral.identifier, JSON.stringify(advertisementData), rssi); 13 | 14 | if (advertisementData.localName == 'test') { 15 | console.log('stopScan'); 16 | centralManager.stopScan(); 17 | 18 | console.log('peripheral connect'); 19 | peripheral.connect(); 20 | 21 | peripheral.once('connect', function() { 22 | console.log('\tperipheral connect =>', peripheral.identifier); 23 | 24 | console.log('peripheral readRSSI'); 25 | peripheral.readRSSI(); 26 | }); 27 | 28 | peripheral.once('disconnect', function(error) { 29 | console.log('\tperipheral disconnect =>', peripheral.identifier, error); 30 | 31 | process.exit(0); 32 | }); 33 | 34 | peripheral.once('connectFail', function(error) { 35 | console.log('\tperipheral connectFail =>', peripheral.identifier, error); 36 | 37 | process.exit(0); 38 | }); 39 | 40 | peripheral.once('rssiUpdate', function(rssi, error) { 41 | console.log('\tperipheral rssiUpdate =>', rssi, error); 42 | 43 | console.log('discoverServices'); 44 | peripheral.discoverServices(); 45 | }); 46 | 47 | peripheral.on('servicesDiscover', function(services, error) { 48 | console.log('\tperipheral servicesDiscover =>', services.toString(), error); 49 | 50 | var service = services[0]; 51 | 52 | console.log('discoverIncludedServices'); 53 | service.discoverIncludedServices(); 54 | 55 | service.on('includedServicesDiscover', function(includedServices, error) { 56 | console.log('\tservice includedServicesDiscover =>', includedServices.toString(), error); 57 | 58 | console.log('discoverCharacteristics'); 59 | service.discoverCharacteristics(); 60 | }); 61 | 62 | service.on('characteristicsDiscover', function(characteristics, error) { 63 | console.log('\tservice characteristicsDiscover =>', characteristics.toString(), error); 64 | 65 | var characteristic = characteristics[0]; 66 | 67 | console.log('discoverDescriptors'); 68 | characteristic.discoverDescriptors(); 69 | characteristic.on('descriptorsDiscover', function(descriptors, error) { 70 | console.log('\tcharacteristic descriptorsDiscover =>', descriptors.toString(), error); 71 | 72 | if (descriptors && descriptors.length) { 73 | var descriptor = descriptors[0]; 74 | 75 | console.log('descriptor - readValue'); 76 | descriptor.readValue(); 77 | 78 | descriptor.on('valueUpdate', function(value, error) { 79 | console.log('\tdescriptor valueUpdate =>', value.toString('hex'), error); 80 | }); 81 | 82 | // console.log('descriptor - writeValue'); 83 | // descriptor.writeValue(new Buffer('0000', 'hex')); 84 | 85 | // descriptor.on('valueWrite', function(error) { 86 | // console.log('\tdescriptor valueWrite =>', error); 87 | // }); 88 | } 89 | 90 | if (characteristic.properties & 0x02) { 91 | console.log('characteristic - readValue'); 92 | characteristic.readValue(); 93 | } else if (characteristic.properties & 0x08) { 94 | console.log('characteristic - writeValue'); 95 | characteristic.writeValue(new Buffer('hello')); 96 | } else if (characteristic.properties & 0x04) { 97 | console.log('characteristic - writeValue'); 98 | characteristic.writeValue(new Buffer('hello'), true); 99 | } else if (characteristic.properties & 0x01) { 100 | console.log('characteristic - setBroadcastValue'); 101 | characteristic.setBroadcastValue(true); 102 | } else if (characteristic.properties & (0x10 | 0x20)) { 103 | console.log('characteristic - setNotifyValue'); 104 | characteristic.setNotifyValue(true); 105 | } 106 | }); 107 | 108 | characteristic.on('valueUpdate', function(value, error) { 109 | console.log('\tcharacteristic valueUpdate =>', value.toString('hex'), error); 110 | 111 | console.log('cancelConnection'); 112 | peripheral.cancelConnection(); 113 | }); 114 | 115 | characteristic.on('valueWrite', function(error) { 116 | console.log('\tcharacteristic valueWrite =>', error); 117 | 118 | console.log('cancelConnection'); 119 | peripheral.cancelConnection(); 120 | }); 121 | 122 | characteristic.on('notificationStateUpdate', function(state, error) { 123 | console.log('\tcharacteristic notificationStateUpdate =>', state, error); 124 | }); 125 | }); 126 | }); 127 | } 128 | }); 129 | -------------------------------------------------------------------------------- /test-peripheral.js: -------------------------------------------------------------------------------- 1 | var CoreBluetooth = require('./index'); 2 | 3 | var PeripheralManager = CoreBluetooth.PeripheralManager; 4 | var MutableService = CoreBluetooth.MutableService; 5 | var MutableCharacteristic = CoreBluetooth.MutableCharacteristic; 6 | var MutableDescriptor = CoreBluetooth.MutableDescriptor; 7 | 8 | var peripheralManager = new PeripheralManager(); 9 | 10 | peripheralManager.on('address', function(address) { 11 | console.log('\taddress => ', address); 12 | }); 13 | 14 | peripheralManager.on('stateUpdate', function(state) { 15 | console.log('\tstateUpdate => ', state); 16 | 17 | console.log('startAdvertising'); 18 | peripheralManager.startAdvertising({ 19 | localName: 'test', 20 | serviceUuids: ['FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF0'] 21 | }); 22 | }); 23 | 24 | peripheralManager.on('advertisingStart', function(error) { 25 | console.log('\tadvertisingStart => ', error); 26 | 27 | var service = new MutableService( 28 | 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF0', 29 | true, 30 | [], 31 | [ 32 | new MutableCharacteristic( 33 | 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF1', 34 | 0x02, 35 | new Buffer('static value'), 36 | 0x01, 37 | [ 38 | new MutableDescriptor('2901', 'static read') 39 | ] 40 | ), 41 | new MutableCharacteristic( 42 | 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF2', 43 | 0x02, 44 | null, 45 | 0x01, 46 | [] 47 | ), 48 | new MutableCharacteristic( 49 | 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF3', 50 | 0x08, 51 | null, 52 | 0x02, 53 | [] 54 | ), 55 | new MutableCharacteristic( 56 | 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF4', 57 | 0x04, 58 | null, 59 | 0x02, 60 | [] 61 | ), 62 | new MutableCharacteristic( 63 | 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFF4', 64 | 0x10, 65 | null, 66 | 0x01, 67 | [] 68 | ) 69 | ] 70 | ); 71 | 72 | console.log('addService'); 73 | peripheralManager.addService(service); 74 | }); 75 | 76 | peripheralManager.on('serviceAdded', function(service, error) { 77 | console.log('\tserviceAdded =>', JSON.stringify(service, null, 2), error); 78 | }); 79 | 80 | peripheralManager.on('accept', function(centralIdentifier, address) { 81 | console.log('\taccept => ', centralIdentifier, address); 82 | }); 83 | 84 | peripheralManager.on('mtuChange', function(mtu) { 85 | console.log('\tmtuChange => ', mtu); 86 | }); 87 | --------------------------------------------------------------------------------