// // BluetoothManager.swift // Twear // // Created by yangbin on 2021/12/6. // import Foundation import CoreBluetooth import UIKit import SwiftUI import SwiftDate import AudioToolbox protocol BluetoothManagerDelegate: NSObjectProtocol { func didDiscover(devices: [ScanDevice]) func didConnect(_ peripheral: CBPeripheral) func centralManagerDidUpdateState(_ state: Int) func didDisconnected() } //@objc protocol BluetoothSyncDelegate { // @objc optional func didReceiveFindCommand(status: Bool) // @objc optional func didConnect(_ peripheral: CBPeripheral) //} protocol BluetoothSyncDelegate { func didReceiveFindCommand(status: Bool) func didReceiveCameraCommand(status: SetCmd) func didReceiveFindPhoneCommand() func didReceiveBattery(value: Int) func didReceiveHeartRate() func didReceiveBloodOxygen() func didReceiveBloodPressure() func didReceiveStep() func firstSync() func syncBug() } extension BluetoothSyncDelegate { func didReceiveFindCommand(status: Bool) { } func didReceiveCameraCommand(status: SetCmd) { } func didReceiveFindPhoneCommand() { } func didReceiveBattery(value: Int) { } func didReceiveHeartRate() { } func didReceiveBloodOxygen() { } func didReceiveBloodPressure() { } func didReceiveStep() { } func firstSync() {} func syncBug() {} } class BluetoothManager: NSObject { //Closure var setCmdClosure: ((_ error: Int?) -> ())? var setDataClosure: ((_ error: Int?) -> ())? var firmwareCallbackClosure: ((_ version: String, _ type: String, _ error: Int?) -> ())? var delegateList: [BluetoothManagerDelegate] = [] var syncDelegateList: [BluetoothSyncDelegate] = [] // var bloodPressureClosure: ((_ hrArray: [BloodPressureModel], _ error: Int?) -> ())? typealias HeartRateClosure = (_ hrArray: [HeartRateModel], _ error: Int?) -> Void var heartRateClosure: HeartRateClosure? typealias BloodPressureClosure = (_ bpArray: [BloodPressureModel], _ error: Int?) -> Void var bloodPressureClosure: BloodPressureClosure? typealias BloodOxygenClosure = (_ boArray: [BloodOxygenModel], _ error: Int?) -> Void var bloodOxygenClosure: BloodOxygenClosure? typealias SleepClosure = (_ sleepArray: [SleepModel], _ error: Int?) -> Void var sleepClosure: SleepClosure? typealias StepClosure = (_ stepArray: [StepModel], _ error: Int?) -> Void var stepClosure: StepClosure? typealias TrainClosure = (_ trainArray: TrainModel?, _ error: Int?) -> Void var trainClosure: TrainClosure? typealias DialProgressClosure = (_ progress: Int) -> Void var dialProgressClosure: DialProgressClosure? typealias DialClosure = (_ error: Int?) -> Void var dialClosure: DialClosure? typealias SyncRTKProgressClosure = (_ progress: Int) -> Void var syncRTKProgressClosure: SyncRTKProgressClosure? typealias SyncRTKClosure = (_ error: Error?) -> Void var syncRTKClosure: SyncRTKClosure? static let shared = BluetoothManager() // weak var delegate: BluetoothManagerDelegate? var manger = MTKBleManager.sharedInstance() as! MTKBleManager private var peripheral: CBPeripheral? = nil var platform: Platform = .other //CBCentralManager private var centralManger: CBCentralManager? = nil private var peripheralDic: [String: CBPeripheral] = [:] private var deviceArray: [ScanDevice] = [] private var connectedPerArray: [CBPeripheral] = [] //mergeData private var totalBytes: [UInt8] = [] private var totalLength: Int = 0 private var mergeLength: Int = 0 //sync private var isSyncReceived: Bool = false //是否收到同步data private var isSync: Bool = false //是否在同步中 private var isSyncCmd: SyncCmd = .other //正在同步的指令 private var isNextSync: Bool = false //是否有下一个同步包 // private var override init() { super.init() // manger.registerClientProfile(BLEConfig.MTKServerUUID, clientProfileDelegate: self) // manger.registerClientProfile(BLEConfig.MTKBasServerUUID, clientProfileDelegate: self) // manger.registerClientProfile(BLEConfig.MTKDialServerUUID, clientProfileDelegate: self) // FmpGattClient.getInstance() platform = CurDevice.platform setupManger(nil) // registerClientProfile() } private func setupManger(_ peripheral: CBPeripheral?) { if peripheral == nil { if CurDevice.uuid == "" { return } else { registerClientProfile() } } else { registerClientProfile() } } private func registerClientProfile() { switch platform { case ._818, ._818_hq7: manger.registerClientProfile(BLEConfig.ServerUUID, clientProfileDelegate: self) case ._816: manger.registerClientProfile(BLEConfig.MTKServerUUID, clientProfileDelegate: self) manger.registerClientProfile(BLEConfig.MTKBasServerUUID, clientProfileDelegate: self) manger.registerClientProfile(BLEConfig.MTKDialServerUUID, clientProfileDelegate: self) FmpGattClient.getInstance() default: break } } func registerDelegate(_ delegate: BluetoothManagerDelegate) { delegateList.append(delegate) print(delegateList) // print(NSStringFromClass(type(of: delegate))) } func unRegisterDelegate(_ delegate: BluetoothManagerDelegate) { delegateList.removeAll{ type(of: $0) == type(of: delegate) } } func registerSyncDelegate(_ delegate: BluetoothSyncDelegate) { syncDelegateList.append(delegate) } func unRegisterSyncDelegate(_ delegate: BluetoothSyncDelegate) { syncDelegateList.removeAll{ type(of: $0) == type(of: delegate) } } func clearDelegate() { delegateList.removeAll() syncDelegateList.removeAll() } func startScanning() { print("开始扫描") peripheralDic = [:] deviceArray = [] centralManger = CBCentralManager(delegate: self, queue: nil) manger.startScanning() //mClientProfileDic __NSDictionaryM * 1 key/value pair 0x0000000281a694c0 } func stopScanning() { if centralManger != nil { if centralManger!.isScanning { centralManger?.stopScan() } centralManger = nil } manger.stopScanning() } func connect(peripheral: CBPeripheral) { // setupManger(peripheral) let name = peripheral.name if (name?.range(of: "You 1_LE") != nil || name?.range(of: "Pro_LE") != nil) { platform = ._816 } else if (name?.range(of: "MTB025B") != nil || name?.range(of: "MTB033B") != nil) { platform = ._818 } else if name?.range(of: "hq7") != nil { platform = ._818_hq7 } registerClientProfile() manger.connectPeripheral(peripheral) } func reConnect() { startScanning() } func forgetPeripheral() { print("forgetPeripheral") manger.forgetPeripheral() // let menstrual = .menstrual let device = DeviceModel() // device.menstrual = menstrual AdminHelper.shared.updateDevice(device) } func disconnect() { for per in connectedPerArray { manger.disconnectPeripheral(per) } } func pairFail() { print("pairFail") disconnect() peripheral = nil // manger.peripheral = nil manger.foundPeripherals = nil manger.tempPeripheral = nil manger.connectedService = nil manger.connectPeripherals = nil // if centralManger != nil { // if centralManger!.isScanning { // centralManger?.stopScan() // } // centralManger = nil // } connectedPerArray = [] } //MARK: - 设置 func getSettingData(completion: ((_ error: Int?) -> ())? = nil) { let data = BleMessage.shared.getSettingCmd() self.setDataClosure = completion sendData(data) } func setUserInfo(_ user: UserInfoModel, completion: ((_ error: Int?) -> ())? = nil) { let data = BleMessage.shared.getUserCmd(user) self.setCmdClosure = completion sendData(data) } func setUnit(_ distance: DistanceUnit, _ temperature: TemperatureUnit, completion: ((_ error: Int?) -> ())? = nil) { let data = BleMessage.shared.getUnitCmd(distance, temperature) self.setCmdClosure = completion sendData(data) } func setLanguage(_ language: AppSettings.Language, timeFormat: TimeFormat, screenOnTime: Int, pair: UInt8, completion: ((_ error: Int?) -> ())? = nil) { let data = BleMessage.shared.getLanguageCmd(language: language, timeFormat: timeFormat, screenOnTime: screenOnTime, pair: pair) self.setCmdClosure = completion sendData(data) } func setTime(format: TimeFormat, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getTimeCmd(format: format) self.setCmdClosure = completion sendData(data) } func setWeather(_ array: [WeatherModel]) { let data = BleMessage.shared.getWeatherCmd(array) sendData(data) } func setMessagePush(_ push: MessagePushModel, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getMessagePush(push) self.setCmdClosure = completion sendData(data) } func setAlarmClock(_ array: [AlarmClockModel], completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getAlarmClockCmd(array) self.setCmdClosure = completion sendData(data) } func setDrinkWaterRemind(_ remind: RemindModel, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getDrinkWaterCmd(remind) self.setCmdClosure = completion sendData(data) } func setSedentaryRemind(_ remind: RemindModel, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getSedentaryCmd(remind) self.setCmdClosure = completion sendData(data) } func setNotDisturb(remind: RemindModel, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getNotDisturbCmd(remind) self.setCmdClosure = completion sendData(data) } func setWristSense(_ bool: Bool, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getWristCmd(bool) self.setCmdClosure = completion sendData(data) } func setMenstrual(_ menstrual: MenstrualCalendarModel, completion: @escaping(_ error: Int?) -> ()) { let data = BleMessage.shared.getMenstrualCmd(menstrual) self.setCmdClosure = completion sendData(data) } func syncLanguage() { let data = BleMessage.shared.getLanguageCmd(language: .system, timeFormat: .hour_24, screenOnTime: 5, pair: 1) sendData(data) } func syncTime(format: TimeFormat) { let data = BleMessage.shared.getTimeCmd(format: format) sendData(data) } func openCamera(_ status: Bool) { let data = BleMessage.shared.getCameraCmd(status) sendData(data) } func findDevice(_ status: Bool) { switch platform { case ._816: let fmp = FmpGattClient.getInstance() as! FmpGattClient fmp.findTarget(status ? 2 : 0) case ._818, ._818_hq7: if status { let data = BleMessage.shared.getFindCmd(status) sendData(data) } case .other: break } } func queryBattery() { let data = BleMessage.shared.getBatteryCmd() sendData(data) } //MARK: - 表盘, 固件升级 func getFirmwareVersion(closure: @escaping(_ version: String, _ type: String, _ error: Int?) -> ()) { firmwareCallbackClosure = closure let data = BleMessage.shared.getFirmwareCmd() sendData(data) } var OTAProfile: RTKOTAProfile? = nil var OTAPeripheral: RTKOTAPeripheral? = nil var DFUPeripheral: RTKMultiDFUPeripheral? = nil var upgradeBins: [RTKOTAUpgradeBin] = [] var isFirmwareUpdate: Bool = false func syncFileWith(path: String, isFirmware: Bool = false, progress: @escaping SyncRTKProgressClosure, completion: @escaping SyncRTKClosure) { isFirmwareUpdate = isFirmware if OTAPeripheral == nil { return } syncRTKProgressClosure = progress syncRTKClosure = completion upgradeBins = try! RTKOTAUpgradeBin.imagesExtracted(fromMPPackFilePath: path) if upgradeBins.count == 1 { if !(upgradeBins.last!.icDetermined) { upgradeBins.last?.assertAvailable(for: OTAPeripheral!) } } if OTAPeripheral!.canUpgradeSliently && !OTAPeripheral!.isRWS { let peripheral = OTAProfile!.dfuPeripheral(of: OTAPeripheral!) DFUPeripheral = peripheral as? RTKMultiDFUPeripheral DFUPeripheral!.delegate = self OTAProfile?.connect(to: DFUPeripheral!) } } func toUpgradeImages() -> [RTKOTAUpgradeBin] { if OTAPeripheral == nil { return [] } switch OTAPeripheral?.activeBank.rawValue { case 0: let imagesForBank1: [RTKOTAUpgradeBin] = self.upgradeBins.filter{$0.upgradeBank == .bank1} if imagesForBank1.count > 0 { return [] } else { return self.upgradeBins } case 1: return self.upgradeBins.filter{$0.upgradeBank == .bank1 || $0.upgradeBank == .unknown} case 2: return self.upgradeBins.filter{$0.upgradeBank == .singleOrBank0 || $0.upgradeBank == .unknown} default: return self.upgradeBins } } var syncDialIndex: Int = 0 var dialLength: Int = 0 var dialFileContents: [String] = [] var dialDirectoryPath: String = "" // var dialRemainData: Data = Data() func syncDialWith(path: String, progress: @escaping DialProgressClosure, completion: @escaping DialClosure) { guard let contentsOfPath = try? FileManager.default.contentsOfDirectory(atPath: path), contentsOfPath.count > 0 else { return } dialDirectoryPath = path dialFileContents = contentsOfPath dialProgressClosure = progress dialClosure = completion dialLength = contentsOfPath.count syncDialIndex = 0 sendDialData(BleMessage.shared.getSyncDialCmd(bytes: [0x02])) } func sendNextSyncData() { if syncDialIndex < dialLength { guard var dataStr = try? String.init(contentsOfFile: "\(dialDirectoryPath)/\(dialFileContents[syncDialIndex])", encoding: .utf8), dataStr.count > 0 else { return } dataStr = dataStr.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: "\r", with: "") sendDialData(BleMessage.shared.getSyncDialCmd(bytes: dataStr.hexaBytes)) if dialProgressClosure != nil { dialProgressClosure?(Int(Float(syncDialIndex)/Float(dialLength)*100)) } } else if syncDialIndex == dialLength { if dialProgressClosure != nil { dialProgressClosure?(100) } sendDialData(BleMessage.shared.getSyncDialCmd(bytes: [0x03])) if dialClosure != nil { dialClosure?(nil) } } else { return } syncDialIndex += 1 } //MARK: - 同步健康数据 func getSleepHistoryData(day: SyncDay = .today, closure: SleepClosure? = nil) { var sleepDay: SyncDay = day let curPlatform = CurDevice.platform if curPlatform == ._818 || curPlatform == ._818_hq7 { if day == .today { sleepDay = .yesterday } else if day == .yesterday { sleepDay = .dayBeforYesterday } } startSyncHealthData(closure: closure, data: BleMessage.shared.getSyncCmd(.sleep, day: sleepDay), cmd: .sleep) } func getBloodPressureHistoryData(day: SyncDay = .today, closure: BloodPressureClosure? = nil) { startSyncHealthData(closure: closure, data: BleMessage.shared.getSyncCmd(.bp, day: day), cmd: .bp) } func getBloodOxygenHistoryData(day: SyncDay = .today, closure: BloodOxygenClosure? = nil) { startSyncHealthData(closure: closure, data: BleMessage.shared.getSyncCmd(.bo, day: day), cmd: .bo) } func getHeartRateHistoryData(day: SyncDay = .today, closure: HeartRateClosure? = nil) { startSyncHealthData(closure: closure, data: BleMessage.shared.getSyncCmd(.hr, day: day), cmd: .hr) } func getStepHistoryData(day: SyncDay = .today, closure: StepClosure? = nil) { startSyncHealthData(closure: closure, data: BleMessage.shared.getSyncCmd(.step, day: day), cmd: .step) } func getTrainHistoryData(day: SyncDay = .today, closure: TrainClosure? = nil) { startSyncHealthData(closure: closure, data: BleMessage.shared.getSyncCmd(.train, day: day), cmd: .train) } func startSyncHealthData(closure: Any?, data: Data, cmd: SyncCmd) { if !isSync { clearClosures(closure: closure) startSyncTimer() isNextSync = false isSync = true isSyncCmd = cmd sendData(data) } else { switch closure { case is BloodPressureClosure: if bloodPressureClosure == nil { bloodPressureClosure = closure as? BloodPressureClosure bloodPressureClosure?([], -1002) bloodPressureClosure = nil } case is HeartRateClosure: if heartRateClosure == nil { heartRateClosure = closure as? HeartRateClosure heartRateClosure?([], -1002) heartRateClosure = nil } case is BloodOxygenClosure: if bloodOxygenClosure == nil { bloodOxygenClosure = closure as? BloodOxygenClosure bloodOxygenClosure?([], -1002) bloodOxygenClosure = nil } case is SleepClosure: if sleepClosure == nil { sleepClosure = closure as? SleepClosure sleepClosure?([], -1002) sleepClosure = nil } case is StepClosure: if stepClosure == nil { stepClosure = closure as? StepClosure stepClosure?([], -1002) stepClosure = nil } case is TrainClosure: if trainClosure == nil { trainClosure = closure as? TrainClosure trainClosure?(nil, -1002) trainClosure = nil } default: break } } } func syncBug() { // if GCDTimer.s GCDTimer.shared.scheduledDispatchTimerNotNow(WithTimerName: "syncBug", timeInterval: 20, queue: .main, repeats: false) { print("同步超时") self.isSync = false self.isSyncCmd = .other self.isSyncReceived = false for delegate in self.syncDelegateList { delegate.syncBug() } } } //MARK: - 解析数据 //心率 func parseHistoryHeartRate(_ bytes: [UInt8]) { if bytes.count == 0 { heartRateClosure?([], nil) return } let hrArray = HeartRateModel.toHeartRateArray(bytes) heartRateClosure?(hrArray, nil) } //血压 func parseHistoryBloodPressure(_ bytes: [UInt8]) { if bytes.count == 0 { bloodPressureClosure?([], nil) return } let bpArray = BloodPressureModel.toBloodPressureArray(bytes) bloodPressureClosure?(bpArray, nil) } //血氧 func parseHistoryBloodOxygen(_ bytes: [UInt8]) { if bytes.count == 0 { bloodOxygenClosure?([], nil) return } let boArray = BloodOxygenModel.toBloodOxygenArray(bytes) bloodOxygenClosure?(boArray, nil) } //睡眠 func parseHistorySleep(_ bytes: [UInt8]) { if bytes.count == 0 { sleepClosure?([], nil) return } let sleepArray = SleepModel.toSleepArray(bytes) sleepClosure?(sleepArray, nil) } //计步 func parseHistoryStep(_ bytes: [UInt8]) { if bytes.count == 0 { stepClosure?([], nil) return } let stepArray = StepModel.toStepArray(bytes) stepClosure?(stepArray, nil) } func parseHistoryTrain(_ bytes: [UInt8]) { if bytes.count == 0 { trainClosure?(nil, 2000) return } let train = TrainModel.toTrainModel(bytes) train.add() if !isNextSync && !isSync { trainClosure?(train, nil) } } func parseRealTimeSteps(_ bytes: [UInt8]) { if bytes.count > 0 { print(StepModel.toStepModel(bytes)) StepModel.toStepModel(bytes).realTimeAdd() for delegate in syncDelegateList { delegate.didReceiveStep() } } } func parseRealTimeBloodOxygen(_ bytes: [UInt8]) { if bytes.count == 1 { BloodOxygenModel(value: Int(bytes[0]), date: DateInRegion().date).realTimeAdd() for delegate in syncDelegateList { delegate.didReceiveBloodOxygen() } } } func parseRealTimeBloodPressure(_ bytes: [UInt8]) { if bytes.count == 2 { BloodPressureModel(dbp: Int(bytes[1]), sbp: Int(bytes[0]), date: DateInRegion().date).realTimeAdd() for delegate in syncDelegateList { delegate.didReceiveBloodPressure() } } } func parseRealTimeHeartRate(_ bytes: [UInt8]) { if bytes.count == 1 { HeartRateModel(value: Int(bytes[0]), date: DateInRegion().date).realTimeAdd() for delegate in syncDelegateList { delegate.didReceiveHeartRate() } } } private func parseSyncData(_ key: SyncCmd?, bytes: [UInt8]) { stopSyncTimer(BLEConfig.SyncHealthTimer) if !isNextSync { isSync = false isSyncCmd = .other } switch key { case .sleep: parseHistorySleep(bytes) case .hr: parseHistoryHeartRate(bytes) case .bp: parseHistoryBloodPressure(bytes) case .bo: parseHistoryBloodOxygen(bytes) case .step: parseHistoryStep(bytes) case .train: parseHistoryTrain(bytes) case .bo_rt: parseRealTimeBloodOxygen(bytes) case .bp_rt: parseRealTimeBloodPressure(bytes) case .hr_rt: parseRealTimeHeartRate(bytes) case .step_rt: parseRealTimeSteps(bytes) default: break } } private func parseSettingData(_ data: Data) { let bytes = Array([UInt8](data)[13.. Void in let mySelf = Unmanaged.fromOpaque(inClientData!).takeUnretainedValue() mySelf.audioServicesPlaySystemSoundCompleted(soundID: soundID) }, observer) AudioServicesPlaySystemSound(soundID) // for delegate in syncDelegateList { // delegate.didReceiveFindCommand(status: key == .find_phone_start) // } case .find_phone_stop: AudioServicesRemoveSystemSoundCompletion(1005) AudioServicesDisposeSystemSoundID(1005) case .find_device: for delegate in syncDelegateList { delegate.didReceiveFindPhoneCommand() } case .sync, .weather: print("同步") case .none: print("") case .dial: print("") case .battery: print("????") case .battery_return: if GCDTimer.shared.isExistTimer(WithTimerName: "queryBattary") { GCDTimer.shared.cancleTimer(WithTimerName: "queryBattary") } Battery = Int([UInt8](data)[13]) for delegate in syncDelegateList { delegate.didReceiveBattery(value: Battery) } } } //音频结束时的回调 func audioServicesPlaySystemSoundCompleted(soundID: SystemSoundID) { print("Completion") AudioServicesPlaySystemSound(1005) } private func sendData(_ data: Data) { if peripheral == nil { print("peripheral == nil") return } var serverUUID: String = "" var writeUUID: String = "" switch platform { case ._816: serverUUID = BLEConfig.MTKServerUUID writeUUID = BLEConfig.MTKWriteUUID case ._818, ._818_hq7: serverUUID = BLEConfig.ServerUUID writeUUID = BLEConfig.WriteUUID default: break } if let services = peripheral?.services { for service in services { if service.uuid.uuidString == serverUUID { if service.characteristics == nil { return } for char in service.characteristics! { if char.uuid.uuidString == writeUUID { if platform == ._818 || platform == ._818_hq7 { if data.count <= 20 { print("发送: \([UInt8](data))") peripheral?.writeValue(data, for: char, type: .withResponse) } else { print("发送 拼: \([UInt8](data))") let n = data.count/20 + (data.count%20 == 0 ? 0 : 1) for i in 0..= 0 else { return } switch characteristic.uuid.uuidString { case BLEConfig.MTKBasReadUUID: print("电量------\([UInt8](data))") Battery = Int([UInt8](data)[0]) for delegate in syncDelegateList { delegate.didReceiveBattery(value: Battery) } case BLEConfig.MTKDialReadUUID: print("表盘------\([UInt8](data))") let bytes = [UInt8](data) if bytes.count > 13 { if bytes[13] == 1 { sendNextSyncData() } else if bytes[13] == 2 { // sendDialData(dialRemainData) } } case BLEConfig.ReadUUID: // print("?????????????\([UInt8](data))") mergeData(data) default: mergeData(data) } } func mergeData(_ data: Data) { let bytes = [UInt8](data) print(bytes) isSyncReceived = true if bytes[0] == 0xBA && (bytes[1] == 48 || bytes[1] == 112) { if bytes.count > 10, BleCmd(rawValue: bytes[8]) == .sync { if isSync { if SyncCmd(rawValue: bytes[10]) != isSyncCmd { syncReceiveFail() } else { isNextSync = bytes[1] == 112 startSyncTimer() isSyncReceived = true } } } totalLength = Int(UInt16(bytes[6]) << 8 | UInt16(bytes[7])) + 8 if data.count == totalLength { parseData(bytes) } else { totalBytes = [] totalBytes.append(contentsOf: bytes) mergeLength = bytes.count } } else { if isSync { if isSyncReceived { startSyncTimer() isSyncReceived = true } } totalBytes.append(contentsOf: bytes) mergeLength += bytes.count if mergeLength == totalLength { parseData(totalBytes) } } } func parseData(_ bytes: [UInt8]) { totalLength = 0 mergeLength = 0 totalBytes = [] guard bytes.count > 10, let cmd = BleCmd(rawValue: bytes[8]) else { return } let data = Data(bytes: bytes, count: bytes.count) switch cmd { case .set, .find, .device, .remind, .firmware: parseSetData(SetCmd(rawValue: bytes[10]), data: data) case .sync: let valueBytes = Array(bytes[13.. Bool { return isFirmwareUpdate } } extension BluetoothManager { private func startSync818(_ delayTime: Double) { let device = CurDevice self.syncBug() GCDTimer.shared.scheduledDispatchTimerNotNow(WithTimerName: "SyncTime", timeInterval: delayTime, queue: .main, repeats: false) { self.setTime(format: TimeFormat(rawValue: UInt8(device.timeFormat))!) {[weak self] error in self?.setMenstrualStatus() } } } func setMenstrualStatus() { if let model = MenstrualCalendarModel.getDate(DateInRegion().date) { self.setMenstrual(model) {[weak self] error in self?.firstSync818() } } else { firstSync818() } } func firstSync818() { for delegate in self.syncDelegateList { delegate.firstSync() } } }