Files
tuiche/lib/pages/mh_page/device/controller/mht_bluetooth_controller.dart
2026-04-07 09:33:43 +08:00

572 lines
21 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:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:easydevice/src/app/thapp.dart';
import 'package:ef/ef.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:json_annotation/json_annotation.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/FirmwareVersionService.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/base64Tool.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/user/controller/mht_register_controller.dart';
part 'mht_bluetooth_controller.g.dart';
@JsonSerializable()
class MHTBlueToothModel {
bool? bluetooth = false; //蓝牙开关
double? singal = -100;
@JsonKey(ignore: true)
List<BlueToothDataModel>? blueRawData = []; //蓝牙原始数据
@JsonKey(ignore: true)
List<BlueToothDataModel>? deviceDataStatus; //已经请求过状态的数据
int? read = 0;
int? deviceType; //绑定设备类型
bool wifiPassShow = false;
String? wifiPass;
MHTBlueToothModel();
static MHTBlueToothModel fromJson(Map<String, dynamic> json) =>
_$MHTBlueToothModelFromJson(json);
Map<String, dynamic> toJson() => _$MHTBlueToothModelToJson(this);
}
class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
MHTBlueToothController() {
attr = GetModel(MHTBlueToothModel()).obs;
}
Timer? _statusTimer;
MHTRegisterController registerController = Get.find();
RxString search = "".obs;
RxString currentDeviceMac = "".obs; //当前正在绑定的设备用来显示loading
THapp? currentDevice; //当前连接的设备
BlueToothDataModel? currentFullDevice; //当前连接的设备
RxInt blueConnectFlag = 0.obs; //当前蓝牙连接状态 0.正在连接 1.未连接 2.已连接
RxBool bluetoothStatus = false.obs; //蓝牙开启状态
RxInt connectStatus = 0.obs; //当前wifi连接状态 0未连接 1已连接
RxInt netType = 0.obs; //当前网络类型 0.正在检测 1.wifi 2.4g设备 3.未知
RxInt wifiConnectStatus = 1.obs; //获取wifi状态 0.正在检测 1.已检测完
RxMap selectWifi = {}.obs; //正在连接wifi信息
int returnPage = 0; //0返回首页 1.返回设备列表
RxInt wifiStatus = 0.obs; //wifi连接状态 0未连接 1已连接
RxList wifiList = [].obs;
RxMap connect_wifi = {}.obs;
RxString? cid = "".obs;
RxBool isScanning = false.obs;
RxMap<String, Map> localUpgradeMac = <String, Map>{}.obs; //mac 进度
String? currentUpgradeVersion; //最新版本号
String? currentUpgradeName; //最新固件名
String? currentUpgradeUrl; //最新固件下载地址
List<FirmwareVersionInfo> firmwareList = []; //固件版本列表
RxBool allSelect = false.obs; //升级是否全选
RxBool autoUpgrade = false.obs; //是否自动升级
// wifi下发指令
final List<Map<String, dynamic>> _commandQueue = []; // 指令队列
bool _isSendingCommand = false; // 是否正在发送指令
Timer? _commandTimeoutTimer; // 指令超时定时器
int _currentRetryCount = 0; // 当前重试次数
Map<String, dynamic>? _currentCommand; // 当前正在发送的指令
// 最大重试次数
static const int _maxRetryCount = 3;
// 超时时间(秒)
static const int _commandTimeoutSeconds = 10;
void startStatusPolling() {
updateDeviceStatus().then((res) {
if (res.code == HttpStatusCodes.ok) {
updateAll();
} else {
safeShowNotification(res.msg ?? "获取设备状态异常".tr);
EasyDartModule.logger.info("获取设备状态异常: $res");
DailyLogUtils.writeLog("获取设备状态异常: $res");
}
});
if (_statusTimer == null) {
_statusTimer = Timer.periodic(Duration(seconds: 2), (timer) {
updateDeviceStatus().then((res) {
if (res.code == HttpStatusCodes.ok) {
updateAll();
} else {
safeShowNotification(res.msg ?? "获取设备状态异常".tr);
EasyDartModule.logger.info("获取设备状态异常: $res");
DailyLogUtils.writeLog("获取设备状态异常: $res");
}
}).catchError((e, stack) {
print("updateDeviceStatus 执行异常: $e\n$stack");
safeShowNotification("设备状态请求失败".tr);
EasyDartModule.logger.info("设备状态异常: $e");
DailyLogUtils.writeLog("设备状态异常: $e");
});
});
}
}
void stopStatusPolling() {
_statusTimer?.cancel();
_statusTimer = null;
}
var shouldScan = true.obs;
void pauseScanning() {
shouldScan.value = false;
update();
}
// 恢复扫描
void resumeScanning() {
shouldScan.value = true;
update();
}
Future<ApiResponse> updateDeviceStatus() async {
try {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.get_bluetooth_device_status;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
if (model.blueRawData != null && model.blueRawData!.isNotEmpty) {
final macParams = model.blueRawData!
.map((device) =>
"mac=${Uri.encodeQueryComponent(device.mac!.replaceAll(':', ''))}")
.join("&");
if (queryUrl.contains('?')) {
queryUrl += '&$macParams';
} else {
queryUrl += '?$macParams';
}
String? language = "";
if (mhLanguageController.selectLanguage != null) {
language = mhLanguageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
var responseData =
response.data is String ? jsonDecode(response.data) : response.data;
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
if (res.code != HttpStatusCodes.ok) return res;
if (response.data['data'] != null && response.data['data'] is List) {
List<dynamic> responseList = response.data['data'];
// 新建一个 Map便于快速通过 mac 查找返回的设备状态
final Map<String, dynamic> responseMap = {
for (var item in responseList)
item['mac'.tr].toString().toUpperCase(): item
};
// 遍历 blueRawData更新 bind 状态
for (var device in model.blueRawData!) {
final macKey = device.mac!.replaceAll(':', '').toUpperCase();
if (responseMap.containsKey(macKey)) {
var item = responseMap[macKey];
// 更新 device 绑定状态等信息
device.bind = item['bind'] ?? device.bind;
device.mac = item['bindMac'] ?? device.mac;
}
}
model.deviceDataStatus =
List<BlueToothDataModel>.from(model.blueRawData!);
} else {
model.deviceDataStatus = [];
}
updateAll();
return res;
} else {
model.deviceDataStatus = [];
return ApiResponse(code: 1, msg: "".tr);
}
} catch (e) {
print("获取设备状态异常: $e");
EasyDartModule.logger.info("获取设备状态异常: $e");
DailyLogUtils.writeLog("获取设备状态异常: $e");
return ApiResponse(code: -1, msg: "请求失败".tr);
}
return ApiResponse(code: -1, msg: "未知错误".tr);
}
bindDeviceAndMAC(BlueToothDataModel bleDevice, BuildContext context) async {
try {
if ((bleDevice.macA == null || bleDevice.macA.isEmpty) &&
(bleDevice.macB == null || bleDevice.macB.isEmpty)) {
TopSlideNotification.show(context,
text: "传感器mac读取失败".tr, textColor: themeController.currentColor.sc9);
currentDeviceMac.value = "";
return;
}
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_bind;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
var data = {
"deviceType": model.deviceType,
"mac".tr: bleDevice.mac,
"macA": bleDevice.macA,
if (bleDevice.macB != null && bleDevice.macB!.isNotEmpty)
"macB": bleDevice.macB,
if (bleDevice.name != null && bleDevice.name!.isNotEmpty)
'param': {
'name': bleDevice.name,
},
};
EasyDartModule.logger.info("绑定传感器数据: $data");
var response =
await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data));
if (response != null) {
var responseData =
response.data is String ? jsonDecode(response.data) : response.data;
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(res, "绑定成功".tr, "绑定成功".tr);
if (res.code == HttpStatusCodes.ok) {
// PersonController personController = Get.find();
// personController.currentPersonId.value = res.data['id'];
//todo 绑定成功需要返回传感器id
currentDeviceMac.value = "";
if (res.data != null) {
if (currentFullDevice != null) {
currentFullDevice!.macAID = res.data['macA'];
currentFullDevice!.macBID = res.data['macB'];
currentFullDevice!.deviceID = res.data['id'];
}
}
return res;
} else {
return res;
}
} else {
return ApiResponse(code: -1, msg: "服务器失败".tr);
}
} catch (e) {
EasyDartModule.logger.info("绑定异常: $e");
DailyLogUtils.writeLog("蓝牙绑定: $e");
}
return ApiResponse(code: -1, msg: "未知错误".tr);
}
Future<bool> saveHabitData(sleepData) async {
bool resFlag = false;
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String type = "sleep_habit_${sleepData['mac']}";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
var data = {
"type": type,
"mac".tr: sleepData['mac'.tr],
"time": DateTime.now().millisecondsSinceEpoch,
"data": sleepData,
};
await requestWithLog(
logTitle: "更新睡眠习惯".tr,
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: data,
onSuccess: (res) {
resFlag = true;
EasyDartModule.logger.info("更新睡眠习惯成功: ${res.msg}");
},
onFailure: (res) {
resFlag = false;
EasyDartModule.logger.info("更新睡眠习惯失败: ${res.msg}");
},
);
return resFlag;
}
Future<Map> loadHabitDataApi(String mac, {int time = 3}) async {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String type = "sleep_habit_${mac}";
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?type=${type}";
// 使用 Future 来等待异步操作完成
Map<String, dynamic> result = {};
await requestWithLog(
logTitle: "更新睡眠习惯".tr,
method: MyHttpMethod.get,
queryUrl: queryUrl,
onSuccess: (res) {
ef.log("加载睡眠习惯成功: ${res.data}");
result = res.data; // 将返回的数据存入 result
},
onFailure: (res) {
ef.log("加载睡眠习惯失败: ${res.msg}");
result = {}; // 如果失败,可以返回空的 Map
},
);
return result; // 在 requestWithLog 完成之后返回 result
}
saveJiYiData(sleepData) async {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String type = "sleep_jiyi_${sleepData['mac']}";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
var data = {
"type": type,
"mac": sleepData['mac'],
"time": DateTime.now().millisecondsSinceEpoch,
"data": sleepData,
};
await requestWithLog(
logTitle: "更新睡眠记忆",
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: data,
);
}
loadJiYiData(String mac, {int time = 3}) async {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String type = "sleep_jiyi_${mac}";
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?type=${type}";
// 使用 Future 来等待异步操作完成
Map<String, dynamic> result = {};
await requestWithLog(
logTitle: "更新记忆",
method: MyHttpMethod.get,
queryUrl: queryUrl,
onSuccess: (res) {
ef.log("加载记忆成功: ${res.data}");
result = res.data; // 将返回的数据存入 result
},
onFailure: (res) {
ef.log("加载记忆失败: ${res.msg}");
result = {}; // 如果失败,可以返回空的 Map
},
);
return result; // 在 requestWithLog 完成之后返回 result
}
saveMattressTimeData(sleepData) async {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String type = "sleep_new_time_${sleepData['mac']}";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
var data = {
"type": type,
"mac": sleepData['mac'],
"time": DateTime.now().millisecondsSinceEpoch,
"data": sleepData,
};
await requestWithLog(
logTitle: "更新新版倒计时",
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: data,
);
}
loadMattressTimeData(String mac, {int time = 3}) async {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String type = "sleep_new_time_${mac}";
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?type=${type}";
// 使用 Future 来等待异步操作完成
Map<String, dynamic> result = {};
await requestWithLog(
logTitle: "更新新版倒计时",
method: MyHttpMethod.get,
queryUrl: queryUrl,
onSuccess: (res) {
ef.log("更新新版倒计时成功: ${res.data}");
result = res.data; // 将返回的数据存入 result
},
onFailure: (res) {
ef.log("更新新版倒计时失败: ${res.msg}");
result = {}; // 如果失败,可以返回空的 Map
},
);
return result; // 在 requestWithLog 完成之后返回 result
}
//下发wifi指令
// sendCommand(Map<String, dynamic> commandData) async {
// if (commandData == null || commandData.isEmpty) {
// throw "指令数据不能为空";
// }
// if (commandData['data'] == "FFFFFFFF00031000010014FD") {
// return;
// }
// ef.log("下发指令内容为: ${commandData['data']}");
// commandData['data'] = Base64Tool.encode(commandData['data']);
// if (Base64Tool.decode(commandData['data']) != "FFFFFFFF00031000010014FD" &&
// Base64Tool.decode(commandData['data']).length <= 24) {
// ef.log("下发指令: ${Base64Tool.decode(commandData['data'])}");
// } else {
// // ef.log("下发指令: ${Base64Tool.decode(commandData['data'])}");
// }
// ef.log("全部指令");
// ef.log("wifi指令控制下发时间-->${DateTime.now()}");
// commandData['wfr'] = true; //是否等待响应 true: 等待 false:不等待(默认)
// commandData['type'] = 0; //0 base64数据 1 二进制数据
// String serviceAddress = ServiceConstant.service_address;
// String serviceName = ServiceConstant.server_service;
// String serviceApi = ServiceConstant.sendWifiCommand;
// String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
// await requestWithLog(
// logTitle: "下发wifi控制指令",
// method: MyHttpMethod.post,
// queryUrl: queryUrl,
// data: commandData,
// onSuccess: (res) {
// ef.log("下发指令成功: ${res.msg}");
// EasyDartModule.logger.info("下发wifi控制指令成功: $res");
// },
// onFailure: (res) {
// ef.log("下发指令失败: ${res.msg}");
// EasyDartModule.logger.info("下发wifi控制指令失败: $res");
// throw "下发wifi控制指令失败: ${res.msg}";
// },
// );
// await Future.delayed(Duration(milliseconds: 100));
// }
//下发wifi指令
Future<void> sendCommand(Map<String, dynamic> commandData,
{int maxRetryCount = 3}) async {
if (commandData == null || commandData.isEmpty) {
throw Exception("指令数据不能为空");
}
if (commandData['data'] == "FFFFFFFF00031000010014FD") {
return;
}
// ef.log("下发指令内容为: ${commandData['data']}");
commandData['data'] = Base64Tool.encode(commandData['data']);
if (Base64Tool.decode(commandData['data']) != "FFFFFFFF00031000010014FD" &&
Base64Tool.decode(commandData['data']).length <= 24) {
// ef.log("下发指令: ${Base64Tool.decode(commandData['data'])}");
}
ef.log("全部指令");
EasyDartModule.logger.info(
"设备mac${commandData['mac']}" + "wifi指令控制下发时间-->${DateTime.now()}");
commandData['wfr'] = true; //是否等待响应 true: 等待 false:不等待(默认)
commandData['type'] = 0; //0 base64数据 1 二进制数据
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.sendWifiCommand;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
int retryCount = 0;
bool success = false;
dynamic lastError;
while (retryCount < maxRetryCount && !success) {
try {
if (retryCount > 0) {
ef.log("$retryCount 次重试下发wifi控制指令");
await Future.delayed(
Duration(milliseconds: 500 * retryCount)); // 递增延迟
}
ef.log("下发指令时间: ");
await requestWithLog(
logTitle:
"下发wifi控制指令${retryCount > 0 ? '(重试 $retryCount/$maxRetryCount)' : ''}",
method: MyHttpMethod.post,
queryUrl: queryUrl,
data: commandData,
onSuccess: (res) {
ef.log("下发指令成功: ${res.msg}");
EasyDartModule.logger.info("下发wifi控制指令成功: $res");
success = true;
},
onFailure: (res) {
ef.log("下发指令失败: ${res.msg}");
EasyDartModule.logger.info("下发wifi控制指令失败: $res");
lastError = "下发wifi控制指令失败: ${res.msg}";
throw Exception(lastError); // 抛出异常触发重试
},
);
await Future.delayed(Duration(milliseconds: 100));
} catch (e) {
lastError = e;
retryCount++;
if (retryCount >= maxRetryCount) {
ef.log("下发wifi控制指令失败已重试 $maxRetryCount");
EasyDartModule.logger.info("下发wifi控制指令失败已重试 $maxRetryCount 次: $e");
DailyLogUtils.writeLog("下发wifi控制指令失败已重试 $maxRetryCount 次: $e");
throw Exception("下发wifi控制指令失败已重试 $maxRetryCount 次: ${e.toString()}");
} else {
ef.log("下发wifi控制指令失败准备第 $retryCount 次重试");
}
}
}
}
//todo 解绑的时候删除自己所拥有的所有设备的睡眠习惯
}
void safeShowNotification(String msg) {
try {
final ctx = Get.context;
if (ctx != null && ctx.mounted) {
TopSlideNotification.show(
ctx,
text: msg,
textColor: themeController.currentColor.sc9,
);
} else {
print("TopSlideNotification 未显示context 不可用或未挂载".tr);
}
} catch (e, stack) {
// print("TopSlideNotification 显示异常: $e\n$stack");
}
}