ble.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // (c) 2014-2016 Don Coleman
  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. /* global cordova, module */
  15. "use strict";
  16. var stringToArrayBuffer = function(str) {
  17. var ret = new Uint8Array(str.length);
  18. for (var i = 0; i < str.length; i++) {
  19. ret[i] = str.charCodeAt(i);
  20. }
  21. return ret.buffer;
  22. };
  23. var base64ToArrayBuffer = function(b64) {
  24. return stringToArrayBuffer(atob(b64));
  25. };
  26. function massageMessageNativeToJs(message) {
  27. if (message.CDVType == 'ArrayBuffer') {
  28. message = base64ToArrayBuffer(message.data);
  29. }
  30. return message;
  31. }
  32. // Cordova 3.6 doesn't unwrap ArrayBuffers in nested data structures
  33. // https://github.com/apache/cordova-js/blob/94291706945c42fd47fa632ed30f5eb811080e95/src/ios/exec.js#L107-L122
  34. function convertToNativeJS(object) {
  35. Object.keys(object).forEach(function (key) {
  36. var value = object[key];
  37. object[key] = massageMessageNativeToJs(value);
  38. if (typeof(value) === 'object') {
  39. convertToNativeJS(value);
  40. }
  41. });
  42. }
  43. // set of auto-connected device ids
  44. var autoconnected = {};
  45. module.exports = {
  46. scan: function (services, seconds, success, failure) {
  47. var successWrapper = function(peripheral) {
  48. convertToNativeJS(peripheral);
  49. success(peripheral);
  50. };
  51. cordova.exec(successWrapper, failure, 'BLE', 'scan', [services, seconds]);
  52. },
  53. startScan: function (services, success, failure) {
  54. var successWrapper = function(peripheral) {
  55. convertToNativeJS(peripheral);
  56. success(peripheral);
  57. };
  58. cordova.exec(successWrapper, failure, 'BLE', 'startScan', [services]);
  59. },
  60. stopScan: function (success, failure) {
  61. cordova.exec(success, failure, 'BLE', 'stopScan', []);
  62. },
  63. startScanWithOptions: function(services, options, success, failure) {
  64. var successWrapper = function(peripheral) {
  65. convertToNativeJS(peripheral);
  66. success(peripheral);
  67. };
  68. options = options || {};
  69. cordova.exec(successWrapper, failure, 'BLE', 'startScanWithOptions', [services, options]);
  70. },
  71. // iOS only
  72. connectedPeripheralsWithServices: function(services, success, failure) {
  73. cordova.exec(success, failure, 'BLE', 'connectedPeripheralsWithServices', [services]);
  74. },
  75. // iOS only
  76. peripheralsWithIdentifiers: function(identifiers, success, failure) {
  77. cordova.exec(success, failure, 'BLE', 'peripheralsWithIdentifiers', [identifiers]);
  78. },
  79. // Android only
  80. bondedDevices: function(success, failure) {
  81. cordova.exec(success, failure, 'BLE', 'bondedDevices', []);
  82. },
  83. // this will probably be removed
  84. list: function (success, failure) {
  85. cordova.exec(success, failure, 'BLE', 'list', []);
  86. },
  87. connect: function (device_id, success, failure) {
  88. // wrap success so nested array buffers in advertising info are handled correctly
  89. var successWrapper = function(peripheral) {
  90. convertToNativeJS(peripheral);
  91. success(peripheral);
  92. };
  93. cordova.exec(successWrapper, failure, 'BLE', 'connect', [device_id]);
  94. },
  95. autoConnect: function (deviceId, connectCallback, disconnectCallback) {
  96. var disconnectCallbackWrapper;
  97. autoconnected[deviceId] = true;
  98. // wrap connectCallback so nested array buffers in advertising info are handled correctly
  99. var connectCallbackWrapper = function(peripheral) {
  100. convertToNativeJS(peripheral);
  101. connectCallback(peripheral);
  102. };
  103. // iOS needs to reconnect on disconnect, unless ble.disconnect was called.
  104. if (cordova.platformId === 'ios') {
  105. disconnectCallbackWrapper = function(peripheral) {
  106. // let the app know the peripheral disconnected
  107. disconnectCallback(peripheral);
  108. // reconnect if we have a peripheral.id and the user didn't call disconnect
  109. if (peripheral.id && autoconnected[peripheral.id]) {
  110. cordova.exec(connectCallbackWrapper, disconnectCallbackWrapper, 'BLE', 'autoConnect', [deviceId]);
  111. }
  112. };
  113. } else { // no wrapper for Android
  114. disconnectCallbackWrapper = disconnectCallback;
  115. }
  116. cordova.exec(connectCallbackWrapper, disconnectCallbackWrapper, 'BLE', 'autoConnect', [deviceId]);
  117. },
  118. disconnect: function (device_id, success, failure) {
  119. try {
  120. delete autoconnected[device_id];
  121. } catch(e) {
  122. // ignore error
  123. }
  124. cordova.exec(success, failure, 'BLE', 'disconnect', [device_id]);
  125. },
  126. requestMtu: function (device_id, mtu, success, failure) {
  127. cordova.exec(success, failure, 'BLE', 'requestMtu', [device_id, mtu]);
  128. },
  129. refreshDeviceCache: function(deviceId, timeoutMillis, success, failure) {
  130. var successWrapper = function(peripheral) {
  131. convertToNativeJS(peripheral);
  132. success(peripheral);
  133. };
  134. cordova.exec(successWrapper, failure, 'BLE', 'refreshDeviceCache', [deviceId, timeoutMillis]);
  135. },
  136. // characteristic value comes back as ArrayBuffer in the success callback
  137. read: function (device_id, service_uuid, characteristic_uuid, success, failure) {
  138. cordova.exec(success, failure, 'BLE', 'read', [device_id, service_uuid, characteristic_uuid]);
  139. },
  140. // RSSI value comes back as an integer
  141. readRSSI: function(device_id, success, failure) {
  142. cordova.exec(success, failure, 'BLE', 'readRSSI', [device_id]);
  143. },
  144. // value must be an ArrayBuffer
  145. write: function (device_id, service_uuid, characteristic_uuid, value, success, failure) {
  146. cordova.exec(success, failure, 'BLE', 'write', [device_id, service_uuid, characteristic_uuid, value]);
  147. },
  148. // value must be an ArrayBuffer
  149. writeWithoutResponse: function (device_id, service_uuid, characteristic_uuid, value, success, failure) {
  150. cordova.exec(success, failure, 'BLE', 'writeWithoutResponse', [device_id, service_uuid, characteristic_uuid, value]);
  151. },
  152. // value must be an ArrayBuffer
  153. writeCommand: function (device_id, service_uuid, characteristic_uuid, value, success, failure) {
  154. console.log("WARNING: writeCommand is deprecated, use writeWithoutResponse");
  155. cordova.exec(success, failure, 'BLE', 'writeWithoutResponse', [device_id, service_uuid, characteristic_uuid, value]);
  156. },
  157. // success callback is called on notification
  158. notify: function (device_id, service_uuid, characteristic_uuid, success, failure) {
  159. console.log("WARNING: notify is deprecated, use startNotification");
  160. cordova.exec(success, failure, 'BLE', 'startNotification', [device_id, service_uuid, characteristic_uuid]);
  161. },
  162. // success callback is called on notification
  163. startNotification: function (device_id, service_uuid, characteristic_uuid, success, failure) {
  164. cordova.exec(success, failure, 'BLE', 'startNotification', [device_id, service_uuid, characteristic_uuid]);
  165. },
  166. // success callback is called when the descriptor 0x2902 is written
  167. stopNotification: function (device_id, service_uuid, characteristic_uuid, success, failure) {
  168. cordova.exec(success, failure, 'BLE', 'stopNotification', [device_id, service_uuid, characteristic_uuid]);
  169. },
  170. isConnected: function (device_id, success, failure) {
  171. cordova.exec(success, failure, 'BLE', 'isConnected', [device_id]);
  172. },
  173. isEnabled: function (success, failure) {
  174. cordova.exec(success, failure, 'BLE', 'isEnabled', []);
  175. },
  176. enable: function (success, failure) {
  177. cordova.exec(success, failure, "BLE", "enable", []);
  178. },
  179. showBluetoothSettings: function (success, failure) {
  180. cordova.exec(success, failure, "BLE", "showBluetoothSettings", []);
  181. },
  182. startStateNotifications: function (success, failure) {
  183. cordova.exec(success, failure, "BLE", "startStateNotifications", []);
  184. },
  185. stopStateNotifications: function (success, failure) {
  186. cordova.exec(success, failure, "BLE", "stopStateNotifications", []);
  187. }
  188. };
  189. module.exports.withPromises = {
  190. scan: module.exports.scan,
  191. startScan: module.exports.startScan,
  192. startScanWithOptions: module.exports.startScanWithOptions,
  193. connect: module.exports.connect,
  194. startNotification: module.exports.startNotification,
  195. startStateNotifications: module.exports.startStateNotifications,
  196. stopScan: function() {
  197. return new Promise(function(resolve, reject) {
  198. module.exports.stopScan(resolve, reject);
  199. });
  200. },
  201. disconnect: function(device_id) {
  202. return new Promise(function(resolve, reject) {
  203. module.exports.disconnect(device_id, resolve, reject);
  204. });
  205. },
  206. read: function(device_id, service_uuid, characteristic_uuid) {
  207. return new Promise(function(resolve, reject) {
  208. module.exports.read(device_id, service_uuid, characteristic_uuid, resolve, reject);
  209. });
  210. },
  211. write: function(device_id, service_uuid, characteristic_uuid, value) {
  212. return new Promise(function(resolve, reject) {
  213. module.exports.write(device_id, service_uuid, characteristic_uuid, value, resolve, reject);
  214. });
  215. },
  216. writeWithoutResponse: function (device_id, service_uuid, characteristic_uuid, value) {
  217. return new Promise(function(resolve, reject) {
  218. module.exports.writeWithoutResponse(device_id, service_uuid, characteristic_uuid, value, resolve, reject);
  219. });
  220. },
  221. stopNotification: function (device_id, service_uuid, characteristic_uuid) {
  222. return new Promise(function(resolve, reject) {
  223. module.exports.stopNotification(device_id, service_uuid, characteristic_uuid, resolve, reject);
  224. });
  225. },
  226. isConnected: function (device_id) {
  227. return new Promise(function(resolve, reject) {
  228. module.exports.isConnected(device_id, resolve, reject);
  229. });
  230. },
  231. isEnabled: function () {
  232. return new Promise(function(resolve, reject) {
  233. module.exports.isEnabled(resolve, reject);
  234. });
  235. },
  236. enable: function () {
  237. return new Promise(function(resolve, reject) {
  238. module.exports.enable(resolve, reject);
  239. });
  240. },
  241. showBluetoothSettings: function () {
  242. return new Promise(function(resolve, reject) {
  243. module.exports.showBluetoothSettings(resolve, reject);
  244. });
  245. },
  246. stopStateNotifications: function () {
  247. return new Promise(function(resolve, reject) {
  248. module.exports.stopStateNotifications(resolve, reject);
  249. });
  250. },
  251. readRSSI: function(device_id) {
  252. return new Promise(function(resolve, reject) {
  253. module.exports.readRSSI(device_id, resolve, reject);
  254. });
  255. }
  256. };