更新棉花糖

This commit is contained in:
wyf
2025-06-16 09:32:24 +08:00
parent d8b46c41ad
commit acde8340a8
27 changed files with 6762 additions and 658 deletions

View File

@@ -0,0 +1,616 @@
import 'dart:async';
import 'package:EasyDartModule/EasyDartModule.dart' as edm;
import 'package:EasyDartModule/base/logger/Logger.dart';
import 'package:easydevice/easydevice.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.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/component/tool/cmd.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';
class DeviceComponentWidget extends StatefulWidget {
BlueToothDataModel bleDevice;
DeviceComponentWidget({
super.key,
required this.bleDevice,
});
@override
State<DeviceComponentWidget> createState() => _DeviceComponentWidgetState();
}
class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
ThemeController themeController = Get.find();
MHTBlueToothController blueteethBindController = Get.find();
var lisObj;
@override
Widget build(BuildContext context) {
Map device = {
"name": widget.bleDevice.name,
"mac": 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, 52.rpx),
onTap: () async {},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
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']) {
return SizedBox(
width: 24.rpx,
height: 24.rpx,
child: CircularProgressIndicator(
strokeWidth: 1,
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']) {
showConfirmDialog(
context, Container(), "其他设备正在绑定中,是否终止其他设备绑定?".tr,
onConfirm: () {
blueteethBindController.currentDeviceMac.value = "";
blueteethBindController.updateAll();
}, onCancel: () {
blueteethBindController.resumeScanning();
});
}
}
blueteethBindController.currentDeviceMac?.value =
device['mac']!;
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);
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) {
//更新设备绑定流程
Get.toNamed("/mHTwifiPage",arguments: widget.bleDevice);
THapp bledevice = THapp(
device: widget.bleDevice.scanResult.device);
blueteethBindController.currentDevice = bledevice;
blueteethBindController.currentDeviceMac.value = "";
} else {
blueteethBindController.resumeScanning();
blueteethBindController.currentDeviceMac.value = "";
blueteethBindController.updateAll();
TopSlideNotification.show(
context,
text: response.msg ?? "蓝牙绑定.连接异常".tr,
textColor: themeController.currentColor.sc9,
);
}
},
onCancel: () {
print('用户点击了取消');
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();
}
}
},
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": mac,
"wifi": false,
"celibration": false,
"person_info": false,
"time": DateTime.now().millisecondsSinceEpoch,
};
requestWithLog(
logTitle: "更新用户绑定流程",
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: FlutterFlowTheme.of(context).bodyMedium.override(
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: 1,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
);
}
return Container();
}),
],
),
),
Row(
children: [
Text(
"蓝牙绑定.信号强度".tr + '${device.rssi ?? '-'}dBm',
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFEBF2F8),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
SizedBox(width: 40.rpx),
Text(
"蓝牙绑定.SN".tr + '${device.sn ?? '-'}',
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFF5F9FD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
],
),
Text(
"蓝牙绑定.蓝牙地址".tr + '${device.deviceId ?? '-'}',
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Text(
"蓝牙绑定.mac".tr + '${device.mac ?? '-'}',
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Row(
children: [
Text(
"蓝牙绑定.网络".tr +
'${device.isOnline == true ? '蓝牙绑定.在线'.tr : '蓝牙绑定.离线'.tr}',
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFEBF2F8),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
SizedBox(width: 145.rpx),
Row(
children: [
Text(
"蓝牙绑定.传感器".tr + "",
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFEBF2F8),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Text(
device.bind == false ? '蓝牙绑定.可绑定'.tr : '蓝牙绑定.已被绑定'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: device.bind == false
? const Color(0xFF1AD2B5)
: themeController.currentColor.sc9,
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
],
),
],
),
Text(
"版本".tr + '${device.version ?? '-'}',
style: FlutterFlowTheme.of(context).bodyMedium.override(
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) 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("蓝牙连接失败");
DailyLogUtils.printLog("蓝牙连接失败");
TopSlideNotification.show(
context,
text: "蓝牙连接失败".tr,
textColor: themeController.currentColor.sc9,
);
throw Exception("蓝牙连接失败");
}
blueteethBindController.blueConnectFlag.value = 2;
blueteethBindController.currentDevice = bledevice;
var read = bledevice.getresource('fff0/fff1');
await read!.characteristic.setNotifyValue(true);
var write = bledevice.getresource('fff0/fff2');
for (int attempt = 0; attempt < maxRetries; attempt++) {
var completer = Completer<String>();
StreamSubscription? subscription;
subscription = read.characteristic.onValueReceived.listen((onData) {
if (onData.length >= 14) {
// 按照你提供的协议,返回数据长度至少 17字节这里保险起见 14起步
String parsedMac = parseMacFromBleResponse(onData);
completer.complete(parsedMac);
}
});
// 发送查询命令
var order = [
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x03,
0x40,
0x01,
0x01,
0x00,
0x45,
0xFD
];
await write!.characteristic.write(order);
try {
macAddress = await completer.future.timeout(timeout);
await subscription.cancel();
break; // 成功拿到,跳出重试
} catch (e) {
await subscription.cancel();
if (attempt == maxRetries - 1) {
TopSlideNotification.show(
context,
text: "获取MAC地址超时请重试".tr,
textColor: themeController.currentColor.sc9,
);
throw Exception("获取MAC地址失败");
}
}
}
if (macAddress == null) {
throw Exception("未能获取到MAC地址");
}
// 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: "设备连接失败,请重试".tr,
textColor: themeController.currentColor.sc9,
);
rethrow;
}
}
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: "获取设备状态",
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'].toString().toUpperCase() == macKey) {
// 更新 bleDevice 的状态
//如果传感器已经绑定 暂时不处理
// bleDevice.bind = item['bind'] ?? bleDevice.bind;
bleDevice.macA = item['mac'];
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;
}
}
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返回数据格式不正确");
}
}