// // BluetoothService.swift // FireBoltt // // Created by lemo on 2018/7/3. // Copyright © 2020年 ecell. All rights reserved. // import Foundation import UIKit import AVFoundation import RxSwift import Moya /// 读取手环配置通知 let ReadProfileNotice = "ReadProfileNotice" /// 指令成功通知 let CmdSuccess = "CmdSuccess" /// 指令超时通知 let CmdTimeout = "CmdTimeout" fileprivate let SendSyncDeviceDataTimer = "BluetoothService_SendSyncDeviceDataTimer" enum HealthDataSyncState { case normal case syncing(progress: Double) } class BluetoothService: NSObject { /// 单例 static let shared = BluetoothService() /// 需要在oc中调用此单例 @objc open class func ocShareInstance() -> BluetoothService { return shared } let baseBl = BLEBaseBlFireBoltt() var totalCount = 0 /// 响铃 private var isPlaying: Bool = false private var systemSoundID: SystemSoundID = 0 private var alertController: UIAlertController? /// 同步数据的总请求次数(用于显示进度) private var totalRequestCount: Int = 0 private var progress: Double = 0.2 // 连接状态 let deviceConnectState = BehaviorRelay(value: false) // 电量 let power = BehaviorRelay(value: 0) let powerState = BehaviorRelay(value: R.image.power_0()) // 同步状态 let syncState = BehaviorRelay(value: .normal) // 实时步数 let realStep = BehaviorRelay(value: RealtimeStepModel()) // 实时心率 let realHeartRate = BehaviorRelay(value: RealtimeHeartRateModel()) // 实时体温 let realTemperature = BehaviorRelay(value: RealtimeTemperatureModel()) // 实时血压 let realBloodPressure = BehaviorRelay(value: RealtimeBloodPressureModel()) // 实时血氧 let realBloodOxygen = BehaviorRelay(value: RealtimeBloodOxygenModel()) // 运动数据 let sportUpdate = PublishSubject() // 睡眠数据 let sleepUpdate = BehaviorSubject(value: ()) /// 云端固件信息 let cloudFirmwareInfo = PublishSubject<(isNew: Bool, isMandatory: Bool)>() /// 固件适配信息更新 let adapterInfoUpdate = PublishSubject() override init() { super.init() baseBl.delegate = self loadDefaultData() monitorNotification() // 测试 // finishedHandle(BLEBaseBl(), data: ["deviceName": "ceshi", "platform": 0, "macID": "asdasd", "adapterNumber": 9527, "firmwareVersion": "1.0.0"], cmd: .firmwareVersion) } private func loadDefaultData() { // 获取实时步数 if let json = UserDefaultsManagerFrieBoltt.objectFromSandBox(UserDefaultsKey.kfRealTimeStep), let step = RealtimeStepModel.deserialize(from: json as? [String: Any]) { if Date(timeIntervalSince1970: TimeInterval(step.timestamp)).isToday() { realStep.accept(step) } } // 获取实时心率 if let json = UserDefaultsManagerFrieBoltt.objectFromSandBox(UserDefaultsKey.kfRealTimeHeartRate), let heartRate = RealtimeHeartRateModel.deserialize(from: json as? [String: Any]) { if Date(timeIntervalSince1970: TimeInterval(heartRate.timestamp)).isToday() { realHeartRate.accept(heartRate) } } // 获取实时体温 if let json = UserDefaultsManagerFrieBoltt.objectFromSandBox(UserDefaultsKey.kfRealTimeTemperature), let model = RealtimeTemperatureModel.deserialize(from: json as? [String: Any]) { if Date(timeIntervalSince1970: TimeInterval(model.timestamp)).isToday() { realTemperature.accept(model) } } // 获取实时血压 if let json = UserDefaultsManagerFrieBoltt.objectFromSandBox(UserDefaultsKey.kfRealTimeBloodPressure), let model = RealtimeBloodPressureModel.deserialize(from: json as? [String: Any]) { if Date(timeIntervalSince1970: TimeInterval(model.timestamp)).isToday() { realBloodPressure.accept(model) } } // 获取实时血氧 if let json = UserDefaultsManagerFrieBoltt.objectFromSandBox(UserDefaultsKey.kfRealTimeBloodOxygen), let model = RealtimeBloodOxygenModel.deserialize(from: json as? [String: Any]) { if Date(timeIntervalSince1970: TimeInterval(model.timestamp)).isToday() { realBloodOxygen.accept(model) } } } private func monitorNotification() { // 连接成功 kNotificationCenter.rx.notification(Notification.Name(rawValue: BluetoothNotificationAtConnectSuccess)) .subscribe(onNext: { [weak self] (notification) in guard let `self` = self else { return } self.deviceConnectState.accept(true) }) .disposed(by: rx.disposeBag) // 断开连接 kNotificationCenter.rx.notification(Notification.Name(rawValue: BluetoothNotificationAtDisconnect)) .subscribe(onNext: { [weak self] (notification) in guard let `self` = self else { return } self.deviceConnectState.accept(false) }) .disposed(by: rx.disposeBag) // 接收到蓝牙控制指令处理 kNotificationCenter.rx.notification(Notification.Name(rawValue: DeviceControlNotification)) .subscribe(onNext: { [weak self] notification in guard let `self` = self else { return } guard let cmd = notification.object as? Int, let bleCmd = BleCMD_FireBoltt(rawValue: cmd) else { return } let data = notification.userInfo?["data"] as! NSData let contentData = data.subdata(with: NSMakeRange(13, data.length - 13)) as NSData self.handleDeviceCmd(blecmd: bleCmd, data: contentData) }) .disposed(by: rx.disposeBag) // 接收到蓝牙上报实时健康数据处理 kNotificationCenter.rx.notification(Notification.Name(rawValue: DeviceReportRealTimeHealthData)) .subscribe(onNext: { [weak self] notification in guard let `self` = self else { return } guard let cmd = notification.object as? Int, let bleCmd = BleCMD_FireBoltt(rawValue: cmd), let dict = notification.userInfo as? [String: Any] else { return } self.handleRealTimeHealthData(bleCmd: bleCmd, dict: dict) }) .disposed(by: rx.disposeBag) } /// 清空实时数据页面 - 解绑使用 func clearRealtimeData() { realStep.accept(RealtimeStepModel()) realHeartRate.accept(RealtimeHeartRateModel()) realTemperature.accept(RealtimeTemperatureModel()) realBloodOxygen.accept(RealtimeBloodOxygenModel()) realBloodPressure.accept(RealtimeBloodPressureModel()) } } // MARK: - 蓝牙指令接口 extension BluetoothService { /// 查找手机 func findDevice() { baseBl.findDevice(withBleCmdTypeFB: .findPhone) } /// 抬手亮屏 func handup(isOn: Bool) { baseBl.gesturesControl(withBleCmdTypeFB: .gesturesControl, hand: 0, raise: isOn, flip: false) } /// 设置推送开关 /// - Parameters: /// - pushType: 推送类型 func setPushStatus(pushType: PushType, isOn: Bool) { let value = pushType.tranfromPushValue(currenValue: GlobalDeviceProfileModel.shareInstance.pushSetValue, isOn: isOn) GlobalDeviceProfileModel.shareInstance.pushSetValue = value baseBl.setPushWithBleCmdTypeFB(.setPush, value: value) } /// 设置心率监测 func setHeartRateCheckWithBleCmdType(model: HeartRateCheckModel) { baseBl.setHeartRateCheckWithBleCmdTypeFB(.heartRateCheck, rateONOFF: model.IsOpen, startTime: model.StartTime, endTime: model.EndTime, interval: model.Interval) } /// 设置勿扰模式 func notDisturbWithBleCmdType(model: NoDisturbModel) { baseBl.notDisturb(withBleCmdTypeFB: .notDisturb, onoff: model.IsOpen, startTime: model.StartTime, endTime: model.EndTime) } /// 设置喝水提醒 func setDrinkRemindWithBleCmdType(model: DrinkingModel) { baseBl.setDrinkRemindWithBleCmdTypeFB(.setDrinkRemind, drinkONOFF: model.IsOpen, startTime: model.StartTime, endTime: model.EndTime, cycle: model.Cycle, interval: model.Interval) } /// 设置久坐提醒 func setSedentaryWithBleCmdType(model: SedentaryModel) { baseBl.setSedentaryWithBleCmdTypeFB(.setSedentary, onoff: model.IsOpen, startTime: model.StartHour, endTime: model.EndHour, cycle: model.Cycle, sittingTime: model.SittingTime, sittingThreshold: model.SittingThreshold) } /// 设置闹钟 func setAlarmClockWithBleCmdType(models: [AlarmModel]) { baseBl.setAlarmClockWithBleCmdTypeFB(.setAlarmClock, alarmClockArr: models) } /// 远程拍照 func cameraSwitchWithBleCmdType(isOpen: Bool) { baseBl.cameraSwitch(withBleCmdTypeFB: .cameraSwitch, onoff: isOpen) } /// 单位设置 /// - Parameter isMetric: 0: 公尺 1:英尺 func unitSetting(metric: Int) { baseBl.unitSetting(withBleCmdTypeFB: .unitSetting, measure: metric, temperature: 0) } /// 设置运动目标 /// - Parameters: /// - step: 步数 /// - sportMode: 0: 日常计步 1:走路 2: 跑步 3: 骑行 4: 爬山 func setSportTarget(step: Int, sportMode: Int) { baseBl.setSportTargetWithBleCmdTypeFB(.setSportTarget, targetSteps: step, sportMode: sportMode) } /// 修改用户信息 /// - Parameter user: 用户信息 func sendBleCmdSetUserInfo(user: UserInfo) { //设置个人信息 let goal = String(format: "%d", user.stepTarget) let age = String(format: "%d", user.age()) let sex = user.gender ? "0" : "1" if let bluetooth = BluetoothFireBoltt.shareInstance(), bluetooth.isConnected { baseBl.setUserInfoWithBleCmdTypeFB(.setUserInfo, gender: sex, age: age, height: String(user.heightCM), weight: String(user.weightKG), goal: goal, bmi: String(user.bmi())) } } /// 天气推送(最多三天,当天、明天、后天) /// - Parameter weathers: 天气数组 func setWeatherPushWithBleCmdType(weathers: [Weather]) { baseBl.setWeatherPushWithBleCmdTypeFB(.fireBoltt_weatherPush, weathers: weathers.toJSON()) } /// 发送FLASH数据命令 /// - Parameter data: 数据 func falshSendWithBleCmdType(total: Int, curren: Int, lenght: Int, data: Data) { baseBl.falshSend(withBleCmdTypeFB: .falshSend, total: total, curren: curren, lenght: lenght, data: data) } /// 推送表盘 /// - Parameters: /// - number: 第几个表盘 /// - type: 0:删除表盘 1:增加表盘 2:更新表盘 /// - fileLenght: 文件大小 func newDialPushWithBleCmdType(number: Int, type: Int, fileLenght: Int) { baseBl.newDialPush(withBleCmdTypeFB: .newDialPush, number: number, type: type, fileLenght: fileLenght) } /// 杰里表盘推送 /// - Parameter filePath: 文件路径 func newJerryDialPushWithBleCmdType(filePath:String, url:String, fileData:Data) { baseBl.newJerryDialPush(filePath, urlName: url, fileData: fileData) } /// 设置体温监测 func setTemperatureCheckWithBleCmdType(model: TemperatureCheckModel) { baseBl.setTemperatureCheckWithBleCmdTypeFB(.temperatureCheck, rateONOFF: model.IsOpen, startTime: model.StartTime, endTime: model.EndTime, interval: model.Interval) } /// 同步通讯录 func syncContactWithBleCmdType(seleteArr:NSArray){ baseBl.syncContact(withBleCmdTypeFB: .syncContact, contentArr: seleteArr as? [Any]) } /// 钱包二维码 func qrCodePushWithBleCmdType(payImgStr: UIImage, type: Int){ baseBl.qrCodePush(withBleCmdTypeFB: .qrCodePush, payType:type , qrCodeImg: payImgStr) } } // MARK: - 蓝牙同步方法 extension BluetoothService { /// 检查蓝牙指令是否可以发送 func checkBleCmdEnable(isShow:Bool) -> Bool { // 连接状态 let isConnect = BluetoothFireBoltt.shareInstance()?.isConnected ?? false if !isConnect { SVProgressHUD.showError(withStatus: MultiLanguageKey_FB.bleTipFB.localized) return isConnect } // 判断同步状态 switch syncState.value { case .normal: return true case .syncing(_): if isShow { SVProgressHUD.showError(withStatus: MultiLanguageKey_FB.syncDataTipFB.localized) } return false } } /// 同步设备配置 func bloothPrepare() { let bluetooth = BluetoothFireBoltt.shareInstance()! if !bluetooth.isConnected { return } // 设置时间 baseBl.setIngTimeWithBleCmdTypeFB(.setIngTime) //获取设备版本信息 baseBl.firmwareVersion(withBleCmdTypeFB: .firmwareVersion) //设置系统信息 baseBl.setSystemUserWithBleCmdTypeFB(.setSystemUser) //设置个人信息 let userInfo = UserDefaultsManagerFrieBoltt.getUserInfo()! sendBleCmdSetUserInfo(user: userInfo) // 读取手环电量 baseBl.requestBattery(withBleCmdTypeFB: .requestBattery) // 读取手环配置请求 baseBl.readProfile(withBleCmdTypeFB: .readProfile) // 读取手环设置请求 baseBl.readBraceletSet(withBleCmdTypeFB: .readBraceletSet) // 获取当前实时数据 baseBl.realTimesync(withBleCmdTypeFB: .realTimesync, syncType: 3) baseBl.realTimesync(withBleCmdTypeFB: .realTimesync, syncType: 2) baseBl.realTimesync(withBleCmdTypeFB: .realTimesync, syncType: 8) } /// 读取手环配置请求 func readDeviceProfile() { let bluetooth = BluetoothFireBoltt.shareInstance() if let isConnected = bluetooth?.isConnected { if isConnected { // 读取手环配置请求 baseBl.readProfile(withBleCmdTypeFB: .readProfile) } } } /// 同步健康数据 + 设备配置数据 func fbsendSyncDeviceData() { // 请求次数置零 totalCount = 0 // 获取功能适配 let adapterInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo()?.adapterInfo let immediately = adapterInfo != nil // 同步数据,延时等待适配功能获取完成(后续可以优化为获取适配ok后才绑定成功) GCDTimer.shared.scheduledDispatchTimer(WithTimerName: SendSyncDeviceDataTimer, timeInterval: 3.0, queue: .main, repeats: false, immediately: immediately) { [weak self] in guard let `self` = self else { return } let adapterInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo()?.adapterInfo // 同步配置信息 self.baseBl.readProfile(withBleCmdTypeFB: .readProfile) // 同步健康数据,分别获取步数,睡眠数据,心率,运动 DataBaseManagerFireBoltt.shared.fbqueryLastRecordData(dataType: .step) { (timestamp) in self.getHealthData(timestamp: timestamp, type: 3) } DataBaseManagerFireBoltt.shared.fbqueryLastRecordData(dataType: .sleep) { (timestamp) in self.getHealthData(timestamp: timestamp, type: 1) } DataBaseManagerFireBoltt.shared.fbqueryLastRecordData(dataType: .heartRate) { (timestamp) in self.getHealthData(timestamp: timestamp, type: 2) } if adapterInfo?.bodyTemperature ?? false == true { DataBaseManagerFireBoltt.shared.fbqueryLastRecordData(dataType: .temperature) { (timestamp) in self.getHealthData(timestamp: timestamp, type: 8) } } if adapterInfo?.bloodPressure ?? false == true { DataBaseManagerFireBoltt.shared.fbqueryLastRecordData(dataType: .bloodPressure) { (timestamp) in self.getHealthData(timestamp: timestamp, type: 5) } } if adapterInfo?.bloodOxygen ?? false == true { DataBaseManagerFireBoltt.shared.fbqueryLastRecordData(dataType: .bloodOxygen) { (timestamp) in self.getHealthData(timestamp: timestamp, type: 7) } } self.totalCount += 1 let todaytimeStr = "\(DateClass.getNowTimeS())" self.baseBl.syncHealth(withBleCmdTypeFB: .syncHealth, type: 4, timeArr: [todaytimeStr]) // 开始同步 self.totalRequestCount = BluetoothFireBoltt.shareInstance()!.bluetoothItems.count self.syncState.accept(.syncing(progress: self.progress)) } } private func getHealthData(timestamp: Int?, type: Int) { // 手环最多存储七天数据,若相差大于七天则取七天数据 var count = 0 // 本地存在,则取最后时间->当前时间的日期数据 if let timeInterval = timestamp { //创建日期 let createDate = Date(timeIntervalSince1970: TimeInterval(timeInterval)) //日历对象(方便比较两个日期之间的差距) let calendar = Calendar.current // NSCalendarUnit枚举代表想获得哪些差值 let cmps = calendar.dateComponents([.year, .month, .day], from: createDate, to: Date()) if let days = cmps.day { count = days > 7 ? 7 : (days > 0 ? days : 2) } }else { count = 7 } // 睡眠数据向前推多一天 if (type == 1) { count += 1 } // 默认至少请求今天以及昨天数据 if (count < 2) { count = 2 } // 记录总共发送多少次请求 totalCount += count // 根据时间差距分别获取数据 for i in 1...count { let time = "\(Int32(Date().timeIntervalSince1970) - Int32((count - i) * 86400))" // 获取数据 baseBl.syncHealth(withBleCmdTypeFB: .syncHealth, type: type, timeArr: [time]) } } } // MARK: - 设备控制指令处理 extension BluetoothService { func handleDeviceCmd(blecmd: BleCMD_FireBoltt, data: NSData) { // 找手机 if blecmd == .findPhone { // 0:停止查找 1:开始查找 let state = data.bytes.load(as: Int.self) if state == 0 { stopFindPhone() }else { if !isPlaying { setVoiceStatus() } } } // 电量 if blecmd == .batteryBack { let power = (data.subdata(with: NSRange(location: 0, length: 1)) as NSData).bytes.load(as: Int.self) let status = (data.subdata(with: NSRange(location: 1, length: 1)) as NSData).bytes.load(as: Int.self) self.power.accept(power) if status == 2 { powerState.accept(R.image.fb_power_charge_icon()) return } powerState.accept(getPowerImage(power: power)) } } func setVoiceStatus() { // 初始化播音器 let audioSession = AVAudioSession.sharedInstance() // 设置播音类型 try? audioSession.setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker) //设置支持后台 try? audioSession.setActive(true) // 播放系统音乐 if let path = Bundle.main.path(forResource: "fbs_music", ofType: "m4r") { AudioServicesCreateSystemSoundID(URL(fileURLWithPath: path) as CFURL, &systemSoundID) } // 重复响铃 //添加音频结束时的回调 let observer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) AudioServicesAddSystemSoundCompletion(systemSoundID, nil, nil, { (soundID, inClientData) -> Void in let mySelf = Unmanaged.fromOpaque(inClientData!) .takeUnretainedValue() mySelf.AudioCallback(soundID: soundID) mySelf.systemAudioCallBack() }, observer) AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) AudioServicesPlaySystemSound(SystemSoundID(systemSoundID)) GlobalDeviceProfileModel.shareInstance.isPlaying = true isPlaying = true showFindPhoneAlert() localNotification(title: MultiLanguageKey_FB.tipFB.localized, body: MultiLanguageKey_FB.findThePhoneCMDFB.localized) } func showFindPhoneAlert() { let alertVC = UIAlertController(title: MultiLanguageKey_FB.tipFB.localized, message: MultiLanguageKey_FB.findThePhoneCMDFB.localized, preferredStyle: .alert) let sureAction = UIAlertAction(title: MultiLanguageKey_FB.cancelFB.localized, style: UIAlertAction.Style.default) { [weak self] (action) in guard let `self` = self else { return } self.stopFindPhone() } alertVC.addAction(sureAction) self.alertController = alertVC currentViewController()?.present(alertVC, animated: true, completion: nil) } /// 响铃震动方法 func systemAudioCallBack() { AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) } func AudioCallback(soundID: SystemSoundID) { AudioServicesPlaySystemSound(soundID) } func stopFindPhone() { isPlaying = false AudioServicesDisposeSystemSoundID(systemSoundID) AudioServicesRemoveSystemSoundCompletion(systemSoundID) AudioServicesDisposeSystemSoundID(kSystemSoundID_Vibrate) AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate) systemSoundID = 0 alertController?.dismiss(animated: true, completion: nil) } // MARK: - 发送本地通知 func localNotification(title: String, body: String, dateComponents: DateComponents? = nil, userInfo : [AnyHashable : Any]? = nil) { if #available(iOS 10, *) { let content = UNMutableNotificationContent() content.title = title content.body = body content.userInfo = userInfo ?? [:] var trigger: UNNotificationTrigger? if let dataCompontnts = dateComponents { trigger = UNCalendarNotificationTrigger(dateMatching: dataCompontnts, repeats: false) } let random = Int(arc4random_uniform(999999999)) let request = UNNotificationRequest(identifier: "id\(random)", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in print(error ?? "error:nil") }) } else { let localNotification = UILocalNotification() localNotification.fireDate = dateComponents?.date ?? Date() localNotification.alertBody = body localNotification.alertTitle = title localNotification.userInfo = userInfo localNotification.timeZone = NSTimeZone.default localNotification.soundName = UILocalNotificationDefaultSoundName UIApplication.shared.scheduleLocalNotification(localNotification) } } } // MARK: - 设备控制指令处理 extension BluetoothService: BLEBLDelegate { func finishedHandle(_ bl: BLEBaseBlFireBoltt!, data: [AnyHashable : Any]!, cmd: BleCMD_FireBoltt) { switch cmd { case .setAlarmClock: print("") case .setSportTarget: print("") case .setUserInfo: print("") case .setTheLost: print("") case .setSedentary: print("") case .setSleep: print("") case .setSystemUser: print("") case .setDrinkRemind: print("") case .setPush: print("") case .readBraceletSet: // 保存mac地址 if let macStr = data["MACString"] as? String { if var deviceInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo() { deviceInfo.MACString = macStr UserDefaultsManagerFrieBoltt.saveDeviceInfo(deviceInfo: deviceInfo) } } case .readProfile: let profileModel = GlobalDeviceProfileModel.shareInstance if data.keys.count == 0 { return } //闹钟 let alarmClocks = data["AlarmClock"] as! [[String: Any]] let alarmModels = [AlarmModel].deserialize(from: alarmClocks) as? [AlarmModel] profileModel.alarmModels = alarmModels?.filter{ $0.Label == "1" } //提醒模式 let remindMode = data["RemindMode"] as! [String: Any] if let mode = remindMode["RemindType"] { profileModel.remindMode = (mode as? Int) ?? 1 } //久坐设置 let sedentary = data["Sedentary"] as! [String: Any] profileModel.sedentaryModel = SedentaryModel.deserialize(from: sedentary) //心率检测设置 let heartRate = data["HeartRate"] as! [String: Any] profileModel.heartRateModel = HeartRateCheckModel.deserialize(from: heartRate) //喝水提醒 let drink = data["DrinkRemind"] as! [String: Any] profileModel.drinkModel = DrinkingModel.deserialize(from: drink) //免打扰 let noDisturb = data["NotDisturb"] as! [String: Any] profileModel.noDisturbModel = NoDisturbModel.deserialize(from: noDisturb) //抬手亮屏 let gesturesSet = data["GesturesDict"] as! [String: Any] profileModel.gestureModel = GestureSetModel.deserialize(from: gesturesSet) // 推送设置 let push = data["SetPush"] as! [String: Any] profileModel.pushSetValue = push["IsOpen"] as! Int // 体温检测设置 let temperature = data["Temperature"] as! [String: Any] profileModel.temperatureModel = TemperatureCheckModel.deserialize(from: temperature) // 读取到手环配置发送通知 let notificationName = Notification.Name(rawValue: ReadProfileNotice) NotificationCenter.default.post(name: notificationName, object: nil) case .fireBoltt_weatherPush: print("") case .fireBoltt_ultravioletPush: print("") case .fireBoltt_airPressurePush: print("") case .fireBoltt_altitudePush: print("") case .requestBattery: // 更新电量 let power = data["power"] as? Int ?? 0 let status = data["status"] as? Int ?? 0 self.power.accept(power) if status == 2 { powerState.accept(R.image.fb_power_charge_icon()) return } powerState.accept(getPowerImage(power: power)) case .batteryBack: print("") case .unbundledDevice: print("") case .unbundledReply: print("") case .bindingReply: print("") case .relationDisplay: print("") case .gesturesControl: print("") case .bindingDevice: print("") case .cameraSwitch: print("") case .cameraON: print("") case .cameraOFF: print("") case .dialPush: print("") case .dialRequest: print("") case .findPhone: print("") case .findDevice: print("") case .syncHealth: let dicData = data as! [String:Any] handleHealthData(dict: dicData) totalCount -= 1 // 进度更新 progressUpdate() case .syncBack: print("") case .sleepData: print("") case .stepData: print("") case .heartData: print("") case .sportModelData: print("") case .realTimesync: // 实时数据 guard let temp = data["DataType"] as? Int, let bleCmd = BleCMD_FireBoltt(rawValue: temp) else { return } handleRealTimeHealthData(bleCmd: bleCmd, dict: data as! [String : Any]) case .realStep: print("") case .realHeartData: print("") case .bloodPressure: print("") case .bloodOxygen: print("") case .realBloodPressure: print("") case .realBloodOxygen: print("") case .sportStateToDevice: print("") case .sportStateToIphone: print("") case .gpsSignalStrength: print("") case .firmwareUpdate: print("") case .firmwareVersion: // 保存固件信息 let firwareModel = FirmwareModel.deserialize(from: data as? [String: Any]) GlobalDeviceProfileModel.shareInstance.firwareModel = firwareModel // 保存适配号 if let adapterNumbe = firwareModel?.adapterNumber, var deviceInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo() { deviceInfo.adapterNumber = adapterNumbe UserDefaultsManagerFrieBoltt.saveDeviceInfo(deviceInfo: deviceInfo) } // 获取云端固件信息 & 云端固件适配信息 if let adapterNumber = firwareModel?.adapterNumber, let firmwareVersion = firwareModel?.firmwareVersion { ProviderRequest(.getLastAppUpgrade(packageName: "FireBoltt.\(adapterNumber)")) .subscribe(onNext: { [weak self] json in var url = json["data"]["url"].stringValue url += json["data"]["apkName"].stringValue let version = json["data"]["version"].intValue let newVersion = String(format: "%d.%d.%d", version / 1000, version / 100 % 10, version % 100) let isNew = newVersion > firmwareVersion let isMandatory = json["data"]["imposed"].boolValue self?.cloudFirmwareInfo.onNext((isNew, isMandatory)) }) .disposed(by: rx.disposeBag) ProviderRequest(.getDeviceFunByBrandCode(brandCode: adapterNumber)) .subscribe(onNext: { [weak self] json in if let data = json["data"].array, data.count > 0 { let funcCmds = data.map { $0["funcCmd"].intValue } let adapterInfo = AdapterInfo(funcCmds: funcCmds, adapterNumber: adapterNumber) if var deviceInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo() { deviceInfo.adapterInfo = adapterInfo UserDefaultsManagerFrieBoltt.saveDeviceInfo(deviceInfo: deviceInfo) } // 更新当前页面 self?.adapterInfoUpdate.onNext(adapterInfo) } }) .disposed(by: rx.disposeBag) } case .remindSwitch: print("") case .notDisturb: print("") case .remindModel: print("") case .messageSync: print("") case .getSleepData: print("") case .sleepDataBack: print("") case .checkDaySport: print("") case .heartRateCheck: print("") case .qrCodePush: NotificationCenter.default.post(name: Notification.Name(rawValue: CmdSuccess), object: cmd) print("") case .weatherIndex: print("") case .braceletPialPush: print("") case .unitSetting: print("") case .restoreFactory: print("") case .syncContact: NotificationCenter.default.post(name: Notification.Name(rawValue: CmdSuccess), object: cmd) default: break } // 发送指令完成通知 NotificationCenter.default.post(name: Notification.Name(rawValue: CmdSuccess), object: cmd) } func timeOutHandle(_ bl: BLEBaseBlFireBoltt!, data: [AnyHashable : Any]!, cmd: BleCMD_FireBoltt) { if cmd == .syncHealth { switch syncState.value { case .syncing: // 进度更新 progressUpdate() default: break } } // 发送指令超时通知 NotificationCenter.default.post(name: Notification.Name(rawValue: CmdTimeout), object: cmd) } /// 进度更新 private func progressUpdate() { // 进度更新 let itemsCount = BluetoothFireBoltt.shareInstance()!.bluetoothItems.count let realProgress = Double(totalRequestCount - itemsCount) / Double(totalRequestCount) if realProgress - progress >= 0.2 { progress += 0.2 syncState.accept(.syncing(progress: progress)) } // 同步完成 if totalCount <= 0 && progress < 1.0 { progress = 1.0 syncState.accept(.normal) } // 完成了恢复默认值 if progress >= 1 { progress = 0.2 } } } // MARK: - 健康数据存储 extension BluetoothService { private func handleHealthData(dict: [String: Any]) { // 当前设备信息 let deviceInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo() let macID = deviceInfo?.MACString ?? "" // 计步数据 if let data = dict["STEP"], let stepModel = StepModel.deserialize(from: data as? [String: Any]) { // 小于等于0不保存 if stepModel.stepNumber <= 0 { return } stepModel.macID = macID DataBaseManagerFireBoltt.shared.insertModels(models: [stepModel], dataType: .step) } if let data = dict["SLEEP"], let sleepModel = SleepModel.deserialize(from: data as? [String: Any]) { // 小于等于0不保存 if sleepModel.totalSleepDuration <= 0 { return } sleepModel.macID = macID // 睡眠时间显示处理,若入睡时间>12点,收到数据+1天 let fallingSleepHour = DateClass.getTimeStrToDate(formatStr: "yyyy-MM-dd HH:mm", timeStr: sleepModel.fallingSleepTimes).hour() if fallingSleepHour > 12, let dataDate = DateClass.getTimeStrToDate(formatStr: "yyyy-MM-dd", timeStr: sleepModel.dataDate).getDay(offset: 1)?.string(withFormat: "yyyy-MM-dd") { sleepModel.dataDate = dataDate } DataBaseManagerFireBoltt.shared.insertModels(models: [sleepModel], dataType: .sleep) // 睡眠更新 sleepUpdate.onNext(()) } if let data = dict["HEARTRATE"], let heartRateModel = HeartRateModel.deserialize(from: data as? [String: Any]) { // 小于等于0不保存 if heartRateModel.heartAvg <= 0 { return } heartRateModel.macID = macID DataBaseManagerFireBoltt.shared.insertModels(models: [heartRateModel], dataType: .heartRate) } if let data = dict["SPORT"], let sportModels = [SportModel].deserialize(from: data as? [[String: Any]]) as? [SportModel] { var temps: [SportModel] = [] sportModels.forEach { sportModel in sportModel.macID = macID // 小于等于0不保存 if sportModel.duration > 0 { temps.append(sportModel) } } DataBaseManagerFireBoltt.shared.insertModels(models: temps, dataType: .sport) // 运动更新 sportUpdate.onNext(()) } if let data = dict["TEMPERATURE"], let model = TemperatureModel.deserialize(from: data as? [String: Any]) { // 小于等于0不保存 if model.temperatureAvg <= 0 { return } model.macID = macID DataBaseManagerFireBoltt.shared.insertModels(models: [model], dataType: .temperature) } if let data = dict["BLOODPRESSURE"], let model = BloodPressureModel.deserialize(from: data as? [String: Any]) { // 小于等于0不保存 if model.sbpAvg <= 0 { return } model.macID = macID DataBaseManagerFireBoltt.shared.insertModels(models: [model], dataType: .bloodPressure) } if let data = dict["BLOODOXYGEN"], let model = BloodOxygenModel.deserialize(from: data as? [String: Any]) { // 小于等于0不保存 if model.bloodOxygenAvg <= 0 { return } model.macID = macID DataBaseManagerFireBoltt.shared.insertModels(models: [model], dataType: .bloodOxygen) } } private func handleRealTimeHealthData(bleCmd: BleCMD_FireBoltt, dict: [String: Any]) { let deviceInfo = UserDefaultsManagerFrieBoltt.getDeviceInfo() let macID = deviceInfo?.MACString ?? "" switch bleCmd { case .realStep: guard let realtimeStepModel = RealtimeStepModel.deserialize(from: dict) else { return } if realtimeStepModel.stepNumber <= 0 { return } realStep.accept(realtimeStepModel) // 缓存实时计步 if let json = realtimeStepModel.toJSON() { UserDefaultsManagerFrieBoltt.saveObjectsToSandBox(key: UserDefaultsKey.kfRealTimeStep, value: json) } // // 计步数据 // let stepModel = StepModel() // let dataDate = DateClass.timeIntervalChangeToTimeStr(timeInterval: Double(realtimeStepModel.timestamp)) // // stepModel.macID = macID // stepModel.dataDate = dataDate // stepModel.stepNumber = realtimeStepModel.stepNumber // stepModel.stepDistance = realtimeStepModel.stepDistance // stepModel.stepCalorie = realtimeStepModel.stepCalorie // DataBaseManagerFireBoltt.shared.insertModels(models: [stepModel], dataType: .step) case .realHeartData: guard let realtimeHeartRateModel = RealtimeHeartRateModel.deserialize(from: dict) else { return } if realtimeHeartRateModel.heartRate <= 0 { return } realHeartRate.accept(realtimeHeartRateModel) // 缓存实时心率 if let json = realtimeHeartRateModel.toJSON() { UserDefaultsManagerFrieBoltt.saveObjectsToSandBox(key: UserDefaultsKey.kfRealTimeHeartRate, value: json) } case .realTemperature: guard let model = RealtimeTemperatureModel.deserialize(from: dict) else { return } if model.temperature <= 0 { return } realTemperature.accept(model) // 缓存实时体温 if let json = model.toJSON() { UserDefaultsManagerFrieBoltt.saveObjectsToSandBox(key: UserDefaultsKey.kfRealTimeTemperature, value: json) } case .realBloodPressure: guard let model = RealtimeBloodPressureModel.deserialize(from: dict) else { return } if model.sbp <= 0 { return } realBloodPressure.accept(model) // 缓存实时体温 if let json = model.toJSON() { UserDefaultsManagerFrieBoltt.saveObjectsToSandBox(key: UserDefaultsKey.kfRealTimeBloodPressure, value: json) } case .realBloodOxygen: guard let model = RealtimeBloodOxygenModel.deserialize(from: dict) else { return } if model.bloodOxygen <= 0 { return } realBloodOxygen.accept(model) // 缓存实时体温 if let json = model.toJSON() { UserDefaultsManagerFrieBoltt.saveObjectsToSandBox(key: UserDefaultsKey.kfRealTimeBloodOxygen, value: json) } default: break } } } extension BluetoothService { private func getPowerImage(power: Int) -> UIImage? { if power > 75 { return R.image.power_4() } if power > 50 { return R.image.power_3() } if power > 25 { return R.image.power_2() } if power > 10 { return R.image.power_1() } return R.image.power_0() } }