1 /* Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 /* Copyright (C) 2015 Sandeep Mistry sandeep.mistry@gmail.com
18 * Permission is hereby granted, free of charge, to any person obtaining a copy
19 * of this software and associated documentation files (the "Software"), to deal
20 * in the Software without restriction, including without limitation the rights
21 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22 * copies of the Software, and to permit persons to whom the Software is
23 * furnished to do so, subject to the following conditions:
25 * The above copyright notice and this permission notice shall be included in
26 * all copies or substantial portions of the Software.
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 var debug = console.log; //requir('debug')('ble_hci-socket_bindings');
39 var events = require('events');
40 var util = require('util');
42 var AclStream = require('ble_hci_socket_acl_stream');
43 var Hci = require('ble_hci_socket_hci');
44 var Gap = require('ble_hci_socket_gap');
45 var Gatt = require('ble_hci_socket_gatt');
47 var BlenoBindings = function() {
50 this._advertising = false;
52 this._hci = new Hci();
53 this._gap = new Gap(this._hci);
54 this._gatt = new Gatt(this._hci);
58 this._aclStream = null;
61 util.inherits(BlenoBindings, events.EventEmitter);
63 BlenoBindings.prototype.startAdvertising = function(name, serviceUuids) {
64 this._advertising = true;
66 this._gap.startAdvertising(name, serviceUuids);
69 BlenoBindings.prototype.startAdvertisingIBeacon = function(data) {
70 this._advertising = true;
72 this._gap.startAdvertisingIBeacon(data);
75 BlenoBindings.prototype.startAdvertisingWithEIRData = function(advertisementData, scanData) {
76 this._advertising = true;
78 this._gap.startAdvertisingWithEIRData(advertisementData, scanData);
81 BlenoBindings.prototype.stopAdvertising = function() {
82 this._advertising = false;
84 this._gap.stopAdvertising();
87 BlenoBindings.prototype.setServices = function(services) {
88 this._gatt.setServices(services);
90 this.emit('servicesSet');
93 BlenoBindings.prototype.disconnect = function() {
95 debug('disconnect by server');
97 this._hci.disconnect(this._handle);
101 BlenoBindings.prototype.updateRssi = function() {
103 this._hci.readRssi(this._handle);
107 BlenoBindings.prototype.init = function() {
108 this.onSigIntBinded = this.onSigInt.bind(this);
110 process.on('SIGINT', this.onSigIntBinded);
111 process.on('exit', this.onExit.bind(this));
113 this._gap.on('advertisingStart', this.onAdvertisingStart.bind(this));
114 this._gap.on('advertisingStop', this.onAdvertisingStop.bind(this));
116 this._gatt.on('mtuChange', this.onMtuChange.bind(this));
118 this._hci.on('stateChange', this.onStateChange.bind(this));
119 this._hci.on('addressChange', this.onAddressChange.bind(this));
120 this._hci.on('readLocalVersion', this.onReadLocalVersion.bind(this));
122 this._hci.on('leConnComplete', this.onLeConnComplete.bind(this));
123 this._hci.on('leConnUpdateComplete', this.onLeConnUpdateComplete.bind(this));
124 this._hci.on('rssiRead', this.onRssiRead.bind(this));
125 this._hci.on('disconnComplete', this.onDisconnComplete.bind(this));
126 this._hci.on('encryptChange', this.onEncryptChange.bind(this));
127 this._hci.on('leLtkNegReply', this.onLeLtkNegReply.bind(this));
128 this._hci.on('aclDataPkt', this.onAclDataPkt.bind(this));
130 this.emit('platform', process.platform);
135 BlenoBindings.prototype.onStateChange = function(state) {
136 if (this._state === state) {
141 if (state === 'unauthorized') {
142 console.log('bleno warning: adapter state unauthorized, please run as root or with sudo');
143 console.log(' or see README for information on running without root/sudo:');
144 console.log(' https://github.com/sandeepmistry/bleno#running-on-linux');
145 } else if (state === 'unsupported') {
146 console.log('bleno warning: adapter does not support Bluetooth Low Energy (BLE, Bluetooth Smart).');
147 console.log(' Try to run with environment variable:');
148 console.log(' [sudo] BLENO_HCI_DEVICE_ID=x node ...');
151 this.emit('stateChange', state);
154 BlenoBindings.prototype.onAddressChange = function(address) {
155 this.emit('addressChange', address);
158 BlenoBindings.prototype.onReadLocalVersion = function(hciVer, hciRev, lmpVer, manufacturer, lmpSubVer) {
159 if (manufacturer === 93) {
160 // Realtek Semiconductor Corporation
161 this._gatt.maxMtu = 23;
165 BlenoBindings.prototype.onAdvertisingStart = function(error) {
166 this.emit('advertisingStart', error);
169 BlenoBindings.prototype.onAdvertisingStop = function() {
170 this.emit('advertisingStop');
173 BlenoBindings.prototype.onLeConnComplete = function(status, handle, role, addressType, address, interval, latency, supervisionTimeout, masterClockAccuracy) {
179 this._address = address;
180 this._handle = handle;
181 this._aclStream = new AclStream(this._hci, handle, this._hci.addressType, this._hci.address, addressType, address);
182 this._gatt.setAclStream(this._aclStream);
184 this.emit('accept', address);
187 BlenoBindings.prototype.onLeConnUpdateComplete = function(handle, interval, latency, supervisionTimeout) {
191 BlenoBindings.prototype.onDisconnComplete = function(handle, reason) {
192 if (this._aclStream) {
193 this._aclStream.push(null, null);
196 var address = this._address;
198 this._address = null;
200 this._aclStream = null;
203 this.emit('disconnect', address); // TODO: use reason
206 if (this._advertising) {
207 this._gap.restartAdvertising();
211 BlenoBindings.prototype.onEncryptChange = function(handle, encrypt) {
212 if (this._handle === handle && this._aclStream) {
213 this._aclStream.pushEncrypt(encrypt);
217 BlenoBindings.prototype.onLeLtkNegReply = function(handle) {
218 if (this._handle === handle && this._aclStream) {
219 this._aclStream.pushLtkNegReply();
223 BlenoBindings.prototype.onMtuChange = function(mtu) {
224 this.emit('mtuChange', mtu);
227 BlenoBindings.prototype.onRssiRead = function(handle, rssi) {
228 this.emit('rssiUpdate', rssi);
231 BlenoBindings.prototype.onAclDataPkt = function(handle, cid, data) {
232 if (this._handle === handle && this._aclStream) {
233 this._aclStream.push(cid, data);
237 BlenoBindings.prototype.onSigInt = function() {
238 var sigIntListeners = process.listeners('SIGINT');
240 if (sigIntListeners[sigIntListeners.length - 1] === this.onSigIntBinded) {
241 // we are the last listener, so exit
242 // this will trigger onExit, and clean up
247 BlenoBindings.prototype.onExit = function() {
248 this._gap.stopAdvertising();
253 module.exports = new BlenoBindings();