- 增加了wrapper处理原生app的交互
This commit is contained in:
@@ -57,6 +57,7 @@ class BleSDK {
|
||||
|
||||
// 批量设备发现缓冲
|
||||
private pendingDevices: Array<{ id: string; name: string | null; rssi: number | null }> = []
|
||||
private discoveredDevices: Array<{ id: string; name: string | null; rssi: number | null }> = []
|
||||
private flushTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
initialize() {
|
||||
@@ -146,59 +147,84 @@ class BleSDK {
|
||||
// ====== 接收原生 App 的消息指令 ======
|
||||
private setupMessageHandler() {
|
||||
addMessageListener((event: Record<string, any>) => {
|
||||
const { action, ...params } = event
|
||||
console.log(`[BleSDK] Received action: ${action}`, params)
|
||||
void this.handleAction(event)
|
||||
})
|
||||
}
|
||||
|
||||
private async handleAction(event: Record<string, any>) {
|
||||
const { action, requestId, ...params } = event
|
||||
console.log(`[BleSDK] Received action: ${action}`, params)
|
||||
|
||||
try {
|
||||
let data: any = null
|
||||
|
||||
switch (action) {
|
||||
case 'scan':
|
||||
this.startScan()
|
||||
await this.startScan()
|
||||
data = { started: true }
|
||||
break
|
||||
case 'stopScan':
|
||||
this.stopScan()
|
||||
data = { stopped: true }
|
||||
break
|
||||
case 'connect':
|
||||
this.connect(params.deviceId)
|
||||
data = await this.connect(params.deviceId)
|
||||
break
|
||||
case 'disconnect':
|
||||
this.disconnect()
|
||||
data = await this.disconnect()
|
||||
break
|
||||
case 'getDeviceInfo':
|
||||
this.getDeviceInfo()
|
||||
data = await this.getDeviceInfo()
|
||||
break
|
||||
case 'getVersion':
|
||||
this.getVersion()
|
||||
data = await this.getVersion()
|
||||
break
|
||||
case 'bind':
|
||||
this.bind(params.userId)
|
||||
data = await this.bind(params.userId)
|
||||
break
|
||||
case 'unbind':
|
||||
this.unbind(params.userId)
|
||||
data = await this.unbind(params.userId)
|
||||
break
|
||||
case 'deleteFile':
|
||||
this.deleteFile(params.key)
|
||||
data = await this.deleteFile(params.key)
|
||||
break
|
||||
case 'transferFile':
|
||||
this.transferFile(params.fileUri, params.commandType)
|
||||
data = await this.transferFile(params.fileUri, params.commandType)
|
||||
break
|
||||
default:
|
||||
sendMessage({ event: 'error', type: 'unknownAction', message: `Unknown action: ${action}` })
|
||||
throw new Error(`Unknown action: ${action}`)
|
||||
}
|
||||
})
|
||||
|
||||
if (requestId) {
|
||||
this.sendResponse(requestId, action, data)
|
||||
}
|
||||
} catch (e: any) {
|
||||
const message = e instanceof Error ? e.message : String(e)
|
||||
if (requestId) {
|
||||
this.sendErrorResponse(requestId, action, message)
|
||||
return
|
||||
}
|
||||
|
||||
sendMessage({ event: 'error', type: action ?? 'unknownAction', message })
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 蓝牙操作实现 ======
|
||||
|
||||
private async startScan() {
|
||||
try {
|
||||
const hasPerms = await this.requestPermissions()
|
||||
if (!hasPerms) {
|
||||
sendMessage({ event: 'error', type: 'permission', message: 'Bluetooth permission denied' })
|
||||
return
|
||||
}
|
||||
const hasPerms = await this.requestPermissions()
|
||||
if (!hasPerms) {
|
||||
const error = new Error('Bluetooth permission denied')
|
||||
this.updateState(STATE_KEYS.ERROR, error.message)
|
||||
sendMessage({ event: 'error', type: 'permission', message: error.message })
|
||||
throw error
|
||||
}
|
||||
|
||||
try {
|
||||
this.updateState(STATE_KEYS.BT_STATE, 'scanning')
|
||||
this.updateState(STATE_KEYS.DISCOVERED_DEVICES, [])
|
||||
this.pendingDevices = []
|
||||
this.discoveredDevices = []
|
||||
|
||||
await this.bleClient.startScan(
|
||||
[BLE_UUIDS.SERVICE],
|
||||
@@ -223,6 +249,7 @@ class BleSDK {
|
||||
this.updateState(STATE_KEYS.BT_STATE, 'idle')
|
||||
this.updateState(STATE_KEYS.ERROR, e.message)
|
||||
sendMessage({ event: 'error', type: 'scan', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +262,7 @@ class BleSDK {
|
||||
|
||||
private async connect(deviceId: string) {
|
||||
if (!deviceId) {
|
||||
sendMessage({ event: 'error', type: 'connect', message: 'deviceId is required' })
|
||||
return
|
||||
throw new Error('deviceId is required')
|
||||
}
|
||||
|
||||
this.stopScan()
|
||||
@@ -258,10 +284,15 @@ class BleSDK {
|
||||
|
||||
// 自动获取设备信息
|
||||
this.getDeviceInfo().catch(() => {})
|
||||
return {
|
||||
id: device.id,
|
||||
name: device.name || device.localName || null,
|
||||
}
|
||||
} catch (e: any) {
|
||||
this.updateState(STATE_KEYS.BT_STATE, 'idle')
|
||||
this.updateState(STATE_KEYS.ERROR, e.message)
|
||||
sendMessage({ event: 'error', type: 'connect', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,68 +309,75 @@ class BleSDK {
|
||||
this.updateState(STATE_KEYS.BT_STATE, 'disconnected')
|
||||
|
||||
sendMessage({ event: 'disconnected' })
|
||||
return { success: true }
|
||||
} catch (e: any) {
|
||||
sendMessage({ event: 'error', type: 'disconnect', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private async getDeviceInfo() {
|
||||
if (!this.connectedDeviceId) return
|
||||
if (!this.connectedDeviceId) throw new Error('No device connected')
|
||||
try {
|
||||
const promise = this.createWaitable<DeviceInfo>('deviceInfo')
|
||||
await this.deviceInfoService.getDeviceInfo(this.connectedDeviceId)
|
||||
return await promise
|
||||
} catch (e: any) {
|
||||
sendMessage({ event: 'error', type: 'deviceInfo', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private async getVersion() {
|
||||
if (!this.connectedDeviceId) return
|
||||
if (!this.connectedDeviceId) throw new Error('No device connected')
|
||||
try {
|
||||
const promise = this.createWaitable<VersionInfo>('versionInfo')
|
||||
await this.deviceInfoService.getDeviceVersion(this.connectedDeviceId)
|
||||
return await promise
|
||||
} catch (e: any) {
|
||||
sendMessage({ event: 'error', type: 'version', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private async bind(userId: string) {
|
||||
if (!this.connectedDeviceId) return
|
||||
if (!this.connectedDeviceId) throw new Error('No device connected')
|
||||
try {
|
||||
const promise = this.createWaitable<BindingResponse>('bindDevice')
|
||||
await this.deviceInfoService.bindDevice(this.connectedDeviceId, userId)
|
||||
return await promise
|
||||
} catch (e: any) {
|
||||
sendMessage({ event: 'error', type: 'bind', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private async unbind(userId: string) {
|
||||
if (!this.connectedDeviceId) return
|
||||
if (!this.connectedDeviceId) throw new Error('No device connected')
|
||||
try {
|
||||
const promise = this.createWaitable<UnBindResponse>('unbindDevice')
|
||||
await this.deviceInfoService.unbindDevice(this.connectedDeviceId, userId)
|
||||
return await promise
|
||||
} catch (e: any) {
|
||||
sendMessage({ event: 'error', type: 'unbind', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteFile(key: string) {
|
||||
if (!this.connectedDeviceId) return
|
||||
if (!this.connectedDeviceId) throw new Error('No device connected')
|
||||
try {
|
||||
const promise = this.createWaitable<DeleteFileResponse>('deleteFile')
|
||||
await this.deviceInfoService.deleteFile(this.connectedDeviceId, key)
|
||||
return await promise
|
||||
} catch (e: any) {
|
||||
sendMessage({ event: 'error', type: 'deleteFile', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private async transferFile(fileUri: string, commandType?: number) {
|
||||
if (!this.connectedDeviceId) return
|
||||
if (!this.connectedDeviceId) throw new Error('No device connected')
|
||||
try {
|
||||
this.updateState(STATE_KEYS.TRANSFER_PROGRESS, 0)
|
||||
const type = commandType ?? COMMAND_TYPES.TRANSFER_ANI_VIDEO
|
||||
@@ -356,9 +394,11 @@ class BleSDK {
|
||||
)
|
||||
|
||||
sendMessage({ event: 'transferComplete' })
|
||||
return { success: true }
|
||||
} catch (e: any) {
|
||||
this.updateState(STATE_KEYS.ERROR, e.message)
|
||||
sendMessage({ event: 'error', type: 'transfer', message: e.message })
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +413,9 @@ class BleSDK {
|
||||
}
|
||||
|
||||
private queueDevice(device: { id: string; name: string | null; rssi: number | null }) {
|
||||
if (this.pendingDevices.some((d) => d.id === device.id)) return
|
||||
if (this.discoveredDevices.some((d) => d.id === device.id)) return
|
||||
|
||||
this.discoveredDevices.push(device)
|
||||
this.pendingDevices.push(device)
|
||||
|
||||
if (this.flushTimer) clearTimeout(this.flushTimer)
|
||||
@@ -382,12 +424,38 @@ class BleSDK {
|
||||
|
||||
private flushDevices() {
|
||||
if (this.pendingDevices.length === 0) return
|
||||
const devices = [...this.pendingDevices]
|
||||
const devices = [...this.discoveredDevices]
|
||||
this.pendingDevices = []
|
||||
this.updateState(STATE_KEYS.DISCOVERED_DEVICES, devices)
|
||||
sendMessage({ event: 'devicesFound', devices })
|
||||
}
|
||||
|
||||
private sendResponse(requestId: string, action: string, data: any) {
|
||||
sendMessage({ event: 'response', requestId, action, success: true, data: this.serializeForNative(data) })
|
||||
}
|
||||
|
||||
private sendErrorResponse(requestId: string, action: string | undefined, message: string) {
|
||||
sendMessage({ event: 'response', requestId, action, success: false, error: message })
|
||||
}
|
||||
|
||||
private serializeForNative(value: any): any {
|
||||
if (typeof value === 'bigint') {
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => this.serializeForNative(item))
|
||||
}
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, nestedValue]) => [key, this.serializeForNative(nestedValue)])
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
private createWaitable<T>(opId: string, timeout = 10000): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
|
||||
Reference in New Issue
Block a user