You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

573 lines
20 KiB

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
};