import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; import 'package:date_format/date_format.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:star_lock/blue/io_protocol/io_getPrivateKey.dart'; import 'package:star_lock/blue/io_protocol/io_getPublicKey.dart'; import 'package:star_lock/blue/io_protocol/io_otaUpgrade.dart'; import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart'; import 'package:star_lock/blue/io_type.dart'; import 'package:star_lock/main/lockDetail/lockSet/lockTime/getServerDatetime_entity.dart'; import 'package:star_lock/mine/addLock/nearbyLock/nearbyLock_page.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import 'package:star_lock/widget/permission/permission_dialog.dart'; import '../../../appRouters.dart'; import '../../../app_settings/app_settings.dart'; import '../../../blue/blue_manage.dart'; import '../../../blue/io_protocol/io_getStarLockStatusInfo.dart'; import '../../../blue/io_reply.dart'; import '../../../blue/io_tool/io_tool.dart'; import '../../../blue/io_tool/manager_event_bus.dart'; import '../../../blue/sender_manage.dart'; import '../../../network/api_repository.dart'; import '../../../tools/storage.dart'; import 'nearbyLock_state.dart'; class NearbyLockLogic extends BaseGetXController { final NearbyLockState state = NearbyLockState(); int otaCount = 0; int otaIndex = 0; Uint8List? otaBin; int startSecond = 0; Map? headJson; String? deviceName; StreamSubscription? _replySubscription; // 点击连接设备 void connect(String deviceName) { showTitleEasyLoading('${'获取锁信息'.tr} 1/3'); showBlueConnetctToastTimer(action: () { dismissEasyLoading(); }); BlueManage().blueSendData(deviceName, (BluetoothConnectionState state) async { // AppLog.log('点击要添加的设备了'); if (state == BluetoothConnectionState.connected) { // AppLog.log('开始获取公钥'); IoSenderManage.getPublicKey(lockId: deviceName); } else if (state == BluetoothConnectionState.disconnected) { dismissEasyLoading(); } }, isAddEquipment: true); } void _initReplySubscription() { _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) { if (reply is GetPublicKeyReply) { _replyGetPublicKey(reply); } if (reply is GetPrivateKeyReply) { _replyGetPrivateKeyKey(reply); } // 获取锁状态信息 if (reply is GetStarLockStatuInfoReply && state.ifCurrentScreen.value) { _replyGetStarLockStatusInfo(reply); } if (reply is OTAUpgradeReply) { if (reply.status == 0x00) { //验证通过,开始发送数据包 dismissEasyLoading(); startOTAData(); processOtaUpgrade(); } else if (reply.status == 0x06) { blueOTAUpgrade(headJson!, reply.token); } } else if (reply is ProcessOtaUpgradeReply && reply.status == 0x00) { otaIndex++; processOtaUpgrade(); } else if (reply is ConfirmationOTAUpgradeReply && reply.status == 0x00) { showToast('固件升级完成'.tr); closeOTADAta(); } }); } Future _replyGetPublicKey(Reply reply) async { // 获取公钥 switch (reply.status) { case 0x00: //成功 // AppLog.log('获取公钥成功'); // 储存公钥 final List publicKey = reply.data.sublist(3); final List saveStrList = changeIntListToStringList(publicKey); Storage.setStringList(saveBluePublicKey, saveStrList); // 获取私钥 // AppLog.log('开始获取私钥'); showTitleEasyLoading('${'获取锁信息'.tr} 2/3'); IoSenderManage.getPrivateKey( lockId: BlueManage().connectDeviceName, keyID: '1', authUserID: await Storage.getUid(), nowTime: state.serverTime, publicKeyData: publicKey, needAuthor: 1); break; default: AppLog.log('获取公钥失败'); break; } } Future _replyGetPrivateKeyKey(Reply reply) async { switch (reply.status) { case 0x00: // AppLog.log('获取私钥成功'); //成功 reply.data.removeAt(0); // 私钥 final List privateKey = reply.data.sublist(0, 16); final List savePrivateKeyList = changeIntListToStringList(privateKey); Storage.setStringList(saveBluePrivateKey, savePrivateKeyList); // signKey final List signKey = reply.data.sublist(16, 32); final List saveSignKeyList = changeIntListToStringList(signKey); Storage.setStringList(saveBlueSignKey, saveSignKeyList); // 时间戳 final List timestamp = reply.data.sublist(32, 36); state.timestampValue = (0xff & timestamp[0]) << 24 | (0xff & timestamp[1]) << 16 | (0xff & timestamp[2]) << 8 | (0xFF & timestamp[3]); showTitleEasyLoading('${'获取锁信息'.tr} 3/3'); _getStarLockStatus(); break; default: break; } } // 获取星锁状态 Future _replyGetStarLockStatusInfo(Reply reply) async { final int status = reply.data[2]; switch (status) { case 0x00: //成功 // AppLog.log('获取锁状态成功'); // 厂商名称 int index = 3; final List vendor = reply.data.sublist(index, index + 20); final String vendorStr = utf8String(vendor); state.lockInfo['vendor'] = vendorStr; // state.lockInfo["vendor"] = "XL"; index = index + 20; // AppLog.log('厂商名称 vendorStr:$vendorStr'); // 锁设备类型 final int product = reply.data[index]; state.lockInfo['product'] = product; index = index + 1; // AppLog.log('锁设备类型 product:$product'); // 产品名称 final List model = reply.data.sublist(index, index + 20); final String modelStr = utf8String(model); state.lockInfo['model'] = modelStr; // state.lockInfo["model"] = "JL-BLE-01"; index = index + 20; // AppLog.log('产品名称 mmodelStr:$modelStr'); // 软件版本 final List fwVersion = reply.data.sublist(index, index + 20); final String fwVersionStr = utf8String(fwVersion); state.lockInfo['fwVersion'] = fwVersionStr; index = index + 20; // AppLog.log('软件版本 fwVersionStr:$fwVersionStr'); // 硬件版本 final List hwVersion = reply.data.sublist(index, index + 20); final String hwVersionStr = utf8String(hwVersion); state.lockInfo['hwVersion'] = hwVersionStr; index = index + 20; // AppLog.log('硬件版本 hwVersionStr:$hwVersionStr'); // 厂商序列号 final List serialNum0 = reply.data.sublist(index, index + 16); final String serialNum0Str = utf8String(serialNum0); state.lockInfo['serialNum0'] = serialNum0Str; // state.lockInfo["serialNum0"] = "${DateTime.now().millisecondsSinceEpoch ~/ 10}"; index = index + 16; AppLog.log('厂商序列号 serialNum0Str:$serialNum0Str'); // 成品商序列号 final List serialNum1 = reply.data.sublist(index, index + 16); final String serialNum1Str = utf8String(serialNum1); state.lockInfo['serialNum1'] = serialNum1Str; index = index + 16; // AppLog.log('成品商序列号 serialNum1Str:$serialNum1Str'); // 蓝牙名称 final List btDeviceName = reply.data.sublist(index, index + 16); final String btDeviceNameStr = utf8String(btDeviceName); state.lockInfo['btDeviceName'] = btDeviceNameStr; index = index + 16; // AppLog.log('蓝牙名称 btDeviceNameStr:$btDeviceNameStr'); // 电池剩余电量 final int battRemCap = reply.data[index]; state.lockInfo['electricQuantity'] = battRemCap; index = index + 1; // AppLog.log('电池剩余电量 battRemCap:$battRemCap'); // 备用电池剩余电量 final int battRemCapStandby = reply.data[index]; state.lockInfo['electricQuantityStandby'] = battRemCapStandby; index = index + 1; // AppLog.log('电池剩余电量 battRemCap:$battRemCap'); // 重置次数 final List restoreCounter = reply.data.sublist(index, index + 2); state.lockInfo['restoreCount'] = restoreCounter[0] * 256 + restoreCounter[1]; index = index + 2; // AppLog.log('重置次数 restoreCounter:${restoreCounter[0] * 256 + restoreCounter[1]}'); // 重置时间 final List restoreDate = reply.data.sublist(index, index + 4); final int restoreDateValue = (0xff & restoreDate[0]) << 24 | (0xff & restoreDate[1]) << 16 | (0xff & restoreDate[2]) << 8 | (0xFF & restoreDate[3]); // String restoreDateStr = DateTool().dateToYMDHNSString(restoreDateValue.toString()); state.lockInfo['restoreDate'] = restoreDateValue * 1000; index = index + 4; // AppLog.log('重置时间 restoreDateValue:$restoreDateValue'); // 主控芯片型号 final List icPartNo = reply.data.sublist(index, index + 10); final String icPartNoStr = utf8String(icPartNo); state.lockInfo['icPartNo'] = icPartNoStr; index = index + 10; // AppLog.log('主控芯片型号 icPartNoStr:$icPartNoStr'); // 有效时间 final List indate = reply.data.sublist(index, index + 4); final int indateValue = (0xff & indate[0]) << 24 | (0xff & indate[1]) << 16 | (0xff & indate[2]) << 8 | (0xFF & indate[3]); // String indateStr = DateTool().dateToYMDHNSString("$indateValue"); state.lockInfo['indate'] = indateValue * 1000; index = index + 4; // AppLog.log('有效时间 indateValue:$indateValue'); // mac地址 final List macAddress = reply.data.sublist(index, index + 20); final String macAddressStr = utf8String(macAddress); state.lockInfo['mac'] = macAddressStr; index = index + 20; // AppLog.log('mac地址 macAddressStr:$macAddressStr'); //时区偏移 state.lockInfo['timezoneOffset'] = DateTime.now().timeZoneOffset.inSeconds; // 锁特征值字符串长度 final int featureValueLength = reply.data[index]; index = index + 1; AppLog.log('锁特征值字符串长度 featureValueLength:$featureValueLength'); // 锁特征值说明(本机能支持的功能) // 获取到锁给的字符数组 final int featureNetxLength = index + featureValueLength; if (reply.data.length < featureNetxLength) { showToast('锁数据异常,请重试'.tr); return; } final List featureValue = reply.data.sublist(index, index + featureValueLength); final String featureValueStr = asciiString(featureValue); state.featureValue = featureValueStr; // List allFeatureValueTwoList = charListChangeIntList(featureValue); // AppLog.log("featureValueLength:$featureValueLength featureValue:$featureValue \n featureValueStr:$featureValueStr"); index = index + featureValueLength; AppLog.log('锁特征值字符串 featureValueStr:$featureValueStr'); // 使能特征值字符串长度 final int featureEnValLength = reply.data[index]; index = index + 1; AppLog.log('使能特征值字符串长度 featureEnValLength:$featureEnValLength'); // 使能锁特征值说明(本机启用的功能) final int featureEnNextLength = index + featureEnValLength; if (reply.data.length < featureEnNextLength) { showToast('锁数据异常,请重试'.tr); return; } final List featureEnVal = reply.data.sublist(index, index + featureEnValLength); final String featureEnValStr = asciiString(featureEnVal); state.featureSettingValue = featureEnValStr; // List allFeatureEnValTwoList = charListChangeIntList(featureEnVal); // AppLog.log("featureEnValLength:$featureEnValLength featureEnVal:$featureEnVal \n featureEnValStr:$featureEnValStr"); index = index + featureEnValLength; AppLog.log('使能锁特征值说明 featureEnValStr:$featureEnValStr'); // 支持的带参数特征值的总条目数 // var featureParaTotal = reply.data[index]; final List featureParaTotalList = reply.data.sublist(index); state.featureSettingParams = featureParaTotalList; AppLog.log('featureParaTotalList:$featureParaTotalList'); Get.toNamed(Routers.lockAddressGaoDePage, arguments: { 'pwdTimestamp': state.timestampValue * 1000, 'lockInfo': state.lockInfo, 'featureValue': state.featureValue, 'featureSettingValue': state.featureSettingValue, 'featureSettingParams': state.featureSettingParams, }); break; case 0x06: //无权限 final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List getPrivateKeyList = changeStringListToIntList(privateKey!); IoSenderManage.senderGetStarLockStatuInfo( lockID: BlueManage().connectDeviceName, userID: await Storage.getUid(), utcTimeStamp: state.serverTime, unixTimeStamp: getLocalTime(), isBeforeAddUser: true, privateKey: getPrivateKeyList, ); break; default: //失败 break; } } // 获取锁状态 Future _getStarLockStatus() async { // 进来之后首先连接 // BlueManage().bludSendData(BlueManage().connectDeviceName, (BluetoothConnectionState state) async { // if (state == BluetoothConnectionState.connected) { // dismissEasyLoading(); // AppLog.log('开始获取锁状态'); final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List getPrivateKeyList = changeStringListToIntList(privateKey!); final String getUTCDate = formatDate( DateTime.fromMillisecondsSinceEpoch(state.serverTime * 1000), [yyyy, '-', mm, '-', dd, ' ', HH, ':', nn, ':', ss]); final String getLocalDate = formatDate( DateTime.fromMillisecondsSinceEpoch(getLocalTime() * 1000), [yyyy, '-', mm, '-', dd, ' ', HH, ':', nn, ':', ss]); // AppLog.log('state.serverTime:${state.serverTime} getUTCDate:$getUTCDate ' // 'getLocalTime:${getLocalTime()} getLocalDate:$getLocalDate ' // '差值:${getLocalTime() - state.serverTime}'); IoSenderManage.senderGetStarLockStatuInfo( lockID: BlueManage().connectDeviceName, userID: await Storage.getUid(), utcTimeStamp: state.serverTime, unixTimeStamp: getLocalTime(), isBeforeAddUser: true, privateKey: getPrivateKeyList, ); } void startScanBlueList() { BlueManage().startScan(2000, DeviceType.blue, (List list) { state.devices.clear(); for (int i = 0; i < list.length; i++) { final ScanResult device = list[i]; if ((device.advertisementData.serviceUuids.isNotEmpty ? device.advertisementData.serviceUuids[0] : '') .toString()[31] != '1') { state.devices.add(list[i]); } } }); } void stopScanBlueList() { BlueManage().disconnect(); BlueManage().stopScan(); } // 点击连接设备,升级 ota 固件 void oTAUpgrade(String deviceName) { showTitleEasyLoading('连接设备中...'.tr); this.deviceName = deviceName; BlueManage().blueSendData(deviceName, (BluetoothConnectionState state) async { // AppLog.log('连接设备'); if (state == BluetoothConnectionState.connected) { // AppLog.log('连接成功'); dismissEasyLoading(); otaUpdate(); } else if (state == BluetoothConnectionState.disconnected) { // AppLog.log('连接失败'); dismissEasyLoading(); } }, isAddEquipment: true); } //手动升级 Future otaUpdate() async { final bool status = await PermissionDialog.requestStorage(); if (status != true) { return; } final FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result == null || result.files.single.path is! String) { return; } final File file = File(result.files.single.path!); final Uint8List data = await file.readAsBytes(); headJson = await getHeadFile(data); if (headJson is! Map) { return; } otaBin = await checkFile(data, headJson!); if (otaBin == null) { return; } final String md5Str = md5.convert(otaBin!).toString(); headJson!['fwMd5'] = md5Str; blueOTAUpgrade(headJson!, [0, 0, 0, 0]); } //蓝牙操作 ota 升级 void blueOTAUpgrade(Map data, List token) { if (deviceName == null) { AppLog.log('blueOTAUpgrade:设备名字为 null'); return; } BlueManage().blueSendData(deviceName!, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { final String uid = await Storage.getUid() ?? ''; BlueManage().writeCharacteristicWithResponse(OTAUpgradeCommand( lockID: deviceName, userID: uid, keyID: deviceName, platform: int.tryParse(data['platform']) ?? 0, product: int.tryParse(data['product']) ?? 0, hwVersion: data['hwVersion'], fwVersion: data['fwVersion'], fwSize: data['fwSize'], fwMD5: data['fwMd5'], needAuthor: 1, token: token, encrypt: false, ).packageData()); } else if (deviceConnectionState == BluetoothConnectionState.disconnected) {} }, isAddEquipment: true); } //循环传输升级固件包 Future processOtaUpgrade() async { if (!state.otaUpdateIng.value) { return; } final int length = otaBin?.length ?? 0; if (otaCount == 0) { //首次 final int difference = length % 240; otaCount = length ~/ 240 + (difference > 0 ? 1 : 0); startSecond = DateTime.now().millisecondsSinceEpoch; } if (otaCount <= otaIndex) { final int now = DateTime.now().millisecondsSinceEpoch; // final String msg = '传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex '; closeOTADAta(); AppLog.log( '传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex '); // showToast(msg); return; } final int star = otaIndex * 240; int end = (otaIndex + 1) * 240; if (end > length) { end = length; } final int size = end - star; final List data = otaBin!.sublist(star, end); state.otaProgress.value = otaIndex / otaCount; await BlueManage().writeCharacteristicWithResponse( ProcessOtaUpgradeCommand(index: otaIndex, size: size, data: data) .packageData()); } //开始 ota升级 void startOTAData() { state.otaUpdateIng.value = true; state.oTAProgressDialog = true; Get.dialog( OTAProgressDialog( logic: this, ), barrierDismissible: false) .then((dynamic value) => state.oTAProgressDialog = false); } //清楚 ata 安装文件 void closeOTADAta() { if (state.oTAProgressDialog) { Get.back(); } state.otaUpdateIng.value = false; state.otaProgress.value = 0; otaIndex = 0; otaCount = 0; startSecond = 0; otaBin = null; } // 拦截返回事件 void getBack() { if (state.otaUpdateIng.value) { closeOTADAta(); } else { Get.back(); } } // 检查文件头 Future getHeadFile(Uint8List data) async { if (data.length <= 16) { showToast('错误固件,请选择正确的文件'.tr); return null; } // 检查文件头 String header; try { header = utf8.decode(data.sublist(0, 12)); } catch (e) { showToast('非SYD固件,请选择正确的文件'.tr); return null; } if (header != 'SYD-BIN-DATA') { showToast('非SYD固件,请选择正确的文件'.tr); return null; } // 解析元数据长度 Uint8List metaLenList; int metaLen; try { metaLenList = data.sublist(12, 16); metaLen = ByteData.sublistView(metaLenList).getUint32(0); } catch (e) { showToast('文件校验失败 0x01'.tr); return null; } if (metaLen < 2 || metaLen > 10240) { showToast('文件校验失败 0x01'.tr); return null; } // 读取和解析元数据 Uint8List metaStrList; String metaStr; try { metaStrList = data.sublist(16, 16 + metaLen); metaStr = utf8.decode(metaStrList); } catch (e) { showToast('解析元数据失败,请选择正确的文件'.tr); return null; } AppLog.log(metaStr); final meta = jsonDecode(metaStr); if (meta is! Map) { showToast('解析元数据失败,请选择正确的文件'.tr); return null; } return meta..['metaLen'] = metaLen; } //检测升级文件并读取 bin Future checkFile(Uint8List data, Map meta) async { final num binOffset = 16 + (meta['metaLen'] ?? 0); // 获取固件数据部分 final Uint8List bin = data.sublist(binOffset.toInt(), data.length); //md5 校验有问题,暂时不解析 final String md5Str = md5.convert(bin).toString().toUpperCase(); // AppLog.log('---> $md5Str ${meta['fwMd5']}'); if (md5Str != meta['fwMd5']) { showToast('文件校验失败 0x02'.tr); return null; } if (bin.length != meta['fwSize']) { showToast('文件校验失败 0x03'.tr); return null; } return bin; } // 从服务器获取锁的时间 开锁时传入 Future getServerDatetime() async { final GetServerDatetimeEntity entity = await ApiRepository.to.getServerDatetimeData(isUnShowLoading: false); if (entity.errorCode!.codeIsSuccessful) { state.serverTime = entity.data!.date! ~/ 1000; if (state.otaState.value) { oTAUpgrade(state.selectLockName.value); } else { connect(state.selectLockName.value); } } } int getLocalTime() { final DateTime now = DateTime.now(); final Duration timeZoneOffset = now.timeZoneOffset; AppLog.log('timeZoneOffset.inSeconds:$timeZoneOffset.inSeconds'); return state.serverTime + timeZoneOffset.inSeconds; } @override void onReady() { super.onReady(); getNearByLimits(); } @override void onInit() { super.onInit(); } @override void onClose() { super.onClose(); _replySubscription?.cancel(); } Future getNearByLimits() async { if (!Platform.isIOS) { // bool bluetoothRequest = false; // try { // bluetoothRequest = await PermissionDialog.requestBluetooth(); // AppLog.log('bluetoothRequest:$bluetoothRequest'); // if (!bluetoothRequest) { // return; // } // } catch (e) { // AppLog.log('bluetoothRequest:$e'); // } final bool bluetoothRequest = await PermissionDialog.requestBluetooth(); final bool locationRequest = await PermissionDialog.request(Permission.location); AppLog.log('locationRequest:$locationRequest'); if (!bluetoothRequest || !locationRequest) { return; } } _initReplySubscription(); state.ifCurrentScreen.value = true; startScanBlueList(); } }