803 lines
30 KiB
Dart
803 lines
30 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:EasyDartModule/EasyDartModule.dart' as edm;
|
||
import 'package:easydevice/easydevice.dart';
|
||
import 'package:ef/ef.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutterflow_ui/flutterflow_ui.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';
|
||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||
import 'package:vbvs_app/common/util/requestWithLog.dart';
|
||
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
|
||
import 'package:vbvs_app/component/tool/CustomCard.dart';
|
||
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
|
||
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/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';
|
||
import 'package:vbvs_app/pages/mh_page/test/WebviewTestModel.dart';
|
||
|
||
class DeviceComponentWidget extends StatefulWidget {
|
||
BlueToothDataModel bleDevice;
|
||
var deviceType;
|
||
|
||
DeviceComponentWidget({
|
||
super.key,
|
||
required this.bleDevice,
|
||
required this.deviceType,
|
||
});
|
||
|
||
@override
|
||
State<DeviceComponentWidget> createState() => _DeviceComponentWidgetState();
|
||
}
|
||
|
||
class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
|
||
ThemeController themeController = Get.find();
|
||
MHTBlueToothController blueteethBindController = Get.find();
|
||
MHTHomeController homeController = Get.find();
|
||
var lisObj;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
Map device = {
|
||
"name": widget.bleDevice.name,
|
||
"mac".tr: widget.bleDevice.mac,
|
||
"rssi": widget.bleDevice.scanResult.rssi,
|
||
"bind": widget.bleDevice.bind,
|
||
};
|
||
return ClickableContainer(
|
||
backgroundColor: Colors.white,
|
||
highlightColor: Colors.white,
|
||
borderRadius: 20.rpx,
|
||
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 36.rpx, 55.rpx, 36.rpx),
|
||
onTap: () async {},
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
mainAxisSize: MainAxisSize.max,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Text(
|
||
"${device['name']}",
|
||
style: TextStyle(
|
||
color: stringToColor("#333333"), fontSize: 30.rpx),
|
||
),
|
||
SizedBox(
|
||
width: 10.rpx,
|
||
),
|
||
Obx(() {
|
||
if (blueteethBindController.currentDeviceMac.value ==
|
||
device['mac'.tr]) {
|
||
return SizedBox(
|
||
width: 24.rpx,
|
||
height: 24.rpx,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(
|
||
stringToColor("#929699"),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
return Container();
|
||
}),
|
||
],
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Expanded(
|
||
child: Column(
|
||
children: [
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Container(
|
||
width: MediaQuery.sizeOf(context).width * 0.14,
|
||
constraints: BoxConstraints(minWidth: 106.rpx),
|
||
child: Text(
|
||
"MAC".tr,
|
||
style: TextStyle(
|
||
color: stringToColor("#929699"),
|
||
fontSize: 26.rpx,
|
||
),
|
||
maxLines: 2,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
Text(
|
||
"${device['mac']}",
|
||
style: TextStyle(
|
||
fontSize: 26.rpx,
|
||
color: stringToColor("#333333")),
|
||
),
|
||
].divide(SizedBox(width: 33.rpx)),
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Container(
|
||
width: MediaQuery.sizeOf(context).width * 0.14,
|
||
constraints: BoxConstraints(minWidth: 106.rpx),
|
||
child: Text(
|
||
"信号强度".tr,
|
||
style: TextStyle(
|
||
color: stringToColor("#929699"),
|
||
fontSize: 26.rpx,
|
||
),
|
||
maxLines: 2,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
Text(
|
||
"${device['rssi']}" + "dBm".tr,
|
||
style: TextStyle(
|
||
fontSize: 26.rpx,
|
||
color: stringToColor("#333333")),
|
||
),
|
||
].divide(SizedBox(width: 33.rpx)),
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Container(
|
||
width: MediaQuery.sizeOf(context).width * 0.14,
|
||
constraints: BoxConstraints(minWidth: 106.rpx),
|
||
child: Text(
|
||
"设备状态".tr,
|
||
style: TextStyle(
|
||
color: stringToColor("#929699"),
|
||
fontSize: 26.rpx,
|
||
),
|
||
maxLines: 2,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
Text(
|
||
"${device['bind'] == true ? "已被绑定" : "可绑定"}",
|
||
style: TextStyle(
|
||
fontSize: 26.rpx,
|
||
color: device['bind'] == true
|
||
? stringToColor("#FF7159")
|
||
: stringToColor("#6BFDAC"),
|
||
),
|
||
),
|
||
].divide(SizedBox(width: 33.rpx)),
|
||
),
|
||
].divide(SizedBox(height: 37.rpx)),
|
||
),
|
||
),
|
||
CustomCard(
|
||
borderRadius: 16.rpx,
|
||
onTap: () async {
|
||
try {
|
||
//连接前暂停扫描
|
||
blueteethBindController.pauseScanning();
|
||
if (blueteethBindController.currentDeviceMac?.value !=
|
||
null &&
|
||
blueteethBindController
|
||
.currentDeviceMac!.value.isNotEmpty) {
|
||
if (blueteethBindController.currentDeviceMac?.value !=
|
||
device['mac'.tr]) {
|
||
showConfirmDialog(
|
||
context, Container(), "其他设备正在绑定中,是否终止其他设备绑定?".tr,
|
||
onConfirm: () {
|
||
blueteethBindController.currentDeviceMac.value = "";
|
||
blueteethBindController.updateAll();
|
||
}, onCancel: () {
|
||
blueteethBindController.resumeScanning();
|
||
});
|
||
}
|
||
}
|
||
|
||
blueteethBindController.currentDeviceMac?.value =
|
||
device['mac'.tr]!;
|
||
blueteethBindController.updateAll();
|
||
|
||
if (device['bind'] == true) {
|
||
await showHaveBindDialog(context);
|
||
blueteethBindController.currentDeviceMac.value = "";
|
||
blueteethBindController.resumeScanning();
|
||
} else {
|
||
showConfirmDialog(
|
||
context,
|
||
Container(),
|
||
'是否确认绑定?'.tr,
|
||
onConfirm: () async {
|
||
//连接蓝牙 ,获取设备信息
|
||
blueteethBindController.currentDeviceMac.value =
|
||
widget.bleDevice.mac;
|
||
blueteethBindController.updateAll();
|
||
String mac = await getBindTHMAC(
|
||
context, widget.bleDevice, widget.deviceType);
|
||
if (mac != null && mac.isNotEmpty) {
|
||
bool flag =
|
||
await fillTHMac(mac, widget.bleDevice, context);
|
||
if (!flag) {
|
||
return;
|
||
}
|
||
}
|
||
blueteethBindController.currentFullDevice =
|
||
widget.bleDevice;
|
||
ApiResponse response = await blueteethBindController
|
||
.bindDeviceAndMAC(widget.bleDevice, context);
|
||
TopSlideNotification.show(context,
|
||
text: response.msg!);
|
||
if (response.code == HttpStatusCodes.ok) {
|
||
homeController.getPersonList();
|
||
//请求绑定设备列表
|
||
// homeController.getSleepReport();
|
||
homeController.getDeviceNum().then((apiResponse) {
|
||
if (apiResponse.code != HttpStatusCodes.ok) {
|
||
TopSlideNotification.show(
|
||
Get.context!,
|
||
text: apiResponse.msg!,
|
||
textColor: themeController.currentColor.sc9,
|
||
);
|
||
}
|
||
});
|
||
homeController
|
||
.getDeviceList(group: 'room')
|
||
.then((apiResponse) {
|
||
if (apiResponse.code != HttpStatusCodes.ok) {
|
||
try {
|
||
WebviewTestController webviewTestController =
|
||
Get.find();
|
||
webviewTestController.web.jsbridge?.dart
|
||
.alterDevice();
|
||
} catch (e) {
|
||
ef.log("[h5]通知列表更新报错:$e");
|
||
}
|
||
TopSlideNotification.show(
|
||
Get.context!,
|
||
text: apiResponse.msg!,
|
||
textColor: themeController.currentColor.sc9,
|
||
);
|
||
} else {
|
||
//请求睡眠报告
|
||
// deviceController.getSleepReport();
|
||
}
|
||
});
|
||
//更新设备绑定流程
|
||
Get.toNamed("/mHTwifiPage",
|
||
arguments: widget.bleDevice);
|
||
THapp bledevice = THapp(
|
||
device: widget.bleDevice.scanResult.device);
|
||
blueteethBindController.currentDevice = bledevice;
|
||
blueteethBindController.currentDeviceMac.value = "";
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
if (homeController
|
||
.homeSleepDays.value.isNotEmpty) {
|
||
homeController.selectedDayIndex.value =
|
||
homeController.homeSleepDays.value.length -
|
||
1;
|
||
}
|
||
});
|
||
await homeController.getPersonList();
|
||
} else {
|
||
blueteethBindController.resumeScanning();
|
||
blueteethBindController.currentDeviceMac.value = "";
|
||
blueteethBindController.updateAll();
|
||
TopSlideNotification.show(
|
||
context,
|
||
text: response.msg ?? "蓝牙绑定.连接异常".tr,
|
||
textColor: themeController.currentColor.sc9,
|
||
);
|
||
}
|
||
blueteethBindController.resumeScanning();
|
||
},
|
||
onCancel: () {
|
||
print('用户点击了取消'.tr);
|
||
blueteethBindController.currentDeviceMac.value = "";
|
||
blueteethBindController.resumeScanning();
|
||
blueteethBindController.updateAll();
|
||
},
|
||
);
|
||
}
|
||
} catch (e) {
|
||
Navigator.pop(context);
|
||
TopSlideNotification.show(
|
||
context,
|
||
text: "连接异常".tr,
|
||
textColor: themeController.currentColor.sc9,
|
||
);
|
||
edm.EasyDartModule.logger.info("连接异常: $e");
|
||
DailyLogUtils.writeLog("连接异常: $e");
|
||
} finally {
|
||
// 确保在任何情况下都恢复扫描
|
||
// if (blueteethBindController
|
||
// .currentDeviceMac.value.isEmpty) {
|
||
// blueteethBindController.resumeScanning();
|
||
// }
|
||
blueteethBindController.resumeScanning();
|
||
}
|
||
},
|
||
colors: [stringToColor("1592AA"), stringToColor("006FA3")],
|
||
child: Container(
|
||
width: 150.rpx,
|
||
height: 90.rpx,
|
||
alignment: Alignment.center,
|
||
child: Text(
|
||
"添加".tr,
|
||
style: TextStyle(color: Colors.white, fontSize: 26.rpx),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
].divide(SizedBox(height: 37.rpx)),
|
||
),
|
||
);
|
||
}
|
||
|
||
//更新设备绑定状态
|
||
void updateDeviceBindStatus(BleDeviceData device) {
|
||
String serviceAddress = ServiceConstant.service_address;
|
||
String serviceName = ServiceConstant.server_service;
|
||
String serviceApi = ServiceConstant.user_setting;
|
||
String mac = device.mac!;
|
||
String type = "device_bind_status_${mac}";
|
||
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
|
||
Map<String, dynamic> data = {
|
||
"type": type,
|
||
"mac".tr: mac,
|
||
"wifi": false,
|
||
"celibration": false,
|
||
"person_info": false,
|
||
"time": DateTime.now().millisecondsSinceEpoch,
|
||
};
|
||
requestWithLog(
|
||
logTitle: "更新用户绑定流程".tr,
|
||
method: MyHttpMethod.put,
|
||
queryUrl: queryUrl,
|
||
data: data,
|
||
onSuccess: (res) {},
|
||
onFailure: (res) {},
|
||
);
|
||
}
|
||
|
||
Widget _buildDeviceInfoSection(device, BuildContext context) {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Padding(
|
||
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 30.rpx, 0.rpx),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
device.name ?? '蓝牙绑定.默认设备名称'.tr,
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFF6FAFD),
|
||
fontSize: 30.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
Obx(() {
|
||
if (blueteethBindController.currentDeviceMac.value ==
|
||
device.mac) {
|
||
return SizedBox(
|
||
width: 24.rpx,
|
||
height: 24.rpx,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(
|
||
themeController.currentColor.sc1,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
return Container();
|
||
}),
|
||
],
|
||
),
|
||
),
|
||
Row(
|
||
children: [
|
||
Text(
|
||
"蓝牙绑定.信号强度".tr + ':${device.rssi ?? '-'}dBm',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFEBF2F8),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
SizedBox(width: 40.rpx),
|
||
Text(
|
||
"蓝牙绑定.SN".tr + ':${device.sn ?? '-'}',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFF5F9FD),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
Text(
|
||
"蓝牙绑定.蓝牙地址".tr + ':${device.deviceId ?? '-'}',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFF6FAFD),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
Text(
|
||
"蓝牙绑定.mac".tr + ':${device.mac ?? '-'}',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFF6FAFD),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
Row(
|
||
children: [
|
||
Text(
|
||
"蓝牙绑定.网络".tr +
|
||
':${device.isOnline == true ? '蓝牙绑定.在线'.tr : '蓝牙绑定.离线'.tr}',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFEBF2F8),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
SizedBox(width: 145.rpx),
|
||
Row(
|
||
children: [
|
||
Text(
|
||
"蓝牙绑定.传感器".tr + ":",
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFEBF2F8),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
Text(
|
||
device.bind == false ? '蓝牙绑定.可绑定'.tr : '蓝牙绑定.已被绑定'.tr,
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: device.bind == false
|
||
? const Color(0xFF1AD2B5)
|
||
: themeController.currentColor.sc9,
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
Text(
|
||
"版本".tr + '${device.version ?? '-'}',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: const Color(0xFFF6FAFD),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
].divide(SizedBox(
|
||
height: 37.rpx,
|
||
)),
|
||
);
|
||
}
|
||
|
||
//获取智能床/床垫mac
|
||
Future<String> getBindTHMAC(
|
||
BuildContext context, BlueToothDataModel device, Map deviceType) async {
|
||
const int maxRetries = 2;
|
||
const Duration timeout = Duration(seconds: 5);
|
||
String? macAddress;
|
||
try {
|
||
// 连接设备
|
||
THapp bledevice = THapp(device: device.scanResult.device);
|
||
await bledevice.connect();
|
||
var res2 = bledevice.isConnected;
|
||
if (!res2) {
|
||
edm.EasyDartModule.logger.error("蓝牙连接失败".tr);
|
||
DailyLogUtils.printLog("蓝牙连接失败".tr);
|
||
TopSlideNotification.show(
|
||
context,
|
||
text: "蓝牙连接失败".tr,
|
||
textColor: themeController.currentColor.sc9,
|
||
);
|
||
throw Exception("蓝牙连接失败".tr);
|
||
}
|
||
blueteethBindController.blueConnectFlag.value = 2;
|
||
blueteethBindController.currentDevice = bledevice;
|
||
await Future.delayed(Duration(seconds: 2));
|
||
|
||
if (deviceType['type'] == 3) {
|
||
//智能床垫
|
||
macAddress = await getMacFromType3(bledevice, timeout);
|
||
} else if (deviceType['type'] == 2) {
|
||
//智能床
|
||
macAddress = await getMacFromType2(bledevice, timeout);
|
||
} else {
|
||
throw Exception("不支持的设备类型".tr);
|
||
}
|
||
|
||
if (macAddress == null) {
|
||
throw Exception("未能获取到MAC地址".tr);
|
||
}
|
||
|
||
// device.macA = macAddress;
|
||
print('MAC地址: $macAddress');
|
||
return macAddress;
|
||
} catch (e) {
|
||
blueteethBindController.currentDeviceMac.value = "";
|
||
edm.EasyDartModule.logger.error("蓝牙获取MAC失败:$e");
|
||
DailyLogUtils.printLog("蓝牙获取MAC失败:$e");
|
||
TopSlideNotification.show(
|
||
context,
|
||
// text: e.message ?? "设备连接失败,请重试".tr,
|
||
text: "获取不到传感器mac,请重试".tr,
|
||
textColor: themeController.currentColor.sc9,
|
||
);
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
// Future<String> getBindTHMAC(
|
||
// BuildContext context, BlueToothDataModel device, Map deviceType) async {
|
||
// const int maxRetries = 2; // 重试2次
|
||
// const Duration timeout = Duration(seconds: 5);
|
||
// String? macAddress;
|
||
|
||
// for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||
// try {
|
||
// // 连接设备
|
||
// THapp bledevice = THapp(device: device.scanResult.device);
|
||
// await bledevice.connect();
|
||
// var res2 = bledevice.isConnected;
|
||
|
||
// if (!res2) {
|
||
// edm.EasyDartModule.logger.error("蓝牙连接失败".tr);
|
||
// DailyLogUtils.printLog("蓝牙连接失败".tr);
|
||
// TopSlideNotification.show(
|
||
// context,
|
||
// text: "蓝牙连接失败".tr,
|
||
// textColor: themeController.currentColor.sc9,
|
||
// );
|
||
// throw Exception("蓝牙连接失败".tr);
|
||
// }
|
||
|
||
// blueteethBindController.blueConnectFlag.value = 2;
|
||
// blueteethBindController.currentDevice = bledevice;
|
||
// await Future.delayed(Duration(seconds: 2));
|
||
|
||
// // 根据设备类型获取 MAC
|
||
// if (deviceType['type'] == 3) {
|
||
// macAddress = await getMacFromType3(bledevice, timeout);
|
||
// } else if (deviceType['type'] == 2) {
|
||
// macAddress = await getMacFromType2(bledevice, timeout);
|
||
// } else {
|
||
// throw Exception("不支持的设备类型".tr);
|
||
// }
|
||
|
||
// if (macAddress != null) {
|
||
// print('MAC地址: $macAddress');
|
||
// return macAddress; // 成功获取直接返回
|
||
// }
|
||
|
||
// // 当前尝试失败,准备重试
|
||
// DailyLogUtils.printLog("第$attempt次尝试未获取到MAC地址");
|
||
|
||
// if (attempt < maxRetries) {
|
||
// await Future.delayed(Duration(seconds: 1)); // 等待后重试
|
||
// }
|
||
// } catch (e) {
|
||
// DailyLogUtils.printLog("第$attempt次蓝牙获取MAC失败: $e");
|
||
// edm.EasyDartModule.logger.error("第$attempt次蓝牙获取MAC失败: $e");
|
||
|
||
// // 最后一次尝试失败后才提示
|
||
// if (attempt == maxRetries) {
|
||
// blueteethBindController.currentDeviceMac.value = "";
|
||
// TopSlideNotification.show(
|
||
// context,
|
||
// text: "获取不到传感器mac,请重试".tr,
|
||
// textColor: themeController.currentColor.sc9,
|
||
// );
|
||
// rethrow; // 抛出最后的异常
|
||
// }
|
||
|
||
// // 否则继续尝试
|
||
// }
|
||
// }
|
||
|
||
// // 正常不会到这里
|
||
// throw Exception("未知错误");
|
||
// }
|
||
|
||
fillTHMac(
|
||
String mac, BlueToothDataModel bleDevice, BuildContext context) async {
|
||
bool flag = false;
|
||
String serviceAddress = ServiceConstant.service_address;
|
||
String serviceName = ServiceConstant.server_service;
|
||
String serviceApi = ServiceConstant.get_bluetooth_device_status;
|
||
String queryUrl = "$serviceAddress$serviceName$serviceApi";
|
||
String macParam =
|
||
"mac=${Uri.encodeQueryComponent(mac.replaceAll(':', ''))}";
|
||
if (queryUrl.contains('?')) {
|
||
queryUrl += '&$macParam';
|
||
} else {
|
||
queryUrl += '?$macParam';
|
||
}
|
||
|
||
await requestWithLog(
|
||
logTitle: "获取设备状态".tr,
|
||
method: MyHttpMethod.get,
|
||
queryUrl: queryUrl,
|
||
onSuccess: (res) {
|
||
flag = true;
|
||
if (res.code != HttpStatusCodes.ok) return;
|
||
|
||
if (res.data != null && res.data is List) {
|
||
List<dynamic> responseList = res.data;
|
||
|
||
// 查找当前MAC对应的数据
|
||
String macKey = mac.replaceAll(':', '').toUpperCase();
|
||
for (var item in responseList) {
|
||
if (item['mac'.tr].toString().toUpperCase() == macKey) {
|
||
// 更新 bleDevice 的状态
|
||
//如果传感器已经绑定 暂时不处理
|
||
// bleDevice.bind = item['bind'] ?? bleDevice.bind;
|
||
bleDevice.macA = item['mac'.tr];
|
||
if (item['bindMac'] != null &&
|
||
item['bindMac'].toString().isNotEmpty) {
|
||
bleDevice.macB = item['bindMac'];
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
onFailure: (res) {
|
||
flag = false;
|
||
TopSlideNotification.show(context,
|
||
text: "获取设备状态失败".tr, textColor: themeController.currentColor.sc9);
|
||
},
|
||
);
|
||
return flag;
|
||
}
|
||
|
||
Future<String> getMacFromType3(THapp bledevice, Duration timeout) async {
|
||
final read = bledevice.getresource('fff0/fff1');
|
||
await read!.characteristic.setNotifyValue(true);
|
||
final write = bledevice.getresource('fff0/fff2');
|
||
|
||
const int maxRetries = 2;
|
||
for (int attempt = 0; attempt < maxRetries; attempt++) {
|
||
final completer = Completer<String>();
|
||
final subscription = read.characteristic.onValueReceived.listen((data) {
|
||
if (data.length >= 14) {
|
||
completer.complete(parseMacFromBleResponse(data));
|
||
}
|
||
});
|
||
|
||
final order = [
|
||
0xFF,
|
||
0xFF,
|
||
0xFF,
|
||
0xFF,
|
||
0x00,
|
||
0x03,
|
||
0x40,
|
||
0x01,
|
||
0x01,
|
||
0x00,
|
||
0x45,
|
||
0xFD
|
||
];
|
||
await write!.characteristic.write(order);
|
||
|
||
try {
|
||
final mac = await completer.future.timeout(timeout);
|
||
if (mac == null || mac.isEmpty) {
|
||
throw Exception("获取MAC失败".tr);
|
||
}
|
||
if (mac == "000000000000") {
|
||
throw Exception("获取MAC失败".tr);
|
||
}
|
||
await subscription.cancel();
|
||
return mac;
|
||
} catch (_) {
|
||
await subscription.cancel();
|
||
if (attempt == maxRetries - 1) rethrow;
|
||
}
|
||
}
|
||
throw Exception("获取MAC超时".tr);
|
||
}
|
||
|
||
Future<String> getMacFromType2(THapp bledevice, Duration timeout) async {
|
||
try {
|
||
final read = bledevice.getresource('ffe0/ffe1');
|
||
await read!.characteristic.setNotifyValue(true);
|
||
|
||
final write =
|
||
bledevice.getresource('ffe0/ffe1'); // 与 read 同 characteristic
|
||
const int maxRetries = 2;
|
||
for (int attempt = 0; attempt < maxRetries; attempt++) {
|
||
final completer = Completer<String>();
|
||
final subscription = read.characteristic.onValueReceived.listen((data) {
|
||
if (data.length >= 17) {
|
||
completer.complete(parseMacFromTH2Response(data));
|
||
}
|
||
});
|
||
|
||
final order = [0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x0C, 0x0B, 0x0A];
|
||
int checksum = order.reduce((a, b) => a + b) & 0xFFFF;
|
||
order.add(checksum & 0xFF); // 低位
|
||
order.add((checksum >> 8) & 0xFF); // 高位
|
||
|
||
await write!.characteristic.write(order);
|
||
|
||
try {
|
||
final mac = await completer.future.timeout(timeout);
|
||
await subscription.cancel();
|
||
return mac;
|
||
} catch (_) {
|
||
await subscription.cancel();
|
||
if (attempt == maxRetries - 1) rethrow;
|
||
}
|
||
}
|
||
} catch (e) {
|
||
ef.log("[获取设备 MAC]:失败:$e");
|
||
}
|
||
|
||
throw Exception("获取MAC超时".tr);
|
||
}
|
||
}
|
||
|
||
String parseMacFromBleResponse(List<int> data) {
|
||
// 先做简单的合法性判断
|
||
if (data.length >= 17 &&
|
||
data[0] == 0xFF &&
|
||
data[1] == 0xFF &&
|
||
data[2] == 0xFF &&
|
||
data[3] == 0xFF &&
|
||
data[6] == 0x40 &&
|
||
data[7] == 0x01) {
|
||
// 取出 Byte8 ~ Byte13
|
||
List<int> macBytes = data.sublist(8, 14);
|
||
String macAddress = macBytes
|
||
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
|
||
.join('');
|
||
return macAddress;
|
||
} else {
|
||
throw Exception("BLE返回数据格式不正确".tr);
|
||
}
|
||
}
|
||
|
||
String parseMacFromTH2Response(List<int> data) {
|
||
if (data.length < 17) {
|
||
throw Exception("数据长度不足,无法解析MAC".tr);
|
||
}
|
||
|
||
int status = data[8];
|
||
if (status != 0x03 && status != 0x04) {
|
||
throw Exception("未连接心率带".tr);
|
||
}
|
||
|
||
// 提取9~14字节的MAC地址
|
||
List<int> macBytes = data.sublist(9, 15);
|
||
return macBytes
|
||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||
.join(":")
|
||
.toUpperCase();
|
||
}
|