蓝牙模块

This commit is contained in:
WindowBird 2025-10-08 09:43:52 +08:00
parent 7c8cc6a87d
commit 14ed880ef3
24 changed files with 11602 additions and 42 deletions

View File

@ -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',
}
]
},

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
<script lang="ts" setup>
</script>
<template>
</template>
<style scoped>
</style>

File diff suppressed because it is too large Load Diff

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

View 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('');
}

View 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为truefoundDevices发现的设备列表
* 若发现失败参数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

File diff suppressed because it is too large Load Diff

View 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) {}
}

File diff suppressed because it is too large Load Diff

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

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

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

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

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

View 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

File diff suppressed because one or more lines are too long

View 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,
}

View 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,
}

File diff suppressed because it is too large Load Diff

View 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,
}

View File

@ -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
View File

@ -0,0 +1,12 @@
<script lang="ts" setup>
</script>
<template>
<div>
<UContainer>
<AppHeader/>
<slot/>
</UContainer>
</div>
</template>

View File

@ -0,0 +1,15 @@
<script lang="ts" setup>
definePageMeta({
layout: 'empty'
})
</script>
<template>
<view>
<BlueToothTestItem1/>
</view>
</template>
<style scoped>
</style>