bleat
Version:
Abstraction library following Web Bluetooth specification for hiding differences in JavaScript BLE APIs
81 lines • 19.1 kB
JavaScript
/* @license
*
* BLE Abstraction Tool: bluetooth helpers
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Rob Moran
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.bleatHelpers=t()}(this,function(){"use strict";var t={alert_notification:6161,automation_io:6165,battery_service:6159,blood_pressure:6160,body_composition:6171,bond_management:6174,continuous_glucose_monitoring:6175,current_time:6149,cycling_power:6168,cycling_speed_and_cadence:6166,device_information:6154,environmental_sensing:6170,generic_access:6144,generic_attribute:6145,glucose:6152,health_thermometer:6153,heart_rate:6157,human_interface_device:6162,immediate_alert:6146,indoor_positioning:6177,internet_protocol_support:6176,link_loss:6147,location_and_navigation:6169,next_dst_change:6151,phone_alert_status:6158,pulse_oximeter:6178,reference_time_update:6150,running_speed_and_cadence:6164,scan_parameters:6163,tx_power:6148,user_data:6172,weight_scale:6173},r={aerobic_heart_rate_lower_limit:10878,aerobic_heart_rate_upper_limit:10884,aerobic_threshold:10879,age:10880,aggregate:10842,alert_category_id:10819,alert_category_id_bit_mask:10818,alert_level:10758,alert_notification_control_point:10820,alert_status:10815,altitude:10931,anaerobic_heart_rate_lower_limit:10881,anaerobic_heart_rate_upper_limit:10882,anaerobic_threshold:10883,analog:10840,apparent_wind_direction:10867,apparent_wind_speed:10866,"gap.appearance":10753,barometric_pressure_trend:10915,battery_level:10777,blood_pressure_feature:10825,blood_pressure_measurement:10805,body_composition_feature:10907,body_composition_measurement:10908,body_sensor_location:10808,bond_management_control_point:10916,bond_management_feature:10917,boot_keyboard_input_report:10786,boot_keyboard_output_report:10802,boot_mouse_input_report:10803,"gap.central_address_resolution_support":10918,cgm_feature:10920,cgm_measurement:10919,cgm_session_run_time:10923,cgm_session_start_time:10922,cgm_specific_ops_control_point:10924,cgm_status:10921,csc_feature:10844,csc_measurement:10843,current_time:10795,cycling_power_control_point:10854,cycling_power_feature:10853,cycling_power_measurement:10851,cycling_power_vector:10852,database_change_increment:10905,date_of_birth:10885,date_of_threshold_assessment:10886,date_time:10760,day_date_time:10762,day_of_week:10761,descriptor_value_changed:10877,"gap.device_name":10752,dew_point:10875,digital:10838,dst_offset:10765,elevation:10860,email_address:10887,exact_time_256:10764,fat_burn_heart_rate_lower_limit:10888,fat_burn_heart_rate_upper_limit:10889,firmware_revision_string:10790,first_name:10890,five_zone_heart_rate_limits:10891,floor_number:10930,gender:10892,glucose_feature:10833,glucose_measurement:10776,glucose_measurement_context:10804,gust_factor:10868,hardware_revision_string:10791,heart_rate_control_point:10809,heart_rate_max:10893,heart_rate_measurement:10807,heat_index:10874,height:10894,hid_control_point:10828,hid_information:10826,hip_circumference:10895,humidity:10863,"ieee_11073-20601_regulatory_certification_data_list":10794,indoor_positioning_configuration:10925,intermediate_blood_pressure:10806,intermediate_temperature:10782,irradiance:10871,language:10914,last_name:10896,latitude:10926,ln_control_point:10859,ln_feature:10858,"local_east_coordinate.xml":10929,local_north_coordinate:10928,local_time_information:10767,location_and_speed:10855,location_name:10933,longitude:10927,magnetic_declination:10796,magnetic_flux_density_2D:10912,magnetic_flux_density_3D:10913,manufacturer_name_string:10793,maximum_recommended_heart_rate:10897,measurement_interval:10785,model_number_string:10788,navigation:10856,new_alert:10822,"gap.peripheral_preferred_connection_parameters":10756,"gap.peripheral_privacy_flag":10754,plx_continuous_measurement:10847,plx_features:10848,plx_spot_check_measurement:10846,pnp_id:10832,pollen_concentration:10869,position_quality:10857,pressure:10861,protocol_mode:10830,rainfall:10872,"gap.reconnection_address":10755,record_access_control_point:10834,reference_time_information:10772,report:10829,report_map:10827,resting_heart_rate:10898,ringer_control_point:10816,ringer_setting:10817,rsc_feature:10836,rsc_measurement:10835,sc_control_point:10837,scan_interval_window:10831,scan_refresh:10801,sensor_location:10845,serial_number_string:10789,"gatt.service_changed":10757,software_revision_string:10792,sport_type_for_aerobic_and_anaerobic_thresholds:10899,supported_new_alert_category:10823,supported_unread_alert_category:10824,system_id:10787,temperature:10862,temperature_measurement:10780,temperature_type:10781,three_zone_heart_rate_limits:10900,time_accuracy:10770,time_source:10771,time_update_control_point:10774,time_update_state:10775,time_with_dst:10769,time_zone:10766,true_wind_direction:10865,true_wind_speed:10864,two_zone_heart_rate_limit:10901,tx_power_level:10759,uncertainty:10932,unread_alert_status:10821,user_control_point:10911,user_index:10906,uv_index:10870,vo2_max:10902,waist_circumference:10903,weight:10904,weight_measurement:10909,weight_scale_feature:10910,wind_chill:10873},_={"gatt.characteristic_extended_properties":10496,"gatt.characteristic_user_description":10497,"gatt.client_characteristic_configuration":10498,"gatt.server_characteristic_configuration":10499,"gatt.characteristic_presentation_format":10500,"gatt.characteristic_aggregate_format":10501,valid_range:10502,external_report_reference:10503,report_reference:10504,number_of_digitals:10505,value_trigger_setting:10506,es_configuration:10507,es_measurement:10508,es_trigger_setting:10509,time_trigger_setting:10510};function a(e){return"number"==typeof e&&(e=e.toString(16)),(e=e.toLowerCase()).length<=8&&(e=("00000000"+e).slice(-8)+"-0000-1000-8000-00805f9b34fb"),32===e.length&&(e=e.match(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/).splice(1).join("-")),e}return{Services:t,Characteristics:r,Descriptors:_,getCanonicalUUID:a,getServiceUUID:function(e){return t[e]&&(e=t[e]),a(e)},getCharacteristicUUID:function(e){return r[e]&&(e=r[e]),a(e)},getDescriptorUUID:function(e){return _[e]&&(e=_[e]),a(e)}}});
/* @license
*
* BLE Abstraction Tool: core functionality - classic specification
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Rob Moran
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
!function(t,i){"function"==typeof define&&define.amd?define(["bluetooth.helpers"],i):"object"==typeof exports?module.exports=i(require("./bluetooth.helpers")):t.bleat=i(t.bleatHelpers)}(this,function(t){"use strict";var c=null,e={};function s(i,e){return function(t){i&&i(e+": "+t)}}function n(i){return function(){if("function"==typeof i){var t=[].slice.call(arguments);i.apply(this,t)}}}function r(i,t){var e=0,r=!1;this.addCallback=function(t){return e++,r=!0,function(){t&&t.apply(null,arguments),0==--e&&i&&i()}},this.error=function(){t&&t.apply(null,arguments),0==--e&&i&&i()},this.finish=function(){!r&&i&&i()}}function d(t){this._handle=t._handle,this.address=t._handle,this.name=t.name,this.serviceUUIDs=t.uuids,this.adData=t.adData,this.connected=!1,this.services={}}d.prototype.hasService=function(i){return this.serviceUUIDs.some(function(t){return t===i})},d.prototype.connect=function(t,i,e,r){c.connect(this._handle,function(){if(this.connected=!0,"boolean"==typeof e&&(r=e,e=null),r)return n(t)();this.discoverAll(t,e)}.bind(this),function(){this.connected=!1,this.services={},n(i)()}.bind(this),s(e,"connect error"))},d.prototype.disconnect=function(t){c.disconnect(this._handle,s(t,"disconnect error"))},d.prototype.discoverServices=function(t,i,e){if(!1===this.connected)return s(e,"discovery error")("device not connected");"function"==typeof t?(i=t,t=[]):"string"==typeof t&&(t=[t]),c.discoverServices(this._handle,t,function(t){t.forEach(function(t){this.services[t.uuid]=new o(t)},this),i&&i()}.bind(this),s(e,"service discovery error"))},d.prototype.discoverAll=function(t,i){if(!1===this.connected)return s(i,"discovery error")("device not connected");var e=new r(t,i);this.discoverServices(e.addCallback(function(){Object.keys(this.services).forEach(function(t){var i=this.services[t];i.discoverIncludedServices(e.addCallback(),e.error),i.discoverCharacteristics(e.addCallback(function(){Object.keys(i.characteristics).forEach(function(t){i.characteristics[t].discoverDescriptors(e.addCallback(),e.error)},this)}.bind(this)),e.error)},this)}.bind(this)),e.error),e.finish()};var o=function(t){this._handle=t._handle,this.uuid=t.uuid,this.primary=t.primary,this.includedServices={},this.characteristics={}};o.prototype.discoverIncludedServices=function(t,i,e){"function"==typeof t?(i=t,t=[]):"string"==typeof t&&(t=[t]),c.discoverIncludedServices(this._handle,t,function(t){t.forEach(function(t){this.includedServices[t.uuid]=new o(t)},this),i&&i()}.bind(this),s(e,"included service discovery error"))},o.prototype.discoverCharacteristics=function(t,i,e){"function"==typeof t?(i=t,t=[]):"string"==typeof t&&(t=[t]),c.discoverCharacteristics(this._handle,t,function(t){t.forEach(function(t){this.characteristics[t.uuid]=new a(t)},this),i&&i()}.bind(this),s(e,"characteristic discovery error"))};var a=function(t){this._handle=t._handle,this.uuid=t.uuid,this.properties=t.properties,this.descriptors={}};a.prototype.discoverDescriptors=function(t,i,e){"function"==typeof t?(i=t,t=[]):"string"==typeof t&&(t=[t]),c.discoverDescriptors(this._handle,t,function(t){t.forEach(function(t){this.descriptors[t.uuid]=new h(t)},this),i&&i()}.bind(this),s(e,"descriptor discovery error"))},a.prototype.read=function(t,i){c.readCharacteristic(this._handle,n(t),s(i,"read characteristic error"))},a.prototype.write=function(t,i,e){c.writeCharacteristic(this._handle,t,n(i),s(e,"write characteristic error"))},a.prototype.enableNotify=function(t,i,e){c.enableNotify(this._handle,n(t),n(i),s(e,"enable notify error"))},a.prototype.disableNotify=function(t,i){c.disableNotify(this._handle,n(t),s(i,"disable notify error"))};var h=function(t){this._handle=t._handle,this.uuid=t.uuid};return h.prototype.read=function(t,i){c.readDescriptor(this._handle,n(t),s(i,"read descriptor error"))},h.prototype.write=function(t,i,e){c.writeDescriptor(this._handle,t,n(i),s(e,"write descriptor error"))},{_addAdapter:function(t,i){e[t]=i,c=i},startScan:function(t,e,i,r,n){"function"==typeof t?(n=r,r=i,i=e,e=t,t=[]):"string"==typeof t&&(t=[t]),c.stopScan(s(r,"stop scan error"));var o={};c.startScan(t,function(t){var i=new d(t);o[i.address]&&!n||(o[i.address]=i,e&&e(i))}.bind(this),i,s(r,"scan error"))},stopScan:function(t){c.stopScan(s(t,"stop scan error"))}}});
/* @license
*
* BLE Abstraction Tool: noble adapter
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Rob Moran
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
!function(i,e){"function"==typeof define&&define.amd?define(["noble","bleat","bluetooth.helpers"],e):"object"==typeof exports?module.exports=function(i){return e(require("noble"),i,require("./bluetooth.helpers"))}:e(i.noble,i.bleat,i.bleatHelpers)}(this,function(s,i,c){"use strict";function a(t,n){return function(i){if(i)t(i);else if("function"==typeof n){var e=[].slice.call(arguments,1);n.apply(this,e)}}}function d(i){var e=new Uint8Array(i).buffer;return new DataView(e)}function o(i){var e=new Uint8Array(i.buffer);return new Buffer(e)}i._addAdapter&&s&&i._addAdapter("noble",{deviceHandles:{},serviceHandles:{},characteristicHandles:{},descriptorHandles:{},charNotifies:{},foundFn:null,initialised:!1,init:function(i,e){if(this.initialised)return i();s.on("discover",function(i){if(this.foundFn){var e=i.address&&"unknown"!==i.address?i.address:i.id;this.deviceHandles[e]||(this.deviceHandles[e]=i);var t=[];i.advertisement.serviceUuids&&i.advertisement.serviceUuids.forEach(function(i){t.push(c.getCanonicalUUID(i))});var n={};if(i.advertisement.manufacturerData){var r=i.advertisement.manufacturerData.readUInt16LE(0);r=("0000"+r.toString(16)).slice(-4);var s=i.advertisement.manufacturerData.slice(2);n[r]=d(s)}var a={};i.advertisement.serviceData&&i.advertisement.serviceData.forEach(function(i){a[c.getCanonicalUUID(i.uuid)]=d(i.data)}),this.foundFn({_handle:e,id:e,name:i.advertisement.localName,uuids:t,adData:{manufacturerData:n,serviceData:a,txPower:i.advertisement.txPowerLevel,rssi:i.rssi}})}}.bind(this)),this.initialised=!0,i()},startScan:function(t,n,e,r){this.init(function(){this.deviceHandles={};var i=function(i){"poweredOn"===i?(0===t.length?this.foundFn=n:this.foundFn=function(e){t.forEach(function(i){0<=e.uuids.indexOf(i)&&n(e)})},s.startScanning([],!1,a(r,e))):r("adapter not enabled")}.bind(this);"unknown"===s.state?s.once("stateChange",i.bind(this)):i(s.state)}.bind(this),r)},stopScan:function(i){this.foundFn=null,s.stopScanning()},connect:function(i,e,t,n){var r=this.deviceHandles[i];r.once("connect",e),r.once("disconnect",function(){this.serviceHandles={},this.characteristicHandles={},this.descriptorHandles={},this.charNotifies={},t()}.bind(this)),r.connect(a(n))},disconnect:function(i,e){this.deviceHandles[i].disconnect(a(e))},discoverServices:function(i,n,e,t){this.deviceHandles[i].discoverServices([],a(t,function(i){var t=[];i.forEach(function(i){var e=c.getCanonicalUUID(i.uuid);(0===n.length||0<=n.indexOf(e))&&(this.serviceHandles[e]||(this.serviceHandles[e]=i),t.push({_handle:e,uuid:e,primary:!0}))},this),e(t)}.bind(this)))},discoverIncludedServices:function(i,n,e,t){this.serviceHandles[i].discoverIncludedServices([],a(t,function(i){var t=[];i.forEach(function(i){var e=c.getCanonicalUUID(i.uuid);(0===n.length||0<=n.indexOf(e))&&(this.serviceHandles[e]||(this.serviceHandles[e]=i),t.push({_handle:e,uuid:e,primary:!1}))},this),e(t)}.bind(this)))},discoverCharacteristics:function(i,t,n,e){this.serviceHandles[i].discoverCharacteristics([],a(e,function(i){var e=[];i.forEach(function(i){var n=c.getCanonicalUUID(i.uuid);(0===t.length||0<=t.indexOf(n))&&(this.characteristicHandles[n]||(this.characteristicHandles[n]=i),e.push({_handle:n,uuid:n,properties:{broadcast:0<=i.properties.indexOf("broadcast"),read:0<=i.properties.indexOf("read"),writeWithoutResponse:0<=i.properties.indexOf("writeWithoutResponse"),write:0<=i.properties.indexOf("write"),notify:0<=i.properties.indexOf("notify"),indicate:0<=i.properties.indexOf("indicate"),authenticatedSignedWrites:0<=i.properties.indexOf("authenticatedSignedWrites"),reliableWrite:0<=i.properties.indexOf("reliableWrite"),writableAuxiliaries:0<=i.properties.indexOf("writableAuxiliaries")}}),i.on("data",function(i,e){if(!0===e&&"function"==typeof this.charNotifies[n]){var t=d(i);this.charNotifies[n](t)}}.bind(this)))},this),n(e)}.bind(this)))},discoverDescriptors:function(i,r,e,t){var s=this.characteristicHandles[i];s.discoverDescriptors(a(t,function(i){var n=[];i.forEach(function(i){var e=c.getCanonicalUUID(i.uuid);if(0===r.length||0<=r.indexOf(e)){var t=s.uuid+"-"+i.uuid;this.descriptorHandles[t]||(this.descriptorHandles[t]=i),n.push({_handle:t,uuid:e})}},this),e(n)}.bind(this)))},readCharacteristic:function(i,t,e){this.characteristicHandles[i].read(a(e,function(i){var e=d(i);t(e)}))},writeCharacteristic:function(i,e,t,n){var r=o(e);this.characteristicHandles[i].write(r,!0,a(n,t))},enableNotify:function(e,t,n,r){if(this.charNotifies[e])return this.charNotifies[e]=t,n();this.characteristicHandles[e].once("notify",function(i){if(!0!==i)return r("notify failed to enable");this.charNotifies[e]=t,n()}.bind(this)),this.characteristicHandles[e].notify(!0,a(r))},disableNotify:function(e,t,n){if(!this.charNotifies[e])return t();this.characteristicHandles[e].once("notify",function(i){if(!1!==i)return n("notify failed to disable");this.charNotifies[e]&&delete this.charNotifies[e],t()}.bind(this)),this.characteristicHandles[e].notify(!1,a(n))},readDescriptor:function(i,t,e){this.descriptorHandles[i].readValue(a(e,function(i){var e=d(i);t(e)}))},writeDescriptor:function(i,e,t,n){var r=o(e);this.descriptorHandles[i].writeValue(r,a(n,t))}})});