import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:bluetooth_low_energy/bluetooth_low_energy.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:permission_handler/permission_handler.dart';
import 'comm_protocol.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:flutter/services.dart';
class BleAdvertiseManager {
// 单例实例
static final BleAdvertiseManager _instance = BleAdvertiseManager._internal();
static const MethodChannel _methodChannel = MethodChannel('com.gdmsdz.light/ble_power');
// 获取当前发射功率级别
// 工厂构造函数
factory BleAdvertiseManager() {
return _instance;
}
// 私有构造函数
BleAdvertiseManager._internal() {
// 初始化代码
}
// 蓝牙低功耗核心管理器(仅插件实例)
final PeripheralManager _peripheralManager = PeripheralManager();
Timer? _stopTimer;
// 状态管理变量
bool _isAdvertising = false;
bool _isSendingCommand = false;
String? _currentCommand;
bool _shouldCancelCurrentCommand = false;
final LightProtocol _protocol = LightProtocol.withSequentialProductId();
int _sequenceNumber = -1;
// 防快速点击:记录最后一次命令发送时间
DateTime? _lastCommandSentTime;
// 当前操作码,用于动态生成数据
int? _currentOpCode;
// 当前设备识别码,用于动态生成数据
int? _currentDeviceId;
// 添加一个标志,跟踪设备ID是否已手动设置
bool _isDeviceIdManuallySet = false;
// 使用ValueNotifier来管理广播状态
final ValueNotifier<bool> isAdvertisingNotifier = ValueNotifier(false);
// 全局快速广播开关与默认参数
bool fastMode = true;
bool muteAdvertising = false; // 静音广播开关
// 快速广播⬇️
static const int _fastRepeat = 10; // 连发次数
static const int _fastIntervalMs = 100; // 包间隔(ms)
static const double _fastBroadcastDurationMs = 200;// 单次广播持续(ms, 可为小数)
static const int _packetsPerBroadcast = 40;
int txPowerLevel = 4;
// 普通广播⬇️
static const int _normalRepeat = 1;
static const int _normalIntervalMs = 200;
static const double _normalBroadcastDurationMs = 150.0;
int get _repeatDefault => fastMode ? _fastRepeat : _normalRepeat;
int get _intervalDefaultMs => fastMode ? _fastIntervalMs : _normalIntervalMs;
double get _broadcastDefaultMs =>
fastMode ? _fastBroadcastDurationMs : _normalBroadcastDurationMs;
bool _shouldGenerateNewDeviceId = false;
Future<List<int>> getAllDeviceIds() async {
return await DeviceIdManager().loadAllDeviceIds();
}
// 设置发射功率(仅安卓有效)
Future<bool> setTxPowerLevel(int powerLevel) async {
if (!Platform.isAndroid) {
debugPrint('发射功率设置仅支持安卓平台');
return false;
}
try {
// 调用原生方法,传入功率级别(如-21、-15、-7、0、4)
final bool success = await _methodChannel.invokeMethod(
'setTxPowerLevel',
{'powerLevel': powerLevel},
);
debugPrint('发射功率设置${success ? '成功' : '失败'},级别: $powerLevel dBm');
return success;
} on PlatformException catch (e) {
debugPrint('设置发射功率出错: ${e.message}');
return false;
}
}
// 创建一个新设备。
Future<int> createNewDevice() async {
final newDeviceId = await DeviceIdManager().getNextAvailableDeviceId();
_currentDeviceId = newDeviceId;
await DeviceIdManager().saveDeviceId(newDeviceId);
// 不在此处设置上次使用的设备ID,以避免冲突。
debugPrint('创建新设备,分配了新的设备ID: $newDeviceId');
await _initProductId(newDeviceId); // 为新设备初始化产品ID
return newDeviceId;
}
/// 将管理器的上下文切换到现有设备。
/// 如果成功则返回true,如果设备ID不存在则返回false。
Future<bool> switchToDeviceId(int deviceId) async {
final deviceIds = await DeviceIdManager().loadAllDeviceIds();
if (!deviceIds.contains(deviceId)) {
debugPrint('切换失败:设备ID $deviceId 不存在');
return false;
}
_currentDeviceId = deviceId;
await _initProductId(deviceId); // 加载现有设备的产品ID
debugPrint('已切换到设备ID: $deviceId');
return true;
}
Future<void> _saveDeviceId(int deviceId) async {
try {
final directory = await getApplicationDocumentsDirectory();
final deviceIdPath = '${directory.path}/device_id.dat';
await File(deviceIdPath).writeAsString(deviceId.toString());
debugPrint('保存设备ID: $deviceId');
} catch (e) {
debugPrint('保存设备ID时出错: $e');
}
}
void checkDeviceIdStatus() {
debugPrint(
'当前设备ID状态 - ID: $_currentDeviceId, 手动设置: $_isDeviceIdManuallySet');
}
// 将2字节值转换为完整的128位蓝牙UUID
UUID _convertToBluetoothUUID(int twoByteValue) {
// 交换高低位
int swappedValue =
((twoByteValue & 0xFF) << 8) | ((twoByteValue >> 8) & 0xFF);
String uuidStr =
'0000${swappedValue.toRadixString(16).padLeft(4, '0')}-0000-1000-8000-00805F9B34FB';
return UUID.fromString(uuidStr);
}
Future<List<int>> generateNewProductId() async {
final newId = await _generateSequentialProductId();
_protocol.setProductId(Uint8List.fromList(newId));
final counterPath = await _counterFilePath;
final counterFile = File(counterPath);
int counter = 1;
if (await counterFile.exists()) {
final content = await counterFile.readAsString();
counter = int.tryParse(content) ?? 1;
}
// 修复:使用当前设备ID保存新识别码
if (_currentDeviceId != null) {
await _saveProductId(_currentDeviceId!, newId);
} else {
await _saveProductId(0, newId);
}
return newId;
}
Future<void> _initProductId(int deviceId) async {
try {
final path = await _getProductIdFilePath(deviceId);
final file = File(path);
if (await file.exists()) {
final bytes = await file.readAsBytes();
if (bytes.length == 6) {
_protocol.setProductId(Uint8List.fromList(bytes));
debugPrint('加载设备ID $deviceId 的识别码: 0x${_bytesToHex(bytes)}');
return;
}
}
} catch (e) {
debugPrint('加载产品识别码时出错: $e');
}
List<int> newId = [deviceId, 0x00, 0x00, 0x00, 0x00, 0x00];
_protocol.setProductId(Uint8List.fromList(newId));
await _saveProductId(deviceId, newId);
debugPrint('为设备ID $deviceId 生成新识别码: 0x${_bytesToHex(newId)}');
}
void setProductId(int deviceId, List<int> productId) {
_currentDeviceId = deviceId; // 保存设备ID用于动态生成
if (productId.length != 6) {
throw ArgumentError("产品识别码必须是6字节");
}
_protocol.setProductId(Uint8List.fromList(productId));
_saveProductId(deviceId, productId);
}
List<int> get currentProductId => _protocol.productId.toList();
Future<void> _saveProductId(int deviceId, List<int> productId) async {
try {
final directory = await getApplicationDocumentsDirectory();
final path = '${directory.path}/product_id_$deviceId.dat';
final file = File(path);
await file.writeAsBytes(productId);
await _saveProductIdHistory(productId);
} catch (e) {
debugPrint('保存产品识别码时出错: $e');
}
}
Future<String> get _counterFilePath async {
final directory = await getApplicationDocumentsDirectory();
return '${directory.path}/product_counter.txt';
}
static String _bytesToHex(List<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
}
Future<List<int>> _generateSequentialProductId() async {
try {
final counterPath = await _counterFilePath;
final counterFile = File(counterPath);
int counter = 1;
if (await counterFile.exists()) {
final content = await counterFile.readAsString();
counter = int.tryParse(content) ?? 1;
}
Uint8List id = _protocol.generateSequentialProductId();
await counterFile.writeAsString((counter + 1).toString());
debugPrint('生成顺序产品识别码: 0x${_bytesToHex(id)}');
return id.toList();
} catch (e) {
debugPrint('生成顺序产品识别码时出错: $e');
return _generateFallbackProductId();
}
}
List<int> _generateFallbackProductId() {
final random = Random();
int firstByte = random.nextInt(256);
List<int> id = [firstByte, 0x00, 0x00, 0x00, 0x00, 0x00];
debugPrint(
'新产品识别码: 0x${id[0].toRadixString(16).padLeft(2, '0').toUpperCase()}');
return id;
}
Future<void> _saveProductIdHistory(List<int> productId) async {
try {
final historyPath = await _productIdHistoryPath;
final historyFile = File(historyPath);
String history =
await historyFile.exists() ? await historyFile.readAsString() : '';
history +=
'${DateTime.now().toIso8601String()}: 0x${_bytesToHex(productId)}\n';
await historyFile.writeAsString(history);
} catch (e) {
debugPrint('保存产品识别码历史记录时出错: $e');
}
}
Future<String> get _productIdHistoryPath async {
final directory = await getApplicationDocumentsDirectory();
return '${directory.path}/product_id_history.txt';
}
Future<String> _getProductIdFilePath(int deviceId) async {
final directory = await getApplicationDocumentsDirectory();
return '${directory.path}/product_id_$deviceId.dat';
}
int _getNextSequenceNumber() {
_sequenceNumber = (_sequenceNumber + 1);
if (_sequenceNumber > 256) {
_sequenceNumber = 1;
}
return _sequenceNumber;
}
// 通用方法:根据设备ID和操作码动态填充数据体
// 仅填充00 01,剩余用00补充(示例)
void _fillDynamicDataToBody(
Uint8List dataBody,
int startIndex,
int length,
int opCode,
) {
if (_currentDeviceId == null) {
debugPrint('设备ID未初始化,使用硬编码00 01填充数据体');
for (int i = 0; i < length; i++) {
int position = startIndex + i;
if (position >= dataBody.length) break;
// 前两个字节为00、01,后续都为00
dataBody[position] = i == 0 ? 0x00 : (i == 1 ? 0x01 : 0x00);
}
return;
}
}
// 修改广播发送逻辑,优化广播数据结构(可配置持续时间与更短启停间隔)
Future<void> _sendAdvNonConnIndBroadcast(
int opCode, Uint8List dataBody, {double broadcastDurationMs = 100.0, required int txPowerLevel, }) async {
try {
// 权限检查
if (!await _checkBluetoothPermissions()) {
debugPrint('蓝牙权限不足,无法发送广播');
EasyLoading.showError('蓝牙权限不足,请授予必要权限');
// 抛出特定异常,让上层调用者知道是权限问题
throw Exception('蓝牙权限不足');
}
if (Platform.isAndroid) {
await setTxPowerLevel(txPowerLevel);
}
// 确保设备ID已初始化
if (_currentDeviceId == null) {
debugPrint('设备ID未初始化,将创建一个新设备');
await createNewDevice();
}
Uint8List broadcastPacket = dataBody;
debugPrint('收到的广播包长度: ${broadcastPacket.length}字节');
// 将广播包转换为十六进制字符串
String broadcastPacketHex = broadcastPacket
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
.join('')
.toUpperCase();
// 添加调试日志:打印广播包十六进制字符串和设备ID
debugPrint(
'生成的广播包十六进制字符串 (长度: ${broadcastPacketHex.length}): $broadcastPacketHex');
debugPrint('当前使用的设备ID: $_currentDeviceId');
// 使用当前设备ID、操作码和广播包十六进制字符串生成动态UUID列表
List<UUID> uuidList = UuidManager.getDynamicServiceUuids(
deviceId: _currentDeviceId!,
opCode: opCode,
broadcastPacketHex: broadcastPacketHex)
.map((uuid16) {
int twoByteValue = int.parse(uuid16, radix: 16);
return _convertToBluetoothUUID(twoByteValue);
}).toList();
// 添加调试日志:打印UUID列表
debugPrint(
'生成的UUID列表 (数量: ${uuidList.length}): ${uuidList.map((uuid) => uuid.toString()).join(', ')}');
// 广播数据结构
Advertisement advertisement = Advertisement(
name: 'MS',
serviceUUIDs: uuidList,
);
if (_isAdvertising) {
debugPrint('停止当前广播...');
await stopAdvertising();
// 缩短停播到重启之间的等待,依据本次广播持续时间动态调整,最小5ms,最大50ms
final int coolDownMs = max(5, min(50, (broadcastDurationMs / 3).round()));
await Future.delayed(Duration(milliseconds: coolDownMs));
}
if (_shouldCancelCurrentCommand) {
debugPrint("当前命令 ($_currentCommand) 在广播会话间隔期间被取消。");
}
// 启动广播
await _peripheralManager.startAdvertising(advertisement);
_isAdvertising = true;
isAdvertisingNotifier.value = true;
debugPrint('广播已成功启动,操作码: 0x${opCode.toRadixString(16).padLeft(2, '0')}');
debugPrint('当前广播状态: _isAdvertising=$_isAdvertising, _shouldCancelCurrentCommand=$_shouldCancelCurrentCommand');
// 按可配置时长自动停止广播
_stopTimer?.cancel();
_stopTimer = Timer(Duration(milliseconds: broadcastDurationMs.round()), () {
if (_isAdvertising) {
stopAdvertising();
}
});
debugPrint('广播已启动,将在 ${broadcastDurationMs}ms 后自动停止');
} catch (e) {
debugPrint('发送广播时捕获到未处理的异常: $e');
// 专门处理蓝牙错误代码3(权限或蓝牙未开启)
if (e.toString().contains('error code: 3')) {
// debugPrint('蓝牙广播失败:错误代码3 - 请检查蓝牙权限和蓝牙开关状态');
// EasyLoading.showError('蓝牙广播失败,请检查蓝牙权限和开关状态');
// 抛出特定异常,让外层循环能够正确处理,避免无限广播
throw Exception('蓝牙广播失败:错误代码3 - 权限或蓝牙未开启');
}
// 确保即使发生错误也重置状态
_isAdvertising = false;
isAdvertisingNotifier.value = false;
// 对于其他异常也重新抛出,让外层处理
rethrow;
}
}
// 权限检查函数
Future<bool> _checkBluetoothPermissions() async {
// iOS平台需要特殊处理蓝牙权限
if (Platform.isIOS) {
// iOS上使用蓝牙权限检查
PermissionStatus bluetoothStatus = await Permission.bluetooth.status;
if (bluetoothStatus != PermissionStatus.granted) {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetooth,
].request();
return statuses[Permission.bluetooth] == PermissionStatus.granted;
}
return true;
} else {
// Android平台使用标准蓝牙权限检查
PermissionStatus advertiseStatus =
await Permission.bluetoothAdvertise.status;
PermissionStatus connectStatus = await Permission.bluetoothConnect.status;
PermissionStatus scanStatus = await Permission.bluetoothScan.status;
if (advertiseStatus != PermissionStatus.granted ||
connectStatus != PermissionStatus.granted ||
scanStatus != PermissionStatus.granted) {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothAdvertise,
Permission.bluetoothConnect,
Permission.bluetoothScan,
].request();
return statuses[Permission.bluetoothAdvertise] ==
PermissionStatus.granted &&
statuses[Permission.bluetoothConnect] == PermissionStatus.granted &&
statuses[Permission.bluetoothScan] == PermissionStatus.granted;
}
return true;
}
}
void _stopBroadcast() async {
try {
await _peripheralManager.stopAdvertising();
_isAdvertising = false;
isAdvertisingNotifier.value = false;
} catch (e) {
debugPrint('停止广播失败: $e');
}
}
Future<void> stopAdvertising() async {
if (_isAdvertising) {
_stopTimer?.cancel();
await _peripheralManager.stopAdvertising();
_isAdvertising = false;
isAdvertisingNotifier.value = false;
debugPrint('广播已停止');
}
}
Future<void> cancelCurrentCommand() async {
if (_isSendingCommand) {
_shouldCancelCurrentCommand = true;
_stopTimer?.cancel();
debugPrint('取消当前命令: $_currentCommand');
if (_isAdvertising) {
await _peripheralManager.stopAdvertising().catchError((e) {
debugPrint('强制停止广播错误: $e');
});
_isAdvertising = false;
isAdvertisingNotifier.value = false;
}
// 移除立即重置状态的代码,统一由_sendCommand的finally块处理
// 保留取消标志,确保循环能够检测到取消
}
}
Future<void> startAdvertising(String serviceUuid, String command) async {
await _sendCommand(
commandName: 'startAdvertising',
opCode: 0x0C,
buildDataBody: (sequence) {
return _protocol.buildPairingPacket(sequence, true);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendUnbindCommand() async {
await _sendCommand(
commandName: 'unbindDevice',
opCode: 0x0C,
buildDataBody: (sequence) {
return _protocol.buildPairingPacket(sequence, false); // isPair: false for unbinding
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
// {int count = 10}
Future<void> advertiseButtonCommand(String command,) async {
await _sendCommand(
commandName: 'buttonCommandAdvNonConn',
opCode: 0x15,
buildDataBody: (sequence) {
Uint8List dataBody = Uint8List(13);
List<int> commandBytes = ('TC$command').codeUnits;
int copyLength = commandBytes.length < 13 ? commandBytes.length : 13;
dataBody.setRange(0, copyLength, commandBytes);
// 动态填充剩余部分
int remainingLength = 13 - copyLength;
if (remainingLength > 0) {
_fillDynamicDataToBody(dataBody, copyLength, remainingLength, 0x15);
}
return dataBody;
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendFanModeCommand(int mode) async {
await _sendCommand(
commandName: 'fanModeControlAdvNonConn',
opCode: 0x16,
buildDataBody: (sequence) {
return _protocol.buildFanModePacket(sequence, mode);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendMotorDirectionCommand(bool isReverse) async {
await _sendCommand(
commandName: 'motorDirectionCommandAdvNonConn',
opCode: 0x15,
buildDataBody: (sequence) {
return _protocol.buildMotorDirectionPacket(sequence, !isReverse);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendOscillatingCommand(bool isEnabled) async {
await _sendCommand(
commandName: 'oscillatingControlAdvNonConn',
opCode: 0x17,
buildDataBody: (sequence) {
return _protocol.buildSwingPacket(sequence, isEnabled);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendSwitchCommand(bool isOn) async {
await _sendCommand(
commandName: 'switchControlAdvNonConn',
opCode: 0x00,
buildDataBody: (sequence) {
return _protocol.buildSwitchControlPacket(sequence, isOn);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendMotorSpeedCommand(int speed) async {
if (speed < 1 || speed > 100) {
throw ArgumentError('风速值必须在1~100范围内,当前值: $speed');
}
await _sendCommand(
commandName: 'motorSpeedControlAdvNonConn',
opCode: 0x14,
buildDataBody: (sequence) {
return _protocol.buildMotorSpeedPacket(sequence, speed);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> advertisePairCommand(bool isPair) async {
await _sendCommand(
commandName: 'pairingCommandAdvNonConn',
opCode: 0x0C,
buildDataBody: (sequence) {
Uint8List dataBody = _protocol.buildPairingPacket(sequence, isPair);
// 确保数据体长度并动态填充剩余部分
if (dataBody.length < 14) {
Uint8List extendedBody = Uint8List(14);
extendedBody.setRange(0, dataBody.length, dataBody);
_fillDynamicDataToBody(
extendedBody, dataBody.length, 14 - dataBody.length, 0x0C);
return extendedBody;
}
return dataBody;
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendSetTimerCommandAdvNonConn(
int hour, int minute, int second, int weekByte) async {
await _sendCommand(
commandName: 'setTimerCommandAdvNonConn',
opCode: 0x0D,
buildDataBody: (sequence) {
return _protocol.buildSetTimerPacket(
sequence, hour, minute, second, weekByte);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendReadTimerCommand() async {
await _sendCommand(
commandName: 'readTimerCommand',
opCode: 0x0E,
buildDataBody: (sequence) {
Uint8List dataBody = _protocol.buildReadTimerPacket(sequence);
// 确保数据体长度并动态填充剩余部分
if (dataBody.length < 14) {
Uint8List extendedBody = Uint8List(14);
extendedBody.setRange(0, dataBody.length, dataBody);
_fillDynamicDataToBody(
extendedBody, dataBody.length, 14 - dataBody.length, 0x0E);
return extendedBody;
}
return dataBody;
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendUnbindDeviceCommand() async {
await advertiseButtonCommand('UNPAIR_DEVICE');
}
Future<void> sendSetBrightnessCommand(int brightness) async {
if (brightness < 0 || brightness > 100) {
throw ArgumentError('亮度值必须在0~100范围内');
}
await _sendCommand(
commandName: 'setBrightnessCommandAdvNonConn',
opCode: 0x01,
buildDataBody: (sequence) {
return _protocol.buildSetBrightnessPacket(sequence, brightness);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendSetColorTemperatureCommand(int temperature) async {
await _sendCommand(
commandName: 'setColorTemperatureCommandAdvNonConn',
opCode: 0x02,
buildDataBody: (sequence) {
Uint8List dataBody =
_protocol.buildSetColorTemperaturePacket(sequence, temperature);
// 确保数据体长度并动态填充剩余部分
if (dataBody.length < 14) {
Uint8List extendedBody = Uint8List(14);
extendedBody.setRange(0, dataBody.length, dataBody);
_fillDynamicDataToBody(
extendedBody, dataBody.length, 14 - dataBody.length, 0x02);
return extendedBody;
}
return dataBody;
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendSceneModeCommand(int mode) async {
await _sendCommand(
commandName: 'sceneModeCommandAdvNonConn',
opCode: 0x04,
buildDataBody: (sequence) {
return _protocol.buildSceneModePacket(sequence, mode);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendNightLightCommand() async {
await _sendCommand(
commandName: 'nightLightCommandAdvNonConn',
opCode: 0x19,
buildDataBody: (sequence) {
return _protocol.buildNightLightPacket(sequence);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendAuxLightCommand(bool isEnabled) async {
await _sendCommand(
commandName: 'auxLightCommandAdvNonConn',
opCode: 0x1A,
buildDataBody: (sequence) {
return _protocol.buildAuxLightPacket(sequence, isEnabled);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
// 发送设置灯珠数量命令
Future<void> sendSetLedCountCommand(int count) async {
await _sendCommand(
commandName: 'setLedCountCommandAdvNonConn',
opCode: 0x1B,
buildDataBody: (sequence) {
return _protocol.buildLedCountPacket(sequence, count);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
// 发送0x18操作码的广播命令(风扇开关)
Future<void> sendFanSwitchCommand(bool enable) async {
await _sendCommand(
commandName: 'FanSwitch',
opCode: 0x18,
buildDataBody: (sequence) {
return _protocol.buildFanSwitchPacket(sequence, enable);
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
Future<void> sendSetColorCommand(int r, int g, int b, int w,
bool setBrightness, int brightness) async {
await _sendCommand(
commandName: 'setColorCommandAdvNonConn',
opCode: 0x03,
buildDataBody: (sequence) {
Uint8List dataBody = _protocol.buildSetColorPacket(
sequence, r, g, b, w, setBrightness, brightness);
// 确保数据体长度并动态填充剩余部分
if (dataBody.length < 14) {
Uint8List extendedBody = Uint8List(14);
extendedBody.setRange(0, dataBody.length, dataBody);
_fillDynamicDataToBody(
extendedBody, dataBody.length, 14 - dataBody.length, 0x03);
return extendedBody;
}
return dataBody;
},
repeat: _repeatDefault,
intervalMs: _intervalDefaultMs,
broadcastDurationMs: _broadcastDefaultMs,
txPowerLevel: txPowerLevel,
);
}
// 支持在单次广播中发送多个数据包
Future<void> _sendCommand({
required String commandName,
required int opCode,
required Uint8List Function(int sequence) buildDataBody,
int? repeat,
int? intervalMs,
double? broadcastDurationMs,
int? packetsPerBroadcast, // 新增参数
int txPowerLevel = 4,
}) async {
// 防快速点击:检查是否在短时间内重复发送相同命令
final now = DateTime.now();
if (_lastCommandSentTime != null &&
now.difference(_lastCommandSentTime!) < const Duration(milliseconds: 50) &&
_currentCommand == commandName) {
debugPrint('防快速点击:跳过重复命令: $commandName');
return;
}
// 更新最后发送时间
_lastCommandSentTime = now;
// 在开始新命令前取消之前的命令
// await cancelCurrentCommand();
// await Future.delayed(const Duration(milliseconds: 50)); // 给足够时间让系统停止广播
final int finalRepeat = repeat ?? _repeatDefault;
final int finalIntervalMs = intervalMs ?? _intervalDefaultMs;
final double finalBroadcastDurationMs = broadcastDurationMs ?? _broadcastDefaultMs;
final int finalPacketsPerBroadcast = packetsPerBroadcast ?? _packetsPerBroadcast; // 默认使用10个数据包
debugPrint('当前设置: 重复次数=$finalRepeat, 数据包数量=$finalPacketsPerBroadcast, 间隔=${finalIntervalMs}ms, 广播持续时间=${finalBroadcastDurationMs}ms');
// 调试静音:直接跳过所有广播发送
if (muteAdvertising) {
debugPrint('广播已静音,跳过发送: ' + commandName);
return;
}
// 确保设备ID已初始化
if (_currentDeviceId == null) {
debugPrint('设备ID为null,开始初始化...');
await createNewDevice();
} else {
debugPrint('设备ID已初始化: $_currentDeviceId');
}
// 增强取消逻辑:确保在发送新命令前完全停止之前的所有广播
// 此逻辑已被开头的cancelCurrentCommand()调用替代
_isSendingCommand = true;
_currentCommand = commandName;
_shouldCancelCurrentCommand = false;
_currentOpCode = opCode;
try {
// 在命令开始时获取序列号,确保整个命令使用相同的序列号
int sequence = _getNextSequenceNumber();
for (int i = 0; i < finalRepeat; i++) {
if (_shouldCancelCurrentCommand) {
debugPrint("当前命令 ($commandName) 在重复期间被取消。");
break;
}
// 单次广播会话中发送多个数据包
for (int j = 0; j < finalPacketsPerBroadcast; j++) {
debugPrint("开始处理数据包 ${j+1}/$finalPacketsPerBroadcast");
// 使用相同的序列号,不再为每个数据包获取新序列号
if (_shouldCancelCurrentCommand) {
debugPrint("当前命令 ($commandName) 在发送数据包期间被取消。");
break; // 直接返回而不是break,确保完全停止
}
Uint8List dataBody = buildDataBody(sequence);
debugPrint('发送数据包(${j + 1}/$finalPacketsPerBroadcast) - 操作码: 0x${opCode.toRadixString(16).padLeft(2, '0')}');
await _sendAdvNonConnIndBroadcast(opCode, dataBody, broadcastDurationMs: finalBroadcastDurationMs,txPowerLevel: txPowerLevel,);
debugPrint("数据包 ${j+1}/$finalPacketsPerBroadcast 发送完成");
if (j < finalPacketsPerBroadcast - 1) {
if (_shouldCancelCurrentCommand) {
debugPrint("当前命令 ($commandName) 在数据包间隔期间被取消。");
break; // 直接返回而不是break,确保完全停止
}
// 数据包之间的间隔可以比广播会话之间的间隔更小
debugPrint('数据包间隔前状态: _shouldCancelCurrentCommand=$_shouldCancelCurrentCommand');
await Future.delayed(Duration(milliseconds: finalIntervalMs ~/ 2));
debugPrint('数据包间隔后状态: _shouldCancelCurrentCommand=$_shouldCancelCurrentCommand');
}
}
if (i < finalRepeat - 1) {
if (_shouldCancelCurrentCommand) {
debugPrint("当前命令 ($commandName) 在广播会话间隔期间被取消。");
break; // 直接返回而不是break,确保完全停止
}
await Future.delayed(Duration(milliseconds: finalIntervalMs));
}
}
} catch (e) {
debugPrint('发送命令出错: $e');
// 如果是权限不足的异常,不显示错误提示,因为已经在权限检查时显示过了
if (!e.toString().contains('蓝牙权限不足')) {
// EasyLoading.showError('发送命令失败: $e');
}
// 强制停止所有广播,防止异常后继续发送
try {
await forceStopAllAdvertising();
} catch (stopError) {
debugPrint('异常后强制停止广播失败: $stopError');
}
// 重新抛出异常,确保状态正确重置
rethrow;
} finally {
// 兜底:确保广播在命令完成后被停止,防止极端情况下仍在播
try {
await stopAdvertising();
debugPrint('命令完成,已确保停止广播');
} catch (e) {
debugPrint('命令完成后停止广播异常: $e');
}
// 无论命令是否被取消,都重置状态(移除条件判断)
_isSendingCommand = false;
_currentCommand = null;
_currentOpCode = null;
_shouldCancelCurrentCommand = false;
_lastCommandSentTime = null;
}
}
// 添加一个强制停止所有广播的方法
Future<void> forceStopAllAdvertising() async {
debugPrint('强制停止所有广播...');
_stopTimer?.cancel();
try {
await _peripheralManager.stopAdvertising();
} catch (e) {
debugPrint('强制停止广播异常: $e');
}
_isAdvertising = false;
isAdvertisingNotifier.value = false;
debugPrint('强制停止广播完成');
}
}
class UuidManager {
// 动态生成 UUID,从广播包的十六进制字符串中截取
static List<String> getDynamicServiceUuids({
required int deviceId,
required int opCode,
required String broadcastPacketHex,
}) {
List<String> dynamicUuids = [];
String protocolString =
broadcastPacketHex.padLeft(52, '0').substring(0, 52);
// 从协议字符串中每 2 字节截取生成动态 UUID
for (int i = 0; i < protocolString.length; i += 4) {
int end = i + 4;
if (end > protocolString.length) {
end = protocolString.length;
}
// 2 字节作为一个 UUID 部分
String uuidPart = protocolString.substring(i, end);
// 如果不足2 字节,用 0 填充
if (uuidPart.length < 4) {
uuidPart = uuidPart.padRight(4, '0');
}
dynamicUuids.add(uuidPart.toUpperCase());
}
while (dynamicUuids.length < 13) {
// 当数据不足时,用0000填充剩余UUID
dynamicUuids.add('0000');
}
int seed = (deviceId ^ opCode ^ dynamicUuids.length) & 0xFFFF;
// 交换高低位
int swappedSeed = ((seed & 0xFF) << 8) | ((seed >> 8) & 0xFF);
String uuid = swappedSeed.toRadixString(16).padLeft(4, '0').toUpperCase();
if (dynamicUuids.length > 13) {
dynamicUuids = dynamicUuids.sublist(0, 13);
}
// 调试日志
debugPrint(
'UuidManager生成的动态UUID列表 (数量: ${dynamicUuids.length}): $dynamicUuids');
return dynamicUuids;
}
}
// 添加到文件末尾,在UuidManager类之后
class DeviceIdManager {
static final DeviceIdManager _instance = DeviceIdManager._internal();
factory DeviceIdManager() => _instance;
DeviceIdManager._internal();
// 加载所有保存的设备ID
Future<List<int>> loadAllDeviceIds() async {
try {
final directory = await getApplicationDocumentsDirectory();
final deviceIdsPath = '${directory.path}/device_ids.dat';
final deviceIdsFile = File(deviceIdsPath);
if (await deviceIdsFile.exists()) {
final content = await deviceIdsFile.readAsString();
return content
.split(',')
.map((id) => int.tryParse(id) ?? 0)
.where((id) => id > 0)
.toList();
}
} catch (e) {
debugPrint('加载设备ID列表出错: $e');
}
return [];
}
// 保存新的设备ID
Future<void> saveDeviceId(int deviceId) async {
try {
final directory = await getApplicationDocumentsDirectory();
final deviceIdsPath = '${directory.path}/device_ids.dat';
final deviceIdsFile = File(deviceIdsPath);
List<int> deviceIds = [];
if (await deviceIdsFile.exists()) {
final content = await deviceIdsFile.readAsString();
deviceIds = content
.split(',')
.map((id) => int.tryParse(id) ?? 0)
.where((id) => id > 0)
.toList();
}
// 确保设备ID唯一
if (!deviceIds.contains(deviceId)) {
deviceIds.add(deviceId);
deviceIds.sort(); // 保持ID有序
await deviceIdsFile.writeAsString(deviceIds.join(','));
debugPrint('保存新设备ID: $deviceId, 当前设备ID列表: $deviceIds');
}
} catch (e) {
debugPrint('保存设备ID出错: $e');
}
}
// 获取下一个可用的设备ID
Future<int> getNextAvailableDeviceId() async {
List<int> deviceIds = await loadAllDeviceIds();
if (deviceIds.isEmpty) {
return 1; // 如果没有设备ID,从1开始
}
// 找到第一个缺失的ID
int nextId = 1;
for (int id in deviceIds) {
if (id > nextId) {
break;
}
nextId = id + 1;
}
return nextId;
}
// 获取上次使用的设备ID
Future<int?> getLastUsedDeviceId() async {
try {
final directory = await getApplicationDocumentsDirectory();
final lastUsedPath = '${directory.path}/last_used_device_id.dat';
final lastUsedFile = File(lastUsedPath);
if (await lastUsedFile.exists()) {
final content = await lastUsedFile.readAsString();
return int.tryParse(content) ?? null;
}
} catch (e) {
debugPrint('加载上次使用的设备ID出错: $e');
}
return null;
}
// 设置上次使用的设备ID
Future<void> setLastUsedDeviceId(int deviceId) async {
try {
final directory = await getApplicationDocumentsDirectory();
final lastUsedPath = '${directory.path}/last_used_device_id.dat';
await File(lastUsedPath).writeAsString(deviceId.toString());
debugPrint('设置上次使用的设备ID: $deviceId');
} catch (e) {
debugPrint('保存上次使用的设备ID出错: $e');
}
}
// 删除上次使用的设备ID
Future<void> deleteLastUsedDeviceId() async {
try {
final directory = await getApplicationDocumentsDirectory();
final lastUsedPath = '${directory.path}/last_used_device_id.dat';
final file = File(lastUsedPath);
if (await file.exists()) {
await file.delete();
debugPrint('上次使用的设备ID已删除');
}
} catch (e) {
debugPrint('删除上次使用的设备ID时出错: $e');
}
}
// 删除指定的设备ID
Future<void> deleteDeviceId(int deviceId) async {
try {
final directory = await getApplicationDocumentsDirectory();
final deviceIdsPath = '${directory.path}/device_ids.dat';
final deviceIdsFile = File(deviceIdsPath);
if (await deviceIdsFile.exists()) {
List<int> deviceIds = await loadAllDeviceIds();
if (deviceIds.remove(deviceId)) {
await deviceIdsFile.writeAsString(deviceIds.join(','));
debugPrint('删除设备ID: $deviceId, 当前设备ID列表: $deviceIds');
}
}
} catch (e) {
debugPrint('删除设备ID出错: $e');
}
}
}