diff --git a/README.md b/README.md index 79354cf16..44391e8c6 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,8 @@ API structure: * [Peripheral](#peripheral) * [Connect](#connect) * [_Event: Connected_](#event-connected) - * [Disconnect or cancel a pending connection](#disconnect-or-cancel-a-pending-connection) + * [Cancel a pending connection](#cancel-a-pending-connection) + * [Disconnect](#disconnect) * [_Event: Disconnected_](#event-disconnected) * [Update RSSI](#update-rssi) * [_Event: RSSI updated_](#event-rssi-updated) @@ -342,7 +343,14 @@ Some of the bluetooth devices doesn't connect seamlessly, may be because of blue peripheral.once('connect', callback); ``` -#### Disconnect or cancel a pending connection +#### Cancel a pending connection + +```javascript +peripheral.cancelConnect(); +// Will emit a 'connect' event with error +``` + +#### Disconnect ```javascript peripheral.disconnect([callback(error)]); diff --git a/lib/hci-socket/bindings.js b/lib/hci-socket/bindings.js index 1a1e2021f..40359623c 100644 --- a/lib/hci-socket/bindings.js +++ b/lib/hci-socket/bindings.js @@ -59,6 +59,12 @@ NobleBindings.prototype.disconnect = function (peripheralUuid) { this._hci.disconnect(this._handles[peripheralUuid]); }; +NobleBindings.prototype.cancelConnect = function (peripheralUuid) { + // TODO: check if it was not in the queue and only then issue cancel on hci + this._connectionQueue = this._connectionQueue.filter(c => c.id !== peripheralUuid); + this._hci.cancelConnect(this._handles[peripheralUuid]); +}; + NobleBindings.prototype.reset = function () { this._hci.reset(); }; diff --git a/lib/noble.js b/lib/noble.js index 183e11465..29d7ce122 100644 --- a/lib/noble.js +++ b/lib/noble.js @@ -229,6 +229,10 @@ Noble.prototype.onConnect = function (peripheralUuid, error) { } }; +Noble.prototype.cancelConnect = function (peripheralUuid, parameters) { + this._bindings.cancelConnect(peripheralUuid, parameters); +}; + Noble.prototype.disconnect = function (peripheralUuid) { this._bindings.disconnect(peripheralUuid); }; diff --git a/lib/peripheral.js b/lib/peripheral.js index b2f8ef701..1e0f8f660 100644 --- a/lib/peripheral.js +++ b/lib/peripheral.js @@ -56,6 +56,13 @@ Peripheral.prototype.connectAsync = function (options) { return util.promisify(callback => this.connect(options, callback))(); }; +Peripheral.prototype.cancelConnect = function (options) { + if (this.state === 'connecting') { + this.emit('connect', new Error('connection canceled!')); + this._noble.cancelConnect(this.id, options); + } +}; + const disconnect = function (callback) { if (callback) { this.once('disconnect', () => { diff --git a/test/test-noble.js b/test/test-noble.js index 6421320be..edd776349 100644 --- a/test/test-noble.js +++ b/test/test-noble.js @@ -17,6 +17,7 @@ describe('Noble', () => { on: () => {}, setScanParameters: fake.returns(null), connect: fake.returns(true), + cancelConnect: fake.returns(null), startScanning: sinon.spy(), stopScanning: sinon.spy() }; @@ -109,4 +110,16 @@ describe('Noble', () => { assert.calledWith(mockBindings.setScanParameters, interval, window); }); }); + + describe('cancelConnect', () => { + it('should delegate to binding', () => { + const peripheralUuid = 'peripheral-uuid'; + const parameters = {}; + + noble.cancelConnect(peripheralUuid, parameters); + + assert.calledOnce(mockBindings.cancelConnect); + assert.calledWith(mockBindings.cancelConnect, peripheralUuid, parameters); + }); + }); }); diff --git a/test/test-peripheral.js b/test/test-peripheral.js index ced706a2e..1f22728df 100644 --- a/test/test-peripheral.js +++ b/test/test-peripheral.js @@ -20,6 +20,7 @@ describe('Peripheral', function () { beforeEach(function () { mockNoble = { connect: sinon.spy(), + cancelConnect: fake.returns(null), disconnect: sinon.spy(), updateRssi: sinon.spy(), discoverServices: sinon.spy(), @@ -141,6 +142,28 @@ describe('Peripheral', function () { }); }); + describe('cancelConnect', () => { + it('not connecting, should resolve', async () => { + await peripheral.cancelConnect(); + + assert.notCalled(mockNoble.cancelConnect); + }); + + it('connecting, should emit connect with error', async () => { + const options = { options: true }; + const connectCallback = fake.returns(null); + + peripheral.connect(connectCallback); + peripheral.cancelConnect(options); + + assert.calledOnce(connectCallback); + assert.calledWith(connectCallback, sinon.match({ message: 'connection canceled!' })); + + assert.calledOnce(mockNoble.cancelConnect); + assert.calledWith(mockNoble.cancelConnect, peripheral.id, options); + }); + }); + describe('disconnect', function () { it('should delegate to noble', function () { peripheral.disconnect();