蓝牙模块
This commit is contained in:
parent
7c8cc6a87d
commit
14ed880ef3
|
|
@ -117,6 +117,12 @@ const items = computed<NavigationMenuItem[]>(() => [
|
|||
icon: 'i-lucide-cloud',
|
||||
description: '灵活、可靠、安全,有效降低成本',
|
||||
to: '/IoTSolutions/iotCloudPlatform',
|
||||
},
|
||||
{
|
||||
label: '蓝牙测试',
|
||||
icon: 'i-lucide-bluetooth',
|
||||
description: '测试',
|
||||
to: '/IoTSolutions/blueToothTest',
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
1284
app/components/blueToothTest/item1.vue
Normal file
1284
app/components/blueToothTest/item1.vue
Normal file
File diff suppressed because it is too large
Load Diff
11
app/components/blueToothTest/item2.vue
Normal file
11
app/components/blueToothTest/item2.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
1165
app/components/blufi/crypto/aes.js
Normal file
1165
app/components/blufi/crypto/aes.js
Normal file
File diff suppressed because it is too large
Load Diff
44
app/components/blufi/crypto/crypto-dh.js
Normal file
44
app/components/blufi/crypto/crypto-dh.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
var generatePrime = require("./lib/generatePrime.js");
|
||||
|
||||
var DH = require("./lib/dh.js");
|
||||
|
||||
var Buffer = require("./lib/safe-buffer.js").Buffer;
|
||||
|
||||
function getDiffieHellman(mod) {
|
||||
var prime = new Buffer(primes[mod].prime, 'hex');
|
||||
var gen = new Buffer(primes[mod].gen, 'hex');
|
||||
return new DH(prime, gen);
|
||||
}
|
||||
|
||||
var ENCODINGS = {
|
||||
'binary': true,
|
||||
'hex': true,
|
||||
'base64': true
|
||||
};
|
||||
|
||||
function createDiffieHellman(prime, enc, generator, genc) {
|
||||
if (Buffer.isBuffer(enc) || ENCODINGS[enc] === undefined) {
|
||||
return createDiffieHellman(prime, 'binary', enc, generator);
|
||||
}
|
||||
|
||||
enc = enc || 'binary';
|
||||
genc = genc || 'binary';
|
||||
generator = generator || new Buffer([2]);
|
||||
|
||||
if (!Buffer.isBuffer(generator)) {
|
||||
generator = new Buffer(generator, genc);
|
||||
}
|
||||
|
||||
if (typeof prime === 'number') {
|
||||
return new DH(generatePrime(prime, generator), generator, true);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(prime)) {
|
||||
prime = new Buffer(prime, enc);
|
||||
}
|
||||
|
||||
return new DH(prime, generator, true);
|
||||
}
|
||||
|
||||
exports.DiffieHellmanGroup = exports.createDiffieHellmanGroup = exports.getDiffieHellman = getDiffieHellman;
|
||||
exports.createDiffieHellman = exports.DiffieHellman = createDiffieHellman;
|
||||
118
app/components/blufi/crypto/lib/base64-js.js
Normal file
118
app/components/blufi/crypto/lib/base64-js.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
'use strict';
|
||||
|
||||
exports.byteLength = byteLength;
|
||||
exports.toByteArray = toByteArray;
|
||||
exports.fromByteArray = fromByteArray;
|
||||
var lookup = [];
|
||||
var revLookup = [];
|
||||
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
|
||||
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
for (var i = 0, len = code.length; i < len; ++i) {
|
||||
lookup[i] = code[i];
|
||||
revLookup[code.charCodeAt(i)] = i;
|
||||
} // Support decoding URL-safe base64 strings, as Node.js does.
|
||||
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
||||
|
||||
|
||||
revLookup['-'.charCodeAt(0)] = 62;
|
||||
revLookup['_'.charCodeAt(0)] = 63;
|
||||
|
||||
function getLens(b64) {
|
||||
var len = b64.length;
|
||||
|
||||
if (len % 4 > 0) {
|
||||
throw new Error('Invalid string. Length must be a multiple of 4');
|
||||
} // Trim off extra bytes after placeholder bytes are found
|
||||
// See: https://github.com/beatgammit/base64-js/issues/42
|
||||
|
||||
|
||||
var validLen = b64.indexOf('=');
|
||||
if (validLen === -1) validLen = len;
|
||||
var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4;
|
||||
return [validLen, placeHoldersLen];
|
||||
} // base64 is 4/3 + up to two characters of the original data
|
||||
|
||||
|
||||
function byteLength(b64) {
|
||||
var lens = getLens(b64);
|
||||
var validLen = lens[0];
|
||||
var placeHoldersLen = lens[1];
|
||||
return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
|
||||
}
|
||||
|
||||
function _byteLength(b64, validLen, placeHoldersLen) {
|
||||
return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
|
||||
}
|
||||
|
||||
function toByteArray(b64) {
|
||||
var tmp;
|
||||
var lens = getLens(b64);
|
||||
var validLen = lens[0];
|
||||
var placeHoldersLen = lens[1];
|
||||
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen));
|
||||
var curByte = 0; // if there are placeholders, only get up to the last complete 4 chars
|
||||
|
||||
var len = placeHoldersLen > 0 ? validLen - 4 : validLen;
|
||||
|
||||
for (var i = 0; i < len; i += 4) {
|
||||
tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)];
|
||||
arr[curByte++] = tmp >> 16 & 0xFF;
|
||||
arr[curByte++] = tmp >> 8 & 0xFF;
|
||||
arr[curByte++] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
if (placeHoldersLen === 2) {
|
||||
tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4;
|
||||
arr[curByte++] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
if (placeHoldersLen === 1) {
|
||||
tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2;
|
||||
arr[curByte++] = tmp >> 8 & 0xFF;
|
||||
arr[curByte++] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
function tripletToBase64(num) {
|
||||
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F];
|
||||
}
|
||||
|
||||
function encodeChunk(uint8, start, end) {
|
||||
var tmp;
|
||||
var output = [];
|
||||
|
||||
for (var i = start; i < end; i += 3) {
|
||||
tmp = (uint8[i] << 16 & 0xFF0000) + (uint8[i + 1] << 8 & 0xFF00) + (uint8[i + 2] & 0xFF);
|
||||
output.push(tripletToBase64(tmp));
|
||||
}
|
||||
|
||||
return output.join('');
|
||||
}
|
||||
|
||||
function fromByteArray(uint8) {
|
||||
var tmp;
|
||||
var len = uint8.length;
|
||||
var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
||||
|
||||
var parts = [];
|
||||
var maxChunkLength = 16383; // must be multiple of 3
|
||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
||||
|
||||
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
||||
parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));
|
||||
} // pad the end with zeros, but make sure to not forget the extra bytes
|
||||
|
||||
|
||||
if (extraBytes === 1) {
|
||||
tmp = uint8[len - 1];
|
||||
parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 0x3F] + '==');
|
||||
} else if (extraBytes === 2) {
|
||||
tmp = (uint8[len - 2] << 8) + uint8[len - 1];
|
||||
parts.push(lookup[tmp >> 10] + lookup[tmp >> 4 & 0x3F] + lookup[tmp << 2 & 0x3F] + '=');
|
||||
}
|
||||
|
||||
return parts.join('');
|
||||
}
|
||||
404
app/components/blufi/crypto/lib/blelib.js
Normal file
404
app/components/blufi/crypto/lib/blelib.js
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
function BLEDevice(arg) {
|
||||
this.deviceId = "";
|
||||
this.serviceId = "";
|
||||
this.debug = arg ? (arg['debug'] || false) : false;
|
||||
this.connectTimeout = arg ? (arg['connectTimeout'] || 10000) : 10000;
|
||||
this.onBLECharacteristicValueChange = {}
|
||||
};
|
||||
|
||||
/**
|
||||
* 日志输出
|
||||
*
|
||||
* @param {msg} String
|
||||
* 输出的信息
|
||||
* @param {Array} args
|
||||
* 辅助输出信息
|
||||
*/
|
||||
BLEDevice.prototype.log = function (msg, ...args) {
|
||||
if (this.debug) {
|
||||
console.log(msg, ...args)
|
||||
}
|
||||
};
|
||||
|
||||
BLEDevice.prototype.setStateCallBack = function (cb) {
|
||||
wx.onBluetoothAdapterStateChange((res) => {
|
||||
cb(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化蓝牙模块
|
||||
*
|
||||
* @return
|
||||
* 返回蓝牙模块Promise
|
||||
*/
|
||||
BLEDevice.prototype.openBluetoothAdapter = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.openBluetoothAdapter({
|
||||
success: (res) => {
|
||||
this.log("openBluetoothAdapter", res);
|
||||
resolve(res);
|
||||
},
|
||||
fail: (res) => {
|
||||
this.log("openBluetoothAdapter failed", res)
|
||||
reject(res);
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭蓝牙模块
|
||||
*/
|
||||
BLEDevice.prototype.closeBluetoothAdapter = function() {
|
||||
wx.closeBluetoothAdapter();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取本机蓝牙适配器状态
|
||||
*/
|
||||
BLEDevice.prototype.getBluetoothAdapterState = function () {
|
||||
wx.getBluetoothAdapterState({
|
||||
success: (res) => {
|
||||
if (res.discovering) {
|
||||
this.onBluetoothDeviceFound()
|
||||
} else if (res.available) {
|
||||
setTimeout(()=>{
|
||||
this.startBluetoothDevicesDiscovery(state.servicesId, state.key);
|
||||
},500)
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 开始搜寻附近的蓝牙外围设备
|
||||
*
|
||||
* @param {String} servicesId
|
||||
* 要搜索的蓝牙设备主服务的uuid列表
|
||||
* @param {Function} cb
|
||||
* 发现设备后的回调
|
||||
* 若发现成功,参数success为true,foundDevices:发现的设备列表
|
||||
* 若发现失败,参数success为false
|
||||
* @return
|
||||
* 返回搜索蓝牙设备Promise
|
||||
*/
|
||||
BLEDevice.prototype.startBluetoothDevicesDiscovery = function (servicesId, cb) {
|
||||
let foundDevices = [];
|
||||
wx.onBluetoothDeviceFound((res) => {
|
||||
res.devices.forEach(device => {
|
||||
if (!device.name && !device.localName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = inArray(foundDevices, 'deviceId', device.deviceId)
|
||||
|
||||
if (idx === -1) {
|
||||
foundDevices.push(device)
|
||||
} else {
|
||||
foundDevices[idx] = device
|
||||
}
|
||||
this.log("onBluetoothDeviceFound", foundDevices);
|
||||
|
||||
cb({ success: true, foundDevices: foundDevices});
|
||||
})
|
||||
})
|
||||
|
||||
wx.startBluetoothDevicesDiscovery({
|
||||
services: servicesId,
|
||||
allowDuplicatesKey: true,
|
||||
success: (res) => {
|
||||
this.log("startBluetoothDevicesDiscovery", res);
|
||||
},
|
||||
fail: (res) => {
|
||||
cb({ success: false });
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止搜寻附近的蓝牙外围设备
|
||||
*/
|
||||
BLEDevice.prototype.stopBluetoothDevicesDiscovery = function () {
|
||||
wx.stopBluetoothDevicesDiscovery();
|
||||
};
|
||||
|
||||
/**
|
||||
* 连接低功耗蓝牙设备
|
||||
*
|
||||
* @param {String} deviceId
|
||||
* 连接设备的ID
|
||||
*
|
||||
* @return
|
||||
* 返回设备连接的Promise
|
||||
*/
|
||||
BLEDevice.prototype.createBLEConnection = function (deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.createBLEConnection({
|
||||
deviceId: this.deviceId,
|
||||
success: (res) => {
|
||||
this.log("createBLEConnection", res)
|
||||
this.getBLEDeviceServices()
|
||||
.then(() => {
|
||||
resolve();
|
||||
}).catch((res) => {
|
||||
reject(res);
|
||||
this.closeBLEConnection();
|
||||
})
|
||||
},
|
||||
fail: (res) => {
|
||||
this.log("createBLEConnection failed", res)
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
this.stopBluetoothDevicesDiscovery();
|
||||
|
||||
setTimeout(() => {
|
||||
reject('Connect timeout');
|
||||
this.closeBLEConnection();
|
||||
}, this.connectTimeout);
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 断开与低功耗蓝牙设备的连接
|
||||
*/
|
||||
BLEDevice.prototype.closeBLEConnection = function () {
|
||||
wx.closeBLEConnection({
|
||||
deviceId: this.deviceId,
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取蓝牙设备的所有服务
|
||||
*
|
||||
* @param {String} findServicesUUID
|
||||
* 要查找的主服务uuid
|
||||
*
|
||||
* @param {Number} findServicesIndex
|
||||
* 开始查找的位置
|
||||
*
|
||||
* @return
|
||||
* 返回获取服务的Promise
|
||||
*/
|
||||
BLEDevice.prototype.getBLEDeviceServices = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getBLEDeviceServices({
|
||||
deviceId: this.deviceId,
|
||||
success: (res) => {
|
||||
let total = res.services.length
|
||||
for (let i = 0; i < res.services.length; i++) {
|
||||
this.log("getBLEDeviceServices", res.services[i]);
|
||||
this.getBLEDeviceCharacteristics(res.services[i].uuid)
|
||||
.then(() => {
|
||||
total --;
|
||||
if (total === 0) {
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.catch(res => {
|
||||
reject(res);
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (res) => {
|
||||
reject(res);
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取蓝牙设备某个服务中所有特征值
|
||||
*
|
||||
* @param {String} serviceId
|
||||
* 要查找特征的service uuid
|
||||
*
|
||||
* @return
|
||||
* 返回服务特征值的Promise
|
||||
*/
|
||||
BLEDevice.prototype.getBLEDeviceCharacteristics = function (serviceId) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getBLEDeviceCharacteristics({
|
||||
deviceId: this.deviceId,
|
||||
serviceId: serviceId,
|
||||
success: (res) => {
|
||||
resolve(res);
|
||||
|
||||
for (let i = 0; i < res.characteristics.length; i++) {
|
||||
this.log("getBLEDeviceCharacteristics", i, res.characteristics[i]);
|
||||
let item = res.characteristics[i];
|
||||
if (item.properties.read) {
|
||||
this.log("可读:", res.characteristics[i]);
|
||||
}
|
||||
if (item.properties.write) {
|
||||
this.log("可写:", res.characteristics[i]);
|
||||
}
|
||||
if (item.properties.notify || item.properties.indicate) {
|
||||
this.log("可订阅", res.characteristics[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: (res) => {
|
||||
reject(res);
|
||||
},
|
||||
complete: (res) => {
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 读取低功耗蓝牙设备的特征值的二进制数据值
|
||||
*
|
||||
* @param {String} serviceId
|
||||
* 蓝牙可订阅服务的uuid
|
||||
*
|
||||
* @param {String} readCharacteristicId
|
||||
* 蓝牙可读特征值的uuid
|
||||
*
|
||||
* @param {Function} callback
|
||||
* 特征变化的回调函数
|
||||
*/
|
||||
BLEDevice.prototype.readBLECharacteristicValue = function (serviceId, readCharacteristicId, callback) {
|
||||
wx.onBLECharacteristicValueChange((characteristic) => {
|
||||
this.log("onBLECharacteristicValueChange", characteristic.value)
|
||||
let cb = this.onBLECharacteristicValueChange[readCharacteristicId]
|
||||
if (cb) {
|
||||
cb(characteristic)
|
||||
}
|
||||
})
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.readBLECharacteristicValue({
|
||||
deviceId: this.deviceId,
|
||||
serviceId: serviceId,
|
||||
characteristicId: readCharacteristicId,
|
||||
success: (res) => {
|
||||
resolve(res)
|
||||
this.log('readBLECharacteristicValue', res);
|
||||
this.onBLECharacteristicValueChange[readCharacteristicId] = callback;
|
||||
},
|
||||
fail: (res) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 向低功耗蓝牙设备特征值中写入二进制数据
|
||||
*
|
||||
* @param {String} writeCharacteristicId
|
||||
* 蓝牙可写特征值的uuid
|
||||
*
|
||||
* @param {ArrayBuffer} buffer
|
||||
* 特征值对应的二进制值
|
||||
*/
|
||||
BLEDevice.prototype.writeBLECharacteristicValue = function (serviceId, writeCharacteristicId, buffer) {
|
||||
// // 向蓝牙设备发送一个0x00的16进制数据
|
||||
this.log("writeBLECharacteristicValue buffer", buffer);
|
||||
wx.writeBLECharacteristicValue({
|
||||
deviceId: this.deviceId,
|
||||
serviceId: serviceId,
|
||||
characteristicId: writeCharacteristicId,
|
||||
value: buffer,
|
||||
success: (res) => {
|
||||
this.log('writeBLECharacteristicValue', res);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 启用低功耗蓝牙设备特征值变化时的notify功能,订阅特征值
|
||||
*
|
||||
* @param {String} serviceId
|
||||
* 蓝牙可订阅服务的uuid
|
||||
*
|
||||
* @param {String} notifyCharacteristicId
|
||||
* 蓝牙可订阅特征值的uuid
|
||||
*
|
||||
* @param {Function} callback
|
||||
* 特征变化的回调函数
|
||||
*/
|
||||
BLEDevice.prototype.notifyBLECharacteristicValueChange = function (serviceId, notifyCharacteristicId, callback) {
|
||||
|
||||
wx.onBLECharacteristicValueChange((characteristic) => {
|
||||
this.log("onBLECharacteristicValueChange", characteristic.value)
|
||||
let cb = this.onBLECharacteristicValueChange[notifyCharacteristicId]
|
||||
if (cb) {
|
||||
cb(characteristic)
|
||||
}
|
||||
})
|
||||
return new Promise((reslove, reject) => {
|
||||
wx.notifyBLECharacteristicValueChange({
|
||||
deviceId: this.deviceId,
|
||||
serviceId: serviceId,
|
||||
characteristicId: notifyCharacteristicId,
|
||||
state: true,
|
||||
success: (res) => {
|
||||
reslove(res)
|
||||
this.log('notifyBLECharacteristicValueChange', res);
|
||||
this.onBLECharacteristicValueChange[notifyCharacteristicId] = callback
|
||||
},
|
||||
fail: (res) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭低功耗蓝牙设备特征值变化时的notify功能,订阅特征值
|
||||
*
|
||||
* @param {String} serviceId
|
||||
* 蓝牙可订阅服务的uuid
|
||||
*
|
||||
* @param {String} notifyCharacteristicId
|
||||
* 蓝牙可订阅特征值的uuid
|
||||
*/
|
||||
BLEDevice.prototype.unNotifyBLECharacteristicValueChange = function (serviceId, notifyCharacteristicId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
delete this.onBLECharacteristicValueChange[notifyCharacteristicId]
|
||||
|
||||
wx.notifyBLECharacteristicValueChange({
|
||||
deviceId: this.deviceId,
|
||||
serviceId: serviceId,
|
||||
characteristicId: notifyCharacteristicId,
|
||||
state: false,
|
||||
success: function (res) {
|
||||
resolve(res)
|
||||
},
|
||||
fail: function (res) {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const inArray = (arr, key, val) => {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i][key] === val) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* ArrayBuffer转16进度字符串示例
|
||||
*/
|
||||
const ab2hex = (buffer) => {
|
||||
var hexArr = Array.prototype.map.call(
|
||||
new Uint8Array(buffer),
|
||||
function (bit) {
|
||||
return ('00' + bit.toString(16)).slice(-2);
|
||||
}
|
||||
)
|
||||
return hexArr.join('');
|
||||
};
|
||||
|
||||
module.exports = BLEDevice
|
||||
3448
app/components/blufi/crypto/lib/bn.js
Normal file
3448
app/components/blufi/crypto/lib/bn.js
Normal file
File diff suppressed because it is too large
Load Diff
53
app/components/blufi/crypto/lib/brorand.js
Normal file
53
app/components/blufi/crypto/lib/brorand.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
var r;
|
||||
|
||||
module.exports = function rand(len) {
|
||||
if (!r) r = new Rand(null);
|
||||
return r.generate(len);
|
||||
};
|
||||
|
||||
function Rand(rand) {
|
||||
this.rand = rand;
|
||||
}
|
||||
|
||||
module.exports.Rand = Rand;
|
||||
|
||||
Rand.prototype.generate = function generate(len) {
|
||||
return this._rand(len);
|
||||
}; // Emulate crypto API using randy
|
||||
|
||||
|
||||
Rand.prototype._rand = function _rand(n) {
|
||||
console.log(this);
|
||||
if (this.rand.getBytes) return this.rand.getBytes(n);
|
||||
var res = new Uint8Array(n);
|
||||
|
||||
for (var i = 0; i < res.length; i++) res[i] = this.rand.getByte();
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
if (typeof self === 'object') {
|
||||
Rand.prototype._rand = function _rand(n) {
|
||||
var list = [];
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
list.push(Math.ceil(Math.random() * 255));
|
||||
}
|
||||
|
||||
var arr = new Uint8Array(list);
|
||||
return arr;
|
||||
};
|
||||
} else {
|
||||
// Node.js or Web worker with no crypto support
|
||||
try {
|
||||
// var crypto = require("./crypto.js");
|
||||
var crypto = 0;
|
||||
|
||||
if (typeof crypto.randomBytes !== 'function') throw new Error('Not supported');
|
||||
|
||||
Rand.prototype._rand = function _rand(n) {
|
||||
// return crypto.randomBytes(n);
|
||||
return crypto;
|
||||
};
|
||||
} catch (e) {}
|
||||
}
|
||||
1799
app/components/blufi/crypto/lib/buffer.js
Normal file
1799
app/components/blufi/crypto/lib/buffer.js
Normal file
File diff suppressed because it is too large
Load Diff
195
app/components/blufi/crypto/lib/dh.js
Normal file
195
app/components/blufi/crypto/lib/dh.js
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
var BN = require("./bn.js");
|
||||
|
||||
var MillerRabin = require("./miller-rabin.js");
|
||||
|
||||
var millerRabin = new MillerRabin();
|
||||
var TWENTYFOUR = new BN(24);
|
||||
var ELEVEN = new BN(11);
|
||||
var TEN = new BN(10);
|
||||
var THREE = new BN(3);
|
||||
var SEVEN = new BN(7);
|
||||
|
||||
var Buffer = require("./safe-buffer.js").Buffer;
|
||||
|
||||
var primes = require("./generatePrime.js");
|
||||
|
||||
var randomBytes = require("./randombytes.js");
|
||||
|
||||
module.exports = DH;
|
||||
|
||||
function setPublicKey(pub, enc) {
|
||||
enc = enc || 'utf8';
|
||||
|
||||
if (!Buffer.isBuffer(pub)) {
|
||||
pub = new Buffer(pub, enc);
|
||||
}
|
||||
|
||||
this._pub = new BN(pub);
|
||||
return this;
|
||||
}
|
||||
|
||||
function setPrivateKey(priv, enc) {
|
||||
enc = enc || 'utf8';
|
||||
|
||||
if (!Buffer.isBuffer(priv)) {
|
||||
priv = new Buffer(priv, enc);
|
||||
}
|
||||
|
||||
this._priv = new BN(priv);
|
||||
return this;
|
||||
}
|
||||
|
||||
var primeCache = {};
|
||||
|
||||
function checkPrime(prime, generator) {
|
||||
var gen = generator.toString('hex');
|
||||
var hex = [gen, prime.toString(16)].join('_');
|
||||
|
||||
if (hex in primeCache) {
|
||||
return primeCache[hex];
|
||||
}
|
||||
|
||||
var error = 0;
|
||||
|
||||
if (prime.isEven() || !primes.simpleSieve || !primes.fermatTest(prime) || !millerRabin.test(prime)) {
|
||||
//not a prime so +1
|
||||
error += 1;
|
||||
|
||||
if (gen === '02' || gen === '05') {
|
||||
// we'd be able to check the generator
|
||||
// it would fail so +8
|
||||
error += 8;
|
||||
} else {
|
||||
//we wouldn't be able to test the generator
|
||||
// so +4
|
||||
error += 4;
|
||||
}
|
||||
|
||||
primeCache[hex] = error;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!millerRabin.test(prime.shrn(1))) {
|
||||
//not a safe prime
|
||||
error += 2;
|
||||
}
|
||||
|
||||
var rem;
|
||||
|
||||
switch (gen) {
|
||||
case '02':
|
||||
if (prime.mod(TWENTYFOUR).cmp(ELEVEN)) {
|
||||
// unsuidable generator
|
||||
error += 8;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '05':
|
||||
rem = prime.mod(TEN);
|
||||
|
||||
if (rem.cmp(THREE) && rem.cmp(SEVEN)) {
|
||||
// prime mod 10 needs to equal 3 or 7
|
||||
error += 8;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
error += 4;
|
||||
}
|
||||
|
||||
primeCache[hex] = error;
|
||||
return error;
|
||||
}
|
||||
|
||||
function DH(prime, generator, malleable) {
|
||||
this.setGenerator(generator);
|
||||
this.__prime = new BN(prime);
|
||||
this._prime = BN.mont(this.__prime);
|
||||
this._primeLen = 128;
|
||||
this._pub = undefined;
|
||||
this._priv = undefined;
|
||||
this._primeCode = undefined;
|
||||
|
||||
if (malleable) {
|
||||
this.setPublicKey = setPublicKey;
|
||||
this.setPrivateKey = setPrivateKey;
|
||||
} else {
|
||||
this._primeCode = 8;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(DH.prototype, 'verifyError', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
if (typeof this._primeCode !== 'number') {
|
||||
this._primeCode = checkPrime(this.__prime, this.__gen);
|
||||
}
|
||||
|
||||
return this._primeCode;
|
||||
}
|
||||
});
|
||||
|
||||
DH.prototype.generateKeys = function () {
|
||||
if (!this._priv) {
|
||||
this._priv = new BN(randomBytes(this._primeLen));
|
||||
}
|
||||
|
||||
this._pub = this._gen.toRed(this._prime).redPow(this._priv).fromRed();
|
||||
return this.getPublicKey();
|
||||
};
|
||||
|
||||
DH.prototype.computeSecret = function (other) {
|
||||
other = new BN(other);
|
||||
other = other.toRed(this._prime);
|
||||
var secret = other.redPow(this._priv).fromRed();
|
||||
var out = new Buffer(secret.toArray());
|
||||
var prime = this.getPrime();
|
||||
|
||||
if (out.length < prime.length) {
|
||||
var front = new Buffer(prime.length - out.length);
|
||||
front.fill(0);
|
||||
out = Buffer.concat([front, out]);
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
DH.prototype.getPublicKey = function getPublicKey(enc) {
|
||||
return formatReturnValue(this._pub, enc);
|
||||
};
|
||||
|
||||
DH.prototype.getPrivateKey = function getPrivateKey(enc) {
|
||||
return formatReturnValue(this._priv, enc);
|
||||
};
|
||||
|
||||
DH.prototype.getPrime = function (enc) {
|
||||
return formatReturnValue(this.__prime, enc);
|
||||
};
|
||||
|
||||
DH.prototype.getGenerator = function (enc) {
|
||||
return formatReturnValue(this._gen, enc);
|
||||
};
|
||||
|
||||
DH.prototype.setGenerator = function (gen, enc) {
|
||||
enc = enc || 'utf8';
|
||||
|
||||
if (!Buffer.isBuffer(gen)) {
|
||||
gen = new Buffer(gen, enc);
|
||||
}
|
||||
|
||||
this.__gen = gen;
|
||||
this._gen = new BN(gen);
|
||||
return this;
|
||||
};
|
||||
|
||||
function formatReturnValue(bn, enc) {
|
||||
var buf = new Buffer(bn.toArray());
|
||||
|
||||
if (!enc) {
|
||||
return buf;
|
||||
} else {
|
||||
return buf.toString(enc);
|
||||
}
|
||||
}
|
||||
109
app/components/blufi/crypto/lib/generatePrime.js
Normal file
109
app/components/blufi/crypto/lib/generatePrime.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
var randomBytes = require("./randombytes.js");
|
||||
|
||||
module.exports = findPrime;
|
||||
findPrime.simpleSieve = simpleSieve;
|
||||
findPrime.fermatTest = fermatTest;
|
||||
|
||||
var BN = require("./bn.js");
|
||||
|
||||
var TWENTYFOUR = new BN(24);
|
||||
|
||||
var MillerRabin = require("./miller-rabin.js");
|
||||
|
||||
var millerRabin = new MillerRabin();
|
||||
var ONE = new BN(1);
|
||||
var TWO = new BN(2);
|
||||
var FIVE = new BN(5);
|
||||
var SIXTEEN = new BN(16);
|
||||
var EIGHT = new BN(8);
|
||||
var TEN = new BN(10);
|
||||
var THREE = new BN(3);
|
||||
var SEVEN = new BN(7);
|
||||
var ELEVEN = new BN(11);
|
||||
var FOUR = new BN(4);
|
||||
var TWELVE = new BN(12);
|
||||
var primes = null;
|
||||
|
||||
function _getPrimes() {
|
||||
if (primes !== null) return primes;
|
||||
var limit = 0x100000;
|
||||
var res = [];
|
||||
res[0] = 2;
|
||||
|
||||
for (var i = 1, k = 3; k < limit; k += 2) {
|
||||
var sqrt = Math.ceil(Math.sqrt(k));
|
||||
|
||||
for (var j = 0; j < i && res[j] <= sqrt; j++) if (k % res[j] === 0) break;
|
||||
|
||||
if (i !== j && res[j] <= sqrt) continue;
|
||||
res[i++] = k;
|
||||
}
|
||||
|
||||
primes = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
function simpleSieve(p) {
|
||||
var primes = _getPrimes();
|
||||
|
||||
for (var i = 0; i < primes.length; i++) if (p.modn(primes[i]) === 0) {
|
||||
if (p.cmpn(primes[i]) === 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function fermatTest(p) {
|
||||
var red = BN.mont(p);
|
||||
return TWO.toRed(red).redPow(p.subn(1)).fromRed().cmpn(1) === 0;
|
||||
}
|
||||
|
||||
function findPrime(bits, gen) {
|
||||
if (bits < 16) {
|
||||
// this is what openssl does
|
||||
if (gen === 2 || gen === 5) {
|
||||
return new BN([0x8c, 0x7b]);
|
||||
} else {
|
||||
return new BN([0x8c, 0x27]);
|
||||
}
|
||||
}
|
||||
|
||||
gen = new BN(gen);
|
||||
var num, n2;
|
||||
|
||||
while (true) {
|
||||
num = new BN(randomBytes(Math.ceil(bits / 8)));
|
||||
|
||||
while (num.bitLength() > bits) {
|
||||
num.ishrn(1);
|
||||
}
|
||||
|
||||
if (num.isEven()) {
|
||||
num.iadd(ONE);
|
||||
}
|
||||
|
||||
if (!num.testn(1)) {
|
||||
num.iadd(TWO);
|
||||
}
|
||||
|
||||
if (!gen.cmp(TWO)) {
|
||||
while (num.mod(TWENTYFOUR).cmp(ELEVEN)) {
|
||||
num.iadd(FOUR);
|
||||
}
|
||||
} else if (!gen.cmp(FIVE)) {
|
||||
while (num.mod(TEN).cmp(THREE)) {
|
||||
num.iadd(FOUR);
|
||||
}
|
||||
}
|
||||
|
||||
n2 = num.shrn(1);
|
||||
|
||||
if (simpleSieve(n2) && simpleSieve(num) && fermatTest(n2) && fermatTest(num) && millerRabin.test(n2) && millerRabin.test(num)) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
88
app/components/blufi/crypto/lib/ieee754.js
Normal file
88
app/components/blufi/crypto/lib/ieee754.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
|
||||
var e, m;
|
||||
var eLen = nBytes * 8 - mLen - 1;
|
||||
var eMax = (1 << eLen) - 1;
|
||||
var eBias = eMax >> 1;
|
||||
var nBits = -7;
|
||||
var i = isLE ? nBytes - 1 : 0;
|
||||
var d = isLE ? -1 : 1;
|
||||
var s = buffer[offset + i];
|
||||
i += d;
|
||||
e = s & (1 << -nBits) - 1;
|
||||
s >>= -nBits;
|
||||
nBits += eLen;
|
||||
|
||||
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
|
||||
|
||||
m = e & (1 << -nBits) - 1;
|
||||
e >>= -nBits;
|
||||
nBits += mLen;
|
||||
|
||||
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
|
||||
|
||||
if (e === 0) {
|
||||
e = 1 - eBias;
|
||||
} else if (e === eMax) {
|
||||
return m ? NaN : (s ? -1 : 1) * Infinity;
|
||||
} else {
|
||||
m = m + Math.pow(2, mLen);
|
||||
e = e - eBias;
|
||||
}
|
||||
|
||||
return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
|
||||
};
|
||||
|
||||
exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
|
||||
var e, m, c;
|
||||
var eLen = nBytes * 8 - mLen - 1;
|
||||
var eMax = (1 << eLen) - 1;
|
||||
var eBias = eMax >> 1;
|
||||
var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
|
||||
var i = isLE ? 0 : nBytes - 1;
|
||||
var d = isLE ? 1 : -1;
|
||||
var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
|
||||
value = Math.abs(value);
|
||||
|
||||
if (isNaN(value) || value === Infinity) {
|
||||
m = isNaN(value) ? 1 : 0;
|
||||
e = eMax;
|
||||
} else {
|
||||
e = Math.floor(Math.log(value) / Math.LN2);
|
||||
|
||||
if (value * (c = Math.pow(2, -e)) < 1) {
|
||||
e--;
|
||||
c *= 2;
|
||||
}
|
||||
|
||||
if (e + eBias >= 1) {
|
||||
value += rt / c;
|
||||
} else {
|
||||
value += rt * Math.pow(2, 1 - eBias);
|
||||
}
|
||||
|
||||
if (value * c >= 2) {
|
||||
e++;
|
||||
c /= 2;
|
||||
}
|
||||
|
||||
if (e + eBias >= eMax) {
|
||||
m = 0;
|
||||
e = eMax;
|
||||
} else if (e + eBias >= 1) {
|
||||
m = (value * c - 1) * Math.pow(2, mLen);
|
||||
e = e + eBias;
|
||||
} else {
|
||||
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
|
||||
e = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
|
||||
|
||||
e = e << mLen | m;
|
||||
eLen += mLen;
|
||||
|
||||
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
|
||||
|
||||
buffer[offset + i - d] |= s * 128;
|
||||
};
|
||||
98
app/components/blufi/crypto/lib/miller-rabin.js
Normal file
98
app/components/blufi/crypto/lib/miller-rabin.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
var bn = require("./bn.js");
|
||||
|
||||
var brorand = require("./brorand.js");
|
||||
|
||||
function MillerRabin(rand) {
|
||||
this.rand = rand || new brorand.Rand();
|
||||
}
|
||||
|
||||
module.exports = MillerRabin;
|
||||
|
||||
MillerRabin.create = function create(rand) {
|
||||
return new MillerRabin(rand);
|
||||
};
|
||||
|
||||
MillerRabin.prototype._randbelow = function _randbelow(n) {
|
||||
var len = n.bitLength();
|
||||
var min_bytes = Math.ceil(len / 8); // Generage random bytes until a number less than n is found.
|
||||
// This ensures that 0..n-1 have an equal probability of being selected.
|
||||
|
||||
do var a = new bn(this.rand.generate(min_bytes)); while (a.cmp(n) >= 0);
|
||||
|
||||
return a;
|
||||
};
|
||||
|
||||
MillerRabin.prototype._randrange = function _randrange(start, stop) {
|
||||
// Generate a random number greater than or equal to start and less than stop.
|
||||
var size = stop.sub(start);
|
||||
return start.add(this._randbelow(size));
|
||||
};
|
||||
|
||||
MillerRabin.prototype.test = function test(n, k, cb) {
|
||||
var len = n.bitLength();
|
||||
var red = bn.mont(n);
|
||||
var rone = new bn(1).toRed(red);
|
||||
if (!k) k = Math.max(1, len / 48 | 0); // Find d and s, (n - 1) = (2 ^ s) * d;
|
||||
|
||||
var n1 = n.subn(1);
|
||||
|
||||
for (var s = 0; !n1.testn(s); s++) {}
|
||||
|
||||
var d = n.shrn(s);
|
||||
var rn1 = n1.toRed(red);
|
||||
var prime = true;
|
||||
|
||||
for (; k > 0; k--) {
|
||||
var a = this._randrange(new bn(2), n1);
|
||||
|
||||
if (cb) cb(a);
|
||||
var x = a.toRed(red).redPow(d);
|
||||
if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) continue;
|
||||
|
||||
for (var i = 1; i < s; i++) {
|
||||
x = x.redSqr();
|
||||
if (x.cmp(rone) === 0) return false;
|
||||
if (x.cmp(rn1) === 0) break;
|
||||
}
|
||||
|
||||
if (i === s) return false;
|
||||
}
|
||||
|
||||
return prime;
|
||||
};
|
||||
|
||||
MillerRabin.prototype.getDivisor = function getDivisor(n, k) {
|
||||
var len = n.bitLength();
|
||||
var red = bn.mont(n);
|
||||
var rone = new bn(1).toRed(red);
|
||||
if (!k) k = Math.max(1, len / 48 | 0); // Find d and s, (n - 1) = (2 ^ s) * d;
|
||||
|
||||
var n1 = n.subn(1);
|
||||
|
||||
for (var s = 0; !n1.testn(s); s++) {}
|
||||
|
||||
var d = n.shrn(s);
|
||||
var rn1 = n1.toRed(red);
|
||||
|
||||
for (; k > 0; k--) {
|
||||
var a = this._randrange(new bn(2), n1);
|
||||
|
||||
var g = n.gcd(a);
|
||||
if (g.cmpn(1) !== 0) return g;
|
||||
var x = a.toRed(red).redPow(d);
|
||||
if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) continue;
|
||||
|
||||
for (var i = 1; i < s; i++) {
|
||||
x = x.redSqr();
|
||||
if (x.cmp(rone) === 0) return x.fromRed().subn(1).gcd(n);
|
||||
if (x.cmp(rn1) === 0) break;
|
||||
}
|
||||
|
||||
if (i === s) {
|
||||
x = x.redSqr();
|
||||
return x.fromRed().subn(1).gcd(n);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
32
app/components/blufi/crypto/lib/randombytes.js
Normal file
32
app/components/blufi/crypto/lib/randombytes.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
function oldBrowser() {
|
||||
throw new Error('Secure random number generation is not supported by this browser.\nUse Chrome, Firefox or Internet Explorer 11');
|
||||
}
|
||||
|
||||
var Buffer = require("./safe-buffer.js").Buffer;
|
||||
|
||||
module.exports = randomBytes;
|
||||
|
||||
function randomBytes(size, cb) {
|
||||
// phantomjs needs to throw
|
||||
if (size > 65536) throw new Error('requested too many random bytes'); // in case browserify isn't using the Uint8Array version
|
||||
|
||||
var arr = [];
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
arr.push(Math.ceil(Math.random() * 255));
|
||||
}
|
||||
|
||||
var rawBytes = new Uint8Array(arr); // XXX: phantomjs doesn't like a buffer being passed here
|
||||
|
||||
var bytes = Buffer.from(rawBytes.buffer);
|
||||
|
||||
if (typeof cb === 'function') {
|
||||
return process.nextTick(function () {
|
||||
cb(null, bytes);
|
||||
});
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
69
app/components/blufi/crypto/lib/safe-buffer.js
Normal file
69
app/components/blufi/crypto/lib/safe-buffer.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* eslint-disable node/no-deprecated-api */
|
||||
var buffer = require("./buffer.js");
|
||||
|
||||
var Buffer = buffer.Buffer; // alternative to using Object.keys for old browsers
|
||||
|
||||
function copyProps(src, dst) {
|
||||
for (var key in src) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {
|
||||
module.exports = buffer;
|
||||
} else {
|
||||
// Copy properties from require('buffer')
|
||||
copyProps(buffer, exports);
|
||||
exports.Buffer = SafeBuffer;
|
||||
}
|
||||
|
||||
function SafeBuffer(arg, encodingOrOffset, length) {
|
||||
return Buffer(arg, encodingOrOffset, length);
|
||||
} // Copy static methods from Buffer
|
||||
|
||||
|
||||
copyProps(Buffer, SafeBuffer);
|
||||
|
||||
SafeBuffer.from = function (arg, encodingOrOffset, length) {
|
||||
if (typeof arg === 'number') {
|
||||
throw new TypeError('Argument must not be a number');
|
||||
}
|
||||
|
||||
return Buffer(arg, encodingOrOffset, length);
|
||||
};
|
||||
|
||||
SafeBuffer.alloc = function (size, fill, encoding) {
|
||||
if (typeof size !== 'number') {
|
||||
throw new TypeError('Argument must be a number');
|
||||
}
|
||||
|
||||
var buf = Buffer(size);
|
||||
|
||||
if (fill !== undefined) {
|
||||
if (typeof encoding === 'string') {
|
||||
buf.fill(fill, encoding);
|
||||
} else {
|
||||
buf.fill(fill);
|
||||
}
|
||||
} else {
|
||||
buf.fill(0);
|
||||
}
|
||||
|
||||
return buf;
|
||||
};
|
||||
|
||||
SafeBuffer.allocUnsafe = function (size) {
|
||||
if (typeof size !== 'number') {
|
||||
throw new TypeError('Argument must be a number');
|
||||
}
|
||||
|
||||
return Buffer(size);
|
||||
};
|
||||
|
||||
SafeBuffer.allocUnsafeSlow = function (size) {
|
||||
if (typeof size !== 'number') {
|
||||
throw new TypeError('Argument must be a number');
|
||||
}
|
||||
|
||||
return buffer.SlowBuffer(size);
|
||||
};
|
||||
149
app/components/blufi/crypto/md5.min.js
vendored
Normal file
149
app/components/blufi/crypto/md5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
113
app/components/blufi/other/onfire.js
Normal file
113
app/components/blufi/other/onfire.js
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
Copyright (c) 2016 hustcc http://www.atool.org/
|
||||
License: MIT
|
||||
https://github.com/hustcc/onfire.js
|
||||
**/
|
||||
|
||||
const __onfireEvents = {}
|
||||
let __cnt = 0 // event counter
|
||||
|
||||
const string_str = 'string'
|
||||
const function_str = 'function'
|
||||
const hasOwnKey = Function.call.bind(Object.hasOwnProperty)
|
||||
const slice = Function.call.bind(Array.prototype.slice)
|
||||
|
||||
function _bind(eventName, callback, is_one, context) {
|
||||
if (typeof eventName !== string_str || typeof callback !== function_str) {
|
||||
throw new Error('args: ' + string_str + ', ' + function_str + '')
|
||||
}
|
||||
|
||||
if (!hasOwnKey(__onfireEvents, eventName)) {
|
||||
__onfireEvents[eventName] = {}
|
||||
}
|
||||
|
||||
__onfireEvents[eventName][++__cnt] = [callback, is_one, context]
|
||||
return [eventName, __cnt]
|
||||
}
|
||||
|
||||
function _each(obj, callback) {
|
||||
for (const key in obj) {
|
||||
if (hasOwnKey(obj, key)) callback(key, obj[key])
|
||||
}
|
||||
}
|
||||
|
||||
function on(eventName, callback, context) {
|
||||
return _bind(eventName, callback, 0, context)
|
||||
}
|
||||
|
||||
function one(eventName, callback, context) {
|
||||
return _bind(eventName, callback, 1, context)
|
||||
}
|
||||
|
||||
function _fire_func(eventName, args) {
|
||||
if (hasOwnKey(__onfireEvents, eventName)) {
|
||||
_each(__onfireEvents[eventName], (key, item) => {
|
||||
item[0].apply(item[2], args) // do the function
|
||||
if (item[1]) delete __onfireEvents[eventName][key] // when is one, delete it after trigger
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function fire(eventName) {
|
||||
// fire events
|
||||
const args = slice(arguments, 1)
|
||||
setTimeout(() => {
|
||||
_fire_func(eventName, args)
|
||||
})
|
||||
}
|
||||
|
||||
function fireSync(eventName) {
|
||||
_fire_func(eventName, slice(arguments, 1))
|
||||
}
|
||||
|
||||
function un(event) {
|
||||
let eventName,
|
||||
key,
|
||||
r = false
|
||||
const type = typeof event
|
||||
|
||||
if (type === string_str) {
|
||||
// cancel the event name if exist
|
||||
if (hasOwnKey(__onfireEvents, event)) {
|
||||
delete __onfireEvents[event]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if (type === 'object') {
|
||||
eventName = event[0]
|
||||
key = event[1]
|
||||
|
||||
if (hasOwnKey(__onfireEvents, eventName) && hasOwnKey(__onfireEvents[eventName], key)) {
|
||||
delete __onfireEvents[eventName][key]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if (type === function_str) {
|
||||
_each(__onfireEvents, (key1, item1) => {
|
||||
_each(item1, (key2, item2) => {
|
||||
if (item2[0] === event) {
|
||||
delete __onfireEvents[key1][key2]
|
||||
r = true
|
||||
}
|
||||
})
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function clear() {
|
||||
for (const key in __onfireEvents) {
|
||||
delete __onfireEvents[key]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
on,
|
||||
one,
|
||||
un,
|
||||
fire,
|
||||
fireSync,
|
||||
clear,
|
||||
}
|
||||
519
app/components/blufi/util.js
Normal file
519
app/components/blufi/util.js
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
export const SERVICE_TYPE = '_notice_service._tcp.'
|
||||
export const FACE_DERECT = 'face_detect'
|
||||
export const FACE_RECOGNIZE = 'face_recognize'
|
||||
export const FACE_ENROll = 'face_enroll'
|
||||
export const STOP_STREAM = 'stop_stream'
|
||||
export const GET_ID = 'get_id'
|
||||
export const FACE_DELETE = 'face_delete'
|
||||
export const DELETE_ALL = 'delete_all'
|
||||
export const ON_STATUS = 1
|
||||
export const OFF_STATUS = 0
|
||||
export const HTTP_STR = 'http://null:80/control'
|
||||
export const HTTP_STATUS = 'http://null:80/status'
|
||||
export const HTTP_STREAM = 'http://null:81/stream'
|
||||
export const FRAME_CTRL_POSITION_ENCRYPTED = 0
|
||||
export const FRAME_CTRL_POSITION_CHECKSUM = 1
|
||||
export const FRAME_CTRL_POSITION_DATA_DIRECTION = 2
|
||||
export const FRAME_CTRL_POSITION_REQUIRE_ACK = 3
|
||||
export const FRAME_CTRL_POSITION_FRAG = 4
|
||||
export const DIRECTION_OUTPUT = 0
|
||||
export const DIRECTION_INPUT = 1
|
||||
export const AES_BASE_IV = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
export const NEG_SET_SEC_TOTAL_LEN = 0x00
|
||||
export const NEG_SET_SEC_ALL_DATA = 0x01
|
||||
export const PACKAGE_VALUE = 0x01
|
||||
export const SUBTYPE_NEG = 0x00
|
||||
export const SUBTYPE_WIFI_MODEl = 0x02
|
||||
export const SUBTYPE_END = 0x03
|
||||
export const PACKAGE_CONTROL_VALUE = 0x00
|
||||
export const SUBTYPE_WIFI_NEG = 0x09
|
||||
export const SUBTYPE_SET_SSID = 0x2
|
||||
export const SUBTYPE_SET_PWD = 0x3
|
||||
export const SUBTYPE_WIFI_LIST_NEG = 11
|
||||
export const SUBTYPE_NEGOTIATION_NEG = 0
|
||||
export const SUBTYPE_CUSTOM_DATA = 0x13
|
||||
export const DH_P =
|
||||
'cf5cf5c38419a724957ff5dd323b9c45c3cdd261eb740f69aa94b8bb1a5c96409153bd76b24222d03274e4725a5406092e9e82e9135c643cae98132b0d95f7d65347c68afc1e677da90e51bbab5f5cf429c291b4ba39c6b2dc5e8c7231e46aa7728e87664532cdf547be20c9a3fa8342be6e34371a27c06f7dc0edddd2f86373'
|
||||
export const DH_G = '02'
|
||||
export const descSucListEN = [
|
||||
'Bluetooth connecting...',
|
||||
'Bluetooth connection successful',
|
||||
'Device information is successfully obtained',
|
||||
'Attribute information is successfully obtained',
|
||||
'Send configuration information...',
|
||||
'Configuration information sent successfully',
|
||||
'Connection successfully',
|
||||
]
|
||||
|
||||
export const descFailListEN = [
|
||||
'Bluetooth connection failed',
|
||||
'Device information acquisition failed',
|
||||
'Attribute information acquisition failed',
|
||||
'Configuration information sent failed',
|
||||
'Distribution network failed',
|
||||
]
|
||||
|
||||
export const descSucList = [
|
||||
'蓝牙连接...',
|
||||
'蓝牙连接成功',
|
||||
'设备信息已成功获取',
|
||||
'属性信息已成功获取',
|
||||
'发送配置信息...',
|
||||
'成功发送配置信息',
|
||||
'成功连接',
|
||||
]
|
||||
|
||||
export const descFailList = [
|
||||
'蓝牙连接失败',
|
||||
'设备信息获取失败',
|
||||
'属性信息获取失败',
|
||||
'配置信息发送失败',
|
||||
'网络配置失败',
|
||||
'蓝牙异常断开',
|
||||
]
|
||||
|
||||
export const successList = {
|
||||
0: 'NULL',
|
||||
1: 'STA',
|
||||
2: 'SoftAP',
|
||||
3: 'SoftAP & STA',
|
||||
}
|
||||
|
||||
export const failList = {
|
||||
0: 'sequence error',
|
||||
1: 'checksum error',
|
||||
2: 'decrypt error',
|
||||
3: 'encrypt error',
|
||||
4: 'init security error',
|
||||
5: 'dh malloc error',
|
||||
6: 'dh param error',
|
||||
7: 'read param error',
|
||||
8: 'make public error',
|
||||
}
|
||||
export const CRC_TB = [
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
|
||||
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||
0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
|
||||
0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
|
||||
0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
|
||||
0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
|
||||
0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
|
||||
0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
|
||||
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
|
||||
0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
|
||||
0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
|
||||
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
|
||||
] //转16进制
|
||||
// ArrayBuffer转16进制字符串
|
||||
export const ab2hex = buffer => {
|
||||
const hexArr = Array.from(new Uint8Array(buffer), bit => bit.toString(16).padStart(2, '0'))
|
||||
return hexArr
|
||||
}
|
||||
|
||||
// 16进制字符串转普通字符串
|
||||
export const hexCharCodeToStr = hexCharCodeStr => {
|
||||
const trimedStr = hexCharCodeStr.trim()
|
||||
const rawStr = trimedStr.toLowerCase().startsWith('0x') ? trimedStr.slice(2) : trimedStr
|
||||
const len = rawStr.length
|
||||
|
||||
if (len % 2 !== 0) {
|
||||
console.error('Illegal Format ASCII Code!')
|
||||
return ''
|
||||
}
|
||||
|
||||
const resultStr = []
|
||||
for (let i = 0; i < len; i += 2) {
|
||||
const curCharCode = parseInt(rawStr.substr(i, 2), 16)
|
||||
resultStr.push(String.fromCharCode(curCharCode))
|
||||
}
|
||||
|
||||
return resultStr.join('')
|
||||
}
|
||||
|
||||
// 过滤设备列表
|
||||
export const filterDevice = (devices, filterName) => {
|
||||
const re = new RegExp(`^(${filterName})`)
|
||||
return devices.filter(device => re.test(device.name))
|
||||
}
|
||||
|
||||
// 获取类型
|
||||
export const getType = (pkgType, subType) => {
|
||||
return (subType << 2) | pkgType
|
||||
}
|
||||
|
||||
// Uint8Array转数组
|
||||
export const uint8ArrayToArray = uint8Array => {
|
||||
return Array.from(uint8Array)
|
||||
}
|
||||
|
||||
// 16进制转二进制数组
|
||||
export const hexToBinArray = str => {
|
||||
const dec = parseInt(str, 16)
|
||||
let bin = dec.toString(2)
|
||||
const len = bin.length
|
||||
|
||||
if (len < 8) {
|
||||
bin = bin.padStart(8, '0')
|
||||
}
|
||||
|
||||
return bin.split('')
|
||||
}
|
||||
|
||||
// 16进制字符串转数组
|
||||
export const hexByArray = str => {
|
||||
let formattedStr = str
|
||||
if (str.length % 2 !== 0) {
|
||||
formattedStr = '0' + str
|
||||
}
|
||||
|
||||
const arr = []
|
||||
for (let i = 0; i < formattedStr.length; i += 2) {
|
||||
arr.push(formattedStr.substring(i, i + 2))
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
// 16进制字符串转整型数组
|
||||
export const hexByInt = str => {
|
||||
let formattedStr = str
|
||||
if (str.length % 2 !== 0) {
|
||||
formattedStr = '0' + str
|
||||
}
|
||||
|
||||
const arr = []
|
||||
for (let i = 0; i < formattedStr.length; i += 2) {
|
||||
arr.push(parseInt(formattedStr.substring(i, i + 2), 16))
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
// 排序函数
|
||||
export const sortBy = (attr, rev = 1) => {
|
||||
return (a, b) => {
|
||||
const aVal = a[attr]
|
||||
const bVal = b[attr]
|
||||
|
||||
if (aVal < bVal) return rev * -1
|
||||
if (aVal > bVal) return rev * 1
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否为空
|
||||
export const _isEmpty = str => {
|
||||
return str === '' || str === null || str === undefined || str === 'null' || str === 'undefined'
|
||||
}
|
||||
|
||||
// 设置失败背景色
|
||||
export const setFailBg = () => {
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: '#ffffff',
|
||||
backgroundColor: '#737d89',
|
||||
})
|
||||
}
|
||||
|
||||
// 设置成功背景色
|
||||
export const setSucBg = () => {
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: '#ffffff',
|
||||
backgroundColor: '#4d9efb',
|
||||
})
|
||||
}
|
||||
|
||||
// 组装数据格式
|
||||
export const writeData = (type, subType, frameCtl, seq, len, data = []) => {
|
||||
const value = []
|
||||
const combinedType = getType(type, subType)
|
||||
value.push(combinedType, frameCtl, seq, len)
|
||||
|
||||
if (data.length > 0) {
|
||||
value.push(...data)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// 分包处理
|
||||
export const isSubcontractor = (data, checksum, sequence, encrypt) => {
|
||||
const total = data.length
|
||||
let lenData = []
|
||||
let laveData = []
|
||||
let flag = false
|
||||
|
||||
if (total > 16) {
|
||||
if (checksum) {
|
||||
lenData = data.slice(0, 12)
|
||||
laveData = data.slice(12)
|
||||
} else {
|
||||
lenData = data.slice(0, 14)
|
||||
laveData = data.slice(14)
|
||||
}
|
||||
|
||||
const len1 = (total >> 8) & 0xff
|
||||
const len2 = total & 0xff
|
||||
lenData.unshift(len1, len2)
|
||||
flag = true
|
||||
} else {
|
||||
lenData = data
|
||||
}
|
||||
|
||||
const len = lenData.length
|
||||
|
||||
if (checksum) {
|
||||
lenData = assemblyChecksum(lenData, len, sequence)
|
||||
}
|
||||
|
||||
return {
|
||||
len,
|
||||
lenData,
|
||||
laveData,
|
||||
flag,
|
||||
}
|
||||
}
|
||||
|
||||
// 组装校验和
|
||||
export const assemblyChecksum = (list, len, sequence) => {
|
||||
const checkData = [sequence, len, ...list]
|
||||
const crc = caluCRC(0, checkData)
|
||||
const checksumByte1 = crc & 0xff
|
||||
const checksumByte2 = (crc >> 8) & 0xff
|
||||
|
||||
return [...list, checksumByte1, checksumByte2]
|
||||
}
|
||||
|
||||
// 加密数据
|
||||
export const encrypt = (aesjs, md5Key, sequence, data, checksum) => {
|
||||
const iv = generateAESIV(sequence)
|
||||
let list = []
|
||||
let sumArr = []
|
||||
|
||||
if (checksum) {
|
||||
const len = data.length - 2
|
||||
list = data.slice(0, len)
|
||||
sumArr = data.slice(len)
|
||||
} else {
|
||||
list = data
|
||||
}
|
||||
|
||||
const encryptData = uint8ArrayToArray(blueAesEncrypt(aesjs, md5Key, iv, new Uint8Array(list)))
|
||||
|
||||
return [...encryptData, ...sumArr]
|
||||
}
|
||||
|
||||
// DH加密
|
||||
export const blueDH = (p, g, crypto) => {
|
||||
const client = crypto.createDiffieHellman(p, 'hex', g, 'hex')
|
||||
client.generateKeys()
|
||||
return client
|
||||
}
|
||||
|
||||
// MD5加密
|
||||
export const blueMd5 = (md5, key) => {
|
||||
return md5.array(key)
|
||||
}
|
||||
|
||||
// AES加密
|
||||
export const blueAesEncrypt = (aesjs, mdKey, iv, bytes) => {
|
||||
const aesOfb = new aesjs.ModeOfOperation.ofb(mdKey, iv)
|
||||
return aesOfb.encrypt(bytes)
|
||||
}
|
||||
|
||||
// AES解密
|
||||
export const blueAesDecrypt = (aesjs, mdKey, iv, bytes) => {
|
||||
const aesOfb = new aesjs.ModeOfOperation.ofb(mdKey, iv)
|
||||
return aesOfb.decrypt(bytes)
|
||||
}
|
||||
|
||||
// 获取Frame Control值
|
||||
export const getFrameCTRLValue = (encrypted, checksum, direction, requireAck, frag) => {
|
||||
let frame = 0
|
||||
|
||||
if (encrypted) frame |= 1 << FRAME极狐_CTRL_POSITION_ENCRYPTED
|
||||
if (checksum) frame |= 1 << FRAME_CTRL_POSITION_CHECKSUM
|
||||
if (direction === DIRECTION_INPUT) frame |= 1 << FRAME_CTRL_POSITION_DATA_DIRECTION
|
||||
if (requireAck) frame |= 1 << FRAME_CTRL_POSITION_REQUIRE_ACK
|
||||
if (frag) frame |= 1 << FRAME_CTRL_POSITION_FRAG
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
// 生成AES IV
|
||||
export const generateAESIV = sequence => {
|
||||
return AES_BASE_IV.map((val, i) => (i === 0 ? sequence : val))
|
||||
}
|
||||
|
||||
// 计算CRC
|
||||
export const caluCRC = (crc, pByte) => {
|
||||
crc = ~crc & 0xffff
|
||||
|
||||
for (const byte of pByte) {
|
||||
crc = CRC_TB[((crc & 0xffff) >> 8) ^ (byte & 0xff)] ^ ((crc & 0xffff) << 8)
|
||||
}
|
||||
|
||||
return ~crc & 0xffff
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
export const send = (method, url, data, suc, error) => {
|
||||
console.log(url)
|
||||
uni.request({
|
||||
url,
|
||||
data,
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
success: res => {
|
||||
suc?.(res.data)
|
||||
},
|
||||
fail: () => {
|
||||
error?.()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 显示加载中
|
||||
export const showLoading = msg => {
|
||||
uni.hideLoading()
|
||||
uni.showLoading({
|
||||
title: msg,
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 显示提示
|
||||
export const showToast = msg => {
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
}
|
||||
|
||||
// OTA擦除
|
||||
export const write_ota_erase = (cmd, len, addr, block_num) => {
|
||||
return [cmd, 4, 256, 1, 54, 0]
|
||||
}
|
||||
|
||||
// OTA编程
|
||||
export const write_ota_program = (cmd, len, addr, buf) => {
|
||||
const value = []
|
||||
const offest = addr
|
||||
let sum = 0
|
||||
|
||||
if (cmd === 0x80) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
sum += buf.getUint8(i + offest)
|
||||
}
|
||||
}
|
||||
|
||||
let calculatedAddr = addr + 4 * 1024
|
||||
calculatedAddr = calculatedAddr / 16
|
||||
|
||||
value.push(cmd, len, calculatedAddr & 0xff, calculatedAddr >> 8)
|
||||
|
||||
if (cmd === 0x80) {
|
||||
value.push(sum & 0xff, (sum & 0xff00) >> 8, 0, 0)
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
value.push(buf.getUint8(i + offest))
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// OTA校验和
|
||||
export const write_ota_sum = (cmd, len, data) => {
|
||||
return [
|
||||
cmd,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
len & 0xff,
|
||||
(len >> 8) & 0xff,
|
||||
(len >> 16) & 0xff,
|
||||
(len >> 24) & 0xff,
|
||||
data & 0xff,
|
||||
(data >> 8) & 0xff,
|
||||
(data >> 16) & 0xff,
|
||||
(data >> 24) & 0xff,
|
||||
]
|
||||
}
|
||||
|
||||
export default {
|
||||
_isEmpty,
|
||||
send,
|
||||
SERVICE_TYPE,
|
||||
FACE_DERECT,
|
||||
FACE_RECOGNIZE,
|
||||
FACE_ENROll,
|
||||
STOP_STREAM,
|
||||
GET_ID,
|
||||
ON_STATUS,
|
||||
OFF_STATUS,
|
||||
HTTP_STR,
|
||||
HTTP_STATUS,
|
||||
HTTP_STREAM,
|
||||
FACE_DELETE,
|
||||
DELETE_ALL,
|
||||
ab2hex,
|
||||
hexCharCodeToStr,
|
||||
filterDevice,
|
||||
getType,
|
||||
hexToBinArray,
|
||||
hexByArray,
|
||||
hexByInt,
|
||||
sortBy,
|
||||
setFailBg,
|
||||
setSucBg,
|
||||
writeData,
|
||||
isSubcontractor,
|
||||
getFrameCTRLValue,
|
||||
blueDH,
|
||||
blueMd5,
|
||||
blueAesEncrypt,
|
||||
blueAesDecrypt,
|
||||
uint8ArrayToArray,
|
||||
generateAESIV,
|
||||
caluCRC,
|
||||
encrypt,
|
||||
DH_P,
|
||||
DH_G,
|
||||
DIRECTION_OUTPUT,
|
||||
DIRECTION_INPUT,
|
||||
NEG_SET_SEC_TOTAL_LEN,
|
||||
NEG_SET_SEC_ALL_DATA,
|
||||
PACKAGE_VALUE,
|
||||
SUBTYPE_NEG,
|
||||
PACKAGE_CONTROL_VALUE,
|
||||
SUBTYPE_WIFI_NEG,
|
||||
SUBTYPE_WIFI_LIST_NEG,
|
||||
SUBTYPE_NEGOTIATION_NEG,
|
||||
SUBTYPE_WIFI_MODEl,
|
||||
SUBTYPE_SET_SSID,
|
||||
SUBTYPE_SET_PWD,
|
||||
SUBTYPE_END,
|
||||
SUBTYPE_CUSTOM_DATA,
|
||||
descSucList,
|
||||
descFailList,
|
||||
successList,
|
||||
failList,
|
||||
showToast,
|
||||
showLoading,
|
||||
write_ota_sum,
|
||||
write_ota_program,
|
||||
write_ota_erase,
|
||||
}
|
||||
1657
app/components/blufi/xBlufi-wx-impl.js
Normal file
1657
app/components/blufi/xBlufi-wx-impl.js
Normal file
File diff suppressed because it is too large
Load Diff
169
app/components/blufi/xBlufi.js
Normal file
169
app/components/blufi/xBlufi.js
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import mOnFire from './other/onfire.js'
|
||||
import $wxBlufiImpl from './xBlufi-wx-impl.js' // 0表示阿里支付宝小程序 1表示微信小程序
|
||||
|
||||
export const XMQTT_SYSTEM = {
|
||||
Alis: 0,
|
||||
WeChat: 1,
|
||||
}
|
||||
|
||||
export const XBLUFI_TYPE = {
|
||||
TYPE_STATUS_CONNECTED: '-2', // 设备连接状态回调
|
||||
TYPE_CLOSE_CONNECTED: '-1', // 主动关闭连接
|
||||
TYPE_CONNECTED: '0', // 主动连接
|
||||
TYPE_GET_DEVICE_LISTS: '1', // 发现设备列表回调
|
||||
TYPE_INIT_ESP32_RESULT: '2',
|
||||
TYPE_RECIEVE_CUSTON_DATA: '3', // 接收到自定义数据
|
||||
TYPE_CONNECT_ROUTER_RESULT: '4',
|
||||
TYPE_RECIEVE_MY_DATA: '5',
|
||||
TYPE_GET_DEVICE_LISTS_START: '41', // 发现设备列表回调开始
|
||||
TYPE_GET_DEVICE_LISTS_STOP: '42', // 停止发现设备列表回调
|
||||
}
|
||||
|
||||
export const OnFireEvent = {
|
||||
EVENT_START_DISCONORY: '0', // 蓝牙状态事件 发现设备
|
||||
EVENT_CONNECT_DISCONNECT: '1', // 通知连接或断开蓝牙
|
||||
EVENT_NOFITY_INIT_ESP32: '3', // 通知获取蓝牙设备的服务uuid列表等初始化工作
|
||||
ENENT_ALL: '6',
|
||||
EVENT_NOFITY_SEND_ROUTER_SSID_PASSWORD: '50', // 通知发送路由器的ssid和password
|
||||
EVENT_NOFITY_SEND_CUSTON_DATA: '51', // 通知发送自定义数据
|
||||
}
|
||||
|
||||
let once = 0
|
||||
let myPath = ''
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param {number} type 参考 XMQTT_SYSTEM
|
||||
*/
|
||||
export const initXBlufi = type => {
|
||||
if (type === XMQTT_SYSTEM.WeChat && once === 0) {
|
||||
console.log('初始化@@@@@@@!!!!!!!!!!!!')
|
||||
once = 1
|
||||
$wxBlufiImpl.init()
|
||||
}
|
||||
}
|
||||
|
||||
export const setPath = path => {
|
||||
myPath = path
|
||||
}
|
||||
|
||||
export const getPath = () => myPath
|
||||
|
||||
export const notifyDeviceMsgEvent = options => {
|
||||
mOnFire.fire(OnFireEvent.ENENT_ALL, options)
|
||||
}
|
||||
|
||||
export const listenDeviceMsgEvent = (isSetListener, callback) => {
|
||||
isSetListener ? mOnFire.on(OnFireEvent.ENENT_ALL, callback) : mOnFire.un(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始或停止发现附近的蓝牙设备
|
||||
* @param {Object} options 连接参数 {"isStart":true , "filter":"名字过滤"} :是否开始发现设备
|
||||
*/
|
||||
export const notifyStartDiscoverBle = options => {
|
||||
mOnFire.fire(OnFireEvent.EVENT_START_DISCONORY, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听开始/停止发现设备事件
|
||||
* @param {boolean} isSetListener 是否设置监听
|
||||
* @param {Function} callback 回调函数
|
||||
*/
|
||||
export const listenStartDiscoverBle = (isSetListener, callback) => {
|
||||
isSetListener ? mOnFire.on(OnFireEvent.EVENT_START_DISCONORY, callback) : mOnFire.un(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接或断开蓝牙连接
|
||||
* @param {Object} options 连接参数 {"connect":true,"deviceID":"设备id,蓝牙发现列表获取"}
|
||||
*/
|
||||
export const notifyConnectBle = options => {
|
||||
mOnFire.fire(OnFireEvent.EVENT_CONNECT_DISCONNECT, options)
|
||||
}
|
||||
|
||||
export const listenConnectBle = (isSetListener, callback) => {
|
||||
isSetListener ? mOnFire.on(OnFireEvent.EVENT_CONNECT_DISCONNECT, callback) : mOnFire.un(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知初始化获取设备的服务列表等信息
|
||||
* @param {Object} options 连接参数 {"deviceId":"设备的设备id"}
|
||||
*/
|
||||
export const notifyInitBleEsp32 = options => {
|
||||
mOnFire.fire(OnFireEvent.EVENT_NOFITY_INIT_ESP32, options)
|
||||
}
|
||||
|
||||
export const listenInitBleEsp32 = (isSetListener, callback) => {
|
||||
isSetListener ? mOnFire.on(OnFireEvent.EVENT_NOFITY_INIT_ESP32, callback) : mOnFire.un(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送要连接的路由器的ssid和密码
|
||||
* @param {Object} options 连接参数 {"deviceId":"设备的设备id","serverId":"服务id","characterId":"通道","ssid":"路由器名字","password":"密码"}
|
||||
*/
|
||||
export const notifySendRouterSsidAndPassword = options => {
|
||||
mOnFire.fire(OnFireEvent.EVENT_NOFITY_SEND_ROUTER_SSID_PASSWORD, options)
|
||||
}
|
||||
|
||||
export const listenSendRouterSsidAndPassword = (isSetListener, callback) => {
|
||||
isSetListener
|
||||
? mOnFire.on(OnFireEvent.EVENT_NOFITY_SEND_ROUTER_SSID_PASSWORD, callback)
|
||||
: mOnFire.un(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送自定义数据
|
||||
* @param {Object} options 连接参数 {"deviceId":"设备的设备id","serverId":"服务id","characterId":"通道","customData":"自定义数据""}
|
||||
*/
|
||||
export const notifySendCustomData = options => {
|
||||
mOnFire.fire(OnFireEvent.EVENT_NOFITY_SEND_CUSTON_DATA, options)
|
||||
}
|
||||
|
||||
export const listenSendCustomData = (isSetListener, callback) => {
|
||||
isSetListener
|
||||
? mOnFire.on(OnFireEvent.EVENT_NOFITY_SEND_CUSTON_DATA, callback)
|
||||
: mOnFire.un(callback)
|
||||
}
|
||||
|
||||
export const mDeviceEvent = {
|
||||
XMQTT_SYSTEM,
|
||||
XBLUFI_TYPE,
|
||||
OnFireEvent,
|
||||
notifyDeviceMsgEvent,
|
||||
listenDeviceMsgEvent,
|
||||
notifyStartDiscoverBle,
|
||||
listenStartDiscoverBle,
|
||||
notifyConnectBle,
|
||||
listenConnectBle,
|
||||
notifyInitBleEsp32,
|
||||
listenInitBleEsp32,
|
||||
notifySendRouterSsidAndPassword,
|
||||
listenSendRouterSsidAndPassword,
|
||||
notifySendCustomData,
|
||||
listenSendCustomData,
|
||||
initXBlufi,
|
||||
setPath,
|
||||
getPath,
|
||||
}
|
||||
|
||||
export default {
|
||||
XMQTT_SYSTEM,
|
||||
XBLUFI_TYPE,
|
||||
OnFireEvent,
|
||||
notifyDeviceMsgEvent,
|
||||
listenDeviceMsgEvent,
|
||||
notifyStartDiscoverBle,
|
||||
listenStartDiscoverBle,
|
||||
notifyConnectBle,
|
||||
listenConnectBle,
|
||||
notifyInitBleEsp32,
|
||||
listenInitBleEsp32,
|
||||
notifySendRouterSsidAndPassword,
|
||||
listenSendRouterSsidAndPassword,
|
||||
notifySendCustomData,
|
||||
listenSendCustomData,
|
||||
initXBlufi,
|
||||
setPath,
|
||||
getPath,
|
||||
}
|
||||
|
|
@ -4,14 +4,15 @@
|
|||
<section class="relative py-16 md:py-24 lg:py-32 overflow-hidden">
|
||||
<!-- 背景图片 -->
|
||||
<div class="absolute inset-0">
|
||||
<img
|
||||
src="https://picsum.photos/1920/1080?random=tech-background"
|
||||
alt="技术背景"
|
||||
class="w-full h-full object-cover opacity-20 dark:opacity-10"
|
||||
<img
|
||||
alt="技术背景"
|
||||
class=" w-full h-full object-cover opacity-20 dark:opacity-10"
|
||||
src="https://picsum.photos/1920/1080?random=tech-background"
|
||||
>
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-blue-50/80 to-indigo-100/80 dark:from-gray-900/80 dark:to-gray-800/80"></div>
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-br from-blue-50/80 to-indigo-100/80 dark:from-gray-900/80 dark:to-gray-800/80"/>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 内容 -->
|
||||
<div class="relative z-10 container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center">
|
||||
|
|
@ -32,38 +33,40 @@
|
|||
<!-- 主内容卡片 -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl overflow-hidden">
|
||||
<div class="p-8 md:p-12 lg:p-16">
|
||||
|
||||
|
||||
<!-- 产品列表 -->
|
||||
<div class="space-y-16 md:space-y-20">
|
||||
<div
|
||||
v-for="(product, index) in pageData.products"
|
||||
:key="product.id"
|
||||
class="group"
|
||||
<div
|
||||
v-for="(product, index) in pageData.products"
|
||||
:key="product.id"
|
||||
class="group"
|
||||
>
|
||||
<!-- 产品项目 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 items-center"
|
||||
:class="{ 'lg:grid-flow-col-dense': index % 2 === 1 }">
|
||||
|
||||
<div
|
||||
:class="{ 'lg:grid-flow-col-dense': index % 2 === 1 }"
|
||||
class="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 items-center">
|
||||
|
||||
<!-- 产品图片 -->
|
||||
<div
|
||||
class="relative order-2 lg:order-1"
|
||||
:class="{ 'lg:order-2': index % 2 === 1 }"
|
||||
<div
|
||||
:class="{ 'lg:order-2': index % 2 === 1 }"
|
||||
class="relative order-2 lg:order-1"
|
||||
>
|
||||
<div class="relative overflow-hidden rounded-2xl bg-gray-100 dark:bg-gray-700 p-8">
|
||||
<img
|
||||
:src="product.image.src"
|
||||
:alt="product.image.alt"
|
||||
class="w-full h-64 md:h-80 object-contain transition-transform duration-300 group-hover:scale-105"
|
||||
<img
|
||||
:alt="product.image.alt"
|
||||
:src="product.image.src"
|
||||
class="w-full h-64 md:h-80 object-contain transition-transform duration-300 group-hover:scale-105"
|
||||
>
|
||||
<!-- 装饰性背景 -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-blue-100/50 to-indigo-200/50 dark:from-blue-900/30 dark:to-indigo-800/30 rounded-2xl"></div>
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-br from-blue-100/50 to-indigo-200/50 dark:from-blue-900/30 dark:to-indigo-800/30 rounded-2xl"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产品信息 -->
|
||||
<div
|
||||
class="order-1 lg:order-2"
|
||||
:class="{ 'lg:order-1': index % 2 === 1 }"
|
||||
<div
|
||||
:class="{ 'lg:order-1': index % 2 === 1 }"
|
||||
class="order-1 lg:order-2"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<!-- 产品标题 -->
|
||||
|
|
@ -73,10 +76,10 @@
|
|||
|
||||
<!-- 产品描述 -->
|
||||
<div class="space-y-4">
|
||||
<p
|
||||
v-for="(paragraph, pIndex) in product.description"
|
||||
:key="pIndex"
|
||||
class="text-lg text-gray-600 dark:text-gray-300 leading-relaxed"
|
||||
<p
|
||||
v-for="(paragraph, pIndex) in product.description"
|
||||
:key="pIndex"
|
||||
class="text-lg text-gray-600 dark:text-gray-300 leading-relaxed"
|
||||
>
|
||||
{{ paragraph }}
|
||||
</p>
|
||||
|
|
@ -86,14 +89,14 @@
|
|||
<div v-if="product.features" class="space-y-3">
|
||||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">主要特性:</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="feature in product.features"
|
||||
:key="feature"
|
||||
class="flex items-start space-x-3"
|
||||
<li
|
||||
v-for="feature in product.features"
|
||||
:key="feature"
|
||||
class="flex items-start space-x-3"
|
||||
>
|
||||
<UIcon
|
||||
name="i-lucide-check-circle"
|
||||
class="w-5 h-5 text-green-500 dark:text-green-400 mt-0.5 flex-shrink-0"
|
||||
<UIcon
|
||||
class="w-5 h-5 text-green-500 dark:text-green-400 mt-0.5 flex-shrink-0"
|
||||
name="i-lucide-check-circle"
|
||||
/>
|
||||
<span class="text-gray-600 dark:text-gray-300">{{ feature }}</span>
|
||||
</li>
|
||||
|
|
@ -102,14 +105,14 @@
|
|||
|
||||
<!-- 了解更多按钮 -->
|
||||
<div class="pt-4">
|
||||
<NuxtLink
|
||||
:to="pageData.learnMoreButton.link"
|
||||
class="inline-flex items-center space-x-2 px-6 py-3 bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 text-white font-semibold rounded-lg transition-colors duration-200 shadow-lg hover:shadow-xl"
|
||||
<NuxtLink
|
||||
:to="pageData.learnMoreButton.link"
|
||||
class="inline-flex items-center space-x-2 px-6 py-3 bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 text-white font-semibold rounded-lg transition-colors duration-200 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
<span>{{ pageData.learnMoreButton.text }}</span>
|
||||
<UIcon
|
||||
name="i-lucide-arrow-right"
|
||||
class="w-4 h-4 transition-transform duration-200 group-hover:translate-x-1"
|
||||
<UIcon
|
||||
class="w-4 h-4 transition-transform duration-200 group-hover:translate-x-1"
|
||||
name="i-lucide-arrow-right"
|
||||
/>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
|
|
|||
12
app/layouts/empty.vue
Normal file
12
app/layouts/empty.vue
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<UContainer>
|
||||
<AppHeader/>
|
||||
<slot/>
|
||||
|
||||
</UContainer>
|
||||
</div>
|
||||
</template>
|
||||
15
app/pages/IoTSolutions/blueToothTest.vue
Normal file
15
app/pages/IoTSolutions/blueToothTest.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
layout: 'empty'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<BlueToothTestItem1/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user