更新ios蓝牙扫描

This commit is contained in:
wyf
2025-08-20 16:53:57 +08:00
parent 7c2d169ab3
commit f288d2383d
7 changed files with 254 additions and 32 deletions

View File

@@ -394,7 +394,7 @@
"全部消息": "全部消息", "全部消息": "全部消息",
"请先在设置里的消息通知打开全部消息配置": "请先在设置里的消息通知打开全部消息配置", "请先在设置里的消息通知打开全部消息配置": "请先在设置里的消息通知打开全部消息配置",
"请先打开全部消息配置": "请先打开全部消息配置", "请先打开全部消息配置": "请先打开全部消息配置",
"正常值": "正常值:", "正常值": "范围:",
"今日": "今日", "今日": "今日",
"深色": "深色", "深色": "深色",
"皮肤指数": "皮肤指数", "皮肤指数": "皮肤指数",

View File

@@ -395,7 +395,7 @@
"全部消息": "全部消息", "全部消息": "全部消息",
"请先在设置里的消息通知打开全部消息配置": "請先在設置裡的消息通知打開全部消息配置", "请先在设置里的消息通知打开全部消息配置": "請先在設置裡的消息通知打開全部消息配置",
"请先打开全部消息配置": "請先打開全部消息配置", "请先打开全部消息配置": "請先打開全部消息配置",
"正常值": "正常值:", "正常值": "范围:",
"今日": "今日", "今日": "今日",
"深色": "深色", "深色": "深色",
"皮肤指数": "皮膚指數", "皮肤指数": "皮膚指數",

View File

@@ -593,7 +593,7 @@
"该设备的历史数据将被清除": "该设备的历史数据将被清除", "该设备的历史数据将被清除": "该设备的历史数据将被清除",
"有一条新的设备分享消息": "有一条新的设备分享消息", "有一条新的设备分享消息": "有一条新的设备分享消息",
"去查看": "去查看", "去查看": "去查看",
"正常值": "正常值:", "正常值": "范围:",
"绑定设备": "绑定设备", "绑定设备": "绑定设备",
"设备分享提醒": "设备分享提醒", "设备分享提醒": "设备分享提醒",
"生命体征": "生命体征", "生命体征": "生命体征",

View File

@@ -593,7 +593,7 @@
"该设备的历史数据将被清除": "該設備的歷史數據將被清除", "该设备的历史数据将被清除": "該設備的歷史數據將被清除",
"有一条新的设备分享消息": "有一條新的設備分享消息", "有一条新的设备分享消息": "有一條新的設備分享消息",
"去查看": "去查看", "去查看": "去查看",
"正常值": "正常值:", "正常值": "范围:",
"绑定设备": "绑定设备", "绑定设备": "绑定设备",
"设备分享提醒": "設備分享提醒", "设备分享提醒": "設備分享提醒",
"生命体征": "生命體徵", "生命体征": "生命體徵",

View File

@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'package:ef/ef.dart'; import 'package:ef/ef.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -11,7 +12,6 @@ import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/CommonVariables.dart'; import 'package:vbvs_app/common/util/CommonVariables.dart';
import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
@@ -230,6 +230,66 @@ class _MHTBlueteethDevicePageState extends State<MHTBlueteethDevicePage> {
await FlutterBluePlus.startScan(timeout: Duration(seconds: 10)); await FlutterBluePlus.startScan(timeout: Duration(seconds: 10));
// _scanSubscription = FlutterBluePlus.scanResults.listen((results) {
// if (!mounted) return;
// final signalThreshold = mhtBlueToothController.model.singal!;
// final searchKey =
// mhtBlueToothController.search.value.trim().toLowerCase();
// final filteredResults = results.where((r) {
// final localName = r.advertisementData.localName;
// final isTarget = r.rssi > signalThreshold &&
// isTargetDevice(
// localName, widget.deviceType['reg'].cast<String>());
// if (!isTarget) return false;
// final name = r.advertisementData.advName.toLowerCase();
// // String macAddress = r.device.remoteId.str;
// String macAddress = getDeviceId(r);
// final mac = macAddress.replaceAll(':', '');
// final search = searchKey.trim().replaceAll(':', '').toLowerCase();
// if (search.isNotEmpty &&
// !name.contains(search) &&
// !mac.replaceAll(':', '').toLowerCase().contains(search)) {
// return false;
// }
// return true;
// }).map((r) {
// return BlueToothDataModel.fromScanResult(
// r, widget.deviceType['type']?.toInt(),
// bind: false,
// name: r.advertisementData.localName,
// mac:getDeviceId(r).replaceAll(':', ''));
// }).toList();
// final currentDevices = mhtBlueToothController.model.blueRawData ?? [];
// final newDevices = <BlueToothDataModel>[];
// for (var newDevice in filteredResults) {
// // 检查设备是否已存在
// final existingIndex =
// currentDevices.indexWhere((d) => d.mac == newDevice.mac);
// if (existingIndex >= 0) {
// // 更新已有设备信息(如信号强度)
// currentDevices[existingIndex] = newDevice;
// } else {
// // 添加新设备
// newDevices.add(newDevice);
// }
// }
// setState(() {
// mhtBlueToothController.model.blueRawData = [
// ...currentDevices,
// ...newDevices
// ];
// });
// });
_scanSubscription = FlutterBluePlus.scanResults.listen((results) { _scanSubscription = FlutterBluePlus.scanResults.listen((results) {
if (!mounted) return; if (!mounted) return;
@@ -237,45 +297,90 @@ class _MHTBlueteethDevicePageState extends State<MHTBlueteethDevicePage> {
final searchKey = final searchKey =
mhtBlueToothController.search.value.trim().toLowerCase(); mhtBlueToothController.search.value.trim().toLowerCase();
final filteredResults = results.where((r) { final filteredResults = results.map((r) {
final localName = r.advertisementData.localName; Map<String, dynamic> d = {
final isTarget = r.rssi > signalThreshold && "updateTime": DateTime.now().millisecondsSinceEpoch,
isTargetDevice( "name": r.advertisementData.localName?.trim() ??
localName, widget.deviceType['reg'].cast<String>()); r.advertisementData.advName?.trim() ??
if (!isTarget) return false; r.device.name ??
final name = r.advertisementData.advName.toLowerCase(); "",
String macAddress = r.device.remoteId.str; "rssi": r.rssi,
final mac = macAddress.replaceAll(':', ''); "device": r.device,
final search = searchKey.trim().replaceAll(':', '').toLowerCase(); "connectable": r.advertisementData.connectable
};
if (search.isNotEmpty && // 从 manufacturerData 解析设备唯一 ID
!name.contains(search) && Map<int, List<int>> m_d = r.advertisementData.manufacturerData;
!mac.replaceAll(':', '').toLowerCase().contains(search)) { String? deviceId;
m_d.forEach((key, value) {
if (value == null || value.isEmpty) return;
if (key == 65517) {
List<int> a = [0, 0, ...value];
advertisDataFormatter(a, d); // 你原来的处理
if (d['adData']?['deviceId'] != null)
deviceId = d['adData']['deviceId'];
} else if (key == 11125 && value.length == 8) {
deviceId = ab2str(value.sublist(2, 8)).toUpperCase();
} else if (value.length == 8 && isQuanShiDevice(d["name"])) {
deviceId = ab2str(value.sublist(2, 8)).toUpperCase();
} else if ((value.length == 4 || value.length == 6) &&
isMHTSWES(d["name"])) {
List<int> a;
if (value.length == 4) {
ByteData bd = ByteData(2);
bd.setUint16(0, key, Endian.little);
a = [bd.getUint8(0), bd.getUint8(1), ...value];
} else {
a = [...value];
}
deviceId = ab2str(a).toUpperCase();
}
});
d['id'] = deviceId ?? r.device.remoteId.str; // fallback UUID
return BlueToothDataModel.fromScanResult(
r,
widget.deviceType['type']?.toInt(),
bind: false,
name: d['name'],
mac: d['id'],
);
}).where((d) {
// 信号强度过滤
if (d.scanResult.rssi <= signalThreshold) return false;
// 搜索关键字过滤
if (searchKey.isNotEmpty) {
String name = d.name.toLowerCase();
String mac = d.mac.toLowerCase();
if (!name.contains(searchKey) && !mac.contains(searchKey))
return false; return false;
} }
// 名称过滤规则,必须包含 deviceType['reg'] 列表中的某个字符串
List<String> regList =
widget.deviceType['reg']?.cast<String>() ?? [];
if (regList.isNotEmpty) {
String lowerName = d.name.toLowerCase();
bool match =
regList.any((reg) => lowerName.contains(reg.toLowerCase()));
if (!match) return false;
}
return true; return true;
}).map((r) {
return BlueToothDataModel.fromScanResult(
r, widget.deviceType['type']?.toInt(),
bind: false,
name: r.advertisementData.localName,
mac: r.device.remoteId.str.replaceAll(':', ''));
}).toList(); }).toList();
final currentDevices = mhtBlueToothController.model.blueRawData ?? []; final currentDevices = mhtBlueToothController.model.blueRawData ?? [];
final newDevices = <BlueToothDataModel>[]; final newDevices = <BlueToothDataModel>[];
for (var newDevice in filteredResults) { for (var newDevice in filteredResults) {
// 检查设备是否已存在
final existingIndex = final existingIndex =
currentDevices.indexWhere((d) => d.mac == newDevice.mac); currentDevices.indexWhere((d) => d.mac == newDevice.mac);
if (existingIndex >= 0) { if (existingIndex >= 0) {
// 更新已有设备信息(如信号强度)
currentDevices[existingIndex] = newDevice; currentDevices[existingIndex] = newDevice;
} else { } else {
// 添加新设备
newDevices.add(newDevice); newDevices.add(newDevice);
} }
} }
@@ -863,4 +968,118 @@ class _MHTBlueteethDevicePageState extends State<MHTBlueteethDevicePage> {
}); });
} }
} }
String getDeviceId(ScanResult result) {
// AndroidremoteId 就是 MAC
if (Platform.isAndroid) {
return result.device.remoteId.str.replaceAll(':', '').toUpperCase();
}
// iOS尝试从 manufacturerData 里解析
Map<int, List<int>> mData = result.advertisementData.manufacturerData;
for (var key in mData.keys) {
List<int>? bytes = mData[key];
if (bytes != null && bytes.length == 6) {
// 假设这 6 个字节就是 MAC
return bytes
.map((b) => b.toRadixString(16).padLeft(2, '0'))
.join('')
.toUpperCase();
}
}
return result.device.remoteId.str.toUpperCase();
}
void advertisDataFormatter(var a, item) {
Map<String, dynamic> obj = {};
try {
if (a[2] == 1) {
obj['sn'] = a[3];
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
obj['b'] = a[10];
obj['h'] = a[11];
obj['t'] = a[12];
item['adData'] = obj;
} else if (a[2] == 2) {
obj['sn'] = a[3];
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
obj['b'] = a[10];
obj['h'] = a[11];
obj['t'] = a[12];
obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线';
obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常';
ByteData byteData = ByteData.sublistView(
Uint8List.fromList(a.sublist(14, 18).reversed.toList()));
obj['version'] = byteData.getUint32(0);
item['adData'] = obj;
} else if (a[2] == 3) {
List<String> otherstr = [];
obj['sn'] = a[3];
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
obj['b'] = a[10];
obj['h'] = a[11];
obj['t'] = a[12];
obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线';
obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常';
if ((a[13] & 4) == 4) {
otherstr.add('呼吸暂停');
}
if ((a[13] & 8) == 8 && (a[13] & 1) == 1) {
obj['isbed'] = '在床';
} else {
obj['isbed'] = '离床';
}
if ((a[13] & 16) == 16) {
otherstr.add('授权过期');
}
if ((a[13] & 64) == 64) {
otherstr.add('设备休眠');
}
obj['other'] = otherstr.join('');
ByteData byteData = ByteData.sublistView(
Uint8List.fromList(a.sublist(14, 18).reversed.toList()));
obj['version'] = byteData.getUint32(0);
ByteData qsnData =
ByteData.sublistView(Uint8List.fromList(a.sublist(17, 19)));
obj['qsn'] = qsnData.getUint16(0) * 256 + obj['sn'];
item['adData'] = obj;
} else if (a.length > 17) {
obj['sn'] = a[3];
obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase();
obj['b'] = a[10];
obj['h'] = a[11];
obj['t'] = a[12];
obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线';
obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常';
ByteData byteData = ByteData.sublistView(
Uint8List.fromList(a.sublist(14, 18).reversed.toList()));
obj['version'] = byteData.getUint32(0);
item['adData'] = obj;
}
} catch (e) {
print(e);
}
}
bool isQuanShiDevice(name) {
return "$name".contains("S4-ZM-M94-4") ||
"$name".contains("S4-ZM-N94-4") ||
"$name".contains("MHT-SWES-D");
}
bool isMHTSWES(name) {
return "$name".contains("MHT-") ||
"$name".contains("MHT-SWES-M") ||
"$name".contains("MHT-SWES-S");
}
} }

View File

@@ -141,6 +141,7 @@ class MHTLoginController extends GetControllerEx<LoginModel> {
"type": type, "type": type,
"userName": account, "userName": account,
"password": password, "password": password,
'khCode':'mht',
}; };
String serviceAddress = ServiceConstant.service_address; String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service; String serviceName = ServiceConstant.server_service;
@@ -273,6 +274,7 @@ class MHTLoginController extends GetControllerEx<LoginModel> {
method: MyHttpMethod.post, method: MyHttpMethod.post,
queryUrl: queryUrl, queryUrl: queryUrl,
data: data); data: data);
ef.log("微信登录授权开始");
if (apiResponse.code == HttpStatusCodes.ok) { if (apiResponse.code == HttpStatusCodes.ok) {
ef.log("微信登录授权成功:${apiResponse}"); ef.log("微信登录授权成功:${apiResponse}");
UserInfoController userInfoController = Get.find(); UserInfoController userInfoController = Get.find();
@@ -310,7 +312,7 @@ class MHTLoginController extends GetControllerEx<LoginModel> {
int code = 0; int code = 0;
requestWithLog( requestWithLog(
logTitle: "注销账号", logTitle: "注销账号",
method: MyHttpMethod.put, method: MyHttpMethod.delete,
queryUrl: queryUrl, queryUrl: queryUrl,
onSuccess: (res) { onSuccess: (res) {
code = res.code!; code = res.code!;

View File

@@ -112,7 +112,8 @@ class MHTRegisterController extends GetControllerEx<RegisterModel> {
"type": 1, "type": 1,
"userName": model.register_phone!, "userName": model.register_phone!,
"password": model.register_pd!, "password": model.register_pd!,
"verify": model.register_code! "verify": model.register_code!,
"khCode": "mht",
}; };
await requestWithLog( await requestWithLog(
logTitle: "用户注册".tr, logTitle: "用户注册".tr,