// 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 { // 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 { return; } async connect(device: BleDevice, options?: BleConnectOptionsExt): Promise { return; } async disconnect(device: BleDevice): Promise { return; } async sendData(device: BleDevice, payload?: SendDataPayload, options?: BleOptions): Promise { return; } async autoConnect(device: BleDevice, options?: BleConnectOptionsExt): Promise { 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 { 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 { // 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 '' } } }