You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
976 lines
41 KiB
976 lines
41 KiB
// |
|
// 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<Bool>(value: false) |
|
// 电量 |
|
let power = BehaviorRelay<Int>(value: 0) |
|
let powerState = BehaviorRelay<UIImage?>(value: R.image.power_0()) |
|
// 同步状态 |
|
let syncState = BehaviorRelay<HealthDataSyncState>(value: .normal) |
|
// 实时步数 |
|
let realStep = BehaviorRelay<RealtimeStepModel>(value: RealtimeStepModel()) |
|
// 实时心率 |
|
let realHeartRate = BehaviorRelay<RealtimeHeartRateModel>(value: RealtimeHeartRateModel()) |
|
// 实时体温 |
|
let realTemperature = BehaviorRelay<RealtimeTemperatureModel>(value: RealtimeTemperatureModel()) |
|
// 实时血压 |
|
let realBloodPressure = BehaviorRelay<RealtimeBloodPressureModel>(value: RealtimeBloodPressureModel()) |
|
// 实时血氧 |
|
let realBloodOxygen = BehaviorRelay<RealtimeBloodOxygenModel>(value: RealtimeBloodOxygenModel()) |
|
|
|
// 运动数据 |
|
let sportUpdate = PublishSubject<Void>() |
|
// 睡眠数据 |
|
let sleepUpdate = BehaviorSubject<Void>(value: ()) |
|
|
|
/// 云端固件信息 |
|
let cloudFirmwareInfo = PublishSubject<(isNew: Bool, isMandatory: Bool)>() |
|
|
|
/// 固件适配信息更新 |
|
let adapterInfoUpdate = PublishSubject<AdapterInfo>() |
|
|
|
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<BluetoothService>.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("运动模式数据返回") |
|
let dicData = data as! [String:Any] |
|
handleHealthData(dict: dicData) |
|
totalCount -= 1 |
|
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() |
|
} |
|
|
|
}
|
|
|