import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:agent_service/agent_service.dart'; // 移除了dart:async导入,因为不再使用Timer import '../../../data/services/novice_guide_service.dart'; import '../../../routes/app_routes.dart'; import '../../../core/utils/logger.dart'; import 'package:flutter/material.dart'; import '../../../data/services/bluetooth_manager.dart'; import '../../../data/services/ble_manager.dart'; import '../../../data/services/ota_check_service.dart'; import '../../../data/models/appconfig.dart'; import '../../../data/models/appconfig_model.dart'; /// 连接状态枚举 enum ConnectionStatus { disconnected, // 未连接 connecting, // 连接中 connected, // 已连接 failed // 连接失败 } /// 主页控制器,管理设备连接状态和信息展示 class HomeController extends GetxController { static const String _tag = 'HomeCtrl'; static const String _lastDeviceNameKey = 'last_connected_device_name'; // 存储键 static const String _lastDeviceImageKey = 'last_connected_device_image'; // 存储上一次设备图片的键 static HomeController get to => Get.find(); final GetStorage _storage = GetStorage(); String? _lastConnectedDeviceImage; // 新增:存储上一次连接设备的图片链接 final _isFullyConnected = false.obs; String? _lastConnectedDeviceName; // 播报打断开关状态(与 AgentController 对齐) final isTtsInterruptEnabled = false.obs; // 是否启用播报打断 // 移除了电池信息定时刷新相关变量 // OTA检查服务 late final OtaCheckService _otaCheckService; bool get isFullyConnected => _isFullyConnected.value; RxBool get isFullyConnectedRx => _isFullyConnected; BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice; Rx get deviceInfo => _bleManager.deviceInfo; static final RxInt leftEarBattery = 0.obs; static final RxInt rightEarBattery = 0.obs; final caseBattery = 0.obs; final leftEarCharging = false.obs; final rightEarCharging = false.obs; final caseCharging = false.obs; final firmwareVersion = ''.obs; void updateDeviceInfo(DeviceInfo? newDeviceInfo) { leftEarBattery.value = newDeviceInfo?.leftEarBattery ?? 0; rightEarBattery.value = newDeviceInfo?.rightEarBattery ?? 0; caseBattery.value = newDeviceInfo?.caseBattery ?? 0; leftEarCharging.value = newDeviceInfo?.leftEarCharging ?? false; rightEarCharging.value = newDeviceInfo?.rightEarCharging ?? false; caseCharging.value = newDeviceInfo?.caseCharging ?? false; firmwareVersion.value = newDeviceInfo?.firmwareVersion ?? ''; } Worker? _deviceInfoWorker; Worker? _classicBluetoothWorker; Worker? _bleConnectionWorker; final _bluetoothManager = Get.find(); final _bleManager = Get.find(); final noviceGuideService = Get.find(); @override void onInit() { super.onInit(); _otaCheckService = Get.find(); _loadLastConnectedDeviceName(); // 加载持久化的设备名称 _initWithCachedInfo(); _loadTtsInterrupt(); // 加载播报打断开关初始状态 // 监听存储中的“agent_tts_interrupe”键变化,实现与 Agent 页同步 // 中文注释:当 Agent 页或 Home 页任意一处切换时,另一处通过此监听自动刷新开关状态 _storage.listenKey('agent_tts_interrupt', (value) { try { final bool newValue = value is bool ? value : (value == 'true'); if (isTtsInterruptEnabled.value != newValue) { isTtsInterruptEnabled.value = newValue; } } catch (e) { // 中文注释:忽略异常,保持现有状态,避免因类型转换失败导致崩溃 } }); // 移除了电池信息定时刷新的启动 } @override void onClose() { _deviceInfoWorker?.dispose(); _classicBluetoothWorker?.dispose(); _bleConnectionWorker?.dispose(); // 移除了定时器停止逻辑 super.onClose(); } void _initWithCachedInfo() { _setupStateListeners(); _checkConnectionStatus(); } void _setupStateListeners() { _deviceInfoWorker = ever(_bleManager.deviceInfo, (DeviceInfo? info) { if (info != null) updateDeviceInfo(info); }); _classicBluetoothWorker = ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) { if (device != null) { _saveLastConnectedDeviceName(device.name); // 使用新的保存方法 } _checkConnectionStatus(); }); _bleConnectionWorker = ever(_bleManager.connectionStateRx, (int state) { _checkConnectionStatus(); }); } /// 加载“播报打断”开关状态 /// 逻辑与 AgentController 保持一致: /// 1)若本地不存在存储,则根据设备产品的 iscontinuouschat 取反作为默认值; /// 2)若存在存储则直接读取; /// 3)同步到原生服务 AgentService.isInterrupt。 Future _loadTtsInterrupt() async { try { if (!_storage.hasData('agent_tts_interrupt')) { final deviceName = getDeviceName(); final DBProduct? product = AppConfig.getproduct(deviceName); if (deviceName.isNotEmpty && product != null) { isTtsInterruptEnabled.value = !(product.iscontinuouschat ?? true); } else { isTtsInterruptEnabled.value = false; } } else { isTtsInterruptEnabled.value = _storage.read('agent_tts_interrupt') ?? false; } await AgentService.isInterrupt(isTtsInterruptEnabled.value); } catch (e) { Logger.e(_tag, '加载播报打断开关失败: $e'); isTtsInterruptEnabled.value = false; } } /// 切换“播报打断”开关 /// 同步保存到本地,并通知原生服务。 Future toggleTtsInterrupt() async { try { isTtsInterruptEnabled.toggle(); await _storage.write('agent_tts_interrupt', isTtsInterruptEnabled.value); await AgentService.isInterrupt(isTtsInterruptEnabled.value); } catch (e) { Logger.e(_tag, '保存播报打断偏好失败: $e'); } } /// 从本地存储加载最后连接的设备名称 void _loadLastConnectedDeviceName() { _lastConnectedDeviceName = _storage.read(_lastDeviceNameKey); Logger.d(_tag, '加载最后连接的设备名称: $_lastConnectedDeviceName'); } /// 保存最后连接的设备名称到本地存储 void _saveLastConnectedDeviceName(String deviceName) { _lastConnectedDeviceName = deviceName; _storage.write(_lastDeviceNameKey, deviceName); Logger.d(_tag, '保存最后连接的设备名称: $deviceName'); } /// 保存上一次连接设备的图片链接到本地存储 void _saveLastConnectedDeviceImage(String? imageUrl) { if (imageUrl != null && imageUrl.isNotEmpty) { _lastConnectedDeviceImage = imageUrl; _storage.write(_lastDeviceImageKey, imageUrl); Logger.d(_tag, '保存上一次连接设备的图片: $imageUrl'); } } Future refreshDeviceConnection() async { await _checkConnectionStatus(); // 如果设备已连接,主动刷新设备信息 if (_isFullyConnected.value) { try { Logger.d(_tag, '刷新设备信息'); await _bleManager.fetchDeviceInfo(); } catch (e) { Logger.e(_tag, '刷新设备信息失败: $e'); } } } void rescanDevices() { Get.toNamed(Routes.pairing); } void openTranslation() { AgentService.interruptCurrentResponse(); Get.toNamed(Routes.translationMiddleware); } /// 打开会议模块 /// 参数:无 /// 返回值:`Future` 无返回数据 /// 异常:不显式抛出;内部仅记录日志 /// 说明:进入会议页前关闭唤醒;返回首页后根据录音状态决定是否开启唤醒(存在录音任务则不启用) void openMeet() { AgentService.interruptCurrentResponse(); Get.toNamed(Routes.meeting); } void exploreAIFeatures() { // 优先读设备检测写入的 storage(YIDONG_PID 匹配时),否则降级到服务端 AGENT_TYPE final agentType = GetStorage().read('device_agent_type') ?? AppConfig.env('AGENT_TYPE') ?? 'stt_llm_tts'; if (agentType == 'yidong') { Get.toNamed(Routes.mobileElf); } else { Get.toNamed(Routes.agent); AgentService.interruptCurrentResponse(); } } Future _checkConnectionStatus() async { try { final currentBleState = await _bleManager.getConnectionState(); print('fdp--_checkConnectionStatus--currentBleState: $currentBleState'); bool hasClassicBluetooth = currentDevice != null; bool hasBleConnection = currentBleState == 2; bool wasConnected = _isFullyConnected.value; bool isNowConnected = hasClassicBluetooth && hasBleConnection; //bool isGuideCompleted = _storage.read('home_guide') ?? false; if (isNowConnected && currentDevice == null) { isNowConnected = false; } if (_isFullyConnected.value != isNowConnected) { _isFullyConnected.value = isNowConnected; _isFullyConnected.refresh(); //update(); if (!wasConnected && isNowConnected) { // 连接成功后等待1秒再获取设备信息 Future.delayed(const Duration(seconds: 1), () async { await _bleManager.fetchDeviceInfo(); }); // 移除了连接成功后的电池信息刷新 // 连接成功后检查OTA升级 if (noviceGuideService.isHomeGuide) { Future.delayed(const Duration(seconds: 1), () { _checkOtaUpgrade(); }); } } // 移除了根据连接状态控制定时器的逻辑 } //update(); } catch (e) { Logger.e(_tag, '${'checkConnectionStatusFailed'.tr}: $e'); if (_isFullyConnected.value) { _isFullyConnected.value = false; _isFullyConnected.refresh(); // 移除了发生错误时停止定时器的逻辑 // 注意:不再重置OTA检查状态,因为现在由全局服务管理 //update(); } } } Color getConnectionStatusColor() { return _isFullyConnected.value ? Colors.green : Colors.grey; } String getConnectionStatusText() { return _isFullyConnected.value ? 'connected'.tr : 'unconnected'.tr; } bool get shouldShowDeviceInfo => currentDevice != null; String getDeviceName() { if (_isFullyConnected.value && currentDevice == null) { _checkConnectionStatus(); } final deviceName = currentDevice?.name; //final deviceName = '102'; if (deviceName != null && deviceName.isNotEmpty) { return deviceName; } final bleDeviceName = deviceInfo.value?.name; if (bleDeviceName != null && bleDeviceName.isNotEmpty) { return bleDeviceName; } if (!_isFullyConnected.value && _lastConnectedDeviceName != null && _lastConnectedDeviceName!.isNotEmpty) { return _lastConnectedDeviceName!; } return 'unknownEquipment'.tr; } String getSignalStrength() { if (!_isFullyConnected.value) { return 'zeroSignal'.tr; } final random = DateTime.now().millisecondsSinceEpoch % 3; switch (random) { case 0: return 'good'.tr; case 1: return 'fine'.tr; default: return 'excellent'.tr; } } String getDeviceId() { return deviceInfo.value?.address ?? currentDevice?.address ?? 'unknown'.tr; } String getBatteryText() { return deviceInfo.value?.batteryLevel != null && deviceInfo.value!.batteryLevel > 0 ? '${deviceInfo.value?.batteryLevel}%' : 'unknown'.tr; } String getEarbudsText() { if (deviceInfo.value == null || (deviceInfo.value!.leftEarBattery <= 0 && deviceInfo.value!.rightEarBattery <= 0 || deviceInfo.value!.caseBattery > 100)) { return 'unknown'.tr; } String leftStatus = deviceInfo.value!.leftEarBattery > 0 ? '${deviceInfo.value!.leftEarBattery}%' : 'N/A'; String rightStatus = deviceInfo.value!.rightEarBattery > 0 ? '${deviceInfo.value!.rightEarBattery}%' : 'N/A'; leftStatus = deviceInfo.value!.leftEarCharging ? '$leftStatus ⚡' : leftStatus; rightStatus = deviceInfo.value!.rightEarCharging ? '$rightStatus ⚡' : rightStatus; return '${'leftEar'.tr}: $leftStatus / ${'rightEar'.tr}: $rightStatus'; // 左耳: xxx / 右耳: xxx } String getCaseText() { if (deviceInfo.value == null || deviceInfo.value!.caseBattery <= 0 || deviceInfo.value!.caseBattery > 100) { return 'unknown'.tr; } String caseStatus = '${deviceInfo.value!.caseBattery}%'; if (deviceInfo.value!.caseCharging) { caseStatus = '$caseStatus ⚡'; } return '${'chargingCase'.tr}: $caseStatus'; // 充电仓: xxx } String getChargingText() { if (deviceInfo.value == null) { return 'unknown'.tr; } bool isCharging = deviceInfo.value!.leftEarCharging || deviceInfo.value!.rightEarCharging || deviceInfo.value!.caseCharging; return isCharging ? 'charging'.tr : 'notCharging'.tr; } /// 获取当前设备类型 /// 返回设备类型,如果无法获取则返回未知类型 DeviceType getCurrentDeviceType() { try { final deviceName = getDeviceName(); if (deviceName == 'unknownEquipment'.tr) { return DeviceType.unknown; } // 通过AppConfig获取产品信息 final product = AppConfig.getproduct(deviceName); if (product != null) { // 根据产品的devicetype字段返回对应的DeviceType switch (product.devicetype) { case 1: return DeviceType.classicBluetoothHeadset; case 2: return DeviceType.bleBluetoothDoubleHeadset; case 3: return DeviceType.bleBluetoothSingleHeadset; case 4: return DeviceType.smartVoiceRecorder; case 5: return DeviceType.smartEye; default: return DeviceType.unknown; } } return DeviceType.unknown; } catch (e) { Logger.e(_tag, '获取设备类型失败: $e'); return DeviceType.unknown; } } /// 判断当前设备是否为单端耳机 bool isSingleHeadset() { return getCurrentDeviceType() == DeviceType.bleBluetoothSingleHeadset || getCurrentDeviceType() == DeviceType.smartVoiceRecorder || getCurrentDeviceType() == DeviceType.smartEye; } String getVersionText() { return deviceInfo.value?.firmwareVersion.isNotEmpty == true ? deviceInfo.value!.firmwareVersion : 'unknown'.tr; } // 移除了电池信息定时刷新相关方法 /// 检查OTA固件升级 /// 在BLE连接成功后调用,检查是否需要升级 Future _checkOtaUpgrade() async { Logger.d(_tag, '触发OTA升级检查...'); // 打印当前OTA检查状态(用于调试) final status = _otaCheckService.getCheckStatus(); Logger.d(_tag, 'OTA检查状态: $status'); // 使用全局OTA检查服务执行检查 final success = await _otaCheckService.performOtaCheck(_isFullyConnected.value); if (success) { Logger.d(_tag, 'OTA检查已成功执行'); } else { Logger.d(_tag, 'OTA检查被跳过或执行失败'); } } /// 调试方法:获取OTA检查状态 /// 可以在开发阶段用于调试 Map getOtaCheckStatus() { return _otaCheckService.getCheckStatus(); } /// 获取当前设备的产品图片路径 /// 如果没有配置产品图片或获取失败,返回null使用默认图片 String? getCurrentDeviceImage() { try { final deviceName = getDeviceName(); if (deviceName == 'unknownEquipment'.tr) { Logger.d(_tag, '设备未知,使用默认图片'); return null; } // 通过AppConfig获取产品信息 if (!AppConfig.isInitialized()) { Logger.w(_tag, 'AppConfig未初始化,使用默认图片'); return null; } final product = AppConfig.getproduct(deviceName); if (product != null && product.productimage != null && product.productimage!.isNotEmpty) { Logger.d(_tag, '获取到设备产品图片: ${product.productimage}'); _saveLastConnectedDeviceImage(product.productimage); return product.productimage; } else { Logger.d(_tag, '设备 "$deviceName" 未配置产品图片,使用默认图片'); return null; } } catch (e) { Logger.e(_tag, '获取设备产品图片失败: $e'); return null; } } /// 获取设备图片的完整资源路径 /// 处理网络图片URL和本地资源路径 String getDeviceImagePath(bool isDarkMode) { final productImage = getCurrentDeviceImage(); if (productImage != null) { // 如果是网络图片URL if (productImage.startsWith('http://') || productImage.startsWith('https://')) { return productImage; } // 如果是本地资源路径,确保以assets/开头 else if (productImage.startsWith('assets/')) { return productImage; } // 如果只是文件名,添加assets/images/前缀 else { return 'assets/images/$productImage'; } } // 使用默认耳机图片 return 'assets/images/defaultPicture.png'; } /// 判断是否为网络图片 bool isNetworkImage(String imagePath) { return imagePath.startsWith('http://') || imagePath.startsWith('https://'); } }