1. // File: easyble.js updated 160713
  2. ;(function()
  3. {
  4. // Load script used by this file.
  5. evothings.loadScript('libs/evothings/util/util.js');
  6. /**
  7. * @namespace
  8. * @description <p>Library for making BLE programming easier.</p>
  9. * <p>An all-in-one file with this library and helper libraries included is
  10. * available in file <a href=""https://github.com/evothings/evothings-libraries/blob/master/libs/evothings/easyble/easyble.dist.js>easyble.dist.js</a>. Include this file in index.html (recommended).</p>
  11. * <p>If you include <code>easyble.js</code> rather than <code>easyble.dist.js</code> it is safe practise to call function {@link evothings.scriptsLoaded}
  12. * to ensure dependent libraries are loaded before calling functions
  13. * in this library (in this case you also need to have the dependent library folders).</p>
  14. */
  15. evothings.easyble = {};
  16. /**
  17. * @namespace
  18. * @description Error string.
  19. */
  20. evothings.easyble.error = {};
  21. /**
  22. * @description BLE device already connected.
  23. */
  24. evothings.easyble.error.DEVICE_ALREADY_CONNECTED = 'EASYBLE_ERROR_DEVICE_ALREADY_CONNECTED';
  25. /**
  26. * @description BLE device was disconnected.
  27. */
  28. evothings.easyble.error.DISCONNECTED = 'EASYBLE_ERROR_DISCONNECTED';
  29. /**
  30. * @description BLE service was not found.
  31. */
  32. evothings.easyble.error.SERVICE_NOT_FOUND = 'EASYBLE_ERROR_SERVICE_NOT_FOUND';
  33. /**
  34. * @description BLE characteristic was not found.
  35. */
  36. evothings.easyble.error.CHARACTERISTIC_NOT_FOUND = 'EASYBLE_ERROR_CHARACTERISTIC_NOT_FOUND';
  37. /**
  38. * @description BLE descriptor was not found.
  39. */
  40. evothings.easyble.error.DESCRIPTOR_NOT_FOUND = 'EASYBLE_ERROR_DESCRIPTOR_NOT_FOUND';
  41. /**
  42. * @private
  43. * This variable is set "lazily", because when this script is loaded
  44. * the Base64 Cordova module may not be loaded yet.
  45. */
  46. var base64;
  47. /**
  48. * Set to true to report found devices only once,
  49. * set to false to report continuously.
  50. * @private
  51. */
  52. var reportDeviceOnce = false;
  53. /**
  54. * @private
  55. */
  56. var serviceFilter = false;
  57. /**
  58. * @private
  59. */
  60. var isScanning = false;
  61. /**
  62. * Internal properties and functions.
  63. * @private
  64. */
  65. var internal = {};
  66. /**
  67. * Internal variable used to track reading of service data.
  68. * @private
  69. */
  70. var readCounter = 0;
  71. /**
  72. * Table of discovered devices.
  73. * @private
  74. */
  75. internal.knownDevices = {};
  76. /**
  77. * Table of connected devices.
  78. * @private
  79. */
  80. internal.connectedDevices = {};
  81. /**
  82. * @description <strong>Deprecated.</strong> Set whether to report devices once or continuously during scan.
  83. * The default is to report continously.
  84. * @deprecated Use the options parameter {@link evothings.easyble.ScanOptions} in
  85. * function {@link evothings.easyble.startScan}.
  86. * @param {boolean} reportOnce - Set to true to report found devices only once.
  87. * Set to false to report continuously.
  88. * @public
  89. */
  90. evothings.easyble.reportDeviceOnce = function(reportOnce)
  91. {
  92. reportDeviceOnce = reportOnce;
  93. };
  94. /**
  95. * @description Set to an Array of UUID strings to enable filtering of devices
  96. * found by startScan().
  97. * @param services - Array of UUID strings. Set to false to disable filtering.
  98. * The default is no filtering. An empty array will cause no devices to be reported.
  99. * @public
  100. */
  101. evothings.easyble.filterDevicesByService = function(services)
  102. {
  103. serviceFilter = services;
  104. };
  105. /**
  106. * @description Called during scanning when a BLE device is found.
  107. * @callback evothings.easyble.scanCallback
  108. * @param {evothings.easyble.EasyBLEDevice} device - EasyBLE device object
  109. * found during scanning.
  110. */
  111. /**
  112. * @description This callback indicates that an operation was successful,
  113. * without specifying and additional information.
  114. * @callback evothings.easyble.emptyCallback - Callback that takes no parameters.
  115. */
  116. /**
  117. * @description This function is called when an operation fails.
  118. * @callback evothings.easyble.failCallback
  119. * @param {string} errorString - A human-readable string that
  120. * describes the error that occurred.
  121. */
  122. /**
  123. * @description Called when successfully connected to a device.
  124. * @callback evothings.easyble.connectCallback
  125. * @param {evothings.easyble.EasyBLEDevice} device - EasyBLE devices object.
  126. */
  127. /**
  128. * @description Called when services are successfully read.
  129. * @callback evothings.easyble.servicesCallback
  130. * @param {evothings.easyble.EasyBLEDevice} device - EasyBLE devices object.
  131. */
  132. /**
  133. * @description Function when data is available.
  134. * @callback evothings.easyble.dataCallback
  135. * @param {ArrayBuffer} data - The data is an array buffer.
  136. * Use an ArrayBufferView to access the data.
  137. */
  138. /**
  139. * @description Called with RSSI value.
  140. * @callback evothings.easyble.rssiCallback
  141. * @param {number} rssi - A negative integer, the signal strength in decibels.
  142. * This value may be 127 which indicates an unknown value.
  143. */
  144. /**
  145. * @typedef {Object} evothings.easyble.ScanOptions
  146. * @description Options for function {evothings.easyble.startScan}
  147. * @property {array} serviceUUIDs - Array with strings of service UUIDs
  148. * to scan for. When providing one service UUID, behaviour is the same on
  149. * Android and iOS, when providing multiple UUIDs behaviour differs between
  150. * platforms.
  151. * On iOS multiple UUIDs are scanned for using logical OR operator,
  152. * any UUID that matches any of the UUIDs adverticed by the device
  153. * will count as a match. On Android, multiple UUIDs are scanned for
  154. * using AND logic, the device must advertise all of the given UUIDs
  155. * to produce a match. Leaving out this parameter or setting it to null
  156. * will scan for all devices regardless of advertised services (default
  157. * behaviour).
  158. * @property {boolean} allowDuplicates - If true same device will be reported
  159. * repeatedly during scanning, if false it will only be reported once.
  160. * Default is true.
  161. */
  162. /**
  163. * Start scanning for devices. Note that the optional parameter serviceUUIDs
  164. * has been deprecated. Please use the options parmameter
  165. * {@link evothings.easyble.ScanOptions} instead to specify any specific
  166. * service UUID to scan for.
  167. * @param {evothings.easyble.scanCallback} success - Success function called when a
  168. * device is found.
  169. * Format: success({@link evothings.easyble.EasyBLEDevice}).
  170. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  171. * @param {evothings.easyble.ScanOptions} [options] - Object with scan options.
  172. * @public
  173. * @example
  174. * // Scan for all services.
  175. * evothings.easyble.startScan(
  176. * function(device)
  177. * {
  178. * console.log('Found device named: ' + device.name);
  179. * },
  180. * function(errorCode)
  181. * {
  182. * console.log('startScan error: ' + errorCode);
  183. * }
  184. * );
  185. *
  186. * // Scan for specific service.
  187. * evothings.easyble.startScan(
  188. * function(device)
  189. * {
  190. * console.log('Found device named: ' + device.name);
  191. * },
  192. * function(errorCode)
  193. * {
  194. * console.log('startScan error: ' + errorCode);
  195. * },
  196. * // Eddystone service UUID specified in options.
  197. * { serviceUUIDs: ['0000FEAA-0000-1000-8000-00805F9B34FB'] }
  198. * );
  199. */
  200. evothings.easyble.startScan = function(arg1, arg2, arg3, arg4)
  201. {
  202. // Stop ongoing scan.
  203. evothings.easyble.stopScan();
  204. // Clear list of found devices.
  205. internal.knownDevices = {};
  206. // Scanning parameters.
  207. var serviceUUIDs;
  208. var success;
  209. var fail;
  210. var options;
  211. var allowDuplicates = undefined;
  212. // Determine parameters.
  213. if (Array.isArray(arg1))
  214. {
  215. // First param is an array of serviceUUIDs.
  216. serviceUUIDs = arg1;
  217. success = arg2;
  218. fail = arg3;
  219. options = arg4;
  220. }
  221. else if ('function' == typeof arg1)
  222. {
  223. // First param is a function.
  224. serviceUUIDs = null;
  225. success = arg1;
  226. fail = arg2;
  227. options = arg3;
  228. }
  229. // Set options.
  230. if (options)
  231. {
  232. if (Array.isArray(options.serviceUUIDs))
  233. {
  234. serviceUUIDs = options.serviceUUIDs;
  235. }
  236. if (options.allowDuplicates === true)
  237. {
  238. allowDuplicates = true;
  239. }
  240. else if (options.allowDuplicates === false)
  241. {
  242. allowDuplicates = false;
  243. }
  244. }
  245. // Start scanning.
  246. isScanning = true;
  247. if (Array.isArray(serviceUUIDs))
  248. {
  249. evothings.ble.startScan(serviceUUIDs, onDeviceFound, onError);
  250. }
  251. else
  252. {
  253. evothings.ble.startScan(onDeviceFound, onError);
  254. }
  255. function onDeviceFound(device)
  256. {
  257. // Don't report devices unless the isScanning flag is true.
  258. // This is to prevent devices being reported after stopScanning
  259. // has been called (this can happen since scanning does not stop
  260. // instantly when evothings.ble.stopScan is called).
  261. if (!isScanning) return;
  262. // Ensure we have advertisementData.
  263. internal.ensureAdvertisementData(device);
  264. // Check if the device matches the filter, if we have a filter.
  265. if (!internal.deviceMatchesServiceFilter(device))
  266. {
  267. return;
  268. }
  269. // Check if we already have got the device.
  270. var existingDevice = internal.knownDevices[device.address]
  271. if (existingDevice)
  272. {
  273. // Do not report device again if flag is set.
  274. if (allowDuplicates === false || reportDeviceOnce === true) { return; }
  275. // Duplicates allowed, report device again.
  276. existingDevice.rssi = device.rssi;
  277. existingDevice.name = device.name;
  278. existingDevice.scanRecord = device.scanRecord;
  279. existingDevice.advertisementData = device.advertisementData;
  280. success(existingDevice);
  281. return;
  282. }
  283. // New device, add to known devices.
  284. internal.knownDevices[device.address] = device;
  285. // Set connect status.
  286. device.__isConnected = false;
  287. // Add methods to the device info object.
  288. internal.addMethodsToDeviceObject(device);
  289. // Call callback function with device info.
  290. success(device);
  291. }
  292. function onError(errorCode)
  293. {
  294. fail(errorCode);
  295. }
  296. };
  297. /**
  298. * Stop scanning for devices.
  299. * @example
  300. * evothings.easyble.stopScan();
  301. */
  302. evothings.easyble.stopScan = function()
  303. {
  304. isScanning = false;
  305. evothings.ble.stopScan();
  306. };
  307. /**
  308. * Disconnect and close all connected BLE devices.
  309. * @example
  310. * evothings.easyble.closeConnectedDevices();
  311. */
  312. evothings.easyble.closeConnectedDevices = function()
  313. {
  314. for (var key in internal.connectedDevices)
  315. {
  316. var device = internal.connectedDevices[key];
  317. device && device.close();
  318. internal.connectedDevices[key] = null;
  319. }
  320. };
  321. /**
  322. * If device already has advertisementData, does nothing.
  323. * If device instead has scanRecord, creates advertisementData.
  324. * See ble.js for AdvertisementData reference.
  325. * @param device - Device object.
  326. * @private
  327. */
  328. internal.ensureAdvertisementData = function(device)
  329. {
  330. if (!base64) { base64 = cordova.require('cordova/base64'); }
  331. // If device object already has advertisementData we
  332. // do not need to parse the scanRecord.
  333. if (device.advertisementData) { return; }
  334. // Must have scanRecord yo continue.
  335. if (!device.scanRecord) { return; }
  336. // Here we parse BLE/GAP Scan Response Data.
  337. // See the Bluetooth Specification, v4.0, Volume 3, Part C, Section 11,
  338. // for details.
  339. var byteArray = evothings.util.base64DecToArr(device.scanRecord);
  340. var pos = 0;
  341. var advertisementData = {};
  342. var serviceUUIDs;
  343. var serviceData;
  344. // The scan record is a list of structures.
  345. // Each structure has a length byte, a type byte, and (length-1) data bytes.
  346. // The format of the data bytes depends on the type.
  347. // Malformed scanRecords will likely cause an exception in this function.
  348. while (pos < byteArray.length)
  349. {
  350. var length = byteArray[pos++];
  351. if (length == 0)
  352. {
  353. break;
  354. }
  355. length -= 1;
  356. var type = byteArray[pos++];
  357. // Parse types we know and care about.
  358. // Skip other types.
  359. var BLUETOOTH_BASE_UUID = '-0000-1000-8000-00805f9b34fb'
  360. // Convert 16-byte Uint8Array to RFC-4122-formatted UUID.
  361. function arrayToUUID(array, offset)
  362. {
  363. var k=0;
  364. var string = '';
  365. var UUID_format = [4, 2, 2, 2, 6];
  366. for (var l=0; l<UUID_format.length; l++)
  367. {
  368. if (l != 0)
  369. {
  370. string += '-';
  371. }
  372. for (var j=0; j<UUID_format[l]; j++, k++)
  373. {
  374. string += evothings.util.toHexString(array[offset+k], 1);
  375. }
  376. }
  377. return string;
  378. }
  379. if (type == 0x02 || type == 0x03) // 16-bit Service Class UUIDs.
  380. {
  381. serviceUUIDs = serviceUUIDs ? serviceUUIDs : [];
  382. for(var i=0; i<length; i+=2)
  383. {
  384. serviceUUIDs.push(
  385. '0000' +
  386. evothings.util.toHexString(
  387. evothings.util.littleEndianToUint16(byteArray, pos + i),
  388. 2) +
  389. BLUETOOTH_BASE_UUID);
  390. }
  391. }
  392. if (type == 0x04 || type == 0x05) // 32-bit Service Class UUIDs.
  393. {
  394. serviceUUIDs = serviceUUIDs ? serviceUUIDs : [];
  395. for (var i=0; i<length; i+=4)
  396. {
  397. serviceUUIDs.push(
  398. evothings.util.toHexString(
  399. evothings.util.littleEndianToUint32(byteArray, pos + i),
  400. 4) +
  401. BLUETOOTH_BASE_UUID);
  402. }
  403. }
  404. if (type == 0x06 || type == 0x07) // 128-bit Service Class UUIDs.
  405. {
  406. serviceUUIDs = serviceUUIDs ? serviceUUIDs : [];
  407. for (var i=0; i<length; i+=16)
  408. {
  409. serviceUUIDs.push(arrayToUUID(byteArray, pos + i));
  410. }
  411. }
  412. if (type == 0x08 || type == 0x09) // Local Name.
  413. {
  414. advertisementData.kCBAdvDataLocalName = evothings.ble.fromUtf8(
  415. new Uint8Array(byteArray.buffer, pos, length));
  416. }
  417. if (type == 0x0a) // TX Power Level.
  418. {
  419. advertisementData.kCBAdvDataTxPowerLevel =
  420. evothings.util.littleEndianToInt8(byteArray, pos);
  421. }
  422. if (type == 0x16) // Service Data, 16-bit UUID.
  423. {
  424. serviceData = serviceData ? serviceData : {};
  425. var uuid =
  426. '0000' +
  427. evothings.util.toHexString(
  428. evothings.util.littleEndianToUint16(byteArray, pos),
  429. 2) +
  430. BLUETOOTH_BASE_UUID;
  431. var data = new Uint8Array(byteArray.buffer, pos+2, length-2);
  432. serviceData[uuid] = base64.fromArrayBuffer(data);
  433. }
  434. if (type == 0x20) // Service Data, 32-bit UUID.
  435. {
  436. serviceData = serviceData ? serviceData : {};
  437. var uuid =
  438. evothings.util.toHexString(
  439. evothings.util.littleEndianToUint32(byteArray, pos),
  440. 4) +
  441. BLUETOOTH_BASE_UUID;
  442. var data = new Uint8Array(byteArray.buffer, pos+4, length-4);
  443. serviceData[uuid] = base64.fromArrayBuffer(data);
  444. }
  445. if (type == 0x21) // Service Data, 128-bit UUID.
  446. {
  447. serviceData = serviceData ? serviceData : {};
  448. var uuid = arrayToUUID(byteArray, pos);
  449. var data = new Uint8Array(byteArray.buffer, pos+16, length-16);
  450. serviceData[uuid] = base64.fromArrayBuffer(data);
  451. }
  452. if (type == 0xff) // Manufacturer-specific Data.
  453. {
  454. // Annoying to have to transform base64 back and forth,
  455. // but it has to be done in order to maintain the API.
  456. advertisementData.kCBAdvDataManufacturerData =
  457. base64.fromArrayBuffer(new Uint8Array(byteArray.buffer, pos, length));
  458. }
  459. pos += length;
  460. }
  461. advertisementData.kCBAdvDataServiceUUIDs = serviceUUIDs;
  462. advertisementData.kCBAdvDataServiceData = serviceData;
  463. device.advertisementData = advertisementData;
  464. /*
  465. // Log raw data for debugging purposes.
  466. console.log("scanRecord: "+evothings.util.typedArrayToHexString(byteArray));
  467. console.log(JSON.stringify(advertisementData));
  468. */
  469. }
  470. /**
  471. * Returns true if the device matches the serviceFilter, or if there is no filter.
  472. * Returns false otherwise.
  473. * @private
  474. */
  475. internal.deviceMatchesServiceFilter = function(device)
  476. {
  477. if (!serviceFilter) { return true; }
  478. var advertisementData = device.advertisementData;
  479. if (advertisementData)
  480. {
  481. var serviceUUIDs = advertisementData.kCBAdvDataServiceUUIDs;
  482. if (serviceUUIDs)
  483. {
  484. for (var i in serviceUUIDs)
  485. {
  486. for (var j in serviceFilter)
  487. {
  488. if (serviceUUIDs[i].toLowerCase() ==
  489. serviceFilter[j].toLowerCase())
  490. {
  491. return true;
  492. }
  493. }
  494. }
  495. }
  496. }
  497. return false;
  498. }
  499. /**
  500. * Add functions to the device object to allow calling them
  501. * in an object-oriented style.
  502. * @private
  503. */
  504. internal.addMethodsToDeviceObject = function(deviceObject)
  505. {
  506. /**
  507. * @namespace
  508. * @alias evothings.easyble.EasyBLEDevice
  509. * @description This is the BLE DeviceInfo object obtained from the
  510. * underlying Cordova plugin.
  511. * @property {string} address - Uniquely identifies the device.
  512. * The form of the address depends on the host platform.
  513. * @property {number} rssi - A negative integer, the signal strength in decibels.
  514. * @property {string} name - The device's name, or nil.
  515. * @property {string} scanRecord - Base64-encoded binary data. Its meaning is
  516. * device-specific. Not available on iOS.
  517. * @property {evothings.easyble.AdvertisementData} advertisementData -
  518. * Object containing some of the data from the scanRecord.
  519. */
  520. var device = deviceObject;
  521. /**
  522. * @typedef {Object} evothings.easyble.AdvertisementData
  523. * @description Information extracted from a scanRecord. Some or all of the fields may be
  524. * undefined. This varies between BLE devices.
  525. * Depending on OS version and BLE device, additional fields, not documented
  526. * here, may be present.
  527. * @property {string} kCBAdvDataLocalName - The device's name. Use this field
  528. * rather than device.name, since on iOS the device name is cached and changes
  529. * are not reflected in device.name.
  530. * @property {number} kCBAdvDataTxPowerLevel - Transmission power level as
  531. * advertised by the device.
  532. * @property {boolean} kCBAdvDataIsConnectable - True if the device accepts
  533. * connections. False if it doesn't.
  534. * @property {array} kCBAdvDataServiceUUIDs - Array of strings, the UUIDs of
  535. * services advertised by the device. Formatted according to RFC 4122,
  536. * all lowercase.
  537. * @property {object} kCBAdvDataServiceData - Dictionary of strings to strings.
  538. * The keys are service UUIDs. The values are base-64-encoded binary data.
  539. * @property {string} kCBAdvDataManufacturerData - Base-64-encoded binary data.
  540. * This field is used by BLE devices to advertise custom data that don't fit
  541. * into any of the other fields.
  542. */
  543. /**
  544. * Get device name. If there is a device name present in
  545. * advertisement data, this is returned. Otherwise the value of
  546. * the device.name field is returned. Note that iOS caches the
  547. * device.name field, but not the name in advertisement data.
  548. * If you change name of the device, it is more reliable to use
  549. * the field in advertisement data (this name is set in the device
  550. * firmware code).
  551. * @return Name of the device.
  552. * @public
  553. * @instance
  554. * @example
  555. * var name = device.getName();
  556. */
  557. device.getName = function()
  558. {
  559. // If there is a device name present in advertisement data,
  560. // check if this matches. (This name is not cached by iOS.)
  561. var deviceName = device.advertisementData ?
  562. device.advertisementData.kCBAdvDataLocalName : false;
  563. if (deviceName)
  564. {
  565. return deviceName;
  566. }
  567. else
  568. {
  569. return device.name;
  570. }
  571. };
  572. /**
  573. * Match device name. First checks the device name present in
  574. * advertisement data, if not present checks device.name field.
  575. * @param name The name to match.
  576. * @return true if device has the given name, false if not.
  577. * @public
  578. * @instance
  579. * @example
  580. * device.hasName('MyBLEDevice');
  581. */
  582. device.hasName = function(name)
  583. {
  584. // If there is a device name present in advertisement data,
  585. // check if this matches. (This name is not cached by iOS.)
  586. var deviceName = device.advertisementData ?
  587. device.advertisementData.kCBAdvDataLocalName : false;
  588. if (deviceName)
  589. {
  590. // TODO: This should be a comparison, but there has been
  591. // instances of the kCBAdvDataLocalName field ending with
  592. // a non-printable character, using indexOf is a quick
  593. // fix for this.
  594. return 0 == deviceName.indexOf(name);
  595. }
  596. // Otherwise check if device.name matches (cached by iOS,
  597. // might not match if device name is updated).
  598. return name == device.name;
  599. };
  600. /**
  601. * Connect to the device.
  602. * @param {evothings.easyble.connectCallback} success -
  603. * Called when connected: success(device).
  604. * @param {evothings.easyble.failCallback} fail -
  605. * Called on error and if a disconnect happens.
  606. * Format: error(errorMessage)
  607. * @public
  608. * @instance
  609. * @example
  610. * device.connect(
  611. * function(device)
  612. * {
  613. * console.log('device connected.');
  614. * // Read services here.
  615. * },
  616. * function(errorCode)
  617. * {
  618. * console.log('connect error: ' + errorCode);
  619. * });
  620. */
  621. device.connect = function(success, fail)
  622. {
  623. internal.connectToDevice(device, success, fail);
  624. };
  625. /**
  626. * Check if device is connected.
  627. * @return true if connected, false if not connected.
  628. * @public
  629. * @instance
  630. * @example
  631. * var connected = device.isConnected();
  632. */
  633. device.isConnected = function()
  634. {
  635. return device.__isConnected;
  636. };
  637. /**
  638. * Close the device. This disconnects from the BLE device.
  639. * @public
  640. * @instance
  641. * @example
  642. * device.close();
  643. */
  644. device.close = function()
  645. {
  646. if (device.deviceHandle)
  647. {
  648. device.__isConnected = false;
  649. evothings.ble.close(device.deviceHandle);
  650. }
  651. };
  652. /**
  653. * Read devices RSSI. Device must be connected.
  654. * @param {evothings.easyble.rssiCallback} success - Callback called with
  655. * RSSI value: success(rssi).
  656. * @param {evothings.easyble.failCallback} fail - Called on error: fail(error).
  657. * @public
  658. * @instance
  659. */
  660. device.readRSSI = function(success, fail)
  661. {
  662. evothings.ble.rssi(device.deviceHandle, success, fail);
  663. };
  664. /**
  665. * @typedef {Object} evothings.easyble.ReadServicesOptions
  666. * @description Options object for function
  667. * {@link evothings.easyble.EasyBLEDevice#readServices}
  668. * @property {array} serviceUUIDs - Array of service UUID strings.
  669. */
  670. /**
  671. * Read services, characteristics and descriptors for the
  672. * specified service UUIDs.
  673. * <strong>Services must be read be able to access characteristics and
  674. * descriptors</strong>. Call this function before reading and writing
  675. * characteristics/descriptors. (This function took an array of service
  676. * UUIDs as first parameter in previous versions of this library, that
  677. * is still supported for backwards compatibility but has ben deprecated.)
  678. * @param {evothings.easyble.servicesCallback} success -
  679. * Called when services are read: success(device)
  680. * @param {evothings.easyble.failCallback} fail - error callback:
  681. * error(errorMessage)
  682. * @param {evothings.easyble.ReadServicesOptions} [options] - Optional
  683. * object with setting that allow specification of which services to
  684. * read. If left out, all services and related characteristics and
  685. * descriptors are read (this can be time-consuming compared to
  686. * reading selected services).
  687. * @public
  688. * @instance
  689. * @example
  690. * // Read all services
  691. * device.readServices(
  692. * function(device)
  693. * {
  694. * console.log('Services available.');
  695. * // Read/write/enable notifications here.
  696. * },
  697. * function(errorCode)
  698. * {
  699. * console.log('readServices error: ' + errorCode);
  700. * });
  701. *
  702. * // Read specific service
  703. * device.readServices(
  704. * function(device)
  705. * {
  706. * console.log('Services available.');
  707. * // Read/write/enable notifications here.
  708. * },
  709. * function(errorCode)
  710. * {
  711. * console.log('readServices error: ' + errorCode);
  712. * },
  713. * { serviceUUIDs: ['19b10000-e8f2-537e-4f6c-d104768a1214'] });
  714. */
  715. device.readServices = function(arg1, arg2, arg3, arg4)
  716. {
  717. // Parameters.
  718. var serviceUUIDs;
  719. var success;
  720. var fail;
  721. var options;
  722. // For backwards compatibility when first arg specified
  723. // an array of service UUIDs.
  724. if (Array.isArray(arg1))
  725. {
  726. serviceUUIDs = arg1;
  727. success = arg2;
  728. fail = arg3;
  729. options = arg4;
  730. }
  731. // Previously you could set first param to null to read all services.
  732. // Here we handle this case for backwards compatibility.
  733. else if (arg1 === undefined || arg1 === null)
  734. {
  735. serviceUUIDs = null;
  736. success = arg2;
  737. fail = arg3;
  738. options = arg4;
  739. }
  740. else
  741. {
  742. success = arg1;
  743. fail = arg2;
  744. options = arg3;
  745. }
  746. if (options && Array.isArray(options.serviceUUIDs))
  747. {
  748. serviceUUIDs = options.serviceUUIDs;
  749. }
  750. internal.readServices(device, serviceUUIDs, success, fail);
  751. };
  752. /**
  753. * Read value of characteristic.
  754. * @param {string} serviceUUID - UUID of service that has the given
  755. * characteristic (previous versions of this library allowed leaving out
  756. * the service UUID, this is unsafe practice and has been deprecated, always
  757. * specify the service UUID).
  758. * @param {string} characteristicUUID - UUID of characteristic to read.
  759. * @param {evothings.easyble.dataCallback} success - Success callback:
  760. * success(data).
  761. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  762. * @public
  763. * @instance
  764. * @example
  765. * device.readCharacteristic(
  766. * serviceUUID,
  767. * characteristicUUID,
  768. * function(data)
  769. * {
  770. * console.log('characteristic data: ' + evothings.ble.fromUtf8(data));
  771. * },
  772. * function(errorCode)
  773. * {
  774. * console.log('readCharacteristic error: ' + errorCode);
  775. * });
  776. */
  777. device.readCharacteristic = function(arg1, arg2, arg3, arg4)
  778. {
  779. if ('function' == typeof arg2)
  780. {
  781. // Service UUID is missing.
  782. internal.readCharacteristic(device, arg1, arg2, arg3);
  783. }
  784. else
  785. {
  786. // Service UUID is present.
  787. internal.readServiceCharacteristic(device, arg1, arg2, arg3, arg4);
  788. }
  789. };
  790. /**
  791. * <strong>Deprecated</strong>.
  792. * Use function {@link evothings.easyble.EasyBLEDevice#readCharacteristic}
  793. * @deprecated
  794. * @instance
  795. */
  796. device.readServiceCharacteristic = function(
  797. serviceUUID, characteristicUUID, success, fail)
  798. {
  799. internal.readServiceCharacteristic(
  800. device, serviceUUID, characteristicUUID, success, fail);
  801. };
  802. /**
  803. * Read value of descriptor.
  804. * @param {string} serviceUUID - UUID of service that has the given
  805. * characteristic (previous versions of this library allowed leaving out
  806. * the service UUID, this is unsafe practice and has been deprecated, always
  807. * specify the service UUID).
  808. * @param {string} characteristicUUID - UUID of characteristic for descriptor.
  809. * @param {string} descriptorUUID - UUID of descriptor to read.
  810. * @param {evothings.easyble.dataCallback} success - Success callback:
  811. * success(data).
  812. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  813. * @public
  814. * @instance
  815. * @example
  816. * device.readDescriptor(
  817. * serviceUUID,
  818. * characteristicUUID,
  819. * descriptorUUID,
  820. * function(data)
  821. * {
  822. * console.log('descriptor data: ' + evothings.ble.fromUtf8(data));
  823. * },
  824. * function(errorCode)
  825. * {
  826. * console.log('readDescriptor error: ' + errorCode);
  827. * });
  828. */
  829. device.readDescriptor = function(arg1, arg2, arg3, arg4, arg5)
  830. {
  831. if ('function' == typeof arg3)
  832. {
  833. // Service UUID is missing.
  834. internal.readDescriptor(device, arg1, arg2, arg3, arg4);
  835. }
  836. else
  837. {
  838. // Service UUID is present.
  839. internal.readServiceDescriptor(device, arg1, arg2, arg3, arg4, arg5);
  840. }
  841. };
  842. /**
  843. * <strong>Deprecated</strong>.
  844. * Use function {@link evothings.easyble.EasyBLEDevice#readDescriptor}
  845. * @deprecated
  846. * @instance
  847. */
  848. device.readServiceDescriptor = function(
  849. serviceUUID, characteristicUUID, descriptorUUID, success, fail)
  850. {
  851. internal.readServiceDescriptor(
  852. device, serviceUUID, characteristicUUID, descriptorUUID, success, fail);
  853. };
  854. /**
  855. * Write value of characteristic.
  856. * @param {string} serviceUUID - UUID of service that has the given
  857. * characteristic (previous versions of this library allowed leaving out
  858. * the service UUID, this is unsafe practice and has been deprecated, always
  859. * specify the service UUID).
  860. * @param {string} characteristicUUID - UUID of characteristic to write to.
  861. * @param {ArrayBufferView} value - Value to write.
  862. * @param {evothings.easyble.emptyCallback} success - Success callback: success().
  863. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  864. * @public
  865. * @instance
  866. * @example
  867. * device.writeCharacteristic(
  868. * serviceUUID,
  869. * characteristicUUID,
  870. * new Uint8Array([1]), // Write byte with value 1.
  871. * function()
  872. * {
  873. * console.log('characteristic written.');
  874. * },
  875. * function(errorCode)
  876. * {
  877. * console.log('writeCharacteristic error: ' + errorCode);
  878. * });
  879. */
  880. device.writeCharacteristic = function(arg1, arg2, arg3, arg4, arg5)
  881. {
  882. if ('function' == typeof arg3)
  883. {
  884. // Service UUID is missing.
  885. internal.writeCharacteristic(device, arg1, arg2, arg3, arg4);
  886. }
  887. else
  888. {
  889. // Service UUID is present.
  890. internal.writeServiceCharacteristic(device, arg1, arg2, arg3, arg4, arg5);
  891. }
  892. };
  893. /**
  894. * <strong>Deprecated</strong>.
  895. * Use function {@link evothings.easyble.EasyBLEDevice#writeCharacteristic}
  896. * @deprecated
  897. * @instance
  898. */
  899. device.writeServiceCharacteristic = function(
  900. serviceUUID, characteristicUUID, value, success, fail)
  901. {
  902. internal.writeServiceCharacteristic(
  903. device, serviceUUID, characteristicUUID, value, success, fail);
  904. };
  905. /**
  906. * Write value of a characteristic for a specific service without response.
  907. * This faster but not as fail safe as writing with response.
  908. * Asks the remote device to NOT send a confirmation message.
  909. * Experimental, implemented on Android.
  910. * @param {string} serviceUUID - UUID of service that has the characteristic.
  911. * @param {string} characteristicUUID - UUID of characteristic to write to.
  912. * @param {ArrayBufferView} value - Value to write.
  913. * @param {evothings.easyble.emptyCallback} success - Success callback: success().
  914. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  915. * @public
  916. * @instance
  917. * @example
  918. * device.writeCharacteristicWithoutResponse(
  919. * serviceUUID,
  920. * characteristicUUID,
  921. * new Uint8Array([1]), // Write byte with value 1.
  922. * function()
  923. * {
  924. * console.log('data sent.');
  925. * },
  926. * function(errorCode)
  927. * {
  928. * console.log('writeCharacteristicWithoutResponse error: ' + errorCode);
  929. * });
  930. */
  931. device.writeCharacteristicWithoutResponse = function(
  932. serviceUUID, characteristicUUID, value, success, fail)
  933. {
  934. internal.writeServiceCharacteristicWithoutResponse(
  935. device, serviceUUID, characteristicUUID, value, success, fail);
  936. };
  937. /**
  938. * <strong>Deprecated</strong>.
  939. * Use function {@link evothings.easyble.EasyBLEDevice#writeCharacteristicWithoutResponse}
  940. * @deprecated
  941. * @instance
  942. */
  943. device.writeServiceCharacteristicWithoutResponse = function(
  944. serviceUUID, characteristicUUID, value, success, fail)
  945. {
  946. internal.writeServiceCharacteristicWithoutResponse(
  947. device, serviceUUID, characteristicUUID, value, success, fail);
  948. };
  949. /**
  950. * Write value of descriptor.
  951. * @param {string} serviceUUID - UUID of service that has the given
  952. * characteristic (previous versions of this library allowed leaving out
  953. * the service UUID, this is unsafe practice and has been deprecated, always
  954. * specify the service UUID).
  955. * @param {string} characteristicUUID - UUID of characteristic for descriptor.
  956. * @param {string} descriptorUUID - UUID of descriptor to write to.
  957. * @param {ArrayBufferView} value - Value to write.
  958. * @param {evothings.easyble.emptyCallback} success - Success callback: success().
  959. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  960. * @public
  961. * @instance
  962. * @example
  963. * device.writeDescriptor(
  964. * serviceUUID,
  965. * characteristicUUID,
  966. * descriptorUUID,
  967. * new Uint8Array([1]), // Write byte with value 1.
  968. * function()
  969. * {
  970. * console.log('descriptor written.');
  971. * },
  972. * function(errorCode)
  973. * {
  974. * console.log('writeDescriptor error: ' + errorCode);
  975. * });
  976. */
  977. device.writeDescriptor = function(arg1, arg2, arg3, arg4, arg5, arg6)
  978. {
  979. if ('function' == typeof arg4)
  980. {
  981. // Service UUID is missing.
  982. internal.writeDescriptor(device, arg1, arg2, arg3, arg4, arg5);
  983. }
  984. else
  985. {
  986. // Service UUID is present.
  987. internal.writeServiceDescriptor(device, arg1, arg2, arg3, arg4, arg5, arg6);
  988. }
  989. };
  990. /**
  991. * <strong>Deprecated</strong>.
  992. * Use function {@link evothings.easyble.EasyBLEDevice#writeDescriptor}
  993. * @deprecated
  994. * @instance
  995. */
  996. device.writeServiceDescriptor = function(
  997. serviceUUID, characteristicUUID, descriptorUUID, value, success, fail)
  998. {
  999. internal.writeServiceDescriptor(
  1000. device,
  1001. serviceUUID,
  1002. characteristicUUID,
  1003. descriptorUUID,
  1004. value,
  1005. success,
  1006. fail);
  1007. };
  1008. /**
  1009. * @typedef {Object} evothings.easyble.NotificationOptions
  1010. * @description Options object for functions
  1011. * {@link evothings.easyble.EasyBLEDevice#enableNotification}
  1012. * and {@link evothings.easyble.EasyBLEDevice#disableNotification}.
  1013. * @property {boolean} writeConfigDescriptor - Supported on Android, ignored on iOS.
  1014. * Set to false to disable automatic writing of notification or indication
  1015. * config descriptor value. This is useful in special cases when full control
  1016. * of writing the config descriptor is needed.
  1017. */
  1018. /**
  1019. * Subscribe to value updates of a characteristic.
  1020. * The success function will be called repeatedly whenever there
  1021. * is new data available.
  1022. * <p>On Android you can disable automatic write of notify/indicate and write
  1023. * the configuration descriptor yourself, supply an options object as
  1024. * last parameter, see example below.</p>
  1025. * @param {string} serviceUUID - UUID of service that has the given
  1026. * characteristic (previous versions of this library allowed leaving out
  1027. * the service UUID, this is unsafe practice and has been deprecated, always
  1028. * specify the service UUID).
  1029. * @param {string} characteristicUUID - UUID of characteristic to subscribe to.
  1030. * @param {evothings.easyble.dataCallback} success - Success callback:
  1031. * success(data).
  1032. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error).
  1033. * @param {evothings.easyble.NotificationOptions} [options] - Optional settings.
  1034. * @public
  1035. * @instance
  1036. * @example
  1037. * // Example call:
  1038. * device.enableNotification(
  1039. * serviceUUID,
  1040. * characteristicUUID,
  1041. * function(data)
  1042. * {
  1043. * console.log('characteristic data: ' + evothings.ble.fromUtf8(data));
  1044. * },
  1045. * function(errorCode)
  1046. * {
  1047. * console.log('enableNotification error: ' + errorCode);
  1048. * });
  1049. *
  1050. * // Turn off automatic writing of the config descriptor (for special cases):
  1051. * device.enableNotification(
  1052. * serviceUUID,
  1053. * characteristicUUID,
  1054. * function(data)
  1055. * {
  1056. * console.log('characteristic data: ' + evothings.ble.fromUtf8(data));
  1057. * },
  1058. * function(errorCode)
  1059. * {
  1060. * console.log('enableNotification error: ' + errorCode);
  1061. * },
  1062. * { writeConfigDescriptor: false });
  1063. */
  1064. device.enableNotification = function(arg1, arg2, arg3, arg4, arg5)
  1065. {
  1066. if ('function' == typeof arg2)
  1067. {
  1068. // Service UUID is missing.
  1069. internal.enableNotification(device, arg1, arg2, arg3, arg4);
  1070. }
  1071. else
  1072. {
  1073. // Service UUID is present.
  1074. internal.enableServiceNotification(device, arg1, arg2, arg3, arg4, arg5);
  1075. }
  1076. };
  1077. /**
  1078. * <strong>Deprecated</strong>.
  1079. * Use function {@link evothings.easyble.EasyBLEDevice#enableNotification}
  1080. * @deprecated
  1081. * @instance
  1082. */
  1083. device.enableServiceNotification = function(
  1084. serviceUUID, characteristicUUID, success, fail, options)
  1085. {
  1086. internal.enableServiceNotification(
  1087. device,
  1088. serviceUUID,
  1089. characteristicUUID,
  1090. success,
  1091. fail,
  1092. options);
  1093. };
  1094. /**
  1095. * Unsubscribe from characteristic updates to stop notifications.
  1096. * <p>On Android you can disable automatic write of notify/indicate and write
  1097. * the configuration descriptor yourself, supply an options object as
  1098. * last parameter, see example below.</p>
  1099. * @param {string} serviceUUID - UUID of service that has the given
  1100. * characteristic (previous versions of this library allowed leaving out
  1101. * the service UUID, this is unsafe practice and has been deprecated, always
  1102. * specify the service UUID).
  1103. * @param serviceUUID - UUID of service that has the given characteristic.
  1104. * @param characteristicUUID - UUID of characteristic to unsubscribe from.
  1105. * @param {evothings.easyble.emptyCallback} success - Success callback: success()
  1106. * @param {evothings.easyble.failCallback} fail - Error callback: fail(error)
  1107. * @param {evothings.easyble.NotificationOptions} [options] - Optional settings.
  1108. * @public
  1109. * @instance
  1110. * @example
  1111. * // Example call:
  1112. * device.disableNotification(
  1113. * serviceUUID,
  1114. * characteristicUUID,
  1115. * function()
  1116. * {
  1117. * console.log('characteristic notification disabled');
  1118. * },
  1119. * function(errorCode)
  1120. * {
  1121. * console.log('disableNotification error: ' + errorCode);
  1122. * });
  1123. */
  1124. device.disableNotification = function(arg1, arg2, arg3, arg4, arg5)
  1125. {
  1126. if ('function' == typeof arg2)
  1127. {
  1128. // Service UUID is missing.
  1129. internal.disableNotification(device, arg1, arg2, arg3, arg4);
  1130. }
  1131. else
  1132. {
  1133. // Service UUID is present.
  1134. internal.disableServiceNotification(device, arg1, arg2, arg3, arg4, arg5);
  1135. }
  1136. };
  1137. /**
  1138. * <strong>Deprecated</strong>.
  1139. * Use function {@link evothings.easyble.EasyBLEDevice#disableNotification}
  1140. * @deprecated
  1141. * @instance
  1142. */
  1143. device.disableServiceNotification = function(
  1144. serviceUUID, characteristicUUID, success, fail, options)
  1145. {
  1146. internal.disableServiceNotification(
  1147. device, serviceUUID, characteristicUUID, success, fail, options);
  1148. };
  1149. };
  1150. /**
  1151. * Connect to a device.
  1152. * Called from evothings.easyble.EasyBLEDevice.
  1153. * @private
  1154. */
  1155. internal.connectToDevice = function(device, success, fail)
  1156. {
  1157. // Check that device is not already connected.
  1158. if (device.__isConnected)
  1159. {
  1160. fail(evothings.easyble.error.DEVICE_ALREADY_CONNECTED);
  1161. return;
  1162. }
  1163. evothings.ble.connect(
  1164. device.address,
  1165. // Success callback.
  1166. function(connectInfo)
  1167. {
  1168. // DEBUG LOG
  1169. console.log('BLE connect state: ' + connectInfo.state);
  1170. if (connectInfo.state == 2) // connected
  1171. {
  1172. device.deviceHandle = connectInfo.deviceHandle;
  1173. device.__uuidMap = {};
  1174. device.__serviceMap = {};
  1175. device.__isConnected = true;
  1176. internal.connectedDevices[device.address] = device;
  1177. success(device);
  1178. }
  1179. else if (connectInfo.state == 0) // disconnected
  1180. {
  1181. device.__isConnected = false;
  1182. internal.connectedDevices[device.address] = null;
  1183. // TODO: Perhaps this should be redesigned, as disconnect is
  1184. // more of a status change than an error? What do you think?
  1185. fail && fail(evothings.easyble.error.DISCONNECTED);
  1186. }
  1187. },
  1188. // Error callback.
  1189. function(errorCode)
  1190. {
  1191. // DEBUG LOG
  1192. console.log('BLE connect error: ' + errorCode);
  1193. // Set isConnected to false on error.
  1194. device.__isConnected = false;
  1195. internal.connectedDevices[device.address] = null;
  1196. fail(errorCode);
  1197. });
  1198. };
  1199. /**
  1200. * Obtain device services, them read characteristics and descriptors
  1201. * for the services with the given uuid(s).
  1202. * If serviceUUIDs is null, info is read for all services.
  1203. * Called from evothings.easyble.EasyBLEDevice.
  1204. * @private
  1205. */
  1206. internal.readServices = function(device, serviceUUIDs, success, fail)
  1207. {
  1208. // Read services.
  1209. evothings.ble.services(
  1210. device.deviceHandle,
  1211. function(services)
  1212. {
  1213. // Array that stores services.
  1214. device.__services = [];
  1215. for (var i = 0; i < services.length; ++i)
  1216. {
  1217. var service = services[i];
  1218. service.uuid = service.uuid.toLowerCase();
  1219. device.__services.push(service);
  1220. device.__uuidMap[service.uuid] = service;
  1221. }
  1222. internal.readCharacteristicsForServices(
  1223. device, serviceUUIDs, success, fail);
  1224. },
  1225. function(errorCode)
  1226. {
  1227. fail(errorCode);
  1228. });
  1229. };
  1230. /**
  1231. * Read characteristics and descriptors for the services with the given uuid(s).
  1232. * If serviceUUIDs is null, info for all services are read.
  1233. * Internal function.
  1234. * Called from evothings.easyble.EasyBLEDevice.
  1235. * @private
  1236. */
  1237. internal.readCharacteristicsForServices = function(device, serviceUUIDs, success, fail)
  1238. {
  1239. var characteristicsCallbackFun = function(service)
  1240. {
  1241. // Array with characteristics for service.
  1242. service.__characteristics = [];
  1243. return function(characteristics)
  1244. {
  1245. --readCounter; // Decrements the count added by services.
  1246. readCounter += characteristics.length;
  1247. for (var i = 0; i < characteristics.length; ++i)
  1248. {
  1249. var characteristic = characteristics[i];
  1250. characteristic.uuid = characteristic.uuid.toLowerCase();
  1251. service.__characteristics.push(characteristic);
  1252. device.__uuidMap[characteristic.uuid] = characteristic;
  1253. device.__serviceMap[service.uuid + ':' + characteristic.uuid] = characteristic;
  1254. // DEBUG LOG
  1255. //console.log('storing service:characteristic key: ' + service.uuid + ':' + characteristic.uuid);
  1256. //if (!characteristic)
  1257. //{
  1258. // console.log(' --> characteristic is null!')
  1259. //}
  1260. // Read descriptors for characteristic.
  1261. evothings.ble.descriptors(
  1262. device.deviceHandle,
  1263. characteristic.handle,
  1264. descriptorsCallbackFun(service, characteristic),
  1265. function(errorCode)
  1266. {
  1267. fail(errorCode);
  1268. });
  1269. }
  1270. };
  1271. };
  1272. /**
  1273. * @private
  1274. */
  1275. var descriptorsCallbackFun = function(service, characteristic)
  1276. {
  1277. // Array with descriptors for characteristic.
  1278. characteristic.__descriptors = [];
  1279. return function(descriptors)
  1280. {
  1281. --readCounter; // Decrements the count added by characteristics.
  1282. for (var i = 0; i < descriptors.length; ++i)
  1283. {
  1284. var descriptor = descriptors[i];
  1285. descriptor.uuid = descriptor.uuid.toLowerCase();
  1286. characteristic.__descriptors.push(descriptor);
  1287. device.__uuidMap[characteristic.uuid + ':' + descriptor.uuid] = descriptor;
  1288. device.__serviceMap[service.uuid + ':' + characteristic.uuid + ':' + descriptor.uuid] = descriptor;
  1289. }
  1290. if (0 == readCounter)
  1291. {
  1292. // Everything is read.
  1293. success(device);
  1294. }
  1295. };
  1296. };
  1297. // Initialize read counter.
  1298. readCounter = 0;
  1299. if (null != serviceUUIDs)
  1300. {
  1301. // Read info for service UUIDs.
  1302. readCounter = serviceUUIDs.length;
  1303. for (var i = 0; i < serviceUUIDs.length; ++i)
  1304. {
  1305. var uuid = serviceUUIDs[i].toLowerCase();
  1306. var service = device.__uuidMap[uuid];
  1307. if (!service)
  1308. {
  1309. fail(evothings.easyble.error.SERVICE_NOT_FOUND + ' ' + uuid);
  1310. return;
  1311. }
  1312. // Read characteristics for service. Will also read descriptors.
  1313. evothings.ble.characteristics(
  1314. device.deviceHandle,
  1315. service.handle,
  1316. characteristicsCallbackFun(service),
  1317. function(errorCode)
  1318. {
  1319. fail(errorCode);
  1320. });
  1321. }
  1322. }
  1323. else
  1324. {
  1325. // Read info for all services.
  1326. readCounter = device.__services.length;
  1327. for (var i = 0; i < device.__services.length; ++i)
  1328. {
  1329. // Read characteristics for service. Will also read descriptors.
  1330. var service = device.__services[i];
  1331. evothings.ble.characteristics(
  1332. device.deviceHandle,
  1333. service.handle,
  1334. characteristicsCallbackFun(service),
  1335. function(errorCode)
  1336. {
  1337. fail(errorCode);
  1338. });
  1339. }
  1340. }
  1341. };
  1342. /**
  1343. * Called from evothings.easyble.EasyBLEDevice.
  1344. * @deprecated Naming is a bit confusing, internally functions
  1345. * named "xxxServiceYYY" are the "future-safe" onces, but in
  1346. * the public API functions "xxxYYY" are new "future-safe"
  1347. * (and backwards compatible).
  1348. * @private
  1349. */
  1350. internal.readCharacteristic = function(device, characteristicUUID, success, fail)
  1351. {
  1352. characteristicUUID = characteristicUUID.toLowerCase();
  1353. var characteristic = device.__uuidMap[characteristicUUID];
  1354. if (!characteristic)
  1355. {
  1356. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' +
  1357. characteristicUUID);
  1358. return;
  1359. }
  1360. evothings.ble.readCharacteristic(
  1361. device.deviceHandle,
  1362. characteristic.handle,
  1363. success,
  1364. fail);
  1365. };
  1366. /**
  1367. * Called from evothings.easyble.EasyBLEDevice.
  1368. * @private
  1369. */
  1370. internal.readServiceCharacteristic = function(
  1371. device, serviceUUID, characteristicUUID, success, fail)
  1372. {
  1373. var key = serviceUUID.toLowerCase() + ':' + characteristicUUID.toLowerCase();
  1374. var characteristic = device.__serviceMap[key];
  1375. if (!characteristic)
  1376. {
  1377. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' + key);
  1378. return;
  1379. }
  1380. evothings.ble.readCharacteristic(
  1381. device.deviceHandle,
  1382. characteristic.handle,
  1383. success,
  1384. fail);
  1385. };
  1386. /**
  1387. * Called from evothings.easyble.EasyBLEDevice.
  1388. * @deprecated Naming is a bit confusing, internally functions
  1389. * named "xxxServiceYYY" are the "future-safe" onces, but in
  1390. * the public API functions "xxxYYY" are new "future-safe"
  1391. * (and backwards compatible).
  1392. * @private
  1393. */
  1394. internal.readDescriptor = function(
  1395. device, characteristicUUID, descriptorUUID, success, fail)
  1396. {
  1397. characteristicUUID = characteristicUUID.toLowerCase();
  1398. descriptorUUID = descriptorUUID.toLowerCase();
  1399. var descriptor = device.__uuidMap[characteristicUUID + ':' + descriptorUUID];
  1400. if (!descriptor)
  1401. {
  1402. fail(evothings.easyble.error.DESCRIPTOR_NOT_FOUND + ' ' + descriptorUUID);
  1403. return;
  1404. }
  1405. evothings.ble.readDescriptor(
  1406. device.deviceHandle,
  1407. descriptor.handle,
  1408. success,
  1409. fail);
  1410. };
  1411. /**
  1412. * Called from evothings.easyble.EasyBLEDevice.
  1413. * @private
  1414. */
  1415. internal.readServiceDescriptor = function(
  1416. device, serviceUUID, characteristicUUID, descriptorUUID, success, fail)
  1417. {
  1418. var key = serviceUUID.toLowerCase() + ':' +
  1419. characteristicUUID.toLowerCase() + ':' +
  1420. descriptorUUID.toLowerCase();
  1421. var descriptor = device.__serviceMap[key];
  1422. if (!descriptor)
  1423. {
  1424. fail(evothings.easyble.error.DESCRIPTOR_NOT_FOUND + ' ' + key);
  1425. return;
  1426. }
  1427. evothings.ble.readDescriptor(
  1428. device.deviceHandle,
  1429. descriptor.handle,
  1430. success,
  1431. fail);
  1432. };
  1433. /**
  1434. * Called from evothings.easyble.EasyBLEDevice.
  1435. * @deprecated Naming is a bit confusing, internally functions
  1436. * named "xxxServiceYYY" are the "future-safe" onces, but in
  1437. * the public API functions "xxxYYY" are new "future-safe"
  1438. * (and backwards compatible).
  1439. * @private
  1440. */
  1441. internal.writeCharacteristic = function(
  1442. device, characteristicUUID, value, success, fail)
  1443. {
  1444. characteristicUUID = characteristicUUID.toLowerCase();
  1445. var characteristic = device.__uuidMap[characteristicUUID];
  1446. if (!characteristic)
  1447. {
  1448. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' +
  1449. characteristicUUID);
  1450. return;
  1451. }
  1452. evothings.ble.writeCharacteristic(
  1453. device.deviceHandle,
  1454. characteristic.handle,
  1455. value,
  1456. function()
  1457. {
  1458. success();
  1459. },
  1460. function(errorCode)
  1461. {
  1462. fail(errorCode);
  1463. });
  1464. };
  1465. /**
  1466. * Called from evothings.easyble.EasyBLEDevice.
  1467. * @private
  1468. */
  1469. internal.writeServiceCharacteristic = function(
  1470. device, serviceUUID, characteristicUUID, value, success, fail)
  1471. {
  1472. var key = serviceUUID.toLowerCase() + ':' + characteristicUUID.toLowerCase();
  1473. var characteristic = device.__serviceMap[key];
  1474. if (!characteristic)
  1475. {
  1476. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' + key);
  1477. return;
  1478. }
  1479. evothings.ble.writeCharacteristic(
  1480. device.deviceHandle,
  1481. characteristic.handle,
  1482. value,
  1483. success,
  1484. fail);
  1485. };
  1486. /**
  1487. * Called from evothings.easyble.EasyBLEDevice.
  1488. * @private
  1489. */
  1490. internal.writeServiceCharacteristicWithoutResponse = function(
  1491. device, serviceUUID, characteristicUUID, value, success, fail)
  1492. {
  1493. var key = serviceUUID.toLowerCase() + ':' + characteristicUUID.toLowerCase();
  1494. // DEBUG LOG
  1495. //console.log('internal.writeServiceCharacteristicWithoutResponse key: ' + key)
  1496. //console.log('internal.writeServiceCharacteristicWithoutResponse serviceMap:')
  1497. for (var theKey in device.__serviceMap)
  1498. {
  1499. console.log(' ' + theKey);
  1500. }
  1501. var characteristic = device.__serviceMap[key];
  1502. if (!characteristic)
  1503. {
  1504. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' + key);
  1505. return;
  1506. }
  1507. evothings.ble.writeCharacteristicWithoutResponse(
  1508. device.deviceHandle,
  1509. characteristic.handle,
  1510. value,
  1511. success,
  1512. fail);
  1513. };
  1514. /**
  1515. * Called from evothings.easyble.EasyBLEDevice.
  1516. * @deprecated Naming is a bit confusing, internally functions
  1517. * named "xxxServiceYYY" are the "future-safe" onces, but in
  1518. * the public API functions "xxxYYY" are new "future-safe"
  1519. * (and backwards compatible).
  1520. * @private
  1521. */
  1522. internal.writeDescriptor = function(
  1523. device, characteristicUUID, descriptorUUID, value, success, fail)
  1524. {
  1525. characteristicUUID = characteristicUUID.toLowerCase();
  1526. descriptorUUID = descriptorUUID.toLowerCase();
  1527. var descriptor = device.__uuidMap[characteristicUUID + ':' + descriptorUUID];
  1528. if (!descriptor)
  1529. {
  1530. fail(evothings.easyble.error.DESCRIPTOR_NOT_FOUND + ' ' + descriptorUUID);
  1531. return;
  1532. }
  1533. evothings.ble.writeDescriptor(
  1534. device.deviceHandle,
  1535. descriptor.handle,
  1536. value,
  1537. function()
  1538. {
  1539. success();
  1540. },
  1541. function(errorCode)
  1542. {
  1543. fail(errorCode);
  1544. });
  1545. };
  1546. /**
  1547. * Called from evothings.easyble.EasyBLEDevice.
  1548. * @private
  1549. */
  1550. internal.writeServiceDescriptor = function(
  1551. device, serviceUUID, characteristicUUID, descriptorUUID, value, success, fail)
  1552. {
  1553. var key = serviceUUID.toLowerCase() + ':' +
  1554. characteristicUUID.toLowerCase() + ':' +
  1555. descriptorUUID.toLowerCase();
  1556. var descriptor = device.__serviceMap[key];
  1557. if (!descriptor)
  1558. {
  1559. fail(evothings.easyble.error.DESCRIPTOR_NOT_FOUND + ' ' + key);
  1560. return;
  1561. }
  1562. evothings.ble.writeDescriptor(
  1563. device.deviceHandle,
  1564. descriptor.handle,
  1565. value,
  1566. success,
  1567. fail);
  1568. };
  1569. /**
  1570. * Called from evothings.easyble.EasyBLEDevice.
  1571. * @deprecated Naming is a bit confusing, internally functions
  1572. * named "xxxServiceYYY" are the "future-safe" onces, but in
  1573. * the public API functions "xxxYYY" are new "future-safe"
  1574. * (and backwards compatible).
  1575. * @private
  1576. */
  1577. internal.enableNotification = function(
  1578. device, characteristicUUID, success, fail, options)
  1579. {
  1580. characteristicUUID = characteristicUUID.toLowerCase();
  1581. var characteristic = device.__uuidMap[characteristicUUID];
  1582. if (!characteristic)
  1583. {
  1584. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' +
  1585. characteristicUUID);
  1586. return;
  1587. }
  1588. evothings.ble.enableNotification(
  1589. device.deviceHandle,
  1590. characteristic.handle,
  1591. success,
  1592. fail,
  1593. options);
  1594. };
  1595. /**
  1596. * Called from evothings.easyble.EasyBLEDevice.
  1597. * @private
  1598. */
  1599. internal.enableServiceNotification = function(
  1600. device, serviceUUID, characteristicUUID, success, fail, options)
  1601. {
  1602. var key = serviceUUID.toLowerCase() + ':' + characteristicUUID.toLowerCase();
  1603. var characteristic = device.__serviceMap[key];
  1604. if (!characteristic)
  1605. {
  1606. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' + key);
  1607. return;
  1608. }
  1609. evothings.ble.enableNotification(
  1610. device.deviceHandle,
  1611. characteristic.handle,
  1612. success,
  1613. fail,
  1614. options);
  1615. };
  1616. /**
  1617. * Called from evothings.easyble.EasyBLEDevice.
  1618. * @deprecated Naming is a bit confusing, internally functions
  1619. * named "xxxServiceYYY" are the "future-safe" onces, but in
  1620. * the public API functions "xxxYYY" are new "future-safe"
  1621. * (and backwards compatible).
  1622. * @private
  1623. */
  1624. internal.disableNotification = function(
  1625. device, characteristicUUID, success, fail, options)
  1626. {
  1627. characteristicUUID = characteristicUUID.toLowerCase();
  1628. var characteristic = device.__uuidMap[characteristicUUID];
  1629. if (!characteristic)
  1630. {
  1631. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' +
  1632. characteristicUUID);
  1633. return;
  1634. }
  1635. evothings.ble.disableNotification(
  1636. device.deviceHandle,
  1637. characteristic.handle,
  1638. success,
  1639. fail,
  1640. options);
  1641. };
  1642. /**
  1643. * Called from evothings.easyble.EasyBLEDevice.
  1644. * @private
  1645. */
  1646. internal.disableServiceNotification = function(
  1647. device, serviceUUID, characteristicUUID, success, fail, options)
  1648. {
  1649. var key = serviceUUID.toLowerCase() + ':' + characteristicUUID.toLowerCase();
  1650. var characteristic = device.__serviceMap[key];
  1651. if (!characteristic)
  1652. {
  1653. fail(evothings.easyble.error.CHARACTERISTIC_NOT_FOUND + ' ' + key);
  1654. return;
  1655. }
  1656. evothings.ble.disableNotification(
  1657. device.deviceHandle,
  1658. characteristic.handle,
  1659. success,
  1660. fail,
  1661. options);
  1662. };
  1663. /**
  1664. * Prints and object for debugging purposes.
  1665. * @deprecated. Defined here for backwards compatibility.
  1666. * Use evothings.printObject().
  1667. * @public
  1668. */
  1669. evothings.easyble.printObject = evothings.printObject;
  1670. /**
  1671. * Reset the BLE hardware. Can be time consuming.
  1672. * @public
  1673. */
  1674. evothings.easyble.reset = function()
  1675. {
  1676. evothings.ble.reset();
  1677. };
  1678. })();