From: liangzhenying <867174124@qq.com>
Date: Tue, 5 Nov 2024 03:37:15 +0000 (+0800)
Subject: 重构文件传输
X-Git-Tag: ç»èæµ·æä¾sdk~5
X-Git-Url: http://git.snicker.com.cn/sitemap.xml?a=commitdiff_plain;h=69655b421fa3fffe4541353c31f1dc2e51fc7e03;p=mirrors%2FHsFileAgentSdk_iOS.git
重构文件传输
---
diff --git a/code/IOTBluetooth/Example/AbilityViewController.swift b/code/IOTBluetooth/Example/AbilityViewController.swift
new file mode 100644
index 0000000..2261447
--- /dev/null
+++ b/code/IOTBluetooth/Example/AbilityViewController.swift
@@ -0,0 +1,170 @@
+//
+// AbilityViewController.swift
+// Example
+//
+// Created by yaozuopan on 2024/3/11.
+//
+
+import UIKit
+import IOTBluetooth
+
+class AbilityViewController: BaseViewController {
+
+
+
+ @IBOutlet weak var listView: UITableView!
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Log", style: .plain, target: self, action: #selector(showLog))
+ }
+
+ @objc func showLog(){
+ let vc = storyboard!.instantiateViewController(identifier: "LogViewController")
+ navigationController?.pushViewController(vc, animated: true)
+ }
+}
+
+extension AbilityViewController: UITableViewDelegate, UITableViewDataSource {
+
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ datas.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let data = datas[indexPath.row]
+ let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
+ let label = cell.viewWithTag(10) as! UILabel
+ label.text = "\(indexPath.row) " + data.ability.description + " \(data.label)"
+ return cell
+ }
+
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+ let data = datas[indexPath.row]
+ if data.label.isEqual("åéOTAæä»¶"){
+ self.sendOta()
+ }
+ else if data.label.isEqual("åéMP3æä»¶"){
+ self.sendMp3()
+ }
+ else if data.label.isEqual("åéEPOæä»¶"){
+ self.sendEpo()
+ }
+ else if data.label.isEqual("åéèªå®ä¹è¡¨çæä»¶"){
+ self.sendCustomDial()
+ }
+ else if data.label.isEqual("åéå¸åºè¡¨çæä»¶"){
+ self.sendMarketDial()
+ }
+ else if data.label.isEqual("æ¤éå½åæä»¶ä¼ è¾"){
+ self.revokeFileSend()
+ }
+ else{
+ activityIndicatorView.startAnimating()
+ IBSender()
+ .ability(data.ability)
+ .parameters(data.parameters)
+ .response {[weak self] result in
+ guard let self = self else {return}
+ print(result)
+ activityIndicatorView.stopAnimating()
+ }
+ }
+ }
+}
+extension AbilityViewController{
+ func sendOta(){
+ guard let path = Bundle.main.path(forResource: "20240604_155433_2_V1.0.1_20240618_113111_ota", ofType: "fwpkg") else {return}
+ let info = IBSendOta()
+ info.major = 2
+ info.mid = 1
+ info.minor = 4
+ info.isForced = 0
+ info.filePath = path
+
+ activityIndicatorView.startAnimating()
+ IBFileSender().ota(info).progress({ total, current in
+// print("\(total) : \(current)")
+ }).response {[weak self] result in
+ print(result)
+ self?.activityIndicatorView.stopAnimating()
+ }
+ }
+ func sendMp3(){
+ guard let path = Bundle.main.path(forResource: "大约å¨å¬å£", ofType: "mp3") else {return}
+ let info = IBSendMp3()
+ info.name = "大约å¨å¬å£.mp3"
+ info.filePath = path
+
+ activityIndicatorView.startAnimating()
+ IBFileSender().mp3(info).progress({ total, current in
+ print("\(total) : \(current)")
+ }).response {[weak self] result in
+ print(result)
+ self?.activityIndicatorView.stopAnimating()
+ }
+ }
+ func sendEpo(){
+
+ }
+ func sendCustomDial(){
+ //èªå®ä¹è¡¨çç®å硬件端è¿ä¸æ¯æ
+// guard let path = Bundle.main.path(forResource: "galaxy", ofType: "lzma") else {return}
+// let file = IBDialFile()
+// file.filePath = path
+// file.fileName = "galaxy.lzma"
+//
+// let info = IBDial()
+// info.UID = 0
+// info.files = [file]
+//
+// info.batteryStyle = 1
+// info.timeStyle = 1
+// info.timeColor = 1
+//
+// activityIndicatorView.startAnimating()
+// IBFileSender().dial(info).progress({ total, current in
+// print("\(total) : \(current)")
+// }).response {[weak self] result in
+// print(result)
+// self?.activityIndicatorView.stopAnimating()
+// }
+ }
+
+ //ç®ååªæ¯æè¡¨çå¸åº
+ func sendMarketDial(){
+//ç³»ç»è¡¨ç
+// ID: 101 | Name: sportdial
+// ID: 102 | Name: kaleidoscope
+// ID: 1052 | Name: classic
+// ID: 103 | Name: jellyfish
+// ID: 1028 | Name: KUMI
+
+ guard let path = Bundle.main.path(forResource: "galaxy", ofType: "lzma") else {return}
+ let file = IBDialFile()
+ file.filePath = path
+ file.fileName = "galaxy.lzma"
+
+ let info = IBDial()
+ info.UID = 10002
+ info.files = [file]
+
+ info.theme = "黿"
+
+// activityIndicatorView.startAnimating()
+ IBFileSender().dial(info).progress({ total, current in
+ print("\(total) : \(current)")
+ }).response {[weak self] result in
+ print(result)
+// self?.activityIndicatorView.stopAnimating()
+ }
+ }
+
+ func revokeFileSend(){
+ IBFileSender().revokeFileSend(){ result in
+ print(result)
+ }
+ }
+}
diff --git a/code/IOTBluetooth/Example/AppDelegate.swift b/code/IOTBluetooth/Example/AppDelegate.swift
new file mode 100644
index 0000000..b65ac44
--- /dev/null
+++ b/code/IOTBluetooth/Example/AppDelegate.swift
@@ -0,0 +1,24 @@
+//
+// AppDelegate.swift
+// Example
+//
+// Created by yaozuopan on 2024/3/11.
+//
+
+import UIKit
+import IOTBluetooth
+
+@main
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ // Override point for customization after application launch.
+ HsFileAgentSdk.shared.isDebug = true
+ HsFileAgentSdk.shared.initSDK()
+
+ return true
+ }
+}
+
diff --git a/code/IOTBluetooth/Example/Assets.xcassets/AccentColor.colorset/Contents.json b/code/IOTBluetooth/Example/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/code/IOTBluetooth/Example/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/code/IOTBluetooth/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/code/IOTBluetooth/Example/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..13613e3
--- /dev/null
+++ b/code/IOTBluetooth/Example/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,13 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/code/IOTBluetooth/Example/Assets.xcassets/Contents.json b/code/IOTBluetooth/Example/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/code/IOTBluetooth/Example/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/code/IOTBluetooth/Example/Base.lproj/LaunchScreen.storyboard b/code/IOTBluetooth/Example/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..865e932
--- /dev/null
+++ b/code/IOTBluetooth/Example/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/IOTBluetooth/Example/Base.lproj/Main.storyboard b/code/IOTBluetooth/Example/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..5a59854
--- /dev/null
+++ b/code/IOTBluetooth/Example/Base.lproj/Main.storyboard
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/IOTBluetooth/Example/BaseViewController.swift b/code/IOTBluetooth/Example/BaseViewController.swift
new file mode 100644
index 0000000..c7ab315
--- /dev/null
+++ b/code/IOTBluetooth/Example/BaseViewController.swift
@@ -0,0 +1,36 @@
+//
+// BaseViewController.swift
+// Example
+//
+// Created by yaozuopan on 2024/3/14.
+//
+
+import UIKit
+
+class BaseViewController: UIViewController {
+
+ func showAlert(_ title: String? = nil, msg: String, confirmLabel: String? = nil, onConfirm: (() -> Void)? = nil) {
+ let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: "è¿å", style: .cancel))
+
+ if let confirmLabel = confirmLabel {
+ alert.addAction(UIAlertAction(title: confirmLabel, style: .default, handler: { _ in
+ onConfirm?()
+ }))
+ }
+ present(alert, animated: true)
+ }
+
+ let activityIndicatorView = UIActivityIndicatorView(style: .large)
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ activityIndicatorView.frame = UIScreen.main.bounds
+ activityIndicatorView.layer.zPosition = 1
+ view.addSubview(activityIndicatorView)
+ }
+
+ deinit {
+ print("deinit \(Self.self)")
+ }
+}
diff --git a/code/IOTBluetooth/Example/Datas.swift b/code/IOTBluetooth/Example/Datas.swift
new file mode 100644
index 0000000..98c64a2
--- /dev/null
+++ b/code/IOTBluetooth/Example/Datas.swift
@@ -0,0 +1,132 @@
+//
+// Datas.swift
+// Example
+//
+// Created by yaozuopan on 2024/3/19.
+//
+
+import Foundation
+import IOTBluetooth
+
+
+
+let datas: [(ability: IBAbility, label: String, parameters: [IBParameter])] = [
+ (.userData,"è®¾ç½®ç¨æ·æ°æ®",[.userInfo(IBUserInfo())]),
+ (.language, "设置è¯è¨", [.language(.chinese)]),
+ (.about,"å
³äºæ¬æºå
容",[]),
+ (.find, "å¼å§æ¥æ¾æè¡¨", [.action(1)]),
+ (.find, "åæ¢æ¥æ¾æè¡¨", [.action(0)]),
+ (.message, "å¼å¯æ¶æ¯æ¨éåè½", [.action(1)]),
+ (.message, "å
³éæ¶æ¯æ¨éåè½", [.action(0)]),
+
+ (.takePhoto, "å¼å¯æå¿æç
§", [.action(1)]),
+ (.takePhoto, "å
³éæå¿æç
§", [.action(0)]),
+ (.restore, "æ¢å¤åºå设置", []),
+ (.reboot, "éæ°å¯å¨", []),
+ (.shutdown, "å
³æº", []),
+ (.fetchScreen, "è·åæè¡¨å±å¹åæ°", []),
+ (.screen, "设置æè¡¨å±å¹åæ°", [.screen(Config.setScreen())]),
+ (.fetchBattery, "è·åçµé", []),
+ (.fetchClock, "è·åé¹é设置", []),
+ (.setClock, "åéé¹é设置", [.clock(IBClock())]),
+
+ (.fetchCurrentDialId, "åéå½å表çID", []),
+ (.switchDial, "åæ¢è¡¨ç", [.dialID(1028)]),
+ (.deleteDial, "å é¤è¡¨ç", [.dialID(10001)]),
+ (.fetchDials, "è·åææè¡¨çUID", []),
+ (.checkDialID, "è·åæè¡¨çIDæ¯å¦å·²å®è£
", [.dialID(10001)]),
+
+ (.fetchWatchState, "è·åå½åæè¡¨ç¶æ", []),
+ (.fetchVibrateSound, "è·åæè¡¨å£°é³ä¸éå¨åæ°", []),
+ (.vibrateSound, "设置æè¡¨å£°é³ä¸éå¨åæ°", [.vibrateSound(Config.setVibrateSound())]),
+ (.fetchAuxiliary, "è·åæè¡¨è¾
å©åè½åæ°", []),
+ (.auxiliary, "设置æè¡¨è¾
å©åè½åæ°", [.auxiliary(.bloodOxygen)]),
+ (.unit, "设置æè¡¨å
¬è±å¶åä½", [.value(1)]),
+
+
+ (.disk, "è·åSDå¡å©ä½ç©ºé´", []),
+ (.unpair, "åéè§£é¤é
å¯¹æ¶æ¯", []),
+ (.sendUnpair, "åéåæ¶è§£é¤é
å¯¹æ¶æ¯", []),
+
+
+ (.startSport, "å¼å¯æå®è¿å¨æ¨¡å¼", [.motionMode(.walking)]),
+ (.trainPauseResume, "è®ç»æå", [.action(0)]),
+ (.trainPauseResume, "è®ç»æ¢å¤", [.action(1)]),
+ (.endSport, "ç»æè¿å¨æ¨¡å¼", []),
+
+ (.heartRateMode, "å¼å¯èªå¨è·å宿¶å¿ç", [.action(1)]),
+ (.heartRateMode, "å
³éèªå¨è·å宿¶å¿ç", [.action(0)]),
+ (.heartRateHigh, "设置æé«å¿çæé", [.value(180)]),
+ (.heartRateHigh, "å
³éæé«å¿çæé", [.value(0)]),
+ (.fetchHeartRate, "è·åæè¡¨å¿çåæ°", []),
+ (.heartRate, "设置æè¡¨å¿çåæ°", [.heartRate(IBHeartRate())]),
+ (.currentHeartRate, "è·å宿¶å¿ç", []),
+
+ (.fetchBloodOxygen, "è·åæè¡¨è¡æ°§è®¾ç½®", []),
+ (.bloodOxygen, "设置æè¡¨è¡æ°§åæ°", [.bloodOxygen(IBBloodOxygen())]),
+ (.trainFrequency, "设置è®ç»å®æ¶æ°æ®éæ ·é¢ç", [.value(30)]),
+
+ (.fetchTargetStep, "è·åç®æ æ¥æ°è®¾ç½®", []),
+ (.targetStep, "è®¾ç½®ç®æ æ¥æ°", [.targetStep(IBTargetStep())]),
+ (.fetchOtherTarget, "è·åå
¶ä»ç®æ 设置", []),
+ (.targetOther, "设置å
¶ä»ç®æ ", [.otherTarget(IBOtherTarget())]),
+ (.trainTarget, "设置è®ç»ç®æ ", [.trainTarget(IBTrainTarget())]),
+ (.fetchSleep, "è·åç¡ç 设置", []),
+ (.sleep, "设置ç¡ç ", [.sleep(IBSleep())]),
+ (.fetchWater, "è·åæè¡¨åæ°´æé设置", []),
+ (.water, "è®¾ç½®åæ°´æé", [.water(IBWater())]),
+
+
+ (.syncHeartRate, "忥å¿çå岿°æ®", [.value(0)]),
+ (.syncBloodOxygen, "åæ¥è¡æ°§å岿°æ®", [.value(0)]),
+ (.syncStepCount, "忥å岿¥æ°æ°æ®", [.value(0)]),
+ (.syncFatBurning, "忥çèæ¶é´å岿°æ®", [.value(0)]),
+ (.syncStand, "忥ç«ç«å岿°æ®", [.value(0)]),
+ (.syncCalorie, "忥å¡è·¯éå岿°æ®", [.value(0)]),
+ (.syncDistance, "忥è·ç¦»å岿°æ®", [.value(0)]),
+ (.syncTrain, "忥åºç¡è®ç»å岿°æ®", [.value(0)]),
+ (.syncSleep, "忥ç¡ç å岿°æ®", [.value(0)]),
+ (.syncGPS, "åæ¥è·¯å¾åæ éå岿°æ®", [.value(1654012800)]),
+ (.sendSyncEnd, "åéåæ¥æ°æ®ç»ææ¶æ¯", []),
+ (.addressBook, "åéé讯å½", [.addressBook([IBAddressBook()])]),
+
+// (.epo, "åéEPOæä»¶", []),
+ (.ota, "åéOTAæä»¶", []),
+ (.mp3, "åéMP3æä»¶", []),
+// (.dial, "åéèªå®ä¹è¡¨çæä»¶", []),
+ (.dial, "åéå¸åºè¡¨çæä»¶", []),
+ (.revokeFileSend, "æ¤éå½åæä»¶ä¼ è¾", []),
+
+
+ (.deleteFile, "å 餿宿件", [.deleteFile(IBDeleteFile())]),
+ (.deleteFiles, "å 餿å®ç±»åæææä»¶", [.file(.mp3)]),
+ (.fetchFiles, "è·åæå®ç±»åæææä»¶", [.file(.mp3)]),
+
+ (.sendWeather, "åéå¤©æ°æ°æ®", [.weather(IBWeather())]),
+]
+
+class Config{
+ static func setScreen()->IBScreenConfig{
+ let model = IBScreenConfig()
+ model.type = 0
+ model.brightness = 255
+
+// model.type = 1
+// model.duration = .minute
+// model.wrist = 0
+ return model
+ }
+ static func setVibrateSound()->IBVibrateConfig{
+ let model = IBVibrateConfig()
+// model.type = 0
+// model.systemValue = 92
+
+// model.type = 1
+// model.ringsValue = 48
+//
+ model.type = 3
+ model.ringsState = 1
+ model.vibrateState = 1
+ return model
+ }
+}
diff --git a/code/IOTBluetooth/Example/Info.plist b/code/IOTBluetooth/Example/Info.plist
new file mode 100644
index 0000000..1ec584a
--- /dev/null
+++ b/code/IOTBluetooth/Example/Info.plist
@@ -0,0 +1,15 @@
+
+
+
+
+ UIBackgroundModes
+
+ bluetooth-central
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+
+
diff --git a/code/IOTBluetooth/Example/LogViewController.swift b/code/IOTBluetooth/Example/LogViewController.swift
new file mode 100644
index 0000000..99f5173
--- /dev/null
+++ b/code/IOTBluetooth/Example/LogViewController.swift
@@ -0,0 +1,28 @@
+//
+// LogViewController.swift
+// Example
+//
+// Created by yaozuopan on 2024/3/14.
+//
+
+import UIKit
+import IOTBluetooth
+import IOTBluetooth
+
+class LogViewController: BaseViewController {
+
+ @IBOutlet weak var textView: UITextView!
+
+ override func viewDidLoad() {
+ title = "èçæ¥å¿"
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ textView.text = HsFileAgentSdk.shared.logs.joined(separator: "\n")
+
+ let range = NSMakeRange(textView.text.count - 1, 1)
+ textView.scrollRangeToVisible(range)
+ }
+}
+
diff --git a/code/IOTBluetooth/Example/Resource/20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg b/code/IOTBluetooth/Example/Resource/20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg
new file mode 100644
index 0000000..08e44ee
Binary files /dev/null and b/code/IOTBluetooth/Example/Resource/20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg differ
diff --git a/code/IOTBluetooth/Example/Resource/AIAudio.sbc b/code/IOTBluetooth/Example/Resource/AIAudio.sbc
new file mode 100644
index 0000000..d96322c
Binary files /dev/null and b/code/IOTBluetooth/Example/Resource/AIAudio.sbc differ
diff --git a/code/IOTBluetooth/Example/Resource/galaxy.lzma b/code/IOTBluetooth/Example/Resource/galaxy.lzma
new file mode 100644
index 0000000..fb481b9
Binary files /dev/null and b/code/IOTBluetooth/Example/Resource/galaxy.lzma differ
diff --git "a/code/IOTBluetooth/Example/Resource/\345\244\247\347\272\246\345\234\250\345\206\254\345\255\243.mp3" "b/code/IOTBluetooth/Example/Resource/\345\244\247\347\272\246\345\234\250\345\206\254\345\255\243.mp3"
new file mode 100644
index 0000000..4209e87
Binary files /dev/null and "b/code/IOTBluetooth/Example/Resource/\345\244\247\347\272\246\345\234\250\345\206\254\345\255\243.mp3" differ
diff --git "a/code/IOTBluetooth/Example/Resource/\351\273\204\346\262\231-\345\220\215\345\206\263.mp3" "b/code/IOTBluetooth/Example/Resource/\351\273\204\346\262\231-\345\220\215\345\206\263.mp3"
new file mode 100644
index 0000000..680ab53
Binary files /dev/null and "b/code/IOTBluetooth/Example/Resource/\351\273\204\346\262\231-\345\220\215\345\206\263.mp3" differ
diff --git a/code/IOTBluetooth/Example/ViewController.swift b/code/IOTBluetooth/Example/ViewController.swift
new file mode 100644
index 0000000..5cfa071
--- /dev/null
+++ b/code/IOTBluetooth/Example/ViewController.swift
@@ -0,0 +1,76 @@
+//
+// ViewController.swift
+// Example
+//
+// Created by yaozuopan on 2024/3/11.
+//
+
+import UIKit
+import IOTBluetooth
+import CoreBluetooth
+
+class ViewController: BaseViewController {
+
+ @IBOutlet weak var listView: UITableView!
+ private var dataList: [CBPeripheral] = []
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ title = "IOTBluetooth"
+ navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Log", style: .plain, target: self, action: #selector(showLog))
+ title = "æ«æ"
+ }
+
+
+ @IBAction func startScan(_ sender: UIButton) {
+ IBBluetoothManager.shared.startScan()
+ IBBluetoothManager.shared.discoverCallBack = { [unowned self] peripheral in
+ if dataList.contains(peripheral){
+ return
+ }
+ if let name = peripheral.name,name.isEmpty{
+ return
+ }
+// print(peripheral.name)
+ dataList.append(peripheral)
+ listView.reloadData()
+ }
+ IBBluetoothManager.shared.didConnectedCallBack = {[unowned self] peripheral in
+ activityIndicatorView.stopAnimating()
+ let vc = storyboard!.instantiateViewController(identifier: "AbilityViewController")
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+ IBBluetoothManager.shared.disConnectedCallBack = { peripheral in
+ print("èçå·²ç»æå¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼")
+ }
+ }
+
+ override func viewWillDisappear(_ animated: Bool) {
+ super.viewWillDisappear(animated)
+ IBBluetoothManager.shared.stopScan()
+ }
+ @objc func showLog(){
+ let vc = storyboard!.instantiateViewController(identifier: "LogViewController")
+ navigationController?.pushViewController(vc, animated: true)
+ }
+}
+
+extension ViewController: UITableViewDelegate, UITableViewDataSource {
+
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ dataList.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
+ let label = cell.viewWithTag(10) as! UILabel
+ label.text = dataList[indexPath.row].name ?? "" + "ï¼ç¹å»è¿æ¥ï¼"
+ return cell
+ }
+
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ activityIndicatorView.startAnimating()
+ let device = dataList[indexPath.row]
+ IBBluetoothManager.shared.connect(device)
+ }
+}
diff --git a/code/IOTBluetooth/Example/ble/IBBluetoothManager.swift b/code/IOTBluetooth/Example/ble/IBBluetoothManager.swift
new file mode 100644
index 0000000..042cadf
--- /dev/null
+++ b/code/IOTBluetooth/Example/ble/IBBluetoothManager.swift
@@ -0,0 +1,243 @@
+//
+// IBluetooth.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/2/4.
+//
+
+import Foundation
+import CoreBluetooth
+import IOTBluetooth
+
+/// Max is 482
+let dataPacketMaxBytes = 450
+
+/// 主æå¡
+let serviceUUID: String = "18E0"
+/// Notify ç¹å¾å¼
+let notifyCharacteristicUUID: String = "2AE5"
+/// Read ç¹å¾å¼
+let readCharacteristicUUID: String = "2AE2"
+/// Write ç¹å¾å¼(åéåèæ°â¤20)
+let writeCharacteristicUUID: String = "2AE3"
+/// Write ç¹å¾å¼(åéåèæ°>20)
+let writeBigCharacteristicUUID: String = "2AE4"
+/// Write ç¹å¾å¼(åéæä»¶OATãMP3ç)
+let writeFileCharacteristicUUID: String = "2AE0"
+
+public typealias IBDiscoverHandler = (_ peripheral: CBPeripheral) -> Void
+public typealias IBDidConectedHandler = (_ peripheral: CBPeripheral) -> Void
+public typealias IBDisConectedHandler = (_ peripheral: CBPeripheral) -> Void
+
+class IBBluetoothManager: NSObject {
+ static let shared = IBBluetoothManager()
+
+ /// 2AE5
+ private var notifyCharacteristic: CBCharacteristic!
+ /// 2AE2
+ private var readCharacteristic: CBCharacteristic!
+ /// 2AE3
+ private var writeCharacteristic: CBCharacteristic!
+ /// 2AE4
+ private var writeBigCharacteristic: CBCharacteristic!
+ /// 2AE0
+ private var writeFileCharacteristic: CBCharacteristic!
+
+ /// ä¸å¤®ç®¡çç±»ï¼ç®¡çèççæ«æè¿æ¥
+ var centralManager: CBCentralManager!
+ /// å½åè¿æ¥è®¾å¤
+ var currentPeripheral: CBPeripheral?
+
+ /// æ«æå°è®¾å¤
+ var discoverCallBack:IBDiscoverHandler?
+ /// è¿ä¸è®¾å¤
+ var didConnectedCallBack:IBDidConectedHandler?
+ /// è®¾å¤æå¼è¿æ¥
+ var disConnectedCallBack:IBDisConectedHandler?
+
+ private override init() {
+ super.init()
+ centralManager = CBCentralManager(delegate: self, queue: nil)
+ setupFileSdk()
+ }
+
+ func setupFileSdk(){
+ HsFileAgentSdk.shared.isDebug = true
+ HsFileAgentSdk.shared.sdkSendToDevice { data in
+ print(data)
+ }
+ }
+}
+
+extension IBBluetoothManager{
+ /// å¼å§æ«æ
+ func startScan() {
+ Logger.log("å¼å§æ«æ...")
+ centralManager.scanForPeripherals(withServices: [CBUUID(string: serviceUUID)], options: nil)
+ if let peripheral = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: serviceUUID)]).first{
+ discoverCallBack?(peripheral)
+ }
+ }
+
+ /// åæ¢æ«æ
+ func stopScan(){
+ Logger.log("åæ¢æ«æèç设å¤...")
+ centralManager.stopScan()
+ }
+
+ /// è¿æ¥èç设å¤
+ func connect(_ peripheral: CBPeripheral) {
+ centralManager.connect(peripheral)
+ }
+
+ /// æå¼ä¸èç设å¤çè¿æ¥
+ func cancel(_ peripheral: CBPeripheral) {
+ centralManager.cancelPeripheralConnection(peripheral)
+ }
+
+ /// åéæ°æ®
+ func send(_ sender: IBSender) {
+ //// if currentPeripheral.peripheral.state == .disconnected{
+ //// sender.completion(.response.code(.failure).message("ææºæªè¿ä¸èç设å¤"))
+ //// sender.remove()
+ //// Logger.log("ææºæªè¿ä¸èç设å¤ï¼")
+ //// return
+ //// }
+ //
+ //
+ // for dataPacket in sender.dataPacket {
+ // Thread.sleep(forTimeInterval:0.02)
+ //
+ // Logger.log("\(sender.ability.characteristicUUID!) Write:", dataPacket.data.joined(" "), "Bytes(\(dataPacket.count))")
+ //
+ // var writeCharacteristic: CBCharacteristic = writeCharacteristic
+ // if sender.ability.characteristicUUID == writeBigCharacteristicUUID {
+ // writeCharacteristic = writeBigCharacteristic
+ // }
+ // if sender.ability.characteristicUUID == writeFileCharacteristicUUID {
+ // writeCharacteristic = writeFileCharacteristic
+ // }
+ // currentPeripheral.peripheral.writeValue(dataPacket.data, for: writeCharacteristic, type: .withResponse)
+ // }
+ }
+ func send(fileData data: Data) {
+ //// Thread.sleep(forTimeInterval:0.02)
+ //// if currentPeripheral.peripheral.state == .disconnected{
+ //// self.fileSender?.lastData?.fileWriteHander?(.response.code(.failure).message("ææºæªè¿ä¸èç设å¤"))
+ //// Logger.log("ææºæªè¿ä¸èç设å¤ï¼")
+ //// return
+ //// }
+ //
+ // Logger.log("\(writeFileCharacteristicUUID) Write:", data)
+ // currentPeripheral.peripheral.writeValue(data, for: writeFileCharacteristic, type: .withResponse)
+ }
+}
+
+extension IBBluetoothManager: CBCentralManagerDelegate {
+ func centralManagerDidUpdateState(_ central: CBCentralManager) {
+ switch central.state {
+ case .unknown:
+ Logger.log("unknown")
+ case .resetting:
+ Logger.log("resetting")
+ case .unsupported:
+ Logger.log("unsupported")
+ case .unauthorized:
+ Logger.log("unauthorized")
+ case .poweredOff:
+ Logger.log("poweredOff")
+ case .poweredOn:
+ startScan()
+ Logger.log("poweredOn")
+ @unknown default:
+ break
+ }
+ }
+
+ func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
+// IBManager.discoverHandler?(device)
+ discoverCallBack?(peripheral)
+ }
+
+ func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
+ currentPeripheral = peripheral
+ peripheral.delegate = self
+ peripheral.discoverServices([CBUUID(string: serviceUUID)])
+ Logger.log("è¿æ¥æå", peripheral.name ?? "")
+ didConnectedCallBack?(peripheral)
+ }
+
+ func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
+ Logger.log("è¿æ¥å¤±è´¥",error?.localizedDescription ?? "" )
+ currentPeripheral = nil
+ }
+
+ func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
+ Logger.log("å·²æå¼è¿æ¥")
+ disConnectedCallBack?(peripheral)
+ }
+}
+
+extension IBBluetoothManager: CBPeripheralDelegate {
+ func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
+
+ }
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
+ peripheral
+ .services?
+ .forEach {
+ peripheral.discoverCharacteristics(nil, for: $0)
+ }
+ }
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
+ service
+ .characteristics?
+ .forEach {
+ if $0.uuid.uuidString == notifyCharacteristicUUID {
+ notifyCharacteristic = $0
+ peripheral.setNotifyValue(true, for: $0)
+ return
+ }
+
+ if $0.uuid.uuidString == readCharacteristicUUID {
+ readCharacteristic = $0
+ peripheral.setNotifyValue(true, for: $0)
+ return
+ }
+
+ if $0.uuid.uuidString == writeCharacteristicUUID {
+ writeCharacteristic = $0
+ return
+ }
+
+ if $0.uuid.uuidString == writeBigCharacteristicUUID {
+ writeBigCharacteristic = $0
+ return
+ }
+
+ if $0.uuid.uuidString == writeFileCharacteristicUUID {
+ writeFileCharacteristic = $0
+ return
+ }
+ }
+ }
+ func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
+ peripheral.readValue(for: characteristic)
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
+ if let error = error {
+ Logger.log(error)
+ }
+ HsFileAgentSdk.shared.sendToSdk(characteristic.value!)
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
+ if let error = error {
+ Logger.log("åé失败 ",characteristic.uuid.uuidString,error.localizedDescription)
+ }
+ else{
+ Logger.log(characteristic.uuid.uuidString, "write success")
+ }
+ }
+}
diff --git a/code/IOTBluetooth/Example/ble/IBManager.swift b/code/IOTBluetooth/Example/ble/IBManager.swift
new file mode 100644
index 0000000..18dda24
--- /dev/null
+++ b/code/IOTBluetooth/Example/ble/IBManager.swift
@@ -0,0 +1,79 @@
+////
+//// IBManager.swift
+//// IOTBluetooth
+////
+//// Created by yaozuopan on 2024/3/12.
+////
+//
+//import Foundation
+//
+//public typealias IBDiscoverHandler = (_ peripheral: IBPeripheral) -> Void
+//public typealias IBCompleteHandler = (_ isSuccess: Bool) -> Void
+//
+///// SDK 管çç±»
+//public class IBManager {
+//
+// /// åå§åå·¥ä½
+// public static func initializationSDK() {
+//// IBBluetooth.shared.scan(.default)
+//
+//// DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
+//// IBBluetooth.shared.reconnected()
+//// }
+//// åéå¤©æ°æ°æ® 设å¤éå¯
+//// è·åsdå¡å©ä½ç©ºé´ 失败
+//// åæ¥è·¯å¾åæ éå岿°æ® æ ååº
+//// ç»æè¿å¨æ¨¡å¼åç«å³åéå
¶ä»æä»¤ 设å¤éå¯
+// }
+//
+// public static var logs: [String] { Logger.logs }
+//}
+//
+//public extension IBManager {
+// /// SDK çæ¬å·
+// static let version: String = "1.0.23"
+// /// é»è®¤ä¸º false
+// static var isDebug: Bool = false {
+// didSet { Logger.isDebug = isDebug }
+// }
+//}
+//
+//// èçç¸å
³ API
+//public extension IBManager {
+//
+// internal static var discoverHandler: IBDiscoverHandler?
+//
+// /// æ«æè®¾å¤
+// /// - Parameters:
+// /// - option: æ«æåæ°
+// /// - discoverHandler: ç»æåè°
+// static func scan(_ option: IBScanOption = .default, discoverHandler: @escaping IBDiscoverHandler) {
+// Self.discoverHandler = discoverHandler
+// IBBluetooth.shared.scan(option)
+// }
+//
+// /// åæ¢æ«æèç设å¤
+// static func stopScan() {
+// Self.discoverHandler = nil
+// IBBluetooth.shared.stopScan()
+// }
+//
+// internal static var completeHandler: IBCompleteHandler?
+//
+// /// è¿æ¥è®¾å¤
+// /// - Parameters:
+// /// - peripheral: æ«æåç°ç设å¤
+// /// - completeHandler: ç»æåè°
+// static func connect(_ peripheral: IBPeripheral, completeHandler: @escaping IBCompleteHandler) {
+// Self.completeHandler = completeHandler
+// IBBluetooth.shared.connect(peripheral)
+// }
+//
+// /// æå¼ä¸èç设å¤çè¿æ¥
+// /// - Parameter peripheral: èç设å¤å¯¹å
+// static func cancel(_ peripheral: IBPeripheral) {
+// IBBluetooth.shared.cancel(peripheral)
+// }
+// /// å½åå·²è¿æ¥è®¾å¤
+// static var currentConnected: IBPeripheral! { IBBluetooth.shared.currentConnected}
+//}
diff --git a/code/IOTBluetooth/Example/ble/Logger.swift b/code/IOTBluetooth/Example/ble/Logger.swift
new file mode 100644
index 0000000..3dbf8bf
--- /dev/null
+++ b/code/IOTBluetooth/Example/ble/Logger.swift
@@ -0,0 +1,78 @@
+//
+// Logger.swift
+//
+//
+// Created by Harvey on 2021/4/27.
+//
+
+import Foundation
+
+/// æ¥è®°ç®¡ç
+public class Logger {
+ static var isDebug: Bool = true
+
+ fileprivate static let shared = Logger()
+ private var fileHandle:FileHandle?
+ private init() {
+ let path = getPath()
+ fileHandle = FileHandle(forWritingAtPath: path)!
+// fileHandle?.seekToEndOfFile()
+ }
+
+ fileprivate var dateString: String {
+ let f = DateFormatter()
+ f.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
+ return f.string(from: Date())
+ }
+
+ static var logs: [String] = []
+
+
+ private func getPath()->String{
+// let path = NSHomeDirectory() + "/Documents/IOTBluetooth/" + "\(Date().format(.middle)).log"
+ let foldPath = NSHomeDirectory() + "/Documents/IOTBluetooth/"
+ let fileManager = FileManager.default
+ if !fileManager.fileExists(atPath: foldPath) {
+ do {
+ try FileManager.default.createDirectory(atPath: foldPath, withIntermediateDirectories: true, attributes: nil)
+ } catch {
+ return ""
+ }
+ }
+
+ let f = DateFormatter()
+ f.dateFormat = "yyyy-MM-dd HHmmss"
+ var time = f.string(from: Date())
+
+ let path = foldPath + "\(time).log"
+ if !FileManager.default.fileExists(atPath: path) {
+ FileManager.default.createFile(atPath: path, contents: nil, attributes: nil)
+ }
+ return path
+ }
+}
+
+public extension Logger {
+ static func log(_ logs: Any..., line: Int = #line, separator: String = " ", terminator: String = "\n") {
+ objc_sync_enter(Logger.shared)
+ let logString = Logger.shared.dateString + " [app-\(String(format: "%03d", line))]: " + logs.map({ "\($0)" }).joined(separator: separator) + terminator
+ if Logger.isDebug {
+ print(Logger.shared.dateString, terminator: " [app-\(String(format: "%03d", line))]: ")
+ print(logs, separator: separator, terminator: terminator)
+ Self.logs.append(logString)
+ }
+// Logger.shared.saveLog(log: logString)
+ objc_sync_exit(Logger.shared)
+
+
+ }
+
+ private func saveLog(log:String){
+ fileHandle?.write(log.data(using: .utf8)!)
+// if #available(iOS 13.0, *) {
+// try? fileHandle.close()
+// } else {
+// // Fallback on earlier versions
+// }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.pbxproj b/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..3c4ed9f
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.pbxproj
@@ -0,0 +1,963 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 180220CE2C2D5D560090D34A /* IBAiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180220CD2C2D5D560090D34A /* IBAiResponse.swift */; };
+ 180B01982BFC831300524729 /* IBHistoryHeartRate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180B01972BFC831300524729 /* IBHistoryHeartRate.swift */; };
+ 180B019E2BFC9CF400524729 /* IBHistoryBaseTraining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180B019D2BFC9CF400524729 /* IBHistoryBaseTraining.swift */; };
+ 180CE2652C0D728F00A7006E /* IBAddressBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180CE2642C0D728F00A7006E /* IBAddressBook.swift */; };
+ 18113B2F2C13195900E04997 /* galaxy.lzma in Resources */ = {isa = PBXBuildFile; fileRef = 18113B2E2C13195900E04997 /* galaxy.lzma */; };
+ 18113B312C1400D700E04997 /* 黿²-åå³.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 18113B302C1400D700E04997 /* 黿²-åå³.mp3 */; };
+ 18113B332C1406CB00E04997 /* 大约å¨å¬å£.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 18113B322C1406CB00E04997 /* 大约å¨å¬å£.mp3 */; };
+ 181441CF2C11571400CCDD46 /* IBMp3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181441CE2C11571400CCDD46 /* IBMp3.swift */; };
+ 181441D12C11575D00CCDD46 /* IBOta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181441D02C11575D00CCDD46 /* IBOta.swift */; };
+ 181441D32C115AC500CCDD46 /* IBFileSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181441D22C115AC500CCDD46 /* IBFileSender.swift */; };
+ 181441D52C116AE500CCDD46 /* IBDial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181441D42C116AE500CCDD46 /* IBDial.swift */; };
+ 18204C002C199B8600F94E69 /* IBFileSender+Mp3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18204BFF2C199B8600F94E69 /* IBFileSender+Mp3.swift */; };
+ 18204C022C199D0E00F94E69 /* IBFileSender+Ota.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18204C012C199D0E00F94E69 /* IBFileSender+Ota.swift */; };
+ 18204C042C199E7800F94E69 /* IBFileSender+Dial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18204C032C199E7800F94E69 /* IBFileSender+Dial.swift */; };
+ 182DDFD42C5CE68900D44D37 /* IBAiValid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182DDFD32C5CE68900D44D37 /* IBAiValid.swift */; };
+ 1839DBD02C14442200E33B6E /* IBFetchFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1839DBCF2C14442200E33B6E /* IBFetchFile.swift */; };
+ 184431322BF3720E00546E4E /* IBOtherTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184431312BF3720E00546E4E /* IBOtherTarget.swift */; };
+ 1849DC8F2BEC8007002C2D2D /* IBVibrateSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1849DC8E2BEC8007002C2D2D /* IBVibrateSound.swift */; };
+ 1849DC912BECD131002C2D2D /* IBCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1849DC902BECD131002C2D2D /* IBCode.swift */; };
+ 184B94CB2BF606CC0042BEB3 /* IBTrainTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184B94CA2BF606CC0042BEB3 /* IBTrainTarget.swift */; };
+ 1859DFA02BFF5A2700112E8C /* IBSendTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1859DF9F2BFF5A2700112E8C /* IBSendTime.swift */; };
+ 185E9C3B2C2564F60078C808 /* 20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg in Resources */ = {isa = PBXBuildFile; fileRef = 185E9C3A2C2564F60078C808 /* 20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg */; };
+ 18884C442C11DBA300E01458 /* IBDeleteFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18884C432C11DBA300E01458 /* IBDeleteFile.swift */; };
+ 18884C462C12A4EA00E01458 /* IBEpo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18884C452C12A4EA00E01458 /* IBEpo.swift */; };
+ 18993AA22C073FED006742D2 /* IBBloodOxygen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18993AA12C073FED006742D2 /* IBBloodOxygen.swift */; };
+ 189C15892C05E0CD00676A80 /* IBTarinState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C15882C05E0CD00676A80 /* IBTarinState.swift */; };
+ 189C158B2C05F27C00676A80 /* IBAbout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C158A2C05F27C00676A80 /* IBAbout.swift */; };
+ 189C158D2C05F58A00676A80 /* IBHistoryBloodOxygen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C158C2C05F58A00676A80 /* IBHistoryBloodOxygen.swift */; };
+ 189C158F2C05F85200676A80 /* IBHistoryStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C158E2C05F85200676A80 /* IBHistoryStep.swift */; };
+ 189C15912C05FBA100676A80 /* IBHistoryBurning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C15902C05FBA100676A80 /* IBHistoryBurning.swift */; };
+ 189C15932C05FFA600676A80 /* IBHistoryStand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C15922C05FFA600676A80 /* IBHistoryStand.swift */; };
+ 189C15952C05FFC700676A80 /* IBHistoryCalorie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C15942C05FFC700676A80 /* IBHistoryCalorie.swift */; };
+ 189C15972C05FFEE00676A80 /* IBHistoryDistance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189C15962C05FFEE00676A80 /* IBHistoryDistance.swift */; };
+ 18C9589B2C082FA7006A5DE8 /* IBWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C9589A2C082FA7006A5DE8 /* IBWeather.swift */; };
+ 18C9589D2C08600C006A5DE8 /* IBHistoryGps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C9589C2C08600C006A5DE8 /* IBHistoryGps.swift */; };
+ 18C9589F2C086A9D006A5DE8 /* IBHistorySleep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C9589E2C086A9D006A5DE8 /* IBHistorySleep.swift */; };
+ 18E7AC922BF44C6800CB9393 /* IBSleep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E7AC912BF44C6800CB9393 /* IBSleep.swift */; };
+ 18E7AC942BF4B31000CB9393 /* IBWater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E7AC932BF4B31000CB9393 /* IBWater.swift */; };
+ 18E7AC962BF5E39900CB9393 /* IBHeartRate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E7AC952BF5E39900CB9393 /* IBHeartRate.swift */; };
+ 18EC6E282BFF4C6D00757CA8 /* IBPhoneInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EC6E272BFF4C6D00757CA8 /* IBPhoneInfo.swift */; };
+ 18EF69512C45130200AA0A36 /* IBAiManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF69502C45130200AA0A36 /* IBAiManager.swift */; };
+ 18EF69532C45136800AA0A36 /* IBPairling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF69522C45136800AA0A36 /* IBPairling.swift */; };
+ 18EF69552C45266D00AA0A36 /* IBReceiveManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF69542C45266D00AA0A36 /* IBReceiveManager.swift */; };
+ 18EF69702C4798BB00AA0A36 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18EF696B2C47766400AA0A36 /* libbz2.tbd */; };
+ 18EF69712C4798C600AA0A36 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18EF69692C47765800AA0A36 /* libiconv.tbd */; };
+ 18EF69722C4798D100AA0A36 /* liblzma.5.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18EF69672C47764400AA0A36 /* liblzma.5.tbd */; };
+ 18EF69732C4798DD00AA0A36 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18EF695F2C476E2000AA0A36 /* libz.tbd */; };
+ 18EF69762C47A0F200AA0A36 /* AIAudio.sbc in Resources */ = {isa = PBXBuildFile; fileRef = 18EF69752C47A0F200AA0A36 /* AIAudio.sbc */; };
+ 18EF69772C47A0F200AA0A36 /* AIAudio.sbc in Resources */ = {isa = PBXBuildFile; fileRef = 18EF69752C47A0F200AA0A36 /* AIAudio.sbc */; };
+ 18EF69792C47A86D00AA0A36 /* IBTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF69782C47A86D00AA0A36 /* IBTools.swift */; };
+ 18EF697A2C47A86D00AA0A36 /* IBTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF69782C47A86D00AA0A36 /* IBTools.swift */; };
+ 18EFF59B2C080F14001B1E0E /* IBUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EFF59A2C080F14001B1E0E /* IBUserInfo.swift */; };
+ 3FC0CFCE2CD8634200F97933 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FC0CFCD2CD8634200F97933 /* Logger.swift */; };
+ 3FFE56DF2CD6157F000C7415 /* IBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F6AC02BA04BE100DA520F /* IBManager.swift */; };
+ 3FFE56E02CD61584000C7415 /* IBBluetoothManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39C52B980C88006A3B18 /* IBBluetoothManager.swift */; };
+ 3FFE56E72CD624D6000C7415 /* HsFileAgentSdk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFE56E62CD624D6000C7415 /* HsFileAgentSdk.swift */; };
+ 8A0A39BF2B980BEF006A3B18 /* IOTBluetooth.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A0A39BE2B980BEF006A3B18 /* IOTBluetooth.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8A0A39C92B980CC2006A3B18 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A0A39C82B980CC2006A3B18 /* CoreBluetooth.framework */; };
+ 8A0A39CB2B980CFC006A3B18 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39CA2B980CFC006A3B18 /* Logger.swift */; };
+ 8A0A39D22B980E4C006A3B18 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39D12B980E4C006A3B18 /* JSON.swift */; };
+ 8A0A39D42B980E93006A3B18 /* Any.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39D32B980E93006A3B18 /* Any.swift */; };
+ 8A0A39D62B980EC9006A3B18 /* IBPeripheralUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39D52B980EC9006A3B18 /* IBPeripheralUUID.swift */; };
+ 8A0A39D82B980F58006A3B18 /* UTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39D72B980F58006A3B18 /* UTF8.swift */; };
+ 8A0A39DA2B981037006A3B18 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0A39D92B981037006A3B18 /* Date.swift */; };
+ 8A1DFF522B9EB4F800B735FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1DFF512B9EB4F800B735FF /* AppDelegate.swift */; };
+ 8A1DFF562B9EB4F800B735FF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1DFF552B9EB4F800B735FF /* ViewController.swift */; };
+ 8A1DFF592B9EB4F900B735FF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A1DFF572B9EB4F900B735FF /* Main.storyboard */; };
+ 8A1DFF5B2B9EB4F900B735FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8A1DFF5A2B9EB4F900B735FF /* Assets.xcassets */; };
+ 8A1DFF5E2B9EB4F900B735FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A1DFF5C2B9EB4F900B735FF /* LaunchScreen.storyboard */; };
+ 8A34B9252BA2A069000F66BD /* IBWatchInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A34B9242BA2A069000F66BD /* IBWatchInfo.swift */; };
+ 8A34B9292BA2AB86000F66BD /* LogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A34B9282BA2AB86000F66BD /* LogViewController.swift */; };
+ 8A34B92B2BA2D446000F66BD /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A34B92A2BA2D446000F66BD /* BaseViewController.swift */; };
+ 8A34B92D2BA2FA02000F66BD /* IBManufacturer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A34B92C2BA2FA02000F66BD /* IBManufacturer.swift */; };
+ 8A5822CF2BA1A67700590AB6 /* IBAbility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5822CE2BA1A67700590AB6 /* IBAbility.swift */; };
+ 8A5822D12BA1C80700590AB6 /* LRC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5822D02BA1C80700590AB6 /* LRC.swift */; };
+ 8A65F95F2BABEC4D005C1EA4 /* IBClock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A65F95E2BABEC4D005C1EA4 /* IBClock.swift */; };
+ 8A65F9612BAC0810005C1EA4 /* IBSimulateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A65F9602BAC0810005C1EA4 /* IBSimulateParser.swift */; };
+ 8A65F9632BAC1018005C1EA4 /* IBTargetStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A65F9622BAC1018005C1EA4 /* IBTargetStep.swift */; };
+ 8A65F9652BAC271B005C1EA4 /* IBScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A65F9642BAC271B005C1EA4 /* IBScreen.swift */; };
+ 8A76B6092BA84C2B0016F2AB /* AbilityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A76B6082BA84C2B0016F2AB /* AbilityViewController.swift */; };
+ 8A76B60B2BA851730016F2AB /* IBParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A76B60A2BA851730016F2AB /* IBParser.swift */; };
+ 8A79D2252BA4278E0096A98C /* IBSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A79D2242BA4278E0096A98C /* IBSender.swift */; };
+ 8A79D2272BA433330096A98C /* IBNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A79D2262BA433330096A98C /* IBNotify.swift */; };
+ 8A79D2292BA43F5A0096A98C /* IBResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A79D2282BA43F5A0096A98C /* IBResponse.swift */; };
+ 8A878FE02BA93CF1008C7F26 /* Datas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A878FDF2BA93CF1008C7F26 /* Datas.swift */; };
+ 8A878FE22BA9444C008C7F26 /* IBParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A878FE12BA9444C008C7F26 /* IBParameter.swift */; };
+ 8A878FE42BA94D09008C7F26 /* IBChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A878FE32BA94D09008C7F26 /* IBChecker.swift */; };
+ 8A87FBB32BAA7F3900096761 /* IBLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A87FBB22BAA7F3900096761 /* IBLanguage.swift */; };
+ 8A87FBB52BAA85FB00096761 /* IBTrainingData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A87FBB42BAA85FB00096761 /* IBTrainingData.swift */; };
+ 8A9F6ABD2BA0463D00DA520F /* CRC16.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F6ABC2BA0463D00DA520F /* CRC16.swift */; };
+ 8A9F6ABE2BA0475100DA520F /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A0A39C82B980CC2006A3B18 /* CoreBluetooth.framework */; };
+ 8A9F6AC22BA05CB500DA520F /* IOTBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A0A39BB2B980BEF006A3B18 /* IOTBluetooth.framework */; };
+ 8A9F6AC32BA05CB500DA520F /* IOTBluetooth.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8A0A39BB2B980BEF006A3B18 /* IOTBluetooth.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 8A9F6AC42BA05CB500DA520F /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8A0A39B22B980BEF006A3B18 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8A0A39BA2B980BEF006A3B18;
+ remoteInfo = IOTBluetooth;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8A9F6AC62BA05CB500DA520F /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 8A9F6AC32BA05CB500DA520F /* IOTBluetooth.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 180220CD2C2D5D560090D34A /* IBAiResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAiResponse.swift; sourceTree = ""; };
+ 180B01972BFC831300524729 /* IBHistoryHeartRate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryHeartRate.swift; sourceTree = ""; };
+ 180B019D2BFC9CF400524729 /* IBHistoryBaseTraining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryBaseTraining.swift; sourceTree = ""; };
+ 180CE2642C0D728F00A7006E /* IBAddressBook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAddressBook.swift; sourceTree = ""; };
+ 18113B2E2C13195900E04997 /* galaxy.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = galaxy.lzma; sourceTree = ""; };
+ 18113B302C1400D700E04997 /* 黿²-åå³.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "黿²-åå³.mp3"; sourceTree = ""; };
+ 18113B322C1406CB00E04997 /* 大约å¨å¬å£.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "大约å¨å¬å£.mp3"; sourceTree = ""; };
+ 181441CE2C11571400CCDD46 /* IBMp3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBMp3.swift; sourceTree = ""; };
+ 181441D02C11575D00CCDD46 /* IBOta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBOta.swift; sourceTree = ""; };
+ 181441D22C115AC500CCDD46 /* IBFileSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBFileSender.swift; sourceTree = ""; };
+ 181441D42C116AE500CCDD46 /* IBDial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBDial.swift; sourceTree = ""; };
+ 18204BFF2C199B8600F94E69 /* IBFileSender+Mp3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IBFileSender+Mp3.swift"; sourceTree = ""; };
+ 18204C012C199D0E00F94E69 /* IBFileSender+Ota.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IBFileSender+Ota.swift"; sourceTree = ""; };
+ 18204C032C199E7800F94E69 /* IBFileSender+Dial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IBFileSender+Dial.swift"; sourceTree = ""; };
+ 182DDFD32C5CE68900D44D37 /* IBAiValid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAiValid.swift; sourceTree = ""; };
+ 1839DBCF2C14442200E33B6E /* IBFetchFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBFetchFile.swift; sourceTree = ""; };
+ 184431312BF3720E00546E4E /* IBOtherTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBOtherTarget.swift; sourceTree = ""; };
+ 1849DC8E2BEC8007002C2D2D /* IBVibrateSound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBVibrateSound.swift; sourceTree = ""; };
+ 1849DC902BECD131002C2D2D /* IBCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBCode.swift; sourceTree = ""; };
+ 184B94CA2BF606CC0042BEB3 /* IBTrainTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBTrainTarget.swift; sourceTree = ""; };
+ 1859DF9F2BFF5A2700112E8C /* IBSendTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBSendTime.swift; sourceTree = ""; };
+ 185E9C3A2C2564F60078C808 /* 20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg */ = {isa = PBXFileReference; lastKnownFileType = file; path = 20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg; sourceTree = ""; };
+ 18884C432C11DBA300E01458 /* IBDeleteFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBDeleteFile.swift; sourceTree = ""; };
+ 18884C452C12A4EA00E01458 /* IBEpo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBEpo.swift; sourceTree = ""; };
+ 18993AA12C073FED006742D2 /* IBBloodOxygen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBBloodOxygen.swift; sourceTree = ""; };
+ 189C15882C05E0CD00676A80 /* IBTarinState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBTarinState.swift; sourceTree = ""; };
+ 189C158A2C05F27C00676A80 /* IBAbout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAbout.swift; sourceTree = ""; };
+ 189C158C2C05F58A00676A80 /* IBHistoryBloodOxygen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryBloodOxygen.swift; sourceTree = ""; };
+ 189C158E2C05F85200676A80 /* IBHistoryStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryStep.swift; sourceTree = ""; };
+ 189C15902C05FBA100676A80 /* IBHistoryBurning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryBurning.swift; sourceTree = ""; };
+ 189C15922C05FFA600676A80 /* IBHistoryStand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryStand.swift; sourceTree = ""; };
+ 189C15942C05FFC700676A80 /* IBHistoryCalorie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryCalorie.swift; sourceTree = ""; };
+ 189C15962C05FFEE00676A80 /* IBHistoryDistance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryDistance.swift; sourceTree = ""; };
+ 18C9589A2C082FA7006A5DE8 /* IBWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBWeather.swift; sourceTree = ""; };
+ 18C9589C2C08600C006A5DE8 /* IBHistoryGps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistoryGps.swift; sourceTree = ""; };
+ 18C9589E2C086A9D006A5DE8 /* IBHistorySleep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHistorySleep.swift; sourceTree = ""; };
+ 18E7AC912BF44C6800CB9393 /* IBSleep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBSleep.swift; sourceTree = ""; };
+ 18E7AC932BF4B31000CB9393 /* IBWater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBWater.swift; sourceTree = ""; };
+ 18E7AC952BF5E39900CB9393 /* IBHeartRate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBHeartRate.swift; sourceTree = ""; };
+ 18EC6E272BFF4C6D00757CA8 /* IBPhoneInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBPhoneInfo.swift; sourceTree = ""; };
+ 18EF69502C45130200AA0A36 /* IBAiManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAiManager.swift; sourceTree = ""; };
+ 18EF69522C45136800AA0A36 /* IBPairling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBPairling.swift; sourceTree = ""; };
+ 18EF69542C45266D00AA0A36 /* IBReceiveManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBReceiveManager.swift; sourceTree = ""; };
+ 18EF695F2C476E2000AA0A36 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+ 18EF69672C47764400AA0A36 /* liblzma.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.5.tbd; path = usr/lib/liblzma.5.tbd; sourceTree = SDKROOT; };
+ 18EF69692C47765800AA0A36 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
+ 18EF696B2C47766400AA0A36 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; };
+ 18EF69752C47A0F200AA0A36 /* AIAudio.sbc */ = {isa = PBXFileReference; lastKnownFileType = file; path = AIAudio.sbc; sourceTree = ""; };
+ 18EF69782C47A86D00AA0A36 /* IBTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBTools.swift; sourceTree = ""; };
+ 18EFF59A2C080F14001B1E0E /* IBUserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBUserInfo.swift; sourceTree = ""; };
+ 3FC0CFCD2CD8634200F97933 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; };
+ 3FFE56E62CD624D6000C7415 /* HsFileAgentSdk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HsFileAgentSdk.swift; sourceTree = ""; };
+ 8A0A39BB2B980BEF006A3B18 /* IOTBluetooth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IOTBluetooth.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8A0A39BE2B980BEF006A3B18 /* IOTBluetooth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOTBluetooth.h; sourceTree = ""; };
+ 8A0A39C52B980C88006A3B18 /* IBBluetoothManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IBBluetoothManager.swift; sourceTree = ""; };
+ 8A0A39C82B980CC2006A3B18 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; };
+ 8A0A39CA2B980CFC006A3B18 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; };
+ 8A0A39D12B980E4C006A3B18 /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = ""; };
+ 8A0A39D32B980E93006A3B18 /* Any.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Any.swift; sourceTree = ""; };
+ 8A0A39D52B980EC9006A3B18 /* IBPeripheralUUID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IBPeripheralUUID.swift; sourceTree = ""; };
+ 8A0A39D72B980F58006A3B18 /* UTF8.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTF8.swift; sourceTree = ""; };
+ 8A0A39D92B981037006A3B18 /* Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; };
+ 8A1DFF4F2B9EB4F800B735FF /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8A1DFF512B9EB4F800B735FF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 8A1DFF552B9EB4F800B735FF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
+ 8A1DFF582B9EB4F900B735FF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 8A1DFF5A2B9EB4F900B735FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 8A1DFF5D2B9EB4F900B735FF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 8A1DFF5F2B9EB4F900B735FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 8A34B9242BA2A069000F66BD /* IBWatchInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBWatchInfo.swift; sourceTree = ""; };
+ 8A34B9282BA2AB86000F66BD /* LogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = ""; };
+ 8A34B92A2BA2D446000F66BD /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; };
+ 8A34B92C2BA2FA02000F66BD /* IBManufacturer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBManufacturer.swift; sourceTree = ""; };
+ 8A5822CE2BA1A67700590AB6 /* IBAbility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAbility.swift; sourceTree = ""; };
+ 8A5822D02BA1C80700590AB6 /* LRC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LRC.swift; sourceTree = ""; };
+ 8A65F95E2BABEC4D005C1EA4 /* IBClock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBClock.swift; sourceTree = ""; };
+ 8A65F9602BAC0810005C1EA4 /* IBSimulateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IBSimulateParser.swift; sourceTree = ""; };
+ 8A65F9622BAC1018005C1EA4 /* IBTargetStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBTargetStep.swift; sourceTree = ""; };
+ 8A65F9642BAC271B005C1EA4 /* IBScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBScreen.swift; sourceTree = ""; };
+ 8A76B6082BA84C2B0016F2AB /* AbilityViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AbilityViewController.swift; sourceTree = ""; };
+ 8A76B60A2BA851730016F2AB /* IBParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBParser.swift; sourceTree = ""; };
+ 8A79D2242BA4278E0096A98C /* IBSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBSender.swift; sourceTree = ""; };
+ 8A79D2262BA433330096A98C /* IBNotify.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IBNotify.swift; sourceTree = ""; };
+ 8A79D2282BA43F5A0096A98C /* IBResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBResponse.swift; sourceTree = ""; };
+ 8A878FDF2BA93CF1008C7F26 /* Datas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Datas.swift; sourceTree = ""; };
+ 8A878FE12BA9444C008C7F26 /* IBParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBParameter.swift; sourceTree = ""; };
+ 8A878FE32BA94D09008C7F26 /* IBChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBChecker.swift; sourceTree = ""; };
+ 8A87FBB22BAA7F3900096761 /* IBLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBLanguage.swift; sourceTree = ""; };
+ 8A87FBB42BAA85FB00096761 /* IBTrainingData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBTrainingData.swift; sourceTree = ""; };
+ 8A9F6ABC2BA0463D00DA520F /* CRC16.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CRC16.swift; sourceTree = ""; };
+ 8A9F6AC02BA04BE100DA520F /* IBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBManager.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8A0A39B82B980BEF006A3B18 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 18EF69732C4798DD00AA0A36 /* libz.tbd in Frameworks */,
+ 18EF69722C4798D100AA0A36 /* liblzma.5.tbd in Frameworks */,
+ 18EF69712C4798C600AA0A36 /* libiconv.tbd in Frameworks */,
+ 18EF69702C4798BB00AA0A36 /* libbz2.tbd in Frameworks */,
+ 8A0A39C92B980CC2006A3B18 /* CoreBluetooth.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8A1DFF4C2B9EB4F800B735FF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8A9F6AC22BA05CB500DA520F /* IOTBluetooth.framework in Frameworks */,
+ 8A9F6ABE2BA0475100DA520F /* CoreBluetooth.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 18113B292C1318FD00E04997 /* Resource */ = {
+ isa = PBXGroup;
+ children = (
+ 18EF69752C47A0F200AA0A36 /* AIAudio.sbc */,
+ 18113B322C1406CB00E04997 /* 大约å¨å¬å£.mp3 */,
+ 18113B302C1400D700E04997 /* 黿²-åå³.mp3 */,
+ 185E9C3A2C2564F60078C808 /* 20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg */,
+ 18113B2E2C13195900E04997 /* galaxy.lzma */,
+ );
+ path = Resource;
+ sourceTree = "";
+ };
+ 3FFE56DE2CD614B8000C7415 /* ble */ = {
+ isa = PBXGroup;
+ children = (
+ 3FC0CFCD2CD8634200F97933 /* Logger.swift */,
+ 8A9F6AC02BA04BE100DA520F /* IBManager.swift */,
+ 8A0A39C52B980C88006A3B18 /* IBBluetoothManager.swift */,
+ );
+ path = ble;
+ sourceTree = "";
+ };
+ 8A0A39B12B980BEF006A3B18 = {
+ isa = PBXGroup;
+ children = (
+ 8A0A39BD2B980BEF006A3B18 /* IOTBluetooth */,
+ 8A1DFF502B9EB4F800B735FF /* Example */,
+ 8A0A39BC2B980BEF006A3B18 /* Products */,
+ 8A0A39C72B980CC2006A3B18 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 8A0A39BC2B980BEF006A3B18 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8A0A39BB2B980BEF006A3B18 /* IOTBluetooth.framework */,
+ 8A1DFF4F2B9EB4F800B735FF /* Example.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 8A0A39BD2B980BEF006A3B18 /* IOTBluetooth */ = {
+ isa = PBXGroup;
+ children = (
+ 8A9F6ABF2BA04B5100DA520F /* Models */,
+ 8A0A39D02B980DFD006A3B18 /* Tools */,
+ 18EF69542C45266D00AA0A36 /* IBReceiveManager.swift */,
+ 8A5822CE2BA1A67700590AB6 /* IBAbility.swift */,
+ 8A878FE32BA94D09008C7F26 /* IBChecker.swift */,
+ 8A76B60A2BA851730016F2AB /* IBParser.swift */,
+ 8A65F9602BAC0810005C1EA4 /* IBSimulateParser.swift */,
+ 8A878FE12BA9444C008C7F26 /* IBParameter.swift */,
+ 8A79D2242BA4278E0096A98C /* IBSender.swift */,
+ 181441D22C115AC500CCDD46 /* IBFileSender.swift */,
+ 18204BFF2C199B8600F94E69 /* IBFileSender+Mp3.swift */,
+ 18204C012C199D0E00F94E69 /* IBFileSender+Ota.swift */,
+ 18204C032C199E7800F94E69 /* IBFileSender+Dial.swift */,
+ 8A79D2262BA433330096A98C /* IBNotify.swift */,
+ 18EF69522C45136800AA0A36 /* IBPairling.swift */,
+ 18EF69502C45130200AA0A36 /* IBAiManager.swift */,
+ 3FFE56E62CD624D6000C7415 /* HsFileAgentSdk.swift */,
+ 8A0A39BE2B980BEF006A3B18 /* IOTBluetooth.h */,
+ );
+ path = IOTBluetooth;
+ sourceTree = "";
+ };
+ 8A0A39C72B980CC2006A3B18 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 18EF696B2C47766400AA0A36 /* libbz2.tbd */,
+ 18EF69692C47765800AA0A36 /* libiconv.tbd */,
+ 18EF69672C47764400AA0A36 /* liblzma.5.tbd */,
+ 18EF695F2C476E2000AA0A36 /* libz.tbd */,
+ 8A0A39C82B980CC2006A3B18 /* CoreBluetooth.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 8A0A39D02B980DFD006A3B18 /* Tools */ = {
+ isa = PBXGroup;
+ children = (
+ 8A0A39D52B980EC9006A3B18 /* IBPeripheralUUID.swift */,
+ 8A0A39D32B980E93006A3B18 /* Any.swift */,
+ 8A0A39D92B981037006A3B18 /* Date.swift */,
+ 8A0A39D12B980E4C006A3B18 /* JSON.swift */,
+ 8A9F6ABC2BA0463D00DA520F /* CRC16.swift */,
+ 8A5822D02BA1C80700590AB6 /* LRC.swift */,
+ 8A0A39D72B980F58006A3B18 /* UTF8.swift */,
+ 8A0A39CA2B980CFC006A3B18 /* Logger.swift */,
+ 18EF69782C47A86D00AA0A36 /* IBTools.swift */,
+ );
+ path = Tools;
+ sourceTree = "";
+ };
+ 8A1DFF502B9EB4F800B735FF /* Example */ = {
+ isa = PBXGroup;
+ children = (
+ 3FFE56DE2CD614B8000C7415 /* ble */,
+ 18113B292C1318FD00E04997 /* Resource */,
+ 8A1DFF512B9EB4F800B735FF /* AppDelegate.swift */,
+ 8A1DFF552B9EB4F800B735FF /* ViewController.swift */,
+ 8A76B6082BA84C2B0016F2AB /* AbilityViewController.swift */,
+ 8A878FDF2BA93CF1008C7F26 /* Datas.swift */,
+ 8A34B92A2BA2D446000F66BD /* BaseViewController.swift */,
+ 8A34B9282BA2AB86000F66BD /* LogViewController.swift */,
+ 8A1DFF572B9EB4F900B735FF /* Main.storyboard */,
+ 8A1DFF5A2B9EB4F900B735FF /* Assets.xcassets */,
+ 8A1DFF5C2B9EB4F900B735FF /* LaunchScreen.storyboard */,
+ 8A1DFF5F2B9EB4F900B735FF /* Info.plist */,
+ );
+ path = Example;
+ sourceTree = "";
+ };
+ 8A9F6ABF2BA04B5100DA520F /* Models */ = {
+ isa = PBXGroup;
+ children = (
+ 8A87FBB22BAA7F3900096761 /* IBLanguage.swift */,
+ 8A79D2282BA43F5A0096A98C /* IBResponse.swift */,
+ 8A65F95E2BABEC4D005C1EA4 /* IBClock.swift */,
+ 18E7AC912BF44C6800CB9393 /* IBSleep.swift */,
+ 8A65F9642BAC271B005C1EA4 /* IBScreen.swift */,
+ 18E7AC932BF4B31000CB9393 /* IBWater.swift */,
+ 8A65F9622BAC1018005C1EA4 /* IBTargetStep.swift */,
+ 8A87FBB42BAA85FB00096761 /* IBTrainingData.swift */,
+ 8A34B92C2BA2FA02000F66BD /* IBManufacturer.swift */,
+ 8A34B9242BA2A069000F66BD /* IBWatchInfo.swift */,
+ 184431312BF3720E00546E4E /* IBOtherTarget.swift */,
+ 184B94CA2BF606CC0042BEB3 /* IBTrainTarget.swift */,
+ 18E7AC952BF5E39900CB9393 /* IBHeartRate.swift */,
+ 18993AA12C073FED006742D2 /* IBBloodOxygen.swift */,
+ 180B01972BFC831300524729 /* IBHistoryHeartRate.swift */,
+ 189C158C2C05F58A00676A80 /* IBHistoryBloodOxygen.swift */,
+ 189C158E2C05F85200676A80 /* IBHistoryStep.swift */,
+ 189C15902C05FBA100676A80 /* IBHistoryBurning.swift */,
+ 189C15922C05FFA600676A80 /* IBHistoryStand.swift */,
+ 189C15942C05FFC700676A80 /* IBHistoryCalorie.swift */,
+ 189C15962C05FFEE00676A80 /* IBHistoryDistance.swift */,
+ 180B019D2BFC9CF400524729 /* IBHistoryBaseTraining.swift */,
+ 18C9589E2C086A9D006A5DE8 /* IBHistorySleep.swift */,
+ 18C9589C2C08600C006A5DE8 /* IBHistoryGps.swift */,
+ 1849DC8E2BEC8007002C2D2D /* IBVibrateSound.swift */,
+ 18EC6E272BFF4C6D00757CA8 /* IBPhoneInfo.swift */,
+ 1859DF9F2BFF5A2700112E8C /* IBSendTime.swift */,
+ 189C15882C05E0CD00676A80 /* IBTarinState.swift */,
+ 189C158A2C05F27C00676A80 /* IBAbout.swift */,
+ 18EFF59A2C080F14001B1E0E /* IBUserInfo.swift */,
+ 18C9589A2C082FA7006A5DE8 /* IBWeather.swift */,
+ 180CE2642C0D728F00A7006E /* IBAddressBook.swift */,
+ 181441CE2C11571400CCDD46 /* IBMp3.swift */,
+ 181441D02C11575D00CCDD46 /* IBOta.swift */,
+ 181441D42C116AE500CCDD46 /* IBDial.swift */,
+ 18884C452C12A4EA00E01458 /* IBEpo.swift */,
+ 18884C432C11DBA300E01458 /* IBDeleteFile.swift */,
+ 1839DBCF2C14442200E33B6E /* IBFetchFile.swift */,
+ 180220CD2C2D5D560090D34A /* IBAiResponse.swift */,
+ 1849DC902BECD131002C2D2D /* IBCode.swift */,
+ 182DDFD32C5CE68900D44D37 /* IBAiValid.swift */,
+ );
+ path = Models;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 8A0A39B62B980BEF006A3B18 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8A0A39BF2B980BEF006A3B18 /* IOTBluetooth.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 8A0A39BA2B980BEF006A3B18 /* IOTBluetooth */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 8A0A39C22B980BEF006A3B18 /* Build configuration list for PBXNativeTarget "IOTBluetooth" */;
+ buildPhases = (
+ 8A0A39B62B980BEF006A3B18 /* Headers */,
+ 8A0A39B72B980BEF006A3B18 /* Sources */,
+ 8A0A39B82B980BEF006A3B18 /* Frameworks */,
+ 8A0A39B92B980BEF006A3B18 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = IOTBluetooth;
+ productName = IOTBluetooth;
+ productReference = 8A0A39BB2B980BEF006A3B18 /* IOTBluetooth.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 8A1DFF4E2B9EB4F800B735FF /* Example */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 8A1DFF622B9EB4F900B735FF /* Build configuration list for PBXNativeTarget "Example" */;
+ buildPhases = (
+ 8A1DFF4B2B9EB4F800B735FF /* Sources */,
+ 8A1DFF4C2B9EB4F800B735FF /* Frameworks */,
+ 8A1DFF4D2B9EB4F800B735FF /* Resources */,
+ 8A9F6AC62BA05CB500DA520F /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 8A9F6AC52BA05CB500DA520F /* PBXTargetDependency */,
+ );
+ name = Example;
+ productName = Example;
+ productReference = 8A1DFF4F2B9EB4F800B735FF /* Example.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 8A0A39B22B980BEF006A3B18 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1500;
+ LastUpgradeCheck = 1500;
+ TargetAttributes = {
+ 8A0A39BA2B980BEF006A3B18 = {
+ CreatedOnToolsVersion = 15.0;
+ LastSwiftMigration = 1500;
+ };
+ 8A1DFF4E2B9EB4F800B735FF = {
+ CreatedOnToolsVersion = 15.0;
+ };
+ };
+ };
+ buildConfigurationList = 8A0A39B52B980BEF006A3B18 /* Build configuration list for PBXProject "IOTBluetooth" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 8A0A39B12B980BEF006A3B18;
+ productRefGroup = 8A0A39BC2B980BEF006A3B18 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8A0A39BA2B980BEF006A3B18 /* IOTBluetooth */,
+ 8A1DFF4E2B9EB4F800B735FF /* Example */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8A0A39B92B980BEF006A3B18 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 18EF69762C47A0F200AA0A36 /* AIAudio.sbc in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8A1DFF4D2B9EB4F800B735FF /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8A1DFF5E2B9EB4F900B735FF /* LaunchScreen.storyboard in Resources */,
+ 18113B312C1400D700E04997 /* 黿²-åå³.mp3 in Resources */,
+ 8A1DFF5B2B9EB4F900B735FF /* Assets.xcassets in Resources */,
+ 8A1DFF592B9EB4F900B735FF /* Main.storyboard in Resources */,
+ 185E9C3B2C2564F60078C808 /* 20240604_155433_2_V1.0.1_20240618_113111_ota.fwpkg in Resources */,
+ 18EF69772C47A0F200AA0A36 /* AIAudio.sbc in Resources */,
+ 18113B332C1406CB00E04997 /* 大约å¨å¬å£.mp3 in Resources */,
+ 18113B2F2C13195900E04997 /* galaxy.lzma in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8A0A39B72B980BEF006A3B18 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8A79D2292BA43F5A0096A98C /* IBResponse.swift in Sources */,
+ 189C15932C05FFA600676A80 /* IBHistoryStand.swift in Sources */,
+ 1849DC8F2BEC8007002C2D2D /* IBVibrateSound.swift in Sources */,
+ 18204C042C199E7800F94E69 /* IBFileSender+Dial.swift in Sources */,
+ 180CE2652C0D728F00A7006E /* IBAddressBook.swift in Sources */,
+ 8A878FE22BA9444C008C7F26 /* IBParameter.swift in Sources */,
+ 184431322BF3720E00546E4E /* IBOtherTarget.swift in Sources */,
+ 189C158B2C05F27C00676A80 /* IBAbout.swift in Sources */,
+ 189C15972C05FFEE00676A80 /* IBHistoryDistance.swift in Sources */,
+ 180B019E2BFC9CF400524729 /* IBHistoryBaseTraining.swift in Sources */,
+ 8A87FBB52BAA85FB00096761 /* IBTrainingData.swift in Sources */,
+ 8A878FE42BA94D09008C7F26 /* IBChecker.swift in Sources */,
+ 18884C442C11DBA300E01458 /* IBDeleteFile.swift in Sources */,
+ 189C15892C05E0CD00676A80 /* IBTarinState.swift in Sources */,
+ 182DDFD42C5CE68900D44D37 /* IBAiValid.swift in Sources */,
+ 8A76B60B2BA851730016F2AB /* IBParser.swift in Sources */,
+ 18EFF59B2C080F14001B1E0E /* IBUserInfo.swift in Sources */,
+ 8A65F9612BAC0810005C1EA4 /* IBSimulateParser.swift in Sources */,
+ 180B01982BFC831300524729 /* IBHistoryHeartRate.swift in Sources */,
+ 189C158D2C05F58A00676A80 /* IBHistoryBloodOxygen.swift in Sources */,
+ 8A79D2272BA433330096A98C /* IBNotify.swift in Sources */,
+ 8A0A39D42B980E93006A3B18 /* Any.swift in Sources */,
+ 1849DC912BECD131002C2D2D /* IBCode.swift in Sources */,
+ 1839DBD02C14442200E33B6E /* IBFetchFile.swift in Sources */,
+ 8A34B92D2BA2FA02000F66BD /* IBManufacturer.swift in Sources */,
+ 184B94CB2BF606CC0042BEB3 /* IBTrainTarget.swift in Sources */,
+ 18204C002C199B8600F94E69 /* IBFileSender+Mp3.swift in Sources */,
+ 8A79D2252BA4278E0096A98C /* IBSender.swift in Sources */,
+ 18C9589D2C08600C006A5DE8 /* IBHistoryGps.swift in Sources */,
+ 189C15912C05FBA100676A80 /* IBHistoryBurning.swift in Sources */,
+ 8A9F6ABD2BA0463D00DA520F /* CRC16.swift in Sources */,
+ 18E7AC942BF4B31000CB9393 /* IBWater.swift in Sources */,
+ 8A5822CF2BA1A67700590AB6 /* IBAbility.swift in Sources */,
+ 8A65F9652BAC271B005C1EA4 /* IBScreen.swift in Sources */,
+ 1859DFA02BFF5A2700112E8C /* IBSendTime.swift in Sources */,
+ 18EF69792C47A86D00AA0A36 /* IBTools.swift in Sources */,
+ 8A65F9632BAC1018005C1EA4 /* IBTargetStep.swift in Sources */,
+ 181441D32C115AC500CCDD46 /* IBFileSender.swift in Sources */,
+ 8A34B9252BA2A069000F66BD /* IBWatchInfo.swift in Sources */,
+ 8A0A39D62B980EC9006A3B18 /* IBPeripheralUUID.swift in Sources */,
+ 189C15952C05FFC700676A80 /* IBHistoryCalorie.swift in Sources */,
+ 181441CF2C11571400CCDD46 /* IBMp3.swift in Sources */,
+ 18EF69512C45130200AA0A36 /* IBAiManager.swift in Sources */,
+ 18204C022C199D0E00F94E69 /* IBFileSender+Ota.swift in Sources */,
+ 18EC6E282BFF4C6D00757CA8 /* IBPhoneInfo.swift in Sources */,
+ 18EF69532C45136800AA0A36 /* IBPairling.swift in Sources */,
+ 18C9589F2C086A9D006A5DE8 /* IBHistorySleep.swift in Sources */,
+ 180220CE2C2D5D560090D34A /* IBAiResponse.swift in Sources */,
+ 8A65F95F2BABEC4D005C1EA4 /* IBClock.swift in Sources */,
+ 8A0A39D22B980E4C006A3B18 /* JSON.swift in Sources */,
+ 18E7AC962BF5E39900CB9393 /* IBHeartRate.swift in Sources */,
+ 18C9589B2C082FA7006A5DE8 /* IBWeather.swift in Sources */,
+ 8A0A39D82B980F58006A3B18 /* UTF8.swift in Sources */,
+ 181441D12C11575D00CCDD46 /* IBOta.swift in Sources */,
+ 18E7AC922BF44C6800CB9393 /* IBSleep.swift in Sources */,
+ 8A0A39CB2B980CFC006A3B18 /* Logger.swift in Sources */,
+ 189C158F2C05F85200676A80 /* IBHistoryStep.swift in Sources */,
+ 8A5822D12BA1C80700590AB6 /* LRC.swift in Sources */,
+ 8A0A39DA2B981037006A3B18 /* Date.swift in Sources */,
+ 181441D52C116AE500CCDD46 /* IBDial.swift in Sources */,
+ 18993AA22C073FED006742D2 /* IBBloodOxygen.swift in Sources */,
+ 3FFE56E72CD624D6000C7415 /* HsFileAgentSdk.swift in Sources */,
+ 18884C462C12A4EA00E01458 /* IBEpo.swift in Sources */,
+ 8A87FBB32BAA7F3900096761 /* IBLanguage.swift in Sources */,
+ 18EF69552C45266D00AA0A36 /* IBReceiveManager.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8A1DFF4B2B9EB4F800B735FF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8A878FE02BA93CF1008C7F26 /* Datas.swift in Sources */,
+ 8A34B92B2BA2D446000F66BD /* BaseViewController.swift in Sources */,
+ 8A76B6092BA84C2B0016F2AB /* AbilityViewController.swift in Sources */,
+ 3FFE56DF2CD6157F000C7415 /* IBManager.swift in Sources */,
+ 18EF697A2C47A86D00AA0A36 /* IBTools.swift in Sources */,
+ 3FC0CFCE2CD8634200F97933 /* Logger.swift in Sources */,
+ 3FFE56E02CD61584000C7415 /* IBBluetoothManager.swift in Sources */,
+ 8A1DFF562B9EB4F800B735FF /* ViewController.swift in Sources */,
+ 8A1DFF522B9EB4F800B735FF /* AppDelegate.swift in Sources */,
+ 8A34B9292BA2AB86000F66BD /* LogViewController.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 8A9F6AC52BA05CB500DA520F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8A0A39BA2B980BEF006A3B18 /* IOTBluetooth */;
+ targetProxy = 8A9F6AC42BA05CB500DA520F /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 8A1DFF572B9EB4F900B735FF /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8A1DFF582B9EB4F900B735FF /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 8A1DFF5C2B9EB4F900B735FF /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8A1DFF5D2B9EB4F900B735FF /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 8A0A39C02B980BEF006A3B18 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.5;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ 8A0A39C12B980BEF006A3B18 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.5;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ VALIDATE_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ 8A0A39C32B980BEF006A3B18 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = M33X987285;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Example",
+ "$(PROJECT_DIR)/IOTBluetooth",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MACH_O_TYPE = staticlib;
+ MARKETING_VERSION = 1.0;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = com.thisiot.IOTBluetooth;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 4.2;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Debug;
+ };
+ 8A0A39C42B980BEF006A3B18 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = M33X987285;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Example",
+ "$(PROJECT_DIR)/IOTBluetooth",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MACH_O_TYPE = staticlib;
+ MARKETING_VERSION = 1.0;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = com.thisiot.IOTBluetooth;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 4.2;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Release;
+ };
+ 8A1DFF602B9EB4F900B735FF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1001;
+ DEVELOPMENT_TEAM = M33X987285;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Example",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Example/Info.plist;
+ INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "æ«ææºè½æè¡¨éè¦å¼å¯èçåè½";
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
+ INFOPLIST_KEY_UIMainStoryboardFile = Main;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UIUserInterfaceStyle = Light;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.5;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.thisiot.IOTBluetooth.Example;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Debug;
+ };
+ 8A1DFF612B9EB4F900B735FF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1001;
+ DEVELOPMENT_TEAM = M33X987285;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Example",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Example/Info.plist;
+ INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "æ«ææºè½æè¡¨éè¦å¼å¯èçåè½";
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
+ INFOPLIST_KEY_UIMainStoryboardFile = Main;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UIUserInterfaceStyle = Light;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.5;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.thisiot.IOTBluetooth.Example;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 8A0A39B52B980BEF006A3B18 /* Build configuration list for PBXProject "IOTBluetooth" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8A0A39C02B980BEF006A3B18 /* Debug */,
+ 8A0A39C12B980BEF006A3B18 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 8A0A39C22B980BEF006A3B18 /* Build configuration list for PBXNativeTarget "IOTBluetooth" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8A0A39C32B980BEF006A3B18 /* Debug */,
+ 8A0A39C42B980BEF006A3B18 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 8A1DFF622B9EB4F900B735FF /* Build configuration list for PBXNativeTarget "Example" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8A1DFF602B9EB4F900B735FF /* Debug */,
+ 8A1DFF612B9EB4F900B735FF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 8A0A39B22B980BEF006A3B18 /* Project object */;
+}
diff --git a/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/code/IOTBluetooth/IOTBluetooth.xcodeproj/xcshareddata/xcschemes/Example.xcscheme
new file mode 100644
index 0000000..d80a4c0
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth.xcodeproj/xcshareddata/xcschemes/Example.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth.xcodeproj/xcshareddata/xcschemes/IOTBluetooth.xcscheme b/code/IOTBluetooth/IOTBluetooth.xcodeproj/xcshareddata/xcschemes/IOTBluetooth.xcscheme
new file mode 100644
index 0000000..7630f1c
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth.xcodeproj/xcshareddata/xcschemes/IOTBluetooth.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth/HsFileAgentSdk.swift b/code/IOTBluetooth/IOTBluetooth/HsFileAgentSdk.swift
new file mode 100644
index 0000000..66571e6
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/HsFileAgentSdk.swift
@@ -0,0 +1,60 @@
+//
+// HsFileAgentSdk.swift
+// IOTBluetooth
+//
+// Created by liangshi on 2024/11/2.
+//
+
+import Foundation
+
+public typealias IBCompleteHandler = (_ data: Data) -> Void
+/// SDK 管çç±»
+public class HsFileAgentSdk:NSObject {
+ public static let shared = HsFileAgentSdk()
+
+ private override init() {
+ super.init()
+ }
+
+ /// åå§åå·¥ä½
+ public func initSDK() {
+ Logger.log("MixSdk version:",HsFileAgentSdk.version)
+ }
+
+ /// æ¥å¿
+ public var logs: [String] { Logger.logs }
+
+ /// SDK çæ¬å·
+ public let version: String = "1.0.0"
+ /// é»è®¤ä¸º false
+ public var isDebug: Bool = false {
+ didSet { Logger.isDebug = isDebug }
+ }
+
+ /// æ¸
空æ¥å¿
+ public func cleanLog(){
+ Logger.logs.removeAll()
+ }
+
+ /// æä»¶åéå®ä¾
+ internal weak var fileSender: IBFileSender?
+
+
+
+ public func sendToSdk(_ data:Data){
+
+ }
+
+ public func sdkSendToDevice(_ callback:IBCompleteHandler){
+
+ }
+
+ func sendToDevice(fileData:Data){
+
+ }
+}
+
+
+extension HsFileAgentSdk {
+
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBAbility.swift b/code/IOTBluetooth/IOTBluetooth/IBAbility.swift
new file mode 100644
index 0000000..977cd54
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBAbility.swift
@@ -0,0 +1,558 @@
+//
+// IBAbility.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/13.
+//
+
+import Foundation
+/// 主æå¡
+let serviceUUID: String = "18E0"
+/// Notify ç¹å¾å¼
+let notifyCharacteristicUUID: String = "2AE5"
+/// Read ç¹å¾å¼
+let readCharacteristicUUID: String = "2AE2"
+/// Write ç¹å¾å¼(åéåèæ°â¤20)
+let writeCharacteristicUUID: String = "2AE3"
+/// Write ç¹å¾å¼(åéåèæ°>20)
+let writeBigCharacteristicUUID: String = "2AE4"
+/// Write ç¹å¾å¼(åéæä»¶OATãMP3ç)
+let writeFileCharacteristicUUID: String = "2AE0"
+
+/// åè½æä»¤
+public class IBAbility {
+
+ /// å½ä»¤ç±»å
+ let type: `Type`
+
+ /// å½ä»¤ID
+ private(set) var id: UInt8 = 0x0
+
+ /// æå®è§£æå¨
+ private(set) var parser: IBParser.Type!
+
+ /// æå®åæ°æ£æ¥å¨
+ private(set) var checker: IBChecker.Type!
+
+ /// æå¡ç¹å¾
+ private(set) var characteristicUUID: String!
+
+ private init(_ type: `Type`) {
+ self.type = type
+ }
+
+ private func id(_ value: UInt8) -> Self {
+ self.id = value
+ return self
+ }
+
+ private func parser(_ parser: T.Type) -> Self {
+ self.parser = parser
+ return self
+ }
+
+ private func checker(_ checker: T.Type) -> Self {
+ self.checker = checker
+ return self
+ }
+
+ private func characteristic(_ uuid: String) -> Self {
+ characteristicUUID = uuid
+ return self
+ }
+
+
+
+ /// çæä»¤(ä¸å¤§äº20个åè)
+ private static var short: IBAbility { IBAbility(.short).characteristic(writeCharacteristicUUID) }
+ /// é¿æä»¤(大äº20个åè)
+ private static var long: IBAbility { IBAbility(.long).characteristic(writeBigCharacteristicUUID) }
+ /// æä»¶æä»¤(ç¨æ¥åéæä»¶ï¼æ¯å¦è¡¨çãMP3ãOTAç)
+ private static var file: IBAbility { IBAbility(.file).characteristic(writeFileCharacteristicUUID) }
+ /// aiæä»¤
+ private static var ai: IBAbility { IBAbility(.ai).characteristic(writeCharacteristicUUID) }
+
+ public var description: String { "[\(String(format: "0x%02X, 0x%02X", type.rawValue, id))] \(characteristicUUID!)" }
+
+ func isAnswer(_ data: Data) -> Bool {
+ let answerTypeValue = data.bytes[1]
+ let answerID = data.bytes[2]
+
+ return type.rawValue == answerTypeValue && id == answerID
+ }
+}
+
+extension IBAbility {
+ enum `Type`: UInt8 {
+ /// çæä»¤
+ case short = 0x01
+ /// é¿æä»¤
+ case long = 0x02
+ /// æä»¶æä»¤(ç¨æ¥åéæä»¶ï¼æ¯å¦è¡¨çãMP3ãOTAç)
+ case file = 0x04
+ /// Aiæä»¤
+ case ai = 0x05
+ }
+}
+
+public extension IBAbility {
+ /// è®¾ç½®ç¨æ·æ°æ®
+ /// - Parameter userInfo: see ``IBUserInfo``
+ static let userData = IBAbility.long.id(0x01).parser(IBGeneralParser.self).checker(IBUserInfoChecker.self)
+
+ /// å
³äºæ¬æºå
容
+ static let about = IBAbility.short.id(0x03).parser(IBAblutParser.self)
+
+ /// åæ¢è¡¨ç
+ /// - Parameter dialID:Int
+ /// 表çid
+ static let switchDial = IBAbility.short.id(0x02).parser(IBSwitchDialParser.self).checker(IBSwitchDialChecker.self)
+
+ /// APPå é¤è¡¨ç
+ /// - Parameter dialID:Int \
+ /// 表çid
+ static let deleteDial = IBAbility.short.id(0x1B).parser(IBGeneralParser.self).checker(IBSwitchDialChecker.self)
+
+ /// åéå½å表çID
+ /// - Returns: Int \
+ /// Read from ``IBResponse``.value
+ static let fetchCurrentDialId = IBAbility.short.id(0x17).parser(IBFetchCurrentDialParser.self)
+
+ /// æ¥æ¾æè¡¨
+ /// - Parameter action:
+ /// 0 = åæ¢æ¥æ¾ \
+ /// 1 = å¼å§æ¥æ¾
+ static let find = IBAbility.short.id(0x04).parser(IBFindParser.self).checker(IBActionChecker.self)
+
+ /// å¼å
³æ¶æ¯æ¨éåè½
+ /// - Parameter action:
+ /// 0 = å
³éæ¶æ¯ \
+ /// 1 = å¼å¯æ¶æ¯
+ static let message = IBAbility.short.id(0x08).parser(IBGeneralParser.self).checker(IBActionChecker.self)
+
+ /// å¼å¯æå®è¿å¨æ¨¡å¼
+ /// - Parameter motionMode: see ``IBMotionMode``
+ static let startSport = IBAbility.short.id(0x0A).parser(IBGeneralParser.self).checker(IBSportChecker.self)
+
+ /// ç»æè¿å¨æ¨¡å¼
+ static let endSport = IBAbility.short.id(0x0B).parser(IBGeneralParser.self)
+
+ /// 设置è®ç»å®æ¶æ°æ®éæ ·é¢ç
+ /// - Parameter value:Int \
+ /// åä½(ç§)
+ static let trainFrequency = IBAbility.short.id(0x14).parser(IBGeneralParser.self).checker(IBTrainFrequencyChecker.self)
+
+ /// è®ç»æå/æ¢å¤
+ /// - Parameter action:
+ /// 0 = æå \
+ /// 1 = æ¢å¤
+ static let trainPauseResume = IBAbility.short.id(0x16).parser(IBGeneralParser.self).checker(IBActionChecker.self)
+
+ /// 设置è¯è¨
+ /// - Parameter language: see ``IBLanguage``
+ static let language = IBAbility.short.id(0x0E).parser(IBGeneralParser.self).checker(IBLanguageChecker.self)
+
+ /// è·åæè¡¨å¿çåæ°
+ /// - Returns: ``IBHeartRate`` \
+ /// Read from ``IBResponse``.value
+ static let fetchHeartRate = IBAbility.short.id(0x1E).parser(IBHeartRateParser.self)
+
+ /// 设置æè¡¨å¿çåæ°
+ /// - Parameter heartRate: see ``IBHeartRate``
+ static let heartRate = IBAbility.long.id(0x12).parser(IBGeneralParser.self).checker(IBHeartRateChecker.self)
+
+ /// è·åæè¡¨è¡æ°§è®¾ç½®
+ /// - Returns: ``IBBloodOxygen`` \
+ /// Read from ``IBResponse``.value
+ static let fetchBloodOxygen = IBAbility.short.id(0x1F).parser(IBBloodOxygenParser.self)
+
+ /// 设置æè¡¨è¡æ°§åæ°
+ /// - Parameter heartRate: see ``IBBloodOxygen``
+ static let bloodOxygen = IBAbility.long.id(0x13).parser(IBGeneralParser.self).checker(IBBloodOxygenChecker.self)
+
+ /// è·å宿¶å¿ç(宿¶è®ç»æ°æ®)
+ /// - Returns: ``IBTrainingData`` \
+ /// Read from ``IBResponse``.value
+ static let currentHeartRate = IBAbility.short.id(0x0F).parser(IBTrainingDataParser.self)
+
+ /// å¼å¯/å
³éèªå¨è·å宿¶å¿ç
+ /// - Parameter action:
+ /// 0 = å
³éèªå¨è·å \
+ /// 1 = å¼å¯èªå¨è·å
+ static let heartRateMode = IBAbility.short.id(0x11).parser(IBGeneralParser.self).checker(IBActionChecker.self)
+
+ /// 设置æé«å¿çæé
+ /// - Parameter value:Int \
+ /// æé«ä¸è¶
è¿ 250 ,妿éè¦å
³éæé«å¿çæéï¼å¯å°æé«å¿ç设为255ï¼å¼å¯å设æ£å¸¸å¼å³å¯ã
+ static let heartRateHigh = IBAbility.short.id(0x15).parser(IBGeneralParser.self).checker(IBHeartRateHighChecker.self)
+
+ /// æå¿æç
§
+ /// - Parameter action:
+ /// 0 = éåºæå¿æç
§ \
+ /// 1 = è¿å
¥æå¿æç
§
+ static let takePhoto = IBAbility.short.id(0x23).parser(IBTakePhotoParser.self).checker(IBActionChecker.self)
+
+ /// æ¢å¤åºå设置
+ static let restore = IBAbility.short.id(0x25).parser(IBGeneralParser.self)
+
+ /// éæ°å¯å¨
+ static let reboot = IBAbility.short.id(0x26).parser(IBGeneralParser.self)
+
+ /// å
³æº
+ static let shutdown = IBAbility.short.id(0x27).parser(IBGeneralParser.self)
+
+ /// è·åæè¡¨çµæ± çµéã
+ /// - Returns: Int \
+ /// Read from ``IBResponse``.value
+ static let fetchBattery = IBAbility.short.id(0x07).parser(IBBatteryParser.self)
+
+ /// è·åé¹é设置
+ /// - Returns: ``[IBClockInfo]`` \
+ /// Read from ``IBResponse``.items
+ static let fetchClock = IBAbility.short.id(0x01).parser(IBClockParser.self)
+
+ /// åéé¹é设置
+ /// - Parameter clock: see ``IBClock``
+ static let setClock = IBAbility.long.id(0x21).parser(IBSetClockParser.self).checker(IBClockChecker.self)
+
+ /// è·åææè¡¨çUID
+ /// - Returns: [Int] \
+ /// Read from ``IBResponse``.items
+ static let fetchDials = IBAbility.short.id(0x2A).parser(IBFetchDialsParser.self)
+
+ /// APPè·åæè¡¨çIDæ¯å¦å·²å®è£
+ /// - Parameter value:Int
+ /// - Returns: Int
+ /// 1 = å·²å®è£
\
+ /// 2 = æªå®è£
\
+ /// Read from ``IBResponse``.value
+ static let checkDialID = IBAbility.long.id(0x0D).parser(IBCheckDialParser.self).checker(IBSwitchDialChecker.self)
+
+ /// è·åå½åæè¡¨ç¶æ
+ /// - Returns: Int \
+ /// 0 = 空é²ç¶æ \
+ /// 9 = USBåå¨ç¶æ \
+ /// 10 = è¿å¨è®ç»ç¶æ \
+ /// Read from ``IBResponse``.value
+ static let fetchWatchState = IBAbility.short.id(0x19).parser(IBWatchStateParser.self)
+
+ /// è·åæè¡¨å±å¹åæ°
+ /// - Returns: ``IBScreen`` \
+ /// Read from ``IBResponse``.value
+ static let fetchScreen = IBAbility.short.id(0x1A).parser(IBScreenParser.self)
+
+ /// 设置æè¡¨å±å¹åæ°
+ /// - Parameter screen: see ``screen``
+ static let screen = IBAbility.long.id(0x0E).parser(IBGeneralParser.self).checker(IBScreenChecker.self)
+
+ /// è·åæè¡¨å£°é³ä¸éå¨åæ°
+ /// - Returns: ``IBVibrateSound`` \
+ /// Read from ``IBResponse``.value
+ static let fetchVibrateSound = IBAbility.short.id(0x1C).parser(IBVibrateSoundParser.self)
+
+ /// APP设置æè¡¨å£°é³ä¸éå¨åæ°
+ /// - Parameter vibrateSound: see ``IBVibrateSound``
+ static let vibrateSound = IBAbility.long.id(0x0F).parser(IBGeneralParser.self).checker(IBVibrateSoundChecker.self)
+
+
+ /// 设置æè¡¨è¾
å©åè½åæ°
+ /// - Parameter auxiliary: see ``IBAuxiliary``
+ static let auxiliary = IBAbility.long.id(0x11).parser(IBGeneralParser.self).checker(IBAuxiliaryChecker.self)
+
+ /// è·åæè¡¨è¾
å©åè½åæ°
+ /// - Returns: IBAuxiliary \
+ /// Read from ``IBResponse``.value
+ static let fetchAuxiliary = IBAbility.short.id(0x1D).parser(IBFetchAuxiliaryParser.self)
+
+ /// è·åç®æ æ¥æ°è®¾ç½®
+ /// - Returns: ``IBTargetStep`` \
+ /// Read from ``IBResponse``.value
+ static let fetchTargetStep = IBAbility.short.id(0x2B).parser(IBTargetStepParser.self)
+
+ /// è®¾ç½®ç®æ æ¥æ°
+ /// - Parameter targetStep: see ``IBTrainTarget``
+ static let targetStep = IBAbility.long.id(0x16).parser(IBGeneralParser.self).checker(IBTargetStepChecker.self)
+
+ /// è·åå
¶ä»ç®æ 设置
+ /// - Returns: ``IBOtherTarget`` \
+ /// Read from ``IBResponse``.value
+ static let fetchOtherTarget = IBAbility.short.id(0x2C).parser(IBOtherTargetParser.self)
+
+ /// 设置å
¶ä»ç®æ
+ /// - Parameter targetOther: see ``IBOtherTarget``
+ static let targetOther = IBAbility.long.id(0x17).parser(IBGeneralParser.self).checker(IBOtherTargetChecker.self)
+
+ /// è·åç¡ç 设置
+ /// - Returns: ``IBSleep`` \
+ /// Read from ``IBResponse``.value
+ static let fetchSleep = IBAbility.short.id(0x29).parser(IBSleepParser.self)
+
+ /// 设置ç¡ç
+ /// - Parameter sleep: see ``IBSleep``
+ static let sleep = IBAbility.long.id(0x15).parser(IBGeneralParser.self).checker(IBSleepChecker.self)
+
+ /// è·åæè¡¨åæ°´æé设置
+ /// - Returns: Int \
+ /// 0 = å
³é \
+ /// 1 = å¼å¯ \
+ /// Read from ``IBResponse``.value
+ static let fetchWater = IBAbility.short.id(0x24).parser(IBWaterParser.self)
+
+ /// è®¾ç½®åæ°´æé
+ /// - Parameter water: see ``IBWater``
+ static let water = IBAbility.long.id(0x14).parser(IBGeneralParser.self).checker(IBWaterChecker.self)
+
+ /// 设置æè¡¨å
¬è±å¶åä½
+ /// - Parameter value:Int \
+ /// 0 = å
¬å¶ \
+ /// 1 = è±å¶
+ static let unit = IBAbility.short.id(0x28).parser(IBGeneralParser.self).checker(IBValueChecker.self)
+
+ /// 设置è®ç»ç®æ
+ /// - Parameter trainTargetï¼see ``IBTrainTarget``
+ static let trainTarget = IBAbility.long.id(0x04).parser(IBGeneralParser.self).checker(IBTrainTargetChecker.self)
+
+ // MARK: - åæ¥æ°æ®
+ /// 忥å¿çå岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryHeartRateItem] \
+ /// Read from ``IBResponse``.items
+ static let syncHeartRate = IBAbility.short.id(0x40).parser(IBSyncHeartRateParser.self).checker(IBValueChecker.self)
+
+ /// åæ¥è¡æ°§å岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryBloodOxygenItem] \
+ /// Read from ``IBResponse``.items
+ static let syncBloodOxygen = IBAbility.short.id(0x41).parser(IBSyncBloodOxygenParser.self).checker(IBValueChecker.self)
+
+ /// 忥å岿¥æ°
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryStepItem] \
+ /// Read from ``IBResponse``.items
+ static let syncStepCount = IBAbility.short.id(0x43).parser(IBSyncStepParser.self).checker(IBValueChecker.self)
+
+ /// 忥çèæ¶é´å岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryBurningItem] \
+ /// Read from ``IBResponse``.items
+ static let syncFatBurning = IBAbility.short.id(0x44).parser(IBSyncBurningParser.self).checker(IBValueChecker.self)
+
+ /// 忥ç«ç«å岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryStandItem] \
+ /// Read from ``IBResponse``.items
+ static let syncStand = IBAbility.short.id(0x45).parser(IBSyncStandParser.self).checker(IBValueChecker.self)
+
+ /// 忥å¡è·¯éå岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryCalorieItem] \
+ /// Read from ``IBResponse``.items
+ static let syncCalorie = IBAbility.short.id(0x46).parser(IBSyncCalorieParser.self).checker(IBValueChecker.self)
+
+ /// 忥è·ç¦»å岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryDistanceItem] \
+ /// Read from ``IBResponse``.items
+ static let syncDistance = IBAbility.short.id(0x47).parser(IBSyncDistanceParser.self).checker(IBValueChecker.self)
+
+ /// 忥åºç¡è®ç»å岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistoryBaseTrainingItem] \
+ /// Read from ``IBResponse``.items
+ static let syncTrain = IBAbility.short.id(0x49).parser(IBSyncTrainParser.self).checker(IBValueChecker.self)
+
+ /// 忥ç¡ç å岿°æ®
+ /// - Parameter value:
+ /// 0 = ä»å¤© \
+ /// 1 = å1天 \
+ /// 2 = å2天 \
+ /// 3 = å3天 \
+ /// 便¤ç±»æ¨ï¼åå¼èå´0~29ã
+ /// - Returns: [IBHistorySleepItem] \
+ /// Read from ``IBResponse``.items
+ static let syncSleep = IBAbility.short.id(0x4B).parser(IBSyncSleepParser.self).checker(IBValueChecker.self)
+
+ /// åæ¥è·¯å¾åæ éå岿°æ®
+ /// - Parameter value:å¼å§è®ç»æ¶é´æ³
+ /// - Returns: [IBHistoryGpsItem] \
+ /// Read from ``IBResponse``.items
+ static let syncGPS = IBAbility.long.id(0x4F).parser(IBSyncGpsParser.self).checker(IBSyncGpsChecker.self)
+
+ /// åéåæ¥æ°æ®ç»ææ¶æ¯
+ static let sendSyncEnd = IBAbility.short.id(0x2E).parser(IBGeneralParser.self)
+
+ /// è·åSDå¡å©ä½ç©ºé´
+ /// - Returns: Int \
+ /// Read from ``IBResponse``.value
+ static let disk = IBAbility.short.id(0x05).parser(IBDiskParser.self)
+
+ /// åéææºä¿¡æ¯
+ /// - Returns: Int \
+ /// 0 = æä»¤æ ¡éªå¤±è´¥ \
+ /// 1 = æè¡¨æªä¸ä»»ä½ææºç»å® \
+ /// 2 = æè¡¨å·²ä¸è¯¥ææºç»å® \
+ /// 3 = æè¡¨å·²ä¸å
¶ä»ææºç»å® \
+ /// Read from ``IBResponse``.value
+ static let sendPhone = IBAbility.long.id(0x10).parser(IBSendPhoneParser.self).checker(IBSendPhoneChecker.self)
+
+ /// åéå½åæ¶é´
+ /// - Parameter sendTime: see ``IBSendTime``
+ static let sendTime = IBAbility.long.id(0x02).parser(IBGeneralParser.self).checker(IBSendTimeChecker.self)
+
+ /// åéè§£é¤é
å¯¹æ¶æ¯
+ static let unpair = IBAbility.short.id(0x10).parser(IBGeneralParser.self)
+
+ /// åéåæ¶è§£é¤é
å¯¹æ¶æ¯
+ static let sendUnpair = IBAbility.short.id(0x2D).parser(IBGeneralParser.self)
+
+ /// åéå¤©æ°æ°æ®
+ /// - Parameter weather: see ``IBWeather``
+ static let sendWeather = IBAbility.long.id(0x20).parser(IBGeneralParser.self).checker(IBWeatherChecker.self)
+
+ /// åéé讯å½
+ /// - Parameter addressBook: see ``IBAddressBook``
+ /// è系人个æ°0~20
+ static let addressBook = IBAbility.long.id(0x0B).parser(IBGeneralParser.self).checker(IBAddressBookChecker.self)
+
+ // MARK: - æä»¶
+
+ /// åéMP3æä»¶ä¿¡æ¯
+ /// - Parameter mp3: see ``IBMp3File``
+ static let mp3 = IBAbility.file.id(0x21).parser(IBGeneralParser.self).checker(IBMp3Checker.self)
+
+// /// åéèªå®ä¹è¡¨çæä»¶æ±æ»ä¿¡æ¯
+// /// - Parameter customDial: see ``IBCustomDial``
+// static let customDial = IBAbility.file.id(0x22).parser(IBGeneralParser.self).checker(IBCustomDialChecker.self)
+
+ /// åé表çæä»¶æ±æ»ä¿¡æ¯
+ /// - Parameter dial: see ``IBDial``
+ static let dial = IBAbility.file.id(0x22).parser(IBGeneralParser.self).checker(IBDialChecker.self)
+
+ /// åéå个表çæä»¶ä¿¡æ¯
+ /// - Parameter dialFile: see ``IBDailFile``
+ internal static let dialFile = IBAbility.file.id(0x24).parser(IBGeneralParser.self).checker(IBDialFileChecker.self)
+
+ /// åéæä»¶æ ¡éªç
+ /// - Parameter value:Int
+ internal static let fileCheckCode = IBAbility.file.id(0x23).parser(IBGeneralParser.self).checker(IBValueChecker.self)
+
+ /// å 餿宿件
+ /// - Parameter deleteFile: see ``IBDeleteFile``
+ static let deleteFile = IBAbility.long.id(0x22).parser(IBGeneralParser.self).checker(IBDeleteFileChecker.self)
+
+ /// å 餿å®ç±»åæææä»¶(å½ååªæ¯æ MP3 å 表ç)
+ /// - Parameter file: see ``IBFile``
+ static let deleteFiles = IBAbility.short.id(0x22).parser(IBGeneralParser.self).checker(IBFileChecker.self)
+
+ /// åéOTAæä»¶ä¿¡æ¯
+ /// - Parameter ota: see ``IBOtaFile``
+ static let ota = IBAbility.file.id(0x20).parser(IBGeneralParser.self).checker(IBOtaChecker.self)
+
+
+ /// åéEPOæä»¶ä¿¡æ¯
+ /// - Parameter epo: see ``[IBEpo]``
+ internal static let epo = IBAbility.file.id(0x28).parser(IBGeneralParser.self).checker(IBEpoChecker.self)
+
+ /// æ¤éå½åæä»¶ä¼ è¾
+ /// - Parameter file: see ``IBFile``
+ static let revokeFileSend = IBAbility.short.id(0x20).parser(IBGeneralParser.self).checker(IBFileChecker.self)
+
+ /// è·åæå®ç±»åæææä»¶(å½ååªæ¯æ MP3 å 表ç)
+ /// - Parameter file: see ``IBFile``
+ /// - Returns: IBFetchFile \
+ /// Read from ``IBResponse``.value
+ static let fetchFiles = IBAbility.short.id(0x21).parser(IBFetchFilesParser.self).checker(IBFileChecker.self)
+
+
+ /// 询é®AIæ¨¡åæ¯å¦å¯ç¨åæ§
+ /// - Parameter aiValid:IBAiValid
+ internal static let aiValid = IBAbility(.ai).characteristic(writeBigCharacteristicUUID).id(0x06).parser(IBGeneralParser.self).checker(IBAiValidChecker.self)
+
+ /// åéAiåå¤ç¶æåæ§
+ /// - Parameter value:Int
+ /// 0 = 失败 \
+ /// 1 = æå \
+ internal static let aiReady = IBAbility.ai.id(0x05).parser(IBGeneralParser.self).checker(IBValueChecker.self)
+
+ /// è·åAiå½é³æä»¶
+ /// - Parameter value:Data \ AIå½é³æä»¶å
容(pcmæ ¼å¼)
+ /// Read from ``IBResponse``.value
+ internal static let fetchAiAudio = IBAbility.short.id(0x34).parser(IBfetchAiAudioParser.self)
+
+ /// åéAiååºå
容
+ /// - Parameter aiResponse: see ``IBAiResponse``
+ internal static let aiResponse = IBAbility.long.id(0x1f).parser(IBGeneralParser.self).checker(IBAiReponseChecker.self)
+}
+
+extension IBAbility {
+
+
+ //
+ //
+ // // MARK: - ç§æ
+ //
+ // /// è·åå½åæ°æ®ï¼jSONæ ¼å¼ï¼ãä¸ä½¿ç¨ã
+ // static let var681B9E92 = IBAbility.short.id(0x06)
+ // /// å¼å
³å¤åªä½åè½ï¼ææ¾ææºé³ä¹ãéè¯çï¼ãä¸ä½¿ç¨ã
+ // static let var681254AC = IBAbility.short.id(0x09)
+ // /// è¿å
¥å·¥åæµè¯æ¨¡å¼ãä¸ä½¿ç¨ã
+ // static let varD3D21DC8 = IBAbility.short.id(0x30)
+ // /// 忥ååå岿°æ®ãä¸ä½¿ç¨ã
+ // static let varA2A25CD8 = IBAbility.short.id(0x42)
+ //
+ // /// ä¿®æ¹æè¡¨èçåç§°ãæªä½¿ç¨ã
+ // static let var8FA46D73 = IBAbility.long.id(0x03)
+ // /// 忥åºç¡è®ç»å®æ¶å¿çè®°å½ãä¸ä½¿ç¨ã
+ // static let var6CFBF636 = IBAbility.long.id(0x4B)
+ // /// 忥å½å¤©ä»»ææ¶é´ä¹åçåºç¡è®ç»è®°å½ãä¸ä½¿ç¨ã
+ // static let var4D0264A9 = IBAbility.long.id(0x4D)
+ // /// APPåéé¶å¿çåºé´å¼ç»æè¡¨ï¼æä¸ç¨ï¼
+ // static let var7EE39B21 = IBAbility.long.id(0x0C)
+ //
+ // /// å鿍¡åå级信æ¯ï¼04,26ï¼ãä¸ä½¿ç¨ã
+ // static let var1FFA68BC = IBAbility.file.id(0x26)
+ // ///  å鿍¡åå个æä»¶å级信æ¯ï¼04,27ï¼ãä¸ä½¿ç¨ã
+ // static let var9AD723C3 = IBAbility.file.id(0x27)
+ // /// åéEPOæä»¶ä¿¡æ¯ï¼04,25ï¼ï¼æ§çï¼ä¸ç¨ï¼
+ // static let var4F791698 = IBAbility.file.id(0x25)
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBAiManager.swift b/code/IOTBluetooth/IOTBluetooth/IBAiManager.swift
new file mode 100644
index 0000000..ba96e93
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBAiManager.swift
@@ -0,0 +1,196 @@
+////
+//// IBAiManager.swift
+//// IOTBluetooth
+////
+//// Created by incar on 2024/7/15.
+////
+//
+//import Foundation
+////import MixSdk
+//class IBAiManager{
+// static let shared = IBAiManager()
+// var audioData = Data()
+//
+// var reply = ""
+//
+// init(){
+// MixSdk.shared.isDebug = true
+// setupChatCallBack()
+// }
+//
+// func aiValid(_ data:Data){
+// let result = IBGeneralParser.dataPacket(data)
+// let code = result.int
+// let desc = ["æå¿ä¸è¨","ChatGPT","KIMI",":è±å
"]
+// Logger.log("è¯¢é®æ¨¡åæ¯å¦å¯ç¨ï¼",desc[code])
+// let model = IBAiValid()
+// model.isValid = true
+// if code == 1 {
+// model.key = "sk-proj-04ogvrZQqyaiv1OWw8OZT3BlbkFJG9Gyr8oFvRdmr3oXQNbX"
+// }
+// IBSender()
+// .ability(IBAbility.aiValid)
+// .parameters([.aiValid(model)])
+// .response { result in
+//
+// }
+//
+// }
+// func aiRequest(_ data:Data){
+// let result = IBGeneralParser.dataPacket(data)
+// let code = result.int
+// let desc = ["æå¿ä¸è¨","ChatGPT","KIMI",":è±å
"]
+// Logger.log("ä½¿ç¨æ¨¡åï¼",desc[code])
+// IBSender()
+// .ability(IBAbility.aiReady)
+// .parameters([.value(1)])
+// .response {[weak self] result in
+// self?.fectchAiAudio()
+// }
+//
+//
+// var platform = MSPlatform.doubao
+// var key = ""
+// if code == 0 {
+// platform = .yiyan
+// }
+// else if code == 1 {
+// platform = .chatGPT
+// key = "sk-proj-04ogvrZQqyaiv1OWw8OZT3BlbkFJG9Gyr8oFvRdmr3oXQNbX"
+//// let gptModel = "gpt-4-turbo"
+//// MixSdk.shared.chatGpt.changeModel(model:gptModel )
+// }
+// else if code == 2 {
+// platform = .kimi
+// }
+// else if code == 3 {
+// platform = .doubao
+// }
+// MixSdk.shared.setPlatform(platform,key: key)
+// }
+// func fectchAiAudio(){
+// IBSender()
+// .ability(IBAbility.fetchAiAudio)
+// .parameters([])
+// .response {[weak self] result in
+// guard let self = self else {return}
+// if result.isSuccess,let data = result.value as? Data{
+// receiveAiData(sbcData: data)
+// }
+// else{
+// Logger.log(result)
+// let model = IBAiResponse()
+// model.type = 0
+// sendRelpy(model: model)
+// }
+// }
+// }
+//
+//
+//}
+//extension IBAiManager{
+// func receiveAiData(sbcData:Data){
+// let pcmData = MSAudioConvert.sbcTopcm(sbcData)
+// if pcmData.count == 0{
+// Logger.log("sbc2pcm失败")
+// let model = IBAiResponse()
+// model.type = 0
+// sendRelpy(model: model)
+// return
+// }
+// guard let pcmPath = IBTools.saveToFile(data: pcmData) else {return}
+// MixSdk.shared.stt.sentenceRecognize(filePath: pcmPath,format:"pcm",sample_rate:"16000",lang: "zh-cn") {[weak self] text, error in
+// guard let self = self else {return}
+// if let text = text,text.count > 0{
+// Logger.log(text)
+// let model = IBAiResponse()
+// model.type = 1
+// model.value = text
+// sendRelpy(model: model)
+//
+// MixSdk.shared.chat.send(content: text)
+// }
+// else{
+// Logger.log(error ?? "")
+// let model = IBAiResponse()
+// model.type = 0
+// sendRelpy(model: model)
+// }
+// }
+// }
+//
+//
+// func setupChatCallBack(){
+// let chat = MixSdk.shared.chat
+// chat.callBack = {[weak self] model in
+// guard let self = self else {return}
+// print("\(model.type) : \(model.content ?? "")")
+//
+// if model.type == "start" {
+// reply = ""
+// }
+// else if model.type == "end" {
+//// let model = IBAiResponse()
+//// model.type = 2
+//// model.value = reply
+//// sendRelpy(model: model)
+// }
+// else if model.type == "middle"{
+//// reply += model.content ?? ""
+// let data = IBAiResponse()
+// data.type = 2
+// data.value = model.content ?? ""
+// sendRelpy(model: data)
+// }
+// }
+// }
+//
+// func sendRelpy(model:IBAiResponse){
+// IBSender()
+// .ability(IBAbility.aiResponse)
+// .parameters([.aiResponse(model)])
+// .response { result in
+// Logger.log(result)
+// }
+// }
+//}
+//
+//extension IBAiManager{
+// func loginAction() {
+// MixSdk.shared.isDebug = true
+// let pwd = "aWGKrBWaijsKD5eEsVVU5rjWPI1sVy"//注åè·å¾çå¯ç
+// self.startLogin(password:pwd)
+// }
+// func startLogin(password:String) {
+// MixSdk.shared.isDebug = true
+// let deviceId = "281106AFB0F7"
+// let productKey = "HHosiYUNdG42t1cTgKOzIUPOvAVMI2QT"
+//
+// MixSdk.shared.auth.login(productKey:productKey,deviceId: deviceId, password: password){[weak self] token,error in
+// if let err = error{
+// if case .reponseError(let code,let message) = err,code == 50021{
+// //50021æ¯è´¦å·æå¯ç é误.å¯ä»¥å¨è¿éè°ç¨æ³¨å,éæ°æ³¨ååï¼ä¹åçå¯ç ä¼è¢«è¦çï¼APPæ´æ°ä¿åçæ°å¯ç
+// self?.registerAction()
+// }
+// }
+// else{
+// print(token ?? "")
+// }
+// }
+// }
+// @objc func registerAction() {
+// let deviceId = "281106AFB0F7"
+// let productId = "pafOCKLiUbeVHbdZlddH"
+// let productKey = "HHosiYUNdG42t1cTgKOzIUPOvAVMI2QT"
+// MixSdk.shared.auth.register(productId: productId, productKey: productKey, deviceId: deviceId){[weak self] password,error in
+// if let pwd = password{
+// print(pwd)
+// //deviceIdåpassword APPè¦ç¡®ä¿è¿ä¸ªä¸ä¸å¯¹åºã
+// self?.startLogin(password: pwd)
+// }
+// else{
+// print(error!)
+// }
+// }
+// }
+//}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBChecker.swift b/code/IOTBluetooth/IOTBluetooth/IBChecker.swift
new file mode 100644
index 0000000..e414359
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBChecker.swift
@@ -0,0 +1,467 @@
+//
+// IBChecker.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/19.
+//
+
+import Foundation
+
+/// åæ°æ£æ¥å¨åè®®
+protocol IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8]
+}
+
+extension Array where Element == IBParameter {
+ subscript(_ key: IBKey) -> Element? {
+ self.filter({ $0.key == key }).first
+ }
+}
+
+struct IBSwitchDialChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .dialID(dailID) = parameters[.dialID] else { fatalError("缺å°åæ°: dialID") }
+ Logger.log(dailID)
+ return dailID.byte2
+ }
+}
+
+struct IBActionChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .action(action) = parameters[.action] else { fatalError("缺å°åæ°: action") }
+ Logger.log(action)
+ return [action.byte]
+ }
+}
+
+struct IBSportChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .motionMode(mode) = parameters[.motionMode] else { fatalError("缺å°åæ°: motionMode") }
+ Logger.log(mode)
+ return [mode.rawValue.byte]
+ }
+}
+
+struct IBLanguageChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .language(value) = parameters[.language] else { fatalError("缺å°åæ°: language") }
+ Logger.log(value)
+ return [value.rawValue.byte]
+ }
+}
+struct IBAuxiliaryChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .auxiliary(value) = parameters[.auxiliary] else { fatalError("缺å°åæ°: auxiliary") }
+ Logger.log(value)
+ return [value.rawValue.byte]
+ }
+}
+struct IBValueChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .value(value) = parameters[.value] else { fatalError("缺å°åæ°: value") }
+ Logger.log(value)
+ return [value.byte]
+ }
+}
+
+struct IBHeartRateHighChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .value(value) = parameters[.value] else { fatalError("缺å°åæ°: value") }
+ Logger.log(value)
+ return [(value == 0 ? 255 : value).byte]
+ }
+}
+
+struct IBTrainFrequencyChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .value(value) = parameters[.value] else { fatalError("缺å°åæ°: value") }
+ Logger.log(value)
+ return value.byte2
+ }
+}
+
+struct IBSyncGpsChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .value(value) = parameters[.value] else { fatalError("缺å°åæ°: value") }
+ Logger.log(value)
+ return value.byte4
+ }
+}
+
+struct IBFileChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .file(value) = parameters[.file] else { fatalError("缺å°åæ°: file") }
+ Logger.log(value)
+ return [value.rawValue.byte]
+ }
+}
+struct IBOtaChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .ota(value) = parameters[.ota] else { fatalError("缺å°åæ°: ota") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: ota") }
+ Logger.log(value.jsonString ?? "")
+ return data.bytes
+ }
+}
+
+struct IBAiReponseChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .aiResponse(value) = parameters[.aiResponse] else { fatalError("缺å°åæ°: aiReponse") }
+ Logger.log(value.description)
+
+ var data = [value.type.byte]
+ if let value = value.value.data(using: .utf8),!value.isEmpty {
+ data += value.count.byte2
+ data += value.bytes
+ }
+ return data
+ }
+}
+struct IBAiValidChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .aiValid(value) = parameters[.aiValid] else { fatalError("缺å°åæ°: aiValid") }
+ Logger.log(value.description)
+
+ var data = value.isValid ? [1.byte]:[0.byte]
+ if value.isValid == true,let value = value.key?.data(using: .utf8),!value.isEmpty {
+ data += value.bytes
+ }
+ return data
+ }
+}
+struct IBTargetStepChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .targetStep(value) = parameters[.targetStep] else { fatalError("缺å°åæ°: targetStep") }
+ Logger.log(value.description)
+ return value.value.byte4 + [value.ring.byte] + [value.vibrate.byte] + [value.notice.byte]
+ }
+}
+
+struct IBSleepChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .sleep(value) = parameters[.sleep] else { fatalError("缺å°åæ°: sleep") }
+ Logger.log(value.description)
+ if value.sleepTimeSet == 1,let sleep = value.sleepTime,let getUp = value.getUpTime {
+ return [value.sleepTimeSet.byte] + [sleep.hour.byte] + [sleep.minute.byte] + [getUp.hour.byte] + [getUp.minute.byte] + [value.alertBeforeMin20.byte]
+ }
+ return [value.sleepTimeSet.byte]
+ }
+}
+struct IBWaterChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+// guard case let .water(value) = parameters[.water] else { fatalError("缺å°åæ°: water") }
+// Logger.log(value.description)
+//
+// var data = [value.alertState.byte]
+// if value.alertState == 1 {
+// data = data + [value.alertType.byte] + value.drinkingValue.byte2 + [value.start.hour.byte] + [value.start.minute.byte]
+// }
+// if value.alertType == 1 {
+// data = data + [value.end.hour.byte] + [value.end.minute.byte] + [value.frequency.rawValue.byte]
+// }
+// return data
+
+ guard case let .water(value) = parameters[.water] else { fatalError("缺å°åæ°: water") }
+ Logger.log(value.description)
+
+ if value.alertState == 0 {
+ return [value.alertState.byte]
+ }
+ var data = [value.alertState.byte] + [value.alertType.byte] + value.drinkingValue.byte2 + [value.start.hour.byte] + [value.start.minute.byte]
+ if value.alertType == 1 {
+ data = data + [value.end.hour.byte] + [value.end.minute.byte] + [value.frequency.rawValue.byte]
+ }
+ return data
+ }
+}
+
+struct IBHeartRateChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .heartRate(value) = parameters[.heartRate] else { fatalError("缺å°åæ°: heartRate") }
+ Logger.log(value.description)
+ var data = [value.rate.rawValue.byte] + [value.state.byte]
+ if value.state == 1 {
+ data = data + [value.higherOrLower.byte] + [value.value.byte]
+ }
+ return data
+ }
+}
+struct IBBloodOxygenChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .bloodOxygen(value) = parameters[.bloodOxygen] else { fatalError("缺å°åæ°: heartRate") }
+ Logger.log(value.description)
+ var data = [value.rate.rawValue.byte] + [value.state.byte]
+ if value.state == 1 {
+ data = data + [value.value.byte]
+ }
+ return data
+ }
+}
+
+
+struct IBOtherTargetChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .otherTarget(value) = parameters[.otherTarget] else { fatalError("缺å°åæ°: otherTarget") }
+ Logger.log(value.description)
+ var data = value.calorie.byte2 + [value.mileage.byte] + [value.stand.byte] + [value.burning.byte] + [value.ringsState.byte] + [value.vibrateState.byte]
+ data = data + [value.noticeState.byte]
+ return data
+ }
+}
+
+struct IBVibrateSoundChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .vibrateSound(value) = parameters[.vibrateSound] else { fatalError("缺å°åæ°: IBVibrateConfig") }
+ Logger.log(value.description)
+ if value.type == 0{
+ if let systemValue = value.systemValue {
+ return [value.type.byte] + [systemValue.byte]
+ }
+ else{
+ fatalError("åæ°é误: IBScreenConfig")
+ }
+ }
+ else if value.type == 1{
+ if let ringsValue = value.ringsValue{
+ return [value.type.byte] + [ringsValue.byte]
+ }
+ else{
+ fatalError("åæ°é误: IBScreenConfig")
+ }
+ }
+ else if value.type == 3{
+ if let ringsState = value.ringsState,let vibrateState = value.vibrateState{
+ return [value.type.byte] + [ringsState.byte] + [vibrateState.byte]
+ }
+ else{
+ fatalError("åæ°é误: IBScreenConfig")
+ }
+ }
+ return []
+ }
+}
+struct IBScreenChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .screen(value) = parameters[.screen] else { fatalError("缺å°åæ°: IBScreenConfig") }
+ Logger.log(value.description)
+ if value.type == 0{
+ if let brightness = value.brightness {
+ return [value.type.byte] + [brightness.byte]
+ }
+ else{
+ fatalError("åæ°é误: IBScreenConfig")
+ }
+ }
+ else{
+ if let duration = value.duration,let wrist = value.wrist {
+ return [value.type.byte] + [duration.rawValue.byte] + [wrist.byte]
+ }
+ else{
+ fatalError("åæ°é误: IBScreenConfig")
+ }
+ }
+ }
+}
+struct IBUserInfoChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .userInfo(value) = parameters[.userInfo] else { fatalError("缺å°åæ°: userInfo") }
+ Logger.log(value.description)
+ var data = [value.age.byte] + [value.gender.byte] + [value.height.byte] + value.weight.byte2 + value.step.byte4 + value.burning.byte2
+ data = data + [value.stand.byte] + [value.hourSystem.byte]
+ return data
+ }
+}
+
+
+struct IBClockChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .clock(value) = parameters[.clock] else { fatalError("缺å°åæ°: clock") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: clock") }
+ Logger.log(value.jsonString ?? "")
+
+ return data.bytes
+ }
+}
+struct IBTrainTargetChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .trainTarget(value) = parameters[.trainTarget] else { fatalError("缺å°åæ°: trainTarget") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: trainTarget") }
+ Logger.log(value.jsonString ?? "")
+
+ return data.bytes
+ }
+}
+struct IBSendPhoneChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .sendPhone(value) = parameters[.sendPhone] else { fatalError("缺å°åæ°: sendPhone") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: sendPhone") }
+ Logger.log(value.jsonString ?? "")
+
+ return data.bytes
+ }
+}
+struct IBSendTimeChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .sendTime(value) = parameters[.sendTime] else { fatalError("缺å°åæ°: sendTime") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: sendTime") }
+ Logger.log(value.jsonString ?? "")
+ return data.bytes
+ }
+}
+struct IBWeatherChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .weather(value) = parameters[.weather] else { fatalError("缺å°åæ°: weather") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: weather") }
+ Logger.log(value.jsonString ?? "")
+ return data.bytes
+ }
+}
+struct IBAddressBookChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .addressBook(list) = parameters[.addressBook] else { fatalError("缺å°åæ°: addressBook") }
+ if list.count > 20 { fatalError("åæ°: addressBook,æå¤ä¸è½è¶
è¿20个è系人") }
+ var data = [list.count.byte]
+ var des = "[ "
+ for item in list {
+ des += item.description
+ //æ§å«
+ data = data + [item.gender.byte]
+ //å§å
+ if let name = item.name,let value = name.data(using: .utf8),!value.isEmpty {
+ data += [value.count.byte]
+ data += value.bytes
+ }
+ else{
+ data += [0.byte]
+ }
+ //çµè¯å·ç 个æ°
+ data += [item.phoneNumbers.count.byte]
+ if item.phoneNumbers.count > 2 { fatalError("åæ°: addressBook,çµè¯å·ç 书æ°ä¸è½è¶
è¿ä¸¤ä¸ª") }
+
+ for phone in item.phoneNumbers {
+ //çµè¯å·ç
+ if let value = phone.number.data(using: .utf8),!value.isEmpty {
+ data += [value.count.byte]
+ data += value.bytes
+ }
+ else{
+ data += [0.byte]
+ }
+ //å½å±å°
+ if let value = phone.location?.data(using: .utf8),!value.isEmpty {
+ data += [value.count.byte]
+ data += value.bytes
+ }
+ else{
+ data += [0.byte]
+ }
+ //è¿è¥å
+ if let value = phone.operator?.data(using: .utf8),!value.isEmpty {
+ data += [value.count.byte]
+ data += value.bytes
+ }
+ else{
+ data += [0.byte]
+ }
+ }
+ }
+ des += " ]"
+ Logger.log(des)
+
+ return data
+ }
+}
+
+// MARK: - æä»¶ä¼ è¾
+struct IBMp3Checker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .MP3(value) = parameters[.mp3] else { fatalError("缺å°åæ°: MP3") }
+ guard let data = value.encode else { fatalError("jsonè§£æå¼å¸¸: MP3") }
+ Logger.log(value.jsonString ?? "")
+ return data.bytes
+ }
+}
+struct IBEpoChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .epo(list) = parameters[.epo] else { fatalError("缺å°åæ°: epo") }
+ var data = [list.count.byte]
+ var des = "[ "
+ for item in list {
+ des += item.description
+ data += [item.UID.byte]
+ if let name = item.fileName.data(using: .utf8),!name.isEmpty {
+ data += [name.count.byte]
+ data += name
+ }
+ data += [item.fileSize.byte]
+ }
+ des += " ]"
+ Logger.log(des)
+ return data
+
+ }
+}
+
+//struct IBCustomDialChecker: IBChecker {
+// static func check(_ parameters: [IBParameter]) -> [UInt8] {
+// guard case let .customDial(value) = parameters[.customDial] else { fatalError("缺å°åæ°: customDial") }
+// Logger.log(value.description)
+// var data = value.UID.byte2 + [value.batteryStyle.byte] + [value.timeStyle.byte] + [value.timeColor.byte]
+// if let list = value.files{
+// data += [list.count.byte]
+// }
+// else{
+// data += [0.byte]
+// }
+// return data
+// }
+//}
+struct IBDialChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .dial(value) = parameters[.dial] else { fatalError("缺å°åæ°: dial") }
+ Logger.log(value.description)
+ var data = value.UID.byte2
+ if let name = value.theme?.data(using: .utf8),!name.isEmpty {
+ data += [name.count.byte]
+ data += name
+ }
+ else{
+ data += [0.byte]
+ }
+ if let list = value.files{
+ data += [list.count.byte]
+ }
+ else{
+ data += [0.byte]
+ }
+ return data
+ }
+}
+struct IBDialFileChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .dialFile(value) = parameters[.dialFile] else { fatalError("缺å°åæ°: dialFile") }
+ Logger.log(value.description)
+ var data = value.UID.byte2 + [value.index.byte] + [value.fileType.byte]
+ if let name = value.fileName.data(using: .utf8),!name.isEmpty {
+ data += [name.count.byte]
+ data += name
+ }
+ else{
+ data += [0.byte]
+ }
+ data += value.size.byte4
+ return data
+ }
+}
+
+struct IBDeleteFileChecker: IBChecker {
+ static func check(_ parameters: [IBParameter]) -> [UInt8] {
+ guard case let .deleteFile(value) = parameters[.deleteFile] else { fatalError("缺å°åæ°: deleteFile") }
+ Logger.log(value.description)
+ var data = [value.uids.count.byte] + [value.fileType.rawValue.byte]
+ for uid in value.uids {
+ data += uid.byte2
+ }
+ return data
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBFileSender+Dial.swift b/code/IOTBluetooth/IOTBluetooth/IBFileSender+Dial.swift
new file mode 100644
index 0000000..672927b
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBFileSender+Dial.swift
@@ -0,0 +1,165 @@
+//
+// IBFileSender+Dial.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/12.
+//
+
+import Foundation
+extension IBFileSender{
+// func sendDial(_ info:IBCustomDial){
+// IBSender()
+// .ability(.customDial)
+// .parameters([.customDial(info)])
+// .response {[weak self] result in
+// guard let self = self else {return}
+// if result.isSuccess{
+// loadFile(info)
+// }
+// else{
+// completion(result)
+// self.remove()
+// }
+// }
+// }
+
+// private func loadFile(_ info:IBCustomDial){
+// guard let files = info.files else {
+// completion(.response.code(.success))
+// self.remove()
+// return
+// }
+//
+// var dataList:[IBFileSenderContent] = []
+// for info in files{
+// let (list,size,lrc) = readFile(from: info.filePath)
+// if let list = list {
+// info.size = size
+// total += info.size
+// dataList.append(IBFileSenderContent(dataList: list,lrc: lrc,dialFile: info))
+// }
+// else{
+// completion(.response.code(.failure).message("读åæä»¶å¤±è´¥"))
+// self.remove()
+// return
+// }
+// }
+// self.fileList = dataList
+// sendDialFileInfo()
+// }
+ private func sendDialFileInfo(){
+ guard let file = self.fileList.first,let info = file.dialFile else {
+ completion(.response.code(.success))
+ self.remove()
+ return;
+ }
+ IBSender()
+ .ability(.dialFile)
+ .parameters([.dialFile(info)])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ self.sendContentDial()
+ }
+ else{
+ completion(result)
+ self.remove()
+ }
+ }
+ }
+
+ private func sendContentDial(){
+ if cancelSend {
+ Logger.log("已忶åé")
+ self.remove()
+ return
+ }
+
+ if let file = fileList.first,let data = file.dataList.first{
+ file.dataList.removeFirst()
+ HsFileAgentSdk.shared.fileSender = self
+ let model = IBFileSenderData()
+ model.data = data
+ model.fileWriteHander = { [weak self] result in
+ guard let self = self else { return }
+ self.lastData = nil
+ if result.isSuccess {
+ self.current += data.count
+ Logger.log("\(total) : \(current)")
+ self.progress?(self.total,self.current)
+ self.sendContentDial()
+ }
+ else{
+ completion(.response.code(.failure).message("åéæä»¶è¶
æ¶ï¼åé失败ï¼"))
+ self.remove()
+ }
+ }
+ self.lastData = model
+ HsFileAgentSdk.shared.sendToDevice(fileData: data)
+ }
+ else{
+ Logger.log( "æä»¶å
容åéç»æ...")
+ startTimer()
+ }
+ }
+
+ func sendDialLrc(){
+ IBSender()
+ .ability(.fileCheckCode)
+ .parameters([.value(fileList.first!.lrc)])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ self.fileList.removeFirst()
+ self.sendDialFileInfo()
+ }
+ else{
+ completion(.response.code(.failure).message("åéæä»¶æ ¡éªç 失败"))
+ self.remove()
+ }
+ }
+ }
+}
+
+extension IBFileSender{
+ func sendDial(_ info:IBDial){
+ IBSender()
+ .ability(.dial)
+ .parameters([.dial(info)])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ loadFile(info)
+ }
+ else{
+ completion(result)
+ self.remove()
+ }
+ }
+ }
+
+ private func loadFile(_ info:IBDial){
+ guard let files = info.files else {
+ completion(.response.code(.success))
+ self.remove()
+ return
+ }
+
+ var dataList:[IBFileSenderContent] = []
+ for info in files{
+ let (list,size,lrc) = readFile(from: info.filePath)
+ if let list = list {
+ info.size = size
+ total += info.size
+ dataList.append(IBFileSenderContent(dataList: list,lrc: lrc,dialFile: info))
+ }
+ else{
+ completion(.response.code(.failure).message("读åæä»¶å¤±è´¥"))
+ self.remove()
+ return
+ }
+ }
+ self.fileList = dataList
+ sendDialFileInfo()
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBFileSender+Mp3.swift b/code/IOTBluetooth/IOTBluetooth/IBFileSender+Mp3.swift
new file mode 100644
index 0000000..90e4e32
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBFileSender+Mp3.swift
@@ -0,0 +1,88 @@
+//
+// IBFileSender+Mp3.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/12.
+//
+
+import Foundation
+
+extension IBFileSender{
+ func sendMp3(_ info:IBSendMp3){
+ let (list,size,lrc) = readFile(from: info.filePath)
+ if let list = list {
+ info.size = size
+ total = info.size
+ let content = IBFileSenderContent(dataList: list,lrc: lrc)
+ self.fileList = [content]
+
+ IBSender()
+ .ability(.mp3)
+ .parameters([.MP3(IBMp3File(data:info))])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ self.sendContentMp3()
+ }
+ else{
+ completion(result)
+ self.remove()
+ }
+ }
+ }
+ else{
+ completion(.response.code(.failure).message("读åæä»¶å¤±è´¥"))
+ self.remove()
+ }
+ }
+ private func sendContentMp3(){
+ if cancelSend {
+ Logger.log("已忶åé")
+ self.remove()
+ return
+ }
+
+ if let file = fileList.first,let data = file.dataList.first{
+ file.dataList.removeFirst()
+ HsFileAgentSdk.shared.fileSender = self
+ let model = IBFileSenderData()
+ model.data = data
+ model.fileWriteHander = { [weak self] result in
+ guard let self = self else { return }
+ self.lastData = nil
+ if result.isSuccess {
+ self.current += data.count
+ Logger.log("\(total) : \(current)")
+ self.progress?(self.total,self.current)
+ self.sendContentMp3()
+ }
+ else{
+ completion(.response.code(.failure).message("åéæä»¶è¶
æ¶ï¼åé失败ï¼"))
+ self.remove()
+ }
+ }
+ self.lastData = model
+ HsFileAgentSdk.shared.sendToDevice(fileData: data)
+ }
+ else{
+ Logger.log( "æä»¶å
容åéç»æ...")
+ startTimer()
+ }
+ }
+ func sendMp3Lrc(){
+ IBSender()
+ .ability(.fileCheckCode)
+ .parameters([.value(fileList.first!.lrc)])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ completion(result)
+ self.remove()
+ }
+ else{
+ completion(.response.code(.failure).message("åéæä»¶æ ¡éªç 失败"))
+ self.remove()
+ }
+ }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBFileSender+Ota.swift b/code/IOTBluetooth/IOTBluetooth/IBFileSender+Ota.swift
new file mode 100644
index 0000000..100c8da
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBFileSender+Ota.swift
@@ -0,0 +1,87 @@
+//
+// IBSender+Ota.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/12.
+//
+
+import Foundation
+extension IBFileSender{
+ func sendOta(_ info:IBSendOta){
+ let (list,size,lrc) = readFile(from: info.filePath)
+ if let list = list {
+ info.size = size
+ total = info.size
+ let content = IBFileSenderContent(dataList: list,lrc: lrc)
+ self.fileList = [content]
+
+ IBSender()
+ .ability(.ota)
+ .parameters([.ota(IBOtaFile(data:info))])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ self.sendContentOta()
+ }
+ else{
+ completion(result)
+ self.remove()
+ }
+ }
+ }
+ else{
+ completion(.response.code(.failure).message("读åæä»¶å¤±è´¥"))
+ self.remove()
+ }
+ }
+ private func sendContentOta(){
+ if cancelSend {
+ Logger.log("已忶åé")
+ self.remove()
+ return
+ }
+
+ if let file = fileList.first,let data = file.dataList.first{
+ file.dataList.removeFirst()
+ HsFileAgentSdk.shared.fileSender = self
+ let model = IBFileSenderData()
+ model.data = data
+ model.fileWriteHander = { [weak self] result in
+ guard let self = self else { return }
+ self.lastData = nil
+ if result.isSuccess {
+ self.current += data.count
+ Logger.log("\(total) : \(current)")
+ self.progress?(self.total,self.current)
+ self.sendContentOta()
+ }
+ else{
+ completion(.response.code(.failure).message("åéæä»¶è¶
æ¶ï¼åé失败ï¼"))
+ self.remove()
+ }
+ }
+ self.lastData = model
+ HsFileAgentSdk.shared.sendToDevice(fileData: data)
+ }
+ else{
+ Logger.log( "æä»¶å
容åéç»æ...")
+ startTimer()
+ }
+ }
+ func sendOtaLrc(){
+ IBSender()
+ .ability(.fileCheckCode)
+ .parameters([.value(fileList.first!.lrc)])
+ .response {[weak self] result in
+ guard let self = self else {return}
+ if result.isSuccess{
+ completion(result)
+ self.remove()
+ }
+ else{
+ completion(.response.code(.failure).message("åéæä»¶æ ¡éªç 失败"))
+ self.remove()
+ }
+ }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBFileSender.swift b/code/IOTBluetooth/IOTBluetooth/IBFileSender.swift
new file mode 100644
index 0000000..f36d7bf
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBFileSender.swift
@@ -0,0 +1,261 @@
+//
+// IBFileSender.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/6.
+//
+
+import Foundation
+
+public typealias IBProgressHandler = (_ total:Int, _ current:Int) -> Void
+internal typealias IBFileWriteHandler = (_ result:IBResponse) -> Void
+
+class IBFileSenderData {
+ /// åéçæ°æ®
+ var data:Data = Data()
+ /// æä»¶åå
¥æååè°
+ var fileWriteHander:IBFileWriteHandler?
+}
+
+ class IBFileSenderContent {
+ var dataList:[Data] = []
+ var lrc = 0
+ var dialFile:IBDialFile?
+
+ init(dataList: [Data], lrc: Int = 0, dialFile: IBDialFile? = nil) {
+ self.dataList = dataList
+ self.lrc = lrc
+ self.dialFile = dialFile
+ }
+}
+
+
+
+public class IBFileSender {
+ var completion: IBCompletionHandler!
+ var progress: IBProgressHandler?
+
+ var otaInfo:IBSendOta?
+ var mp3Info:IBSendMp3?
+ var epoInfo:[IBEpo]?
+// var customInfo:IBCustomDial?
+ var dialInfo:IBDial?
+
+ var fileList:[IBFileSenderContent] = []
+ var total:Int = 0
+ var current:Int = 0
+
+
+ var cancelSend = false
+ var lastData:IBFileSenderData?
+
+ public init() {
+ Self.all.append(self)
+
+ }
+ deinit {
+ Logger.log("deinit IBFileSender")
+ }
+ func sendFileResult(_ success:Bool ){
+ deinitTimer()
+ if (success){
+ Logger.log("æè¡¨æ¥æ¶æä»¶æå")
+ sendFileFinish()
+ }
+ else{
+ completion(.response.code(.failure).message("æè¡¨æ¥æ¶æä»¶å¤±è´¥"))
+ self.remove()
+ }
+ }
+
+ func sendFileFinish(){
+ if let _ = self.mp3Info{
+ sendMp3Lrc()
+ }
+ else if let _ = self.otaInfo {
+ sendOtaLrc()
+ }
+ else if let _ = self.dialInfo{
+ sendDialLrc()
+ }
+// else if let _ = self.marketInfo{
+// sendDialLrc()
+// }
+ // else if let path = self.epoInfo.filePath {
+ // filePath = path
+ // }
+ }
+}
+
+
+
+extension IBFileSender{
+ func start(){
+ if let info = self.mp3Info{
+ self.sendMp3(info)
+ }
+ else if let info = self.otaInfo {
+ self.sendOta(info)
+ }
+ else if let info = self.dialInfo{
+ self.sendDial(info)
+ }
+// else if let info = self.marketInfo{
+// self.sendDial(info)
+// }
+ // else if let path = self.epoInfo.filePath {
+ // filePath = path
+ // }
+ }
+ func readFile(from path:String) ->([Data]?,Int,Int){
+ let fileURL = URL(fileURLWithPath: path)
+ do {
+ let data = try Data(contentsOf:fileURL)
+ let seg = 495
+ var list = [Data]()
+ for i in stride(from: 0, to: data.count, by: seg) {
+ if i+seg < data.count {
+ list.append(data[i ... i+seg - 1])
+ }
+ else{
+ list.append(data[i ... data.count - 1])
+ }
+ }
+ let lrc = LRC.calculateLRC(data.bytes).int
+ return (list,data.count,lrc)
+ } catch {
+ return (nil,0,0)
+ }
+ }
+}
+
+public extension IBFileSender{
+ @discardableResult
+ /// åéOTA
+ /// - Parameters:
+ /// - filePath: æä»¶è·¯å¾
+ /// - info: otaä¿¡æ¯
+ /// - Returns: self
+ func ota(_ info:IBSendOta) ->Self{
+ self.otaInfo = info
+ return self
+ }
+ @discardableResult
+
+
+ /// åémp3
+ /// - Parameter info: mp3ä¿¡æ¯
+ /// - Returns: Self
+ func mp3(_ info:IBSendMp3) ->Self{
+ self.mp3Info = info
+ return self
+ }
+
+// /// åéèªå®ä¹è¡¨ç
+// /// - Parameter info: 表çä¿¡æ¯
+// /// - Returns: Self
+// func customDial(_ info:IBCustomDial) ->Self{
+// if let list = info.files,list.count > 0{
+// for i in 0...list.count - 1 {
+// list[i].index = i
+// list[i].UID = info.UID
+// }
+// }
+// self.customInfo = info
+// return self
+// }
+ /// åé表ç
+ /// - Parameter info: 表çä¿¡æ¯
+ /// - Returns: Self
+ func dial(_ info:IBDial) ->Self{
+// IBMarketDial
+
+ if let list = info.files,list.count > 0{
+ for i in 0...list.count - 1{
+ list[i].index = i
+ list[i].UID = info.UID
+ }
+ }
+
+ self.dialInfo = info
+ return self
+ }
+
+ /// åéEPOæä»¶
+ /// - Parameters:
+ /// - filePath: æä»¶è·¯å¾
+ /// - info: epoä¿¡æ¯
+ /// - Returns: self
+ // func epo(filePath:URL,info:[IBEpo]) ->Self{
+ // self.filePath = filePath
+ // self.epoInfo = info
+ // return self
+ // }
+
+ /// è¿åº¦
+ @discardableResult
+ func progress(_ value: @escaping IBProgressHandler) -> Self {
+ progress = value
+ return self
+ }
+
+ /// æ§è¡ä¸ååºå¤ç
+ @discardableResult
+ func response(_ handler: @escaping IBCompletionHandler) -> Self {
+ if Self.all.count > 1{
+ handler(.response.code(.failure).message("å½åææä»¶æ£å¨ä¼ è¾ä¸..."))
+ self.remove()
+ return self
+ }
+
+ completion = handler
+ self.start()
+ return self
+ }
+
+
+ /// æ¤éå½åçæä»¶ä¼ è¾
+ func revokeFileSend(_ handler: @escaping IBCompletionHandler){
+ for item in Self.all{
+ item.cancelSend = true
+ }
+
+ IBSender()
+ .ability(.revokeFileSend)
+ .parameters([.file(.mp3)])
+ .response { result in
+ handler(result)
+ self.remove()
+ }
+ }
+}
+
+extension IBFileSender {
+ static var all: [IBFileSender] = []
+ func remove() {
+ Self.all.removeAll { $0 === self }
+ }
+}
+
+extension IBFileSender {
+ static var timer: Timer?
+ func startTimer() {
+ deinitTimer()
+
+ Self.timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false, block: { [unowned self] _ in
+ deinitTimer()
+ self.completion(.response.code(.failure).message("åéæä»¶å¤±è´¥"))
+ self.remove()
+ })
+ Logger.log("IBFileSenderåå»ºå®æ¶å¨")
+ }
+
+ func deinitTimer() {
+ guard let _ = Self.timer else { return }
+ Self.timer?.invalidate()
+ Self.timer = nil
+
+ Logger.log("IBFileSender鿝宿¶å¨")
+
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBKeeper.swift b/code/IOTBluetooth/IOTBluetooth/IBKeeper.swift
new file mode 100644
index 0000000..53b8a37
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBKeeper.swift
@@ -0,0 +1,104 @@
+//
+// IBKeeper.swift
+// UnifyBluetooth
+//
+// Created by yaozuopan on 2024/2/5.
+//
+
+import Foundation
+import CoreBluetooth
+
+class UBDevice: Codable {
+ let uuid: String
+ let name: String
+ let vendors: Int
+
+ var battery: Int
+ var rssi: Int
+
+ init(_ uuid: String, vendors: Int, name: String, battery: Int, rssi: Int) {
+ self.uuid = uuid
+ self.name = name
+ self.vendors = vendors
+ self.battery = battery
+ self.rssi = rssi
+ }
+}
+
+/// æ°æ®ä¿å
+struct IBKeeper {
+ static let saver = UserDefaults.standard
+
+ private static let serviceKey = "IBKeeper.Services"
+ private static let connectedKey = "IBKeeper.Connected"
+
+ /// è®°å½ææ°çè¿æ¥è®¾å¤
+ static func lastConnected(_ peripheral: IBPeripheral?) {
+ guard let peripheral = peripheral else { return }
+ addServices(peripheral.peripheral.services)
+ addConnected(peripheral)
+ }
+
+ /// ä¿åèçæå¡ç¹å¾
+ private static func addServices(_ services: [CBService]?) {
+ guard let services = services?.map({ $0.uuid.uuidString }) else { return }
+
+ var recordServices = saver.stringArray(forKey: serviceKey) ?? []
+ recordServices.append(contentsOf: services)
+ recordServices = Array(Set(recordServices))
+ saver.setValue(recordServices, forKey: serviceKey)
+ }
+
+ /// è®°å½å·²è¿æ¥è¿ç设å¤
+ private static func addConnected(_ peripheral: IBPeripheral) {
+ var recordConnected: [UBDevice] = []
+ if let jsonData = saver.data(forKey: connectedKey) {
+ recordConnected = JSON.decode([UBDevice].self, from: jsonData) ?? []
+ }
+
+ if let device = recordConnected.filter({ $0.uuid == peripheral.uuid.uuidString }).first {
+ device.battery = peripheral.battery
+ device.rssi = peripheral.rssi
+ } else {
+ recordConnected.append(UBDevice(peripheral.uuid.uuidString,
+ vendors: peripheral.vendors.rawValue,
+ name: peripheral.peripheral.name ?? "",
+ battery: peripheral.battery,
+ rssi: peripheral.rssi))
+ }
+
+ saver.setValue(recordConnected.jsonData, forKey: connectedKey)
+ }
+
+ /// ä»å·²è¿æ¥è®°å½ä¸ç§»é¤å·²è§£ç»è®¾å¤
+ static func removeConnected(_ uuid: UUID?) {
+ guard let uuid = uuid else { return }
+
+ var recordConnected: [UBDevice] = []
+ if let jsonData = saver.data(forKey: connectedKey) {
+ recordConnected = JSON.decode([UBDevice].self, from: jsonData) ?? []
+ }
+
+ recordConnected.removeAll { $0.uuid == uuid.uuidString }
+ saver.setValue(recordConnected.jsonData, forKey: connectedKey)
+ }
+
+ /// å·²è¿æ¥è¿è®¾å¤çæå¡ç¹å¾å表
+ static var services: [CBUUID] {
+ (saver.stringArray(forKey: serviceKey) ?? []).map { CBUUID(string: $0) }
+ }
+
+ /// å·²è¿æ¥è¿ç设å¤å表
+ static var connected: [IBPeripheral] {
+ guard let jsonData = saver.data(forKey: connectedKey) else { return [] }
+ let recordConnected = JSON.decode([UBDevice].self, from: jsonData) ?? []
+ return recordConnected.map {
+ IBPeripheral(nil, uuid: $0.uuid, name: $0.name, vendors: $0.vendors, rssi: $0.rssi, battery: $0.battery) }
+ }
+
+ /// å·²è¿æ¥è¿ç设å¤UUIDå表
+ static var connectedUUIDs: [UUID] {
+ guard let jsonData = saver.data(forKey: connectedKey) else { return [] }
+ return (JSON.decode([UBDevice].self, from: jsonData) ?? []).compactMap { UUID(uuidString: $0.uuid) }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBNotify.swift b/code/IOTBluetooth/IOTBluetooth/IBNotify.swift
new file mode 100644
index 0000000..cd5b516
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBNotify.swift
@@ -0,0 +1,244 @@
+//
+// IBNotify.swift
+//
+//
+// Created by Harvey on 2020/1/5.
+// Copyright © 2020 å§ä½æ½/Harvey. All rights reserved.
+//
+
+import Foundation
+
+fileprivate let center = NotificationCenter.default
+
+fileprivate class Weakly: NSObject {
+ weak var owner: NSObjectProtocol?
+ var delegateObservers: [NSObjectProtocol]
+ init(_ owner: NSObjectProtocol, delegateObservers: [NSObjectProtocol] = []) {
+ self.owner = owner
+ self.delegateObservers = delegateObservers
+ }
+}
+
+extension IBNotify {
+
+ fileprivate struct Keeper {
+ static var observers: [Weakly] = []
+ static func remove(_ observer: NSObjectProtocol) {
+ observers
+ .filter { $0.owner === observer || isNil($0.owner) }
+ .forEach {
+ $0.delegateObservers.forEach { center.removeObserver($0) }
+ }
+ observers.removeAll { isNil($0.owner) || $0.owner === observer }
+ }
+
+ static func append(_ delegateObserver: NSObjectProtocol, owner: NSObjectProtocol) {
+ guard let weakly = observers.filter({ $0.owner === owner }).first else {
+ observers.append(Weakly(owner, delegateObservers: [delegateObserver]))
+ return
+ }
+ weakly.delegateObservers.append(delegateObserver)
+ }
+ }
+
+ public struct Name {
+ fileprivate let rawValue: String
+ init(_ rawValue: String) {
+ self.rawValue = rawValue
+ }
+ }
+}
+
+public class IBNotify {
+
+ init() {}
+
+ private(set) var name: Notification.Name!
+ @discardableResult
+ func name(_ value: IBNotify.Name) -> Self {
+ name = Notification.Name(rawValue: value.rawValue)
+ return self
+ }
+
+ private(set) var observer: NSObjectProtocol!
+ @discardableResult
+ func observer(_ value: NSObjectProtocol) -> Self {
+ observer = value
+ return self
+ }
+
+ private(set) var queue: OperationQueue = .main
+ @discardableResult
+ func queue(_ value: OperationQueue) -> Self {
+ queue = value
+ return self
+ }
+
+ private(set) var object: Any?
+ @discardableResult
+ func object(_ value: Any) -> Self {
+ object = value
+ return self
+ }
+
+ func subscribe(_ value: @escaping (Notification) -> Void) {
+ if isNil(observer) || isNil(name) {
+ fatalError("observer or name must be not nil")
+ }
+
+ let delegateObserver = center
+ .addObserver(forName: name,
+ object: object,
+ queue: queue,
+ using: value)
+ Keeper.append(delegateObserver, owner: observer)
+ }
+
+ static func remove(_ observer: NSObjectProtocol) {
+ center.removeObserver(observer)
+ Keeper.remove(observer)
+ }
+}
+
+extension IBNotify {
+ static func post(_ name: IBNotify.Name,
+ object: Any? = nil,
+ userInfo: [AnyHashable : Any] = [:]) {
+ center.post(name: Notification.Name(rawValue: name.rawValue),
+ object: object, userInfo: userInfo)
+ }
+}
+
+//public extension IBManager {
+//
+// /// 订é
éç¥
+// static func subscribe(_ name: IBNotify.Name,
+// observer: NSObjectProtocol!,
+// receive: @escaping (_ object: Any?, _ userInfo: [String: Any]) -> Void) {
+// IBNotify()
+// .name(name)
+// .observer(observer)
+// .subscribe { notification in
+// receive(notification.object, notification.userInfo as? [String: Any] ?? [:])
+// }
+// }
+//
+// /// ç§»é¤è®¢é
éç¥çè§å¯è
+// static func remove(_ observer: NSObjectProtocol) {
+// IBNotify.remove(observer)
+// }
+//}
+
+public extension IBNotify.Name {
+ /// æ¯å¦çµéãä¿¡å·å¼ºåº¦
+// static let deviceInfoUpdate = IBNotify.Name("IOTBluetooth.DeviceInfoUpdate")
+
+ /// è¿æ¥è®¾å¤åçåå¨ï¼ä¸ç§æ
嵿å¼è¿æ¥ï¼ä¸ç§æ¯åæ¢è®¾å¤
+// static let connectedDeviceDidChange = IBNotify.Name("IOTBluetooth.DeviceConnected")
+
+ /// é´æç¶ææ¹åï¼éè¿object è·åç¶æï¼0åå¤é´æã1çå¾
æè¡¨åæè¿æ¥ã 2çå¾
ææºç¡®è®¤é
对请æ±ã3é´ææåã4é´æå¤±è´¥ã
+// static let authStateDidChange = IBNotify.Name("IOTBluetooth.AuthStateDidChange")
+
+ /// åç°è®¾å¤
+// static let deviceHasFound = IBNotify.Name("IOTBluetooth.DeviceHasFound")
+
+
+ /// è¿æ¥ç¶æåçæ¹åãéè¿object è·åç¶æï¼0æå¼è¿æ¥ã1è¿ä¸è®¾å¤ã
+ static let connectStateDidChange = IBNotify.Name("IOTBluetooth.ConnectStateDidChange")
+
+ /// è¿ç¨æç
§å¨ä½ãéè¿objectè·åç¶æã2è¿è¡æå¿æç
§ï¼3éåº
+ static let takePhoto = IBNotify.Name("IOTBluetooth.TakePhoto")
+
+ /// æè¡¨è¯·æ±å¤©æ°æ°æ®
+ static let weather = IBNotify.Name("IOTBluetooth.weather")
+
+ /// æ¾å°è®¾å¤
+ static let didFindDevice = IBNotify.Name("IOTBluetooth.FindDevice")
+
+ /// è®ç»ç¶æåå,éè¿objectè·åç¶æIBTarinState对象
+ static let trainStateDidChanged = IBNotify.Name("IOTBluetooth.TrainStateChanged")
+
+ /// è·åæè¡¨æ¨éçè®ç»å®æ¶æ°æ®,éè¿objectè·åç¶æIBTrainingData对象
+ static let trainingData = IBNotify.Name("IOTBluetooth.TrainingData")
+
+ /// æè¡¨åéæä»¶å·²æ¥æ¶å®æ
+ internal static let fileReceiveFinish = IBNotify.Name("IOTBluetooth.TileReceiveFinish")
+}
+
+extension IBNotify {
+ /// å¤çæè¡¨æ¨éè¿æ¥çéç¥
+ static func handle(_ data: Data) -> Bool {
+ //æç
§
+ if IBAbility.takePhoto.isAnswer(data) {
+ let result = IBGeneralParser.dataPacket(data)
+ let code = result[0].int
+ if code == 2 || code == 3 {
+ post(.takePhoto, object:code)
+ }
+ return true
+ }
+ //æ¾å°è®¾å¤
+ if IBAbility.find.isAnswer(data) {
+ var bytes = data.bytes
+ bytes.removeLast()
+ let code = bytes.last!.int
+ if code == 2 {
+ post(.didFindDevice)
+ }
+ return true
+ }
+ //请æ±å¤©æ°
+ if data.bytes[1] == 0x05 && data.bytes[2] == 0x03 {
+ post(.weather)
+ return true
+ }
+ //表çå®è£
ç¶æ
+ if(data.bytes[1] == 0x05 && data.bytes[2] == 0x04){
+ let result = IBGeneralParser.dataPacket(data)
+ let code = result.int
+ let desc = ["å®è£
失败","å®è£
æå"]
+ Logger.log("表çå®è£
ç¶æï¼",desc[code])
+ return true
+ }
+ //æè¡¨è¯¢é®APP AIæ¨¡åæ¯å¦å¯ç¨
+ if(data.bytes[1] == 0x05 && data.bytes[2] == 0x06){
+// IBAiManager.shared.aiValid(data)
+ return true
+ }
+ //AIå½é³å·²åå¤å¥½
+ if(data.bytes[1] == 0x05 && data.bytes[2] == 0x05){
+// IBAiManager.shared.aiRequest(data)
+ return true
+ }
+ //æä»¶å·²æ¥æ¶å®æ
+ if data.bytes[1] == 0x05 && data.bytes[2] == 0x20 {
+ let result = IBGeneralParser.dataPacket(data)
+ HsFileAgentSdk.shared.fileSender?.sendFileResult(result[0].int == 1)
+ return true
+ }
+ //è®ç»ç¶æåå
+ if data.bytes[1] == 0x05 && data.bytes[2] == 0x50 {
+ let result = IBGeneralParser.dataPacket(data)
+ let model = IBTarinState(result.data)
+ post(.trainStateDidChanged, object: model)
+ return true
+ }
+ //è®ç»å®æ¶æ°æ®
+ if IBAbility.currentHeartRate.isAnswer(data) {
+ let dataPacket = IBGeneralParser.dataPacket(data)
+ if let model = IBTrainingData(dataPacket.data) {
+ post(.trainingData, object: model)
+ }
+ return true
+ }
+ //é
对
+ if IBPairing.checkPairing(data){
+ return true
+ }
+ return false
+ }
+
+
+
+
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBPairling.swift b/code/IOTBluetooth/IOTBluetooth/IBPairling.swift
new file mode 100644
index 0000000..69d2b8c
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBPairling.swift
@@ -0,0 +1,72 @@
+//
+// IBPairling.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/7/15.
+//
+
+import Foundation
+
+class IBPairing{
+ static func checkPairing(_ data:Data)->Bool{
+ if data.bytes[1] == 0x05 && data.bytes[2] == 0x11 {
+ let result = IBGeneralParser.dataPacket(data)
+ let code = result[0].int
+ let desc = ["æç»é
对","确认é
对","确认解ç»å
¶ä»ææº","å
¶ä»"]
+ Logger.log("æ¶å°ç¡®è®¤é
å¯¹æ¶æ¯ï¼",desc[code])
+ return true
+ }
+ if data.bytes[1] == 0x05 && data.bytes[2] == 0x1 {
+ //è§£ç»
+ let result = IBGeneralParser.dataPacket(data)
+ let code = result[0].int
+ let desc = ["æç»è§£ç»","确认解ç»"]
+ Logger.log("æ¶å°ç¡®è®¤è§£ç»æ¶æ¯ ",desc[code])
+ return true
+ }
+ if data.bytes[1] == 0x05 && data.bytes[2] == 0x10 {
+ //é
å¯¹ç»æ
+ var result = IBGeneralParser.dataPacket(data)
+ let code = result[0].int
+ if code == 1{
+ result.removeFirst()
+ UserDefaults.standard.setValue(result, forKey: "IOTBletooth.mac")
+ UserDefaults.standard.synchronize()
+
+ Logger.log("æ¶å°é
对æåæ¶æ¯",result)
+ //åéæ¶é´
+ IBPairing.sendTime()
+ }
+ else{
+ Logger.log("æ¶å°é
å¯¹å¤±è´¥æ¶æ¯")
+// IBManager.completeHandler?(false)
+// IBManager.completeHandler = nil
+ }
+
+ return true
+ }
+ return false
+ }
+
+ static func sendTime(){
+ let date = Date()
+ let model = IBSendTime()
+ model.time = Int(date.timeIntervalSince1970)
+ model.week = date.weekDay.rawValue - 1
+ model.year = date.year - 2000
+ model.month = date.month
+ model.day = date.day
+ model.hour = date.hour
+ model.minute = date.minute
+ model.second = date.second
+
+ IBSender()
+ .ability(IBAbility.sendTime)
+ .parameters([.sendTime(model)])
+ .response { result in
+ Logger.log(result)
+// IBManager.completeHandler?(true)
+// IBManager.completeHandler = nil
+ }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBParameter.swift b/code/IOTBluetooth/IOTBluetooth/IBParameter.swift
new file mode 100644
index 0000000..853f8a4
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBParameter.swift
@@ -0,0 +1,216 @@
+//
+// IBParameter.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/19.
+//
+
+import Foundation
+
+enum IBKey: String {
+ case dialID = "dialID"
+ case action = "action"
+ case motionMode = "motionMode"
+ case language = "language"
+ case value = "value"
+ case file = "file"
+ case targetStep = "targetStep"
+ case sleep = "sleep"
+ case water = "water"
+ case clock = "clock"
+ case heartRate = "heartRate"
+ case bloodOxygen = "bloodOxygen"
+ case screen = "screen"
+ case vibrateSound = "vibrateSound"
+ case otherTarget = "otherTarget"
+ case trainTarget = "trainTarget"
+ case sendPhone = "sendPhone"
+ case sendTime = "sendTime"
+ case auxiliary = "auxiliary"
+ case userInfo = "userInfo"
+ case weather = "weather"
+ case addressBook = "addressBook"
+ case epo = "epo"
+ case ota = "ota"
+ case mp3 = "mp3"
+ case dial = "dial"
+ case dialFile = "dialFile"
+ case deleteFile = "deleteFile"
+ case fetchFils = "fetchFils"
+ case aiValid = "aiValid"
+ case aiResponse = "aiResponse"
+}
+
+/// åæ°
+public enum IBParameter {
+ /// 表çID
+ case dialID(Int)
+ /// 1=(å¼å§ãå¯ç¨ãå¼å¯)ï¼0=(忢ãç¦ç¨ãå
³é)
+ case action(Int)
+ /// è¿å¨æ¨¡å¼
+ case motionMode(IBMotionMode)
+ /// è¯è¨
+ case language(IBLanguage)
+ /// éç¨å¼è®¾ç½®
+ case value(Int)
+ /// æä»¶
+ case file(IBFile)
+ /// è®ç»ç®æ
+ case targetStep(IBTargetStep)
+ /// ç¡ç
+ case sleep(IBSleep)
+ /// åæ°´
+ case water(IBWater)
+ /// 设置é¹é
+ case clock(IBClock)
+ /// 设置å¿ç
+ case heartRate(IBHeartRate)
+ /// è¡æ°§
+ case bloodOxygen(IBBloodOxygen)
+ /// 设置å±å¹
+ case screen(IBScreenConfig)
+ /// 设置声é³åéå¨
+ case vibrateSound(IBVibrateConfig)
+ /// è®¾ç½®ç®æ 设置
+ case otherTarget(IBOtherTarget)
+ /// 设置è®ç»ç®æ
+ case trainTarget(IBTrainTarget)
+ /// åéææºä¿¡æ¯
+ case sendPhone(IBPhoneInfo)
+ /// åéå½åæ¶é´
+ case sendTime(IBSendTime)
+ /// 设置è¾
å©åè½
+ case auxiliary(IBAuxiliary)
+ /// è®¾ç½®ç¨æ·æ°æ®
+ case userInfo(IBUserInfo)
+ /// åéå¤©æ°æ°æ®
+ case weather(IBWeather)
+ /// åéé讯å½
+ case addressBook([IBAddressBook])
+ /// åéEPOæä»¶ä¿¡æ¯
+ case epo([IBEpo])
+ /// åéOTAæä»¶ä¿¡æ¯
+ case ota(IBOtaFile)
+ /// åémp3æä»¶ä¿¡æ¯
+ case MP3(IBMp3File)
+ /// åé表çä¿¡æ¯æ±æ»
+ case dial(IBDial)
+ /// åéå个表çæä»¶ä¿¡æ¯
+ case dialFile(IBDialFile)
+ /// å 餿宿件
+ case deleteFile(IBDeleteFile)
+ /// è·åæå®ç±»åæä»¶ç®å½
+ case fetchFils(IBFile)
+ /// åéAiæ¯å¦å¯ç¨åæ§
+ case aiValid(IBAiValid)
+ /// åéAiååºå
容
+ case aiResponse(IBAiResponse)
+
+ var key: IBKey {
+ switch self {
+ case .dialID(_): return .dialID
+ case .action(_): return .action
+ case .motionMode(_): return .motionMode
+ case .language(_): return .language
+ case .value(_): return .value
+ case .file(_): return .file
+ case .targetStep(_): return .targetStep
+ case .sleep(_): return .sleep
+ case .water(_): return .water
+ case .clock(_): return .clock
+ case .heartRate(_): return .heartRate
+ case .bloodOxygen(_): return .bloodOxygen
+ case .screen(_): return .screen
+ case .vibrateSound(_): return .vibrateSound
+ case .auxiliary(_): return .auxiliary
+ case .otherTarget(_): return .otherTarget
+ case .trainTarget(_): return .trainTarget
+ case .sendPhone(_): return .sendPhone
+ case .sendTime(_): return .sendTime
+ case .userInfo(_): return .userInfo
+ case .weather(_): return .weather
+ case .addressBook(_): return .addressBook
+ case .epo(_): return .epo
+ case .ota(_): return .ota
+ case .MP3(_): return .mp3
+ case .dial(_): return .dial
+ case .dialFile(_): return .dialFile
+ case .deleteFile(_): return .deleteFile
+ case .fetchFils(_): return .fetchFils
+ case .aiValid(_): return .aiValid
+ case .aiResponse(_): return .aiResponse
+ }
+ }
+}
+
+public enum IBMotionMode: Int {
+ /// å¥èµ°
+ case walking = 0
+ /// è·æ¥
+ case running
+ /// éªè¡
+ case ride
+}
+
+public enum IBFile: Int,Codable {
+ /// OTA æä»¶
+ case ota = 1
+ /// MP3 æä»¶
+ case mp3
+ /// 表çä¿¡æ¯
+ case dial
+ /// OTA(W4)
+ case ota_w4
+ ///EPO
+ case epo
+}
+//0ï¼è¿å¨
+//1ï¼å¿ç
+//2ï¼è¡æ°§çæµ
+//3ï¼è®ç»
+//4ï¼è®ç»è®°å½
+//5ï¼æåé
+//6ï¼å¤©æ°
+//7ï¼é³ä¹
+//8ï¼çµè¯
+//9ï¼å½é³
+//10ï¼ç§è¡¨
+//11ï¼è®¡æ¶å¨
+//12ï¼é¹é
+//13ï¼èç
+//14ï¼ç¡ç
+//15ï¼è®¾ç½®
+public enum IBAuxiliary: Int {
+ /// è¿å¨
+ case sport = 0
+ /// å¿ç
+ case heart
+ /// è¡æ°§çæµ
+ case bloodOxygen
+ /// è®ç»
+ case train
+ /// è®ç»è®°å½
+ case tarinRecord
+ /// æåé
+ case compass
+ /// 天æ°
+ case weather
+ /// é³ä¹
+ case music
+ /// çµè¯
+ case mobile
+ /// å½é³
+ case record
+ /// ç§è¡¨
+ case secondTimer
+ /// 计æ¶å¨
+ case timer
+ /// é¹é
+ case clock
+ /// èç
+ case bluetooth
+ /// ç¡ç
+ case sleep
+ /// 设置
+ case setting
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBParser.swift b/code/IOTBluetooth/IOTBluetooth/IBParser.swift
new file mode 100644
index 0000000..f77524f
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBParser.swift
@@ -0,0 +1,345 @@
+//
+// IBParser.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/18.
+//
+
+import Foundation
+
+/// èçæ°æ®è§£æåè®®
+protocol IBParser {
+ /// è§£ææ°æ®
+ static func parse(_ data: Data) -> IBResponse
+}
+
+extension IBParser {
+ static func dataPacket(_ data: Data) -> [UInt8] {
+ let dataLength = data[7...10].int
+ return data.suffix(from: 17).prefix(dataLength).map({ $0 })
+ }
+}
+
+struct IBBatteryParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ var bytes = data.bytes
+ bytes.removeLast()
+ return .response.code(.success).value(bytes.last!.int)
+ }
+}
+
+struct IBSwitchDialParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ var bytes = data.bytes
+ bytes.removeLast()
+ let code = bytes.last!.int
+ let desc = ["æä»¤æ ¡éªå¤±è´¥", "忢æå", "æ 该表çèµæº", "åæ¢å¤±è´¥"]
+ return .response.code(code == 1 ? .success:.failure).message(desc[code]).value(code)
+ }
+}
+struct IBFetchCurrentDialParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let bytes = dataPacket(data)
+ let value = bytes.int
+ return .response.code(.success).value(value)
+ }
+}
+struct IBFetchAuxiliaryParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let bytes = dataPacket(data)
+ guard let value = IBAuxiliary(rawValue: bytes.int) else {return .response.code(.parseError) }
+ return .response.code(.success).value(value)
+ }
+}
+
+struct IBCheckDialParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+
+ let code = dataPacket(data).int
+ let desc = ["æä»¤æ ¡éªå¤±è´¥", "å·²å®è£
", "æªå®è£
"]
+ return .response.code(code != 0 ? .success:.failure).message(desc[code]).value(code)
+ }
+}
+
+/// éç¨è§£æ(1 Byte)
+struct IBGeneralParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+// guard data.isEmpty == false else { return IBSingleByteSimulateParser.parse() }
+
+ let code = dataPacket(data).int
+ return .response.code(code == 1 ? .success:.failure)
+ }
+}
+struct IBTakePhotoParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let code = dataPacket(data).int
+ let desc = ["æä»¤æ ¡éªå¤±è´¥", "æä½æå", "è¿è¡æå¿æç
§","éåº"]
+ return .response.code(code != 0 ? .success:.failure).message(desc[code]).value(code)
+ }
+}
+struct IBWaterParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+// guard data.isEmpty == false else { return IBSingleByteSimulateParser.parse() }
+
+ let code = dataPacket(data).int
+ let desc = code == 0 ? "å
³é":"å¼å¯"
+ return .response.code(.success).value(code).message(desc)
+ }
+}
+struct IBFindParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let code = dataPacket(data).int
+ let desc = ["æä»¤æ ¡éªå¤±è´¥", "æä½æå", "æè¡¨å·²æ¾å°ï¼åæ¢æ¥æ¾"]
+ return .response.code(code != 0 ? .success:.failure).message(desc[code]).value(code)
+ }
+}
+struct IBSendPhoneParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let code = dataPacket(data).int
+ let desc = ["æä»¤æ ¡éªå¤±è´¥","æè¡¨æªä¸ä»»ä½ææºç»å®", "æè¡¨å·²ä¸è¯¥ææºç»å®", "æè¡¨å·²ä¸å
¶ä»ææºç»å®"]
+ return .response.code(code != 0 ? .success:.failure).message(desc[code]).value(code)
+ }
+}
+
+
+struct IBWatchStateParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ var bytes = data.bytes
+ bytes.removeLast()
+ let code = bytes.last!.int
+ let desc = [0:"空é²ç¶æ", 9:"USBåå¨ç¶æ", 10:"è¿å¨è®ç»ç¶æ"]
+ return .response.code(.success).value(code).message(desc[code] ?? "")
+ }
+}
+
+
+struct IBDiskParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let code = dataPacket[0].int
+ if code == 1 {
+ var value: Int = 0
+ value = dataPacket.suffix(from: 1).map({ $0 }).int
+ return .response.code(.success).value(value)
+ }
+ else {
+ return .response.code(.failure)
+ }
+ }
+}
+
+struct IBTrainingDataParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ if dataPacket.count == 0 {
+ return .response.code(.success)
+ }
+ guard let model = IBTrainingData(dataPacket.data) else { return .response.code(.parseError) }
+ return .response.code(.success).value(model)
+ }
+}
+struct IBHeartRateParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHeartRate(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+struct IBBloodOxygenParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBBloodOxygen(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+
+struct IBSyncTrainParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ guard let model = JSON.decode(IBHistoryBaseTraining.self, from: dataPacket.data) else { return .response.code(.parseError) }
+ return .response.code(.success).items(model.list)
+
+// let dataPacket = baseData.jsonString()!
+// guard let model = JSON.decode(IBHistoryBaseTraining.self, from: dataPacket.data!) else { return .response.code(.parseError) }
+// Logger.log(model.jsonString ?? "")
+// return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncHeartRateParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryHeartRate(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncBloodOxygenParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryBloodOxygen(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncStepParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryStep(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncBurningParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryBurning(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncStandParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryStand(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncCalorieParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryCalorie(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncDistanceParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryDistance(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncSleepParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistorySleep(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBSyncGpsParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBHistoryGps(dataPacket.data)
+// Logger.log(model.list)
+ return .response.code(.success).items(model.list)
+ }
+}
+struct IBFetchFilesParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ guard let model = IBFetchFile(dataPacket.data) else { return .response.code(.parseError) }
+ Logger.log(model.jsonString ?? "")
+ return .response.code(.success).value(model)
+ }
+}
+
+
+struct IBAblutParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ guard let model = JSON.decode(IBAbout.self, from: dataPacket.data) else { return .response.code(.parseError) }
+ Logger.log(model.jsonString ?? "")
+ return .response.code(.success).value(model)
+ }
+}
+struct IBClockParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ guard let model = JSON.decode(IBClock.self, from: dataPacket.data) else { return .response.code(.parseError) }
+ Logger.log(model.jsonString ?? "")
+ return .response.code(.success).items(model.alarms)
+ }
+}
+struct IBSetClockParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+// guard data.isEmpty == false else { return IBClockSimulateParser.parse() }
+ let code = dataPacket(data).int
+ return .response.code(code == 1 ? .success:.failure)
+ }
+}
+
+struct IBFetchDialsParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let num = dataPacket[0].int
+
+ var list:[Int] = []
+ for i in 0.. IBResponse {
+ let dataPacket = dataPacket(data)
+ guard let model = IBTargetStep(dataPacket.data) else { return .response.code(.parseError) }
+
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+struct IBSleepParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBSleep(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+struct IBScreenParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBScreen(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+struct IBVibrateSoundParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBVibrateSound(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+struct IBOtherParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBVibrateSound(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+struct IBOtherTargetParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data)
+ let model = IBOtherTarget(dataPacket.data)
+ Logger.log(model.description)
+ return .response.code(.success).value(model)
+ }
+}
+
+struct IBfetchAiAudioParser: IBParser {
+ static func parse(_ data: Data) -> IBResponse {
+ let dataPacket = dataPacket(data).data
+ return .response.code(.success).value(dataPacket)
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBReceiveManager.swift b/code/IOTBluetooth/IOTBluetooth/IBReceiveManager.swift
new file mode 100644
index 0000000..e8ad4a0
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBReceiveManager.swift
@@ -0,0 +1,113 @@
+//
+// IBReceiveManager.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/7/15.
+//
+
+import Foundation
+import CoreBluetooth
+
+
+@objc protocol IBReceiveManagerDelegate {
+ func receiveManagerDidReceiveFullData(characteristic: CBCharacteristic,_ data:Data)
+}
+
+class IBReceiveManager{
+ var delegate:IBReceiveManagerDelegate?
+ static let shared = IBReceiveManager()
+
+ var headData = Data()
+ var lastData = Data()
+ var totalData = Data()
+
+
+ func receice(characteristic: CBCharacteristic){
+ guard let data = characteristic.value else { return }
+ if(data.count == 2 && data.int == 0){
+// Logger.log("æ¶å°0000æä»¤å¼å¸¸")
+ return
+ }
+
+ if data.count >= 18,data[0] == 0x72{
+ let mark = characteristic.isNotifying ? "Notify Read" : "Read"
+ let uuid = characteristic.uuid.uuidString
+ let pkgTotal = data[3...6].int
+ let dataTotal = data[7...10].int
+ let pkgIndex = data[11...14].int
+ let pkgLen = data[15...16].int
+
+ Logger.log(mark, uuid, "->", "pkgTotal = \(pkgTotal),dataTotal = \(dataTotal),pkgIndex = \(pkgIndex),pkgLen = \(pkgLen)",data.joined(" "))
+
+// if data[1] == 0x01,data[2] == 0x34{
+// //aiè¯é³
+ if pkgIndex == 1{
+ headData = data.prefix(17)
+ lastData = data.suffix(1)
+ }
+
+ totalData = totalData + data.suffix(from: 17).prefix(pkgLen)
+ if pkgTotal == pkgIndex{
+ Logger.log("total = \(dataTotal),receive = \(totalData.count)")
+ let fullData = headData + totalData + lastData
+ didGetFullData(characteristic: characteristic, data: fullData)
+ }
+// }
+// else{
+// didGetFullData(characteristic: characteristic, data: data)
+// }
+ }
+ else{
+ Logger.log( data.joined(" "))
+ didGetFullData(characteristic: characteristic, data: data)
+ }
+ }
+
+ func didGetFullData(characteristic: CBCharacteristic,data:Data){
+// Logger.log( data.joined(" "))
+
+ delegate?.receiveManagerDidReceiveFullData(characteristic: characteristic, data)
+ totalData = Data()
+
+ if characteristic.uuid.uuidString == readCharacteristicUUID {
+ let info = IBWatchInfo(data)
+ sendDeviceInfo()
+ }
+
+ if IBNotify.handle(data) {
+// Logger.log("éç¥æ¶æ¯")
+ return
+ }
+ }
+
+ func sendDeviceInfo(){
+ let mac = UserDefaults.standard.object(forKey: "IOTBletooth.mac") as? [Int] ?? []
+ let model = IBPhoneInfo()
+ model.mac = mac
+ IBSender()
+ .ability(IBAbility.sendPhone)
+ .parameters([.sendPhone(model)])
+ .response { result in
+ Logger.log(result)
+ if result.code != .success{
+ //éç¥è¿æ¥å¤±è´¥
+
+ return
+ }
+ guard let value = result.value as? Int else {return}
+ if value == 2 {
+ //éç¥è¿æ¥æå
+
+ }
+ else if value == 1{
+ //çå¾
æè¡¨åæè¿æ¥
+ Logger.log("çå¾
æè¡¨åæè¿æ¥")
+ }
+ else if value == 3{
+ //æè¡¨å·²ä¸å
¶ä»ææºç»å®
+
+ }
+ }
+ }
+}
+
diff --git a/code/IOTBluetooth/IOTBluetooth/IBSender.swift b/code/IOTBluetooth/IOTBluetooth/IBSender.swift
new file mode 100644
index 0000000..3a192b7
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBSender.swift
@@ -0,0 +1,140 @@
+//
+// IBSender.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/15.
+//
+
+import Foundation
+
+public typealias IBCompletionHandler = (_ result: IBResponse) -> Void
+
+
+/// èçæ¶æ¯åéç±»
+public class IBSender {
+ public init() {
+ Self.all.append(self)
+ }
+
+ var ability: IBAbility!
+ var parameters: [IBParameter] = []
+ var completion: IBCompletionHandler!
+
+ var dataPacket: [[UInt8]] {
+ var totalData: [UInt8] = []
+ if isValue(ability.checker) {
+ totalData = ability.checker.check(parameters)
+ }
+
+ let list = splitData(totalData)
+ if list.count > 1{
+ var newList = [[UInt8]]()
+ /// åå
æ°
+ let packageCount = list.count
+ /// æ°æ®å
æ»é¿åº¦
+ let dataTotalLength = totalData.count
+ /// æ°æ®å
é¿åº¦
+ let dataLength = list[0].count
+ /// æ°æ®å
ç´¢å¼
+ let index = 1
+
+ var bytes: [UInt8] = [0x73, ability.type.rawValue, ability.id]
+ bytes = bytes + packageCount.byte4 + dataTotalLength.byte4 + index.byte4 + dataLength.byte2
+ bytes = bytes + list[0] + [LRC.calculateLRC(list[0])]
+
+ newList.append(bytes)
+ newList.append(contentsOf: list[1...list.count - 1])
+
+ return newList
+ }
+ else{
+ /// åå
æ°
+ let packageCount = 1
+ /// æ°æ®å
æ»é¿åº¦
+ let dataTotalLength = totalData.count
+ /// æ°æ®å
é¿åº¦
+ let dataLength = totalData.count
+ /// æ°æ®å
ç´¢å¼
+ let index = 1
+
+ var bytes: [UInt8] = [0x73, ability.type.rawValue, ability.id]
+ bytes = bytes + packageCount.byte4 + dataTotalLength.byte4 + index.byte4 + dataLength.byte2
+
+ if totalData.count > 0 {
+ bytes = bytes + totalData
+ bytes = bytes + [LRC.calculateLRC(totalData)]
+ } else {
+ bytes = bytes + [0x00]
+ }
+
+ return [bytes]
+ }
+ }
+
+ private func splitData(_ listData:[UInt8])->[[UInt8]]{
+ let data = listData.data
+ var list = [[UInt8]]()
+ var index = 0
+ let seg = 492
+ while index < data.count {
+ if index < data.count - seg {
+ let sub:[UInt8] = data[index...index + seg - 1].bytes
+ list.append(sub)
+ index += sub.count
+ }
+ else{
+ let sub:[UInt8] = data[index...data.count - 1].bytes
+ index += sub.count
+ list.append(sub)
+ }
+ }
+ return list
+ }
+
+
+ func isAnswer(_ data: Data) -> Bool {
+ return ability.isAnswer(data)
+ }
+
+ func parse(_ data: Data) -> IBResponse {
+ return ability.parser.parse(data)
+ }
+
+ deinit {
+// Logger.log("deinit IBSender")
+ }
+}
+
+public extension IBSender {
+
+ /// åè½æ¥å£
+ @discardableResult
+ func ability(_ value: IBAbility) -> Self {
+ ability = value
+ return self
+ }
+
+ /// åæ°
+ @discardableResult
+ func parameters(_ value: [IBParameter]) -> Self {
+ parameters = value
+ return self
+ }
+
+ /// æ§è¡ä¸ååºå¤ç
+ @discardableResult
+ func response(_ handler: @escaping IBCompletionHandler) -> Self {
+ completion = handler
+// IBBluetooth.shared.send(self)
+ return self
+ }
+}
+
+extension IBSender {
+ static var all: [IBSender] = []
+
+ /// ä»ç¼åå®ä¾éåä¸ç§»é¤è¯¥å®ä¾
+ func remove() {
+ Self.all.removeAll { $0 === self }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/IBSimulateParser.swift b/code/IOTBluetooth/IOTBluetooth/IBSimulateParser.swift
new file mode 100644
index 0000000..4f97eb2
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IBSimulateParser.swift
@@ -0,0 +1,24 @@
+//
+// IBSimulateParser.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/18.
+//
+
+import Foundation
+
+/// éç¨æ¨¡æååº
+//struct IBSingleByteSimulateParser {
+// static func parse() -> IBResponse {
+// return .response
+// }
+//}
+
+///// é¹é模æååº
+//struct IBClockSimulateParser {
+// static func parse() -> IBResponse {
+// let str = #"{"code":1,"alarms":[{"id":0,"repeat":[0],"hour":10,"minute":0,"enable":1},{"id":1,"repeat":[1,2,3,4,5],"hour":7,"minute":45,"enable":1}]}"#
+// guard let clock = JSON.decode(IBClock.self, from: str.data!) else { return .response.message("æ°æ®è§£æå¤±è´¥") }
+// return .response.success(true).items(clock.alarms)
+// }
+//}
diff --git a/code/IOTBluetooth/IOTBluetooth/IOTBluetooth.h b/code/IOTBluetooth/IOTBluetooth/IOTBluetooth.h
new file mode 100644
index 0000000..98258c9
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/IOTBluetooth.h
@@ -0,0 +1,18 @@
+//
+// IOTBluetooth.h
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/6.
+//
+
+#import
+
+//! Project version number for IOTBluetooth.
+FOUNDATION_EXPORT double IOTBluetoothVersionNumber;
+
+//! Project version string for IOTBluetooth.
+FOUNDATION_EXPORT const unsigned char IOTBluetoothVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBAbout.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBAbout.swift
new file mode 100644
index 0000000..2e70140
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBAbout.swift
@@ -0,0 +1,24 @@
+//
+// IBAbout.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// å
³äºæ¬æºæ¨¡å
+public class IBAbout:Codable {
+ /// æ è®°ç ï¼ææ°æ®æ´æ°æ¶ä¸º1ï¼æ æ°æ®æ´æ°æ¶ä¸º0ï¼æä¸ç¨
+ public var code: Int = 0
+ /// åºä»¶ä¸»çæ¬å·
+ public var major: Int = 1
+ /// åºä»¶ä¸çæ¬å·
+ public var mid: Int = 0
+ /// åºä»¶æ¬¡çæ¬å·
+ public var minor: Int = 0
+ /// MACå°å
+ public var macAddress: String = ""
+
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBAddressBook.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBAddressBook.swift
new file mode 100644
index 0000000..c76cad3
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBAddressBook.swift
@@ -0,0 +1,71 @@
+//
+// IBAddressBook.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/3.
+//
+
+import Foundation
+/// éè®¯å½æ¨¡å
+public class IBAddressBook {
+ /// æ§å« 0ï¼æªæå®ï¼1ï¼ç·æ§ ï¼2ï¼å¥³æ§
+ public var gender: Int = 0
+
+ /// å§å,ä¸ä¼ æè¡¨ä¸æ¾ç¤ºååä¸ºâæªç¥â
+ public var name: String?
+
+ /// çµè¯å·ç ï¼ä¸ä¸ªè系人åä¸å¯ææå°1个æå¤2个çµè¯å·ç
+ public var phoneNumbers: [IBPhoneNumber] = []
+
+
+ public init() { }
+
+// public static func test() -> [IBAddressBook]{
+// var list = [IBAddressBook]();
+// for i in 0...10{
+// let model = IBAddressBook()
+// model.gender = i%2
+// model.name = "å¼ \(i)"
+//
+// for j in 0...1{
+// let phone = IBPhoneNumber()
+// phone.number = "1300000000\(j)"
+// phone.location = "æ·±å³å¸å®å®åº\(j)"
+// phone.operator = "ä¸å½ç§»å¨"
+// model.phoneNumbers.append(phone)
+// }
+// list.append(model)
+// }
+//
+// return list
+// }
+
+ public var description: String {
+ var phoneStr = "[ "
+ for phone in phoneNumbers {
+ phoneStr += phone.description
+ }
+ phoneStr += " ]"
+ return "IBAddressBook(gender: \(gender), name:\(name ?? ""), phoneNumbers:\(phoneStr))"
+ }
+}
+
+/// çµè¯å·ç 模å
+public class IBPhoneNumber {
+ /// çµè¯å·ç ï¼é¿åº¦1~255
+ public var number: String = ""
+
+ /// å½å±å°ï¼é¿åº¦0~255ï¼ä¸ä¼ æè¡¨ä¸æ¾ç¤ºå½å±å°ä¸ºâæªç¥
+ public var location: String?
+
+ /// è¿è¥åï¼é¿åº¦0~255ï¼ä¸ä¼ æè¡¨ä¸æ¾ç¤ºè¿è¥åä¸ºâæªç¥
+ public var `operator`: String?
+
+ public init() { }
+
+ public var description: String {
+ return "IBPhoneNumber(number: \(number), location:\(location ?? ""), `operator`:\(`operator` ?? ""))"
+ }
+}
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBAiResponse.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBAiResponse.swift
new file mode 100644
index 0000000..63afd5a
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBAiResponse.swift
@@ -0,0 +1,21 @@
+//
+// IBAiResponse.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/27.
+//
+
+import Foundation
+/// Aiååºæ¨¡å
+public class IBAiResponse {
+ /// ç±»åï¼0ï¼AIååºé误 1ï¼AIå½é³è§£æå
容 2ï¼AIååºå
容ï¼ç±»å为0æ¶ï¼åªåç±»åå³å¯ï¼åé¢åèåä¸åï¼
+ var type: Int = 0
+ /// å
容
+ public var value: String = ""
+
+ public init() { }
+
+ public var description: String {
+ return "IBAiResponse(type: \(type), value:\(value))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBAiValid.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBAiValid.swift
new file mode 100644
index 0000000..d5ccf80
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBAiValid.swift
@@ -0,0 +1,21 @@
+//
+// IBAiValid.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/8/2.
+//
+
+import Foundation
+/// Aiæ¯å¦å¯ç¨æ¨¡å
+public class IBAiValid {
+ /// 0ï¼ä¸å¯ç¨ï¼åé¢ä¸å¸¦keyï¼1ï¼å¯ç¨
+ public var isValid: Bool = true
+ /// key ï¼ChatGPTåé¢å¸¦keyï¼å
¶ä»ä¸å¸¦ï¼
+ public var key: String?
+
+ public init() { }
+
+ public var description: String {
+ return "IBAiValid(isValid: \(isValid), key:\(key ?? ""))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBBloodOxygen.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBBloodOxygen.swift
new file mode 100644
index 0000000..99f6639
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBBloodOxygen.swift
@@ -0,0 +1,42 @@
+//
+// IBBloodOxygen.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/29.
+//
+
+import Foundation
+/// è¡æ°§è®¾ç½®æ¨¡å
+public class IBBloodOxygen {
+ /// çæµé¢ç
+ public var rate: IBBloodOxygenFrequency = .min30
+ /// è¡æ°§æé 0ï¼å
³é 1ï¼å¼å¯ã 为0æ¶åé¢åºé´é弿 é设置ã
+ public var state: Int = 1
+ /// æééå¼ 90~99
+ public var value: Int = 95
+
+ public init() { }
+
+ init(_ data: Data) {
+ rate = IBBloodOxygenFrequency(rawValue:data[0].int) ?? .none
+ state = data[1].int
+ if state == 1 {
+ value = data[2].int
+ }
+ }
+
+ var description: String {
+ return "IBBloodOxygen(rate: \(rate), state:\(state), value:\(value))"
+ }
+}
+/// è¡æ°§çæµé¢ç
+public enum IBBloodOxygenFrequency: Int {
+ /// å
³é
+ case none = 0
+ /// 30åé
+ case min30
+ /// 1å°æ¶
+ case hour1
+ /// 2å°æ¶
+ case hour2
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBClock.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBClock.swift
new file mode 100644
index 0000000..600d82b
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBClock.swift
@@ -0,0 +1,56 @@
+//
+// IBClock.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/21.
+//
+
+import Foundation
+
+/// é¹é模å
+public class IBClock: Codable {
+ /// é¹éå表
+ public var alarms: [IBClockInfo] = []
+
+ public init() {
+// for i in 0...4{
+// let model = IBClockInfo()
+// model.id = i
+// model.hour = i*3
+// model.minute = i*10
+// model.enable = i%2
+// model.repeat = [1,2,3,4,5,6,7]
+// alarms.append(model)
+// }
+ }
+}
+
+/// é¹éä¿¡æ¯æ¨¡å
+public class IBClockInfo: Codable {
+ /// é¹éid èå´ä¸º0~4. ï¼å³é¹éæå®æå¤è®¾ç½®ä¸º5个ï¼
+ public var id: Int = 0
+ /// å°æ¶
+ public var hour: Int = 0
+ /// åé
+ public var minute: Int = 0
+ /// 0å
³éé¹éï¼1å¼å¯é¹é
+ public var enable: Int = 0
+ /// é¹ééå¤å表
+ ///
+ /// - 0 ä¸éå¤ï¼å䏿¬¡
+ /// - 1 å¨ä¸
+ /// - 2 å¨äº
+ /// - ......
+ /// - 7 卿¥
+ public var `repeat`: [Int] = [0]
+
+// enum CodingKeys: String, CodingKey {
+// case id = "id"
+// case hour = "hour"
+// case minute = "minute"
+// case enable = "enable"
+// case `repeat` = "repeat"
+// }
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBCode.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBCode.swift
new file mode 100644
index 0000000..3711401
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBCode.swift
@@ -0,0 +1,45 @@
+//
+// IBError.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/9.
+//
+
+import Foundation
+
+/// ç¶æç
+public enum IBCode: Int,CustomStringConvertible {
+ public var description: String{
+ return "IBError(rawValue:\(rawValue), message:\(message))"
+ }
+ /// æä½æå
+ case success = 1000
+ /// æä½å¤±è´¥
+ case failure = 1001
+ /// åæ°é误
+ case invalidParam = 1002
+ /// è§£æé误
+ case parseError = 1003
+ /// 请æ±è¶
æ¶
+ case timeout = 1004
+ /// æªç¥é误
+ case unknow = 1005
+
+ var message:String{
+ switch self {
+ case .failure:
+ return "æä½å¤±è´¥"
+ case .success:
+ return "æä½æå"
+ case .invalidParam:
+ return "åæ°é误"
+ case .parseError:
+ return "è§£æé误"
+ case .timeout:
+ return "请æ±è¶
æ¶"
+ default:
+ return "æªç¥é误"
+ }
+ }
+
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBDeleteFile.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBDeleteFile.swift
new file mode 100644
index 0000000..a46fd18
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBDeleteFile.swift
@@ -0,0 +1,22 @@
+//
+// IBDeleteFile.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/6.
+//
+
+import Foundation
+
+/// å é¤æä»¶æ¨¡å
+public class IBDeleteFile:Codable {
+ /// æä»¶ç±»åï¼2ï¼MP3 3ï¼è¡¨ç
+ public var fileType: IBFile = .mp3
+ /// æä»¶UIDï¼æä»¶å¯ä¸ç¼å·ã䏿¬¡æå¤5个
+ public var uids: [Int] = []
+
+ public init() { }
+
+ public var description: String {
+ return "IBDeleteFile(fileType: \(fileType), uids:\(uids))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBDial.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBDial.swift
new file mode 100644
index 0000000..6d8060d
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBDial.swift
@@ -0,0 +1,104 @@
+//
+// IBDail.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/6.
+//
+
+import Foundation
+/// 表çæä»¶æ±æ»ä¿¡æ¯æ¨¡åï¼èªå®ä¹ï¼
+//internal class IBCustomDial {
+// /// UIDï¼0~4ï¼èªå®ä¹è¡¨çUIDï¼ä¸å¯åæ´ï¼
+// var UID: Int = 0
+// /// çµæ± 徿 æ ·å¼, 0ï¼æ 徿 1ï¼é»è²å¾æ 2ï¼ç½è²å¾æ
+// var batteryStyle: Int = 0
+// /// æ¶é´æ ·å¼ 0ï¼æ¶é´å¨ä¸æ¹ 1ï¼æ¶é´å¨ä¸æ¹
+// var timeStyle: Int = 0
+// /// æ¶é´åä½é¢è² 0ï¼äº®ç½ 1ï¼é» 2ï¼æç½ 3ï¼ç»¿ 4ï¼æ© 5ï¼è
+// var timeColor: Int = 0
+// /// 表çæä»¶
+// var files: [IBDialFile]?
+//
+// init() { }
+//
+// var description: String {
+// return "IBCustomDail(UID: \(UID), batteryStyle:\(batteryStyle), timeStyle:\(timeStyle), timeColor:\(timeColor))"
+// }
+//}
+//
+///// 表çæä»¶æ±æ»ä¿¡æ¯æ¨¡åï¼è¡¨çå¸åºï¼
+//internal class IBMarketDial {
+// /// UIDï¼0x05~0xffffï¼è¡¨çå¸åºUID
+// var UID: Int = 5
+// /// 主é¢åç§°ï¼åç§°é¿åº¦,0~255
+// var theme: String?
+// /// 主é¢åç§°é¿åº¦
+// internal var themeLength: Int = 0
+// /// 表çæä»¶
+// var files: [IBDialFile]?
+//
+//
+// init() { }
+//
+// var description: String {
+// return "IBMarketDail(UID: \(UID), themeLength:\(themeLength), theme:\(theme ?? ""))"
+// }
+//}
+
+/// 表çæä»¶æ±æ»ä¿¡æ¯æ¨¡åï¼ç®ååªæ¯æè¡¨çå¸åºï¼
+public class IBDial {
+ /// èªå®ä¹è¡¨çï¼0~4
+ /// 表çå¸åº ï¼ 0x05~0xffff
+ public var UID: Int = 5
+
+ /// 主é¢åç§°ï¼åç§°é¿åº¦,0~255 ï¼ä»
表çå¸åºæè¯¥é¡¹ï¼
+ public var theme: String?
+ /// 主é¢åç§°é¿åº¦
+ internal var themeLength: Int = 0
+
+ /// çµæ± 徿 æ ·å¼, 0ï¼æ 徿 1ï¼é»è²å¾æ 2ï¼ç½è²å¾æ ï¼ ä»
å®ä¹è¡¨çæè¯¥é¡¹ï¼
+ public var batteryStyle: Int?
+ /// æ¶é´æ ·å¼ 0ï¼æ¶é´å¨ä¸æ¹ 1ï¼æ¶é´å¨ä¸æ¹ ï¼ ä»
èªå®ä¹è¡¨çæè¯¥é¡¹ï¼
+ public var timeStyle: Int?
+ /// æ¶é´åä½é¢è² 0ï¼äº®ç½ 1ï¼é» 2ï¼æç½ 3ï¼ç»¿ 4ï¼æ© 5ï¼è ï¼ ä»
èªå®ä¹è¡¨çæè¯¥é¡¹ï¼
+ public var timeColor: Int?
+
+ /// 表çæä»¶
+ public var files: [IBDialFile]?
+
+
+ public init() { }
+
+ public var description: String {
+ return "IBMarketDail(UID: \(UID), themeLength:\(themeLength), theme:\(theme ?? ""))"
+ }
+}
+
+/// å个表çæä»¶ä¿¡æ¯æ¨¡å
+public class IBDialFile {
+ /// æä»¶è·¯å¾
+ public var filePath: String = ""
+ /// æä»¶åç§°
+ public var fileName: String = ""
+
+ /// UIDï¼0x05~0xffffï¼è¡¨çå¸åºUID
+ internal var UID: Int = 0
+ /// æä»¶ç¼å·ï¼1~255ï¼å³è¯¥ä¸»é¢è¡¨çä¸ç第å 个æä»¶ï¼æå¤§ä¸ºè¡¨çæä»¶æ±æ»ä¿¡æ¯ä¸çæä»¶æ°ç®
+ internal var index: Int = 0
+ /// æä»¶ç±»åï¼0ï¼è¡¨çjSONæä»¶ï¼å缩åç»ä¸å½å为local_page.lzmaï¼1ï¼å¾çæä»¶ï¼é¤ç¼©ç¥å¾å¤çå
¶ä»å¾çï¼ï¼èæ¯å¾å缩åç»ä¸å½å为main_bg.lzmaï¼2ï¼ç¼©ç¥å¾ï¼ï¼å缩åç»ä¸å½å为thumbnail.lzmaï¼
+ internal var fileType: Int = 1
+ /// æä»¶åç§°é¿åº¦ï¼6~255ï¼ä¸è½ä¸º0ï¼æä»¶åç¼ç»ä¸ä¸º.lzma
+ internal var nameLength: Int{
+ return fileName.count
+ }
+ /// æä»¶å¤§å°ï¼0x01~0xffffffff
+ internal var size: Int = 1
+
+
+ public init() { }
+
+
+ public var description: String {
+ return "IBDialFile(UID: \(UID), index:\(index),fileType: \(fileType), nameLength:\(nameLength), filePath:\(filePath), fileName:\(fileName), size:\(size))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBEpo.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBEpo.swift
new file mode 100644
index 0000000..a5a2340
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBEpo.swift
@@ -0,0 +1,27 @@
+//
+// IBEpo.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/7.
+//
+
+import Foundation
+/// IBEpoä¿¡æ¯æ¨¡å
+public class IBEpo {
+ /// æä»¶ç¼å·ï¼1~255
+ public var UID: Int = 5
+ /// æä»¶åç§°é¿åº¦ï¼6~255ï¼ä¸è½ä¸º0ï¼æä»¶åç¼ç»ä¸ä¸º.lzma
+ public var nameLength: Int = 6
+ /// æä»¶åç§°ï¼åç§°åèæ°ä¸ºåé¢åç§°é¿åº¦æå®ï¼UTF-8ç¼ç
+ public var fileName: String = ""
+ /// æä»¶è·¯å¾
+ public var filePath: String = ""
+ /// æä»¶å¤§å°ï¼åä½byte
+ var fileSize: Int = 0
+
+ public init() { }
+
+ public var description: String {
+ return "IBEpo(UID: \(UID), nameLength:\(nameLength),fileName: \(fileName),filePath: \(filePath),fileSize: \(fileSize))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBFetchFile.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBFetchFile.swift
new file mode 100644
index 0000000..bd7fa42
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBFetchFile.swift
@@ -0,0 +1,80 @@
+//
+// IBFetchFile.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/8.
+//
+
+import Foundation
+/// è·åæå®ç±»åæä»¶ç®å½æ¨¡å
+public class IBFetchFile:Codable {
+ /// ç±»å 2ï¼MP3ï¼ 3ï¼è¡¨ç
+ var type:IBFile = .mp3
+ /// 表ç
+ var dialList:[IBFetchDialItem]?
+ /// mp3
+ var mp3List:[IBFetchMp3Item]?
+
+ init?(_ data: Data) {
+ guard let dict = data.jsonObject() as? [String: Any] else { return nil }
+ guard let type = dict["type"] as? Int,let fileType = IBFile(rawValue: type) else { return nil }
+ self.type = fileType
+
+ if let listDict = dict["list"] as? [[String: Any]] {
+ if self.type == .mp3 {
+ var list = [IBFetchMp3Item]()
+ for item in listDict {
+ let model = IBFetchMp3Item()
+ model.uid = item["uid"] as? Int ?? 0
+ model.name = item["name"] as? String ?? ""
+ model.size = item["size"] as? Int ?? 0
+ model.artist = item["artist"] as? String ?? ""
+ model.duration = item["duration"] as? Int ?? 0
+ list.append(model)
+ }
+ mp3List = list
+ }
+ else if self.type == .dial{
+ var list = [IBFetchDialItem]()
+ for item in listDict {
+ let model = IBFetchDialItem()
+ model.uid = item["uid"] as? Int ?? 0
+ model.name = item["name"] as? String ?? ""
+ model.size = item["size"] as? Int ?? 0
+ list.append(model)
+ }
+ dialList = list
+ }
+ }
+ }
+
+ public init() { }
+}
+
+/// è·åæå®ç±»åæä»¶è¡¨ç模å
+public class IBFetchDialItem:Codable {
+ /// 表çå¨æè¡¨ä¸å¯ä¸ç¼å·
+ var uid:Int = 0
+ /// 表çåå
+ var name:String = ""
+ /// æä»¶å¤§å°ï¼åä½byte
+ var size:Int = 0
+
+ public init() { }
+}
+
+/// è·åæå®ç±»åæä»¶MP3模å
+public class IBFetchMp3Item:Codable {
+ /// ææ²å¨æè¡¨ä¸å¯ä¸ç¼å·
+ var uid:Int = 0
+ /// ææ²åå
+ var name:String = ""
+ /// æä»¶å¤§å°ï¼åä½byte
+ var size:Int = 0
+ /// ä½è
+ var artist:String = ""
+ /// ææ²æ¶é¿ï¼å使¯«ç§
+ var duration:Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHeartRate.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHeartRate.swift
new file mode 100644
index 0000000..eee15b4
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHeartRate.swift
@@ -0,0 +1,43 @@
+//
+// IBHeartRate.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/16.
+//
+
+import Foundation
+/// å¿ç设置模å
+public class IBHeartRate {
+ /// çæµé¢ç
+ public var rate: IBHeartRateFrequency = .min1
+ /// å¿çæé 0ï¼å
³é 1ï¼å¼å¯ã 为0æ¶åé¢åºé´é弿 é设置ã
+ public var state: Int = 1
+ /// å¿çåºé´éå¼ é«äºæä½äº 0ï¼ä½äº 1ï¼é«äº
+ public var higherOrLower: Int = 0
+ /// å¿çåºé´éå¼ 30~200
+ public var value: Int = 100
+
+ public init() { }
+
+ init(_ data: Data) {
+ rate = IBHeartRateFrequency(rawValue:data[0].int) ?? .none
+ state = data[1].int
+ higherOrLower = data[2].int
+ value = data[3].int
+ }
+
+ var description: String {
+ return "IBHeartRate(rate: \(rate), state:\(state), higherOrLower:\(higherOrLower), value:\(value))"
+ }
+}
+/// æéé¢ç
+public enum IBHeartRateFrequency: Int {
+ /// å
³é
+ case none = 0
+ /// 1åé
+ case min1
+ /// 5åé
+ case min5
+ /// 10åé
+ case min10
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBaseTraining.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBaseTraining.swift
new file mode 100644
index 0000000..b93cb22
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBaseTraining.swift
@@ -0,0 +1,113 @@
+//
+// IBHistoryBaseTraining.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/21.
+//
+
+import Foundation
+
+/// åºç¡è®ç»å岿°æ®æ¨¡å
+class IBHistoryBaseTraining:Codable {
+ /// è®ç»åå²è®°å½
+ var list:[IBHistoryBaseTrainingItem] = []
+
+ enum CodingKeys: String, CodingKey {
+ case list = "recs"
+ }
+}
+
+/// åºç¡è®ç»å岿°æ®æ¨¡å
+public class IBHistoryBaseTrainingItem:Codable {
+ /// 0:æè¡¨å¼å¯çè®ç»1:APPå¼å¯çè®ç»
+ public var source: Int = 0
+
+ /// ï¼ä»
W4ï¼0ï¼å¥èº«ç±»1ï¼èè¹ç±»2ï¼ä¼é²è¿å¨3ï¼çç±»4ï¼æ°´ä¸é¡¹ç®5ï¼å°ä¸é¡¹ç®6ï¼æéè¿å¨7ï¼å
¶ä»
+ public var otherType: Int?
+
+ /// è®ç»ç±»åï¼0ï¼å¥èµ°1ï¼è·æ¥2ï¼éªè¡ä»¥ä¸éç¨W2/W3ï¼W4è¯¥åæ®µè¡¨ç¤ºåè®ç»å¤§ç±»ä¸çå°ç±»ï¼å¯¹åºè®ç»åç§°åè èªç±è®ç»176.xlsx è¡¨æ ¼ã
+ public var trainingType: Int = 0
+
+ /// è®ç»å¼å§æ¶é´æ³ï¼åä½ç§
+ public var star: Int = 0
+
+ /// è®ç»ç»ææ¶é´æ³ï¼åä½ç§
+ public var stop: Int = 0
+
+ /// è®ç»ç®æ ç±»åï¼0ï¼è·¯ç¨ 1ï¼å¡è·¯é 2ï¼æ¶é´ 3ï¼æ¥æ°
+ public var targetType: Int = 0
+
+ /// è®ç»ç®æ è·¯ç¨ï¼åä½ç±³ï¼ å¡è·¯éï¼åä½kcal ï¼æ¶é´ï¼åä½åé ï¼æ¥æ°ï¼å使¥
+ public var target: Int = 0
+
+ /// ç®æ å®æåº¦ï¼åä½%
+ public var progress: Int = 0
+
+ /// è®ç»æ»æ¶é´ï¼åä½ç§
+ public var duration: Int = 0
+
+ /// å¡è·¯éï¼ä¸ºè¯¥æ°å¼ä¹ä»¥0.1ï¼åä½kcal
+ public var calorie: Int?
+
+ /// è·ç¦»ï¼åä½ç±³
+ public var distance: Int?
+
+ /// å¹³åè¿å¨å¿ç
+ public var heartRate: Int = 0
+
+ /// è¿å¨æ¥æ°
+ public var step: Int?
+
+ /// è®ç»å¼ºåº¦ï¼ä»
W4), 0 = è¾ä½å¼ºåº¦ï¼1 = ä¸ç强度ï¼2 = é«ç强度ï¼3 = è¶
é«å¼ºåº¦
+ public var trainingLevel:Int?
+
+ /// åæ°ï¼ä»
éè·æ¥è®ç»ï¼
+ public var circle: Int?
+
+ /// æé«æµ·æé«åº¦ï¼ä»
éç»å±±è®ç»ï¼
+ public var elevation: Int?
+
+
+ enum CodingKeys: String, CodingKey {
+ case source = "src"
+ case otherType = "bt"
+ case trainingType = "ty"
+ case star = "st"
+ case stop = "sp"
+ case targetType = "tgt"
+ case target = "tg"
+ case progress = "pg"
+ case duration = "du"
+ case calorie = "ca"
+ case distance = "dl"
+ case heartRate = "hr"
+ case step = "stp"
+ case trainingLevel = "tl"
+ case circle = "cir"
+ case elevation = "alt"
+ }
+}
+
+///// è®ç»ç®æ ç±»å
+//public enum IBTrainingTargetType: Int,Codable {
+// /// è·¯ç¨
+// case distance = 0
+// /// å¡è·¯é
+// case calorie
+// /// æ¶é´
+// case time
+// /// æ¥æ°
+// case step
+//}
+
+///// è®ç»å¼ºåº¦
+//public enum IBTrainingLevel: Int,Codable {
+// /// è¾ä½å¼ºåº¦
+// case low = 0
+// /// ä¸ç强度
+// case medium
+// /// é«ç强度
+// case high
+// /// è¶
é«å¼ºåº¦
+// case superHigh
+//}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBloodOxygen.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBloodOxygen.swift
new file mode 100644
index 0000000..e922be8
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBloodOxygen.swift
@@ -0,0 +1,36 @@
+//
+// IBHistoryBloodOxygen.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// è¡æ°§å岿¨¡å
+class IBHistoryBloodOxygen {
+ /// è®°å½
+ var list:[IBHistoryBloodOxygenItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryBloodOxygenItem] = []
+ for i in stride(from: 0, to: data.count, by: 5){
+ if data.count > i + 4 {
+ let model = IBHistoryBloodOxygenItem()
+ model.time = data[i...i+3].int
+ model.value = data[i+4].int
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// è¡æ°§åå²è®°å½
+public class IBHistoryBloodOxygenItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+ /// è¡æ°§ï¼åä½%
+ public var value: Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBurning.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBurning.swift
new file mode 100644
index 0000000..33f1466
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryBurning.swift
@@ -0,0 +1,36 @@
+//
+// IBHistoryBurning.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// çèå岿¨¡å
+ class IBHistoryBurning {
+ /// è®°å½
+ var list:[IBHistoryBurningItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryBurningItem] = []
+ for i in stride(from: 0, to: data.count, by: 6){
+ if data.count > i + 5 {
+ let model = IBHistoryBurningItem()
+ model.time = data[i...i+3].int
+ model.value = data[i+4...i+5].int
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// çèåå²è®°å½
+public class IBHistoryBurningItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+ /// åä½åé
+ public var value: Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryCalorie.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryCalorie.swift
new file mode 100644
index 0000000..c5f7ed8
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryCalorie.swift
@@ -0,0 +1,36 @@
+//
+// IBHistoryCalorie.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// å¡è·¯éå岿¨¡å
+ class IBHistoryCalorie {
+ /// è®°å½
+ var list:[IBHistoryCalorieItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryCalorieItem] = []
+ for i in stride(from: 0, to: data.count, by: 8){
+ if data.count > i + 7 {
+ let model = IBHistoryCalorieItem()
+ model.time = data[i...i+3].int
+ model.value = data[i+4...i+7].float
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// å¡è·¯éåå²è®°å½
+public class IBHistoryCalorieItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+ /// åä½kcal
+ public var value: Float = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryDistance.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryDistance.swift
new file mode 100644
index 0000000..ef8d97d
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryDistance.swift
@@ -0,0 +1,36 @@
+//
+// IBHistoryDistance.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// å¡è·¯éå岿¨¡å
+ class IBHistoryDistance {
+ /// åå²è®°å½
+ var list:[IBHistoryDistanceItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryDistanceItem] = []
+ for i in stride(from: 0, to: data.count, by: 8){
+ if data.count > i + 7 {
+ let model = IBHistoryDistanceItem()
+ model.time = data[i...i+3].int
+ model.value = data[i+4...i+7].float
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// è·ç¦»åå²è®°å½
+public class IBHistoryDistanceItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+ /// åä½km
+ public var value: Float = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryGps.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryGps.swift
new file mode 100644
index 0000000..78eb6fc
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryGps.swift
@@ -0,0 +1,40 @@
+//
+// IBHistoryGps.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/30.
+//
+
+import Foundation
+
+/// åæ¥è·¯å¾åæ å岿°æ®æ¨¡å
+public class IBHistoryGps {
+ /// è®°å½
+ var list:[IBHistoryGpsItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryGpsItem] = []
+ for i in stride(from: 0, to: data.count, by: 12){
+ if data.count > i + 11 {
+ let model = IBHistoryGpsItem()
+ model.time = data[i...i+3].int
+ model.lon = data[i+4...i+7].int
+ model.lat = data[i+8...i+11].int
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+/// åæ¥è·¯å¾åæ å岿°æ®æ¨¡å
+public class IBHistoryGpsItem {
+ /// unixæ¶é´æ³
+ public var time: Int = 0
+ /// 纬度ï¼è¯¥å¼é¤ä»¥10000000å¾å°å®é
纬度å¼
+ public var lat: Int = 0
+ /// ç»åº¦ï¼è¯¥å¼é¤ä»¥10000000å¾å°å®é
ç»åº¦å¼
+ public var lon: Int = 0
+
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryHeartRate.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryHeartRate.swift
new file mode 100644
index 0000000..ad3248f
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryHeartRate.swift
@@ -0,0 +1,36 @@
+//
+// IBHistoryHeartRate.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/21.
+//
+
+import Foundation
+/// å¿çå岿¨¡å
+ class IBHistoryHeartRate {
+ /// è®°å½
+ var list:[IBHistoryHeartRateItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryHeartRateItem] = []
+ for i in stride(from: 0, to: data.count, by: 5){
+ if data.count > i + 4 {
+ let model = IBHistoryHeartRateItem()
+ model.time = data[i...i+3].int
+ model.value = data[i+4].int
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// å¿çåå²è®°å½
+public class IBHistoryHeartRateItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+ /// å¿ç
+ public var value: Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistorySleep.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistorySleep.swift
new file mode 100644
index 0000000..ef75792
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistorySleep.swift
@@ -0,0 +1,113 @@
+//
+// IBHistorySleep.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/30.
+//
+
+import Foundation
+/// ç¡ç å岿¨¡å
+ class IBHistorySleep {
+ /// è®°å½
+ var list:[IBHistorySleepItem] = []
+
+ init(_ data: Data) {
+ if(data.count <= 0){
+ return
+ }
+ var historyList:[IBHistorySleepItem] = []
+ let times = data[0].int
+ let contentData:Data = data.suffix(from: 1).map({ $0 }).data
+ var index = 0
+ for _ in 0.. index + 1 {
+ let len = contentData[index...index+1].int
+ if contentData.count > index + len {
+ let itemData = contentData[index+2 ... index+1+len].map({ $0 }).data
+ let item = IBHistorySleepItem()
+ item.sleepId = itemData[0].int
+ item.type = itemData[1].int
+ item.startTime = itemData[2...5].int
+ item.endTime = itemData[6...9].int
+ item.totalSleep = IBSleepTime(hour: itemData[10].int, minute: itemData[11].int)
+ if(item.type == 1){
+ item.deepSleep = IBSleepTime(hour: itemData[12].int, minute: itemData[13].int)
+ item.lightSleep = IBSleepTime(hour: itemData[14].int, minute: itemData[15].int)
+ item.remSleep = IBSleepTime(hour: itemData[16].int, minute: itemData[17].int)
+ item.awake = IBSleepTime(hour: itemData[18].int, minute: itemData[19].int)
+
+ var subIndex = 20
+ var list:[IBSleepState] = []
+ while itemData.count >= subIndex + 5{
+ let state = IBSleepState(time: itemData[subIndex...subIndex+3].int, state: itemData[subIndex+4].int)
+ list.append(state)
+ subIndex = subIndex + 5
+ }
+ item.states = list
+ }
+ historyList.append(item)
+ }
+ index += 2 + len
+ }
+ }
+ list = historyList
+ }
+}
+
+/// ç¡ç åå²è®°å½
+public class IBHistorySleepItem {
+ /// ç¡ç IDï¼0~255ï¼è¡¨ç¤ºç¬¬å 次ç¡ç
+ public var sleepId: Int = 0
+ /// ç¡ç ç±»åï¼0ï¼å°ç¡ï¼æ æ»æ¶é¿ä¹åçææé¡¹ç®ï¼1ï¼ç¡ç
+ public var type: Int = 0
+ /// å
¥ç¡æ¶é´æ³ï¼Unixæ¶é´æ³
+ public var startTime: Int = 0
+ /// åºç¡æ¶é´æ³ï¼Unixæ¶é´æ³
+ public var endTime: Int = 0
+ /// æ»æ¶é¿
+ public var totalSleep: IBSleepTime = IBSleepTime(hour: 0, minute: 0)
+ /// æ·±ç¡æ¶é¿
+ public var deepSleep: IBSleepTime?
+ /// æµ
ç¡æ¶é¿
+ public var lightSleep: IBSleepTime?
+ /// å¿«éå¨ç¼æ¶é¿
+ public var remSleep: IBSleepTime?
+ /// æ¸
éæ¶é¿
+ public var awake: IBSleepTime?
+ /// ç¡ç ç¶æ
+ public var states:[IBSleepState]?
+
+
+ public init() { }
+}
+
+/// æ¶é´
+public struct IBSleepTime {
+ /// æ¶
+ public var hour: Int
+ /// å 0~59
+ public var minute: Int
+
+ public init(hour: Int, minute: Int) {
+ self.hour = hour
+ self.minute = minute
+ }
+
+ var description: String {
+ return "IBSleepTime(hour: \(hour), minute:\(minute)"
+ }
+}
+
+
+/// ç¡ç ç¶æ
+public struct IBSleepState {
+ /// Unixæ¶é´æ³
+ public var time: Int = 0
+ /// ç¶æï¼0ï¼è¿å
¥ç¡ç 1ï¼æµ
å±ç¡ç 2ï¼æ·±å±ç¡ç 3ï¼æ¸
é 4ï¼ç»æç¡ç 7ï¼æªåºç¡æåä¸ç¬ç¡ç æ°æ®ï¼èµæä¸æ¥ç»ææ å¿ï¼ 9ï¼åºç¡æåä¸ç¬ç¡ç æ°æ®ï¼èµæä¸æ¥ç»ææ å¿ï¼ 12ï¼å¿«éç¼å¨ 13ï¼åæ¶å
¥ç¡éç¥ï¼ç¡ç ä¸è¶³ 60 åéåºç¡ä¸æ¥ç¶æï¼ 14ï¼ç»æå°ç¡
+ public var state: Int = 0
+
+ public init(time: Int, state: Int) {
+ self.time = time
+ self.state = state
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryStand.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryStand.swift
new file mode 100644
index 0000000..918a8f8
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryStand.swift
@@ -0,0 +1,33 @@
+//
+// IBHistoryStand.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// ç«ç«å岿¨¡å
+ class IBHistoryStand {
+ /// è®°å½
+ var list:[IBHistoryStandItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryStandItem] = []
+ for i in stride(from: 0, to: data.count, by: 4){
+ if data.count > i + 3 {
+ let model = IBHistoryStandItem()
+ model.time = data[i...i+3].int
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// ç«ç«åå²è®°å½
+public class IBHistoryStandItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryStep.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryStep.swift
new file mode 100644
index 0000000..538bc4b
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBHistoryStep.swift
@@ -0,0 +1,36 @@
+//
+// IBHistoryStep.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// æ¥æ°å岿¨¡å
+ class IBHistoryStep {
+ /// è®°å½
+ var list:[IBHistoryStepItem] = []
+
+ init(_ data: Data) {
+ var historyList:[IBHistoryStepItem] = []
+ for i in stride(from: 0, to: data.count, by: 8){
+ if data.count > i + 7 {
+ let model = IBHistoryStepItem()
+ model.time = data[i...i+3].int
+ model.value = data[i+4...i+7].int
+ historyList.append(model)
+ }
+ }
+ list = historyList
+ }
+}
+
+/// æ¥æ°åå²è®°å½
+public class IBHistoryStepItem {
+ /// æ¶é´æ³
+ public var time: Int = 0
+ /// å使¥
+ public var value: Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBLanguage.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBLanguage.swift
new file mode 100644
index 0000000..722f730
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBLanguage.swift
@@ -0,0 +1,58 @@
+//
+// IBLanguage.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/20.
+//
+
+import Foundation
+
+/// è¯è¨åå§å¼ä» 0 å¼å§éå¢
+public enum IBLanguage: Int {
+ /// 䏿
+ case chinese = 0
+ /// è±è¯
+ case english
+ /// æ¥æ¬è¯
+ case japanese
+ /// é©è¯
+ case korean
+ /// 西ççè¯
+ case spanish
+ /// ä¿è¯
+ case russian
+ /// å
ç½å°äºè¯
+ case croatian
+ /// å¾·è¯
+ case german
+ /// è¡èçè¯
+ case portuguese
+ /// æ³è¯
+ case french
+ /// è·å
°è¯
+ case dutch
+ /// æå¤§å©è¯
+ case italian
+ /// æ·å
è¯
+ case czech
+ /// å¸è
è¯
+ case greek
+ /// å°åº¦è¯
+ case hindi
+ /// å°åº¦å°¼è¥¿äºè¯
+ case indonesian
+ /// 马æ¥è¥¿äºè¯
+ case malaysian
+ /// æ³¢å
°è¯
+ case polish
+ /// æ³°è¯
+ case thai
+ /// è¶åè¯
+ case vietnamese
+ /// è¬å
°è¯
+ case finnish
+ /// ç½é©¬å°¼äºè¯
+ case romanian
+ /// åè³å
¶è¯
+ case turkish
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBManufacturer.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBManufacturer.swift
new file mode 100644
index 0000000..6300d84
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBManufacturer.swift
@@ -0,0 +1,37 @@
+//
+// IBManufacturer.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/14.
+//
+
+import Foundation
+
+/// å¹¿ææ°æ®å
è§£æ
+struct IBManufacturer {
+ init?(_ data: Data) {
+ guard data.count == 14 else { return nil }
+
+ code = data[0...1].string ?? ""
+ guard code == "JH" else { return nil }
+
+ isBind = data[2].int
+ macAddress = data[3...8].reversedJoined(":")
+ customerID = data[9].int
+ productID = data[10].int
+ softwareVersion = data[11..<14].joined(format: "%d", ".")
+ }
+
+ /// è¯å«ç¬¦
+ let code: String
+ /// æ¯å¦å·²ç»å®(1=å·²ç»å®)
+ let isBind: Int
+ /// mac å°å
+ let macAddress: String
+ /// å®¢æ· ID
+ let customerID: Int
+ /// 产å ID
+ let productID: Int
+ /// è½¯ä»¶çæ¬
+ let softwareVersion: String
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBMp3.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBMp3.swift
new file mode 100644
index 0000000..3b10293
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBMp3.swift
@@ -0,0 +1,38 @@
+//
+// IBMp3.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/6.
+//
+
+import Foundation
+
+/// åémp3æä»¶æ¨¡å
+public class IBSendMp3:Codable {
+ /// æä»¶è·¯å¾
+ public var filePath: String = ""
+ /// ææ²åå
+ public var name: String = ""
+ /// æä»¶å¤§å°ï¼åä½byte
+ internal var size: Int = 0
+
+
+ public init() { }
+}
+
+
+/// mp3æä»¶æ¨¡å
+public class IBMp3File:Codable {
+ /// ææ²åå
+ var name: String = ""
+ /// æä»¶ç±»åï¼2ï¼MP3
+ var type: Int = 2
+ /// æä»¶å¤§å°ï¼åä½byte
+ var size: Int = 0
+
+ internal init(data:IBSendMp3) {
+ self.name = data.name
+ self.size = data.size
+ }
+}
+
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBOta.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBOta.swift
new file mode 100644
index 0000000..5f8aa00
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBOta.swift
@@ -0,0 +1,48 @@
+//
+// IBOta.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/6/6.
+//
+
+import Foundation
+/// åéOTAæä»¶æ¨¡å
+public class IBSendOta:Codable {
+ /// æä»¶è·¯å¾
+ public var filePath: String = ""
+ /// æä»¶å¤§å°ï¼åä½byte
+ internal var size: Int = 0
+ /// majoræ¯ä¸»çæ¬å·ï¼midæ¯ä¸é´çæ¬å·ï¼minoræ¯æ¬¡çæ¬å·ï¼ç¨äºä¸æè¡¨å½åçæ¬æ¯è¾ï¼æ°äºå½åçæ¬æå¯å级
+ public var major: Int = 0
+ /// midæ¯ä¸é´çæ¬å·
+ public var mid: Int = 0
+ /// minoræ¯æ¬¡çæ¬å·
+ public var minor: Int = 0
+ /// æ¯å¦å¼ºå¶å级ï¼0为é强å¶ï¼1为强å¶
+ public var isForced: Int = 0
+
+ public init() { }
+}
+/// OTAæä»¶æ¨¡å
+public class IBOtaFile:Codable {
+ /// æä»¶ç±»åï¼1:OTA
+ internal var type: Int = 1
+ /// æä»¶å¤§å°ï¼åä½byte
+ internal var size: Int = 0
+ /// majoræ¯ä¸»çæ¬å·ï¼midæ¯ä¸é´çæ¬å·ï¼minoræ¯æ¬¡çæ¬å·ï¼ç¨äºä¸æè¡¨å½åçæ¬æ¯è¾ï¼æ°äºå½åçæ¬æå¯å级
+ internal var major: Int = 0
+ /// midæ¯ä¸é´çæ¬å·
+ internal var mid: Int = 0
+ /// minoræ¯æ¬¡çæ¬å·
+ internal var minor: Int = 0
+ /// æ¯å¦å¼ºå¶å级ï¼0为é强å¶ï¼1为强å¶
+ internal var isForced: Int = 0
+
+ internal init(data:IBSendOta) {
+ self.size = data.size
+ self.major = data.major
+ self.mid = data.mid
+ self.minor = data.minor
+ self.isForced = data.isForced
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBOtherTarget.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBOtherTarget.swift
new file mode 100644
index 0000000..fd0aaf7
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBOtherTarget.swift
@@ -0,0 +1,41 @@
+//
+// IBUserInfo.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/14.
+//
+
+import Foundation
+/// å
¶ä»ç®æ 设置模å
+public class IBOtherTarget {
+ /// ç®æ å¡è·¯éï¼åä½åå¡
+ public var calorie: Int = 0
+ /// ç®æ éç¨ï¼åä½å
Ž
+ public var mileage: Int = 1
+ /// ç®æ ç«ç«æ¬¡æ°ï¼å使¬¡
+ public var stand: Int = 0
+ /// ç®æ çèæ¶é´ï¼åä½åé
+ public var burning: Int = 0
+ /// é声æé 0ï¼å
³é 1ï¼å¼å¯
+ public var ringsState: Int = 0
+ /// é卿é 0ï¼å
³é 1ï¼å¼å¯
+ public var vibrateState: Int = 0
+ /// éç¥æé 0ï¼å
³é 1ï¼å¼å¯
+ public var noticeState: Int = 0
+
+ public init() { }
+
+ init(_ data: Data) {
+ calorie = data[0...1].int
+ mileage = data[2].int
+ stand = data[3].int
+ burning = data[4].int
+ ringsState = data[5].int
+ vibrateState = data[6].int
+ noticeState = data[7].int
+ }
+
+ var description: String {
+ return "IBOtherTarget(calorie: \(calorie), mileage:\(mileage), stand:\(stand), burning:\(burning), ringsState:\(ringsState), vibrateState:\(vibrateState), noticeState:\(noticeState))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBPhoneInfo.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBPhoneInfo.swift
new file mode 100644
index 0000000..0387b03
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBPhoneInfo.swift
@@ -0,0 +1,25 @@
+//
+// IBDeviceInfo.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/23.
+//
+
+import Foundation
+/// ææºä¿¡æ¯æ¨¡å
+public class IBPhoneInfo:Codable {
+ /// ææºåç§°
+ public var name: String = "iphone"
+ /// ææºç±»å 0ï¼å®å 1ï¼è¹æ
+ public var type: Int = 1
+ /// ææºMACå°åæ°ç»ï¼ä»
IOSï¼é
对æåä»æè¡¨è·åï¼æªè·åæ¶ä¸ºç©ºï¼
+ public var mac: [Int]?
+
+ enum CodingKeys: String, CodingKey {
+ case name = "pn"
+ case type = "tp"
+ case mac = "mac"
+ }
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBResponse.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBResponse.swift
new file mode 100644
index 0000000..27891f4
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBResponse.swift
@@ -0,0 +1,77 @@
+//
+// IBResponse.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/15.
+//
+
+import Foundation
+
+public class IBResponseModel {
+ var data:Data!
+ required init(_ data: Data) {
+ self.data = data
+ }
+
+ var pkgData:Data{
+ if(data.count > 18){
+ return data[17...data.count - 2]
+ }
+ return Data()
+ }
+}
+
+
+public class IBResponse:CustomStringConvertible {
+ /// ç¶æç
+ public private(set) var code: IBCode = .success
+ /// åé¦ä¿¡æ¯
+ public private(set) var message: String = "æä½æå"
+ /// å½æåæ¶ä¸º true
+ public private(set) var isSuccess: Bool = false
+ /// å½ç»æä¸ºéåæ¶ï¼åºè¯¥ä»æ¤å段读å
+ public private(set) var items: [Any] = []
+ /// å½ç»æä¸ºééåï¼åºè¯¥ä»æ¤å段读å
+ public private(set) var value: Any?
+
+ static var response: IBResponse { IBResponse() }
+
+ public var description: String {
+ var des = "IBResponse(code: \(code.rawValue),isSuccess: \(isSuccess), massage:\(message), "
+ if let v1 = value {
+ des.append("value:\(v1)ï¼")
+ }
+ else{
+ des.append("items:\(items)ï¼")
+ }
+ return des
+ }
+}
+
+extension IBResponse {
+ @discardableResult
+ func code(_ value: IBCode) -> Self {
+ code = value
+ isSuccess = code == .success
+ message = code.message
+ return self
+ }
+
+ @discardableResult
+ func message(_ value: String) -> Self {
+ message = value
+ return self
+ }
+
+ @discardableResult
+ func items(_ value: [Any]) -> Self {
+ items = value
+ return self
+ }
+
+ @discardableResult
+ func value(_ value: Any) -> Self {
+ self.value = value
+ return self
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBScreen.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBScreen.swift
new file mode 100644
index 0000000..9a1a017
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBScreen.swift
@@ -0,0 +1,77 @@
+//
+// IBScreen.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/21.
+//
+
+import Foundation
+
+/// 设置å±å¹åæ°
+public class IBScreenConfig {
+ /// ç±»å 0ï¼äº®åº¦è°è; 1ï¼äº®å±æ¶é¿ï¼æ¬è
亮å±
+ public var type: Int = 0
+
+ /// 亮度 [51, 255]ï¼ type为0å¿
须设置
+ public var brightness: Int?
+
+ /// äº®å±æ¶é¿ï¼ type为1å¿
须设置
+ public var duration: IBDuration?
+
+ /// æ¬è
亮å±(0=å
³éï¼ 1=å¼å¯)ï¼ type为1å¿
须设置
+ public var wrist: Int?
+
+ public init() { }
+
+ var description: String {
+ var str = "IBScreenConfig(type: \(type),"
+ if let brightness = brightness{
+ str += "brightness: \(brightness ))"
+ }
+ if let duration = duration {
+ str += "duration: \(duration )"
+ }
+ if let wrist = wrist {
+ str += "wrist: \(wrist ))"
+ }
+ return str
+ }
+}
+
+/// å±å¹åæ°
+public class IBScreen {
+ /// 亮度 [0, 255]
+ public var brightness: Int = 10
+
+ /// äº®å±æ¶é¿
+ public var duration: IBDuration = .second10
+
+ /// æ¬è
亮å±(0=å
³éï¼ 1=å¼å¯)
+ public var wrist: Int = 1
+
+ public init() { }
+
+ init(_ data: Data) {
+ brightness = data[0].int
+ duration = IBDuration(rawValue: data[1].int) ?? .second5
+ wrist = data[2].int
+ }
+ var description: String {
+ return "IBScreen(brightness: \(brightness), duration:\(duration), wrist:\(wrist))"
+ }
+}
+
+public enum IBDuration: Int {
+ /// 5 ç§
+ case second5 = 0
+ /// 10 ç§
+ case second10
+ /// 15 ç§
+ case second15
+ /// 30 ç§
+ case second30
+ /// 1 åé
+ case minute
+ /// 5 åé
+ case minute5
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBSendTime.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBSendTime.swift
new file mode 100644
index 0000000..5cb39eb
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBSendTime.swift
@@ -0,0 +1,30 @@
+//
+// IBSendTime.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/23.
+//
+
+import Foundation
+/// åéæ¶é´æ¨¡å
+public class IBSendTime:Codable {
+ /// unixæ¶é´æ³
+ public var time: Int = 0
+ /// 0-6ï¼å¨æ¥-å¨å
+ public var week: Int = 0
+ /// 0-127ï¼å¹´ä»½åå»2000
+ public var year: Int = 0
+ /// 1-12
+ public var month: Int = 1
+ /// 1-31
+ public var day: Int = 1
+ /// 0-24
+ public var hour: Int = 0
+ /// 0-60
+ public var minute: Int = 0
+ /// 0-60
+ public var second: Int = 0
+
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBSleep.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBSleep.swift
new file mode 100644
index 0000000..04e703a
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBSleep.swift
@@ -0,0 +1,56 @@
+//
+// IBSleep.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/15.
+//
+
+import Foundation
+/// ç¡ç 设置模å
+public class IBSleep {
+ /// 0ï¼å
³é 1ï¼å¼å¯ 为0æ¶å颿æé¡¹ç®é½æ é设置ã
+ public var sleepTimeSet: Int = 0
+ /// 就坿¶é´
+ public var sleepTime: IBTimeItem?
+ /// èµ·åºæ¶é´
+ public var getUpTime: IBTimeItem?
+ /// å°±å¯å20åéæé,0ï¼å
³é1ï¼å¼å¯
+ public var alertBeforeMin20: Int = 0
+
+ init(_ data: Data) {
+ sleepTimeSet = data[0].int
+ if sleepTimeSet == 1{
+ sleepTime = IBTimeItem(hour: data[1].int, minute: data[2].int)
+ getUpTime = IBTimeItem(hour: data[3].int, minute: data[4].int)
+ alertBeforeMin20 = data[5].int
+ }
+ }
+ public init() {
+// sleepTimeSet = 1
+// sleepTime = IBTimeItem(hour: 7, minute: 30)
+// getUpTime = IBTimeItem(hour: 15, minute: 50)
+// alertBeforeMin20 = 0
+ }
+
+ var description: String {
+ return "IBSleep(sleepTimeSet: \(sleepTimeSet), sleepTime:\(sleepTime?.description ?? ""), getUpTime:\(getUpTime?.description ?? ""), alertBeforeMin20:\(alertBeforeMin20))"
+ }
+
+}
+
+/// æ¶é´
+public struct IBTimeItem {
+ /// 0~23
+ public var hour: Int
+ /// 0~59
+ public var minute: Int
+
+ public init(hour: Int, minute: Int) {
+ self.hour = hour
+ self.minute = minute
+ }
+
+ var description: String {
+ return "IBTimeItem(hour: \(hour), minute:\(minute)"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBTargetStep.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBTargetStep.swift
new file mode 100644
index 0000000..5666ca8
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBTargetStep.swift
@@ -0,0 +1,37 @@
+//
+// IBTargetStep.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/21.
+//
+
+import Foundation
+/// ç®æ æ¥æ°æ¨¡å
+public class IBTargetStep {
+
+ /// æ¥æ°
+ public var value: Int = 5000
+
+ /// é声æé(0=å
³éï¼1=å¼å¯)
+ public var ring: Int = 0
+
+ /// é卿é(0=å
³éï¼1=å¼å¯)
+ public var vibrate: Int = 0
+
+ /// éç¥æé(0=å
³éï¼1=å¼å¯)
+ public var notice: Int = 0
+
+ init?(_ data: Data) {
+ guard data.count == 7 else { return nil }
+ value = data[0...3].int
+ ring = data[4].int
+ vibrate = data[5].int
+ notice = data[6].int
+ }
+
+ var description: String {
+ return "IBTargetStep(value: \(value), ring:\(ring), vibrate: \(vibrate), notice:\(notice))"
+ }
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBTarinState.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBTarinState.swift
new file mode 100644
index 0000000..9e048fe
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBTarinState.swift
@@ -0,0 +1,26 @@
+//
+// IBTarinState.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/28.
+//
+
+import Foundation
+/// æ¨éè®ç»ç¶ææ¨¡å
+public class IBTarinState {
+ /// ç¶æ 0ï¼ç»æ 1ï¼å¼å§ 2ï¼æå 3ï¼æ¢å¤
+ public var state: Int = 1
+ /// è®ç»æ¶é¿ï¼åä½ç§
+ public var duration: Int = 0
+
+ public init() { }
+
+ init(_ data: Data) {
+ state = data[0].int
+ duration = data[1...2].int
+ }
+
+ public var description: String {
+ return "IBTarinState(state: \(state), duration:\(duration))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBTrainTarget.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBTrainTarget.swift
new file mode 100644
index 0000000..bbc1946
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBTrainTarget.swift
@@ -0,0 +1,46 @@
+//
+// IBaa.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/16.
+//
+
+import Foundation
+/// è®ç»ç®æ 设置模å
+public class IBTrainTarget:Codable {
+ /// ç®æ ç±»å,对åºçç±»åï¼è®¾ç½®å¯¹åºçå¼
+ public var type: IBTrainTargetType = .calorie
+ /// ç®æ è·ç¦»ï¼åä½ç±³
+ public var distance: Int?
+ /// ç®æ å¡è·¯éï¼åä½kcal
+ public var calorie: Int?
+ /// ç®æ æ¶é´ï¼åä½åé
+ public var time: Int?
+ /// ç®æ æ¥æ°ï¼å使¥
+ public var step: Int?
+
+ public init() { }
+
+ enum CodingKeys: String, CodingKey {
+ case type = "ty"
+ case distance = "di"
+ case calorie = "ca"
+ case time = "tm"
+ case step = "st"
+ }
+ var description: String {
+ return "IBTrainTarget(type: \(type), distance:\(distance ?? 0), calorie:\(calorie ?? 0), time:\(time ?? 0), step:\(step ?? 0))"
+ }
+}
+
+/// æéé¢ç
+public enum IBTrainTargetType: Int,Codable {
+ /// è·ç¦»
+ case distance = 0
+ /// å¡è·¯é
+ case calorie = 1
+ /// æ¶é´
+ case time = 2
+ /// æ¥æ°
+ case step = 4
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBTrainingData.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBTrainingData.swift
new file mode 100644
index 0000000..270c59e
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBTrainingData.swift
@@ -0,0 +1,48 @@
+//
+// IBTrainingData.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/20.
+//
+
+import Foundation
+
+/// è®ç»æ°æ®æ¨¡å
+public struct IBTrainingData {
+ /// è®ç»å¼å§æ¶é´(æ¶é´æ³)
+ public let startTime: Int
+
+ /// 宿¶è®ç»å¿çï¼åä½bpm
+ public let heartRate: Int
+
+ /// 宿¶è®ç»å¡è·¯éï¼åä½kcal
+ public let calorie: Int
+
+ /// 宿¶è®ç»è·ç¦»ï¼åä½ç±³
+ public let distance: Int
+
+ /// 宿¶è®ç»æ¥æ°ï¼å使¥
+ public let stepCount: Int
+
+ /// 宿¶è®ç»å¼ºåº¦
+ ///
+ /// - 0ï¼è¾ä½å¼ºåº¦
+ /// - 1ï¼ä¸ç强度
+ /// - 2ï¼é«ç强度
+ /// - 3ï¼è¶
é«å¼ºåº¦
+ public let intensity: Int
+
+ init?(_ data: Data) {
+ guard let dict = data.jsonObject() as? [String: Int] else { return nil }
+ self.startTime = dict["st"]!
+ self.heartRate = dict["hr"]!
+ self.calorie = dict["ca"]!
+ self.distance = dict["di"]!
+ self.stepCount = dict["stp"]!
+ self.intensity = dict["ti"]!
+ }
+
+ public var description: String {
+ return "IBTrainingData(startTime: \(startTime), heartRate:\(heartRate), calorie:\(calorie), distance:\(distance), stepCount:\(stepCount), intensity:\(intensity))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBUserInfo.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBUserInfo.swift
new file mode 100644
index 0000000..34e8192
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBUserInfo.swift
@@ -0,0 +1,45 @@
+//
+// IBUserInfo.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/30.
+//
+
+import Foundation
+/// ç¨æ·æ°æ®æ¨¡å
+public class IBUserInfo {
+ /// å¹´é¾
+ public var age: Int = 0
+ /// æ§å« 0ï¼ç·æ§ 1ï¼å¥³æ§
+ public var gender: Int = 0
+ /// 身é«ï¼ åä½cm
+ public var height: Int = 0
+ /// ä½éï¼ åä½kg
+ public var weight: Int = 0
+ /// ç®æ æ¥æ°
+ public var step: Int = 0
+ /// ç®æ çèæ¶é´ï¼åä½åé
+ public var burning: Int = 0
+ /// ç®æ ç«ç«æ¬¡æ°
+ public var stand: Int = 0
+ /// æ¶å¶ 0:24å°æ¶å¶ 1:12å°æ¶å¶
+ public var hourSystem: Int = 0
+
+
+ public init() { }
+
+ init(_ data: Data) {
+ age = data[0].int
+ gender = data[1].int
+ height = data[2].int
+ weight = data[3...4].int
+ step = data[5...8].int
+ burning = data[9...10].int
+ stand = data[11].int
+ hourSystem = data[12].int
+ }
+
+ public var description: String {
+ return "IBUserInfo(age: \(age), gender:\(gender), height:\(height), weight:\(weight), step:\(step), burning:\(burning), stand:\(stand), hourSystem:\(hourSystem))"
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBVibrateSound.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBVibrateSound.swift
new file mode 100644
index 0000000..88e1ccd
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBVibrateSound.swift
@@ -0,0 +1,63 @@
+////
+//// IBVibrateSound.swift
+//// IOTBluetooth
+////
+//// Created by incar on 2024/5/9.
+////
+//
+
+/// 声é³éå¨è®¾ç½®æ¨¡å
+public class IBVibrateConfig {
+ /// ç±»å 0ç³»ç»é³éè°èï¼1é声é³éè°èï¼3é声å¼å
³ï¼éå¨å¼å
³
+ public var type: Int = 0
+ /// ç³»ç»é³éè°è0~100ï¼type为0设置ï¼
+ public var systemValue: Int?
+
+ /// é声é³éè°è0~100ï¼type为1设置ï¼
+ public var ringsValue: Int?
+
+ /// éé³å¼å
³0ï¼å
³é 1ï¼å¼å¯ï¼type为3设置ï¼
+ public var ringsState: Int?
+
+ /// éå¨è®¾ç½® 0ï¼å
³é 1ï¼å¼± 2ï¼ä¸ 3ï¼å¼º ï¼type为3设置ï¼
+ public var vibrateState: Int?
+
+ public init() { }
+
+ var description: String {
+ return "IBVibrateConfig(type: \(type),systemValue: \(systemValue), ringsValue:\(ringsValue), ringsState:\(ringsState), vibrateState:\(vibrateState))"
+ }
+}
+
+/// 声é³é卿¨¡å
+public class IBVibrateSound {
+ /// ç³»ç»é³éè°è0~100
+ public var systemValue: Int = 0
+
+ /// é声é³éè°è0~100
+ public var ringsValue: Int = 0
+
+ /// åªä½é³éè°è0~100
+ public var mediaValue: Int = 0
+
+ /// éé³å¼å
³0ï¼å
³é 1ï¼å¼å¯
+ public var ringsState: Int = 0
+
+ /// éå¨è®¾ç½® 0ï¼å
³é 1ï¼å¼± 2ï¼ä¸ 3ï¼å¼º
+ public var vibrateState: Int = 0
+
+ public init() { }
+
+ init(_ data: Data) {
+ systemValue = data[0].int
+ ringsValue = data[1].int
+ mediaValue = data[2].int
+ ringsState = data[3].int
+ vibrateState = data[4].int
+ }
+
+ var description: String {
+ return "IBVibrateSound(systemValue: \(systemValue), ringsValue:\(ringsValue), mediaValue:\(mediaValue), ringsState:\(ringsState), vibrateState:\(vibrateState))"
+ }
+}
+
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBWatchInfo.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBWatchInfo.swift
new file mode 100644
index 0000000..2a7c0c1
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBWatchInfo.swift
@@ -0,0 +1,46 @@
+//
+// IBWatchInfo.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/14.
+//
+
+import Foundation
+
+/// æè¡¨ä¿¡æ¯
+struct IBWatchInfo {
+
+ init(_ data: Data) {
+ battery = data[0].int
+ dialID = data[1...2].int
+ firmwareVersion = data[3...5].joined(format: "%d", ".")
+ macAddress = data[6...11].joined(":")
+ messagePush = data[12].int
+ media = data[13].int
+ customerID = data[14].int
+ productID = data[15].int
+ hardwareVersion = data[16...17].joined(format: "%d", ".")
+ bluetoothVersion = data[18...19].joined(format: "%d", ".")
+ }
+
+ /// çµé
+ let battery: Int
+ /// 表çID
+ let dialID: Int
+ /// åºä»¶çæ¬
+ let firmwareVersion: String
+ /// mac å°å
+ let macAddress: String
+ /// æ¶æ¯æ¨é 1=å¼å¯
+ let messagePush: Int
+ /// å¤åªä½ 1=å¼å¯
+ let media: Int
+ /// ç¡¬ä»¶çæ¬
+ let hardwareVersion: String
+ /// èççæ¬
+ let bluetoothVersion: String
+ /// å®¢æ· ID
+ let customerID: Int
+ /// 产å ID
+ let productID: Int
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBWater.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBWater.swift
new file mode 100644
index 0000000..1da5ebd
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBWater.swift
@@ -0,0 +1,45 @@
+//
+// IBWater.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/15.
+//
+
+
+import Foundation
+/// åæ°´æé设置模å
+public class IBWater {
+ /// 0ï¼å
³é 1ï¼å¼å¯ 为0æ¶å颿æé¡¹ç®é½æ é设置ã
+ public var alertState: Int = 0
+ /// æéæ¹å¼0ï¼æ¯å¤©æé䏿¬¡ 1ï¼é´éæé
+ public var alertType: Int = 0
+ /// é¥®æ°´ç®æ 500~5000ml
+ public var drinkingValue: Int = 0
+ /// æéå¼å§æ¶é´
+ public var start:IBTimeItem = IBTimeItem(hour: 0, minute: 0)
+ /// æéç»ææ¶é´ãä»
é´éæééè¦ï¼å
¶ä»æ
åµä¸å¡«
+ public var end:IBTimeItem = IBTimeItem(hour: 0, minute: 0)
+ /// æéé¢çãä»
é´éæééè¦ï¼å
¶ä»æ
åµä¸å¡«
+ public var frequency: IBFrequency = IBFrequency.min30
+
+ public init() { }
+
+ var description: String {
+ return "IBWater(alertState: \(alertState), alertType:\(alertType), drinkingValue:\(drinkingValue), start:\(start.description), end:\(end.description), frequency:\(frequency.rawValue))"
+ }
+}
+
+
+/// æéé¢ç
+public enum IBFrequency: Int {
+ /// 30åé
+ case min30 = 0
+ /// 1å°æ¶
+ case hour1
+ /// 2å°æ¶
+ case hour2
+ /// 3å°æ¶
+ case hour3
+ /// å
¶ä»æ
åµ
+ case none
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Models/IBWeather.swift b/code/IOTBluetooth/IOTBluetooth/Models/IBWeather.swift
new file mode 100644
index 0000000..5409798
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Models/IBWeather.swift
@@ -0,0 +1,70 @@
+//
+// IBWeather.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/5/30.
+//
+
+import Foundation
+/// å¤©æ°æ¨¡å
+public class IBWeather:Codable {
+ /// å½åå®ä½å°å
+ public var address: IBAddress = IBAddress()
+ /// å½å天æ°ä¿¡æ¯
+ public var now: IBWeatherNow = IBWeatherNow()
+ /// ä¸å¤©å¤©æ°ä¿¡æ¯
+ public var daily: [IBWeatherInfo] = []
+
+
+ public init() { }
+}
+
+
+/// å°å模å
+public class IBAddress:Codable {
+ /// åå¸
+ public var city: String = ""
+ /// åº
+ public var subLocality: String = ""
+ /// 纬度ï¼è¯¥å¼é¤ä»¥10000000å¾
+ public var lat: Int = 0
+ /// ç»åº¦ï¼è¯¥å¼é¤ä»¥10000000å¾å°å®é
纬度å¼
+ public var lon: Int = 0
+ /// æ°åï¼åä½hPa
+ public var atm: Int = 0
+
+ public init() { }
+}
+
+/// 天æ°ä¿¡æ¯æ¨¡å
+public class IBWeatherInfo:Codable {
+ /// 颿¥æ¥æï¼æ¶é´æ³
+ public var fxDate: Int = 1654012800
+ /// 颿¥ç½å¤©å¤©æ°ç¶åµç徿 代ç
+ public var iconDay: Int = 0
+ /// 颿¥ç½å¤©å¤©æ°ç¶åµæåæè¿°ï¼å
æ¬é´æ´é¨éªç天æ°ç¶æçæè¿°
+ public var textDay: String = ""
+ /// 颿¥å½å¤©æé«æ¸©åº¦
+ public var tempMax: Int = 0
+ /// 颿¥å½å¤©æä½æ¸©åº¦
+ public var tempMin: Int = 0
+
+ public init() { }
+}
+/// ç°å¨å¤©æ°ä¿¡æ¯æ¨¡å
+public class IBWeatherNow:Codable {
+ /// å½å温度
+ public var temp: Int = 0
+ /// 夿天æ°icon徿
+ public var iconNight: Int = 0
+ /// å¤æå¤©æ°æ
åµ
+ public var textNight: String = ""
+ /// é£å
+ public var windScale: String = ""
+ /// 湿度%
+ public var humidity: Int = 0
+ /// ç´«å¤çº¿å¼ºåº¦ææ°
+ public var uvIndex: Int = 0
+
+ public init() { }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/Any.swift b/code/IOTBluetooth/IOTBluetooth/Tools/Any.swift
new file mode 100644
index 0000000..662e7e1
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/Any.swift
@@ -0,0 +1,196 @@
+//
+// Any.swift
+// MyBase
+//
+// Created by Harvey on 2021/6/2.
+//
+
+import Foundation
+
+/// 夿å¯éç±»åæ¯å¦ä¸º nil
+func isNil(_ any: Any?) -> Bool {
+ switch any {
+ case .none: return true
+ case .some: return false
+ }
+}
+
+/// 夿å¯éç±»åæ¯å¦æå¼
+func isValue(_ any: Any?) -> Bool {
+ return isNil(any) == false
+}
+
+/// for Array
+func isEmpty(_ value: [Any]?) -> Bool {
+ return isNil(value) || value!.count == 0
+}
+
+/// for Array
+func isNotEmpty(_ value: [Any]?) -> Bool {
+ return isEmpty(value) == false
+}
+
+/// for Dictionary
+func isEmpty(_ value: [AnyHashable: Any]?) -> Bool {
+ return isNil(value) || value!.count == 0
+}
+
+/// for Dictionary
+func isNotEmpty(_ value: [AnyHashable: Any]?) -> Bool {
+ return isEmpty(value) == false
+}
+
+/// for String
+func isEmpty(_ value: String?) -> Bool {
+ return isNil(value) || value!.count == 0
+}
+
+/// for String
+func isNotEmpty(_ value: String?) -> Bool {
+ return isEmpty(value) == false
+}
+
+public extension String {
+
+ /// 补足8ä½(é¿åº¦ä¸º8çæ´æ°å)
+ var binary: String {
+ let bytes = (Double(count) / 8.0).upInt
+ let offset = bytes * 8 - count
+ let prefix = Array(repeating: "0", count: offset).joined()
+ return prefix + self
+ }
+}
+
+public extension Double {
+ var upInt: Int { Int(ceil(self)) }
+}
+
+public extension Int {
+ private static let binaryPack = 0b11111111
+
+ /// convert to data with byte (max is 4294967295)
+ ///
+ /// ä½ä½ --> é«ä½
+ var bytes: [UInt8] {
+ var mutatingSelf = self
+ var bytes = [UInt8(mutatingSelf & Self.binaryPack)]
+
+ var count = 1
+ mutatingSelf = mutatingSelf >> (count * 8)
+ while mutatingSelf > 0 {
+ bytes.append(UInt8(mutatingSelf & Self.binaryPack))
+ mutatingSelf = mutatingSelf >> (count * 8)
+ count = count + 1
+ }
+ return bytes
+ }
+
+ /// convert to data with 1 byte
+ var byte: UInt8 {
+ UInt8(self & Self.binaryPack)
+ }
+
+ /// convert to data with 2 byte
+ ///
+ /// ä½ä½ --> é«ä½
+ var byte2: [UInt8] {
+ [
+ UInt8(self & Self.binaryPack),
+ UInt8(self >> 8 & Self.binaryPack)
+ ]
+ }
+
+ /// convert to data with 3 byte
+ ///
+ /// ä½ä½ --> é«ä½
+ var byte3: [UInt8] {
+ [
+ UInt8(self & Self.binaryPack),
+ UInt8(self >> 8 & Self.binaryPack),
+ UInt8(self >> 16 & Self.binaryPack)
+ ]
+ }
+
+ /// convert to data with 4 byte
+ ///
+ /// ä½ä½ --> é«ä½
+ var byte4: [UInt8] {
+ [
+ UInt8(self & Self.binaryPack),
+ UInt8(self >> 8 & Self.binaryPack),
+ UInt8(self >> 16 & Self.binaryPack),
+ UInt8(self >> 24 & Self.binaryPack)
+ ]
+ }
+}
+
+public extension BinaryInteger {
+
+ /// e.g. 0000100110010101
+ var binaryString: String {
+ .init(self, radix: 2).binary
+ }
+}
+
+public extension Array where Element == UInt8 {
+
+ var int: Int {
+ self
+ .map { Int($0) }
+ .enumerated()
+ .map { $1 << ($0 * 8) }
+ .reduce(0, { partialResult, value in
+ partialResult | value
+ })
+ }
+
+ var data: Data { Data(self) }
+}
+
+public extension Data {
+ var bytes: [UInt8] { map { $0 } }
+
+ func joined(format: String = "%02x", _ separator: String = "") -> String {
+ self
+ .map { String(format: format, $0) }
+ .joined(separator: separator)
+ }
+
+ func reversedJoined(format: String = "%02x", _ separator: String = "") -> String {
+ self
+ .map { String(format: format, $0) }
+ .reversed()
+ .joined(separator: separator)
+ }
+
+ var int: Int {
+ self
+ .map { Int($0) }
+ .enumerated()
+ .map { $1 << ($0 * 8) }
+ .reduce(0, { partialResult, value in
+ partialResult | value
+ })
+ }
+
+ var lrc: UInt8 { LRC.calculateLRC(bytes) }
+}
+
+public extension UInt8 {
+ var int: Int { Int(self) }
+}
+
+
+extension Data{
+ var float: Float {
+ var floatValue: Float = 0.0
+ let size = MemoryLayout.size
+ guard self.count >= size else { return 0 }
+
+ self.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Void in
+ _ = memcpy(&floatValue, buffer.baseAddress!, size)
+ }
+
+ return floatValue
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/CRC16.swift b/code/IOTBluetooth/IOTBluetooth/Tools/CRC16.swift
new file mode 100644
index 0000000..0025438
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/CRC16.swift
@@ -0,0 +1,44 @@
+//
+// CRC16.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/12.
+//
+
+import Foundation
+
+/// CRC-16/MODBUS
+class CRC16 {
+ static let shared = CRC16()
+ private init() { }
+
+ func result(_ bytes: [UInt8]) -> [UInt8] {
+ var crcValue = crc(bytes)
+ var crcBytes: [UInt8] = [0, 0]
+ for index in (0..<2).reversed() {
+ crcBytes[index] = UInt8(crcValue % 256)
+ crcValue >>= 8
+ }
+ return crcBytes
+ }
+
+ private func crc(_ bytes: [UInt8]) -> Int {
+
+ var crcSum: Int = 0xffff
+ let polynomial: Int = 0xa001
+ let length = bytes.count
+
+ for index in 0..>= 1
+ crcSum ^= polynomial
+ }else{
+ crcSum >>= 1
+ }
+ }
+ }
+ return crcSum
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/Date.swift b/code/IOTBluetooth/IOTBluetooth/Tools/Date.swift
new file mode 100644
index 0000000..6f0e492
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/Date.swift
@@ -0,0 +1,148 @@
+//
+// Date.swift
+// MyBase
+//
+// Created by Harvey on 2021/5/18.
+//
+
+import Foundation
+
+// MARK: - Extension Date
+extension Date {
+
+ static let formatter = DateFormatter()
+
+ struct Format {
+
+ let rawValue: String
+ init(_ rawValue: String) {
+ self.rawValue = rawValue
+ }
+
+ /// e.g. 01:01:01
+ static let time: Format = .init("HH:mm:ss")
+
+ /// e.g. 01:01
+ static let shortTime: Format = .init("HH:mm")
+
+ /// e.g. 01-01
+ static let date: Format = .init("MM-dd")
+
+ /// e.g. 01æ01æ¥
+ static let date1: Format = .init("MMæddæ¥")
+
+ /// e.g. 2020-01-01
+ static let short: Format = .init("yyyy-MM-dd")
+
+ /// e.g. 2020å¹´01æ01æ¥
+ static let short1: Format = .init("yyyå¹´MMæddæ¥")
+
+ /// e.g. 2020-01-01 01:01
+ static let middle: Format = .init("yyyy-MM-dd HH:mm")
+
+ /// e.g. 2020å¹´01æ01æ¥ 01:01
+ static let middle1: Format = .init("yyyå¹´MMæddæ¥ HH:mm")
+
+ /// e.g. 2020-01-01 01:01:01
+ static let long: Format = .init("yyyy-MM-dd HH:mm:ss")
+
+ /// e.g. 2020å¹´01æ01æ¥ 01:01:01
+ static let long1: Format = .init("yyyyå¹´MMæddæ¥ HH:mm:ss")
+
+ /// e.g. 2020-01-01 010101
+ static let long2: Format = .init("yyyy-MM-dd HHmmss")
+
+ /// e.g. 2020-01-01 01:01:01.088
+ static let full: Format = .init("yyyy-MM-dd HH:mm:ss.SSS")
+
+ /// e.g. 2020å¹´01æ01æ¥ 01:01:01.088
+ static let full1: Format = .init("yyyyå¹´MMæddæ¥ HH:mm:ss.SSS")
+ }
+
+ /// æ ¼å¼åææ¬å°æ¶é´
+ func format(_ format: Date.Format = .middle) -> String {
+ Self.formatter.dateFormat = format.rawValue
+ return Self.formatter.string(from: self)
+ }
+
+ enum Unit {
+ case minute(Int)
+ case hour(Int)
+ case day(Int)
+
+ /// è½¬æ¢æç§
+ var second: TimeInterval {
+ switch self {
+ case .minute(let minute): return 60.0 * Double(minute)
+ case .hour(let hour): return 60.0 * 60.0 * Double(hour)
+ case .day(let day): return 60.0 * 60.0 * 24.0 * Double(day)
+ }
+ }
+ }
+}
+
+let today: Date = .init()
+
+/// æ¬å°å
+extension Date {
+ /// ææå
+ enum Week: Int {
+ /// æææ¥
+ case sun = 1
+ /// ææä¸
+ case mon
+ /// ææäº
+ case tue
+ /// ææä¸
+ case wed
+ /// ææå
+ case thur
+ /// ææäº
+ case fri
+ /// ææå
+ case sat
+ }
+
+// /// å½å¤©ç第ä¸ç§
+// var fromTimeInterval: TimeInterval {
+// let today = format(.short)
+// return today.timeIntervalSince1970(.short)
+// }
+//
+// /// å½å¤©æåä¸ç§
+// var toTimeInterval: TimeInterval { fromTimeInterval + Unit.day(1).second }
+
+ /// åªä¸å¹´
+ var year: Int { component(.year) }
+
+ /// åªä¸ªæ
+ var month: Int { component(.month) }
+
+ /// 彿ç第å 天
+ var day: Int { component(.day) }
+
+ /// 彿ç第å 天
+ var hour: Int { component(.hour)}
+
+ /// 彿ç第å 天
+ var minute: Int { component(.minute)}
+
+ /// 彿ç第å 天
+ var second: Int { component(.second)}
+
+ /// 彿ç第å å¨
+ var weekOfMonth: Int { component(.weekOfMonth) }
+
+ /// ä»å¹´ç第å å¨
+ var weekOfYear: Int { component(.weekOfYear) }
+
+ /// ææå
+ var weekDay: Week { Week(rawValue: component(.weekday))! }
+
+ /// å设å½å¤©æ¯å¨äºï¼è¡¨ç¤ºæ¬æç第å 个å¨äº
+ var weekdayOrdinal: Int { component(.weekdayOrdinal) }
+
+ private func component(_ component: Calendar.Component) -> Int {
+ return Calendar.autoupdatingCurrent.component(component, from: self)
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/IBPeripheralUUID.swift b/code/IOTBluetooth/IOTBluetooth/Tools/IBPeripheralUUID.swift
new file mode 100644
index 0000000..285800b
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/IBPeripheralUUID.swift
@@ -0,0 +1,23 @@
+//
+// IBPeripheralUUID.swift
+// IOTBluetooth
+//
+// Created by Harvey on 2024/2/5.
+//
+
+import CoreBluetooth
+
+public protocol IBPeripheralUUID {
+ /// CBPeripheral.identifier
+ var uuid: UUID { get }
+}
+
+extension UUID: IBPeripheralUUID {
+
+ public var uuid: UUID { self }
+}
+
+public func == (lhs: IBPeripheralUUID?, rhs: IBPeripheralUUID?) -> Bool {
+ guard let lhs = lhs, let rhs = rhs else { return false }
+ return lhs.uuid == rhs.uuid
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/IBTools.swift b/code/IOTBluetooth/IOTBluetooth/Tools/IBTools.swift
new file mode 100644
index 0000000..0965990
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/IBTools.swift
@@ -0,0 +1,35 @@
+//
+// IBTools.swift
+// IOTBluetooth
+//
+// Created by incar on 2024/7/17.
+//
+
+import Foundation
+
+class IBTools{
+ static func saveToFile(data:Data)->String?{
+ let filePath = NSTemporaryDirectory() + "temp.pcm"
+ print(filePath)
+
+ let url = URL(fileURLWithPath: filePath)
+ do {
+ try data.write(to: url, options: .atomic)
+ return filePath
+ }
+ catch{
+ print(error)
+ return nil
+ }
+ }
+
+ static func readFile(from path:String) ->Data?{
+ let fileURL = URL(fileURLWithPath: path)
+ do {
+ let data = try Data(contentsOf:fileURL)
+ return data
+ } catch {
+ return nil
+ }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/JSON.swift b/code/IOTBluetooth/IOTBluetooth/Tools/JSON.swift
new file mode 100644
index 0000000..9c94ed9
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/JSON.swift
@@ -0,0 +1,115 @@
+//
+// JSON.swift
+// MyBase
+//
+// Created by Harvey on 2024/1/7.
+//
+
+import Foundation
+
+ struct JSON { }
+
+ extension Dictionary {
+
+ func jsonString(options: JSONSerialization.WritingOptions = [.fragmentsAllowed]) -> String? {
+ guard JSONSerialization.isValidJSONObject(self) else {
+ Logger.log("æ æç JSONObject")
+ return nil
+ }
+ guard let data = try? JSONSerialization.data(withJSONObject: self, options: options) else { return nil }
+ return data.string
+ }
+}
+
+ extension Array {
+
+ func jsonString(options: JSONSerialization.WritingOptions = [.fragmentsAllowed]) -> String? {
+ guard JSONSerialization.isValidJSONObject(self) else {
+ Logger.log("æ æç JSONObject")
+ return nil
+ }
+ guard let data = try? JSONSerialization.data(withJSONObject: self, options: options) else { return nil }
+ return data.string
+ }
+}
+
+ extension String {
+
+ func jsonObject(options: JSONSerialization.ReadingOptions = [.mutableContainers]) -> Any? {
+ guard let data = self.data else { return nil }
+ return data.jsonObject(options: options)
+ }
+}
+
+ extension Data {
+
+ func jsonObject(options: JSONSerialization.ReadingOptions = [.mutableContainers]) -> Any? {
+ do { return try JSONSerialization.jsonObject(with: self, options: options) }
+ catch { Logger.log(error) }
+ return nil
+ }
+}
+
+// MARK: - Extension Codable
+
+ extension Encodable {
+
+ /// JSON encode
+ var encode: Data? {
+ do {
+ return try JSONEncoder().encode(self)
+ } catch { Logger.log(error) }
+ return nil
+ }
+
+ var jsonString: String? {
+ encode?.string
+ }
+}
+
+ extension Decodable {
+
+ /// JSON decode
+ static func decode(_ from: Data) -> Self? {
+ do {
+ return try JSONDecoder().decode(Self.self, from: from)
+ } catch { Logger.log(error) }
+ return nil
+ }
+
+ /// JSON decode
+ static func decode(_ from: String) -> Self? {
+ guard let data = from.data else { return nil }
+ return decode(data)
+ }
+}
+
+ extension JSON {
+ /// JSON decode
+ static func decode(_ decodable: T.Type, from data: Data) -> T? {
+ do {
+ return try JSONDecoder().decode(decodable.self, from: data)
+ } catch { Logger.log(error) }
+ return nil
+ }
+
+ /// JSON decode
+ static func decode(_ decodable: T.Type, from jsonString: String) -> T? {
+ guard let data = jsonString.data else { return nil }
+ return decode(decodable, from: data)
+ }
+}
+
+ extension Array where Element: Encodable {
+ /// JSON encode
+ var jsonData: Data? {
+ do {
+ return try JSONEncoder().encode(self)
+ } catch { Logger.log(error) }
+ return nil
+ }
+
+ var jsonString: String? {
+ encode?.string
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/LRC.swift b/code/IOTBluetooth/IOTBluetooth/Tools/LRC.swift
new file mode 100644
index 0000000..8270a6a
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/LRC.swift
@@ -0,0 +1,29 @@
+//
+// LRC.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/13.
+//
+
+import Foundation
+
+public struct LRC {
+
+ public static func calculateLRC(_ bytes: [UInt8]) -> UInt8 {
+ let sum = bytes.map({ Int($0) }).reduce(0) { partialResult, value in
+ partialResult + value
+ }
+
+ let lrc = sum % 256
+ return UInt8(lrc == 0 ? 0 : 256 - lrc)
+ }
+
+ public static func calculate(_ bytes: [UInt8]) -> UInt8 {
+ let sum = bytes.map({ Int($0) }).reduce(into: 0) { partialResult, value in
+ partialResult ^= value
+ }
+ return sum.byte
+ }
+}
+
+
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/Logger.swift b/code/IOTBluetooth/IOTBluetooth/Tools/Logger.swift
new file mode 100644
index 0000000..e3c23bc
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/Logger.swift
@@ -0,0 +1,69 @@
+//
+// Logger.swift
+//
+//
+// Created by Harvey on 2021/4/27.
+//
+
+import Foundation
+
+/// æ¥è®°ç®¡ç
+public class Logger {
+ static var isDebug: Bool = false
+
+ fileprivate static let shared = Logger()
+ private var fileHandle:FileHandle?
+ private init() {
+ let path = getPath()
+ fileHandle = FileHandle(forWritingAtPath: path)!
+// fileHandle?.seekToEndOfFile()
+ }
+
+ fileprivate var dateString: String { Date().format(.full) }
+
+ static var logs: [String] = []
+
+
+ private func getPath()->String{
+// let path = NSHomeDirectory() + "/Documents/IOTBluetooth/" + "\(Date().format(.middle)).log"
+ let foldPath = NSHomeDirectory() + "/Documents/IOTBluetooth/"
+ let fileManager = FileManager.default
+ if !fileManager.fileExists(atPath: foldPath) {
+ do {
+ try FileManager.default.createDirectory(atPath: foldPath, withIntermediateDirectories: true, attributes: nil)
+ } catch {
+ return ""
+ }
+ }
+ let path = foldPath + "\(Date().format(.long2)).log"
+ if !FileManager.default.fileExists(atPath: path) {
+ FileManager.default.createFile(atPath: path, contents: nil, attributes: nil)
+ }
+ return path
+ }
+}
+
+public extension Logger {
+ static func log(_ logs: Any..., line: Int = #line, separator: String = " ", terminator: String = "\n") {
+ objc_sync_enter(Logger.shared)
+ let logString = Logger.shared.dateString + " [IOTBluetooth-\(String(format: "%03d", line))]: " + logs.map({ "\($0)" }).joined(separator: separator) + terminator
+ if Logger.isDebug {
+ print(Logger.shared.dateString, terminator: " [IOTBluetooth-\(String(format: "%03d", line))]: ")
+ print(logs, separator: separator, terminator: terminator)
+ Self.logs.append(logString)
+ }
+ Logger.shared.saveLog(log: logString)
+ objc_sync_exit(Logger.shared)
+
+
+ }
+
+ private func saveLog(log:String){
+ fileHandle?.write(log.data(using: .utf8)!)
+// if #available(iOS 13.0, *) {
+// try? fileHandle.close()
+// } else {
+// // Fallback on earlier versions
+// }
+ }
+}
diff --git a/code/IOTBluetooth/IOTBluetooth/Tools/UTF8.swift b/code/IOTBluetooth/IOTBluetooth/Tools/UTF8.swift
new file mode 100644
index 0000000..a88e787
--- /dev/null
+++ b/code/IOTBluetooth/IOTBluetooth/Tools/UTF8.swift
@@ -0,0 +1,16 @@
+//
+// UTF8.swift
+// IOTBluetooth
+//
+// Created by yaozuopan on 2024/3/6.
+//
+
+import Foundation
+
+public extension Data {
+ var string: String? { String(data: self, encoding: .utf8) }
+}
+
+public extension String {
+ var data: Data? { self.data(using: .utf8) }
+}
diff --git a/document/2024.10.17 2ae6.docx b/document/2024.10.17 2ae6.docx
new file mode 100644
index 0000000..31fed24
Binary files /dev/null and b/document/2024.10.17 2ae6.docx differ
diff --git "a/document/\346\227\245\345\277\227/crashlog.crash" "b/document/\346\227\245\345\277\227/crashlog.crash"
new file mode 100644
index 0000000..7ef0126
--- /dev/null
+++ "b/document/\346\227\245\345\277\227/crashlog.crash"
@@ -0,0 +1,359 @@
+Incident Identifier: 3411013E-3426-431E-96C5-F1BE8887879B
+Distributor ID: com.apple.TestFlight
+Hardware Model: iPhone15,4
+Process: f002 [785]
+Path: /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/f002
+Identifier: com.fengeek.f002
+Version: 3.4.27 (50)
+AppStoreTools: 16B39
+AppVariant: 1:iPhone15,4:18
+Beta: YES
+Code Type: ARM-64 (Native)
+Role: Foreground
+Parent Process: launchd [1]
+Coalition: com.fengeek.f002 [677]
+
+Date/Time: 2024-11-04 16:05:00.8204 +0800
+Launch Time: 2024-11-04 15:36:23.8518 +0800
+OS Version: iPhone OS 18.0.1 (22A3370)
+Release Type: User
+Baseband Version: 2.16.06
+Report Version: 104
+
+Exception Type: EXC_BREAKPOINT (SIGTRAP)
+Exception Codes: 0x0000000000000001, 0x000000010a5abaa0
+Triggered by Thread: 16
+
+
+Thread 0 name:
+Thread 0:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 GraphicsServices 0x00000001d605e1c4 GSEventRunModal + 164 (GSEvent.c:2196)
+8 UIKitCore 0x000000018d11e2c0 -[UIApplication _run] + 816 (UIApplication.m:3825)
+9 UIKitCore 0x000000018d1ccddc UIApplicationMain + 340 (UIApplication.m:5477)
+10 f002 0x00000001058fc5e4 main + 64 (AppDelegate.swift:20)
+11 dyld 0x00000001afd9cd34 start + 2724 (dyldMain.cpp:1334)
+
+Thread 1 name:
+Thread 1:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 Foundation 0x000000018925f000 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 (NSRunLoop.m:373)
+8 Foundation 0x000000018925ee50 -[NSRunLoop(NSRunLoop) runUntilDate:] + 64 (NSRunLoop.m:420)
+9 UIKitCore 0x000000018d131714 -[UIEventFetcher threadMain] + 420 (UIEventFetcher.m:1241)
+10 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+11 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+12 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 2 name:
+Thread 2:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 Foundation 0x000000018925f000 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 (NSRunLoop.m:373)
+8 Foundation 0x000000018925eed4 -[NSRunLoop(NSRunLoop) run] + 64 (NSRunLoop.m:398)
+9 f002 0x0000000106329568 +[UMSocialTaskOperation networkRequestThreadEntryPoint:] + 184
+10 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+11 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+12 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 3 name:
+Thread 3:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 CoreFoundation 0x000000018a634850 CFRunLoopRun + 64 (CFRunLoop.c:3440)
+8 f002 0x0000000106108214 +[JCORECacheThread run] + 180 (JCORECacheThread.m:31)
+9 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+10 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+11 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 4 name:
+Thread 4:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 Foundation 0x000000018925f000 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 (NSRunLoop.m:373)
+8 Foundation 0x000000018925eed4 -[NSRunLoop(NSRunLoop) run] + 64 (NSRunLoop.m:398)
+9 f002 0x00000001060914c4 +[JCOREQueueManager threadEntryPoint:] + 216 (JCOREQueueManager.m:32)
+10 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+11 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+12 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 5 name:
+Thread 5:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 CoreFoundation 0x000000018a634850 CFRunLoopRun + 64 (CFRunLoop.c:3440)
+8 f002 0x00000001060a2590 +[JCORENetworkThread run] + 180 (JCORENetworkController.m:40)
+9 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+10 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+11 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 6:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x000000010735f3c0 internal_MSPThread_WaitMessages + 228
+4 f002 0x000000010735f2b4 MSPThread_WaitMessages + 228
+5 f002 0x00000001073757c0 socket_main + 224
+6 f002 0x00000001073605d0 ThreadEntryHook + 548
+7 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+8 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 7:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x00000001073254ec dns_main + 108
+4 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+5 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 8:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x000000010735f3c0 internal_MSPThread_WaitMessages + 228
+4 f002 0x000000010735f2b4 MSPThread_WaitMessages + 228
+5 f002 0x000000010738d27c iFlylua_engine_main + 2832
+6 f002 0x00000001073605d0 ThreadEntryHook + 548
+7 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+8 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 9:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x000000010735f3c0 internal_MSPThread_WaitMessages + 228
+4 f002 0x000000010735f2b4 MSPThread_WaitMessages + 228
+5 f002 0x000000010738d27c iFlylua_engine_main + 2832
+6 f002 0x00000001073605d0 ThreadEntryHook + 548
+7 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+8 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 10:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x000000010735f3c0 internal_MSPThread_WaitMessages + 228
+4 f002 0x000000010735f2b4 MSPThread_WaitMessages + 228
+5 f002 0x000000010738d27c iFlylua_engine_main + 2832
+6 f002 0x00000001073605d0 ThreadEntryHook + 548
+7 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+8 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 11:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x000000010735f3c0 internal_MSPThread_WaitMessages + 228
+4 f002 0x000000010735f2b4 MSPThread_WaitMessages + 228
+5 f002 0x000000010738d27c iFlylua_engine_main + 2832
+6 f002 0x00000001073605d0 ThreadEntryHook + 548
+7 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+8 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 12:
+0 libsystem_kernel.dylib 0x00000001da412030 __psynch_cvwait + 8 (:-1)
+1 libsystem_pthread.dylib 0x0000000211962a50 _pthread_cond_wait + 1204 (pthread_cond.c:862)
+2 f002 0x000000010735f084 native_event_wait + 208
+3 f002 0x000000010735f3c0 internal_MSPThread_WaitMessages + 228
+4 f002 0x000000010735f2b4 MSPThread_WaitMessages + 228
+5 f002 0x000000010738d27c iFlylua_engine_main + 2832
+6 f002 0x00000001073605d0 ThreadEntryHook + 548
+7 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+8 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 13 name:
+Thread 13:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 CFNetwork 0x000000018bb40f44 +[__CFN_CoreSchedulingSetRunnable _run:] + 416 (CoreSchedulingSet.mm:1473)
+8 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+9 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+10 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 14 name:
+Thread 14:
+0 libsystem_kernel.dylib 0x00000001da4142d4 __select + 8 (:-1)
+1 CoreFoundation 0x000000018a6324c0 __CFSocketManager + 704 (CFSocket.c:1341)
+2 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+3 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 15:
+0 libsystem_kernel.dylib 0x00000001da412250 __semwait_signal + 8 (:-1)
+1 libsystem_c.dylib 0x00000001922cba78 nanosleep + 220 (nanosleep.c:104)
+2 libsystem_c.dylib 0x0000000192329550 sleep + 52 (sleep.c:62)
+3 StarburstConnectKit 0x000000010a63705c _mqtt_process_thread + 56
+4 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+5 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 16 Crashed:
+0 StarburstConnectKit 0x000000010a5abaa0 MqttFileManager.outputProgress() + 980
+1 StarburstConnectKit 0x000000010a5a9110 MqttFileManager.onMessageArrival(topic:payload:) + 1180
+2 StarburstConnectKit 0x000000010a520824 AliCloudIotWatchManager_C.onMessageArrival(topic:payload:) + 440
+3 StarburstConnectKit 0x000000010a559d94 closure #2 in IotMqttManager_C.setupSdkWrapperCallback() + 416
+4 StarburstConnectKit 0x000000010a559e4c thunk for @escaping @callee_guaranteed (@guaranteed String, @guaranteed String) -> () + 88
+5 StarburstConnectKit 0x000000010a49d718 __30-[MqttManager_C setupCallback]_block_invoke_2 + 568
+6 StarburstConnectKit 0x000000010a636c94 _mqtt_default_recv_handler + 324
+7 StarburstConnectKit 0x000000010a63f158 _core_mqtt_pub_handler + 1464
+8 StarburstConnectKit 0x000000010a63e1b4 aiot_mqtt_recv + 1288
+9 StarburstConnectKit 0x000000010a63709c _mqtt_recv_thread + 40
+10 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+11 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 17:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 ASIHTTPRequest 0x00000001091f5670 +[ASIHTTPRequest runRequests] + 140 (ASIHTTPRequest.m:4797)
+8 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+9 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+10 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 18 name:
+Thread 18:
+0 libsystem_kernel.dylib 0x00000001da40c728 mach_msg2_trap + 8 (:-1)
+1 libsystem_kernel.dylib 0x00000001da40fe38 mach_msg2_internal + 80 (mach_msg.c:201)
+2 libsystem_kernel.dylib 0x00000001da40fd50 mach_msg_overwrite + 424 (mach_msg.c:0)
+3 libsystem_kernel.dylib 0x00000001da40fb9c mach_msg + 24 (mach_msg.c:323)
+4 CoreFoundation 0x000000018a5c97cc __CFRunLoopServiceMachPort + 160 (CFRunLoop.c:2617)
+5 CoreFoundation 0x000000018a5c8e78 __CFRunLoopRun + 1212 (CFRunLoop.c:3001)
+6 CoreFoundation 0x000000018a5c85b8 CFRunLoopRunSpecific + 572 (CFRunLoop.c:3414)
+7 Foundation 0x000000018925f000 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 (NSRunLoop.m:373)
+8 StarburstSdk 0x000000010bf22c34 -[SRRunLoopThread main] + 216
+9 Foundation 0x0000000189270158 __NSThread__start__ + 724 (NSThread.m:991)
+10 libsystem_pthread.dylib 0x000000021196537c _pthread_start + 136 (pthread.c:931)
+11 libsystem_pthread.dylib 0x0000000211960494 thread_start + 8 (:-1)
+
+Thread 19:
+0 libsystem_pthread.dylib 0x0000000211960480 start_wqthread + 0 (:-1)
+
+Thread 20:
+0 libsystem_pthread.dylib 0x0000000211960480 start_wqthread + 0 (:-1)
+
+Thread 21 name:
+Thread 21:
+0 libsystem_kernel.dylib 0x00000001da412250 __semwait_signal + 8 (:-1)
+1 libsystem_c.dylib 0x00000001922cba78 nanosleep + 220 (nanosleep.c:104)
+2 libsystem_c.dylib 0x0000000192329550 sleep + 52 (sleep.c:62)
+3 f002 0x00000001062c7c40 __48-[BLYLogicManager abortAfterSendingReportIfNeed]_block_invoke + 36
+4 libdispatch.dylib 0x0000000192270370 _dispatch_call_block_and_release + 32 (init.c:1549)
+5 libdispatch.dylib 0x00000001922720d0 _dispatch_client_callout + 20 (object.m:576)
+6 libdispatch.dylib 0x0000000192283f60 _dispatch_root_queue_drain + 860 (queue.c:7331)
+7 libdispatch.dylib 0x0000000192284590 _dispatch_worker_thread2 + 156 (queue.c:7399)
+8 libsystem_pthread.dylib 0x0000000211963c40 _pthread_wqthread + 228 (pthread.c:2709)
+9 libsystem_pthread.dylib 0x0000000211960488 start_wqthread + 8 (:-1)
+
+Thread 22:
+0 libsystem_pthread.dylib 0x0000000211960480 start_wqthread + 0 (:-1)
+
+Thread 23:
+0 libsystem_pthread.dylib 0x0000000211960480 start_wqthread + 0 (:-1)
+
+Thread 24:
+0 libsystem_pthread.dylib 0x0000000211960480 start_wqthread + 0 (:-1)
+
+Thread 25:
+0 libsystem_pthread.dylib 0x0000000211960480 start_wqthread + 0 (:-1)
+
+
+Thread 16 crashed with ARM Thread State (64-bit):
+ x0: 0xe000000000000000 x1: 0x0000000000000000 x2: 0x00000001edacf940 x3: 0x0000000188faad4c
+ x4: 0x00000001edaec480 x5: 0x000000016b40e688 x6: 0x000000016b40e680 x7: 0x0000000000000001
+ x8: 0x000000007fc00000 x9: 0x000000007f800000 x10: 0x0000000000000000 x11: 0xc000000000000000
+ x12: 0x0000000000000003 x13: 0x000000010a5abad8 x14: 0x000000010a5ab750 x15: 0x0000000000000038
+ x16: 0x000000018900926c x17: 0x871080018a57b310 x18: 0x0000000000000000 x19: 0x00000003035ea520
+ x20: 0x00000003035ea520 x21: 0x0000000000000018 x22: 0x40000003004f73c0 x23: 0x00000003035ea550
+ x24: 0x000000016b40e6d0 x25: 0x40000003035fd260 x26: 0xc00000000000003f x27: 0x0000000303aaf3a0
+ x28: 0x000000010a709650 fp: 0x000000016b40e6c0 lr: 0x000000010a5a9110
+ sp: 0x000000016b40e620 pc: 0x000000010a5abaa0 cpsr: 0x40001000
+ esr: 0x56000080 Address size fault
+
+
+Binary Images:
+ 0x104ca8000 - 0x107e3bfff f002 arm64 <0320a73500ea3635a097cc991f7d6a36> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/f002
+ 0x10907c000 - 0x109087fff libobjc-trampolines.dylib arm64e /private/preboot/Cryptexes/OS/usr/lib/libobjc-trampolines.dylib
+ 0x109168000 - 0x10916ffff DeviceUtil arm64 <5b7002a6e4ae393898153b49c2f30a09> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/DeviceUtil.framework/DeviceUtil
+ 0x109180000 - 0x109187fff KeychainItemWrapper_hicool arm64 <0aecce381ae03c38a0d9259834776667> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/KeychainItemWrapper_hicool.framework/KeychainItemWrapper_hicool
+ 0x109194000 - 0x1091affff CocoaLumberjack arm64 <210f1a7f805d396b806eeebf33043c82> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/CocoaLumberjack.framework/CocoaLumberjack
+ 0x1091dc000 - 0x10920ffff ASIHTTPRequest arm64 <19c5ad39057336ea8628b1bd076e7edc> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/ASIHTTPRequest.framework/ASIHTTPRequest
+ 0x109268000 - 0x10926ffff Reachability arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/Reachability.framework/Reachability
+ 0x109290000 - 0x10929ffff FMDB arm64 <0a4574e0c56939f8a3e37d31638c7f8b> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/FMDB.framework/FMDB
+ 0x1092b8000 - 0x1092c7fff MBProgressHUD arm64 <1b35240fbb303e9ebe22109e2e663b29> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/MBProgressHUD.framework/MBProgressHUD
+ 0x1092dc000 - 0x1092ebfff Masonry arm64 <4323d49838413f8586cd40ba8186f27d> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/Masonry.framework/Masonry
+ 0x1093b8000 - 0x1093dffff HandyJSON arm64 <14b18b15e89e3d93a186a51d7560c99a> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/HandyJSON.framework/HandyJSON
+ 0x109428000 - 0x10943ffff MJRefresh arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/MJRefresh.framework/MJRefresh
+ 0x109464000 - 0x10946bfff RxRelay arm64 <7fa90ff5197b3f7ebdc082a2ce376a9b> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/RxRelay.framework/RxRelay
+ 0x109480000 - 0x10948bfff WebViewJavascriptBridge arm64 <56de5e334eb230ff8b976207dc0cf3e1> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/WebViewJavascriptBridge.framework/WebViewJavascriptBridge
+ 0x1094bc000 - 0x109517fff Alamofire arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/Alamofire.framework/Alamofire
+ 0x1095c0000 - 0x1095cffff FOTA arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/FOTA.framework/FOTA
+ 0x109608000 - 0x109623fff SDWebImage arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/SDWebImage.framework/SDWebImage
+ 0x10966c000 - 0x1096d3fff Logan arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/Logan.framework/Logan
+ 0x109718000 - 0x10976bfff OpusKit arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/OpusKit.framework/OpusKit
+ 0x1097c4000 - 0x1097c7fff iCloudDriveFileProviderOverride arm64e /System/Library/Frameworks/FileProvider.framework/OverrideBundles/iCloudDriveFileProviderOverride.bundle/iCloudDriveFileProviderOverride
+ 0x1097d8000 - 0x1097effff DequeModule arm64 <1ee643420f0e35c2bcc4d7f17ba3246d> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/DequeModule.framework/DequeModule
+ 0x109890000 - 0x1098c7fff DeviceManager arm64 <01f9ebcc6b43309dbfcf690e05e759ce> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/DeviceManager.framework/DeviceManager
+ 0x1098f8000 - 0x10996bfff RxCocoa arm64 <700265132423300abd58e67fec50fec4> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/RxCocoa.framework/RxCocoa
+ 0x109a6c000 - 0x109ae3fff SQLite arm64 <213c6061424036b7a3ae409de50876b0> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/SQLite.framework/SQLite
+ 0x109c6c000 - 0x109d1bfff RxSwift arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/RxSwift.framework/RxSwift
+ 0x109e74000 - 0x109e9ffff CocoaAsyncSocket arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/CocoaAsyncSocket.framework/CocoaAsyncSocket
+ 0x109ff8000 - 0x109ffffff FileProviderOverride arm64e <39396754b75931aab410d42869570a7f> /System/Library/Frameworks/FileProvider.framework/OverrideBundles/FileProviderOverride.bundle/FileProviderOverride
+ 0x10a010000 - 0x10a093fff FIILBluetoothSDK arm64 <57216117281d3a8097dc3b77cd08f8c1> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/FIILBluetoothSDK.framework/FIILBluetoothSDK
+ 0x10a488000 - 0x10a6bffff StarburstConnectKit arm64 <4d919c9bb7ef35b89df0c74d01609e6c> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/StarburstConnectKit.framework/StarburstConnectKit
+ 0x10aaac000 - 0x10ab9bfff Airoha1530 arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/Airoha1530.framework/Airoha1530
+ 0x10bf0c000 - 0x10c08bfff StarburstSdk arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/StarburstSdk.framework/StarburstSdk
+ 0x10c654000 - 0x10c86ffff SpeechSDK arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/SpeechSDK.framework/SpeechSDK
+ 0x10ca00000 - 0x10ca0bfff NativePeq arm64 <19f476b9c5af33bdb0d97bf148547821> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/NativePeq.framework/NativePeq
+ 0x110660000 - 0x110687fff NativeAnc arm64 <572dbe6843623f2e8138893fbba3528b> /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/NativeAnc.framework/NativeAnc
+ 0x116708000 - 0x116e5ffff AirohaUnifyLibrary arm64 /private/var/containers/Bundle/Application/CE756EE5-0E65-4D5D-BAF6-684565D7958B/f002.app/Frameworks/AirohaUnifyLibrary.framework/AirohaUnifyLibrary
+ 0x188bd0000 - 0x18916ffff libswiftCore.dylib arm64e <24ec482a274c32f2aa6da809fabfdc58> /usr/lib/swift/libswiftCore.dylib
+ 0x1891a8000 - 0x189ec6fff Foundation arm64e <6d058a2d05cf3a298bebb1030ae97282> /System/Library/Frameworks/Foundation.framework/Foundation
+ 0x18a575000 - 0x18aabafff CoreFoundation arm64e <4250d45c51a93ac58c3d69de936fad46> /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
+ 0x18ba43000 - 0x18be05fff CFNetwork arm64e <7d799a941e3c3a4fb1a0d9414e8eaca5> /System/Library/Frameworks/CFNetwork.framework/CFNetwork
+ 0x18cd40000 - 0x18ec00fff UIKitCore arm64e <187c890e8e4c32f2a97efa905a55f094> /System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore
+ 0x19226e000 - 0x1922b3fff libdispatch.dylib arm64e /usr/lib/system/libdispatch.dylib
+ 0x1922b4000 - 0x192333ff3 libsystem_c.dylib arm64e <1ad666a001a831c188b9808915fb97b4> /usr/lib/system/libsystem_c.dylib
+ 0x1afd69000 - 0x1afdec693 dyld arm64e <5db839882ee63756bd07b8d67b1133a5> /usr/lib/dyld
+ 0x1d605d000 - 0x1d6065fff GraphicsServices arm64e <227122eb72fd3ecb9f66986addd8e048> /System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
+ 0x1da40b000 - 0x1da444ffb libsystem_kernel.dylib arm64e <8d929b3bd4d639bfadbbbe59e928b332> /usr/lib/system/libsystem_kernel.dylib
+ 0x21195f000 - 0x21196bff3 libsystem_pthread.dylib arm64e /usr/lib/system/libsystem_pthread.dylib
+
+EOF