Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
// H5平台 Web Bluetooth 设备扫描实现
import { DeviceManager } from './device-manager.uts';
import { ServiceManager } from './service-manager.uts';
import type { BleDevice, BleOptions, BleConnectOptionsExt, BleDataReceivedCallback, BleConnectionStateChangeCallback } from '../interface.uts'
const DEFAULT_OPTIONAL_SERVICES = [
'00001800-0000-1000-8000-00805f9b34fb', // GAP
'0000180a-0000-1000-8000-00805f9b34fb', // Device Information
'0000180f-0000-1000-8000-00805f9b34fb', // Battery
'00001812-0000-1000-8000-00805f9b34fb', // Human Interface Device
'0000fe59-0000-1000-8000-00805f9b34fb', // Nordic DFU / vendor specific
'6e400001-b5a3-f393-e0a9-e50e24dcca9e', // Nordic UART primary service
'6e400010-b5a3-f393-e0a9-e50e24dcca9e', // Custom health service (observed on Android)
'6e400020-b5a3-f393-e0a9-e50e24dcca9e' // Additional vendor service
];
export const BLE_SERVICE_PREFIXES = [
'6e4000',
'0000180f',
'00001812',
'0000fe59'
];
function normalizeServiceUuid(uuid: string): string {
if (!uuid) return uuid;
let u = uuid.trim().toLowerCase();
if (u.startsWith('0x')) {
u = u.slice(2);
}
if (/^[0-9a-f]{4}$/.test(u)) {
return `0000${u}-0000-1000-8000-00805f9b34fb`;
}
return u;
}
function mergeOptionalServices(userServices?: string[]): string[] {
const set = new Set<string>();
DEFAULT_OPTIONAL_SERVICES.forEach((svc) => set.add(normalizeServiceUuid(svc)));
if (userServices != null) {
for (let i = 0; i < userServices.length; i++) {
const normalized = normalizeServiceUuid(userServices[i]);
if (normalized != null && normalized !== '') {
set.add(normalized);
}
}
}
return Array.from(set);
}
// 实例化各个管理器
const deviceManager = new DeviceManager();
const serviceManager = new ServiceManager();
// 导出简化接口
export const scanDevices = async (options?: { optionalServices?: string[] }) => {
const mergedOptions = options != null ? { ...options } : {};
mergedOptions.optionalServices = mergeOptionalServices(options?.optionalServices ?? []);
return deviceManager.startScan(mergedOptions);
};
export const connectDevice = async (deviceId: string, options?: BleConnectOptionsExt) => deviceManager.connectDevice(deviceId, options);
export const disconnectDevice = async (deviceId: string) => deviceManager.disconnectDevice(deviceId);
export const getConnectedDevices = () => deviceManager.getConnectedDevices();
export const getKnownDevices = () => Object.keys((deviceManager as any).devices || {})
export const discoverServices = async (deviceId: string) => {
// 获取 server 实例
const server = deviceManager.getServer(deviceId)
if (!server) throw new Error(`设备未连接: ${deviceId}`)
return serviceManager.discoverServices(deviceId, server);
};
export const getCharacteristics = async (deviceId: string, serviceId: string) => serviceManager.getCharacteristics(deviceId, serviceId);
export const writeCharacteristic = async (deviceId: string, serviceId: string, characteristicId: string, data: string | ArrayBuffer) => serviceManager.writeCharacteristic(deviceId, serviceId, characteristicId, data);
export const subscribeCharacteristic = async (deviceId: string, serviceId: string, characteristicId: string, callback) => {
console.log('[bluetooth_manager] subscribeCharacteristic called:', deviceId, serviceId, characteristicId)
return serviceManager.subscribeCharacteristic(deviceId, serviceId, characteristicId, callback);
}
export const unsubscribeCharacteristic = async (deviceId: string, serviceId: string, characteristicId: string) => serviceManager.unsubscribeCharacteristic(deviceId, serviceId, characteristicId);
export const readCharacteristic = async (deviceId: string, serviceId: string, characteristicId: string) => serviceManager.readCharacteristic(deviceId, serviceId, characteristicId);
export const sendCommand = async (deviceId: string, serviceId: string, writeCharId: string, notifyCharId: string, command: string, params: any = null, timeout: number = 5000) => dataProcessor.sendAndReceive(deviceId, serviceId, writeCharId, notifyCharId, command, params, timeout);
// Event adapter helpers: translate DeviceManager callbacks into payload objects
export const onDeviceFound = (listener) => deviceManager.onDeviceFound((device) => {
try { listener({ device }); } catch (e) { /* ignore listener errors */ }
});
export const onScanFinished = (listener) => deviceManager.onScanFinished(() => {
try { listener({}); } catch (e) {}
});
export const onConnectionStateChange = (listener) => deviceManager.onConnectionStateChange((deviceId, state) => {
try { listener({ device: { deviceId }, state }); } catch (e) {}
});
/**
* 自动连接并初始化蓝牙设备获取可用serviceId、writeCharId、notifyCharId
* @param deviceId 设备ID
* @returns {Promise<{serviceId: string, writeCharId: string, notifyCharId: string}>}
*/
export const autoConnect = async (deviceId: string): Promise<{serviceId: string, writeCharId: string, notifyCharId: string}> => {
// 1. 连接设备
await connectDevice(deviceId);
// 2. 服务发现
const services = await discoverServices(deviceId);
if (!services || services.length === 0) throw new Error('未发现服务');
// 3. 获取私有serviceId优先bae前缀或通过dataProcessor模板
let serviceId = '';
for (const s of services) {
if (s.uuid && BLE_SERVICE_PREFIXES.some(prefix => s.uuid.startsWith(prefix))) {
serviceId = s.uuid;
break;
}
}
if (!serviceId) {
// 可扩展通过dataProcessor获取模板serviceId
serviceId = services[0].uuid;
}
// 4. 获取特征值
const characteristics = await getCharacteristics(deviceId, serviceId);
if (!characteristics || characteristics.length === 0) throw new Error('未发现特征值');
// 5. 找到write和notify特征
let writeCharId = '';
let notifyCharId = '';
for (const c of characteristics) {
if (!writeCharId && (c.properties.write || c.properties.writeWithoutResponse)) writeCharId = c.uuid;
if (!notifyCharId && (c.properties.notify || c.properties.indicate)) notifyCharId = c.uuid;
}
if (!writeCharId || !notifyCharId) throw new Error('未找到可用的写/通知特征');
// 6. 注册notification
await subscribeCharacteristic(deviceId, serviceId, notifyCharId, (data) => {
// 可在此处分发/处理notification
// console.log('Notification:', data);
});
// 7. 返回结果
return { serviceId, writeCharId, notifyCharId };
};