diff --git a/star_lock/images/lan/lan_en.json b/star_lock/images/lan/lan_en.json index f56c79b8..7a5e3144 100644 --- a/star_lock/images/lan/lan_en.json +++ b/star_lock/images/lan/lan_en.json @@ -796,4 +796,14 @@ "购买":"Buy", "实名认证为付费功能,请购买后再使用":"Real-name authentication is a paid function, please use it after purchase" "密码不一致哦":"The passwords are inconsistent", + "点击返回设备配对":"Tap Back to device pairing", + "无法连接?尝试升级":"Can't connect?Upgrade attempted", + "固件升级提示":"Firmware upgrade prompt", + "请先获取固件文件到手机本地,再选择升级":"Please obtain the firmware file to the local phone first, and then select Upgrade", + "固件升级中":"The firmware is being upgraded", + "取消升级":"Cancel the upgrade", + "固件传输中":"Firmware in transit", + "关闭":"Shut down", + "传输中'":"In transit" + } diff --git a/star_lock/images/lan/lan_keys.json b/star_lock/images/lan/lan_keys.json index 64c7c078..ef985320 100644 --- a/star_lock/images/lan/lan_keys.json +++ b/star_lock/images/lan/lan_keys.json @@ -822,5 +822,14 @@ "实名认证":"实名认证", "当前剩余数量":"当前剩余数量", "购买":"购买", - "实名认证为付费功能,请购买后再使用":"实名认证为付费功能,请购买后再使用" + "实名认证为付费功能,请购买后再使用":"实名认证为付费功能,请购买后再使用", + "点击返回设备配对":"点击返回设备配对", + "无法连接?尝试升级":"无法连接?尝试升级", + "固件升级提示":"固件升级提示", + "请先获取固件文件到手机本地,再选择升级":"请先获取固件文件到手机本地,再选择升级", + "固件升级中":"固件升级中", + "取消升级":"取消升级", + "固件传输中":"固件传输中", + "关闭":"关闭", + "传输中'":"传输中" } diff --git a/star_lock/images/lan/lan_zh.json b/star_lock/images/lan/lan_zh.json index 11104186..d8e7bc35 100644 --- a/star_lock/images/lan/lan_zh.json +++ b/star_lock/images/lan/lan_zh.json @@ -825,5 +825,16 @@ "实名认证":"实名认证", "当前剩余数量":"当前剩余数量", "购买":"购买", - "实名认证为付费功能,请购买后再使用":"实名认证为付费功能,请购买后再使用" + "实名认证为付费功能,请购买后再使用":"实名认证为付费功能,请购买后再使用", + "点击返回设备配对":"点击返回设备配对", + "无法连接?尝试升级":"无法连接?尝试升级", + "固件升级提示":"固件升级提示", + "请先获取固件文件到手机本地,再选择升级":"请先获取固件文件到手机本地,再选择升级", + "固件升级中":"固件升级中", + "取消升级":"取消升级", + "固件传输中":"固件传输中", + "关闭":"关闭", + "传输中'":"传输中" + + } diff --git a/star_lock/images/ota_upgrade_icon.png b/star_lock/images/ota_upgrade_icon.png new file mode 100644 index 00000000..14618cb5 Binary files /dev/null and b/star_lock/images/ota_upgrade_icon.png differ diff --git a/star_lock/ios/Flutter/AppFrameworkInfo.plist b/star_lock/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/star_lock/ios/Flutter/AppFrameworkInfo.plist +++ b/star_lock/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/star_lock/lib/blue/blue_manage.dart b/star_lock/lib/blue/blue_manage.dart index 0c34dd25..2f723984 100644 --- a/star_lock/lib/blue/blue_manage.dart +++ b/star_lock/lib/blue/blue_manage.dart @@ -145,7 +145,8 @@ class BlueManage { _connectionStateSubscription?.cancel(); _connectionStateSubscription = null; - _connectionStateSubscription = bluetoothConnectDevice!.connectionState.listen((state) async { + _connectionStateSubscription = + bluetoothConnectDevice!.connectionState.listen((state) async { bluetoothConnectionState = state; // AppLog.log("蓝牙连接状态:$state"); }); @@ -214,7 +215,9 @@ class BlueManage { completer.complete(); } }, onError: (e) { - AppLog.log("扫描失败:$e",); + AppLog.log( + "扫描失败:$e", + ); }); FlutterBluePlus.cancelWhenScanComplete(subscription); await completer.future; @@ -281,7 +284,9 @@ class BlueManage { // EventBusManager().eventBusFir(scanDevices); // FlutterBluePlus.stopScan(); }, onError: (e) { - AppLog.log("扫描失败:$e",); + AppLog.log( + "扫描失败:$e", + ); }); FlutterBluePlus.cancelWhenScanComplete(subscription); @@ -304,7 +309,9 @@ class BlueManage { } /// 调用发送数据 List senderData, - Future bludSendData(String deviceName, ConnectStateCallBack stateCallBack, {bool isAddEquipment = false}) async { + Future bludSendData( + String deviceName, ConnectStateCallBack stateCallBack, + {bool isAddEquipment = false}) async { FlutterBluePlus.isSupported.then((isAvailable) async { if (isAvailable) { if (_adapterState == BluetoothAdapterState.on) { @@ -336,21 +343,13 @@ class BlueManage { {bool isAddEquipment = false}) async { connectDeviceName = deviceName; List devicesList = scanDevices; - //判断列表里面有设备则不开启扫描 - bool isExistDevice = scanDevices.any((element) => - element.device.platformName == connectDeviceName || - element.advertisementData.advName == connectDeviceName); - if (isAddEquipment == false && isExistDevice == false) { - // AppLog.log("需要开启扫描"); - // startScan(10, (scanDevices){ - startScanSingle(deviceName, 10, (List scanDevices) { - // AppLog.log("扫描到的设备:$scanDevices"); - devicesList = scanDevices; + if (isAddEquipment == false) { + //取消缓存直接使用,存在配对场景设备信息会更变 + startScan(10, (List scanDevices) { _connectDevice(devicesList, deviceName, connectStateCallBack, isAddEquipment: isAddEquipment); }); } else { - // AppLog.log("不需要开启扫描"); _connectDevice(devicesList, deviceName, connectStateCallBack, isAddEquipment: isAddEquipment); } @@ -364,6 +363,7 @@ class BlueManage { final knownDeviceIndex = devicesList.indexWhere((d) => (d.device.platformName == deviceName) || (d.advertisementData.advName == deviceName)); + if (knownDeviceIndex >= 0) { // 存在的时候赋值 connectDeviceMacAddress = @@ -375,6 +375,7 @@ class BlueManage { // AppLog.log('bluetoothConnectDevice: $bluetoothConnectDevice'); scanResult = devicesList[knownDeviceIndex]; + _initGetMtuSubscription(); _initListenConnectionState(); } @@ -446,7 +447,8 @@ class BlueManage { } on Exception catch (e) { bluetoothConnectionState = BluetoothConnectionState.disconnected; connectStateCallBack(bluetoothConnectionState!); - AppLog.log('发现设备时失败 e:$e bluetoothConnectionState:$bluetoothConnectionState'); + AppLog.log( + '发现设备时失败 e:$e bluetoothConnectionState:$bluetoothConnectionState'); rethrow; } } @@ -461,7 +463,7 @@ class BlueManage { _subScribeToCharacteristic(BluetoothCharacteristic characteristic) async { final subscription = characteristic.onValueReceived.listen((data) { - // AppLog.log("订阅获取的数据:$data"); + AppLog.log("订阅获取的数据:$data"); if (data == lastTimeData || data.isEmpty) { return; } else { @@ -477,7 +479,7 @@ class BlueManage { // 判断是否需要分包 dataLen = data[8] * 256 + data[9]; // 高16位用来指示后面数据块内容的长度 // AppLog.log("dataLen1111:$dataLen getDataLength:${data.length} data:$data"); - if (dataLen! + 12 > data.length) { + if (dataLen! + 14 > data.length) { // 当前包的长度小于实际的包时 分包添加 不解析 allData.addAll(data); } else { @@ -555,15 +557,16 @@ class BlueManage { // 写入 Future writeCharacteristicWithResponse(List value) async { - - List services = await bluetoothConnectDevice!.discoverServices(); + List services = + await bluetoothConnectDevice!.discoverServices(); for (BluetoothService service in services) { // AppLog.log("33333 service.remoteId:${service.remoteId}" // " service.uuid:${service.uuid}\n\n" // " service.characteristics:${service.characteristics}\n\n" // " service.includedServices:${service.includedServices}"); if (service.uuid == _serviceIdConnect) { - for (BluetoothCharacteristic characteristic in service.characteristics) { + for (BluetoothCharacteristic characteristic + in service.characteristics) { // AppLog.log("44444 characteristic.remoteId:${characteristic.remoteId}" // " characteristic.uuid:${characteristic.uuid}\n\n" // " characteristic.secondaryServiceUuid:${characteristic @@ -578,7 +581,9 @@ class BlueManage { for (int i = 0; i < subData.length; i++) { if (characteristic.properties.writeWithoutResponse) { // 使用WRITE_NO_RESPONSE属性写入值 - await characteristic.write(subData[i],withoutResponse: true).then((value) async { + await characteristic + .write(subData[i], withoutResponse: true) + .then((value) async { // await Future.delayed(const Duration(milliseconds: 1)).then(( // value) async { // AppLog.log('分包发送成功了'); @@ -594,7 +599,8 @@ class BlueManage { }); } else { // 特性不支持写入 - throw Exception('This characteristic does not support writing.'); + throw Exception( + 'This characteristic does not support writing.'); } } } on Exception catch (e, s) { diff --git a/star_lock/lib/blue/io_protocol/io_otaUpgrade.dart b/star_lock/lib/blue/io_protocol/io_otaUpgrade.dart index c4b37137..3d358a74 100644 --- a/star_lock/lib/blue/io_protocol/io_otaUpgrade.dart +++ b/star_lock/lib/blue/io_protocol/io_otaUpgrade.dart @@ -26,6 +26,7 @@ class OTAUpgradeCommand extends SenderProtocol { List? signKey; List? privateKey; List? token; + bool encrypt; OTAUpgradeCommand( {this.lockID, @@ -40,10 +41,10 @@ class OTAUpgradeCommand extends SenderProtocol { this.needAuthor, this.signKey, this.privateKey, - this.token}) + this.token, + this.encrypt = true}) : super(CommandType.startOATUpgrade); - @override String toString() { return 'OTAUpgradeCommand{lockID: $lockID, userID: $userID, ' @@ -53,6 +54,15 @@ class OTAUpgradeCommand extends SenderProtocol { 'privateKey: $privateKey, token: $token}'; } + @override + int identifierValue() { + if (encrypt) { + return super.identifierValue(); + } else { + return 0x20; + } + } + @override List messageDetail() { List data = []; @@ -66,21 +76,18 @@ class OTAUpgradeCommand extends SenderProtocol { data.add(type1); data.add(type2); - AppLog.log('---> 指令 : $type1 $type2' ); // 锁id 40 int lockIDLength = utf8.encode(lockID!).length; data.addAll(utf8.encode(lockID!)); data = getFixedLengthList(data, 40 - lockIDLength); - AppLog.log('---> 锁id :${utf8.encode(lockID!)}'); //userID 20 int userIDLength = utf8.encode(userID!).length; data.addAll(utf8.encode(userID!)); data = getFixedLengthList(data, 20 - userIDLength); - AppLog.log('---> userID :${utf8.encode(userID!)}'); //platform 2 int platform0 = (platform! & 0xFF00) >> 8; @@ -88,36 +95,31 @@ class OTAUpgradeCommand extends SenderProtocol { data.add(platform0); data.add(platform1); - AppLog.log('---> platform : $platform0 $platform1'); //product 2 // int product0 = (product! & 0xFF00) >> 8; // int product1 = product! & 0xFF; // data.add(product0); // data.add(product1); - data.addAll([0,1]);//先默认是 01 + data.addAll([0, 1]); //先默认是 01 - AppLog.log('---> platform : ${[0,1]}'); //HwVersion 20 int hwVersionLength = utf8.encode(hwVersion!).length; data.addAll(utf8.encode(hwVersion!)); data = getFixedLengthList(data, 20 - hwVersionLength); - //FwVersion 20 int fwVersionLength = utf8.encode(fwVersion!).length; data.addAll(utf8.encode(fwVersion!)); data = getFixedLengthList(data, 20 - fwVersionLength); - //fwSize 4 ByteData bytes = ByteData(4); // 创建一个长度为4的字节数据 bytes.setInt32(0, fwSize!); List byteList = bytes.buffer.asUint8List(); data.addAll(byteList); - // 创建一个16字节的字节数组 Uint8List result = Uint8List(16); // 将每个十六进制字符转换为4位二进制数据,并将其存储到结果字节数组中 @@ -142,11 +144,9 @@ class OTAUpgradeCommand extends SenderProtocol { authCodeData.addAll(utf8.encode(userID!)); //token 4 首次请求 Token 填 0,如果锁需要鉴权操作者身份,则会分配动态口令并在应答消息中返回,二次请求时带上。 - authCodeData.addAll(token!); + authCodeData.addAll(token??[]); - authCodeData.addAll(signKey!); - - AppLog.log('---> ${utf8.encode(keyID!)} ${utf8.encode(userID!)} $token $signKey'); + authCodeData.addAll(signKey??[]); // 把KeyID、authUserID、时间戳、公钥通过md5加密之后就是authCode var authCode = crypto.md5.convert(authCodeData); @@ -161,11 +161,17 @@ class OTAUpgradeCommand extends SenderProtocol { data.add(0); } } - printLog(data); - // 拿到数据之后通过LockId进行SM4 ECB加密 key:544d485f633335373034383064613864 - ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB); - return ebcData; + + if (encrypt) { + + // 拿到数据之后通过LockId进行SM4 ECB加密 key:544d485f633335373034383064613864 + ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB); + return ebcData; + } else { + data.add(0); + return data; + } } } diff --git a/star_lock/lib/blue/io_sender.dart b/star_lock/lib/blue/io_sender.dart index 8c470f38..045b46d9 100644 --- a/star_lock/lib/blue/io_sender.dart +++ b/star_lock/lib/blue/io_sender.dart @@ -33,8 +33,9 @@ abstract class SenderProtocol extends IOData { _commandIndex = IoManager().commandIndex; } - void printLog(List data){ - AppLog.log("App -> 锁,指令类型:${commandType!.typeName} \n\n参数是:\n${toString()} \n\n加密之前数据是:\n$data"); + void printLog(List data) { + AppLog.log( + "App -> 锁,指令类型:${commandType!.typeName} \n\n参数是:\n${toString()} \n\n加密之前数据是:\n$data"); } //TODO:拼装数据Ï @@ -58,7 +59,8 @@ abstract class SenderProtocol extends IOData { // 包标识 // 指令类型 高 4 位表示包版本,低 4 位用来指示后面数据的加密类型,长度为 1 字节,加密类型取值说明,0:明文,1:AES128,2:SM4(事先约定密钥),3:SM4(设备指定密钥) - commandList.add(commandType!.identifierValue); + int value = identifierValue(); + commandList.add(value); // 数据长度 int dataLen = dataSourceLength(); @@ -84,6 +86,10 @@ abstract class SenderProtocol extends IOData { return commandList; } + // 包标识 + // 指令类型 高 4 位表示包版本,低 4 位用来指示后面数据的加密类型,长度为 1 字节,加密类型取值说明,0:明文,1:AES128,2:SM4(事先约定密钥),3:SM4(设备指定密钥) + int identifierValue() => commandType!.identifierValue; + //TODO:长度 int dataSourceLength() => commandData!.length; diff --git a/star_lock/lib/main.dart b/star_lock/lib/main.dart index 54dd55eb..5b7c18bc 100644 --- a/star_lock/lib/main.dart +++ b/star_lock/lib/main.dart @@ -20,9 +20,7 @@ FutureOr main() async { // 设置国际化信息 await _initTranslation(); - DebugConsole.listen(() { - runApp(const MyApp()); - }); + runApp(const MyApp()); if (AppPlatform.isAndroid) { SystemUiOverlayStyle systemUiOverlayStyle = diff --git a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart index 05cd0e47..1e6faeba 100644 --- a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart +++ b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart @@ -1,10 +1,20 @@ - import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutter_easyloading/flutter_easyloading.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/mine/addLock/nearbyLock/nearbyLock_page.dart'; +import 'package:star_lock/permission/permission_dialog.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import '../../../appRouters.dart'; @@ -21,6 +31,15 @@ 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("获取锁信息 1/3"); @@ -30,11 +49,12 @@ class NearbyLockLogic extends BaseGetXController { // state.sureBtnState.value = 1; // showEasyLoading(); - showBlueConnetctToastTimer(action: (){ + showBlueConnetctToastTimer(action: () { dismissEasyLoading(); // state.sureBtnState.value = 0; }); - BlueManage().bludSendData(deviceName, (BluetoothConnectionState state) async { + BlueManage().bludSendData(deviceName, + (BluetoothConnectionState state) async { AppLog.log("点击要添加的设备了"); if (state == BluetoothConnectionState.connected) { AppLog.log("开始获取公钥"); @@ -45,12 +65,11 @@ class NearbyLockLogic extends BaseGetXController { }, isAddEquipment: true); } - // 获取解析后的数据 - late StreamSubscription _replySubscription; void _initReplySubscription() { - _replySubscription = EventBusManager().eventBus!.on().listen((reply) { + _replySubscription = + EventBusManager().eventBus!.on().listen((reply) { if (reply is GetPublicKeyReply) { - _replyGetPublicKey(reply); + _replyGetPublicKey(reply); } if (reply is GetPrivateKeyReply) { @@ -61,6 +80,23 @@ class NearbyLockLogic extends BaseGetXController { if (reply is GetStarLockStatuInfoReply) { _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(); + } }); } @@ -197,8 +233,10 @@ class NearbyLockLogic extends BaseGetXController { // 重置次数 var restoreCounter = reply.data.sublist(133, 135); - state.lockInfo["restoreCount"] = restoreCounter[0] * 256 + restoreCounter[1]; - AppLog.log("重置次数 restoreCounter:${restoreCounter[0] * 256 + restoreCounter[1]}"); + state.lockInfo["restoreCount"] = + restoreCounter[0] * 256 + restoreCounter[1]; + AppLog.log( + "重置次数 restoreCounter:${restoreCounter[0] * 256 + restoreCounter[1]}"); // 重置时间 var restoreDate = reply.data.sublist(135, 139); @@ -312,21 +350,6 @@ class NearbyLockLogic extends BaseGetXController { } } - // List charListChangeIntList(List featureValue){ - // // 字符数组转化为16进制字符串 - // String featureValueStr = asciiString(featureValue); - // // 16进制字符串转化为2进制的字符串 获取的是逆序的需要倒序 前面有0会消失 需要自动补全 暂时定位57个功能 要补全60 - // String featureValueTwoStr = int.parse(featureValueStr,radix: 16).toRadixString(2); - // List featureValueTwoList = []; - // for(int i = 0;i _getStarLockStatus() async { // 进来之后首先连接 @@ -334,39 +357,26 @@ class NearbyLockLogic extends BaseGetXController { // if (state == BluetoothConnectionState.connected) { // dismissEasyLoading(); - AppLog.log("开始获取锁状态"); - var privateKey = await Storage.getStringList(saveBluePrivateKey); - List getPrivateKeyList = changeStringListToIntList(privateKey!); - // IoSenderManage.senderGetLockStatu( - // lockID:BlueManage().connectDeviceName, - // userID:await Storage.getUid(), - // privateKey:getPrivateKeyList, - // ); - IoSenderManage.senderGetStarLockStatuInfo( - lockID: BlueManage().connectDeviceName, - userID: await Storage.getUid(), - isBeforeAddUser: true, - privateKey: getPrivateKeyList, - ); + AppLog.log("开始获取锁状态"); + var privateKey = await Storage.getStringList(saveBluePrivateKey); + List getPrivateKeyList = changeStringListToIntList(privateKey!); + // IoSenderManage.senderGetLockStatu( + // lockID:BlueManage().connectDeviceName, + // userID:await Storage.getUid(), + // privateKey:getPrivateKeyList, + // ); + IoSenderManage.senderGetStarLockStatuInfo( + lockID: BlueManage().connectDeviceName, + userID: await Storage.getUid(), + isBeforeAddUser: true, + privateKey: getPrivateKeyList, + ); // } else if (state == BluetoothConnectionState.disconnected) { // dismissEasyLoading(); // } // }, isAddEquipment: true); } - // late StreamSubscription>_scanListDiscoveredDeviceSubscription; - // void _scanListDiscoveredDeviceSubscriptionAction() { - // _scanListDiscoveredDeviceSubscription = EventBusManager().eventBus!.on>().listen((List list) { - // state.devices.clear(); - // for (int i = 0; i < list.length; i++) { - // ScanResult device = list[i]; - // if (((device.advertisementData.serviceUuids.isNotEmpty ? device.advertisementData.serviceUuids[0] : "").toString()[31] != "1")) { - // state.devices.add(list[i]); - // } - // } - // }); - // } - void startScanBlueList() { BlueManage().startScan(2000, (List list) { state.devices.clear(); @@ -388,30 +398,234 @@ class NearbyLockLogic extends BaseGetXController { BlueManage().stopScan(); } + // 点击连接设备,升级 ota 固件 + void oTAUpgrade(String deviceName) { + showTitleEasyLoading("连接设备中..."); + this.deviceName = deviceName; + BlueManage().bludSendData(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 { + var status = await PermissionDialog.request( + Permission.storage, '需要访问读写权限才能使用手动升级固件'.tr); + if (status != true) { + return; + } + FilePickerResult? result = await FilePicker.platform.pickFiles(); + if (result == null || result.files.single.path is! String) { + return; + } + File file = File(result.files.single.path!); + Uint8List data = await file.readAsBytes(); + headJson = await getHeadFile(data); + if (headJson is! Map) { + return; + } + otaBin = await checkFile(data, headJson!); + if (otaBin == null) { + return; + } + 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().bludSendData(deviceName!, + (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected) { + 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()); + showTitleEasyLoading("连接设备中..."); + } else if (deviceConnectionState == + BluetoothConnectionState.disconnected) {} + },isAddEquipment: true); + } + + //循环传输升级固件包 + Future processOtaUpgrade() async { + if (!state.otaUpdateIng.value) { + return; + } + int length = otaBin?.length ?? 0; + if (otaCount == 0) { + //首次 + int difference = length % 240; + otaCount = length ~/ 240 + (difference > 0 ? 1 : 0); + startSecond = DateTime.now().millisecondsSinceEpoch ~/ 1000; + } + if (otaCount <= otaIndex) { + int now = DateTime.now().millisecondsSinceEpoch ~/ 1000; + String msg = + '传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex '; + closeOTADAta(); + AppLog.log(msg); + // showToast(msg); + return; + } + int star = otaIndex * 240; + int end = (otaIndex + 1) * 240; + if (end > length) { + end = length; + } + int size = end - star; + 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((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); + var meta = jsonDecode(metaStr); + if (meta is! Map) { + showToast('解析元数据失败,请选择正确的文件'.tr); + return null; + } + return meta..['metaLen'] = metaLen; + } + + //检测升级文件并读取 bin + Future checkFile(Uint8List data, Map meta) async { + num binOffset = 16 + (meta['metaLen'] ?? 0); + // 获取固件数据部分 + Uint8List bin = data.sublist(binOffset.toInt(), data.length); + //md5 校验有问题,暂时不解析 + 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; + } + @override void onReady() { - // TODO: implement onReady super.onReady(); - _initReplySubscription(); - // _scanListDiscoveredDeviceSubscriptionAction(); - state.ifCurrentScreen.value = true; - startScanBlueList(); } @override void onInit() { - // TODO: implement onInit super.onInit(); } @override void onClose() { - // TODO: implement onClose super.onClose(); - _replySubscription.cancel(); - // _scanListDiscoveredDeviceSubscription.cancel(); + _replySubscription?.cancel(); } } diff --git a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_page.dart b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_page.dart index a474bc92..3740694b 100644 --- a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_page.dart +++ b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_page.dart @@ -23,12 +23,6 @@ class _NearbyLockPageState extends State with RouteAware { final logic = Get.put(NearbyLockLogic()); final state = Get.find().state; - @override - void initState() { - // TODO: implement initState - super.initState(); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -60,32 +54,62 @@ class _NearbyLockPageState extends State with RouteAware { ]), ), body: Obx(() { - return ListView.separated( - itemCount: state.devices.length, - itemBuilder: (c, index) { - return nearbyLockItem( - 'images/icon_lockGroup_item.png', state.devices[index], () { - // Navigator.pushNamed(context, Routers.lockAddressPage); - // logic.getPublicKey(state.devices[index].serviceUuids[0].toString()); - state.selectLockName.value = - state.devices[index].advertisementData.advName; - logic.connect(state.devices[index].advertisementData.advName); - // Get.toNamed(Routers.lockAddressGaoDePage); - }); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider( - height: 1, - color: AppColors.greyLineColor, - indent: 20.w, - endIndent: 0, - ); - }, - ); + return listView(); }), ); } + Widget listView() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ListView.separated( + itemCount: state.devices.length, + itemBuilder: (c, index) { + return nearbyLockItem( + 'images/icon_lockGroup_item.png', state.devices[index], () { + String advName = state.devices[index].advertisementData.advName; + state.selectLockName.value = advName; + if (state.otaState.value) { + logic.oTAUpgrade(advName); + } else { + logic.connect(advName); + } + }); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider( + height: 1, + color: AppColors.greyLineColor, + indent: 20.w, + endIndent: 0, + ); + }, + ), + ), + Padding( + padding: EdgeInsets.only(left: 15.w, bottom: 10.h), + child: TextButton( + onPressed: () async { + bool skip = false; + if (!state.otaState.value) { + skip = await Get.dialog( + const _TipDialog(), + ); + } + state.otaState.value = skip; + }, + child: Text( + state.otaState.value ? '点击返回设备配对'.tr : '无法连接?尝试升级'.tr, + style: TextStyle(fontSize: 22.sp), + ), + ), + ), + ], + ); + } + Widget nearbyLockItem( String lockTypeIcon, ScanResult scanResult, Function() action) { return GestureDetector( @@ -97,7 +121,6 @@ class _NearbyLockPageState extends State with RouteAware { ? action : null, child: Column( - // mainAxisAlignment: MainAxisAlignment.center, children: [ Container( height: 89.h, @@ -141,9 +164,12 @@ class _NearbyLockPageState extends State with RouteAware { ), Expanded(child: SizedBox(width: 20.w)), Image.asset( - 'images/main/icon_main_addLock.png', + state.otaState.value + ? 'images/ota_upgrade_icon.png' + : 'images/main/icon_main_addLock.png', width: 36.w, height: 36.w, + color: AppColors.mainColor, ), SizedBox(width: 30.w), ], @@ -156,7 +182,6 @@ class _NearbyLockPageState extends State with RouteAware { @override void didChangeDependencies() { - // TODO: implement didChangeDependencies super.didChangeDependencies(); /// 路由订阅 @@ -165,7 +190,6 @@ class _NearbyLockPageState extends State with RouteAware { @override void dispose() { - // TODO: implement dispose /// 取消路由订阅 AppRouteObserver().routeObserver.unsubscribe(this); super.dispose(); @@ -183,11 +207,9 @@ class _NearbyLockPageState extends State with RouteAware { super.didPop(); EasyLoading.isShow ? EasyLoading.dismiss() : null; - state.ifCurrentScreen.value = false; logic.cancelBlueConnetctToastTimer(); logic.stopScanBlueList(); - BlueManage().disconnect(); } /// 从下级返回 当前界面即将出现 @@ -203,10 +225,88 @@ class _NearbyLockPageState extends State with RouteAware { @override void didPushNext() { super.didPushNext(); - - state.ifCurrentScreen.value = false; - logic.cancelBlueConnetctToastTimer(); - logic.stopScanBlueList(); - BlueManage().disconnect(); + if (!logic.state.otaState.value) { + state.ifCurrentScreen.value = false; + logic.cancelBlueConnetctToastTimer(); + logic.stopScanBlueList(); + } + } +} + +class _TipDialog extends StatelessWidget { + const _TipDialog({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return CupertinoAlertDialog( + title: Text( + '固件升级提示'.tr, + ), + content: Text('请先获取固件文件到手机本地,再选择升级'.tr), + actions: [ + TextButton( + onPressed: () { + Get.back(); + }, + child: Text( + '取消'.tr, + style: TextStyle(fontSize: 22.sp, color: AppColors.blackColor), + ), + ), + TextButton( + onPressed: () async { + Get.back(result: true); + }, + child: Text( + '确定'.tr, + style: TextStyle(fontSize: 22.sp, color: AppColors.blackColor), + ), + ), + ], + ); + } +} + +class OTAProgressDialog extends StatelessWidget { + NearbyLockLogic logic; + + OTAProgressDialog({required this.logic, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Obx(() { + return CupertinoAlertDialog( + title: Text( + '固件升级中'.tr, + ), + content: Row( + children: [ + Text( + '传输中'.tr, + style: TextStyle(fontSize: 22.sp, color: AppColors.mainColor), + ), + SizedBox( + width: 15.w, + ), + Expanded( + child: LinearProgressIndicator( + value: logic.state.otaProgress.value, + color: AppColors.mainColor, + )), + ], + ), + actions: [ + TextButton( + onPressed: () { + logic.closeOTADAta(); + }, + child: Text( + '取消升级'.tr, + style: TextStyle(fontSize: 22.sp, color: AppColors.blackColor), + ), + ), + ], + ); + }); } } diff --git a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_state.dart b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_state.dart index 4e7d61c2..82a63f33 100644 --- a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_state.dart +++ b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_state.dart @@ -1,9 +1,7 @@ - import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; class NearbyLockState { - RxList devices = [].obs; var ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 // var sureBtnState = 0.obs;// 0可点击 1 不可点击 @@ -16,4 +14,8 @@ class NearbyLockState { var featureSettingValue = ''; var featureSettingParams = []; + var otaState = false.obs; //ota 升级 + var otaUpdateIng = false.obs; + var otaProgress = 0.00.obs; + bool oTAProgressDialog = false; } diff --git a/star_lock/pubspec.yaml b/star_lock/pubspec.yaml index 71d1dd87..8ea7e109 100644 --- a/star_lock/pubspec.yaml +++ b/star_lock/pubspec.yaml @@ -99,7 +99,7 @@ dependencies: url_launcher: ^6.1.10 #蓝牙 # flutter_reactive_ble: ^5.1.1 - flutter_blue_plus: ^1.31.16 + flutter_blue_plus: 1.31.16 # event_bus: ^2.0.0 #菊花