Files
tuiche/lib/pages/mh_page/device/component/tool/BedControlService.dart
2025-10-31 15:03:36 +08:00

513 lines
17 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<int> frameHeader = [0xFF, 0xFF, 0xFF, 0xFF, 0x05];
static const int minFrameLength = 11;
final Future<bool> Function(Uint8List) _writeFn;
final StreamController<BedStatus> _statusController =
StreamController.broadcast();
Stream<BedStatus> get statusStream => _statusController.stream;
BedControlService(this._writeFn);
// 校验和计算(根据协议文档硬编码)
static List<int> _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<bool> enableRemoteReport() => _sendCommand(
[0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0x00, 0xF2, 0x56, 0x85]);
// 2. 基础控制指令
Future<bool> stop() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x00));
Future<bool> moveHeadUp() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x01));
Future<bool> moveHeadDown() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x02));
Future<bool> moveBackUp() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x03));
Future<bool> moveBackDown() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x04));
Future<bool> moveLegUp() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x06));
Future<bool> moveLegDown() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x07));
Future<bool> moveHipUp() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x0D));
Future<bool> moveHipDown() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x0E));
Future<bool> resetPosition() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x08));
// 3. 预设模式指令
Future<bool> setMovieMode([int progress = 0]) => progress > 0
? _sendCommand(_buildProgressCommand(progress, 0x0500, 0x05))
: _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x05));
Future<bool> setZeroGravity([int progress = 0]) => progress > 0
? _sendCommand(_buildProgressCommand(progress, 0x0900, 0x09))
: _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x09));
Future<bool> setAntiSnore([int progress = 0]) => progress > 0
? _sendCommand(_buildProgressCommand(progress, 0x0F00, 0x0F))
: _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x0F));
//一键放松
Future<bool> setSleepAssist() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x6A));
// 休闲模式
Future<bool> setXiuxianMode() =>
_sendCommand(_buildCommand(0x00, 0x05, 0x00, 0x47));
// 腰部放松
Future<bool> setYaoRelaxMode() =>
_sendCommand(_buildCommand(0x00, 0x05, 0x00, 0xE6));
// 腿部放松
Future<bool> setLegRelaxMode() =>
_sendCommand(_buildCommand(0x00, 0x05, 0x00, 0xE5));
Future<bool> setReadingMode() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x48));
Future<bool> setRelaxMode() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x49));
Future<bool> setYogaMode() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x4E));
// 4. 记忆功能
Future<bool> saveMemory(int slot) {
assert(slot >= 1 && slot <= 2);
return _sendCommand(
_buildCommand(0x00, 0x00, 0x00, slot == 1 ? 0x0A : 0x0B));
}
// 5. 按摩控制
Future<bool> increaseBackMassage() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x10));
Future<bool> decreaseBackMassage() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x11));
Future<bool> increaseLegMassage() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x12));
Future<bool> decreaseLegMassage() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x13));
Future<bool> increaseMassageFreq() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x14));
Future<bool> decreaseMassageFreq() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x15));
Future<bool> 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<bool> stopMassage() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x1C));
// 6. 灯光控制
Future<bool> toggleLight10Min() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x19));
Future<bool> toggleLight8H() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x1A));
Future<bool> toggleLight10H() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x1B));
// 7. 组合控制
Future<bool> moveBothUp() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x2B));
Future<bool> moveBothDown() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x2C));
// 8. 童锁功能固定11字节
Future<bool> toggleChildLock() => _sendCommand(
[0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0x05, 0x00, 0x0C, 0xC7, 0x04]);
// 背部按摩强度控制
Future<bool> setBackMassageLevel1() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x50));
Future<bool> setBackMassageLevel2() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x51));
Future<bool> setBackMassageLevel3() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x52));
// 腿部按摩强度控制
Future<bool> setLegMassageLevel1() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x54));
Future<bool> setLegMassageLevel2() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x55));
Future<bool> setLegMassageLevel3() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x56));
//按摩时间
Future<bool> setMassageTime10Min() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x16));
Future<bool> setMassageTime20Min() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x17));
Future<bool> setMassageTime30Min() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x18));
Future<bool> setMassageFrequency1() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x57));
Future<bool> setMassageFrequency2() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x58));
Future<bool> setMassageFrequency3() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x59));
Future<bool> setMassageFrequency4() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x5A));
Future<bool> stopLeg() => _sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x53));
Future<bool> stopBack() =>
_sendCommand(_buildCommand(0x00, 0x00, 0x00, 0x4F));
//====== 核心方法 ======//
Future<bool> _sendCommand(List<int> 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<int> 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<bool> setAlarm({
required bool isStart,
required List<String> wakeTime,
required List<String> weeks,
required bool isMassage,
required bool isBell,
}) async {
// 1. 参数校验
if (wakeTime.length != 2 || weeks.length != 7) {
debugPrint('参数错误:唤醒时间或星期数不符'.tr);
return false;
}
// 2. 构造指令(固定头 + 动态数据)
final arr = <int>[
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<int> _calculateChecksum(List<int> data) {
int sum = 0;
for (var byte in data) {
sum += byte;
}
return [sum & 0xFF, (sum >> 8) & 0xFF];
}
void _addSumInEnd(List<int> arr) {
int sum = arr.fold(0, (acc, byte) => acc + byte);
arr.add(sum & 0xFF); // 低字节
arr.add((sum >> 8) & 0xFF); // 高字节
}
Future<bool> setDeviceTime(DateTime dateTime) async {
// 1. 参数校验
// if (dateTime.year < 2000 || dateTime.year > 2099) {
// debugPrint('年份必须在2000-2099之间');
// return false;
// }
DateTime date = DateTime.now();
// 2. 构造基础指令数据(不含校验和)
final arr = <int>[
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<bool> syncDeviceTime() async {
final now = DateTime.now();
debugPrint('开始同步设备时间: ${now.toString()}');
return await setDeviceTime(now);
}
/// 设置自定义时间(部分参数可选)
Future<bool> 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<bool> setTime(int hour, int minute) async {
return await setCustomTime(hour: hour, minute: minute, second: 0);
}
/// 设置日期(年、月、日)
Future<bool> setDate(int year, int month, int day) async {
return await setCustomTime(year: year, month: month, day: day);
}
Future<bool> 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<int> 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,
);
}