import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/CommonVariables.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.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/user_info_controller.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart'; import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart'; import 'package:vbvs_app/pages/mh_page/device/component/DeviceComponentWidget.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/searchWidget.dart'; class MHTBlueteethDevicePage extends StatefulWidget { var deviceType; MHTBlueteethDevicePage({super.key, required this.deviceType}); @override State createState() => _MHTBlueteethDevicePageState(); } class _MHTBlueteethDevicePageState extends State { MHTBlueToothController mhtBlueToothController = Get.find(); GlobalController globalController = Get.find(); UserInfoController userInfoController = Get.find(); ThemeController themeController = Get.find(); late FlutterBluePlus flutterBlue; List scanResults = []; bool isScanning = false; Timer? _timer; bool _isDialogShowing = false; var currentConnectedDeviceProp; var connectDeviceCurrent = null; List bleDevice = []; String currentMsg = "寻找设备中..."; Timer? connectTimer; bool isFind = false; List bindArrBackup = []; List bindArr = ["", "", ""]; StreamSubscription>? _scanSubscription; @override void initState() { super.initState(); mhtBlueToothController.model.blueRawData = []; mhtBlueToothController.model.deviceDataStatus = []; flutterBlue = FlutterBluePlus(); _checkBluetoothPermission(); mhtBlueToothController.startStatusPolling(); mhtBlueToothController.search.value = ""; mhtBlueToothController.currentDeviceMac?.value = ""; } Future _checkBluetoothPermission() async { PermissionStatus bluetoothStatus = await Permission.bluetooth.status; PermissionStatus locationStatus = await Permission.location.status; if (bluetoothStatus.isGranted && locationStatus.isGranted) { _startScanning(); _startPeriodicScan(); } else { _requestBluetoothPermission(); } } // Future _requestBluetoothPermission() async { // // showPermissionInfoDialog(Get.context!, CommonVariables().permissionInfo); // // Map statuses = await [ // // Permission.bluetoothScan, // // Permission.bluetoothConnect, // // Permission.location, // // ].request(); // Map statuses = {}; // try { // // 1️⃣ 先显示自定义提示弹窗 // showPermissionInfoDialog(Get.context!, CommonVariables().permissionInfo); // // 2️⃣ 等待一小会,让 UI 有时间渲染提示弹窗 // await Future.delayed(const Duration(milliseconds: 300)); // // 3️⃣ 再去申请权限(系统权限弹窗可能会弹出) // statuses = await [ // Permission.bluetoothScan, // Permission.bluetoothConnect, // Permission.location, // ].request(); // // 这里你可以检查每个权限的状态 // ef.log("权限状态: $statuses"); // } catch (e) { // ef.log("申请权限出错: $e"); // } finally { // // 4️⃣ 无论成功失败,最后关闭提示弹窗 // if (Get.context != null) { // Navigator.of(Get.context!, rootNavigator: true).pop(); // } // } // bool allGranted = statuses[Permission.bluetoothScan]?.isGranted == true && // statuses[Permission.bluetoothConnect]?.isGranted == true && // statuses[Permission.location]?.isGranted == true; // if (allGranted) { // _startScanning(); // _startPeriodicScan(); // } else { // _showPermissionDeniedDialog(); // } // } Future _requestBluetoothPermission() async { Map statuses = {}; bool dialogShown = false; // 标记是否弹过权限提示弹窗 if (Platform.isIOS) { PermissionStatus isBleGranted = await Permission.bluetooth.request(); print('checkBlePermissions-ios, isBleGranted=$isBleGranted'); if (isBleGranted.isGranted) { // startBluetoothScanning(); _startScanning(); _startPeriodicScan(); } else { // showToast("蓝牙开关或蓝牙权限未开启,请开启蓝牙开关与蓝牙权限".tr, closeTime: 7); try { _startScanning(); _startPeriodicScan(); } catch (e) { TopSlideNotification.show(context, text: "蓝牙权限未开启,请在设置中开启蓝牙权限".tr, textColor: themeController.currentColor.sc9); } } } else if (Platform.isAndroid) { try { // 检查是否已授权 bool alreadyGranted = await Permission.bluetoothScan.isGranted && await Permission.bluetoothConnect.isGranted && await Permission.location.isGranted; if (!alreadyGranted) { // 弹出自定义提示 showPermissionInfoDialog( Get.context!, CommonVariables().bluetoothpermissionInfo); dialogShown = true; await Future.delayed(const Duration(milliseconds: 300)); // 请求权限 statuses = await [ Permission.bluetoothScan, Permission.bluetoothConnect, Permission.location, ].request(); ef.log("权限状态: $statuses"); } else { statuses = { Permission.bluetoothScan: PermissionStatus.granted, Permission.bluetoothConnect: PermissionStatus.granted, Permission.location: PermissionStatus.granted, }; } } catch (e) { ef.log("申请权限出错: $e"); } finally { // 只有真的弹过提示才关闭 if (dialogShown && Get.context != null) { Navigator.of(Get.context!, rootNavigator: true).pop(); } } bool allGranted = statuses[Permission.bluetoothScan]?.isGranted == true && statuses[Permission.bluetoothConnect]?.isGranted == true && statuses[Permission.location]?.isGranted == true; if (allGranted) { _startScanning(); _startPeriodicScan(); } else { _showPermissionDeniedDialog(context); } } else { TopSlideNotification.show(context, text: "当前系统不支持蓝牙,无法使用此功能".tr, textColor: themeController.currentColor.sc9); } } void _showPermissionDeniedDialog(BuildContext context) { TopSlideNotification.show(context, text: "应用需要蓝牙和位置权限才能扫描设备。请授予权限。".tr, textColor: themeController.currentColor.sc9); } void _startScanning() async { try { if (!mounted || isScanning || !mhtBlueToothController.shouldScan.value) return; _scanSubscription?.cancel(); var bluetoothState = await FlutterBluePlus.isOn; mhtBlueToothController.model.bluetooth = bluetoothState; mhtBlueToothController.updateAll(); if (!bluetoothState && !_isDialogShowing) { _isDialogShowing = true; mhtBlueToothController.model.blueRawData = []; mhtBlueToothController.model.deviceDataStatus = []; mhtBlueToothController.updateAll(); await _showBluetoothNotEnabledDialog(); _isDialogShowing = false; return; } if (!isScanning) { setState(() { isScanning = true; }); 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()); // 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 = []; // 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) { if (!mounted) return; final signalThreshold = mhtBlueToothController.model.singal!; final searchKey = mhtBlueToothController.search.value.trim().toLowerCase(); final filteredResults = results.map((r) { Map d = { "updateTime": DateTime.now().millisecondsSinceEpoch, "name": r.advertisementData.localName?.trim() ?? r.advertisementData.advName?.trim() ?? r.device.name ?? "", "rssi": r.rssi, "device": r.device, "connectable": r.advertisementData.connectable }; // 从 manufacturerData 解析设备唯一 ID Map> m_d = r.advertisementData.manufacturerData; String? deviceId; m_d.forEach((key, value) { if (value == null || value.isEmpty) return; if (key == 65517) { List 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 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; } // 名称过滤规则,必须包含 deviceType['reg'] 列表中的某个字符串 List regList = widget.deviceType['reg']?.cast() ?? []; if (regList.isNotEmpty) { String lowerName = d.name.toLowerCase(); bool match = regList.any((reg) => lowerName.contains(reg.toLowerCase())); if (!match) return false; } return true; }).toList(); final currentDevices = mhtBlueToothController.model.blueRawData ?? []; final newDevices = []; 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 ]; }); }); await Future.delayed(Duration(seconds: 10)); await FlutterBluePlus.stopScan(); if (mounted) { setState(() { isScanning = false; }); } } } catch (e) { ef.log("$e"); } finally { setState(() { isScanning = false; }); } } void _startPeriodicScan() { _timer = Timer.periodic(Duration(seconds: 3), (timer) { if (mhtBlueToothController.shouldScan.value && !isScanning) { _removeOldDevices(); // 先清理老旧设备 _startScanning(); } }); } void _stopScanning() { if (isScanning) { FlutterBluePlus.stopScan(); _scanSubscription?.cancel(); if (mounted) { setState(() { isScanning = false; }); } } } void _stopPeriodicScan() { _timer?.cancel(); } @override void dispose() { _stopPeriodicScan(); _stopScanning(); _scanSubscription?.cancel(); connectTimer?.cancel(); mhtBlueToothController.stopStatusPolling(); mhtBlueToothController.model.blueRawData = []; mhtBlueToothController.model.deviceDataStatus = []; super.dispose(); } bool isTargetDevice(String? name, List keywords) { if (name == null) return false; return keywords.any((k) => name.contains(k)); } _showBluetoothNotEnabledDialog() async { await showTipDialog( backgroundColor: Colors.white, context, Column( children: [ Text( "蓝牙未开启".tr, style: TextStyle( fontSize: AppConstants().title_text_fontSize, color: stringToColor("#333333"), ), ), SizedBox( height: 20.rpx, ), Text( "请先打开蓝牙在进行设备扫描".tr, style: TextStyle( fontSize: AppConstants().normal_text_fontSize, color: stringToColor("#333333"), ), ), ], )); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, boxConstraints) => GestureDetector( // onTap: () => FocusScope.of(context).unfocus(),, child: Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage('assets/images/new_background.png'), fit: BoxFit.fill, ), ), child: Scaffold( resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, appBar: AppBar( iconTheme: IconThemeData(color: themeController.currentColor.sc3), backgroundColor: Colors.transparent, automaticallyImplyLeading: false, titleSpacing: 0, title: Container( width: double.infinity, height: 180.rpx, child: Stack( alignment: Alignment.center, children: [ Text( '添加设备'.tr, style: TextStyle( fontFamily: 'Readex Pro', color: themeController.currentColor.sc3, letterSpacing: 0, fontSize: 30.rpx, ), ), Positioned( left: 0, child: returnIconButtom, ), ], ), ), actions: [], centerTitle: false, ), body: SafeArea( top: true, child: Padding( padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), child: Column( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Column( children: [ Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), child: Container( width: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( colors: [ stringToColor("#003058").withOpacity(0.8), stringToColor("#003058").withOpacity(0.8), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), borderRadius: BorderRadius.circular(20.rpx), ), child: Align( alignment: AlignmentDirectional(0, 0), child: Padding( padding: EdgeInsetsDirectional.fromSTEB( 0, 30.rpx, 0, 30.rpx), child: Obx(() { return Text( (mhtBlueToothController.model.bluetooth == null || mhtBlueToothController .model.bluetooth == false) ? "等待扫描".tr : '扫描中'.tr, style: TextStyle( fontFamily: 'Inter', color: stringToColor("#FFFFFF"), fontSize: 26.rpx, letterSpacing: 0.0, ), ); }), ), ), ), ), Container( width: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( colors: [ // stringToColor("FCFCFC"), // stringToColor("CEECE3") Colors.transparent, Colors.transparent, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), borderRadius: BorderRadius.circular(20.rpx), ), child: Padding( padding: EdgeInsetsDirectional.fromSTEB( 21.rpx, 5.rpx, 21.rpx, 5.rpx), child: Row( mainAxisSize: MainAxisSize.max, children: [ Text( '最小信号强度'.tr, style: TextStyle( fontFamily: 'Inter', color: stringToColor("#FFFFFF"), fontSize: 26.rpx, letterSpacing: 0.0, ), ), Expanded( child: Obx(() { return Slider( activeColor: Color(0xFF1FCC9B), inactiveColor: Colors.white, min: -100, max: 50, value: mhtBlueToothController.model.singal!, onChanged: (newValue) { newValue = double.parse( newValue.toStringAsFixed(0)); mhtBlueToothController.model.singal = newValue; _startScanning(); mhtBlueToothController.updateAll(); }, ); }), ), Obx(() { return Text( '${mhtBlueToothController.model.singal!.toInt()}', style: TextStyle( fontFamily: 'Inter', color: stringToColor("#FFFFFF"), fontSize: 26.rpx, letterSpacing: 0.0, ), ); }), ].divide(SizedBox(width: 30.rpx)), ), ), ), // Container( // width: double.infinity, // decoration: BoxDecoration( // color: themeController.currentColor.sc3, // borderRadius: BorderRadius.circular(20.rpx), // ), // // child: Padding( // // padding: EdgeInsetsDirectional.fromSTEB( // // 35.rpx, 0, 35.rpx, 0), // // child: Row( // // mainAxisSize: MainAxisSize.max, // // mainAxisAlignment: MainAxisAlignment.spaceBetween, // // children: [ // // Expanded( // // child: Row( // // mainAxisSize: MainAxisSize.max, // // children: [ // // Padding( // // padding: EdgeInsetsDirectional.fromSTEB( // // 0, 0.rpx, 0, 0), // // child: Container( // // width: 25.rpx, // // height: 25.rpx, // // decoration: BoxDecoration(), // // child: SvgPicture.asset( // // 'assets/img/icon/query.svg', // // fit: BoxFit.cover, // // color: stringToColor("#333333"), // // ), // // ), // // ), // // Expanded( // // child: Container( // // width: 100.rpx, // // height: 90.rpx, // // decoration: BoxDecoration( // // color: Colors.white, // // ), // // child: Align( // // alignment: // // AlignmentDirectional(-1, 0), // // child: TextFormField( // // initialValue: // // mhtBlueToothController // // .search.value, // // onChanged: (Value) { // // mhtBlueToothController // // .search.value = Value; // // }, // // autofocus: false, // // obscureText: false, // // decoration: InputDecoration( // // isDense: true, // // labelStyle: TextStyle( // // fontFamily: 'Inter', // // fontSize: 26.rpx, // // letterSpacing: 0.0, // // ), // // hintText: '检索设备'.tr, // // hintStyle: TextStyle( // // fontFamily: 'Inter', // // fontSize: 26.rpx, // // letterSpacing: 0.0, // // ), // // enabledBorder: // // OutlineInputBorder( // // borderSide: BorderSide( // // color: Color(0x00000000), // // width: 1.rpx, // // ), // // borderRadius: // // BorderRadius.circular( // // 8.rpx), // // ), // // focusedBorder: // // OutlineInputBorder( // // borderSide: BorderSide( // // color: Color(0x00000000), // // width: 1.rpx, // // ), // // borderRadius: // // BorderRadius.circular( // // 8.rpx), // // ), // // errorBorder: OutlineInputBorder( // // borderSide: BorderSide( // // color: Colors.red, // // width: 1.rpx, // // ), // // borderRadius: // // BorderRadius.circular( // // 8.rpx), // // ), // // focusedErrorBorder: // // OutlineInputBorder( // // borderSide: BorderSide( // // color: Colors.red, // // width: 1.rpx, // // ), // // borderRadius: // // BorderRadius.circular( // // 8.rpx), // // ), // // filled: false, // // fillColor: themeController // // .currentColor.sc22, // // ), // // style: TextStyle( // // fontFamily: 'Inter', // // fontSize: 26.rpx, // // letterSpacing: 0.0, // // ), // // cursorColor: // // stringToColor("#003058"), // // ), // // ), // // ), // // ), // // ].divide(SizedBox(width: 6.rpx)), // // ), // // ), // // Padding( // // padding: EdgeInsetsDirectional.fromSTEB( // // 26.rpx, 0, 0, 0), // // child: Row( // // mainAxisSize: MainAxisSize.max, // // children: [ // // SizedBox( // // height: 50.rpx, // // child: VerticalDivider( // // thickness: 2.rpx, // // color: stringToColor("#333333"), // // ), // // ), // // ClickableContainer( // // backgroundColor: Colors.transparent, // // highlightColor: // // themeController.currentColor.sc4, // // borderRadius: 6.rpx, // // padding: EdgeInsets.zero, // // onTap: () async { // // _startScanning(); // // }, // // child: Text( // // '搜索'.tr, // // style: TextStyle( // // fontFamily: 'Inter', // // fontSize: 30.rpx, // // letterSpacing: 0.0, // // color: stringToColor("#333333"), // // ), // // ), // // ), // // ].divide(SizedBox(width: 26.rpx)), // // ), // // ), // // ], // // ), // // ), // child: Padding( // padding: // EdgeInsetsDirectional.fromSTEB(0, 10, 0, 23), // child: SearchWidget( // keyword: mhtBlueToothController.search.value, // color: Colors.red, // hint: "检索设备", // onChange: (d) { // mhtBlueToothController.search.value = d; // }, // findCallback: () { // // controller.getDeviceList(); // }, // ), // ), // ), Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), child: SearchWidget( padding: EdgeInsets.all(0), keyword: mhtBlueToothController.search.value, color: Colors.red, hint: "检索设备".tr, onChange: (d) { mhtBlueToothController.search.value = d; }, findCallback: () { _startScanning(); }, ), ), Padding( padding: EdgeInsetsDirectional.fromSTEB( 0, 60.rpx, 0, 32.rpx), child: Container( width: double.infinity, decoration: BoxDecoration(), child: Padding( padding: EdgeInsetsDirectional.fromSTEB( 19.rpx, 0, 0, 0), child: Obx(() { return Text( '匹配出的外围设备'.tr + "(${mhtBlueToothController.model.deviceDataStatus!.length})", style: TextStyle( fontFamily: 'Inter', fontSize: 30.rpx, letterSpacing: 0.0, color: themeController.currentColor.sc3, ), ); }), ), ), ), Obx(() { if (mhtBlueToothController .model.deviceDataStatus!.isNotEmpty) { final sortedList = mhtBlueToothController .model.deviceDataStatus! .toList() ..sort((a, b) => b.scanResult.rssi .compareTo(a.scanResult.rssi)); return Expanded( child: Container( width: double.infinity, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.max, children: [ ...sortedList .map((device) { return DeviceComponentWidget( bleDevice: device, deviceType: widget.deviceType, ); }) .toList() .divide(SizedBox(height: 30.rpx)) .addToEnd(SizedBox(height: 30.rpx)), ], ), ), ), ); } return Container(); }), ].divide(SizedBox( height: 30.rpx, )), )), Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 52.rpx, 0, 30.rpx), child: Container( width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.rpx), border: Border.all( color: themeController.currentColor.sc4 .withOpacity(0.5), width: AppConstants().border_width, ), ), child: Padding( padding: EdgeInsetsDirectional.fromSTEB( 30.rpx, 30.rpx, 30.rpx, 30.rpx), child: Row( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsetsDirectional.fromSTEB( 0, 8.rpx, 0, 0), child: Container( width: 23.rpx, height: 23.rpx, decoration: BoxDecoration(), child: SvgPicture.asset( 'assets/img/icon/tips.svg', fit: BoxFit.cover, color: themeController.currentColor.sc4, ), ), ), Expanded( child: Text( '蓝牙绑定提示'.tr, style: TextStyle( fontFamily: 'Inter', color: themeController.currentColor.sc4, fontSize: 26.rpx, letterSpacing: 0.0, ), ), ), ].divide(SizedBox(width: 23.rpx)), ), ), ), ), ].divide(SizedBox(height: 30.rpx)), ), ), ), ), ), ), ); } void _removeOldDevices() { final now = DateTime.now(); final currentDevices = mhtBlueToothController.model.blueRawData ?? []; // 移除30秒内未出现的设备 final updatedDevices = currentDevices.where((device) { return now.difference(device.lastSeen) < Duration(seconds: 30); }).toList(); if (updatedDevices.length != currentDevices.length) { setState(() { mhtBlueToothController.model.blueRawData = updatedDevices; }); } } String getDeviceId(ScanResult result) { // Android:remoteId 就是 MAC if (Platform.isAndroid) { return result.device.remoteId.str.replaceAll(':', '').toUpperCase(); } // iOS:尝试从 manufacturerData 里解析 Map> mData = result.advertisementData.manufacturerData; for (var key in mData.keys) { List? 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 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 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"); } }