116 lines
5.3 KiB
Plaintext
116 lines
5.3 KiB
Plaintext
// Minimal ProtocolHandler runtime class used by pages and components.
|
|
// This class adapts the platform `BluetoothService` to a small protocol API
|
|
// expected by pages: setConnectionParameters, initialize, testBatteryLevel,
|
|
// testVersionInfo. Implemented conservatively to avoid heavy dependencies.
|
|
|
|
import type { BluetoothService, AutoBleInterfaces, AutoDiscoverAllResult, BleService, BleCharacteristic, BleProtocolType, BleDevice, ScanDevicesOptions, BleConnectOptionsExt, SendDataPayload, BleOptions } from './interface.uts'
|
|
|
|
export class ProtocolHandler {
|
|
// bluetoothService may be omitted for lightweight wrappers; allow null
|
|
bluetoothService: BluetoothService | null = null
|
|
protocol: BleProtocolType = 'standard'
|
|
deviceId: string | null = null
|
|
serviceId: string | null = null
|
|
writeCharId: string | null = null
|
|
notifyCharId: string | null = null
|
|
initialized: boolean = false
|
|
|
|
// Accept an optional BluetoothService so wrapper subclasses can call
|
|
// `super()` without forcing a runtime instance.
|
|
constructor(bluetoothService?: BluetoothService) {
|
|
if (bluetoothService != null) this.bluetoothService = bluetoothService
|
|
}
|
|
|
|
setConnectionParameters(deviceId: string, serviceId: string, writeCharId: string, notifyCharId: string) {
|
|
this.deviceId = deviceId
|
|
this.serviceId = serviceId
|
|
this.writeCharId = writeCharId
|
|
this.notifyCharId = notifyCharId
|
|
}
|
|
|
|
// initialize: optional setup, returns a Promise that resolves when ready
|
|
async initialize(): Promise<void> {
|
|
// Simple async initializer — keep implementation minimal and generator-friendly.
|
|
try {
|
|
// If bluetoothService exposes any protocol-specific setup, call it here.
|
|
this.initialized = true
|
|
return
|
|
} catch (e) {
|
|
throw e
|
|
}
|
|
}
|
|
|
|
// Protocol lifecycle / operations — default no-ops so generated code has
|
|
// concrete member references and platform-specific handlers can override.
|
|
async scanDevices(options?: ScanDevicesOptions): Promise<void> { return; }
|
|
async connect(device: BleDevice, options?: BleConnectOptionsExt): Promise<void> { return; }
|
|
async disconnect(device: BleDevice): Promise<void> { return; }
|
|
async sendData(device: BleDevice, payload?: SendDataPayload, options?: BleOptions): Promise<void> { return; }
|
|
async autoConnect(device: BleDevice, options?: BleConnectOptionsExt): Promise<AutoBleInterfaces> { return { serviceId: '', writeCharId: '', notifyCharId: '' }; }
|
|
|
|
// Example: testBatteryLevel will attempt to read the battery characteristic
|
|
// if write/notify-based protocol is not available. Returns number percentage.
|
|
async testBatteryLevel(): Promise<number> {
|
|
if (this.deviceId == null) throw new Error('deviceId not set')
|
|
// copy to local so Kotlin generator can smart-cast the value across awaits
|
|
const deviceId = this.deviceId
|
|
// try reading standard Battery characteristic (180F -> 2A19)
|
|
if (this.bluetoothService == null) throw new Error('bluetoothService not set')
|
|
const services = await this.bluetoothService.getServices(deviceId)
|
|
|
|
|
|
let found: BleService | null = null
|
|
for (let i = 0; i < services.length; i++) {
|
|
const s = services[i]
|
|
const uuidCandidate: string | null = (s != null && s.uuid != null ? s.uuid : null)
|
|
const uuid = uuidCandidate != null ? ('' + uuidCandidate).toLowerCase() : ''
|
|
if (uuid.indexOf('180f') !== -1) { found = s; break }
|
|
}
|
|
if (found == null) {
|
|
// fallback: if writeCharId exists and notify available use protocol (not implemented)
|
|
return 0
|
|
}
|
|
const foundUuid = found!.uuid
|
|
const charsRaw = await this.bluetoothService.getCharacteristics(deviceId, foundUuid)
|
|
const chars: BleCharacteristic[] = charsRaw
|
|
const batChar = chars.find((c: BleCharacteristic) => ((c.properties != null && c.properties.read) || (c.uuid != null && ('' + c.uuid).toLowerCase().includes('2a19'))))
|
|
if (batChar == null) return 0
|
|
const buf = await this.bluetoothService.readCharacteristic(deviceId, foundUuid, batChar.uuid)
|
|
const arr = new Uint8Array(buf)
|
|
if (arr.length > 0) {
|
|
return arr[0]
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// testVersionInfo: try to read Device Information characteristics or return empty
|
|
async testVersionInfo(hw: boolean): Promise<string> {
|
|
// copy to local so Kotlin generator can smart-cast the value across awaits
|
|
const deviceId = this.deviceId
|
|
if (deviceId == null) return ''
|
|
// Device Information service 180A, characteristics: 2A26 (SW), 2A27 (HW) sometimes
|
|
if (this.bluetoothService == null) return ''
|
|
const _services = await this.bluetoothService.getServices(deviceId)
|
|
const services2: BleService[] = _services
|
|
let found2: BleService | null = null
|
|
for (let i = 0; i < services2.length; i++) {
|
|
const s = services2[i]
|
|
const uuidCandidate: string | null = (s != null && s.uuid != null ? s.uuid : null)
|
|
const uuid = uuidCandidate != null ? ('' + uuidCandidate).toLowerCase() : ''
|
|
if (uuid.indexOf('180a') !== -1) { found2 = s; break }
|
|
}
|
|
if (found2 == null) return ''
|
|
const _found2 = found2
|
|
const found2Uuid = _found2!.uuid
|
|
const chars = await this.bluetoothService.getCharacteristics(deviceId, found2Uuid)
|
|
const target = chars.find((c) => {
|
|
const id = ('' + c.uuid).toLowerCase()
|
|
if (hw) return id.includes('2a27') || id.includes('hardware')
|
|
return id.includes('2a26') || id.includes('software')
|
|
})
|
|
if (target == null) return ''
|
|
const buf = await this.bluetoothService.readCharacteristic(deviceId, found2Uuid, target.uuid)
|
|
try { return new TextDecoder().decode(new Uint8Array(buf)) } catch (e) { return '' }
|
|
}
|
|
}
|