Update Iot.js
[platform/upstream/iotjs.git] / src / js / ble.js
1 /* Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
2  *
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
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 /* Copyright (C) 2015 Sandeep Mistry sandeep.mistry@gmail.com
17  *
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:
24  *
25  * The above copyright notice and this permission notice shall be included in
26  * all copies or substantial portions of the Software.
27  *
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
34  * SOFTWARE.
35  */
36
37 var debug = console.log; //requir('debug')('ble');
38
39 var events = require('events');
40 var util = require('util');
41
42 var UuidUtil = require('ble_uuid_util');
43
44 var PrimaryService = require('ble_primary_service');
45 var Characteristic = require('ble_characteristic');
46 var Descriptor = require('ble_descriptor');
47
48 var bindings = null;
49
50 var platform = process.platform;
51
52 if (platform === 'darwin') {
53   // `requir` is intentional errta  to avoid pre-build module dependency analyzer error
54   // The analyzer does not understand comment
55   // bindings = requir('./mac/bindings');
56 } else if (platform === 'linux' || platform === 'win32' || platform === 'android') {
57   bindings = require('ble_hci_socket_bindings');
58 } else {
59   throw new Error('Unsupported platform');
60 }
61
62 function Bleno() {
63   this.platform = 'unknown';
64   this.state = 'unknown';
65   this.address = 'unknown';
66   this.rssi = 0;
67   this.mtu = 20;
68
69   this._bindings = bindings;
70
71   this._bindings.on('stateChange', this.onStateChange.bind(this));
72   this._bindings.on('platform', this.onPlatform.bind(this));
73   this._bindings.on('addressChange', this.onAddressChange.bind(this));
74   this._bindings.on('advertisingStart', this.onAdvertisingStart.bind(this));
75   this._bindings.on('advertisingStop', this.onAdvertisingStop.bind(this));
76   this._bindings.on('servicesSet', this.onServicesSet.bind(this));
77   this._bindings.on('accept', this.onAccept.bind(this));
78   this._bindings.on('mtuChange', this.onMtuChange.bind(this));
79   this._bindings.on('disconnect', this.onDisconnect.bind(this));
80
81   this._bindings.on('rssiUpdate', this.onRssiUpdate.bind(this));
82
83   this._bindings.init();
84 }
85
86 util.inherits(Bleno, events.EventEmitter);
87
88 Bleno.prototype.PrimaryService = PrimaryService;
89 Bleno.prototype.Characteristic = Characteristic;
90 Bleno.prototype.Descriptor = Descriptor;
91
92 Bleno.prototype.onPlatform = function(platform) {
93   debug('platform ' + platform);
94
95   this.platform = platform;
96 };
97
98 Bleno.prototype.onStateChange = function(state) {
99   debug('stateChange ' + state);
100
101   this.state = state;
102
103   this.emit('stateChange', state);
104 };
105
106 Bleno.prototype.onAddressChange = function(address) {
107   debug('addressChange ' + address);
108
109   this.address = address;
110 };
111
112 Bleno.prototype.onAccept = function(clientAddress) {
113   debug('accept ' + clientAddress);
114   this.emit('accept', clientAddress);
115 };
116
117 Bleno.prototype.onMtuChange = function(mtu) {
118   debug('mtu ' + mtu);
119
120   this.mtu = mtu;
121
122   this.emit('mtuChange', mtu);
123 };
124
125 Bleno.prototype.onDisconnect = function(clientAddress) {
126   debug('disconnect' + clientAddress);
127   this.emit('disconnect', clientAddress);
128 };
129
130 Bleno.prototype.startAdvertising = function(name, serviceUuids, callback) {
131   if (this.state !== 'poweredOn') {
132     var error = new Error('Could not start advertising, state is ' + this.state + ' (not poweredOn)');
133
134     if (typeof callback === 'function') {
135       callback(error);
136     } else {
137       throw error;
138     }
139   } else {
140     if (callback) {
141       this.once('advertisingStart', callback);
142     }
143
144     var undashedServiceUuids = [];
145
146     if (serviceUuids && serviceUuids.length) {
147       for (var i = 0; i < serviceUuids.length; i++) {
148         undashedServiceUuids[i] = UuidUtil.removeDashes(serviceUuids[i]);
149       }
150     }
151
152     this._bindings.startAdvertising(name, undashedServiceUuids);
153   }
154 };
155
156 Bleno.prototype.startAdvertisingIBeacon = function(uuid, major, minor, measuredPower, callback) {
157   if (this.state !== 'poweredOn') {
158     var error = new Error('Could not start advertising, state is ' + this.state + ' (not poweredOn)');
159
160     if (typeof callback === 'function') {
161       callback(error);
162     } else {
163       throw error;
164     }
165   } else {
166     var undashedUuid =  UuidUtil.removeDashes(uuid);
167     var uuidData = new Buffer(undashedUuid, 'hex');
168     var uuidDataLength = uuidData.length;
169     var iBeaconData = new Buffer(uuidData.length + 5);
170
171     for (var i = 0; i < uuidDataLength; i++) {
172       iBeaconData[i] = uuidData[i];
173     }
174
175     iBeaconData.writeUInt16BE(major, uuidDataLength);
176     iBeaconData.writeUInt16BE(minor, uuidDataLength + 2);
177     iBeaconData.writeInt8(measuredPower, uuidDataLength + 4);
178
179     if (callback) {
180       this.once('advertisingStart', callback);
181     }
182
183     debug('iBeacon data = ' + iBeaconData.toString('hex'));
184
185     this._bindings.startAdvertisingIBeacon(iBeaconData);
186   }
187 };
188
189 Bleno.prototype.onAdvertisingStart = function(error) {
190   debug('advertisingStart: ' + error);
191
192   if (error) {
193     this.emit('advertisingStartError', error);
194   }
195
196   this.emit('advertisingStart', error);
197 };
198
199 Bleno.prototype.startAdvertisingWithEIRData = function(advertisementData, scanData, callback) {
200   if (typeof scanData === 'function') {
201     callback = scanData;
202     scanData = null;
203   }
204
205   if (this.state !== 'poweredOn') {
206     var error = new Error('Could not advertising scanning, state is ' + this.state + ' (not poweredOn)');
207
208     if (typeof callback === 'function') {
209       callback(error);
210     } else {
211       throw error;
212     }
213   } else {
214     if (callback) {
215       this.once('advertisingStart', callback);
216     }
217
218     this._bindings.startAdvertisingWithEIRData(advertisementData, scanData);
219   }
220 };
221
222 Bleno.prototype.stopAdvertising = function(callback) {
223   if (callback) {
224     this.once('advertisingStop', callback);
225   }
226   this._bindings.stopAdvertising();
227 };
228
229 Bleno.prototype.onAdvertisingStop = function() {
230   debug('advertisingStop');
231   this.emit('advertisingStop');
232 };
233
234 Bleno.prototype.setServices = function(services, callback) {
235   if (callback) {
236     this.once('servicesSet', callback);
237   }
238   this._bindings.setServices(services);
239 };
240
241 Bleno.prototype.onServicesSet = function(error) {
242   debug('servicesSet');
243
244   if (error) {
245     this.emit('servicesSetError', error);
246   }
247
248   this.emit('servicesSet', error);
249 };
250
251 Bleno.prototype.disconnect = function() {
252   debug('disconnect');
253   this._bindings.disconnect();
254 };
255
256 Bleno.prototype.updateRssi = function(callback) {
257   if (callback) {
258     this.once('rssiUpdate', function(rssi) {
259       callback(null, rssi);
260     });
261   }
262
263   this._bindings.updateRssi();
264 };
265
266 Bleno.prototype.onRssiUpdate = function(rssi) {
267   this.emit('rssiUpdate', rssi);
268 };
269
270 module.exports = new Bleno();