import 'dart:async'; import 'package:flutter/services.dart'; /// Smartcar(车载香薰)原生链路的 Dart 通道封装。 /// /// 只做三件事:扫描、连接(**含杰理 RCSP 认证**)、收发 C2 私有通道的裸字节。 /// 组帧 / CRC / 广播解析都在 App 侧的 `smartcar_protocol.dart`,这里不解释任何字节。 /// /// `connect()` 只在**认证通过**后才返回——固件在那之前不把手机当成已连接。 class DeviceSmartcar { DeviceSmartcar._(); static final DeviceSmartcar instance = DeviceSmartcar._(); static const MethodChannel _method = MethodChannel('com.yimai.device_smartcar'); static const EventChannel _events = EventChannel('com.yimai.device_smartcar/events'); Stream>? _stream; /// 原生事件(广播流)。每条形如 `{type: scan|scanStopped|state|custom|adapter, ...}`。 Stream> get events { return _stream ??= _events .receiveBroadcastStream() .map((e) => Map.from(e as Map)) .asBroadcastStream(); } Future adapterState() async => (await _method.invokeMethod('adapterState')) ?? 'unknown'; /// 起不来抛 [PlatformException](code `SCAN_FAILED`,message 是原因)。 Future startScan({int timeoutSec = 15}) => _method.invokeMethod('startScan', {'timeoutSec': timeoutSec}); Future stopScan() => _method.invokeMethod('stopScan'); /// 认证通过才 resolve;失败抛 [PlatformException](code `CONNECT_FAILED` / `NOT_FOUND` / `BUSY`)。 Future connect(String id, {int timeoutSec = 45}) => _method.invokeMethod('connect', {'id': id, 'timeoutSec': timeoutSec}); Future disconnect() => _method.invokeMethod('disconnect'); Future isConnected() async => (await _method.invokeMethod('isConnected')) ?? false; /// 认证通过后的杰理 RCSP 收尾序列(设时间 / GET_TARGET_INFO / GET_SYS_INFO), /// 返回各步的 JL_CMDStatus(0 = 成功)。未认证抛 `NOT_READY`。 Future> jlPostAuth() async { final r = await _method.invokeMethod('jlPostAuth'); return Map.from(r ?? const {}); } /// 开始把设备麦克风的 opus 上行解码成 16k PCM 往上抛(`{type:'audio'}` 事件)。 /// /// ⚠️ **不控制设备开麦**:设备是收到 `0xCC 0x11` 唤醒后自己开麦的, /// 这里只决定原生要不要解码转发。返回 false = opus 解码器起不来。 Future audioStart() async => (await _method.invokeMethod('audioStart')) ?? false; Future audioStop() => _method.invokeMethod('audioStop'); /// 排查用:`{running, framesIn, framesDup, pcmBytesOut}`。 /// 真机"没声音"时先看这几个数,能立刻区分是没收到帧、全被当重复丢了、还是解码没出字节。 Future> audioStats() async { final r = await _method.invokeMethod('audioStats'); return Map.from(r ?? const {}); } /// 启动杰理 OTA(原生含 stage2 扫描回连)。进度从 [events] 的 `{type:'ota'}` 来。 /// 起不来抛 [PlatformException](`OTA_FAILED` / `NOT_READY`)。 Future otaStart(String filePath) => _method.invokeMethod('otaStart', {'filePath': filePath}); Future otaCancel() => _method.invokeMethod('otaCancel'); Future otaIsRunning() async => (await _method.invokeMethod('otaIsRunning')) ?? false; /// 往 ABC1 写一帧(已组好、含 CRC 的裸字节) Future writeCustom(Uint8List data) async => (await _method.invokeMethod('writeCustom', {'data': data})) ?? false; }