const logEnable = false; let isAndroid = false; let ecBluetoothAdapterStateChangeCallback = () => { }; let ecBLEConnectionStateChangeCallback = () => { }; let ecDeviceId = ''; let ecGattServerUUID = ''; const ecGattServerUUIDOption1 = '0000FFF0-0000-1000-8000-00805F9B34FB'; const ecGattServerUUIDOption2 = 'FFF0'; let ecGattCharacteristicWriteUUID = ''; const ecGattCharacteristicWriteUUIDOption1 = '0000FFF2-0000-1000-8000-00805F9B34FB'; const ecGattCharacteristicWriteUUIDOption2 = 'FFF2'; const log = data => { if (logEnable) { console.log('[eciot]:' + JSON.stringify(data)); } }; const onBluetoothAdapterStateChange = cb => { ecBluetoothAdapterStateChangeCallback = cb; }; // const getSetting = () => { // return new Promise(function (resolve, reject) { // jd.getSetting({ // success(res) { // log(res); // if (res.authSetting && res.authSetting['scope.bluetooth']) { // resolve({ // ok: true, // errCode: 0, // errMsg: '' // }); // } else { // resolve({ // ok: false, // errCode: 30001, // errMsg: 'getSetting fail' // }); // } // }, // fail(res) { // log(res); // resolve({ // ok: false, // errCode: res.errCode ? res.errCode : 30000, // errMsg: res.errMsg ? res.errMsg : 'getSetting fail' // }); // } // }); // }); // }; // const authorize = () => { // return new Promise(function (resolve, reject) { // jd.authorize({ // scope: 'scope.bluetooth', // success(res) { // log(res); // resolve({ // ok: true, // errCode: 0, // errMsg: '' // }); // }, // fail(res) { // log(res); // // {"errMsg":"authorize:fail:auth deny"} // resolve({ // ok: false, // errCode: 30000, // errMsg: res.errMsg // }); // } // }); // }); // }; const _openBluetoothAdapter = () => { return new Promise(function (resolve, reject) { jd.openBluetoothAdapter({ success(res) { log(res); // {errno: 0, errMsg: "openBluetoothAdapter:ok"} resolve({ ok: true, errCode: 0, errMsg: '' }); }, fail(res) { log(res); resolve({ ok: false, errCode: res.errCode ? res.errCode : 30000, errMsg: res.errMsg }); } }); }); }; const openBluetoothAdapter = async () => { // await _openBluetoothAdapter() const systemInfo = jd.getSystemInfoSync(); log(systemInfo); if (systemInfo.platform.toLowerCase() === 'android') { isAndroid = true; } // if (!systemInfo.bluetoothEnabled) { // ecBluetoothAdapterStateChangeCallback({ // ok: false, // errCode: 30001, // errMsg: '请打开系统蓝牙开关' // }); // return; // } // if (isAndroid && !systemInfo.locationEnabled) { // ecBluetoothAdapterStateChangeCallback({ // ok: false, // errCode: 30002, // errMsg: '请打开系统定位开关' // }); // return; // } // if (isAndroid && !systemInfo.locationAuthorized) { // ecBluetoothAdapterStateChangeCallback({ // ok: false, // errCode: 30003, // errMsg: '请打开微信定位权限,允许微信使用您的位置信息' // }); // return; // } // const setting = await getSetting(); //小程序蓝牙权限 // if (!setting.ok) { // const authRes = await authorize(); // if (!authRes.ok) { // ecBluetoothAdapterStateChangeCallback({ // ok: false, // errCode: 30004, // errMsg: '请打开小程序蓝牙开关,点击右上角三个点,然后点击设置' // }); // return; // } // } jd.offBluetoothAdapterStateChange(); jd.onBluetoothAdapterStateChange(res => { log(res); // {available: true, discovering: true} if (!res.available) { ecBluetoothAdapterStateChangeCallback({ ok: false, errCode: 30005, errMsg: '蓝牙适配器不可用' }); } }); const openRes = await _openBluetoothAdapter(); ecBluetoothAdapterStateChangeCallback(openRes); }; const onBluetoothDeviceFound = cb => { jd.offBluetoothDeviceFound(); jd.onBluetoothDeviceFound(res => { // log(res); const device = res.devices[0]; const name = device.name ? device.name : device.localName; if (!name) { return; } let id = device.deviceId; let rssi = device.RSSI; cb({ id, name, rssi }); }); }; const startBluetoothDevicesDiscovery = () => { jd.startBluetoothDevicesDiscovery({ //services: [ecServerId], allowDuplicatesKey: true, powerLevel: 'high', complete(res) { log(res); } }); }; const stopBluetoothDevicesDiscovery = () => { jd.stopBluetoothDevicesDiscovery({ complete(res) { // {errno: 0, errMsg: "stopBluetoothDevicesDiscovery:ok", isDiscovering: false} log(res); } }); }; const onBLEConnectionStateChange = cb => { ecBLEConnectionStateChangeCallback = cb; }; const _createBLEConnection = () => { return new Promise(function (resolve, reject) { jd.createBLEConnection({ deviceId: ecDeviceId, success(res) { log(res); // {"errno":0,"errCode":0,"errMsg":"createBLEConnection:ok"} resolve({ ok: true, errCode: 0, errMsg: '' }); }, fail(res) { log(res); // {"errno":1001,"errMsg":"createBLEConnection:fail parameter error: parameter.deviceId should be String instead of Undefined;"} resolve({ ok: false, errCode: res.errCode ? res.errCode : res.errno, errMsg: res.errMsg }); } }); if (!isAndroid) { setTimeout(() => { jd.createBLEConnection({ deviceId: ecDeviceId, success(res) { log(res); // {"errno":0,"errCode":0,"errMsg":"createBLEConnection:ok"} resolve({ ok: true, errCode: 0, errMsg: '' }); }, fail(res) { log(res); // {"errno":1001,"errMsg":"createBLEConnection:fail parameter error: parameter.deviceId should be String instead of Undefined;"} resolve({ ok: false, errCode: res.errCode ? res.errCode : res.errno, errMsg: res.errMsg }); } }); }, 1000) } }); }; const getBLEDeviceServices = () => { return new Promise(function (resolve, reject) { jd.getBLEDeviceServices({ deviceId: ecDeviceId, success(res) { log(res); //{"services":[{"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB","isPrimary":true}],"errCode":0,"errno":0,"errMsg":"getBLEDeviceServices:ok"} // {"errno":0,"deviceId":"7C7E20F2-CB75-6DA8-F8DF-FFF702B0D63F","services":[{"isPrimary":true,"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB"}],"errMsg":"getBLEDeviceServices:ok","errCode":0} resolve({ ok: true, errCode: 0, errMsg: '', services: res.services }); }, fail(res) { log(res); resolve({ ok: false, errCode: res.errCode, errMsg: res.errMsg }); } }); }); }; const getBLEDeviceCharacteristics = serviceId => { return new Promise(function (resolve, reject) { jd.getBLEDeviceCharacteristics({ deviceId: ecDeviceId, serviceId, success(res) { log(res); // {"characteristics":[{"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB","handle":3,"properties":{"read":false,"write":true,"notify":false,"indicate":false,"writeNoResponse":true,"writeDefault":true}},{"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB","handle":5,"properties":{"read":true,"write":true,"notify":true,"indicate":false,"writeNoResponse":true,"writeDefault":true}}],"errCode":0,"errno":0,"errMsg":"getBLEDeviceCharacteristics:ok"} // {"characteristics":[{"properties":{"writeDefault":true,"notify":false,"write":true,"indicate":false,"read":false,"writeNoResponse":true},"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB"},{"properties":{"writeDefault":true,"notify":true,"write":true,"indicate":false,"read":true,"writeNoResponse":true},"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB"}],"deviceId":"7C7E20F2-CB75-6DA8-F8DF-FFF702B0D63F","serviceId":"0000FFF0-0000-1000-8000-00805F9B34FB","errno":0,"errMsg":"getBLEDeviceCharacteristics:ok","errCode":0} resolve({ ok: true, errCode: 0, errMsg: '', characteristics: res.characteristics }); }, fail(res) { log(res); resolve({ ok: false, errCode: res.errCode, errMsg: res.errMsg }); } }); }); }; const notifyBLECharacteristicValueChange = (serviceId, characteristicId) => { return new Promise(function (resolve, reject) { jd.notifyBLECharacteristicValueChange({ state: true, deviceId: ecDeviceId, serviceId, characteristicId, success(res) { log(res); // {"errCode":0,"errno":0,"errMsg":"notifyBLECharacteristicValueChange:ok"} resolve({ ok: true, errCode: 0, errMsg: '' }); }, fail(res) { log(res); resolve({ ok: false, errCode: res.errCode, errMsg: res.errMsg }); } }); }); }; const setBLEMTU = mtu => { return new Promise(function (resolve, reject) { jd.setBLEMTU({ deviceId: ecDeviceId, mtu, success(res) { log(res); // {"errMsg":"setBLEMTU:ok","errno":0,"errCode":0,"mtu":50} resolve({ ok: true, errCode: 0, errMsg: '' }); }, fail(res) { log(res); // {"errCode":-1,"errno":1500104,"errMsg":"setBLEMTU:fail:internal error"} resolve({ ok: false, errCode: res.errCode, errMsg: res.errMsg }); } }); }); }; //和设备建立连接 const createBLEConnection = async id => { ecDeviceId = id; jd.offBLEConnectionStateChange(); jd.onBLEConnectionStateChange(async res => { log(res); // {"deviceId":"EC:22:05:13:78:49","connected":true} if (res.connected) { const servicesResult = await getBLEDeviceServices(); if (!servicesResult.ok) { ecBLEConnectionStateChangeCallback(servicesResult); closeBLEConnection(); return; } for (const service of servicesResult.services) { if (service.uuid.toUpperCase() === ecGattServerUUIDOption1 || service.uuid.toUpperCase() === ecGattServerUUIDOption2) { ecGattServerUUID = service.uuid; } const characteristicsResult = await getBLEDeviceCharacteristics(service.uuid); if (!characteristicsResult.ok) { ecBLEConnectionStateChangeCallback(characteristicsResult); closeBLEConnection(); return; } for (const characteristic of characteristicsResult.characteristics) { if (characteristic.properties && characteristic.properties.notify) { const notifyResult = await notifyBLECharacteristicValueChange(service.uuid, characteristic.uuid); if (!notifyResult.ok) { ecBLEConnectionStateChangeCallback({ ok: false, errCode: 30000, errMsg: 'notify error' }); closeBLEConnection(); return; } } if (characteristic.uuid.toUpperCase() === ecGattCharacteristicWriteUUIDOption1 || characteristic.uuid.toUpperCase() === ecGattCharacteristicWriteUUIDOption2) { ecGattCharacteristicWriteUUID = characteristic.uuid; } } } if (isAndroid) { await setBLEMTU(247); } ecBLEConnectionStateChangeCallback({ ok: true, errCode: 0, errMsg: '' }); } else { ecBLEConnectionStateChangeCallback({ ok: false, errCode: 0, errMsg: 'disconnect' }); } }); const res = await _createBLEConnection(); if (!res.ok) { ecBLEConnectionStateChangeCallback(res); } }; //关闭当前连接 const closeBLEConnection = () => { jd.closeBLEConnection({ deviceId: ecDeviceId, complete(res) { log(res); } }); }; const onBLECharacteristicValueChange = cb => { jd.offBLECharacteristicValueChange(); jd.onBLECharacteristicValueChange(res => { log(res); let x = new Uint8Array(res.value); log(x); let str = utf8BytesToStr(x); let strHex = ''; for (let i = 0; i < x.length; i++) { strHex = strHex + x[i].toString(16).padStart(2, '0').toUpperCase(); } log(str); log(strHex); cb(str, strHex); }); }; const _writeBLECharacteristicValue = buffer => { return new Promise(function (resolve, reject) { jd.writeBLECharacteristicValue({ deviceId: ecDeviceId, serviceId: ecGattServerUUID, characteristicId: ecGattCharacteristicWriteUUID, value: buffer, writeType: 'writeNoResponse', success(res) { log(res); // {"errno":0,"errCode":0,"errMsg":"writeBLECharacteristicValue:ok"} resolve({ ok: true, errCode: 0, errMsg: '' }); }, fail(res) { log(res); resolve({ ok: false, errCode: res.errCode, errMsg: res.errMsg }); } }); }); }; const writeBLECharacteristicValue = async (str, isHex) => { if (str.length === 0) return { ok: false, errCode: 30000, errMsg: 'data is null' }; let buffer; if (isHex) { buffer = new ArrayBuffer(str.length / 2); let x = new Uint8Array(buffer); for (let i = 0; i < x.length; i++) { x[i] = parseInt(str.substr(2 * i, 2), 16); } } else { buffer = new Uint8Array(strToUtf8Bytes(str)).buffer; } return await _writeBLECharacteristicValue(buffer); }; const utf8BytesToStr = utf8Bytes => { let unicodeStr = ''; for (let pos = 0; pos < utf8Bytes.length;) { let flag = utf8Bytes[pos]; let unicode = 0; if (flag >>> 7 === 0) { unicodeStr += String.fromCharCode(utf8Bytes[pos]); pos += 1; } // else if ((flag & 0xFC) === 0xFC) { // unicode = (utf8Bytes[pos] & 0x3) << 30 // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24 // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18 // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12 // unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6 // unicode |= (utf8Bytes[pos + 5] & 0x3F) // unicodeStr += String.fromCharCode(unicode) // pos += 6 // } // else if ((flag & 0xF8) === 0xF8) { // unicode = (utf8Bytes[pos] & 0x7) << 24 // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18 // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12 // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6 // unicode |= (utf8Bytes[pos + 4] & 0x3F) // unicodeStr += String.fromCharCode(unicode) // pos += 5 // } else if ((flag & 0xf0) === 0xf0) { unicode = (utf8Bytes[pos] & 0xf) << 18; unicode |= (utf8Bytes[pos + 1] & 0x3f) << 12; unicode |= (utf8Bytes[pos + 2] & 0x3f) << 6; unicode |= utf8Bytes[pos + 3] & 0x3f; unicodeStr += String.fromCharCode(unicode); pos += 4; } else if ((flag & 0xe0) === 0xe0) { unicode = (utf8Bytes[pos] & 0x1f) << 12; unicode |= (utf8Bytes[pos + 1] & 0x3f) << 6; unicode |= utf8Bytes[pos + 2] & 0x3f; unicodeStr += String.fromCharCode(unicode); pos += 3; } else if ((flag & 0xc0) === 0xc0) { //110 unicode = (utf8Bytes[pos] & 0x3f) << 6; unicode |= utf8Bytes[pos + 1] & 0x3f; unicodeStr += String.fromCharCode(unicode); pos += 2; } else { unicodeStr += String.fromCharCode(utf8Bytes[pos]); pos += 1; } } return unicodeStr; }; const strToUtf8Bytes = str => { let bytes = []; for (let i = 0; i < str.length; ++i) { let code = str.charCodeAt(i); if (code >= 0x10000 && code <= 0x10ffff) { bytes.push(code >> 18 | 0xf0); // 第一个字节 bytes.push(code >> 12 & 0x3f | 0x80); bytes.push(code >> 6 & 0x3f | 0x80); bytes.push(code & 0x3f | 0x80); } else if (code >= 0x800 && code <= 0xffff) { bytes.push(code >> 12 | 0xe0); bytes.push(code >> 6 & 0x3f | 0x80); bytes.push(code & 0x3f | 0x80); } else if (code >= 0x80 && code <= 0x7ff) { bytes.push(code >> 6 | 0xc0); bytes.push(code & 0x3f | 0x80); } else { bytes.push(code); } } return bytes; }; module.exports = { onBluetoothAdapterStateChange, openBluetoothAdapter, onBluetoothDeviceFound, startBluetoothDevicesDiscovery, stopBluetoothDevicesDiscovery, onBLEConnectionStateChange, createBLEConnection, closeBLEConnection, onBLECharacteristicValueChange, writeBLECharacteristicValue };