修复眠花糖设置闹钟无效

This commit is contained in:
wyf
2025-10-31 15:03:36 +08:00
parent 3c5db3bdf2
commit 58f4b1f0bb
18 changed files with 812 additions and 124 deletions

View File

@@ -2,6 +2,6 @@
# flutter.android.versionName=2.0.5
# flutter.android.versionCode=11
# 智慧眠花糖版本信息
flutter.android.versionName=2.0.8
flutter.android.versionCode=12
# 智慧眠花糖版本信息【最新,未提交商店】
flutter.android.versionName=2.0.9
flutter.android.versionCode=13

View File

@@ -1,3 +1,3 @@
[
"assets/miniapp/mhtControl_1.0.87.zip"
"assets/miniapp/mhtControl_1.0.89.zip"
]

View File

@@ -2,7 +2,7 @@ class ServiceConstant {
// static const String baseHost = "zhmht.swes.com.cn:27021"; //服务地址 眠花糖测试地址
static const String baseHost = "zhmht.swes.com.cn:27020"; //服务地址 眠花糖正式地址
// static const String baseHost = "vsbs-test.he-info.cn"; //服务地址 本地测试地址
// static const String baseHost = "vsbst-api.he-info.cn";//服务地址
// static const String baseHost = "vsbst-api.he-info.com";//服务地址
// static const String service_address = "http://$baseHost";
static const String service_address = "https://$baseHost";

View File

@@ -93,6 +93,7 @@ Future<ApiResponse> requestWithLog({
EasyDartModule.logger.error("$logTitle 失败->$e");
DailyLogUtils.writeError("$logTitle 失败->$e");
onFailure?.call(apiResponse);
apiResponse.msg = e.toString();
return apiResponse;
}
}

View File

@@ -6,8 +6,10 @@ import 'package:EasyDartModule/base/logger/Logger.dart';
import 'package:EasyDartModule/base/websocket/WebSocket.dart';
import 'package:easyweb/utils/appmanger.dart';
import 'package:ef/ef.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:fluwx/fluwx.dart';
import 'package:get_storage/get_storage.dart';
@@ -98,6 +100,8 @@ Future<void> main() async {
await initLanguageSetting();
WidgetsFlutterBinding.ensureInitialized();
initServiceAddress();
ApiService.init();
await GetStorage.init();
// 初始化登录
@@ -111,7 +115,7 @@ Future<void> main() async {
// 检查网络
Checknetwork.checkNetwork();
// 微信开放平台注册
initWX();
await initWX();
// // 初始化 flutter_xupdate android app 更新
// initXUpdate();
@@ -124,6 +128,20 @@ Future<void> main() async {
});
}
//初始化服务器地址
void initServiceAddress() {
if (AppConstants().ent_type == APPPackageType.MHT.code) {
// 眠花糖
ServiceConstant.baseHost = "zhmht.swes.com.cn:27020";
} else if (AppConstants().ent_type == APPPackageType.TH.code) {
// 太和
ServiceConstant.baseHost = "vsbst-api.he-info.com";
} else {
//默认
ServiceConstant.baseHost = "vsbst-api.he-info.com";
}
}
// languageCode 系统默认语言码 en/zh
loadLanguageSetting(String? languageCode) async {
int code = AppConstants().ent_type;
@@ -185,17 +203,46 @@ initLanguageSetting() async {
}
// 初始化微信开放平台注册
// Future<void> initWX() async {
// Fluwx fluwx = Fluwx();
// fluwx.registerApi(
// //请填写自己的微信appid
// // appId: "wxeb2688220799e2c5", //太和
// appId: "wx929c548fea6af9c7", //眠花糖
// doOnAndroid: true,
// doOnIOS: true,
// // universalLink: "https://app.he-info.com/theh/",
// universalLink: "https://zhmht.swes.com.cn/app/",
// );
// }
Future<void> initWX() async {
Fluwx fluwx = Fluwx();
fluwx.registerApi(
//请填写自己的微信appid
appId: "wxeb2688220799e2c5", //太和
// appId: "wx929c548fea6af9c7", //眠花糖
final fluwx = Fluwx();
String appId = "";
String universalLink = "";
if (AppConstants().ent_type == APPPackageType.MHT.code) {
// 眠花糖
appId = "wx929c548fea6af9c7";
universalLink = "https://zhmht.swes.com.cn/app/";
} else if (AppConstants().ent_type == APPPackageType.TH.code) {
// 太和
appId = "wxeb2688220799e2c5";
universalLink = "https://app.he-info.com/theh/";
} else {
ef.log("[initWX] 未匹配到有效的包类型 ent_type=${AppConstants().ent_type}");
return;
}
await fluwx.registerApi(
appId: appId,
doOnAndroid: true,
doOnIOS: true,
universalLink: "https://app.he-info.com/theh/",
// universalLink: "https://zhmht.swes.com.cn/app/",
universalLink: universalLink,
);
ef.log("[initWX] 微信注册完成 -> appId: $appId, link: $universalLink");
}
Timer? _messageTimer;
@@ -628,11 +675,8 @@ messageStatus() async {
void initEasyDartModule() {
try {
EasyDartModule.init(
loggerConfig:
// LoggerConfig(host: ServiceConstant.logService, serviceName: "智慧眠花糖在线2025-10-9"),
LoggerConfig(
host: ServiceConstant.logService,
serviceName: "太和e护在线2025-10-14"),
loggerConfig: LoggerConfig(
host: ServiceConstant.logService, serviceName: "眠花糖在线2025-10-28"),
webSocketConfig:
WebSocketConfig(ServiceConstant.webSocketService, (data) {
// 接收到服务消息

View File

@@ -12,6 +12,7 @@ import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/mh_controller/mh_language_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/enum/APPPackageType.dart';
import 'package:vbvs_app/model/WebSocketMessage.dart';
import 'package:vbvs_app/pages/common/bezier_bottom_navigation_bar.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
@@ -126,27 +127,29 @@ class _HomePageState extends State<MainPageBBottomChange>
final getStorage = GetStorage();
@override
Widget build(BuildContext context) {
Future.delayed(const Duration(milliseconds: 0), () {
String? isShowYingShiDialog = getStorage.read("isShowYingShiDialog");
if (isShowYingShiDialog == null || isShowYingShiDialog != "true") {
String btnName = "同意".tr;
String cancelName = "取消".tr;
if (Platform.isAndroid) {
cancelName = "退出".tr;
}
showCustomConfirmOfWebViewDialog(context, "隐私协议".tr, getPrivacy(1),
btnName: btnName, showCancel: true, cancelName: cancelName)
.then((e) {
if (e == "confirm") {
getStorage.write("isShowYingShiDialog", "true");
} else {
if (cancelName == "退出") {
SystemNavigator.pop();
}
if (AppConstants().ent_type == APPPackageType.MHT.code) {
Future.delayed(const Duration(milliseconds: 0), () {
String? isShowYingShiDialog = getStorage.read("isShowYingShiDialog");
if (isShowYingShiDialog == null || isShowYingShiDialog != "true") {
String btnName = "同意".tr;
String cancelName = "取消".tr;
if (Platform.isAndroid) {
cancelName = "退出".tr;
}
});
}
});
showCustomConfirmOfWebViewDialog(context, "隐私协议".tr, getPrivacy(1),
btnName: btnName, showCancel: true, cancelName: cancelName)
.then((e) {
if (e == "confirm") {
getStorage.write("isShowYingShiDialog", "true");
} else {
if (cancelName == "退出") {
SystemNavigator.pop();
}
}
});
}
});
}
return Obx(() {
final currentLanguage =
languageController.selectLanguage.value; // 监听此变量变化

View File

@@ -269,7 +269,6 @@
// }
// }
import 'dart:async';
import 'dart:io';
@@ -286,6 +285,7 @@ import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart';
import 'package:vbvs_app/controller/message/message_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/enum/APPPackageType.dart';
import 'package:vbvs_app/enum/LoginStatus.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
import 'package:vbvs_app/pages/main_bottom/e_page.dart';
@@ -315,12 +315,10 @@ class MainPageBottomChange extends GetView<MainPageController> {
bottomItems = [
getBottomNavigationBarItem("assets/img/menu/home.svg",
"assets/img/menu/n_home.svg", "菜单.首页".tr),
getBottomNavigationBarItem("assets/img/menu/e.svg",
"assets/img/menu/n_e.svg", "菜单.小e".tr),
getBottomNavigationBarItem(
"assets/img/menu/message.svg",
"assets/img/menu/n_message.svg",
"菜单.消息".tr,
"assets/img/menu/e.svg", "assets/img/menu/n_e.svg", "菜单.小e".tr),
getBottomNavigationBarItem("assets/img/menu/message.svg",
"assets/img/menu/n_message.svg", "菜单.消息".tr,
showBadge: (messageController.model.body_message_read == 1 ||
messageController.model.system_message_read == 1)),
getBottomNavigationBarItem("assets/img/menu/mine.svg",
@@ -335,10 +333,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
bottomItems = [
getBottomNavigationBarItem("assets/img/menu/home.svg",
"assets/img/menu/n_home.svg", "菜单.首页".tr),
getBottomNavigationBarItem(
"assets/img/menu/message.svg",
"assets/img/menu/n_message.svg",
"菜单.消息".tr,
getBottomNavigationBarItem("assets/img/menu/message.svg",
"assets/img/menu/n_message.svg", "菜单.消息".tr,
showBadge: (messageController.model.body_message_read == 1 ||
messageController.model.system_message_read == 1)),
getBottomNavigationBarItem("assets/img/menu/mine.svg",
@@ -391,24 +387,26 @@ class MainPageBottomChange extends GetView<MainPageController> {
@override
Widget build(BuildContext context) {
Future.delayed(const Duration(milliseconds: 0), () {
String? isShowYingShiDialog = getStorage.read("isShowYingShiDialog");
if (isShowYingShiDialog == null || isShowYingShiDialog != "true") {
String btnName = "同意".tr;
String cancelName = "取消".tr;
if (Platform.isAndroid) cancelName = "退出".tr;
if (AppConstants().ent_type == APPPackageType.TH.code) {
Future.delayed(const Duration(milliseconds: 0), () {
String? isShowYingShiDialog = getStorage.read("isShowYingShiDialog");
if (isShowYingShiDialog == null || isShowYingShiDialog != "true") {
String btnName = "同意".tr;
String cancelName = "取消".tr;
if (Platform.isAndroid) cancelName = "退出".tr;
showCustomConfirmOfWebViewDialog(context, "隐私协议".tr, getPrivacy(1),
btnName: btnName, showCancel: true, cancelName: cancelName)
.then((e) {
if (e == "confirm") {
getStorage.write("isShowYingShiDialog", "true");
} else {
if (cancelName == "退出") SystemNavigator.pop();
}
});
}
});
showCustomConfirmOfWebViewDialog(context, "隐私协议".tr, getPrivacy(1),
btnName: btnName, showCancel: true, cancelName: cancelName)
.then((e) {
if (e == "confirm") {
getStorage.write("isShowYingShiDialog", "true");
} else {
if (cancelName == "退出") SystemNavigator.pop();
}
});
}
});
}
return PopScope(
canPop: false,
@@ -436,7 +434,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
return Scaffold(
body: IndexedStack(
index: currentIndex,
children: arr.map((page) => SizedBox.expand(child: page)).toList(),
children:
arr.map((page) => SizedBox.expand(child: page)).toList(),
),
);
} else {
@@ -451,7 +450,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
backgroundColor: Colors.transparent,
body: IndexedStack(
index: currentIndex,
children: arr.map((page) => SizedBox.expand(child: page)).toList(),
children:
arr.map((page) => SizedBox.expand(child: page)).toList(),
),
bottomNavigationBar: Theme(
data: Theme.of(context).copyWith(
@@ -497,7 +497,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
Get.currentRoute == '/messagePage') {
Get.back();
}
Future.delayed(const Duration(milliseconds: 100), () {
Future.delayed(const Duration(milliseconds: 100),
() {
Get.toNamed("/otherLoginPage");
});
});
@@ -505,7 +506,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
}
if (controller.model.currentIndex != index) {
globalController.model.hideBottomNavigationBar = false;
globalController.model.hideBottomNavigationBar =
false;
globalController.updateAll();
}
@@ -527,7 +529,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
Future<bool> _handleBackPressed(BuildContext context) async {
final currentTime = DateTime.now();
if (_lastBackPressedTime == null ||
currentTime.difference(_lastBackPressedTime!) > const Duration(seconds: 2)) {
currentTime.difference(_lastBackPressedTime!) >
const Duration(seconds: 2)) {
_lastBackPressedTime = currentTime;
TopSlideNotification.show(context, text: "滑动退出提醒".tr);
return false;

View File

@@ -106,53 +106,53 @@ class _VitalSignsSensorState extends State<VitalSignsSensorPage> {
left: 0.rpx,
child: returnIconButtomNew(),
),
// Positioned(
// right: 0.rpx,
// child: ClickableContainer(
// backgroundColor: Colors.transparent,
// highlightColor: Colors.transparent,
// padding: EdgeInsets.symmetric(
// horizontal: 30.rpx, vertical: 30.rpx),
// onTap: () {
// // TopSlideNotification.show(context);
// //检测蓝牙是否开启,没有开启提示开启蓝牙
// //如果已经开启则进行x秒的蓝牙扫描
// BluetoothHelper.listenBluetoothState((isOn) {
// mhtBlueToothController.model.bluetooth = isOn;
// mhtBlueToothController.updateAll();
// if (!isOn) {
// mhtBlueToothController.model.blueRawData = [];
// mhtBlueToothController
// .model.deviceDataStatus = [];
// mhtBlueToothController.updateAll();
// _showBluetoothNotEnabledDialog();
// }
// });
// _checkBluetoothPermission();
// },
// child: Obx(() {
// return SizedBox(
// width: 34.rpx,
// height: mhtBlueToothController.isScanning.value
// ? 34.rpx
// : 24.rpx,
// child: mhtBlueToothController.isScanning.value
// ? CircularProgressIndicator(
// strokeWidth: 2,
// valueColor:
// AlwaysStoppedAnimation<Color>(
// themeController.currentColor.sc3,
// ),
// )
// : SvgPicture.asset(
// 'assets/img/icon/upgrade.svg',
// fit: BoxFit.cover,
// color: Colors.white,
// ),
// );
// }),
// ),
// ),
Positioned(
right: 0.rpx,
child: ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.symmetric(
horizontal: 30.rpx, vertical: 30.rpx),
onTap: () {
// TopSlideNotification.show(context);
//检测蓝牙是否开启,没有开启提示开启蓝牙
//如果已经开启则进行x秒的蓝牙扫描
BluetoothHelper.listenBluetoothState((isOn) {
mhtBlueToothController.model.bluetooth = isOn;
mhtBlueToothController.updateAll();
if (!isOn) {
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController
.model.deviceDataStatus = [];
mhtBlueToothController.updateAll();
_showBluetoothNotEnabledDialog();
}
});
_checkBluetoothPermission();
},
child: Obx(() {
return SizedBox(
width: 34.rpx,
height: mhtBlueToothController.isScanning.value
? 34.rpx
: 24.rpx,
child: mhtBlueToothController.isScanning.value
? CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
themeController.currentColor.sc3,
),
)
: SvgPicture.asset(
'assets/img/icon/upgrade.svg',
fit: BoxFit.cover,
color: Colors.white,
),
);
}),
),
),
],
),
),

View File

@@ -5,6 +5,7 @@ import 'package:easydevice/easydevice.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:mhtctrl/mhtctrl.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
@@ -18,6 +19,8 @@ import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/model/BleDeviceData.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/component/tool/BedControlService.dart';
import 'package:vbvs_app/pages/mh_page/device/component/tool/DeviceType.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart';
@@ -42,6 +45,8 @@ class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
MHTBlueToothController blueteethBindController = Get.find();
MHTHomeController homeController = Get.find();
var lisObj;
late MattressControlService service;
late BedControlService bedService;
@override
Widget build(BuildContext context) {
@@ -232,6 +237,8 @@ class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
TopSlideNotification.show(context,
text: response.msg!);
if (response.code == HttpStatusCodes.ok) {
//关闭闹钟和打鼾干预
// resetLastDeviceConfig(widget.bleDevice);
homeController.getPersonList();
//请求绑定设备列表
// homeController.getSleepReport();
@@ -761,6 +768,49 @@ class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
throw Exception("获取MAC超时".tr);
}
//重置设备闹钟数据和打鼾干预
// void resetLastDeviceConfig(BlueToothDataModel bleDevice) {
// int deviceType = bleDevice.type;
// if (deviceType == 2) {
// //床垫
// service = MattressControlService((pkg) async {
// var bytes = pkg.toBytes();
// String serviceCode = "";
// if (bleDevice != null) {
// if (deviceType == DeviceType.smartBed.value) {
// serviceCode = "ffe0/ffe1";
// }
// if (deviceType == DeviceType.smartMattress.value) {
// serviceCode = "fff0/fff2";
// }
// }
// if (bytes[7] != 0x00) {
// //打印bytes
// String hex = bytes.toString();
// print("--------->" + hex);
// String hexString = bytes
// .map((e) => e.toRadixString(16).padLeft(2, '0'))
// .join(' ')
// .toUpperCase();
// print("--------->hex->" + hexString);
// }
// int aa = await blueteethBindController.currentDevice!
// .write(bleDevice.mac, serviceCode, bytes, withresponse: true);
// ef.log(bytes.toString());
// return aa == 0 ? true : false;
// });
// } else if (deviceType == 3) {
// //床
// bedService = BedControlService(_writeToDevice); // 初始化
// }
// }
// Future<bool> _writeToDevice(Uint8List pkg) async {
// int result = await bleToolController.ble
// .write(macAddress.value, "ffe0/ffe1", pkg, withresponse: true);
// return result == 0;
// }
}
String parseMacFromBleResponse(List<int> data) {

View File

@@ -0,0 +1,512 @@
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,
);
}

View File

@@ -0,0 +1,40 @@
import 'package:ef/ef.dart';
enum DeviceType {
smartBed, // 2: 智能床
smartMattress // 3: 智能床垫
}
extension DeviceTypeExtension on DeviceType {
/// 获取整型值
int get value {
switch (this) {
case DeviceType.smartBed:
return 2;
case DeviceType.smartMattress:
return 3;
}
}
/// 根据整型值解析为枚举
static DeviceType? fromInt(int? type) {
switch (type) {
case 2:
return DeviceType.smartBed;
case 3:
return DeviceType.smartMattress;
default:
return null;
}
}
String get description {
switch (this) {
case DeviceType.smartBed:
return "智能电动床".tr;
case DeviceType.smartMattress:
return "律动智能床垫".tr;
}
}
}

View File

@@ -74,9 +74,9 @@ class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
RxBool isScanning = false.obs;
RxMap<String, Map> localUpgradeMac = <String, Map>{}.obs; //mac 进度
String? currentUpgradeVersion;//最新版本号
String? currentUpgradeName;//最新固件名
String? currentUpgradeUrl;//最新固件名
String? currentUpgradeVersion; //最新版本号
String? currentUpgradeName; //最新固件名
String? currentUpgradeUrl; //最新固件名
void startStatusPolling() {
updateDeviceStatus().then((res) {
@@ -264,7 +264,8 @@ class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
return ApiResponse(code: -1, msg: "未知错误".tr);
}
saveHabitData(sleepData) async {
Future<bool> saveHabitData(sleepData) async {
bool resFlag = false;
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
@@ -281,7 +282,14 @@ class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: data,
onSuccess: (res) {
resFlag = true;
},
onFailure: (res) {
resFlag = false;
},
);
return resFlag;
}
Future<Map> loadHabitDataApi(String mac, {int time = 3}) async {

View File

@@ -24,6 +24,7 @@ import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/BleDeviceData.dart';
import 'package:vbvs_app/pages/device_bind/blueteeth_device_page.dart';
import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:EasyDartModule/EasyDartModule.dart' as edm;
@@ -39,6 +40,7 @@ class _MHTWifiPageState extends State<MHTWifiPage> {
GlobalController globalController = Get.find();
UserInfoController userInfoController = Get.find();
BlueteethBindController blueteethBindController = Get.find();
MHTBlueToothController mhtBlueToothController = Get.find();
PersonController personController = Get.find();
ThemeController themeController = Get.find();
var lisObj;
@@ -226,6 +228,7 @@ class _MHTWifiPageState extends State<MHTWifiPage> {
onBack: () {
// Get.offAllNamed('/mHTBlueteethDevicePage',
// arguments: widget.deviceInfo.type);
blueteethBindController.currentDevice?.disconnect();
Get.back();
},
),
@@ -928,6 +931,14 @@ class _MHTWifiPageState extends State<MHTWifiPage> {
}
} else {
wifiStatus = false;
if (needSuccess) {
haveSuccess = true;
TopSlideNotification.show(
context,
text: "配网失败".tr,
textColor: themeController.currentColor.sc9,
);
}
}
blueteethBindController.wifiStatus.value = wifiStatus ? 1 : 0;
} catch (e) {
@@ -1348,6 +1359,9 @@ class _MHTWifiPageState extends State<MHTWifiPage> {
await blueteethBindController.currentDevice!.disconnect();
// blueteethBindController.currentDevice = null;
}
if (mhtBlueToothController.currentDevice != null) {
await mhtBlueToothController.currentDevice!.disconnect();
}
DailyLogUtils.writeLog("关闭蓝牙连接成功".tr);
} catch (e) {
DailyLogUtils.writeError("关闭蓝牙连接失败: $e");

View File

@@ -891,6 +891,14 @@ class _MHTWifiAfterPageState extends State<MHTWifiAfterPage> {
}
} else {
wifiStatus = false;
if (needSuccess) {
haveSuccess = true;
TopSlideNotification.show(
context,
text: "配网失败".tr,
textColor: themeController.currentColor.sc9,
);
}
}
blueteethBindController.wifiStatus.value = wifiStatus ? 1 : 0;
} catch (e) {

View File

@@ -87,6 +87,11 @@ class _NewHomePageState extends State<NewHomePage> {
if (AppConstants().ent_type != APPPackageType.MHT.code) {
return;
}
if (userInfoController.model.login! == null ||
userInfoController.model.login! == 0) {
//未登录
return;
}
showTipUpgradeDialog(
context,
Column(

View File

@@ -253,7 +253,7 @@ class _SettingPageState extends State<SettingPage> {
),
].divide(SizedBox(width: 22.rpx)),
),
Text('SWES2025.10.28',
Text('SWES2025.10.31',
style: TextStyle(
color: Colors.white,
fontSize: 26.rpx,

View File

@@ -58,7 +58,7 @@ class WebviewTestController extends GetControllerEx<WebviewTestModel> {
},
onLoadResource: (controller, resource) {
// sourceTime = web.requestTimestamp;
// ef.log("[请求资源时间]${web.requestTimestamp}");`
// ef.log("[请求资源时间]${web.requestTimestamp}");
// _startMonitoringIfNeeded();
},
),
@@ -216,7 +216,7 @@ class WebviewTestController extends GetControllerEx<WebviewTestModel> {
ef.log('更新睡眠习惯: $args[0]');
try {
MHTBlueToothController blueToothController = Get.find();
await blueToothController.saveHabitData(args[0]);
return await blueToothController.saveHabitData(args[0]);
} catch (e) {
ef.log("[更新睡眠习惯失败]:$e");
}