import 'dart:async'; import 'dart:typed_data'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:mhtctrl/mhtctrl.dart'; class BedControlService { // 协议常量 static const List frameHeader = [0xFF, 0xFF, 0xFF, 0xFF, 0x05]; static const int minFrameLength = 11; final Future Function(Uint8List) _writeFn; final StreamController _statusController = StreamController.broadcast(); Stream get statusStream => _statusController.stream; BedControlService(this._writeFn); // 校验和计算(根据协议文档硬编码) static List _getChecksum(int commandCode) { final checksumMap = { 0x00: [0xD7, 0x00], // 停止 0x01: [0x16, 0xC0], // 头部上升 0x02: [0x56, 0xC1], // 头部下降 0x03: [0x97, 0x01], // 背部上升 0x04: [0xD6, 0xC3], // 背部下降 0x05: [0x17, 0x03], // 观影模式 0x06: [0x57, 0x02], // 腿部上升 0x07: [0x96, 0xC2], // 腿部下降 0x08: [0xD6, 0xC6], // 复位 0x09: [0x17, 0x06], // 零重力 0x0A: [0x57, 0x07], // 记忆1 0x0B: [0x96, 0xC7], // 记忆2 0x0C: [0xC7, 0x04], // 童锁 0x0D: [0x16, 0xC5], // 臀部上升 0x0E: [0x56, 0xC4], // 臀部下降 0x0F: [0x97, 0x04], // 止鼾 0x10: [0xD6, 0xCC], // 背部按摩+ 0x11: [0x17, 0x0C], // 背部按摩- 0x12: [0x57, 0x0D], // 腿部按摩+ 0x13: [0x96, 0xCD], // 腿部按摩- 0x14: [0xD7, 0x0F], // 按摩频率+ 0x15: [0x16, 0xCF], // 按摩频率- 0x16: [0x56, 0xCE], // 按摩时间10min 0x17: [0x97, 0x0E], // 按摩时间20min 0x18: [0xD7, 0x0A], // 按摩时间30min 0x19: [0x16, 0xCA], // 灯光10min 0x1A: [0x56, 0xCB], // 灯光8H 0x1B: [0x97, 0x0B], // 灯光10H 0x1C: [0xD6, 0xC9], // 按摩停止 0x2B: [0x97, 0x1F], // 背腿同上 0x2C: [0xD6, 0xDD], // 背腿同下 0x48: [0xD7, 0x36], // 阅读模式 0x49: [0x16, 0xF6], // 舒压模式 0x4E: [0x57, 0x34], // 瑜伽模式 0x6A: [0x57, 0x2F], // 助眠模式 0x50: [0xD7, 0x3C], // 背部按摩强度1 0x51: [0x16, 0xFC], // 背部按摩强度2 0x52: [0x56, 0xFD], // 背部按摩强度3 0x54: [0xD6, 0xFF], // 腿部按摩强度1 0x55: [0x17, 0x3F], // 腿部按摩强度2 0x56: [0x57, 0x3E], // 腿部按摩强度3 0x57: [0x96, 0xFE], // 按摩频率1 0x58: [0xD6, 0xFA], // 按摩频率2 0x59: [0x17, 0x3A], // 按摩频率3 0x5A: [0x57, 0x3B], // 按摩频率4 0x4F: [0x96, 0xF4], // 停止背 0x53: [0x97, 0x3D], // 停止腿 0x47: [0x87, 0x33], // 休闲模式 0xE6: [0x46, 0x8B], // 腰部放松 0xE5: [0x06, 0x8A], // 腿部放松 }; return checksumMap[commandCode] ?? [0x00, 0x00]; } // 基础指令构建(11字节完整格式) Uint8List _buildCommand(int byte5, int byte6, int byte7, int byte8) { final data = Uint8List(11); data.setAll(0, frameHeader); // FF FF FF FF 05 data[5] = byte5; data[6] = byte6; data[7] = byte7; data[8] = byte8; final checksum = _getChecksum(byte8); data[9] = checksum[0]; // 校验和高字节 data[10] = checksum[1]; // 校验和低字节 return data; } // 进度控制指令构建(特殊格式) Uint8List _buildProgressCommand(int progress, int baseCmd, int commandCode) { final data = Uint8List(11); data.setAll(0, frameHeader); data[5] = progress; data[6] = baseCmd >> 8; data[7] = baseCmd & 0xFF; data[8] = commandCode; final checksum = _getChecksum(commandCode); data[9] = checksum[0]; data[10] = checksum[1]; return data; } //====== 指令集 ======// // 1. 遥控器上报功能(固定10字节) Future enableRemoteReport() => _sendCommand( [0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0x00, 0xF2, 0x56, 0x85]); // 2. 基础控制指令 Future stop() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x00)); Future moveHeadUp() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x01)); Future moveHeadDown() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x02)); Future moveBackUp() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x03)); Future moveBackDown() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x04)); Future moveLegUp() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x06)); Future moveLegDown() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x07)); Future moveHipUp() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x0D)); Future moveHipDown() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x0E)); Future resetPosition() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x08)); // 3. 预设模式指令 Future setMovieMode([int progress = 0]) => progress > 0 ? _sendCommand(_buildProgressCommand(progress, 0x0500, 0x05)) : _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x05)); Future setZeroGravity([int progress = 0]) => progress > 0 ? _sendCommand(_buildProgressCommand(progress, 0x0900, 0x09)) : _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x09)); Future setAntiSnore([int progress = 0]) => progress > 0 ? _sendCommand(_buildProgressCommand(progress, 0x0F00, 0x0F)) : _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x0F)); //一键放松 Future setSleepAssist() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x6A)); // 休闲模式 Future setXiuxianMode() => _sendCommand(_buildCommand(0x00, 0x05, 0x00, 0x47)); // 腰部放松 Future setYaoRelaxMode() => _sendCommand(_buildCommand(0x00, 0x05, 0x00, 0xE6)); // 腿部放松 Future setLegRelaxMode() => _sendCommand(_buildCommand(0x00, 0x05, 0x00, 0xE5)); Future setReadingMode() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x48)); Future setRelaxMode() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x49)); Future setYogaMode() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x4E)); // 4. 记忆功能 Future saveMemory(int slot) { assert(slot >= 1 && slot <= 2); return _sendCommand( _buildCommand(0x00, 0x00, 0x00, slot == 1 ? 0x0A : 0x0B)); } // 5. 按摩控制 Future increaseBackMassage() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x10)); Future decreaseBackMassage() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x11)); Future increaseLegMassage() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x12)); Future decreaseLegMassage() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x13)); Future increaseMassageFreq() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x14)); Future decreaseMassageFreq() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x15)); Future setMassageTime(int minutes) { final cmd = switch (minutes) { 10 => _buildCommand(0x00, 0x00, 0x00, 0x16), 20 => _buildCommand(0x00, 0x00, 0x00, 0x17), 30 => _buildCommand(0x00, 0x00, 0x00, 0x18), _ => _buildCommand(0x00, 0x00, 0x00, 0x16), }; return _sendCommand(cmd); } Future stopMassage() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x1C)); // 6. 灯光控制 Future toggleLight10Min() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x19)); Future toggleLight8H() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x1A)); Future toggleLight10H() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x1B)); // 7. 组合控制 Future moveBothUp() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x2B)); Future moveBothDown() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x2C)); // 8. 童锁功能(固定11字节) Future toggleChildLock() => _sendCommand( [0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0x05, 0x00, 0x0C, 0xC7, 0x04]); // 背部按摩强度控制 Future setBackMassageLevel1() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x50)); Future setBackMassageLevel2() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x51)); Future setBackMassageLevel3() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x52)); // 腿部按摩强度控制 Future setLegMassageLevel1() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x54)); Future setLegMassageLevel2() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x55)); Future setLegMassageLevel3() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x56)); //按摩时间 Future setMassageTime10Min() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x16)); Future setMassageTime20Min() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x17)); Future setMassageTime30Min() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x18)); Future setMassageFrequency1() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x57)); Future setMassageFrequency2() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x58)); Future setMassageFrequency3() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x59)); Future setMassageFrequency4() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x5A)); Future stopLeg() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x53)); Future stopBack() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x4F)); //====== 核心方法 ======// Future _sendCommand(List command) async { debugPrint( '发送指令: ${command.map((e) => e.toRadixString(16).padLeft(2, '0'))}'); try { return await _writeFn(Uint8List.fromList(command)); } catch (e) { debugPrint('指令发送失败: $e'); return false; } } void parseResponse(List data) { if (data.length >= minFrameLength && data[0] == 0xFF && data[1] == 0xFF) { // 设备上报帧处理(第7字节为0x06) if (data[7] == 0x06) { final functionCode = data[8]; debugPrint('收到设备上报: 功能码=${functionCode.toRadixString(16)}'); } _statusController.add(BedStatus.fromBytes(data)); } } void dispose() => _statusController.close(); Future setAlarm({ required bool isStart, required List wakeTime, required List weeks, required bool isMassage, required bool isBell, }) async { // 1. 参数校验 if (wakeTime.length != 2 || weeks.length != 7) { debugPrint('参数错误:唤醒时间或星期数不符'.tr); return false; } // 2. 构造指令(固定头 + 动态数据) final arr = [ 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x02, 0x13, // 固定头 isStart ? 0x01 : 0xA1, // 开关状态 int.parse(wakeTime[0], radix: 16), // 小时(16进制) int.parse(wakeTime[1], radix: 16), // 分钟(16进制) 0x00, // 保留位 ]; // 3. 计算星期掩码 final weekValues = weeks.map((w) => int.tryParse(w) ?? 0).toList(); final weekMask = weekValues[0] * 2 + // 周一 weekValues[1] * 4 + // 周二 weekValues[2] * 8 + // 周三 weekValues[3] * 16 + // 周四 weekValues[4] * 32 + // 周五 weekValues[5] * 64 + // 周六 weekValues[6] * 128; // 周日 arr.addAll([ weekMask, weekMask > 1 ? 1 : 0, // 是否重复 0x01, // 固定值 isMassage ? 1 : 0, isBell ? 1 : 0, ]); // 4. 计算校验和(小端序) _addSumInEnd(arr); // 5. 发送指令并等待响应 debugPrint('闹钟指令: ${arr.map((e) => e.toRadixString(16).padLeft(2, '0'))}'); return await _writeFn(Uint8List.fromList(arr)); } List _calculateChecksum(List data) { int sum = 0; for (var byte in data) { sum += byte; } return [sum & 0xFF, (sum >> 8) & 0xFF]; } void _addSumInEnd(List arr) { int sum = arr.fold(0, (acc, byte) => acc + byte); arr.add(sum & 0xFF); // 低字节 arr.add((sum >> 8) & 0xFF); // 高字节 } Future setDeviceTime(DateTime dateTime) async { // 1. 参数校验 // if (dateTime.year < 2000 || dateTime.year > 2099) { // debugPrint('年份必须在2000-2099之间'); // return false; // } DateTime date = DateTime.now(); // 2. 构造基础指令数据(不含校验和) final arr = [ 0xFF, 0xFF, 0xFF, 0xFF, // 帧头 (0-3) 0x01, 0x00, 0x01, 0x11, // 功能标识:时间校验 (4-7) ]; arr.add(int.parse("${date.hour}", radix: 16)); arr.add(int.parse("${date.minute}", radix: 16)); arr.add(int.parse("${date.second}", radix: 16)); arr.add(int.parse("${date.weekday}", radix: 16)); arr.add(int.parse("${date.year}".substring(2), radix: 16)); arr.add(int.parse("${date.month}", radix: 16)); arr.add(int.parse("${date.day}", radix: 16)); // 3. 计算并添加校验和(低字节在前) _addSumInEnd(arr); // 4. 发送指令 debugPrint( '时间校验指令: ${arr.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}'); debugPrint('设置时间: ${dateTime.toString()}'); return _sendCommand(arr); } /// 将DateTime的weekday(1-7)转换为协议要求的星期值 /// DateTime.weekday: 1=Monday, 2=Tuesday, ..., 7=Sunday /// 协议要求: 1=周一, 2=周二, ..., 7=周日 int _getWeekdayValue(int weekday) { // 由于DateTime的weekday已经是1=周一, 7=周日,直接返回即可 return weekday; } /// 使用当前系统时间同步设备时间 Future syncDeviceTime() async { final now = DateTime.now(); debugPrint('开始同步设备时间: ${now.toString()}'); return await setDeviceTime(now); } /// 设置自定义时间(部分参数可选) Future setCustomTime({ int? hour, int? minute, int? second = 0, int? year, int? month, int? day, }) async { final now = DateTime.now(); final targetTime = DateTime( year ?? now.year, month ?? now.month, day ?? now.day, hour ?? now.hour, minute ?? now.minute, second ?? 0, ); debugPrint('设置自定义时间: ${targetTime.toString()}'); return await setDeviceTime(targetTime); } /// 快速设置时间(仅小时和分钟) Future setTime(int hour, int minute) async { return await setCustomTime(hour: hour, minute: minute, second: 0); } /// 设置日期(年、月、日) Future setDate(int year, int month, int day) async { return await setCustomTime(year: year, month: month, day: day); } Future exec({ required ProtocolDoExecFunc func, ProtocolOnStatusFunc? onstatus, int retry = 3, Duration timeout = const Duration(seconds: 3), }) async { for (int i = 0; i < retry; i++) { try { if (i > 0) { //缓一下 await Future.delayed(Duration(milliseconds: 100)); } var re = await func(); if (onstatus == null || re == false) return re; //等待 dynamic status = await statusStream.first.timeout(timeout); bool ok = onstatus(status); if (ok) { return true; } } catch (e) { e; if (onstatus == null) return false; } } return false; } } // BedStatus类保持不变... // 床状态模型 class BedStatus { final bool headUp; final bool headDown; final bool backUp; final bool backDown; final bool legUp; final bool legDown; final bool hipUp; final bool hipDown; final bool massageOn; final int massageLevel; final int massageTimeRemaining; final bool isChildLocked; const BedStatus({ required this.headUp, required this.headDown, required this.backUp, required this.backDown, required this.legUp, required this.legDown, required this.hipUp, required this.hipDown, required this.massageOn, required this.massageLevel, required this.massageTimeRemaining, required this.isChildLocked, }); factory BedStatus.fromBytes(List data) { return BedStatus( headUp: (data[5] & 0x01) != 0, headDown: (data[5] & 0x02) != 0, backUp: (data[6] & 0x01) != 0, backDown: (data[6] & 0x02) != 0, legUp: (data[7] & 0x01) != 0, legDown: (data[7] & 0x02) != 0, hipUp: (data[8] & 0x01) != 0, hipDown: (data[8] & 0x02) != 0, massageOn: (data[9] & 0x0F) != 0, massageLevel: (data[9] >> 4) & 0x0F, massageTimeRemaining: data.length > 10 ? data[10] : 0, isChildLocked: data.length > 11 ? (data[11] & 0x80) != 0 : false, ); } static const defaultStatus = BedStatus( headUp: false, headDown: false, backUp: false, backDown: false, legUp: false, legDown: false, hipUp: false, hipDown: false, massageOn: false, massageLevel: 0, massageTimeRemaining: 0, isChildLocked: false, ); }