You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

785 lines
24 KiB

import 'dart:async';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:get/get.dart';
import 'package:ble_service/ble_service.dart';
import 'package:deep_voice/core/utils/logger.dart';
import 'package:get_storage/get_storage.dart';
import 'dart:convert';
import 'package:agent_service/agent_service.dart';
// 重新导出BLE连接状态常量,让外部可以通过BleManager访问
export 'package:ble_service/ble_service.dart' show BleConnectionState;
/// 命令类型常量(与原生层保持一致)
class BleCommands {
static const int GET_VERSION = 0x01;
static const int GET_PRODUCT_ID = 0x02;
static const int GET_PRODUCT_COLOR = 0x03;
static const int GET_BATTERY_INFO = 0x04;
static const int CONTROL_CODEC = 0x05;
}
/// 蓝牙设备信息缓存模型
class DeviceInfo {
final String address;
final String name;
// 整体电池电量
final int batteryLevel;
// 详细电池信息
final int leftEarBattery;
final int rightEarBattery;
final int caseBattery;
// 充电状态
final bool leftEarCharging;
final bool rightEarCharging;
final bool caseCharging;
final String firmwareVersion;
final String firmwareProductId;
final String firmwareProductColor;
final DateTime lastUpdated;
DeviceInfo({
required this.address,
required this.name,
this.batteryLevel = 0,
this.leftEarBattery = 0,
this.rightEarBattery = 0,
this.caseBattery = 0,
this.leftEarCharging = false,
this.rightEarCharging = false,
this.caseCharging = false,
this.firmwareVersion = '',
this.firmwareProductId = '',
this.firmwareProductColor = '',
DateTime? lastUpdated,
}) : lastUpdated = lastUpdated ?? DateTime.now();
/// 创建更新后的设备信息副本
DeviceInfo copyWith({
String? address,
String? name,
int? batteryLevel,
int? leftEarBattery,
int? rightEarBattery,
int? caseBattery,
bool? leftEarCharging,
bool? rightEarCharging,
bool? caseCharging,
String? firmwareVersion,
String? firmwareProductId,
String? firmwareProductColor,
}) {
return DeviceInfo(
address: address ?? this.address,
name: name ?? this.name,
batteryLevel: batteryLevel ?? this.batteryLevel,
leftEarBattery: leftEarBattery ?? this.leftEarBattery,
rightEarBattery: rightEarBattery ?? this.rightEarBattery,
caseBattery: caseBattery ?? this.caseBattery,
leftEarCharging: leftEarCharging ?? this.leftEarCharging,
rightEarCharging: rightEarCharging ?? this.rightEarCharging,
caseCharging: caseCharging ?? this.caseCharging,
firmwareVersion: firmwareVersion ?? this.firmwareVersion,
firmwareProductId: firmwareProductId ?? this.firmwareProductId,
firmwareProductColor: firmwareProductColor ?? this.firmwareProductColor,
lastUpdated: DateTime.now(),
);
}
@override
String toString() {
return 'DeviceInfo(address: $address, name: $name, '
'battery: $batteryLevel%, L: $leftEarBattery%, R: $rightEarBattery%, Case: $caseBattery%, '
'charging: L:$leftEarCharging, R:$rightEarCharging, C:$caseCharging, '
'version: $firmwareVersion)';
}
}
/// BLE管理器 - 专门处理BLE设备连接和信息管理
class BleManager extends GetxService {
static BleManager get to => Get.find<BleManager>();
// BLE服务实例
final BleService _bleService = BleService();
// 经典蓝牙实例
// BLE连接状态
final _connectionState = BleConnectionState.disconnected.obs;
int get connectionState => _connectionState.value;
// 公开的连接状态流,供外部监听
RxInt get connectionStateRx => _connectionState;
// 连接状态监听
final _isConnected = false.obs;
bool get isConnected => _isConnected.value;
// 设备信息缓存
final deviceInfo = Rx<DeviceInfo?>(null); // 使用 Rx 包装
// 订阅
StreamSubscription<dynamic>? _statusSubscription;
StreamSubscription<dynamic>? _dataSubscription;
final GetStorage _storage = GetStorage();
/// 初始化
@override
void onInit() {
super.onInit();
Logger.info('BLE管理器初始化');
// 设置BLE事件监听
_setupBleListener();
}
/// 释放资源
@override
void onClose() {
_statusSubscription?.cancel();
_dataSubscription?.cancel();
super.onClose();
}
/// 设置BLE事件监听
void _setupBleListener() {
_statusSubscription?.cancel();
_dataSubscription?.cancel();
try {
// 监听BLE状态更新
_statusSubscription = _bleService.onStatusUpdated.listen(
(event) {
if (event is Map) {
// 处理连接状态更新
if (event['type'] == 'connectionStateChanged') {
final state = event['state'] as int;
final oldState = _connectionState.value;
_connectionState.value = state;
Logger.d('BleManager', '🔄 BLE状态更新: $oldState → $state');
Logger.d('BleManager', '📡 通知监听器状态变化: ${_connectionState.value}');
// 连接状态转换
if (state == BleConnectionState.connected) {
Logger.info('BLE设备已连接');
_isConnected.value = true;
// 在连接成功后延迟 300 毫秒获取设备信息(可根据需要调整时间)
Future.delayed(const Duration(milliseconds: 300), () {
// 可选:检查是否仍处于连接状态
if (_isConnected.value) {
// fetchDeviceInfo();
}
});
} else if (state == BleConnectionState.disconnected ||
state == BleConnectionState.error) {
Logger.info('BLE设备已断开或发生错误');
_isConnected.value = false;
// 更新设备信息
_updateDeviceInfoIfNeeded(
batteryLevel: 0,
leftEarBattery: 0,
rightEarBattery: 0,
caseBattery: 0,
leftEarCharging: false,
rightEarCharging: false,
caseCharging: false,
firmwareVersion: '',
firmwareProductId: '',
firmwareProductColor: '',
);
}
}
// 处理设备信息
_processDeviceInfo(event);
}
},
onError: (error) {
Logger.error('BLE状态事件流错误: $error');
},
);
// 监听BLE数据流
} catch (e) {
Logger.error('设置BLE事件监听器失败: ${e.toString()}');
}
}
/// 处理来自原生层的设备信息
void _processDeviceInfo(Map<dynamic, dynamic> event) {
try {
// 检查是否是设备信息响应
if (event.containsKey('command') && event.containsKey('success')) {
final bool success = event['success'] as bool;
if (!success) {
Logger.warning('设备命令执行失败: ${event['command']}');
return;
}
// 根据命令类型处理不同信息
final command = event['command'];
if (command is int) {
// 基于命令ID处理
_processDeviceInfoByCommandId(command, event);
} else if (command is String) {
// 基于命令名称处理
_processDeviceInfoByCommandName(command, event);
}
}
} catch (e) {
Logger.error('处理设备信息异常: ${e.toString()}');
}
}
/// 根据命令ID处理设备信息
void _processDeviceInfoByCommandId(
int commandId, Map<dynamic, dynamic> data) {
switch (commandId) {
case BleCommands.GET_VERSION:
_updateVersionInfoFromData(data);
break;
case BleCommands.GET_PRODUCT_ID:
_updateProductIdFromData(data);
break;
case BleCommands.GET_PRODUCT_COLOR:
break;
case BleCommands.GET_BATTERY_INFO:
_updateBatteryInfoFromData(data);
break;
default:
Logger.info('未处理的命令ID: $commandId');
}
}
/// 根据命令名称处理设备信息
void _processDeviceInfoByCommandName(
String commandName, Map<dynamic, dynamic> data) {
switch (commandName) {
case 'batteryInfo':
_updateBatteryInfoFromData(data);
break;
case 'versionInfo':
_updateVersionInfoFromData(data);
break;
case 'productId':
_updateProductIdFromData(data);
break;
case 'productColor':
break;
default:
Logger.info('未处理的命令名称: $commandName');
}
}
/// 更新电池信息
void _updateBatteryInfoFromData(Map<dynamic, dynamic> data) {
try {
// 确保数据中包含电池信息
if (!data.containsKey('leftEarBattery') ||
!data.containsKey('rightEarBattery') ||
!data.containsKey('caseBattery')) {
Logger.warning('电池信息数据不完整');
return;
}
// 提取电池信息
final int leftBattery = data['leftEarBattery'] as int? ?? 0;
final int rightBattery = data['rightEarBattery'] as int? ?? 0;
final int caseBattery = data['caseBattery'] as int? ?? 0;
final bool leftCharging = data['leftEarCharging'] as bool? ?? false;
final bool rightCharging = data['rightEarCharging'] as bool? ?? false;
final bool caseCharging = data['caseCharging'] as bool? ?? false;
// 获取整体电量(原生层使用左耳电量作为主要指示)
final int overallBattery = data['batteryLevel'] as int? ?? leftBattery;
// 更新设备信息
_updateDeviceInfoIfNeeded(
batteryLevel: overallBattery,
leftEarBattery: leftBattery,
rightEarBattery: rightBattery,
caseBattery: caseBattery,
leftEarCharging: leftCharging,
rightEarCharging: rightCharging,
caseCharging: caseCharging,
);
Logger.info(
'设备电池信息已更新: 电量=$overallBattery%, 左耳=$leftBattery%, 右耳=$rightBattery%, 充电仓=$caseBattery%');
} catch (e) {
Logger.error('更新电池信息失败: ${e.toString()}');
}
}
/// 更新版本信息
void _updateVersionInfoFromData(Map<dynamic, dynamic> data) {
try {
// 确保数据中包含版本信息
if (!data.containsKey('version')) {
Logger.warning('版本信息数据不完整');
return;
}
// 提取版本信息
final String version = data['version'] as String? ?? '';
// 更新设备信息
_updateDeviceInfoIfNeeded(
firmwareVersion: version,
);
Logger.info('设备版本信息已更新: 版本=$version');
} catch (e) {
Logger.error('更新版本信息失败: ${e.toString()}');
}
}
/// 更新设备id
void _updateProductIdFromData(Map<dynamic, dynamic> data) {
try {
// 确保数据中包含版本信息
if (!data.containsKey('productId')) {
Logger.warning('版本信息数据不完整');
return;
}
// 提取版本信息
final String productId = data['productId'] as String? ?? '';
// 更新设备信息
_updateDeviceInfoIfNeeded(
firmwareProductId: productId,
);
Logger.info('设备设备id已更新: 设备id=$productId');
} catch (e) {
Logger.error('更新设备id失败: ${e.toString()}');
}
}
/// 更新设备信息(如果已存在设备)
void _updateDeviceInfoIfNeeded({
int? batteryLevel,
int? leftEarBattery,
int? rightEarBattery,
int? caseBattery,
bool? leftEarCharging,
bool? rightEarCharging,
bool? caseCharging,
String? firmwareVersion,
String? firmwareProductId,
String? firmwareProductColor,
}) {
// 如果设备信息已存在,则更新
if (deviceInfo.value != null) {
deviceInfo.value = deviceInfo.value!.copyWith(
batteryLevel: batteryLevel ?? deviceInfo.value?.batteryLevel,
leftEarBattery: leftEarBattery ?? deviceInfo.value?.leftEarBattery,
rightEarBattery: rightEarBattery ?? deviceInfo.value?.rightEarBattery,
caseBattery: caseBattery ?? deviceInfo.value?.caseBattery,
leftEarCharging: leftEarCharging ?? deviceInfo.value!.leftEarCharging,
rightEarCharging:
rightEarCharging ?? deviceInfo.value!.rightEarCharging,
caseCharging: caseCharging ?? deviceInfo.value!.caseCharging,
firmwareVersion: firmwareVersion ?? deviceInfo.value!.firmwareVersion,
firmwareProductId:
firmwareProductId ?? deviceInfo.value!.firmwareProductId,
firmwareProductColor:
firmwareProductColor ?? deviceInfo.value!.firmwareProductColor,
);
}
// 创建设备信息
// 如果设备信息不存在,但有地址和名称,则创建新的设备信息
else if (deviceInfo.value == null &&
_isConnected.value &&
(firmwareVersion != null || batteryLevel != null)) {
// 提示尝试创建设备信息但缺少必要信息
Logger.warning('尝试更新设备信息,但设备基本信息不存在');
deviceInfo.value = DeviceInfo(
address: "", // 应确保从连接信息中获取真实值
name: "",
batteryLevel: batteryLevel ?? 0,
leftEarBattery: leftEarBattery ?? 0,
rightEarBattery: rightEarBattery ?? 0,
caseBattery: caseBattery ?? 0,
leftEarCharging: leftEarCharging ?? false,
rightEarCharging: rightEarCharging ?? false,
caseCharging: caseCharging ?? false,
firmwareVersion: firmwareVersion ?? '',
firmwareProductId: firmwareProductId ?? '',
firmwareProductColor: firmwareProductColor ?? '',
);
}
}
/// 连接成功后获取设备信息
Future<void> fetchDeviceInfo() async {
Logger.info('开始获取BLE设备详细信息');
try {
// 获取电池信息
final batteryResult = await _bleService.getBatteryInfo();
if (batteryResult) {
Logger.info('电池信息获取指令已发送');
} else {
Logger.warning('电池信息获取指令发送失败');
}
// 添加延迟
await Future.delayed(const Duration(seconds: 1));
// 获取设备版本信息
final versionResult = await _bleService.getDeviceVersion();
if (versionResult) {
Logger.info('版本信息获取指令已发送');
} else {
Logger.warning('版本信息获取指令发送失败');
}
// 添加延迟
await Future.delayed(const Duration(seconds: 1));
// 获取产品id
final productIdResult = await _bleService.getProductId();
if (productIdResult) {
Logger.info('产品id获取指令已发送');
} else {
Logger.warning('产品id获取指令发送失败');
}
} catch (e) {
Logger.error('获取BLE设备信息失败: ${e.toString()}');
}
}
/// 检查BLE连接状态
Future<int> getConnectionState() async {
try {
final isConnected = await _bleService.isConnected();
_connectionState.value = isConnected
? BleConnectionState.connected
: BleConnectionState.disconnected;
_isConnected.value = isConnected;
return _connectionState.value;
} catch (e) {
Logger.error('获取BLE连接状态失败: ${e.toString()}');
return BleConnectionState.error;
}
}
/// 启动BLE服务
Future<bool> startAgentService() async {
try {
// 从.env文件直接读取环境变量
// final azureSpeechKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
// final azureSpeechRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? 'eastasia';
// final openaiApiKey = dotenv.env['OPENAI_API_KEY'] ?? '';
// final openaiBaseUrl =
// dotenv.env['OPENAI_BASE_URL'] ?? 'https://api.openai.com/v1';
// final openaiModel = dotenv.env['OPENAI_MODEL'] ?? 'gpt-3.5-turbo';
// 从Flutter资源包中加载.mcp.json
// final mcpConfig = await rootBundle.loadString('.mcp.json');
// 验证JSON格式
// final jsonData = jsonDecode(mcpConfig);
final azureSpeechKey = AppConfig.env('AZURE_SPEECH_KEY') ?? '';
final azureSpeechRegion =
AppConfig.env('AZURE_SPEECH_REGION') ?? 'eastasia';
final openaiApiKey = AppConfig.env('OPENAI_API_KEY') ?? '';
final openaiBaseUrl =
AppConfig.env('OPENAI_BASE_URL') ?? 'https://api.openai.com/v1';
final openaiModel = AppConfig.env('OPENAI_MODEL') ?? 'gpt-3.5-turbo';
final openaiagentid =
AppConfig.env('OPENAI_AGENT_ID') ?? 'personal_assistant';
final systemPrompt = AppConfig.agentsystemPrompt(openaiagentid);
// 从Flutter资源包中加载.mcp.json
final jsonData = AppConfig.mcpConfig();
final config = <String, dynamic>{
// 语音代理配置参数
'azureSpeechKey': azureSpeechKey,
'azureSpeechRegion': azureSpeechRegion,
'openaiApiKey': openaiApiKey,
'openaiBaseUrl': openaiBaseUrl,
'openaiModel': openaiModel,
'systemPrompt': systemPrompt,
'mcpServer': jsonEncode(jsonData),
'volcanoAppId': '2316081933',
'volcanoToken': 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En',
};
// 记录日志(不记录敏感信息)
Logger.info('启动BLE服务,配置参数键名: ${config}');
if (azureSpeechKey.isEmpty || openaiApiKey.isEmpty) {
Logger.warning('缺少必要的API密钥,请检查.env文件配置');
// 检查.env中相关密钥是否存在
if (azureSpeechKey.isEmpty) {
Logger.warning('AZURE_SPEECH_KEY未配置在.env文件中');
}
if (openaiApiKey.isEmpty) {
Logger.warning('OPENAI_API_KEY未配置在.env文件中');
}
}
final result = await AgentService.startAgentService(config);
if (result) {
Logger.info('BLE服务启动成功');
// 添加延迟以等待设备连接
await Future.delayed(const Duration(seconds: 2));
// 检查连接状态
final isConnected = await _bleService.isConnected();
if (isConnected) {
_isConnected.value = true;
_connectionState.value = BleConnectionState.connected;
// 如果设备信息存在,则获取详细信息
if (deviceInfo.value != null) {
// 获取详细设备信息
// fetchDeviceInfo();
}
}
} else {
Logger.error('BLE服务启动失败');
}
return result;
} catch (e) {
Logger.error('启动BLE服务失败: ${e.toString()}');
return false;
}
}
/// 停止BLE服务
Future<bool> stopAgentService() async {
try {
final result = await AgentService.stopAgentService();
if (result) {
Logger.info('BLE服务停止成功');
_isConnected.value = false;
_connectionState.value = BleConnectionState.disconnected;
} else {
Logger.error('BLE服务停止失败');
}
return result;
} catch (e) {
Logger.error('停止BLE服务失败: ${e.toString()}');
return false;
}
}
/// 获取电池电量
Future<int> getBatteryLevel() async {
try {
return await _bleService.getBatteryLevel();
} catch (e) {
Logger.error('获取电池电量失败: ${e.toString()}');
return 0;
}
}
/// 主动获取电池信息
Future<bool> fetchBatteryInfo() async {
try {
return await _bleService.getBatteryInfo();
} catch (e) {
Logger.error('获取电池信息失败: ${e.toString()}');
return false;
}
}
/// 主动产品id
Future<bool> getProductId() async {
try {
return await _bleService.getProductId();
} catch (e) {
Logger.error('获取设备版本失败: ${e.toString()}');
return false;
}
}
/// 主动获取设备版本信息
Future<bool> fetchDeviceVersion() async {
try {
return await _bleService.getDeviceVersion();
} catch (e) {
Logger.error('获取设备版本失败: ${e.toString()}');
return false;
}
}
/// 打开编码器
Future<bool> openEncoder() async {
try {
return await _bleService.openEncoder();
} catch (e) {
Logger.error('打开编码器失败: ${e.toString()}');
return false;
}
}
/// 打开解码器
Future<bool> openDecoder() async {
try {
return await _bleService.openDecoder();
} catch (e) {
Logger.error('打开解码器失败: ${e.toString()}');
return false;
}
}
/// 打开解码器
Future<bool> openA2DPDecoder() async {
try {
return await _bleService.openA2DPDecoder();
} catch (e) {
Logger.error('打开解码器失败: ${e.toString()}');
return false;
}
}
/// 关闭编解码器
Future<bool> closeCodec() async {
try {
return await _bleService.closeCodec();
} catch (e) {
Logger.error('关闭编解码器失败: ${e.toString()}');
return false;
}
}
/// 播放A2DP
Future<bool> playA2DP() async {
try {
return await _bleService.playA2DP();
} catch (e) {
Logger.error('播放A2DP失败: ${e.toString()}');
return false;
}
}
// ==================== 配对相关方法 ====================
/// 检查设备是否支持Companion Device Manager
Future<bool> isCompanionSupported() async {
try {
return await _bleService.isCompanionSupported();
} catch (e) {
Logger.error('检查Companion支持失败: ${e.toString()}');
return false;
}
}
/// 获取已配对的MAC地址
Future<String?> getPairedMacAddress() async {
try {
return await _bleService.getPairedMacAddress();
} catch (e) {
Logger.error('获取已配对MAC地址失败: ${e.toString()}');
return null;
}
}
/// 检查BLE连接状态
Future<bool> isBleConnected() async {
try {
return await _bleService.isBleConnected();
} catch (e) {
Logger.error('检查BLE连接状态失败: ${e.toString()}');
return false;
}
}
/// 设置是否是ota模式
Future<void> setEnterOta(bool enterOta) async {
try {
await _bleService.setEnterOta(enterOta);
} catch (e) {
Logger.error('设置是否是ota模式失败: ${e.toString()}');
}
}
/// 断开连接
Future<void> disconnect() async {
try {
await _bleService.disconnect();
} catch (e) {
Logger.error('断开连接失败: ${e.toString()}');
}
}
/// 验证设备是否可以配对
Future<bool> verifyDeviceCanPair(String macAddress) async {
try {
return true;
} catch (e) {
Logger.error('验证设备配对失败: ${e.toString()}');
return false;
}
}
/// 启动设备关联
Future<bool> startDeviceAssociation({
required String deviceNamePattern,
required String macAddress,
}) async {
try {
return await _bleService.startDeviceAssociation(
deviceNamePattern: deviceNamePattern,
macAddress: macAddress,
);
} catch (e) {
Logger.error('启动设备关联失败: ${e.toString()}');
return false;
}
}
/// 注册后台扫描
Future<bool> registerBackgroundScan({String? macAddress}) async {
try {
return await _bleService.registerBackgroundScan(macAddress: macAddress);
} catch (e) {
Logger.error('注册后台扫描失败: ${e.toString()}');
return false;
}
}
/// 注销后台扫描
Future<bool> unregisterBackgroundScan() async {
try {
return await _bleService.unregisterBackgroundScan();
} catch (e) {
Logger.error('注销后台扫描失败: ${e.toString()}');
return false;
}
}
/// 清除BLE配对设备
Future<bool> clearPairedDevices() async {
try {
return await _bleService.clearAssociations();
} catch (e) {
Logger.error('清除BLE配对设备失败: ${e.toString()}');
return false;
}
}
/// 注册服务
static Future<BleManager> init() async {
if (!Get.isRegistered<BleManager>()) {
Get.put(BleManager(), permanent: true);
}
return Future.value(Get.find<BleManager>());
}
}