import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:EasyDartModule/EasyDartModule.dart' as edm; import 'package:easydevice/easydevice.dart'; import 'package:easyweb/base/easyws.dart'; import 'package:easyweb/base/minisdk.dart'; import 'package:easyweb/easyweb.dart'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:vbvs_app/common/color/ServiceConstant.dart'; import 'package:vbvs_app/common/util/FitTool.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/controller/user_info_controller.dart'; import 'package:vbvs_app/model/WebSocketMessage.dart'; import 'package:vbvs_app/pages/main_bottom/component/main_page_b_bottom_change.dart'; import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; import 'package:vbvs_app/pages/mh_page/device/mht_blueteeth_device_page.dart'; import 'package:vbvs_app/pages/mh_page/homepage/component/HomeDeviceStausWidget.dart'; import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart'; class WebviewTestModel { WebviewTestModel(); //EasyFlutter Start[Model] //EasyFlutter End } class WebviewTestController extends GetControllerEx { var selectDevice = {}; var lastSelectDevice = {}; var bluetooth = 0; List personList = []; List instantData = []; RxBool initFlag = false.obs; Timer? _resourceLoadTimer; Widget webviewWidget = Container(); var wifiResponseData; WebviewTestController() : super(WebviewTestModel()) { web = WebviewHelper( isheadless: false, jsbridge: buildsdk( father: this, // clientId: '494641114', // clientId: '123', // dbgserverUrl: 'ws://192.168.1.2:9001', ), settings: buildsettings(), params: PlatformHeadlessInAppWebViewCreationParams( onLoadStop: (controller, url) { setState(() { ready.value = true; }); }, onLoadResource: (controller, resource) { // sourceTime = web.requestTimestamp; // ef.log("[请求资源时间]${web.requestTimestamp}"); // _startMonitoringIfNeeded(); }, ), ); try { web.jsbridge!.bind((bridge) { var minible = Minibleapp( bridge: bridge, onConnect: (args1, args2) { final devices = args1.devices as Map; final mac = args2 as String; if (devices.containsKey(mac)) { final device = devices[mac]; ef.log('Minibleapp connected(Android): $mac -> $device'); return device; } // iOS: 遍历所有设备,解析 manufacturerData 拿真实 deviceId(mac) for (final entry in devices.entries) { final manufacturerData = entry.value.manufacturerData; String? deviceId; if (manufacturerData != null) { manufacturerData.forEach((key, data) { if (data.isEmpty) return; if (key == 65517) { List a = [0, 0, ...data]; final d = {}; advertisDataFormatter(a, d); if (d['adData']?['deviceId'] != null) { deviceId = d['adData']['deviceId']; } } else if (key == 11125 && data.length == 8) { deviceId = ab2str(data.sublist(2, 8)).toUpperCase(); } else if (data.length == 8 && isQuanShiDevice(entry.value.name)) { deviceId = ab2str(data.sublist(2, 8)).toUpperCase(); } else if ((data.length == 4 || data.length == 6) && isMHTSWES(entry.value.name)) { List a; if (data.length == 4) { ByteData bd = ByteData(2); bd.setUint16(0, key, Endian.little); a = [bd.getUint8(0), bd.getUint8(1), ...data]; } else { a = [...data]; } deviceId = ab2str(a).toUpperCase(); } }); } final normalizedMac = mac.replaceAll(":", "").toUpperCase(); if (deviceId != null && deviceId!.toUpperCase() == normalizedMac) { return entry.value; // 返回匹配的设备 } } ef.log('Minibleapp connected: $mac not found in devices'); return null; }, ); // ef.kvroot.devicelist.listen((x) {}); bridge.sdk.updateDeviceRoute((args) async { ef.log('updateDeviceRoute: $args'); selectDevice['blueToothStatus'] = bluetooth; Get.toNamed("${args[0]}", arguments: selectDevice); return true; }); bridge.sdk.selectDevice((args) async { try { ef.log('selectDevice: $args'); selectDevice = args[0]; await queryPersonInfoByMac(); if (selectDevice != null && selectDevice.isNotEmpty && selectDevice['mac'.tr] != null && (selectDevice['mac'.tr] != args[0]['mac'.tr])) { lastSelectDevice = selectDevice; } edm.EasyDartModule.websocket.sendData(jsonEncode( WebSocketMessage(path: "/smartbed/connect", type: 5, data: { 'mac'.tr: selectDevice['mac'.tr], }))); dealInstantData(selectDevice); dealDeviceData(selectDevice); } catch (e) { ef.log("[切换设备失败]$e"); } //查询人员信息 return true; }); bridge.sdk.updateBlueToothStatus((args) async { ef.log('updateBlueToothStatus: $args'); bluetooth = args[0]; selectDevice['blueToothStatus'] = bluetooth; return true; }); //sdk定义接口 bridge.sdk.querySelectDevice((args) async { ef.log('querySelectDevice: $args'); // bluetooth = args[0]; MHTHomeController deviceController = Get.find(); await deviceController.getDeviceList(group: 'room'); final allDevices = deviceController.deviceList.values .expand((list) => list) .toList(); return allDevices; }); //请求token信息 bridge.sdk.queryUserToken((args) async { ef.log('queryUserToken: $args'); // bluetooth = args[0]; return edm.EasyDartModule.dio.token; }); //请求设备人员信息 bridge.sdk.queryPersonInfo((args) async { ef.log('queryPersonInfo: $args'); // bluetooth = args[0]; return personList; }); //请求实时体征数据 bridge.sdk.queryInstantData((args) async { ef.log('queryInstantData: $args'); // bluetooth = args[0]; return instantData; }); bridge.sdk.startTimer((args) async { ef.log('开启定时: $args'); MHTHomeController homeController = Get.find(); homeController.startTimer(args); return true; }); bridge.sdk.cancelTimer((args) async { ef.log('退出定时: $args'); MHTHomeController homeController = Get.find(); homeController.cancelTimer(args); return true; }); bridge.sdk.restoreTimer((args) async { ef.log('更新定时: $args[0]'); ef.log('更新定时: $args'); MHTHomeController homeController = Get.find(); var data = await homeController.restoreTimer(args); return data; }); bridge.sdk.toBindDevice((args) async { ef.log('绑定设备: $args'); Get.toNamed("/mHTDeviceTypePage"); return true; }); bridge.sdk.saveSleepHabit((args) async { ef.log('更新睡眠习惯: $args[0]'); try { MHTBlueToothController blueToothController = Get.find(); return await blueToothController.saveHabitData(args[0]); } catch (e) { ef.log("[更新睡眠习惯失败]:$e"); } return true; }); bridge.sdk.loadSleepHabit((args) async { ef.log('查询睡眠习惯: $args[0]'); try { MHTBlueToothController blueToothController = Get.find(); var sleepData = await blueToothController.loadHabitDataApi(args[0]); return sleepData['data']; } catch (e) { ef.log("[查询睡眠习惯失败]:$e"); } return true; }); //-- bridge.sdk.saveMattressTimeData((args) async { ef.log('更新睡眠习惯: $args[0]'); try { MHTBlueToothController blueToothController = Get.find(); await blueToothController.saveMattressTimeData(args[0]); } catch (e) { ef.log("[更新睡眠习惯失败]:$e"); } return true; }); bridge.sdk.loadMattressTimeData((args) async { ef.log('查询睡眠习惯: $args[0]'); try { MHTBlueToothController blueToothController = Get.find(); var sleepData = await blueToothController.loadMattressTimeData(args[0]); return sleepData['data']; } catch (e) { ef.log("[查询睡眠习惯失败]:$e"); } return true; }); bridge.sdk.saveMemory((args) async { ef.log('更新记忆: $args[0]'); try { MHTBlueToothController blueToothController = Get.find(); // await blueToothController.saveHabitData(args[0]); await blueToothController.saveJiYiData(args[0]); } catch (e) { ef.log("[更新记忆失败]:$e"); } return true; }); bridge.sdk.loadMemory((args) async { try { ef.log('查询记忆: $args[0]'); MHTBlueToothController blueToothController = Get.find(); var sleepData = await blueToothController.loadJiYiData(args[0]); return sleepData['data']; } catch (e) { ef.log("[查询记忆失败]:$e"); } return true; }); bridge.sdk.webPageBuild((args) async { ef.log('网页加载完成: $args[0]'); try { initFlag.value = true; return true; } catch (e) { ef.log("[网页加载失败]:$e"); } return true; }); bridge.sdk.updatePermisson((args) async { // ef.log('获取权限: $args[0]'); try { if (MainPageBBottomChange.getCurrentIndex() != 2) { return false; } Map data = await checkBlueToothPermissin(); if (data == null || data.isEmpty) { return false; } if (Platform.isIOS) { if (!data['bluetoothScanGranted'] || !data['bluetoothConnectGranted']) { return false; } } else if (Platform.isAndroid) { if (!data['bluetoothScanGranted'] || !data['bluetoothConnectGranted'] || !data['locationGranted']) { return false; } } else { return false; } return true; } catch (e) { ef.log("[网页加载失败]:$e"); } return true; }); // bridge.sdk.bluetoothConnect((args) async { // ef.log('[蓝牙连接失败]: $args[0]'); // try { // selectDevice['blueToothStatus'] = 2; // return true; // } catch (e) { // ef.log("[蓝牙连接失败]:$e"); // } // return true; // }); bridge.sdk.loadFinish((args) async { ef.log('[资源加载完成]: $args[0]'); try { loadRecource.value = true; updateAll(); return true; } catch (e) { ef.log("[资源加载完成]:$e"); } return true; }); bridge.sdk.sendCommand((args) async { ef.log('wifi控制指令: $args[0]'); try { MHTBlueToothController blueToothController = Get.find(); // await blueToothController.saveHabitData(args[0]); await blueToothController.sendCommand(args[0]); } catch (e) { ef.log("[wifi控制下发指令失败]:$e"); } return true; }); }); } catch (e, s) { ef.log('$e,$s'); } } late WebviewHelper web; var ready = false.obs; var cnt = 0.obs; var loadRecource = false.obs; int sourceTime = 0; Future queryPersonInfoByMac() async { UserInfoController userInfoController = Get.find(); String serviceAddress = ServiceConstant.service_address; String serviceName = ServiceConstant.server_service; String serviceApi = ServiceConstant.person_info; String queryUrl = "${serviceAddress}${serviceName}${serviceApi}?mac=${selectDevice['mac']}"; try { final res = await requestWithLog( logTitle: "查询设备绑定人员列表".tr, method: MyHttpMethod.get, queryUrl: queryUrl, onSuccess: (res) { personList = res.data; }); } catch (e) { print("查询设备绑定列表失败: $e"); } } void dealInstantData(selectDevice) { //处理实时数据 // edm.EasyDartModule.websocket.sendData( // jsonEncode(WebSocketMessage(path: "/vsbs/web/rt/marttress", type: 2))); lastSelectDevice; var ws; ws = Easyws( url: ServiceConstant.webSocketService, onData: (data) { // ef.log("ws recv =>$data"); try { var tmp; if (data is String) { tmp = jsonDecode(data); } else if (data is Map) { tmp = data; // 直接用 } else { print("未知数据格式".tr); } if (tmp['data'] != null && tmp['data'] is Map) { var newData = tmp['data']; var mac = newData['mac'.tr]; if (mac != null) { // 删除已有的同 mac 项 instantData.removeWhere((element) => element['mac'.tr] == mac); // 添加新的数据 instantData.add(newData); } } } catch (e) { ef.log("ws error =>$e"); } }, onStateChange: (x) { ef.log("ws =>$x"); if (x == EasywsState.connected) { try { if (lastSelectDevice != null && lastSelectDevice.isNotEmpty && lastSelectDevice['mac'] != selectDevice['mac']) { List oldMacList = [ lastSelectDevice['bind_mac_a'], lastSelectDevice['bind_mac_b'], ]; for (String? mac in oldMacList) { if (mac != null && mac.isNotEmpty) { bool success = ws.send({ "type": 2, "path": "/vsbs/web/rt/marttress", "data": {"mac".tr: mac}, }); if (success) { // ef.log("✅ 已取消监听:$mac"); } } } } } catch (e) { ef.log("❌ 取消旧设备监听失败: $e"); } try { // 2. 开始监听新设备(A/B 都监听) if (selectDevice != null && selectDevice.isNotEmpty) { List newMacList = [ selectDevice['bind_mac_a'], selectDevice['bind_mac_b'], ]; for (String? mac in newMacList) { if (mac != null && mac.isNotEmpty) { bool success = ws.send({ "type": 1, "path": "/vsbs/web/rt/marttress", "data": {"mac".tr: mac}, }); if (success) { // ef.log("✅ 开始监听新设备:$mac"); } } } // 更新记录 lastSelectDevice = selectDevice; } } catch (e) { ef.log("❌ 监听新设备失败: $e"); } } }, ); ws.connect(); } void dealDeviceData(selectDevice) { // 处理设备本身的数据 var ws; ws = Easyws( url: ServiceConstant.webSocketService, onData: (data) { try { var tmp; if (data is String) { tmp = jsonDecode(data); } else if (data is Map) { tmp = data; // 直接用 } else { print("未知数据格式".tr); return; } if (tmp['data'] != null && tmp['data'] is Map) { var newData = tmp['data']; var mac = newData['mac']; String order = Base64Tool.decode(newData['data']); final webviewTestController = Get.find(); webviewTestController.web.jsbridge?.dart ?.updateDeviceStatusByWifi(order); } } catch (e) { ef.log("ws error =>$e"); } }, onStateChange: (x) { ef.log("ws状态变化 =>$x"); if (x == EasywsState.connected) { try { // 1. 先取消可能存在的旧设备监听 if (lastSelectDevice != null && lastSelectDevice.isNotEmpty) { String? oldMac = lastSelectDevice['mac']; if (oldMac != null && oldMac.isNotEmpty && oldMac != selectDevice['mac']) { bool success = ws.send({ "type": 2, // 取消监听 "path": "/vsbs/web/rt/marttress", // 注意:可能需要不同的路径 "data": {"mac": oldMac}, }); if (success) { ef.log("✅ 已取消监听旧设备:$oldMac"); } } } } catch (e) { ef.log("❌ 取消旧设备监听失败: $e"); } try { // 2. 开始监听新设备 if (selectDevice != null && selectDevice.isNotEmpty) { String? newMac = selectDevice['mac']; if (newMac != null && newMac.isNotEmpty) { bool success = ws.send({ "type": 1, // 开始监听 "path": "/vsbs/web/rt/marttress", // 设备数据的路径,可能需要确认 "data": {"mac": newMac}, }); if (success) { ef.log("✅ 开始监听设备:$newMac"); // 更新记录 lastSelectDevice = selectDevice; } } } } catch (e) { ef.log("❌ 监听新设备失败: $e"); } } }, ); ws.connect(); } @override void onInit() { ef.log("webview test init =>${DateTime.now()}"); super.onInit(); try { web.runApp('mhtControl').then((x) { ready.value = true; }); // web // .network('https://wyf.it.real.he-info.cn:94/goods-front/index.html') // .then((x) { // ready.value = true; // }); } catch (e, s) { ef.log('$e,$s'); } } } class WebviewTestView extends GetComponent { WebviewTestView({super.key, super.oncreate}); @override WebviewTestController newinstance() { if (ef.kvRoot.WebviewTestController == null) { ef.kvRoot.WebviewTestController = WebviewTestController(); if (Get.isRegistered() == false) { Get.put(ef.kvRoot.WebviewTestController); WebviewTestController webviewTestController = Get.find(); webviewTestController.global = true; return webviewTestController; } } return ef.kvRoot.WebviewTestController; } @override Widget build(BuildContext context) { // controller.webviewWidget = controller.web.build(); return Obx(() { UserInfoController userInfoController = Get.find(); MHTHomeController deviceController = Get.find(); final isLoggedIn = userInfoController.model.login == 1; final hasDevice = deviceController.deviceList.values.isNotEmpty; final showWeb = isLoggedIn && hasDevice; final mainAxisAlignment = showWeb ? MainAxisAlignment.start : MainAxisAlignment.center; return Scaffold( backgroundColor: Colors.transparent, body: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max, mainAxisAlignment: mainAxisAlignment, children: [ // 未登录提示 if (!isLoggedIn) Center( child: InkWell( onTap: () => Get.toNamed("/loginPage"), child: RichText( text: TextSpan( children: [ TextSpan( text: "请先".tr, style: TextStyle( color: Colors.white, fontSize: 30.rpx, ), ), WidgetSpan( child: Stack( children: [ Text( "登录".tr, style: TextStyle( color: stringToColor("#84F5FF"), fontSize: 30.rpx, ), ), Positioned( bottom: 0, left: 0, right: 0, child: Container( height: 1, color: stringToColor("#84F5FF"), ), ), ], ), ), TextSpan( text: "后,再进行设备控制".tr, style: TextStyle( color: Colors.white, fontSize: 30.rpx, ), ), ], ), ), ), ), // 已登录但无设备提示 if (isLoggedIn && !hasDevice) Center( child: InkWell( onTap: () => Get.toNamed("/mHTDeviceTypePage"), child: RichText( text: TextSpan( children: [ TextSpan( text: "请先".tr, style: TextStyle( color: Colors.white, fontSize: 30.rpx, ), ), WidgetSpan( child: Stack( children: [ Text( "绑定设备".tr, style: TextStyle( color: stringToColor("#84F5FF"), fontSize: 30.rpx, ), ), Positioned( bottom: 0, left: 0, right: 0, child: Container( height: 1, color: stringToColor("#84F5FF"), ), ), ], ), ), TextSpan( text: "后,再进行设备控制".tr, style: TextStyle( color: Colors.white, fontSize: 30.rpx, ), ), ], ), ), ), ), // 显示 WebView if (showWeb) Expanded( child: Align( alignment: Alignment.topLeft, child: Obx(() { // return (controller.ready.value && (MainPageBBottomChange.getCurrentIndex() == 2)) // return (controller.ready.value && (MainPageBBottomChange.getCurrentIndex() == 2)&& controller.loadRecource.value == true) // return Stack(children: [ // controller.ready.value // ? controller.web.build() // : Container(), // Text("1222121212"), // ]); // return Stack( // children: [ // controller.ready.value // ? controller.web.build() // : Container(), // // 只有 loadRecource == false 时才显示覆盖文字 // if (!controller.loadRecource.value) // Positioned.fill( // child: Container( // color: Colors.black54, // 半透明遮罩,可选 // alignment: Alignment.center, // child: Text( // "资源加载中...", // style: TextStyle( // color: Colors.red, // fontSize: 30.rpx, // fontWeight: FontWeight.bold, // ), // ), // ), // ), // ], // ); return Stack( children: [ controller.ready.value ? controller.web.build() : Container(), if (!controller.loadRecource.value) Positioned.fill( child: Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage( 'assets/images/new_background.png'), fit: BoxFit.fill, ), ), alignment: Alignment.center, child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Colors.white), ), ], ), ), ), ], ); }), ), ), ], ), ); }); } }