diff --git a/experiments/paired-devices-test.js b/experiments/paired-devices-test.js new file mode 100644 index 00000000..48b4dc42 --- /dev/null +++ b/experiments/paired-devices-test.js @@ -0,0 +1,14 @@ +(function() { + "use strict"; + + var util = require('util'); + var DeviceINQ = require("../lib/device-inquiry.js").DeviceINQ; + var BluetoothSerialPort = require("../lib/bluetooth-serial-port.js").BluetoothSerialPort; + var serial = new BluetoothSerialPort(); + + serial.listPairedDevices(function(pairedDevices) { + pairedDevices.forEach(function(device) { + console.log(device); + }); + }) +})(); diff --git a/lib/bluetooth-serial-port.js b/lib/bluetooth-serial-port.js index c897a2a0..0a5e1554 100644 --- a/lib/bluetooth-serial-port.js +++ b/lib/bluetooth-serial-port.js @@ -40,6 +40,10 @@ util.inherits(BluetoothSerialPort, EventEmitter); exports.BluetoothSerialPort = BluetoothSerialPort; + BluetoothSerialPort.prototype.listPairedDevices = function(callback) { + this.inq.listPairedDevices(callback); + }; + BluetoothSerialPort.prototype.inquire = function() { this.inq.inquire(); }; diff --git a/src/DeviceINQ.h b/src/DeviceINQ.h index 086bc6e2..5924931f 100644 --- a/src/DeviceINQ.h +++ b/src/DeviceINQ.h @@ -48,6 +48,7 @@ class DeviceINQ : public node::ObjectWrap { static v8::Handle New(const v8::Arguments& args); static v8::Handle Inquire(const v8::Arguments& args); static v8::Handle SdpSearch(const v8::Arguments& args); + static v8::Handle ListPairedDevices(const v8::Arguments& args); }; #endif \ No newline at end of file diff --git a/src/linux/DeviceINQ.cc b/src/linux/DeviceINQ.cc index 7eb8a204..a80ad6cc 100644 --- a/src/linux/DeviceINQ.cc +++ b/src/linux/DeviceINQ.cc @@ -153,6 +153,8 @@ void DeviceINQ::Init(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "inquire", Inquire); NODE_SET_PROTOTYPE_METHOD(t, "findSerialPortChannel", SdpSearch); + NODE_SET_PROTOTYPE_METHOD(t, "listPairedDevices", ListPairedDevices); + target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); } @@ -273,3 +275,36 @@ Handle DeviceINQ::SdpSearch(const Arguments& args) { return Undefined(); } + +Handle DeviceINQ::ListPairedDevices(const Arguments& args) { + HandleScope scope; + + const char *usage = "usage: listPairedDevices(callback)"; + if (args.Length() != 1) { + return scope.Close(ThrowException(Exception::Error(String::New(usage)))); + } + + if(!args[0]->IsFunction()) { + return scope.Close(ThrowException(Exception::TypeError(String::New("First argument must be a function")))); + } + Local cb = Local::Cast(args[0]); + + Local resultArray = Array::New(0); + + // TODO: build an array of objects representing a paired device: + // ex: { + // name: 'MyBluetoothDeviceName', + // address: '12-34-56-78-90', + // services: [ + // { name: 'SPP', channel: 1 }, + // { name: 'iAP', channel: 2 } + // ] + // } + + Local argv[1] = { + resultArray + }; + cb->Call(Context::GetCurrent()->Global(), 1, argv); + + return scope.Close(Undefined()); +} diff --git a/src/osx/DeviceINQ.mm b/src/osx/DeviceINQ.mm index 223e5156..368e1779 100644 --- a/src/osx/DeviceINQ.mm +++ b/src/osx/DeviceINQ.mm @@ -33,6 +33,10 @@ } #import +#import +#import +#import +#import #import "BluetoothWorker.h" using namespace node; @@ -79,6 +83,8 @@ NODE_SET_PROTOTYPE_METHOD(t, "inquire", Inquire); NODE_SET_PROTOTYPE_METHOD(t, "findSerialPortChannel", SdpSearch); + NODE_SET_PROTOTYPE_METHOD(t, "listPairedDevices", ListPairedDevices); + target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); } @@ -185,3 +191,63 @@ return Undefined(); } + +Handle DeviceINQ::ListPairedDevices(const Arguments& args) { + HandleScope scope; + + const char *usage = "usage: listPairedDevices(callback)"; + if (args.Length() != 1) { + return scope.Close(ThrowException(Exception::Error(String::New(usage)))); + } + + if(!args[0]->IsFunction()) { + return scope.Close(ThrowException(Exception::TypeError(String::New("First argument must be a function")))); + } + Local cb = Local::Cast(args[0]); + + NSArray *pairedDevices = [IOBluetoothDevice pairedDevices]; + + Local resultArray = Array::New((int)pairedDevices.count); + + // Builds an array of objects representing a paired device: + // ex: { + // name: 'MyBluetoothDeviceName', + // address: '12-34-56-78-90', + // services: [ + // { name: 'SPP', channel: 1 }, + // { name: 'iAP', channel: 2 } + // ] + // } + for (int i = 0; i < (int)pairedDevices.count; ++i) { + IOBluetoothDevice *device = [pairedDevices objectAtIndex:i]; + + Local deviceObj = Object::New(); + + deviceObj->Set(String::NewSymbol("name"), String::New([device.nameOrAddress UTF8String])); + deviceObj->Set(String::NewSymbol("address"), String::New([device.addressString UTF8String])); + + // A device may have multiple services, so enumerate each one + Local servicesArray = Array::New((int)device.services.count); + for (int j = 0; j < (int)device.services.count; ++j) { + IOBluetoothSDPServiceRecord *service = [device.services objectAtIndex:j]; + BluetoothRFCOMMChannelID channelID; + [service getRFCOMMChannelID:&channelID]; + + Local serviceObj = Object::New(); + serviceObj->Set(String::NewSymbol("channel"), Int32::New((int)channelID)); + serviceObj->Set(String::NewSymbol("name"), [service getServiceName] ? + String::New([[service getServiceName] UTF8String]) : Undefined()); + servicesArray->Set(j, serviceObj); + } + deviceObj->Set(String::NewSymbol("services"), servicesArray); + + resultArray->Set(i, deviceObj); + } + + Local argv[1] = { + resultArray + }; + cb->Call(Context::GetCurrent()->Global(), 1, argv); + + return scope.Close(Undefined()); +} diff --git a/src/windows/DeviceINQ.cc b/src/windows/DeviceINQ.cc index 69a90f75..a9c859cb 100644 --- a/src/windows/DeviceINQ.cc +++ b/src/windows/DeviceINQ.cc @@ -120,6 +120,8 @@ void DeviceINQ::Init(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "inquire", Inquire); NODE_SET_PROTOTYPE_METHOD(t, "findSerialPortChannel", SdpSearch); + NODE_SET_PROTOTYPE_METHOD(t, "listPairedDevices", ListPairedDevices); + target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); target->Set(String::NewSymbol("DeviceINQ"), t->GetFunction()); } @@ -278,3 +280,36 @@ Handle DeviceINQ::SdpSearch(const Arguments& args) { return Undefined(); } + +Handle DeviceINQ::ListPairedDevices(const Arguments& args) { + HandleScope scope; + + const char *usage = "usage: listPairedDevices(callback)"; + if (args.Length() != 1) { + return scope.Close(ThrowException(Exception::Error(String::New(usage)))); + } + + if(!args[0]->IsFunction()) { + return scope.Close(ThrowException(Exception::TypeError(String::New("First argument must be a function")))); + } + Local cb = Local::Cast(args[0]); + + Local resultArray = Array::New(0); + + // TODO: build an array of objects representing a paired device: + // ex: { + // name: 'MyBluetoothDeviceName', + // address: '12-34-56-78-90', + // services: [ + // { name: 'SPP', channel: 1 }, + // { name: 'iAP', channel: 2 } + // ] + // } + + Local argv[1] = { + resultArray + }; + cb->Call(Context::GetCurrent()->Global(), 1, argv); + + return scope.Close(Undefined()); +}