| @@ -7,6 +7,18 @@ | |||
| objects = { | |||
| /* Begin PBXBuildFile section */ | |||
| 7802C5AD2C903A31008D7616 /* ELMeatProbeCustomDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */; }; | |||
| 7802C5B02C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */; }; | |||
| 7802C5C92C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5B22C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m */; }; | |||
| 7802C5CA2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5B42C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m */; }; | |||
| 7802C5CB2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5B72C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m */; }; | |||
| 7802C5CC2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5BA2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m */; }; | |||
| 7802C5CD2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7802C5BB2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib */; }; | |||
| 7802C5CE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5BE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m */; }; | |||
| 7802C5CF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5C02C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m */; }; | |||
| 7802C5D02C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5C32C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m */; }; | |||
| 7802C5D12C903A7F008D7616 /* MeatProbeScreenBoxVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7802C5C62C903A7F008D7616 /* MeatProbeScreenBoxVC.m */; }; | |||
| 7802C5D22C903A7F008D7616 /* MeatProbeScreenBoxVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7802C5C72C903A7F008D7616 /* MeatProbeScreenBoxVC.xib */; }; | |||
| 78350BB329D2826900456695 /* libHTBodyfat_SDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB129D2826900456695 /* libHTBodyfat_SDK.a */; }; | |||
| 78350BB729D282A300456695 /* libBhBodyComposition120.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB429D282A300456695 /* libBhBodyComposition120.a */; }; | |||
| 78350BBB29D2869000456695 /* BodyScaleVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78350BB929D2869000456695 /* BodyScaleVC.m */; }; | |||
| @@ -120,6 +132,30 @@ | |||
| /* End PBXContainerItemProxy section */ | |||
| /* Begin PBXFileReference section */ | |||
| 7802C5AB2C903A31008D7616 /* ELMeatProbeCustomDataModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeCustomDataModel.h; sourceTree = "<group>"; }; | |||
| 7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeCustomDataModel.m; sourceTree = "<group>"; }; | |||
| 7802C5AE2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxCustomDataModel.h; sourceTree = "<group>"; }; | |||
| 7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxCustomDataModel.m; sourceTree = "<group>"; }; | |||
| 7802C5B12C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleBBQParamModel.h; sourceTree = "<group>"; }; | |||
| 7802C5B22C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxMultipleBleBBQParamModel.m; sourceTree = "<group>"; }; | |||
| 7802C5B32C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleCmdTool.h; sourceTree = "<group>"; }; | |||
| 7802C5B42C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxMultipleBleCmdTool.m; sourceTree = "<group>"; }; | |||
| 7802C5B52C903A7F008D7616 /* ELMeatProbeBoxMultipleBleHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleHeader.h; sourceTree = "<group>"; }; | |||
| 7802C5B62C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxMultipleBleParser.h; sourceTree = "<group>"; }; | |||
| 7802C5B72C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxMultipleBleParser.m; sourceTree = "<group>"; }; | |||
| 7802C5B92C903A7F008D7616 /* MultipleMeatProbeBoxVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultipleMeatProbeBoxVC.h; sourceTree = "<group>"; }; | |||
| 7802C5BA2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultipleMeatProbeBoxVC.m; sourceTree = "<group>"; }; | |||
| 7802C5BB2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MultipleMeatProbeBoxVC.xib; sourceTree = "<group>"; }; | |||
| 7802C5BD2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleBBQParamModel.h; sourceTree = "<group>"; }; | |||
| 7802C5BE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeScreenBoxBleBBQParamModel.m; sourceTree = "<group>"; }; | |||
| 7802C5BF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleCmdTool.h; sourceTree = "<group>"; }; | |||
| 7802C5C02C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeScreenBoxBleCmdTool.m; sourceTree = "<group>"; }; | |||
| 7802C5C12C903A7F008D7616 /* ELMeatProbeScreenBoxBleHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleHeader.h; sourceTree = "<group>"; }; | |||
| 7802C5C22C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeScreenBoxBleParser.h; sourceTree = "<group>"; }; | |||
| 7802C5C32C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeScreenBoxBleParser.m; sourceTree = "<group>"; }; | |||
| 7802C5C52C903A7F008D7616 /* MeatProbeScreenBoxVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeatProbeScreenBoxVC.h; sourceTree = "<group>"; }; | |||
| 7802C5C62C903A7F008D7616 /* MeatProbeScreenBoxVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeatProbeScreenBoxVC.m; sourceTree = "<group>"; }; | |||
| 7802C5C72C903A7F008D7616 /* MeatProbeScreenBoxVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MeatProbeScreenBoxVC.xib; sourceTree = "<group>"; }; | |||
| 78350BB129D2826900456695 /* libHTBodyfat_SDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libHTBodyfat_SDK.a; sourceTree = "<group>"; }; | |||
| 78350BB229D2826900456695 /* HTBodyfat_SDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTBodyfat_SDK.h; sourceTree = "<group>"; }; | |||
| 78350BB429D282A300456695 /* libBhBodyComposition120.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libBhBodyComposition120.a; sourceTree = "<group>"; }; | |||
| @@ -337,6 +373,56 @@ | |||
| /* End PBXFrameworksBuildPhase section */ | |||
| /* Begin PBXGroup section */ | |||
| 7802C5B82C903A7F008D7616 /* BLE */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 7802C5B12C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.h */, | |||
| 7802C5B22C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m */, | |||
| 7802C5B32C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.h */, | |||
| 7802C5B42C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m */, | |||
| 7802C5B52C903A7F008D7616 /* ELMeatProbeBoxMultipleBleHeader.h */, | |||
| 7802C5B62C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.h */, | |||
| 7802C5B72C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m */, | |||
| ); | |||
| path = BLE; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 7802C5BC2C903A7F008D7616 /* 27C_MultipleMeatProbeBox */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 7802C5B82C903A7F008D7616 /* BLE */, | |||
| 7802C5B92C903A7F008D7616 /* MultipleMeatProbeBoxVC.h */, | |||
| 7802C5BA2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m */, | |||
| 7802C5BB2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib */, | |||
| ); | |||
| path = 27C_MultipleMeatProbeBox; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 7802C5C42C903A7F008D7616 /* BLE */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 7802C5BD2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.h */, | |||
| 7802C5BE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m */, | |||
| 7802C5BF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.h */, | |||
| 7802C5C02C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m */, | |||
| 7802C5C12C903A7F008D7616 /* ELMeatProbeScreenBoxBleHeader.h */, | |||
| 7802C5C22C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.h */, | |||
| 7802C5C32C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m */, | |||
| ); | |||
| path = BLE; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 7802C5C82C903A7F008D7616 /* 27B_MeatProbeScreenBox */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 7802C5C42C903A7F008D7616 /* BLE */, | |||
| 7802C5C52C903A7F008D7616 /* MeatProbeScreenBoxVC.h */, | |||
| 7802C5C62C903A7F008D7616 /* MeatProbeScreenBoxVC.m */, | |||
| 7802C5C72C903A7F008D7616 /* MeatProbeScreenBoxVC.xib */, | |||
| ); | |||
| path = 27B_MeatProbeScreenBox; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 78350BB029D280D300456695 /* DeviceDemo */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| @@ -367,6 +453,8 @@ | |||
| 78DC4DF72990FC10006C6911 /* 25_HeightBodyFatScale */, | |||
| 78E07B6C2A49664D00E68200 /* 26_MeatProbe */, | |||
| 78E07B702A49664E00E68200 /* 27_MeatProbeBox */, | |||
| 7802C5C82C903A7F008D7616 /* 27B_MeatProbeScreenBox */, | |||
| 7802C5BC2C903A7F008D7616 /* 27C_MultipleMeatProbeBox */, | |||
| 78CF43522C2EC21A00D0A165 /* 28_Hygrothermograph */, | |||
| ); | |||
| path = DeviceDemo; | |||
| @@ -471,6 +559,8 @@ | |||
| 78E07B6F2A49664D00E68200 /* MeatProbeVC.h */, | |||
| 78E07B6D2A49664D00E68200 /* MeatProbeVC.m */, | |||
| 78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */, | |||
| 7802C5AB2C903A31008D7616 /* ELMeatProbeCustomDataModel.h */, | |||
| 7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */, | |||
| ); | |||
| path = 26_MeatProbe; | |||
| sourceTree = "<group>"; | |||
| @@ -494,6 +584,8 @@ | |||
| 78E07B732A49664E00E68200 /* ELMeatProbeBoxBleHeader.h */, | |||
| 78E07B722A49664E00E68200 /* ELMeatProbeBoxBleParser.h */, | |||
| 78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */, | |||
| 7802C5AE2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.h */, | |||
| 7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */, | |||
| ); | |||
| path = Ble; | |||
| sourceTree = "<group>"; | |||
| @@ -977,8 +1069,10 @@ | |||
| 78DC4DE32990FB2E006C6911 /* ELDemoScanVC.xib in Resources */, | |||
| E622E26E243F1CA6003C319D /* LaunchScreen.storyboard in Resources */, | |||
| 78CF43542C2EC21B00D0A165 /* HygrothermographVC.xib in Resources */, | |||
| 7802C5CD2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib in Resources */, | |||
| E622E26B243F1CA6003C319D /* Assets.xcassets in Resources */, | |||
| 78AC3B2B2C0D994900A62C61 /* PrivacyInfo.bundle in Resources */, | |||
| 7802C5D22C903A7F008D7616 /* MeatProbeScreenBoxVC.xib in Resources */, | |||
| 78DC4DF62990FB40006C6911 /* ELDeviceScanCell.xib in Resources */, | |||
| 78E07B7B2A49664E00E68200 /* MeatProbeVC.xib in Resources */, | |||
| 78350BBD29D28C1200456695 /* README.md in Resources */, | |||
| @@ -1005,8 +1099,10 @@ | |||
| 95565B7A2786EA3B000265EA /* SkipScanViewController.m in Sources */, | |||
| 78DC4DFB2990FC10006C6911 /* HeightBodyFatScaleVC.m in Sources */, | |||
| 9557263B276C38CD003BFBAC /* FaceMaskScanViewController.m in Sources */, | |||
| 7802C5D02C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m in Sources */, | |||
| 9557263F276C38CD003BFBAC /* EightScaleConnectionViewController.m in Sources */, | |||
| 9557262B276C38CD003BFBAC /* WheelMonitorScanViewController.m in Sources */, | |||
| 7802C5AD2C903A31008D7616 /* ELMeatProbeCustomDataModel.m in Sources */, | |||
| 95572629276C38CD003BFBAC /* BloodSugarConnectionViewController.m in Sources */, | |||
| 78DC4DE52990FB2E006C6911 /* ELDemoScanVC.m in Sources */, | |||
| 9557262A276C38CD003BFBAC /* WheelMonitorConnectViewController.m in Sources */, | |||
| @@ -1021,6 +1117,7 @@ | |||
| 78DC4DF22990FB40006C6911 /* ELDeviceTypeCell.m in Sources */, | |||
| 78DC4DF52990FB40006C6911 /* ELDeviceScanCell.m in Sources */, | |||
| 78DC4DF12990FB40006C6911 /* ELDemoView.m in Sources */, | |||
| 7802C5B02C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m in Sources */, | |||
| 95572626276C38CD003BFBAC /* AiFreshNutritionScaleConnectionViewController.m in Sources */, | |||
| 9557262F276C38CD003BFBAC /* ELBfsWifiConnectTableViewCell.m in Sources */, | |||
| 95572623276C38CD003BFBAC /* BloodConnectViewController.m in Sources */, | |||
| @@ -1032,6 +1129,7 @@ | |||
| 958B4EB5263C08E800476BAE /* MASViewConstraint.m in Sources */, | |||
| 95572628276C38CD003BFBAC /* BloodSugarScanViewController.m in Sources */, | |||
| 9557264C276C38CD003BFBAC /* RemoteControlScanViewController.m in Sources */, | |||
| 7802C5D12C903A7F008D7616 /* MeatProbeScreenBoxVC.m in Sources */, | |||
| 9557262C276C38CD003BFBAC /* BroadcastNutritionScaleViewController.m in Sources */, | |||
| 95572643276C38CD003BFBAC /* BodyFatScaleScanViewController.m in Sources */, | |||
| 95572627276C38CD003BFBAC /* BroadcastScaleViewController.m in Sources */, | |||
| @@ -1049,23 +1147,29 @@ | |||
| E622E271243F1CA6003C319D /* main.m in Sources */, | |||
| 78E07B7F2A49664E00E68200 /* MeatProbeBoxVC.m in Sources */, | |||
| 95572646276C38CD003BFBAC /* FoodThermometerConnectionViewController.m in Sources */, | |||
| 7802C5CE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m in Sources */, | |||
| 958B4EB3263C08E800476BAE /* View+MASAdditions.m in Sources */, | |||
| 958B4EB1263C08E800476BAE /* MASLayoutConstraint.m in Sources */, | |||
| 9557263A276C38CD003BFBAC /* FaceMaskConnectionViewController.m in Sources */, | |||
| 95572636276C38CD003BFBAC /* OximeterScanViewController.m in Sources */, | |||
| 78E07B7D2A49664E00E68200 /* ELMeatProbeBoxBleParser.m in Sources */, | |||
| 958B4EA4263C08E800476BAE /* ELInputAlertView.m in Sources */, | |||
| 7802C5CC2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m in Sources */, | |||
| 958B4EB6263C08E800476BAE /* MASViewAttribute.m in Sources */, | |||
| 958B4EBB263C08E800476BAE /* InheritConnectViewController.m in Sources */, | |||
| 958B4EB4263C08E800476BAE /* MASConstraint.m in Sources */, | |||
| 7802C5CA2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m in Sources */, | |||
| 958B4EAA263C08E800476BAE /* AilinkSuperConnectViewController.m in Sources */, | |||
| 958B4EB0263C08E800476BAE /* MASConstraintMaker.m in Sources */, | |||
| 7802C5C92C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m in Sources */, | |||
| 78DC4DF32990FB40006C6911 /* ELDemoButton.m in Sources */, | |||
| 7802C5CF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m in Sources */, | |||
| 958B4EA5263C08E800476BAE /* UILabel+WZAdd.m in Sources */, | |||
| 95572633276C38CD003BFBAC /* BabyScaleConnectViewController.m in Sources */, | |||
| 78E07B7A2A49664E00E68200 /* MeatProbeVC.m in Sources */, | |||
| 95572644276C38CD003BFBAC /* BodyFatScaleConnectViewController.m in Sources */, | |||
| 958B4EBD263C08E800476BAE /* AppDelegate.m in Sources */, | |||
| 7802C5CB2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m in Sources */, | |||
| 95565B792786EA3B000265EA /* SkipConnectionViewController.m in Sources */, | |||
| 958B4EAE263C08E800476BAE /* ViewController+MASAdditions.m in Sources */, | |||
| 958B4EA8263C08E800476BAE /* CABasicAnimation+ELAnimation.m in Sources */, | |||
| @@ -1233,7 +1337,7 @@ | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = 20240628; | |||
| CURRENT_PROJECT_VERSION = 20240910; | |||
| DEVELOPMENT_TEAM = YEU77XR292; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| @@ -1255,7 +1359,7 @@ | |||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | |||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | |||
| ); | |||
| MARKETING_VERSION = 1.9.2; | |||
| MARKETING_VERSION = 1.10.0; | |||
| OTHER_LDFLAGS = "-ObjC"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| @@ -1268,7 +1372,7 @@ | |||
| buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = 20240628; | |||
| CURRENT_PROJECT_VERSION = 20240910; | |||
| DEVELOPMENT_TEAM = YEU77XR292; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| @@ -1290,7 +1394,7 @@ | |||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | |||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | |||
| ); | |||
| MARKETING_VERSION = 1.9.2; | |||
| MARKETING_VERSION = 1.10.0; | |||
| OTHER_LDFLAGS = "-ObjC"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| @@ -9,6 +9,6 @@ | |||
| #define AILinkBleSDK_Version_h | |||
| //版本号 SDK Version | |||
| #define AiLinkBleSDKVersion @"1.9.0" // 20240603 | |||
| #define AiLinkBleSDKVersion @"1.10.0" // 20240910 | |||
| #endif /* AILinkBleSDK_Version_h */ | |||
| @@ -70,6 +70,8 @@ typedef NS_ENUM(NSInteger , ELBodyFatScaleBleDataType) { | |||
| ELBodyFatScaleBleDataType_MCUBackHeartRateMeasureFailed = 0x0D , | |||
| /** MCU->APP:MCU请求补全体脂数据 */ | |||
| ELBodyFatScaleBleDataType_MCUBackNeedsFillBodyData = 0x0E , | |||
| /** MCU->APP:设备上报模式 */ | |||
| ELBodyFatScaleBleDataType_MCUReportingMode = 0x0F , | |||
| /** App发送指令切换单位 */ | |||
| ELBodyFatScaleBleDataType_AppSendUnit = 0x81 , | |||
| @@ -114,6 +114,10 @@ MCU requests user information( MCU 请求用户信息) | |||
| */ | |||
| - (void)bodyFatScaleManagerCallBackSetIPAddressResult:(NSInteger)result; | |||
| /** MCU reporting mode (MCU上报模式 ) | |||
| * @param mode : 0:体脂模式(默认),1:体重模式(没有阻抗和体脂数据) | |||
| */ | |||
| - (void)bodyFatScaleManagerCallBackReportingMode:(NSInteger)mode; | |||
| @end | |||
| @@ -14,6 +14,14 @@ typedef NS_ENUM(NSInteger, PHSex) { | |||
| PHSex_MALE = 1,//男 | |||
| }; | |||
| //矿物质重量 | |||
| typedef NS_ENUM(NSUInteger, AILinkMineralWeightType) { | |||
| AILinkMineralWeightTypeUnknown = 0,//未知 | |||
| AILinkMineralWeightTypeInsufficient = 1,//不足 | |||
| AILinkMineralWeightTypeStandard = 2,//标准 | |||
| AILinkMineralWeightTypeExcellent = 3,//偏高 | |||
| }; | |||
| //四肢肌肉指数 | |||
| typedef NS_ENUM(NSUInteger, AILinkLimbMuscleIndexType) { | |||
| @@ -158,6 +166,7 @@ NS_ASSUME_NONNULL_BEGIN | |||
| @interface ELBodyIndexAlgorithmModel : NSObject | |||
| #pragma mark ============ 标准判断临界值数组 ============== | |||
| /// 四肢肌肉指数 标准判断 | |||
| /// @param sex 性别 | |||
| /// @param cmHeight 身高(cm) | |||
| @@ -233,6 +242,15 @@ NS_ASSUME_NONNULL_BEGIN | |||
| /// @param age 年龄 | |||
| + (NSArray<NSString *> *)getSkeletalMuscleMassScopeSex:(PHSex)sex age:(float)age; | |||
| /// 水分重量 标准判断2个临界值 | |||
| /// @param sex 性别 | |||
| /// @param weight 体重 | |||
| + (NSArray<NSString *> *)getWaterWeightScopeSex:(PHSex)sex weight:(float)weight; | |||
| /// 皮下脂肪量 标准判断2个临界值 | |||
| /// @param sex 性别 | |||
| /// @param weight 体重 | |||
| + (NSArray<NSString *> *)getSubcutaneousFatMassScopeSex:(PHSex)sex weight:(float)weight; | |||
| #pragma mark ============ 判断标准数组 ============== | |||
| //四肢肌肉指数标准词条 | |||
| +(NSArray<NSNumber *> *)getLimbMuscleIndexStandardArray; | |||
| @@ -422,6 +440,19 @@ NS_ASSUME_NONNULL_BEGIN | |||
| /// @param weight 体重(KG) | |||
| /// @param standardWeight 标准体重(KG) | |||
| + (AILinkFatLevelType)getfatLevelWithweight:(double)weight andStandardWeight:(double)standardWeight; | |||
| #pragma mark - 新增指标 | |||
| /** | |||
| * 矿物质重量 标准判断 | |||
| * | |||
| * @param mineralWeightValue 体重(KG) | |||
| * @param weight 体重 | |||
| * | |||
| * @return 不足、标准、偏高、Null | |||
| */ | |||
| + (AILinkMineralWeightType)getMineralWeightType:(float)mineralWeightValue; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -21,6 +21,8 @@ typedef NS_ENUM(NSUInteger, BroadcastScaleTestStatus) { | |||
| BroadcastScaleTestStatusADCTestSuccess, | |||
| //Impedance measurement failed (the impedance value is 0xFFFF at this time)(阻抗测量失败(此时阻抗数值为 0xFFFF)) | |||
| BroadcastScaleTestStatusADCTestFailed, | |||
| //Weight mode (skip impedance measurement, the impedance value is 0, and the APP will not prompt impedance failure)( 体重模式(跳过阻抗测量,此时阻抗数值为 0, APP不会提示阻抗失败的提示)) | |||
| BroadcastScaleTestStatusWeightMode, | |||
| //End of test(测试结束) | |||
| BroadcastScaleTestStatusTestEnd = 0xFF, | |||
| }; | |||
| @@ -70,7 +70,8 @@ NS_ASSUME_NONNULL_BEGIN | |||
| ///MCU返回设备测量信息(测量完成的时候才会回调该方法) | |||
| - (void)eightScaleBleReportTestData:(ELEightScaleBleDataModel *)dataModle bfrData:(ELEightScaleBleBfrDataModel *)bfrDataModle; | |||
| // 双频八电极mcu测量完成返回数据(测量完成的时候才会回调该方法) | |||
| - (void)eightScaleBle0x0068ReportTestData:(ELEightScaleBleDataModel *)dataModel bodyFatData:(ELEightScaleBleBfrDataModel *)bfrDataModel; | |||
| /** MCU返回操作结果 */ | |||
| - (void)eightScaleBleReportOperationWithType:(enum EightScaleOperationType)type unitResult:(enum EightScaleSwitchUnitResult)result; | |||
| @@ -82,7 +83,14 @@ NS_ASSUME_NONNULL_BEGIN | |||
| ///MCU请求补全体脂数据 | |||
| - (void)eightScaleBleCompletionBodyFatData; | |||
| - (void)eightScaleBleCompletionBodyFatData:(ELEightScaleBleDataModel *)dataModel; | |||
| /** | |||
| Back to impedance measurement(返回阻抗测量) | |||
| @param status 阻抗测量状态 | |||
| */ | |||
| -(void)eightScaleScaleManagerCallBackAdcMeasureStatus:(DualBandEightScaleAdcMeasureStatus)status; | |||
| @end | |||
| @interface ELEightScaleBleManager : ELBluetoothManager | |||
| @@ -164,4 +164,13 @@ typedef NS_ENUM(NSUInteger, EightScaleErrorCodeType) { | |||
| EightScaleErrorCodeType_Overweight = 0x01 , | |||
| } ; | |||
| //Impedance measurement process(阻抗测量过程) 双频八电极 | |||
| typedef NS_ENUM(NSUInteger, DualBandEightScaleAdcMeasureStatus) { | |||
| DualBandEightScaleAdcMeasureStatus_Testing = 0x04,//测阻抗中 | |||
| DualBandEightScaleAdcMeasureStatus_Fail = 0x06,//测阻抗失败 | |||
| DualBandEightScaleAdcMeasureStatus_SuccessCustom = 0x07,//The impedance measurement is successful, and the APP algorithm is used. The APP will identify the corresponding algorithm according to VID and PID(测阻抗成功,并使用 APP 算法,APP 会根据 VID,PID 来识 别对应算法) | |||
| }; | |||
| #endif /* ELEightScaleSDKHeader_h */ | |||
| @@ -119,7 +119,7 @@ typedef NS_ENUM(NSUInteger, ELSupportDeviceType) { | |||
| ELSupportDeviceTypeBroadcastScale_T6 = (0xffff + 0x09) , //65544 T6广播体脂秤 | |||
| ELSupportDeviceType_dualFrequencyEightelectrodeMCUAlgorithm = 0x0068, //双频八电极MCU算法秤 | |||
| }; | |||
| #endif /* ELExSupportDeviceType_h */ | |||
| @@ -195,6 +195,15 @@ typedef NS_ENUM(NSInteger, TH_Dev_SupportFeatures) { | |||
| TH_Dev_SupportFeatures_GroundLight = 5 , | |||
| /// 寻物功能(APP点击寻物时,设备蜂鸣器会响) | |||
| TH_Dev_SupportFeatures_Lost = 6 , | |||
| /// 接收 APP 下发指令 | |||
| TH_Dev_SupportFeatures_ReceiveCommand = 7 , | |||
| /// 温度单位切换 | |||
| TH_Dev_SupportFeatures_TemperatureUnitSwitch = 8 , | |||
| /// 温度报警 | |||
| TH_Dev_SupportFeatures_TemperatureAlarm = 9 , | |||
| /// 湿度报警 | |||
| TH_Dev_SupportFeatures_HumidityAlarm = 10 , | |||
| }; | |||
| @@ -39,7 +39,9 @@ NS_ASSUME_NONNULL_BEGIN | |||
| /// @param unit 温度单位 | |||
| /// @param hum 湿度,单位% | |||
| /// @param state 当前设备状态,详细内容见枚举注释 | |||
| - (void)newTHManagerReturnDevStatePower:(int)power bootTime:(long long)time tem:(float)tem unit:(enum ELDeviceTemperatureUnit)unit hum:(float)hum devState:(enum TH_DevStateType)state; | |||
| /// @param isCharging 设备充电状态 默认是关闭状态 | |||
| - (void)newTHManagerReturnDevStatePower:(int)power bootTime:(long long)time tem:(float)tem unit:(enum ELDeviceTemperatureUnit)unit hum:(float)hum devState:(enum TH_DevStateType)state isCharging:(BOOL)isCharging; | |||
| /** 设备返回阀值 | |||
| @@ -269,6 +271,15 @@ NS_ASSUME_NONNULL_BEGIN | |||
| // App绑定设备 完成 | |||
| - (void)bindDeviceDone; | |||
| /// 解析A7指令 | |||
| - (void)processA7Data:(NSData *)rawData withMac:(NSData *)mac withCid:(UInt16)cid; | |||
| /// 解析A6指令 | |||
| - (void)processA6Data:(NSData *)rawData; | |||
| /// 生成A7数据包 | |||
| - (NSData * _Nullable)makePacketA7Payload:(NSData *)payload withMac:(NSData *)mac withCid:(UInt16)cid; | |||
| /// 生成A6数据包 | |||
| - (NSData * _Nullable)makePacketA6Payload:(NSData *)payload; | |||
| @end | |||
| @@ -0,0 +1,22 @@ | |||
| { | |||
| "images" : [ | |||
| { | |||
| "idiom" : "universal", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "filename" : "aibbq_add_equipment_probe_100_ic@2x.png", | |||
| "idiom" : "universal", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "filename" : "aibbq_add_equipment_probe_100_ic@3x.png", | |||
| "idiom" : "universal", | |||
| "scale" : "3x" | |||
| } | |||
| ], | |||
| "info" : { | |||
| "author" : "xcode", | |||
| "version" : 1 | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| { | |||
| "images" : [ | |||
| { | |||
| "idiom" : "universal", | |||
| "scale" : "1x" | |||
| }, | |||
| { | |||
| "filename" : "ailink_cid85_banding_cid_0062_100_ic@2x.png", | |||
| "idiom" : "universal", | |||
| "scale" : "2x" | |||
| }, | |||
| { | |||
| "filename" : "ailink_cid85_banding_cid_0062_100_ic@3x.png", | |||
| "idiom" : "universal", | |||
| "scale" : "3x" | |||
| } | |||
| ], | |||
| "info" : { | |||
| "author" : "xcode", | |||
| "version" : 1 | |||
| } | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| // | |||
| // ELMeatProbeCustomDataModel.h | |||
| // AILinkBleSDK | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| #import <AILinkBleSDK/ELMeatProbeBleTool.h> | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| #pragma pack(1) | |||
| typedef struct ELMeatProbeCustomDataStruct { | |||
| UInt8 ver; //数据版本 (当前版本 0x02) //数据总长度: modelSize() | |||
| UInt32 cookingId; //烧烤id (选择食物的时间戳) | |||
| UInt8 foodType; //食物类型 | |||
| UInt8 foodRawness; //食物熟度 | |||
| SInt16 targetTemperature_C; //食物目标温度:℃ | |||
| SInt16 targetTemperature_F; //食物目标温度:℉ | |||
| SInt16 ambientMinTemperature_C; //炉温目标下限:℃ | |||
| SInt16 ambientMinTemperature_F; //炉温目标下限:℉ | |||
| SInt16 ambientMaxTemperature_C; //炉温目标上限:℃ | |||
| SInt16 ambientMaxTemperature_F; //炉温目标上限:℉ | |||
| double alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0 | |||
| UInt32 timerStart;//计时开始时间戳 | |||
| UInt32 timerEnd;//计时结束时间戳 | |||
| UInt8 currentUnit; //当前温度单位 | |||
| SInt16 alarmTemperature_C; //食物提醒温度:℃ | |||
| SInt16 alarmTemperature_F; //食物提醒温度:℉ | |||
| UInt8 remarkBytes[32]; //UTF-8 编码 | |||
| } ELMeatProbeCustomDataStruct; | |||
| #pragma pack() | |||
| /// | |||
| @interface ELMeatProbeCustomDataModel : NSObject | |||
| @property (nonatomic, assign) ELMeatProbeCustomDataStruct customStruct; | |||
| @property (nonatomic, assign, readonly) NSInteger ver; | |||
| @property (nonatomic, assign, readonly) NSInteger cookingId; | |||
| @property (nonatomic, assign, readonly) NSInteger foodType; | |||
| @property (nonatomic, assign, readonly) NSInteger foodRawness; | |||
| @property (nonatomic, assign) double alarmTemperaturePercent; | |||
| @property (nonatomic, assign, readonly) ELDeviceTemperatureUnit currentUnit; | |||
| + (instancetype _Nullable)modelWithData:(NSData *)data; | |||
| - (NSData *)dataValue; | |||
| - (NSString *)remark; | |||
| - (NSString *)setRemark:(NSString *)remark; | |||
| + (Byte)version; | |||
| + (Byte)modelSize; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,154 @@ | |||
| // | |||
| // ELMeatProbeCustomDataModel.m | |||
| // AILinkBleSDK | |||
| // | |||
| #import "ELMeatProbeCustomDataModel.h" | |||
| #import <AILinkBleSDK/NSData+AILinkBle.h> | |||
| #import <objc/runtime.h> | |||
| @implementation ELMeatProbeCustomDataModel | |||
| + (Byte)version { | |||
| return 0x02; | |||
| } | |||
| + (Byte)modelSize { | |||
| return 128;//sizeof(ELMeatProbeCustomDataStruct); | |||
| } | |||
| + (instancetype)modelWithData:(NSData *)data { | |||
| if (data.length != self.modelSize) { | |||
| NSLog(@"数据长度错误 data:%@", data); | |||
| return nil; | |||
| } | |||
| ELMeatProbeCustomDataModel *model = [[ELMeatProbeCustomDataModel alloc] initWithData:data]; | |||
| if (model.customStruct.ver != self.version) { | |||
| NSLog(@"数据版本错误 data:%@", data); | |||
| return nil; | |||
| } | |||
| return model; | |||
| } | |||
| - (instancetype)init { | |||
| if (self = [super init]) { | |||
| memset(&_customStruct, 0, sizeof(_customStruct)); | |||
| _customStruct.ver = self.class.version; | |||
| } | |||
| return self; | |||
| } | |||
| - (instancetype)initWithData:(NSData *)data { | |||
| if (self = [self init]) { | |||
| memcpy(&_customStruct, data.bytes, sizeof(_customStruct)); | |||
| } | |||
| return self; | |||
| } | |||
| - (NSData *)dataValue { | |||
| Byte bytes[128] = {0x00}; | |||
| memcpy(bytes, &_customStruct, sizeof(_customStruct)); | |||
| return [NSData dataWithBytes:bytes length:sizeof(bytes)]; | |||
| } | |||
| - (NSString *)remark { | |||
| char *remarkBytes[64] = {0}; | |||
| memcpy(remarkBytes, _customStruct.remarkBytes, sizeof(_customStruct.remarkBytes)); | |||
| NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes]; | |||
| return str; | |||
| } | |||
| - (NSString *)setRemark:(NSString *)remark { | |||
| NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| if (data.length > 32) { | |||
| while (data.length > 29) { | |||
| remark = [remark substringToIndex:(remark.length - 1)]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| remark = [remark stringByAppendingString:@"..."]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| Byte *bytes = (Byte *)data.bytes; | |||
| NSUInteger total = data.length > 32 ? 32 : data.length; | |||
| memset(_customStruct.remarkBytes, 0, 32); | |||
| memcpy(_customStruct.remarkBytes, bytes, total); | |||
| return [self remark]; | |||
| } | |||
| #pragma mark setter getter | |||
| - (NSInteger)ver { | |||
| return self.customStruct.ver; | |||
| } | |||
| - (NSInteger)cookingId { | |||
| return self.customStruct.cookingId; | |||
| } | |||
| - (NSInteger)foodType { | |||
| return self.customStruct.foodType; | |||
| } | |||
| - (NSInteger)foodRawness { | |||
| return self.customStruct.foodRawness; | |||
| } | |||
| - (double)alarmTemperaturePercent { | |||
| return self.customStruct.alarmTemperaturePercent; | |||
| } | |||
| - (NSInteger)alarmTemperature_C { | |||
| return self.customStruct.alarmTemperature_C; | |||
| } | |||
| - (NSInteger)alarmTemperature_F { | |||
| return self.customStruct.alarmTemperature_F; | |||
| } | |||
| - (void)setAlarmTemperaturePercent:(double)alarmTemperaturePercent { | |||
| _customStruct.alarmTemperaturePercent = alarmTemperaturePercent; | |||
| _customStruct.alarmTemperature_C = _customStruct.targetTemperature_C * alarmTemperaturePercent; | |||
| _customStruct.alarmTemperature_F = _customStruct.targetTemperature_F * alarmTemperaturePercent; | |||
| } | |||
| - (ELDeviceTemperatureUnit)currentUnit { | |||
| return self.customStruct.currentUnit; | |||
| } | |||
| #pragma mark ---- description | |||
| - (NSString *)description { | |||
| NSMutableString *desc = [NSStringFromClass([self class]) stringByAppendingString:@": {\n"].mutableCopy; | |||
| [desc appendString:[NSString stringWithFormat:@"ver: %@,\n", @(self.customStruct.ver)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"cookingId: %@,\n", @(self.customStruct.cookingId)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"foodType: %@,\n", @(self.customStruct.foodType)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"foodRawness: %@,\n", @(self.customStruct.foodRawness)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"targetTemperature_C: %@,\n", @(self.customStruct.targetTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"targetTemperature_F: %@,\n", @(self.customStruct.targetTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_C: %@,\n", @(self.customStruct.ambientMinTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_F: %@,\n", @(self.customStruct.ambientMinTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_C: %@,\n", @(self.customStruct.ambientMaxTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_F: %@,\n", @(self.customStruct.ambientMaxTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"alarmTemperaturePercent: %@,\n", @(self.alarmTemperaturePercent)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"timerStart: %@,\n", @(self.customStruct.timerStart)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"timerEnd: %@,\n", @(self.customStruct.timerEnd)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"alarmTemperature_C: %@,\n", @(self.customStruct.alarmTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"alarmTemperature_F: %@,\n", @(self.customStruct.alarmTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"currentUnit: %@,\n", @(self.customStruct.currentUnit)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"remark: %@,\n", self.remark]]; | |||
| [desc appendString:@"}"]; | |||
| return desc; | |||
| } | |||
| - (NSString *)debugDescription { | |||
| return [self description]; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,62 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleBBQParamModel.h | |||
| // AILinkComponentMeatProbe | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @interface ELMeatProbeScreenBoxBleBBQParamModel : NSObject | |||
| //生成时间 | |||
| @property (nonatomic, assign) NSTimeInterval timestamp; | |||
| @property (nonatomic, strong) NSData *macData; | |||
| @property (nonatomic, copy, readonly) NSString *macString; | |||
| @property (nonatomic, assign) UInt8 state; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始 | |||
| @property (nonatomic, assign) UInt8 foodType; //食物类型 | |||
| @property (nonatomic, assign) UInt16 targetTemperatureRaw_C; //食物目标温度:℃ | |||
| @property (nonatomic, assign, readonly) NSInteger targetTemperature_C; | |||
| @property (nonatomic, assign) UInt16 targetTemperatureRaw_F; //食物目标温度:℉ | |||
| @property (nonatomic, assign, readonly) NSInteger targetTemperature_F; | |||
| @property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_C; //炉温目标下限:℃ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_C; | |||
| @property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_F; //炉温目标下限:℉ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_F; | |||
| @property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_C; //炉温目标上限:℃ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_C; | |||
| @property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_F; //炉温目标上限:℉ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_F; | |||
| @property (nonatomic, assign) float alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0 | |||
| @property (nonatomic, assign) UInt32 cookingId; //CookingID | |||
| @property (nonatomic, assign) UInt32 workTime; //任务已运行时间,单位s | |||
| @property (nonatomic, assign) UInt32 timerCount; //计时剩余时间(秒) | |||
| @property (nonatomic, assign) UInt8 foodRawness; //食物熟度 (maturityID) | |||
| @property (nonatomic, strong) NSData *remarkData; | |||
| - (void)clearAll; | |||
| - (void)cookDone; | |||
| - (NSData *)dataValue; | |||
| - (NSString *)remark; | |||
| - (NSString *)setRemark:(NSString *)remark; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,130 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleBBQParamModel.m | |||
| // AILinkComponentMeatProbe | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #import "ELMeatProbeScreenBoxBleBBQParamModel.h" | |||
| @implementation ELMeatProbeScreenBoxBleBBQParamModel | |||
| - (NSString *)macString { | |||
| if (self.macData == nil || self.macData.length != 6) { | |||
| return nil; | |||
| } | |||
| Byte *bytes = (Byte *)self.macData.bytes; | |||
| NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]]; | |||
| return str; | |||
| } | |||
| - (NSInteger)targetTemperature_C { | |||
| NSInteger value = self.targetTemperatureRaw_C & 0x3FFF; | |||
| if (self.targetTemperatureRaw_C & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)targetTemperature_F { | |||
| NSInteger value = self.targetTemperatureRaw_F & 0x3FFF; | |||
| if (self.targetTemperatureRaw_F & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMinTemperature_C { | |||
| NSInteger value = self.ambientMinTemperatureRaw_C & 0x3FFF; | |||
| if (self.ambientMinTemperatureRaw_C & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMinTemperature_F { | |||
| NSInteger value = self.ambientMinTemperatureRaw_F & 0x3FFF; | |||
| if (self.ambientMinTemperatureRaw_F & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMaxTemperature_C { | |||
| NSInteger value = self.ambientMaxTemperatureRaw_C & 0x3FFF; | |||
| if (self.ambientMaxTemperatureRaw_C & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMaxTemperature_F { | |||
| NSInteger value = self.ambientMaxTemperatureRaw_F & 0x3FFF; | |||
| if (self.ambientMaxTemperatureRaw_F & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (void)clearAll { | |||
| self.state = 0; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始 | |||
| self.foodType = 0xFF; //食物类型 | |||
| self.targetTemperatureRaw_C = 0;//0xFFFF; //食物目标温度:℃// | |||
| self.targetTemperatureRaw_F = 0;//0xFFFF; //食物目标温度:℉// | |||
| self.ambientMaxTemperatureRaw_C = 0;//0xFFFF; //炉温目标上限:℃ | |||
| self.ambientMaxTemperatureRaw_F = 0;//0xFFFF; //炉温目标上限:℉ | |||
| self.ambientMinTemperatureRaw_C = 0;//0xFFFF; //炉温目标下限:℃ | |||
| self.ambientMinTemperatureRaw_F = 0;//0xFFFF; //炉温目标下限:℉ | |||
| self.cookingId = 0; //CookingID | |||
| self.workTime = 0; //任务已运行时间,单位s | |||
| self.timerCount = 0; //计时剩余时间(秒) | |||
| self.alarmTemperaturePercent = 0.95; //提醒温度对目标温度百分比 0~1.0 | |||
| self.foodRawness = 0xFF; //食物熟度 | |||
| Byte bytes[32] = {0}; | |||
| self.remarkData = [NSData dataWithBytes:bytes length:sizeof(bytes)]; | |||
| } | |||
| - (void)cookDone { | |||
| [self clearAll]; | |||
| } | |||
| - (NSData *)dataValue { | |||
| NSMutableData *data = [NSMutableData data]; | |||
| [data appendData:self.macData]; | |||
| UInt32 alarmTemperaturePercent = 0; | |||
| memcpy(&alarmTemperaturePercent, &(_alarmTemperaturePercent), sizeof(float)); | |||
| Byte bytes[] = { self.state, self.foodType, self.targetTemperatureRaw_C, self.targetTemperatureRaw_C >> 8, self.targetTemperatureRaw_F, self.targetTemperatureRaw_F >> 8, self.ambientMaxTemperatureRaw_C, self.ambientMaxTemperatureRaw_C >> 8, self.ambientMaxTemperatureRaw_F, self.ambientMaxTemperatureRaw_F >> 8, self.ambientMinTemperatureRaw_C, self.ambientMinTemperatureRaw_C >> 8, self.ambientMinTemperatureRaw_F, self.ambientMinTemperatureRaw_F >> 8, self.cookingId, self.cookingId >> 8, self.cookingId >> 16, self.cookingId >> 24, self.workTime, self.workTime >> 8, self.workTime >> 16, self.workTime >> 24, self.timerCount, self.timerCount >> 8, self.timerCount >> 16, self.timerCount >> 24, alarmTemperaturePercent, alarmTemperaturePercent >> 8, alarmTemperaturePercent >> 16, alarmTemperaturePercent >> 24, self.foodRawness }; | |||
| [data appendBytes:bytes length:sizeof(bytes)]; | |||
| [data appendData:self.remarkData]; | |||
| return data.copy; | |||
| } | |||
| - (NSString *)setRemark:(NSString *)remark { | |||
| NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| if (data.length > 32) { | |||
| while (data.length > 29) { | |||
| remark = [remark substringToIndex:(remark.length - 1)]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| remark = [remark stringByAppendingString:@"..."]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| Byte *bytes = (Byte *)data.bytes; | |||
| NSUInteger total = data.length > 32 ? 32 : data.length; | |||
| Byte remarkBytes[32] = {0}; | |||
| memset(remarkBytes, 0, 32); | |||
| memcpy(remarkBytes, bytes, total); | |||
| self.remarkData = [NSData dataWithBytes:remarkBytes length:sizeof(remarkBytes)]; | |||
| return [self remark]; | |||
| } | |||
| - (NSString *)remark { | |||
| Byte remarkBytes[64] = {0}; | |||
| [self.remarkData getBytes:remarkBytes length:self.remarkData.length]; | |||
| NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes]; | |||
| return str; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,32 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleCmdTool.h | |||
| // Pods | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| #import "ELMeatProbeScreenBoxBleHeader.h" | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @class ELAILinkBleManager; | |||
| @class ELMeatProbeScreenBoxStatusModel, ELMeatProbeScreenBoxProbeStatusModel, ELMeatProbeScreenBoxBleBBQParamModel; | |||
| @interface ELMeatProbeScreenBoxBleCmdTool : NSObject | |||
| //APP获取设备状态数据(Type:0x01) | |||
| + (NSData *)requestStatus:(ELAILinkBleManager *)manager; | |||
| //切换单位 | |||
| + (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager; | |||
| //获取设置烧烤任务参数(Type:0x09) | |||
| + (NSData *)requestParams:(ELAILinkBleManager *)manager; | |||
| + (NSData *)setupParams:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager; | |||
| + (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,59 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleCmdTool.m | |||
| // Pods | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #import "ELMeatProbeScreenBoxBleCmdTool.h" | |||
| #import <AILinkBleSDK/ELAILinkBleManager.h> | |||
| #import "ELMeatProbeScreenBoxBleBBQParamModel.h" | |||
| @implementation ELMeatProbeScreenBoxBleCmdTool | |||
| //APP获取设备基础信息 | |||
| + (NSData *)requestStatus:(ELAILinkBleManager *)manager { | |||
| const Byte cmd = ELMeatProbeScreenBoxCMDAppGetStatus_Type01; | |||
| Byte payloadBytes[] = { cmd, 0x01 }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| //切换单位 | |||
| + (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager { | |||
| if (unit > 1) { return nil; } | |||
| const Byte cmd = ELMeatProbeScreenBoxCCMDAppSwitchUnit_Type03; | |||
| Byte payloadBytes[] = { cmd, unit }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| + (NSData *)requestParams:(ELAILinkBleManager *)manager { | |||
| const Byte cmd = ELMeatProbeScreenBoxCMDSetInfo_Type09; | |||
| Byte payloadBytes[] = { cmd, 0x01 }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| + (NSData *)setupParams:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager { | |||
| const Byte cmd = ELMeatProbeScreenBoxCMDSetInfo_Type09; | |||
| Byte payloadBytes[] = { cmd, 0x00, supportProbe }; | |||
| NSMutableData *payloadData = [NSMutableData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| for (ELMeatProbeScreenBoxBleBBQParamModel *param in params) { | |||
| [payloadData appendData:param.dataValue]; | |||
| } | |||
| [manager sendA7Payload:payloadData.copy]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| + (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager { | |||
| const Byte ELInetGetCmdTypeSpecialFunc = 0x49, op = 0x01; | |||
| Byte payloadBytes[16] = { ELInetGetCmdTypeSpecialFunc, op }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA6Payload:payloadData.copy]; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,35 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleHeader.h | |||
| // Pods | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #ifndef ELMeatProbeScreenBoxBleHeader_h | |||
| #define ELMeatProbeScreenBoxBleHeader_h | |||
| typedef NS_ENUM(UInt8, ELMeatProbeScreenBoxCMD) { | |||
| ELMeatProbeScreenBoxCMDAppGetStatus_Type01 = 0x01, //0x01:Type:APP 获取设备状态 | |||
| ELMeatProbeScreenBoxCMDMcuUploadStatus_Type02 = 0x02, //0x02:Type:设备上报设备数据 | |||
| ELMeatProbeScreenBoxCCMDAppSwitchUnit_Type03 = 0x03, //0x03:Type:切换单位 | |||
| ELMeatProbeScreenBoxCCMDAppSwitchUnitResponse_Type04 = 0x04, //0x04:Type:切换单位 response | |||
| ELMeatProbeScreenBoxCMDStartAlarmState_Type05 = 0x05, //0x05:Type:发起报警 | |||
| ELMeatProbeScreenBoxCMDStartAlarmStateResponse_Type06 = 0x06, //0x06:Type:发起报警 Response | |||
| ELMeatProbeScreenBoxCMDStoplAlarm_Type07 = 0x07, //0x07:Type:停止报警 | |||
| ELMeatProbeScreenBoxCMDStoplAlarmResponse_Type08 = 0x08, //0x08:Type:停止报警 Response | |||
| ELMeatProbeScreenBoxCMDSetInfo_Type09 = 0x09, //Type:0x09 设置烧烤任务参数 | |||
| ELMeatProbeScreenBoxCMDSetInfoResponse_Type0A = 0x0A, //Type:0x0A 设置烧烤任务参数 Response | |||
| }; | |||
| #endif /* ELMeatProbeScreenBoxBleHeader_h */ | |||
| @@ -0,0 +1,114 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleParser.h | |||
| // Pods | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| #import <AILinkBleSDK/ELSDKHeader.h> | |||
| #import "ELMeatProbeScreenBoxBleHeader.h" | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @protocol ELMeatProbeScreenBoxBleParserDelegate; | |||
| @class ELMeatProbeScreenBoxStatusModel, ELMeatProbeScreenBoxProbeStatusModel, ELMeatProbeScreenBoxBleBBQParamModel; | |||
| @interface ELMeatProbeScreenBoxBleParser : NSObject | |||
| @property (nonatomic, copy) NSString *mac; | |||
| @property (nonatomic, assign) NSInteger deviceId; | |||
| @property (nonatomic, weak) id<ELMeatProbeScreenBoxBleParserDelegate> delegate; | |||
| - (void)parseA7Payload:(NSData *)payload; | |||
| @end | |||
| @protocol ELMeatProbeScreenBoxBleParserDelegate <NSObject> | |||
| @optional | |||
| //MCU 上报设备状态数据(Type:02) | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser boxStatus:(ELMeatProbeScreenBoxStatusModel *)boxStatus; | |||
| //切换单位(Type:04) | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser unit:(ELDeviceTemperatureUnit)unit; | |||
| //报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1 | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac; | |||
| //取消报警(Type:08) | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac; | |||
| //烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务 | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe params:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params; | |||
| @end | |||
| @interface ELMeatProbeScreenBoxStatusModel : NSObject | |||
| //设备状态数据协议版本:0x01 | |||
| @property (nonatomic, assign) UInt8 version; | |||
| //支持的探针数量 M | |||
| @property (nonatomic, assign) UInt8 probeMaxCount; | |||
| //当前连接的探针数量 N | |||
| @property (nonatomic, assign) UInt8 probeCount; | |||
| //充电盒电池状态 | |||
| @property (nonatomic, assign) UInt8 batteryStatus; | |||
| @property (nonatomic, assign) UInt8 batteryPercent; | |||
| //温度单位 | |||
| @property (nonatomic, assign) ELDeviceTemperatureUnit unit; | |||
| //生成时间 | |||
| @property (nonatomic, assign) NSTimeInterval timestamp; | |||
| @property (nonatomic, strong) NSArray <ELMeatProbeScreenBoxProbeStatusModel *>*probeStatusArray; | |||
| @end | |||
| @interface ELMeatProbeScreenBoxProbeStatusModel : NSObject | |||
| //探针编号(1-M) 无法确定可连 接探针的编号,上报 0xFF | |||
| @property (nonatomic, assign) UInt8 index; | |||
| //探针 mac 地址 | |||
| @property (nonatomic, strong) NSData *macData; | |||
| @property (nonatomic, copy, readonly) NSString *macString; | |||
| // | |||
| //食物温度 | |||
| @property (nonatomic, assign, readonly) NSInteger internalTemperature_C; | |||
| @property (nonatomic, assign, readonly) NSInteger internalTemperature_F; | |||
| @property (nonatomic, assign, readonly) ELDeviceTemperatureUnit internalTemperatureUnit; | |||
| @property (nonatomic, assign) UInt16 internalTemperatureRaw; | |||
| // | |||
| //环境温度 | |||
| @property (nonatomic, assign, readonly) NSInteger ambientTemperature_C; | |||
| @property (nonatomic, assign, readonly) NSInteger ambientTemperature_F; | |||
| @property (nonatomic, assign, readonly) ELDeviceTemperatureUnit ambientTemperatureUnit; | |||
| @property (nonatomic, assign) UInt16 ambientTemperatureRaw; | |||
| // | |||
| //探针电池状态 | |||
| @property (nonatomic, assign) UInt8 batteryStatus; | |||
| @property (nonatomic, assign) UInt8 batteryPercent; | |||
| @property (nonatomic, assign) UInt8 batteryRaw; | |||
| //探针插入食物状态 | |||
| @property (nonatomic, assign) UInt8 insertStatusRaw; | |||
| //探针连接状态 | |||
| @property (nonatomic, assign) UInt8 connectState; | |||
| //生成时间 | |||
| @property (nonatomic, assign) double timestamp; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,297 @@ | |||
| // | |||
| // ELMeatProbeScreenBoxBleParser.m | |||
| // Pods | |||
| // | |||
| // Created by Larry Zhang on 2024/2/26. | |||
| // | |||
| #import "ELMeatProbeScreenBoxBleParser.h" | |||
| #import "ELMeatProbeScreenBoxBleBBQParamModel.h" | |||
| @interface ELMeatProbeScreenBoxBleParser() | |||
| @end | |||
| @implementation ELMeatProbeScreenBoxBleParser | |||
| - (void)parseA7Payload:(nonnull NSData *)payload { | |||
| NSLog(@"#### parseA7Payload payload:%@", payload.debugDescription); | |||
| Byte *bytes = (Byte *)payload.bytes; | |||
| Byte cmd = bytes[0]; | |||
| switch (cmd) { | |||
| case ELMeatProbeScreenBoxCMDMcuUploadStatus_Type02: | |||
| [self parseStatusData:payload]; | |||
| break; | |||
| case ELMeatProbeScreenBoxCCMDAppSwitchUnit_Type03: | |||
| { | |||
| UInt8 unit = bytes[1]; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:unit:)]) { | |||
| [self.delegate meatProbeScreenBoxBleParser:self unit:unit]; | |||
| } | |||
| } | |||
| break; | |||
| // case ELMeatProbeScreenBoxCCMDAppSwitchUnitResponse_Type04: | |||
| // break; | |||
| case ELMeatProbeScreenBoxCMDStartAlarmState_Type05: { | |||
| Byte i = 1; | |||
| Byte macBytes[6] = { bytes[i+5], bytes[i+4], bytes[i+3], bytes[i+2], bytes[i+1], bytes[i] }; | |||
| NSData *macData = [NSData dataWithBytes:macBytes length:6]; | |||
| i += 6; | |||
| Byte state = bytes[i]; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:startAlarmWithState:macData:mac:)]) { | |||
| [self.delegate meatProbeScreenBoxBleParser:self startAlarmWithState:state macData:macData mac:[self macDataToString:macData]]; | |||
| } | |||
| } | |||
| break; | |||
| // case ELMeatProbeScreenBoxCMDStartAlarmStateResponse_Type06: | |||
| // break; | |||
| case ELMeatProbeScreenBoxCMDStoplAlarm_Type07: { | |||
| Byte i = 1; | |||
| Byte macBytes[6] = { bytes[i+5], bytes[i+4], bytes[i+3], bytes[i+2], bytes[i+1], bytes[i] }; | |||
| NSData *macData = [NSData dataWithBytes:macBytes length:6]; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:stopAlarmWithMacData:mac:)]) { | |||
| [self.delegate meatProbeScreenBoxBleParser:self stopAlarmWithMacData:macData mac:[self macDataToString:macData]]; | |||
| } | |||
| } | |||
| break; | |||
| // case ELMeatProbeScreenBoxCMDStoplAlarmResponse_Type08: | |||
| // break; | |||
| case ELMeatProbeScreenBoxCMDSetInfo_Type09: | |||
| case ELMeatProbeScreenBoxCMDSetInfoResponse_Type0A: { | |||
| [self parseSetInfoParamData:payload]; | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| - (NSString *)macDataToString:(NSData *)macData { | |||
| if (macData == nil || macData.length != 6) { | |||
| return nil; | |||
| } | |||
| Byte *bytes = (Byte *)macData.bytes; | |||
| NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]]; | |||
| return str; | |||
| } | |||
| - (void)parseSetInfoParamData:(NSData *)payload { | |||
| NSLog(@"##### parseSetInfoParamData payload:%@", payload.debugDescription); | |||
| if (payload.length != 72) { | |||
| return; | |||
| } | |||
| Byte *bytes = (Byte *)payload.bytes; | |||
| Byte i = 1; | |||
| Byte option = bytes[i]; | |||
| i = 2; | |||
| Byte supportProbe = bytes[i]; | |||
| i = 3; | |||
| NSMutableArray *arr = [NSMutableArray array]; | |||
| while (i < payload.length) { | |||
| //探针 1 | |||
| ELMeatProbeScreenBoxBleBBQParamModel *param = [[ELMeatProbeScreenBoxBleBBQParamModel alloc] init]; | |||
| param.timestamp = [NSDate date].timeIntervalSince1970; | |||
| param.macData = [payload subdataWithRange:NSMakeRange(i, 6)]; | |||
| i += 6; | |||
| param.state = bytes[i]; | |||
| i += 1; | |||
| param.foodType = bytes[i];//if (param.foodType == 0xFF) param.foodType = 0xFE; | |||
| i += 1; | |||
| param.targetTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.targetTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMaxTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMaxTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMinTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMinTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.cookingId = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| i += 4; | |||
| param.workTime = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| i += 4; | |||
| param.timerCount = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| i += 4; | |||
| UInt32 alarmTemperaturePercentU32 = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| float alarmTemperaturePercent = 0; | |||
| memcpy(&alarmTemperaturePercent, &alarmTemperaturePercentU32, sizeof(UInt32)); | |||
| param.alarmTemperaturePercent = alarmTemperaturePercent; | |||
| i += 4; | |||
| param.foodRawness = bytes[i]; | |||
| i += 1; | |||
| param.remarkData = [payload subdataWithRange:NSMakeRange(i, 32)]; | |||
| i += 32; | |||
| if (param.state == 0) { | |||
| [param clearAll]; | |||
| } | |||
| [arr addObject:param]; | |||
| } | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:option:supportProbe:params:)]) { | |||
| [self.delegate meatProbeScreenBoxBleParser:self option:option supportProbe:supportProbe params:arr.copy]; | |||
| } | |||
| } | |||
| - (void)parseStatusData:(NSData *)payload { | |||
| if (payload.length != 25) { | |||
| return; | |||
| } | |||
| Byte *bytes = (Byte *)payload.bytes; | |||
| ELMeatProbeScreenBoxStatusModel *boxStatus = [[ELMeatProbeScreenBoxStatusModel alloc] init]; | |||
| boxStatus.timestamp = [NSDate date].timeIntervalSince1970; | |||
| Byte i = 1; | |||
| boxStatus.version = bytes[i]; | |||
| i = 2; | |||
| boxStatus.probeMaxCount = bytes[i]; | |||
| i = 3; | |||
| boxStatus.probeCount = bytes[i]; | |||
| i = 4; | |||
| boxStatus.batteryStatus = bytes[i] >> 7; | |||
| boxStatus.batteryPercent = bytes[i] & 0x7F; | |||
| i = 5; | |||
| boxStatus.unit = bytes[i]; | |||
| i = 9; | |||
| NSMutableArray *arr = [NSMutableArray array]; | |||
| { | |||
| ELMeatProbeScreenBoxProbeStatusModel *probeStatus = [[ELMeatProbeScreenBoxProbeStatusModel alloc] init]; | |||
| probeStatus.timestamp = boxStatus.timestamp; | |||
| probeStatus.index = bytes[i]; | |||
| i += 1; | |||
| NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)]; | |||
| probeStatus.macData = macData; | |||
| i += 6; | |||
| probeStatus.internalTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| probeStatus.ambientTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| // 凸(艹皿艹 ) //0x639F (25503) | |||
| if (probeStatus.ambientTemperatureRaw == 25503) { | |||
| probeStatus.ambientTemperatureRaw = 0xFFFF; | |||
| } | |||
| i += 2; | |||
| probeStatus.batteryStatus = bytes[i] >> 7; | |||
| probeStatus.batteryPercent = bytes[i] & 0x7F; | |||
| probeStatus.batteryRaw = bytes[i]; | |||
| i += 1; | |||
| probeStatus.insertStatusRaw = bytes[i]; | |||
| i += 1; | |||
| probeStatus.connectState = bytes[i]; | |||
| i += 3; | |||
| [arr addObject:probeStatus]; | |||
| } | |||
| boxStatus.probeStatusArray = arr.copy; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeScreenBoxBleParser:boxStatus:)]) { | |||
| [self.delegate meatProbeScreenBoxBleParser:self boxStatus:boxStatus]; | |||
| } | |||
| } | |||
| @end | |||
| @implementation ELMeatProbeScreenBoxStatusModel | |||
| @end | |||
| @implementation ELMeatProbeScreenBoxProbeStatusModel | |||
| - (NSString *)macString { | |||
| if (self.macData == nil || self.macData.length != 6) { | |||
| return nil; | |||
| } | |||
| Byte *bytes = (Byte *)self.macData.bytes; | |||
| NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]]; | |||
| return str; | |||
| } | |||
| - (NSInteger)internalTemperature_C { | |||
| if (self.internalTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_C) { | |||
| return self.internalTemperature; | |||
| } | |||
| return [self convertF2C:self.internalTemperature]; | |||
| } | |||
| - (NSInteger)internalTemperature_F { | |||
| if (self.internalTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_F) { | |||
| return self.internalTemperature; | |||
| } | |||
| return [self convertC2F:self.internalTemperature]; | |||
| } | |||
| - (NSInteger)internalTemperature { | |||
| NSInteger value = self.internalTemperatureRaw & 0x3FFF; | |||
| if (self.internalTemperatureRaw & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (ELDeviceTemperatureUnit)internalTemperatureUnit { | |||
| return self.internalTemperatureRaw >> 15; | |||
| } | |||
| - (NSInteger)ambientTemperature_C { | |||
| if (self.ambientTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_C) { | |||
| return self.ambientTemperature; | |||
| } | |||
| return [self convertF2C:self.ambientTemperature]; | |||
| } | |||
| - (NSInteger)ambientTemperature_F { | |||
| if (self.ambientTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_F) { | |||
| return self.ambientTemperature; | |||
| } | |||
| return [self convertC2F:self.ambientTemperature]; | |||
| } | |||
| - (NSInteger)ambientTemperature { | |||
| NSInteger value = self.ambientTemperatureRaw & 0x3FFF; | |||
| if (self.ambientTemperatureRaw & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (ELDeviceTemperatureUnit)ambientTemperatureUnit { | |||
| return self.ambientTemperatureRaw >> 15; | |||
| } | |||
| //温度换算 | |||
| - (float)convertF2C:(float)fahrenheit { | |||
| return round((fahrenheit - 32) / 1.8); | |||
| } | |||
| - (float)convertC2F:(float)celsius { | |||
| return round(celsius * 1.8 + 32); | |||
| } | |||
| @end | |||
| @@ -0,0 +1,21 @@ | |||
| // | |||
| // MeatProbeScreenBoxVC.h | |||
| // AILinkBleSDK_Example | |||
| // | |||
| // Created by LarryZhang on 2024/9/7. | |||
| // Copyright © 2024 zhengzida. All rights reserved. | |||
| // | |||
| #import <UIKit/UIKit.h> | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @class ELAILinkPeripheral; | |||
| @interface MeatProbeScreenBoxVC : UIViewController | |||
| @property (nonatomic, strong) ELAILinkPeripheral *per; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,312 @@ | |||
| // | |||
| // MeatProbeScreenBoxVC.m | |||
| // AILinkBleSDK_Example | |||
| // | |||
| // Created by LarryZhang on 2024/9/7. | |||
| // Copyright © 2024 zhengzida. All rights reserved. | |||
| // | |||
| #import "MeatProbeScreenBoxVC.h" | |||
| #import <AILinkBleSDK/ELAILinkBleManager.h> | |||
| #import <AILinkBleSDK/NSData+AILinkBle.h> | |||
| #import "ELMeatProbeScreenBoxBleCmdTool.h" | |||
| #import "ELMeatProbeScreenBoxBleParser.h" | |||
| #import "ELMeatProbeScreenBoxBleBBQParamModel.h" | |||
| @interface MeatProbeScreenBoxVC () <ELAILinkBleManagerDelegate, ELMeatProbeScreenBoxBleParserDelegate> | |||
| @property (weak, nonatomic) IBOutlet UILabel *bleStatusLabel; | |||
| @property (weak, nonatomic) IBOutlet UITextView *logTextView; | |||
| @property (nonatomic, strong) ELAILinkBleManager *bleManager; | |||
| @property (nonatomic, assign) NELBleManagerConnectState bleConnectState; | |||
| @property (nonatomic, strong) ELMeatProbeScreenBoxBleParser *bleParser; | |||
| @property (nonatomic, strong) ELMeatProbeScreenBoxStatusModel *boxStatus; | |||
| @property (nonatomic, strong) ELMeatProbeScreenBoxBleBBQParamModel *param; | |||
| @end | |||
| @implementation MeatProbeScreenBoxVC | |||
| - (void)viewDidLoad { | |||
| [super viewDidLoad]; | |||
| // Do any additional setup after loading the view from its nib. | |||
| [self initBle]; | |||
| } | |||
| - (void)dealloc { | |||
| [self deinitBle]; | |||
| } | |||
| #pragma mark - Action | |||
| - (IBAction)requestParamsAction:(id)sender { | |||
| NSData *payload = [ELMeatProbeScreenBoxBleCmdTool requestParams:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
| [self addLog:log]; | |||
| } | |||
| - (IBAction)setupParamsAction:(id)sender { | |||
| if (self.param == nil) { | |||
| return; | |||
| } | |||
| ELMeatProbeScreenBoxBleBBQParamModel *param = self.param; | |||
| param.foodType = 1; | |||
| param.targetTemperatureRaw_C = 70; | |||
| param.targetTemperatureRaw_F = 212; | |||
| param.foodRawness = 1; | |||
| param.alarmTemperaturePercent = 0.95; | |||
| param.cookingId = [NSDate date].timeIntervalSince1970; | |||
| NSData *payload = [ELMeatProbeScreenBoxBleCmdTool setupParams:@[param] supportProbe:1 manager:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
| [self addLog:log]; | |||
| } | |||
| - (IBAction)switchUnitAction:(id)sender { | |||
| if (self.boxStatus.unit == 1) { | |||
| NSData *payload = [ELMeatProbeScreenBoxBleCmdTool switchUnit:0 manager:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℃", payload]; | |||
| [self addLog:log]; | |||
| } else { | |||
| NSData *payload = [ELMeatProbeScreenBoxBleCmdTool switchUnit:1 manager:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℉", payload]; | |||
| [self addLog:log]; | |||
| } | |||
| } | |||
| - (IBAction)clearAction:(id)sender { | |||
| self.logTextView.text = @""; | |||
| } | |||
| - (IBAction)shareLogAction:(id)sender { | |||
| } | |||
| #pragma mark - ELAILinkBleManagerDelegate | |||
| - (void)initBle { | |||
| self.bleManager = [[ELAILinkBleManager alloc] init]; | |||
| self.bleManager.ailinkDelegate = self; | |||
| self.bleParser = [[ELMeatProbeScreenBoxBleParser alloc] init]; | |||
| self.bleParser.delegate = self; | |||
| self.bleParser.mac = self.per.macAddressString; | |||
| } | |||
| - (void)scanBle { | |||
| [self.bleManager scanFilterWithCidArray:@[@(self.per.cid)]]; | |||
| } | |||
| - (void)connect:(ELAILinkPeripheral *)peripheral { | |||
| [self.bleManager connectAILinkPeripheral:peripheral]; | |||
| [self updateBleStatusView:NELBleManagerConnectStateConnecting]; | |||
| } | |||
| - (void)deinitBle { | |||
| [self.bleManager stopScan]; | |||
| self.bleManager.ailinkDelegate = nil; | |||
| [self.bleManager disconnectPeripheral]; | |||
| } | |||
| - (void)updateBleStatusView:(NELBleManagerConnectState)state { | |||
| if (self.bleManager.central.state == CBManagerStatePoweredOff) { | |||
| self.bleStatusLabel.text = @"蓝牙关闭"; | |||
| [self addLog:self.bleStatusLabel.text]; | |||
| return; | |||
| } | |||
| switch (state) { | |||
| case NELBleManagerConnectStateDisconnected: | |||
| self.bleStatusLabel.text = @"断开连接"; | |||
| break; | |||
| case NELBleManagerConnectStateFailed: | |||
| case NELBleManagerConnectStateFailedValidation: | |||
| self.bleStatusLabel.text = @"连接失败"; | |||
| break; | |||
| case NELBleManagerConnectStateConnecting: | |||
| self.bleStatusLabel.text = @"正在连接..."; | |||
| break; | |||
| case NELBleManagerConnectStatePassed: | |||
| self.bleStatusLabel.text = @"连接成功"; | |||
| [self requestDeviceData]; | |||
| // read RSSI | |||
| // [self readRSSI]; | |||
| break; | |||
| case NELBleManagerConnectStateCentralScanning: | |||
| self.bleStatusLabel.text = @"正在扫描..."; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| [self addLog:self.bleStatusLabel.text]; | |||
| } | |||
| - (void)requestDeviceData { | |||
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{ | |||
| }); | |||
| } | |||
| // 设备状态变更 | |||
| - (void)managerDidUpdateState:(CBCentralManager *)central { | |||
| NSLog(@"%s state:%@", __func__, @(central.state)); | |||
| if (central.state == CBManagerStatePoweredOn) { | |||
| [self scanBle]; | |||
| } else if (central.state == CBManagerStatePoweredOff) { | |||
| self.bleConnectState = NELBleManagerConnectStateCentralPowerOff; | |||
| [self updateBleStatusView:NELBleManagerConnectStateCentralPowerOff]; | |||
| } | |||
| } | |||
| - (void)managerScanState:(BOOL)scanning { | |||
| NSLog(@"%s scanning:%@", __func__, @(scanning)); | |||
| if (scanning) { | |||
| [self updateBleStatusView:NELBleManagerConnectStateCentralScanning]; | |||
| } | |||
| } | |||
| // 扫描到设备 | |||
| - (void)managerDidDiscoverPeripheral:(ELAILinkPeripheral *)peripheral { | |||
| NSLog(@"managerDidDiscoverPeripheral cid:%02x vid:%02x pid:%02x mac:%@", peripheral.cid, peripheral.vid, peripheral.pid, peripheral.macAddressString); | |||
| if ([peripheral.macAddressString isEqualToString:self.per.macAddressString]) { | |||
| [self.bleManager stopScan]; | |||
| [self connect:peripheral]; | |||
| } | |||
| } | |||
| - (void)managerDidUpdateConnect:(NELBleManagerConnectState)state { | |||
| NSLog(@"%s NELBleManagerConnectState:%@", __func__, @(state)); | |||
| [self updateBleStatusView:state]; | |||
| } | |||
| //A7数据 | |||
| - (void)aiLinkBleReceiveA7Data:(NSData *)payload { | |||
| NSLog(@"%s #### payload:%@", __func__, payload.elHexString); | |||
| NSString *log = [NSString stringWithFormat:@"收: payload: %@", payload.elHexString]; | |||
| [self addLog:log]; | |||
| [self.bleParser parseA7Payload:payload]; | |||
| } | |||
| //A6数据 | |||
| - (void)aiLinkBleReceiveA6Data:(NSData *)packet { | |||
| NSLog(@"%s ##### packet:%@", __func__, packet.elHexString); | |||
| Byte *bytes = (Byte *)packet.bytes; | |||
| Byte cmd = bytes[2]; | |||
| if (cmd == ELInetGetCmdTypeGetBMVersion) { | |||
| NSString *bmVersion = self.bleManager.bmVersion; | |||
| NSLog(@"##### bmVersion: %@", bmVersion); | |||
| } | |||
| if (cmd == ELInetGetCmdTypeGetBMVersionPro) { | |||
| NSString *bmVersionPro = self.bleManager.bmVersionPro; | |||
| NSLog(@"##### bmVersionPro: %@", bmVersionPro); | |||
| } | |||
| } | |||
| #pragma mark - ELMeatProbeScreenBoxBleParser | |||
| //MCU 上报设备状态数据(Type:02) | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser boxStatus:(ELMeatProbeScreenBoxStatusModel *)boxStatus { | |||
| NSLog(@"##### boxStatus(): %@ ", boxStatus.debugDescription); | |||
| self.boxStatus = boxStatus; | |||
| NSString *log = [NSString stringWithFormat:@"boxStatus: { mac:%@, probeMaxCount:%hhu, probeCount:%hhu, batteryStatus:%hhu batteryPercent:%hhu, unit:%ld }", parser.mac, boxStatus.probeMaxCount, boxStatus.probeCount, boxStatus.batteryStatus, boxStatus.batteryPercent, boxStatus.unit]; | |||
| [self addLog:log]; | |||
| ELMeatProbeScreenBoxProbeStatusModel *probeStatus = boxStatus.probeStatusArray.firstObject; | |||
| NSString *log2 = [NSString stringWithFormat:@"probeStatus: { connectState:%@ macString:%hhu, internalTemperature_C:%ld ℃, ambientTemperature_C:%ld ℃ batteryStatus:%hhu batteryPercent:%hhu }", probeStatus.macString, probeStatus.connectState, probeStatus.internalTemperature_C, probeStatus.ambientTemperature_C, probeStatus.batteryStatus, probeStatus.batteryPercent]; | |||
| [self addLog:log2]; | |||
| } | |||
| //切换单位(Type:04) | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser unit:(ELDeviceTemperatureUnit)unit { | |||
| NSLog(@"##### unit(): %@", @(unit)); | |||
| NSString *log = [NSString stringWithFormat:@"unit(): %@", unit == 0 ? @"℃" : @"°F"]; | |||
| [self addLog:log]; | |||
| } | |||
| //报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1 | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac { | |||
| NSLog(@"##### box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state)); | |||
| NSString *log = [NSString stringWithFormat:@"box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state)]; | |||
| [self addLog:log]; | |||
| } | |||
| //取消报警(Type:08) | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac { | |||
| NSLog(@"##### box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac); | |||
| NSString *log = [NSString stringWithFormat:@"box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac]; | |||
| [self addLog:log]; | |||
| } | |||
| //烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务 | |||
| - (void)meatProbeScreenBoxBleParser:(ELMeatProbeScreenBoxBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe params:(NSArray <ELMeatProbeScreenBoxBleBBQParamModel *>*)params { | |||
| NSLog(@"##### box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe)); | |||
| ELMeatProbeScreenBoxBleBBQParamModel *param = params.firstObject; | |||
| NSLog(@"##### param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType)); | |||
| self.param = param; | |||
| NSString *log = [NSString stringWithFormat:@"box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe)]; | |||
| NSString *log2 = [NSString stringWithFormat:@"param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType)]; | |||
| [self addLog:log]; | |||
| [self addLog:log2]; | |||
| } | |||
| - (void)peripheralDidReadRSSI:(nonnull NSNumber *)RSSI { | |||
| NSLog(@"%s peripheralDidReadRSSI: %@", __FUNCTION__, RSSI); | |||
| // main thread | |||
| dispatch_async(dispatch_get_main_queue(), ^{ | |||
| [self addLog:[NSString stringWithFormat:@"peripheralDidReadRSSI: %@", RSSI]]; | |||
| }); | |||
| [self readRSSI]; | |||
| } | |||
| - (void)readRSSI { | |||
| // delay | |||
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |||
| // Read RSSI | |||
| [self.bleManager readRSSI]; | |||
| }); | |||
| } | |||
| #pragma mark - addLog | |||
| - (void)addLog:(NSString *)log { | |||
| [self addLog:log newline:YES]; | |||
| } | |||
| - (void)addWithoutNewlineLog:(NSString *)log { | |||
| [self addLog:log newline:NO]; | |||
| } | |||
| - (void)addLog:(NSString *)log newline:(BOOL)newline { | |||
| NSDateFormatter *format = [[NSDateFormatter alloc] init]; | |||
| format.dateFormat = @"HH:mm:ss.SSS"; | |||
| NSString *time = [format stringFromDate:[NSDate date]]; | |||
| if (newline) { | |||
| self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log]; | |||
| } else { | |||
| self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log]; | |||
| } | |||
| [self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)]; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,133 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | |||
| <device id="retina6_12" orientation="portrait" appearance="light"/> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> | |||
| <capability name="Safe area layout guides" minToolsVersion="9.0"/> | |||
| <capability name="System colors in document resources" minToolsVersion="11.0"/> | |||
| <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | |||
| </dependencies> | |||
| <objects> | |||
| <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MeatProbeScreenBoxVC"> | |||
| <connections> | |||
| <outlet property="bleStatusLabel" destination="lTu-0Z-Tge" id="aaN-iq-CUB"/> | |||
| <outlet property="logTextView" destination="QAW-Ww-9FQ" id="jSk-nf-wNh"/> | |||
| <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> | |||
| </connections> | |||
| </placeholder> | |||
| <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | |||
| <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT"> | |||
| <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <subviews> | |||
| <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lTu-0Z-Tge"> | |||
| <rect key="frame" x="0.0" y="59" width="393" height="21"/> | |||
| <fontDescription key="fontDescription" type="system" pointSize="17"/> | |||
| <nil key="textColor"/> | |||
| <nil key="highlightedColor"/> | |||
| </label> | |||
| <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BL3-Qk-9Hg"> | |||
| <rect key="frame" x="8" y="380" width="377" height="438"/> | |||
| <subviews> | |||
| <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QAW-Ww-9FQ"> | |||
| <rect key="frame" x="0.0" y="0.0" width="377" height="438"/> | |||
| <color key="backgroundColor" systemColor="labelColor"/> | |||
| <color key="textColor" systemColor="opaqueSeparatorColor"/> | |||
| <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="10"/> | |||
| <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> | |||
| </textView> | |||
| </subviews> | |||
| <color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
| <constraints> | |||
| <constraint firstAttribute="trailing" secondItem="QAW-Ww-9FQ" secondAttribute="trailing" id="18q-Eb-y4q"/> | |||
| <constraint firstItem="QAW-Ww-9FQ" firstAttribute="leading" secondItem="BL3-Qk-9Hg" secondAttribute="leading" id="APS-gg-hbg"/> | |||
| <constraint firstAttribute="bottom" secondItem="QAW-Ww-9FQ" secondAttribute="bottom" id="Dz9-Nx-gZg"/> | |||
| <constraint firstItem="QAW-Ww-9FQ" firstAttribute="top" secondItem="BL3-Qk-9Hg" secondAttribute="top" id="HfL-Mt-VYw"/> | |||
| </constraints> | |||
| <userDefinedRuntimeAttributes> | |||
| <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> | |||
| <real key="value" value="8"/> | |||
| </userDefinedRuntimeAttribute> | |||
| </userDefinedRuntimeAttributes> | |||
| </view> | |||
| <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qs9-zC-31a"> | |||
| <rect key="frame" x="354" y="342" width="31" height="30"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="分享"/> | |||
| <connections> | |||
| <action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="O10-vI-2uq"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9po-bu-c16"> | |||
| <rect key="frame" x="8" y="342" width="31" height="30"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="清空"/> | |||
| <connections> | |||
| <action selector="clearAction:" destination="-1" eventType="touchUpInside" id="MJ3-27-vjh"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Crb-qO-me7"> | |||
| <rect key="frame" x="16" y="96" width="128" height="35"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="Button"/> | |||
| <buttonConfiguration key="configuration" style="filled" title="请求烧烤参数"/> | |||
| <connections> | |||
| <action selector="requestParamsAction:" destination="-1" eventType="touchUpInside" id="ihu-lG-9Zg"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bId-6O-Ndu"> | |||
| <rect key="frame" x="176" y="96" width="128" height="35"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="Button"/> | |||
| <buttonConfiguration key="configuration" style="filled" title="设置烧烤参数"/> | |||
| <connections> | |||
| <action selector="setupParamsAction:" destination="-1" eventType="touchUpInside" id="o7Z-30-SJV"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cgo-YJ-V1h"> | |||
| <rect key="frame" x="16" y="147" width="93.333333333333329" height="35"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="Button"/> | |||
| <buttonConfiguration key="configuration" style="filled" title="单位切换"/> | |||
| <connections> | |||
| <action selector="switchUnitAction:" destination="-1" eventType="touchUpInside" id="YRG-hs-YTh"/> | |||
| </connections> | |||
| </button> | |||
| </subviews> | |||
| <viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/> | |||
| <color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
| <constraints> | |||
| <constraint firstItem="Q5M-cg-NOt" firstAttribute="bottom" secondItem="BL3-Qk-9Hg" secondAttribute="bottom" id="3vZ-eL-8ye"/> | |||
| <constraint firstItem="bId-6O-Ndu" firstAttribute="top" secondItem="Crb-qO-me7" secondAttribute="top" id="Adu-Nt-6GA"/> | |||
| <constraint firstItem="bId-6O-Ndu" firstAttribute="leading" secondItem="Crb-qO-me7" secondAttribute="trailing" constant="32" id="GgJ-IF-Lyh"/> | |||
| <constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="BL3-Qk-9Hg" secondAttribute="trailing" constant="8" id="Hvr-Qw-Sfd"/> | |||
| <constraint firstItem="lTu-0Z-Tge" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" id="J1z-u5-gvv"/> | |||
| <constraint firstItem="lTu-0Z-Tge" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" id="OLV-Lf-DKV"/> | |||
| <constraint firstItem="9po-bu-c16" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="8" id="QjW-lL-FtU"/> | |||
| <constraint firstItem="BL3-Qk-9Hg" firstAttribute="top" secondItem="9po-bu-c16" secondAttribute="bottom" constant="8" symbolic="YES" id="RtA-G5-d6U"/> | |||
| <constraint firstItem="BL3-Qk-9Hg" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="8" id="SNc-kd-Xyj"/> | |||
| <constraint firstItem="BL3-Qk-9Hg" firstAttribute="top" secondItem="lTu-0Z-Tge" secondAttribute="bottom" constant="300" id="SSt-fZ-CNI"/> | |||
| <constraint firstItem="lTu-0Z-Tge" firstAttribute="trailing" secondItem="Q5M-cg-NOt" secondAttribute="trailing" id="YG3-5g-z3J"/> | |||
| <constraint firstItem="Crb-qO-me7" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="16" id="a7S-pR-2xD"/> | |||
| <constraint firstItem="BL3-Qk-9Hg" firstAttribute="top" secondItem="Qs9-zC-31a" secondAttribute="bottom" constant="8" symbolic="YES" id="cR7-NU-eXL"/> | |||
| <constraint firstItem="Cgo-YJ-V1h" firstAttribute="leading" secondItem="Crb-qO-me7" secondAttribute="leading" id="hXk-fs-S6Y"/> | |||
| <constraint firstItem="Crb-qO-me7" firstAttribute="top" secondItem="lTu-0Z-Tge" secondAttribute="bottom" constant="16" id="kC5-sR-XD2"/> | |||
| <constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="Qs9-zC-31a" secondAttribute="trailing" constant="8" id="oJa-9R-5R5"/> | |||
| <constraint firstItem="Cgo-YJ-V1h" firstAttribute="top" secondItem="Crb-qO-me7" secondAttribute="bottom" constant="16" id="vTs-18-bkM"/> | |||
| </constraints> | |||
| <point key="canvasLocation" x="139" y="21"/> | |||
| </view> | |||
| </objects> | |||
| <resources> | |||
| <systemColor name="labelColor"> | |||
| <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
| </systemColor> | |||
| <systemColor name="opaqueSeparatorColor"> | |||
| <color red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
| </systemColor> | |||
| <systemColor name="systemBackgroundColor"> | |||
| <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
| </systemColor> | |||
| </resources> | |||
| </document> | |||
| @@ -0,0 +1,60 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleBBQParamModel.h | |||
| // AILinkComponentMeatProbe | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @interface ELMeatProbeBoxMultipleBleBBQParamModel : NSObject | |||
| //生成时间 | |||
| @property (nonatomic, assign) NSTimeInterval timestamp; | |||
| @property (nonatomic, strong) NSData *macData; | |||
| @property (nonatomic, copy, readonly) NSString *macString; | |||
| @property (nonatomic, assign) UInt8 state; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始 | |||
| @property (nonatomic, assign) UInt8 foodType; //食物类型 | |||
| @property (nonatomic, assign) UInt16 targetTemperatureRaw_C; //食物目标温度:℃ | |||
| @property (nonatomic, assign, readonly) NSInteger targetTemperature_C; | |||
| @property (nonatomic, assign) UInt16 targetTemperatureRaw_F; //食物目标温度:℉ | |||
| @property (nonatomic, assign, readonly) NSInteger targetTemperature_F; | |||
| @property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_C; //炉温目标下限:℃ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_C; | |||
| @property (nonatomic, assign) UInt16 ambientMinTemperatureRaw_F; //炉温目标下限:℉ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMinTemperature_F; | |||
| @property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_C; //炉温目标上限:℃ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_C; | |||
| @property (nonatomic, assign) UInt16 ambientMaxTemperatureRaw_F; //炉温目标上限:℉ | |||
| @property (nonatomic, assign, readonly) NSInteger ambientMaxTemperature_F; | |||
| @property (nonatomic, assign) float alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0 | |||
| @property (nonatomic, assign) UInt32 cookingId; //CookingID | |||
| @property (nonatomic, assign) UInt32 workTime; //任务已运行时间,单位s | |||
| @property (nonatomic, assign) UInt32 timerCount; //计时剩余时间(秒) | |||
| @property (nonatomic, assign) UInt8 foodRawness; //食物熟度 (maturityID) | |||
| @property (nonatomic, strong) NSData *remarkData; | |||
| - (void)clearAll; | |||
| - (void)cookDone; | |||
| - (NSData *)dataValue; | |||
| - (NSString *)remark; | |||
| - (NSString *)setRemark:(NSString *)remark; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,128 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleBBQParamModel.m | |||
| // AILinkComponentMeatProbe | |||
| // | |||
| #import "ELMeatProbeBoxMultipleBleBBQParamModel.h" | |||
| @implementation ELMeatProbeBoxMultipleBleBBQParamModel | |||
| - (NSString *)macString { | |||
| if (self.macData == nil || self.macData.length != 6) { | |||
| return nil; | |||
| } | |||
| Byte *bytes = (Byte *)self.macData.bytes; | |||
| NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]]; | |||
| return str; | |||
| } | |||
| - (NSInteger)targetTemperature_C { | |||
| NSInteger value = self.targetTemperatureRaw_C & 0x3FFF; | |||
| if (self.targetTemperatureRaw_C & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)targetTemperature_F { | |||
| NSInteger value = self.targetTemperatureRaw_F & 0x3FFF; | |||
| if (self.targetTemperatureRaw_F & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMinTemperature_C { | |||
| NSInteger value = self.ambientMinTemperatureRaw_C & 0x3FFF; | |||
| if (self.ambientMinTemperatureRaw_C & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMinTemperature_F { | |||
| NSInteger value = self.ambientMinTemperatureRaw_F & 0x3FFF; | |||
| if (self.ambientMinTemperatureRaw_F & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMaxTemperature_C { | |||
| NSInteger value = self.ambientMaxTemperatureRaw_C & 0x3FFF; | |||
| if (self.ambientMaxTemperatureRaw_C & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (NSInteger)ambientMaxTemperature_F { | |||
| NSInteger value = self.ambientMaxTemperatureRaw_F & 0x3FFF; | |||
| if (self.ambientMaxTemperatureRaw_F & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (void)clearAll { | |||
| self.state = 0; //State: 0x00:未开始,结束任务时,状态切为未开始,未开始状态时,不需要判定其他参数 0x01:已开始,开始任务时,状态切位已开始 | |||
| self.foodType = 0xFF; //食物类型 | |||
| self.targetTemperatureRaw_C = 0;//0xFFFF; //食物目标温度:℃// | |||
| self.targetTemperatureRaw_F = 0;//0xFFFF; //食物目标温度:℉// | |||
| self.ambientMaxTemperatureRaw_C = 0;//0xFFFF; //炉温目标上限:℃ | |||
| self.ambientMaxTemperatureRaw_F = 0;//0xFFFF; //炉温目标上限:℉ | |||
| self.ambientMinTemperatureRaw_C = 0;//0xFFFF; //炉温目标下限:℃ | |||
| self.ambientMinTemperatureRaw_F = 0;//0xFFFF; //炉温目标下限:℉ | |||
| self.cookingId = 0; //CookingID | |||
| self.workTime = 0; //任务已运行时间,单位s | |||
| self.timerCount = 0; //计时剩余时间(秒) | |||
| self.alarmTemperaturePercent = 0.95; //提醒温度对目标温度百分比 0~1.0 | |||
| self.foodRawness = 0xFF; //食物熟度 | |||
| Byte bytes[32] = {0}; | |||
| self.remarkData = [NSData dataWithBytes:bytes length:sizeof(bytes)]; | |||
| } | |||
| - (void)cookDone { | |||
| [self clearAll]; | |||
| } | |||
| - (NSData *)dataValue { | |||
| NSMutableData *data = [NSMutableData data]; | |||
| [data appendData:self.macData]; | |||
| UInt32 alarmTemperaturePercent = 0; | |||
| memcpy(&alarmTemperaturePercent, &(_alarmTemperaturePercent), sizeof(float)); | |||
| Byte bytes[] = { self.state, self.foodType, self.targetTemperatureRaw_C, self.targetTemperatureRaw_C >> 8, self.targetTemperatureRaw_F, self.targetTemperatureRaw_F >> 8, self.ambientMaxTemperatureRaw_C, self.ambientMaxTemperatureRaw_C >> 8, self.ambientMaxTemperatureRaw_F, self.ambientMaxTemperatureRaw_F >> 8, self.ambientMinTemperatureRaw_C, self.ambientMinTemperatureRaw_C >> 8, self.ambientMinTemperatureRaw_F, self.ambientMinTemperatureRaw_F >> 8, self.cookingId, self.cookingId >> 8, self.cookingId >> 16, self.cookingId >> 24, self.workTime, self.workTime >> 8, self.workTime >> 16, self.workTime >> 24, self.timerCount, self.timerCount >> 8, self.timerCount >> 16, self.timerCount >> 24, alarmTemperaturePercent, alarmTemperaturePercent >> 8, alarmTemperaturePercent >> 16, alarmTemperaturePercent >> 24, self.foodRawness }; | |||
| [data appendBytes:bytes length:sizeof(bytes)]; | |||
| [data appendData:self.remarkData]; | |||
| return data.copy; | |||
| } | |||
| - (NSString *)setRemark:(NSString *)remark { | |||
| NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| if (data.length > 32) { | |||
| while (data.length > 29) { | |||
| remark = [remark substringToIndex:(remark.length - 1)]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| remark = [remark stringByAppendingString:@"..."]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| Byte *bytes = (Byte *)data.bytes; | |||
| NSUInteger total = data.length > 32 ? 32 : data.length; | |||
| Byte remarkBytes[32] = {0}; | |||
| memset(remarkBytes, 0, 32); | |||
| memcpy(remarkBytes, bytes, total); | |||
| self.remarkData = [NSData dataWithBytes:remarkBytes length:sizeof(remarkBytes)]; | |||
| return [self remark]; | |||
| } | |||
| - (NSString *)remark { | |||
| Byte remarkBytes[64] = {0}; | |||
| [self.remarkData getBytes:remarkBytes length:self.remarkData.length]; | |||
| NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes]; | |||
| return str; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,32 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleCmdTool.h | |||
| // Pods | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| #import "ELMeatProbeBoxMultipleBleHeader.h" | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @class ELAILinkBleManager; | |||
| @class ELMeatProbeBoxMultipleStatusModel, ELMeatProbeBoxMultipleProbeStatusModel, ELMeatProbeBoxMultipleBleBBQParamModel; | |||
| @interface ELMeatProbeBoxMultipleBleCmdTool : NSObject | |||
| //APP获取设备状态数据(Type:0x01) | |||
| + (NSData *)requestStatus:(ELAILinkBleManager *)manager; | |||
| //切换单位 | |||
| + (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager; | |||
| //获取设置烧烤任务参数(Type:0x09) | |||
| + (NSData *)requestParams:(ELAILinkBleManager *)manager; | |||
| + (NSData *)setupParam:(ELMeatProbeBoxMultipleBleBBQParamModel *)param supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager; | |||
| + (void)requestIColornfoWithManager:(ELAILinkBleManager *)manager; | |||
| + (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,62 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleCmdTool.m | |||
| // Pods | |||
| // | |||
| #import "ELMeatProbeBoxMultipleBleCmdTool.h" | |||
| #import <AILinkBleSDK/ELAILinkBleManager.h> | |||
| #import "ELMeatProbeBoxMultipleBleBBQParamModel.h" | |||
| @implementation ELMeatProbeBoxMultipleBleCmdTool | |||
| //APP获取设备基础信息 | |||
| + (NSData *)requestStatus:(ELAILinkBleManager *)manager { | |||
| const Byte cmd = ELMeatProbeBoxMultipleCMDAppGetStatus_Type01; | |||
| Byte payloadBytes[] = { cmd, 0x01 }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| //切换单位 | |||
| + (NSData *)switchUnit:(UInt8)unit manager:(ELAILinkBleManager *)manager { | |||
| if (unit > 1) { return nil; } | |||
| const Byte cmd = ELMeatProbeBoxMultipleCCMDAppSwitchUnit_Type03; | |||
| Byte payloadBytes[] = { cmd, unit }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| + (NSData *)requestParams:(ELAILinkBleManager *)manager { | |||
| const Byte cmd = ELMeatProbeBoxMultipleCMDSetInfo_Type09; | |||
| Byte payloadBytes[] = { cmd, 0x01 }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| + (NSData *)setupParam:(ELMeatProbeBoxMultipleBleBBQParamModel *)param supportProbe:(UInt8)supportProbe manager:(ELAILinkBleManager *)manager { | |||
| const Byte cmd = ELMeatProbeBoxMultipleCMDSetInfo_Type09; | |||
| Byte payloadBytes[] = { cmd, 0x00, supportProbe }; | |||
| NSMutableData *payloadData = [NSMutableData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [payloadData appendData:param.dataValue]; | |||
| [manager sendA7Payload:payloadData.copy]; | |||
| return [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| } | |||
| + (void)requestIColornfoWithManager:(ELAILinkBleManager *)manager { | |||
| const Byte read = 0x00; | |||
| Byte payloadBytes[] = { ELMeatProbeBoxMultipleCMDColorInfoRequest_Type0B, read }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA7Payload:payloadData.copy]; | |||
| } | |||
| + (void)requestOTAStatusWithManager:(ELAILinkBleManager *)manager { | |||
| const Byte ELInetGetCmdTypeSpecialFunc = 0x49, op = 0x01; | |||
| Byte payloadBytes[16] = { ELInetGetCmdTypeSpecialFunc, op }; | |||
| NSData *payloadData = [NSData dataWithBytes:payloadBytes length:sizeof(payloadBytes)]; | |||
| [manager sendA6Payload:payloadData.copy]; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,36 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleHeader.h | |||
| // Pods | |||
| // | |||
| #ifndef ELMeatProbeBoxMultipleBleHeader_h | |||
| #define ELMeatProbeBoxMultipleBleHeader_h | |||
| typedef NS_ENUM(UInt8, ELMeatProbeBoxMultipleCMD) { | |||
| ELMeatProbeBoxMultipleCMDAppGetStatus_Type01 = 0x01, //0x01:Type:APP 获取设备状态 | |||
| ELMeatProbeBoxMultipleCMDMcuUploadStatus_Type02 = 0x02, //0x02:Type:设备上报设备数据 | |||
| ELMeatProbeBoxMultipleCCMDAppSwitchUnit_Type03 = 0x03, //0x03:Type:切换单位 | |||
| ELMeatProbeBoxMultipleCCMDAppSwitchUnitResponse_Type04 = 0x04, //0x04:Type:切换单位 response | |||
| ELMeatProbeBoxMultipleCMDStartAlarmState_Type05 = 0x05, //0x05:Type:发起报警 | |||
| ELMeatProbeBoxMultipleCMDStartAlarmStateResponse_Type06 = 0x06, //0x06:Type:发起报警 Response | |||
| ELMeatProbeBoxMultipleCMDStoplAlarm_Type07 = 0x07, //0x07:Type:停止报警 | |||
| ELMeatProbeBoxMultipleCMDStoplAlarmResponse_Type08 = 0x08, //0x08:Type:停止报警 Response | |||
| ELMeatProbeBoxMultipleCMDSetInfo_Type09 = 0x09, //Type:0x09 设置烧烤任务参数 | |||
| ELMeatProbeBoxMultipleCMDSetInfoResponse_Type0A = 0x0A, //Type:0x0A 设置烧烤任务参数 Response | |||
| ELMeatProbeBoxMultipleCMDColorInfoRequest_Type0B = 0x0B, | |||
| ELMeatProbeBoxMultipleCMDColorInfoResponse_Type0C = 0x0C, | |||
| }; | |||
| #endif /* ELMeatProbeBoxMultipleBleHeader_h */ | |||
| @@ -0,0 +1,128 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleParser.h | |||
| // Pods | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| #import <AILinkBleSDK/ELSDKHeader.h> | |||
| #import "ELMeatProbeBoxMultipleBleHeader.h" | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @protocol ELMeatProbeBoxMultipleBleParserDelegate; | |||
| @class ELMeatProbeBoxMultipleStatusModel, ELMeatProbeBoxMultipleProbeStatusModel, ELMeatProbeBoxMultipleBleBBQParamModel, ELMeatProbeBoxMultipleColorInfoModel; | |||
| @interface ELMeatProbeBoxMultipleBleParser : NSObject | |||
| @property (nonatomic, copy) NSString *mac; | |||
| @property (nonatomic, assign) NSInteger deviceId; | |||
| @property (nonatomic, weak) id<ELMeatProbeBoxMultipleBleParserDelegate> delegate; | |||
| - (void)parseA7Payload:(NSData *)payload; | |||
| @end | |||
| @protocol ELMeatProbeBoxMultipleBleParserDelegate <NSObject> | |||
| @optional | |||
| //MCU 上报设备状态数据(Type:02) | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser boxStatus:(ELMeatProbeBoxMultipleStatusModel *)boxStatus; | |||
| //切换单位(Type:04) | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser unit:(ELDeviceTemperatureUnit)unit; | |||
| //报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1 | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac; | |||
| //取消报警(Type:08) | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac; | |||
| //烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务 | |||
| //- (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe params:(NSArray <ELMeatProbeBoxMultipleBleBBQParamModel *>*)params; | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe param:(ELMeatProbeBoxMultipleBleBBQParamModel *)param; | |||
| //color info | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser colorInfos:(NSArray<ELMeatProbeBoxMultipleColorInfoModel *> *)colorInfos; | |||
| @end | |||
| @interface ELMeatProbeBoxMultipleStatusModel : NSObject | |||
| //设备状态数据协议版本:0x01 | |||
| @property (nonatomic, assign) UInt8 version; | |||
| //支持的探针数量 M | |||
| @property (nonatomic, assign) UInt8 probeMaxCount; | |||
| //当前连接的探针数量 N | |||
| @property (nonatomic, assign) UInt8 probeCount; | |||
| //充电盒电池状态 | |||
| @property (nonatomic, assign) UInt8 batteryStatus; | |||
| @property (nonatomic, assign) UInt8 batteryPercent; | |||
| //温度单位 | |||
| @property (nonatomic, assign) ELDeviceTemperatureUnit unit; | |||
| //生成时间 | |||
| @property (nonatomic, assign) NSTimeInterval timestamp; | |||
| @property (nonatomic, strong) NSArray <ELMeatProbeBoxMultipleProbeStatusModel *>*probeStatusArray; | |||
| //盒子 mac 地址 | |||
| @property (nonatomic, copy) NSString *boxMacString; | |||
| @end | |||
| @interface ELMeatProbeBoxMultipleProbeStatusModel : NSObject | |||
| //探针编号(1-M) 无法确定可连 接探针的编号,上报 0xFF | |||
| @property (nonatomic, assign) UInt8 index; | |||
| //探针 mac 地址 | |||
| @property (nonatomic, strong) NSData *macData; | |||
| @property (nonatomic, copy, readonly) NSString *macString; | |||
| // | |||
| //食物温度 | |||
| @property (nonatomic, assign, readonly) NSInteger internalTemperature_C; | |||
| @property (nonatomic, assign, readonly) NSInteger internalTemperature_F; | |||
| //@property (nonatomic, assign, readonly) NSInteger internalTemperature; | |||
| @property (nonatomic, assign, readonly) ELDeviceTemperatureUnit internalTemperatureUnit; | |||
| @property (nonatomic, assign) UInt16 internalTemperatureRaw; | |||
| // | |||
| //环境温度 | |||
| @property (nonatomic, assign, readonly) NSInteger ambientTemperature_C; | |||
| @property (nonatomic, assign, readonly) NSInteger ambientTemperature_F; | |||
| //@property (nonatomic, assign, readonly) NSInteger ambientTemperature; | |||
| @property (nonatomic, assign, readonly) ELDeviceTemperatureUnit ambientTemperatureUnit; | |||
| @property (nonatomic, assign) UInt16 ambientTemperatureRaw; | |||
| // | |||
| //探针电池状态 | |||
| @property (nonatomic, assign) UInt8 batteryStatus; | |||
| @property (nonatomic, assign) UInt8 batteryPercent; | |||
| @property (nonatomic, assign) UInt8 batteryRaw; | |||
| //探针插入食物状态 | |||
| @property (nonatomic, assign) UInt8 insertStatusRaw; | |||
| //探针连接状态 | |||
| @property (nonatomic, assign) UInt8 connectState; | |||
| //生成时间 | |||
| @property (nonatomic, assign) double timestamp; | |||
| @end | |||
| @interface ELMeatProbeBoxMultipleColorInfoModel : NSObject | |||
| @property (nonatomic, assign) UInt8 probeIndex; | |||
| @property (nonatomic, assign) UInt8 colorIndex; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,325 @@ | |||
| // | |||
| // ELMeatProbeBoxMultipleBleParser.m | |||
| // Pods | |||
| // | |||
| #import "ELMeatProbeBoxMultipleBleParser.h" | |||
| #import "ELMeatProbeBoxMultipleBleBBQParamModel.h" | |||
| @interface ELMeatProbeBoxMultipleBleParser() | |||
| //@property (nonatomic, strong) ELMeatProbeBoxMultipleStatusModel *boxStatus; | |||
| @end | |||
| @implementation ELMeatProbeBoxMultipleBleParser | |||
| - (void)parseA7Payload:(nonnull NSData *)payload { | |||
| NSLog(@"#### parseA7Payload payload:%@", payload.debugDescription); | |||
| Byte *bytes = (Byte *)payload.bytes; | |||
| Byte cmd = bytes[0]; | |||
| switch (cmd) { | |||
| case ELMeatProbeBoxMultipleCMDMcuUploadStatus_Type02: | |||
| [self parseStatusData:payload]; | |||
| break; | |||
| case ELMeatProbeBoxMultipleCCMDAppSwitchUnit_Type03: | |||
| { | |||
| UInt8 unit = bytes[1]; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:unit:)]) { | |||
| [self.delegate meatProbeBoxMultipleBleParser:self unit:unit]; | |||
| } | |||
| } | |||
| break; | |||
| // case ELMeatProbeBoxMultipleCCMDAppSwitchUnitResponse_Type04: | |||
| // break; | |||
| case ELMeatProbeBoxMultipleCMDStartAlarmState_Type05: { | |||
| Byte i = 1; | |||
| NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)]; | |||
| i += 6; | |||
| Byte state = bytes[i]; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:startAlarmWithState:macData:mac:)]) { | |||
| [self.delegate meatProbeBoxMultipleBleParser:self startAlarmWithState:state macData:macData mac:[self macDataToString:macData]]; | |||
| } | |||
| } | |||
| break; | |||
| // case ELMeatProbeBoxMultipleCMDStartAlarmStateResponse_Type06: | |||
| // break; | |||
| case ELMeatProbeBoxMultipleCMDStoplAlarm_Type07: { | |||
| Byte i = 1; | |||
| NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)]; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:stopAlarmWithMacData:mac:)]) { | |||
| [self.delegate meatProbeBoxMultipleBleParser:self stopAlarmWithMacData:macData mac:[self macDataToString:macData]]; | |||
| } | |||
| } | |||
| break; | |||
| // case ELMeatProbeBoxMultipleCMDStoplAlarmResponse_Type08: | |||
| // break; | |||
| case ELMeatProbeBoxMultipleCMDSetInfo_Type09: | |||
| case ELMeatProbeBoxMultipleCMDSetInfoResponse_Type0A: { | |||
| [self parseSetInfoParamData:payload]; | |||
| } | |||
| break; | |||
| case ELMeatProbeBoxMultipleCMDColorInfoResponse_Type0C: { | |||
| NSMutableArray *arr = [NSMutableArray array]; | |||
| if (payload.length < 4) break; | |||
| for (int i=2; i<payload.length; i+=2) { | |||
| UInt8 probeIndex = bytes[i]; | |||
| UInt8 colorIndex = bytes[i+1]; | |||
| ELMeatProbeBoxMultipleColorInfoModel *color = [[ELMeatProbeBoxMultipleColorInfoModel alloc] init]; | |||
| color.probeIndex = probeIndex; | |||
| color.colorIndex = colorIndex; | |||
| [arr addObject:color]; | |||
| } | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:colorInfos:)]) { | |||
| [self.delegate meatProbeBoxMultipleBleParser:self colorInfos:arr.copy]; | |||
| } | |||
| } | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| - (NSString *)macDataToString:(NSData *)macData { | |||
| if (macData == nil || macData.length != 6) { | |||
| return nil; | |||
| } | |||
| Byte *bytes = (Byte *)macData.bytes; | |||
| NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]]; | |||
| return str; | |||
| } | |||
| - (void)parseSetInfoParamData:(NSData *)payload { | |||
| NSLog(@"##### parseSetInfoParamData payload:%@", payload.debugDescription); | |||
| if (payload.length != 72 && payload.length != 140) { | |||
| return; | |||
| } | |||
| Byte *bytes = (Byte *)payload.bytes; | |||
| Byte i = 1; | |||
| Byte option = bytes[i]; | |||
| i = 2; | |||
| Byte supportProbe = bytes[i]; | |||
| i = 3; | |||
| // NSMutableArray *arr = [NSMutableArray array]; | |||
| // | |||
| // while (i < payload.length) { | |||
| //探针 1 | |||
| ELMeatProbeBoxMultipleBleBBQParamModel *param = [[ELMeatProbeBoxMultipleBleBBQParamModel alloc] init]; | |||
| param.timestamp = [NSDate date].timeIntervalSince1970; | |||
| param.macData = [payload subdataWithRange:NSMakeRange(i, 6)]; | |||
| i += 6; | |||
| param.state = bytes[i]; | |||
| i += 1; | |||
| param.foodType = bytes[i];//if (param.foodType == 0xFF) param.foodType = 0xFE; | |||
| i += 1; | |||
| param.targetTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.targetTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMaxTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMaxTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMinTemperatureRaw_C = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.ambientMinTemperatureRaw_F = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| i += 2; | |||
| param.cookingId = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| i += 4; | |||
| param.workTime = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| i += 4; | |||
| param.timerCount = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| i += 4; | |||
| UInt32 alarmTemperaturePercentU32 = (bytes[i] << 0) | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); | |||
| float alarmTemperaturePercent = 0; | |||
| memcpy(&alarmTemperaturePercent, &alarmTemperaturePercentU32, sizeof(UInt32)); | |||
| param.alarmTemperaturePercent = alarmTemperaturePercent; | |||
| i += 4; | |||
| param.foodRawness = bytes[i]; | |||
| i += 1; | |||
| param.remarkData = [payload subdataWithRange:NSMakeRange(i, 32)]; | |||
| i += 32; | |||
| if (param.state == 0) { | |||
| [param clearAll]; | |||
| } | |||
| // [arr addObject:param]; | |||
| // } | |||
| // if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:option:supportProbe:params:)]) { | |||
| // [self.delegate meatProbeBoxMultipleBleParser:self option:option supportProbe:supportProbe params:arr.copy]; | |||
| // } | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:option:supportProbe:param:)]) { | |||
| [self.delegate meatProbeBoxMultipleBleParser:self option:option supportProbe:supportProbe param:param]; | |||
| } | |||
| } | |||
| - (void)parseStatusData:(NSData *)payload { | |||
| if (payload.length != 25 && payload.length != 41) { | |||
| return; | |||
| } | |||
| Byte *bytes = (Byte *)payload.bytes; | |||
| ELMeatProbeBoxMultipleStatusModel *boxStatus = [[ELMeatProbeBoxMultipleStatusModel alloc] init]; | |||
| boxStatus.timestamp = [NSDate date].timeIntervalSince1970; | |||
| boxStatus.boxMacString = self.mac; | |||
| Byte i = 1; | |||
| boxStatus.version = bytes[i]; | |||
| i = 2; | |||
| boxStatus.probeMaxCount = bytes[i]; | |||
| i = 3; | |||
| boxStatus.probeCount = bytes[i]; | |||
| i = 4; | |||
| boxStatus.batteryStatus = bytes[i] >> 7; | |||
| boxStatus.batteryPercent = bytes[i] & 0x7F; | |||
| i = 5; | |||
| boxStatus.unit = bytes[i]; | |||
| i = 9; | |||
| NSMutableArray *arr = [NSMutableArray array]; | |||
| while (i < payload.length) { | |||
| ELMeatProbeBoxMultipleProbeStatusModel *probeStatus = [[ELMeatProbeBoxMultipleProbeStatusModel alloc] init]; | |||
| probeStatus.timestamp = boxStatus.timestamp; | |||
| probeStatus.index = bytes[i]; | |||
| i += 1; | |||
| NSData *macData = [payload subdataWithRange:NSMakeRange(i, 6)]; | |||
| probeStatus.macData = macData; | |||
| i += 6; | |||
| probeStatus.internalTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| // 凸(艹皿艹 ) //0x639F (25503) | |||
| if (probeStatus.internalTemperatureRaw == 25503) { | |||
| probeStatus.internalTemperatureRaw = 0xFFFF; | |||
| } | |||
| i += 2; | |||
| probeStatus.ambientTemperatureRaw = (bytes[i] << 0) | (bytes[i+1] << 8); | |||
| // 凸(艹皿艹 ) //0x639F (25503) | |||
| if (probeStatus.ambientTemperatureRaw == 25503) { | |||
| probeStatus.ambientTemperatureRaw = 0xFFFF; | |||
| } | |||
| i += 2; | |||
| probeStatus.batteryStatus = bytes[i] >> 7; | |||
| probeStatus.batteryPercent = bytes[i] & 0x7F; | |||
| probeStatus.batteryRaw = bytes[i]; | |||
| i += 1; | |||
| probeStatus.insertStatusRaw = bytes[i]; | |||
| i += 1; | |||
| probeStatus.connectState = bytes[i]; | |||
| i += 3; | |||
| [arr addObject:probeStatus]; | |||
| } | |||
| boxStatus.probeStatusArray = arr.copy; | |||
| if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxMultipleBleParser:boxStatus:)]) { | |||
| [self.delegate meatProbeBoxMultipleBleParser:self boxStatus:boxStatus]; | |||
| } | |||
| } | |||
| @end | |||
| @implementation ELMeatProbeBoxMultipleStatusModel | |||
| @end | |||
| @implementation ELMeatProbeBoxMultipleProbeStatusModel | |||
| - (NSString *)macString { | |||
| if (self.macData == nil || self.macData.length != 6) { | |||
| return nil; | |||
| } | |||
| Byte *bytes = (Byte *)self.macData.bytes; | |||
| NSString *str = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]]; | |||
| return str; | |||
| } | |||
| - (NSInteger)internalTemperature_C { | |||
| if (self.internalTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_C) { | |||
| return self.internalTemperature; | |||
| } | |||
| return [self convertF2C:self.internalTemperature]; | |||
| } | |||
| - (NSInteger)internalTemperature_F { | |||
| if (self.internalTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.internalTemperatureUnit == ELDeviceTemperatureUnit_F) { | |||
| return self.internalTemperature; | |||
| } | |||
| return [self convertC2F:self.internalTemperature]; | |||
| } | |||
| - (NSInteger)internalTemperature { | |||
| NSInteger value = self.internalTemperatureRaw & 0x3FFF; | |||
| if (self.internalTemperatureRaw & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (ELDeviceTemperatureUnit)internalTemperatureUnit { | |||
| return self.internalTemperatureRaw >> 15; | |||
| } | |||
| - (NSInteger)ambientTemperature_C { | |||
| if (self.ambientTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_C) { | |||
| return self.ambientTemperature; | |||
| } | |||
| return [self convertF2C:self.ambientTemperature]; | |||
| } | |||
| - (NSInteger)ambientTemperature_F { | |||
| if (self.ambientTemperatureRaw == 0xFFFF) { | |||
| return 0xFFFF; | |||
| } | |||
| if (self.ambientTemperatureUnit == ELDeviceTemperatureUnit_F) { | |||
| return self.ambientTemperature; | |||
| } | |||
| return [self convertC2F:self.ambientTemperature]; | |||
| } | |||
| - (NSInteger)ambientTemperature { | |||
| NSInteger value = self.ambientTemperatureRaw & 0x3FFF; | |||
| if (self.ambientTemperatureRaw & 0x4000) { | |||
| value = -value; | |||
| } | |||
| return value; | |||
| } | |||
| - (ELDeviceTemperatureUnit)ambientTemperatureUnit { | |||
| return self.ambientTemperatureRaw >> 15; | |||
| } | |||
| //温度换算 | |||
| - (float)convertF2C:(float)fahrenheit { | |||
| return round((fahrenheit - 32) / 1.8); | |||
| } | |||
| - (float)convertC2F:(float)celsius { | |||
| return round(celsius * 1.8 + 32); | |||
| } | |||
| @end | |||
| @implementation ELMeatProbeBoxMultipleColorInfoModel | |||
| @end | |||
| @@ -0,0 +1,21 @@ | |||
| // | |||
| // MultipleMeatProbeBoxVC.h | |||
| // AILinkBleSDK_Example | |||
| // | |||
| // Created by LarryZhang on 2024/9/7. | |||
| // Copyright © 2024 zhengzida. All rights reserved. | |||
| // | |||
| #import <UIKit/UIKit.h> | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| @class ELAILinkPeripheral; | |||
| @interface MultipleMeatProbeBoxVC : UIViewController | |||
| @property (nonatomic, strong) ELAILinkPeripheral *per; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,334 @@ | |||
| // | |||
| // MultipleMeatProbeBoxVC.m | |||
| // AILinkBleSDK_Example | |||
| // | |||
| // Created by LarryZhang on 2024/9/7. | |||
| // Copyright © 2024 zhengzida. All rights reserved. | |||
| // | |||
| #import "MultipleMeatProbeBoxVC.h" | |||
| #import <AILinkBleSDK/ELAILinkBleManager.h> | |||
| #import <AILinkBleSDK/NSData+AILinkBle.h> | |||
| #import "ELMeatProbeBoxMultipleBleParser.h" | |||
| #import "ELMeatProbeBoxMultipleBleCmdTool.h" | |||
| #import "ELMeatProbeBoxMultipleBleBBQParamModel.h" | |||
| @interface MultipleMeatProbeBoxVC () <ELAILinkBleManagerDelegate, ELMeatProbeBoxMultipleBleParserDelegate> | |||
| @property (weak, nonatomic) IBOutlet UILabel *bleStatusLabel; | |||
| @property (weak, nonatomic) IBOutlet UITextView *logTextView; | |||
| @property (nonatomic, strong) ELAILinkBleManager *bleManager; | |||
| @property (nonatomic, assign) NELBleManagerConnectState bleConnectState; | |||
| @property (nonatomic, strong) ELMeatProbeBoxMultipleBleParser *bleParser; | |||
| @property (nonatomic, strong) ELMeatProbeBoxMultipleStatusModel *boxStatus; | |||
| @property (nonatomic, strong) NSMutableDictionary<NSString *, ELMeatProbeBoxMultipleBleBBQParamModel *> *paramDic; | |||
| @end | |||
| @implementation MultipleMeatProbeBoxVC | |||
| - (void)viewDidLoad { | |||
| [super viewDidLoad]; | |||
| // Do any additional setup after loading the view from its nib. | |||
| self.paramDic = [NSMutableDictionary dictionary]; | |||
| [self initBle]; | |||
| } | |||
| - (void)dealloc { | |||
| [self deinitBle]; | |||
| } | |||
| #pragma mark - Action | |||
| - (IBAction)requestParamsAction:(id)sender { | |||
| NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool requestParams:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
| [self addLog:log]; | |||
| } | |||
| - (IBAction)setupParamsAction:(id)sender { | |||
| if (self.boxStatus == nil) { | |||
| return; | |||
| } | |||
| if (self.boxStatus.probeStatusArray == nil || self.boxStatus.probeStatusArray.count != 2) { | |||
| return; | |||
| } | |||
| ELMeatProbeBoxMultipleProbeStatusModel *status = self.boxStatus.probeStatusArray.firstObject; | |||
| if (status == nil) { | |||
| return; | |||
| } | |||
| ELMeatProbeBoxMultipleBleBBQParamModel *param = self.paramDic[status.macString]; | |||
| if (param == nil) { | |||
| return; | |||
| } | |||
| param.foodType = 1; | |||
| param.targetTemperatureRaw_C = 70; | |||
| param.targetTemperatureRaw_F = 212; | |||
| param.foodRawness = 1; | |||
| param.alarmTemperaturePercent = 0.95; | |||
| param.cookingId = [NSDate date].timeIntervalSince1970; | |||
| NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool setupParam:param supportProbe:1 manager:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
| [self addLog:log]; | |||
| } | |||
| - (IBAction)switchUnitAction:(id)sender { | |||
| if (self.boxStatus.unit == 1) { | |||
| NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool switchUnit:0 manager:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℃", payload]; | |||
| [self addLog:log]; | |||
| } else { | |||
| NSData *payload = [ELMeatProbeBoxMultipleBleCmdTool switchUnit:1 manager:self.bleManager]; | |||
| NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to ℉", payload]; | |||
| [self addLog:log]; | |||
| } | |||
| } | |||
| - (IBAction)clearAction:(id)sender { | |||
| self.logTextView.text = @""; | |||
| } | |||
| - (IBAction)shareLogAction:(id)sender { | |||
| } | |||
| #pragma mark - ELAILinkBleManagerDelegate | |||
| - (void)initBle { | |||
| self.bleManager = [[ELAILinkBleManager alloc] init]; | |||
| self.bleManager.ailinkDelegate = self; | |||
| self.bleParser = [[ELMeatProbeBoxMultipleBleParser alloc] init]; | |||
| self.bleParser.delegate = self; | |||
| self.bleParser.mac = self.per.macAddressString; | |||
| } | |||
| - (void)scanBle { | |||
| [self.bleManager scanFilterWithCidArray:@[@(self.per.cid)]]; | |||
| } | |||
| - (void)connect:(ELAILinkPeripheral *)peripheral { | |||
| [self.bleManager connectAILinkPeripheral:peripheral]; | |||
| [self updateBleStatusView:NELBleManagerConnectStateConnecting]; | |||
| } | |||
| - (void)deinitBle { | |||
| [self.bleManager stopScan]; | |||
| self.bleManager.ailinkDelegate = nil; | |||
| [self.bleManager disconnectPeripheral]; | |||
| } | |||
| - (void)updateBleStatusView:(NELBleManagerConnectState)state { | |||
| if (self.bleManager.central.state == CBManagerStatePoweredOff) { | |||
| self.bleStatusLabel.text = @"蓝牙关闭"; | |||
| [self addLog:self.bleStatusLabel.text]; | |||
| return; | |||
| } | |||
| switch (state) { | |||
| case NELBleManagerConnectStateDisconnected: | |||
| self.bleStatusLabel.text = @"断开连接"; | |||
| break; | |||
| case NELBleManagerConnectStateFailed: | |||
| case NELBleManagerConnectStateFailedValidation: | |||
| self.bleStatusLabel.text = @"连接失败"; | |||
| break; | |||
| case NELBleManagerConnectStateConnecting: | |||
| self.bleStatusLabel.text = @"正在连接..."; | |||
| break; | |||
| case NELBleManagerConnectStatePassed: | |||
| self.bleStatusLabel.text = @"连接成功"; | |||
| [self requestDeviceData]; | |||
| // read RSSI | |||
| // [self readRSSI]; | |||
| break; | |||
| case NELBleManagerConnectStateCentralScanning: | |||
| self.bleStatusLabel.text = @"正在扫描..."; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| [self addLog:self.bleStatusLabel.text]; | |||
| } | |||
| - (void)requestDeviceData { | |||
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{ | |||
| }); | |||
| } | |||
| // 设备状态变更 | |||
| - (void)managerDidUpdateState:(CBCentralManager *)central { | |||
| NSLog(@"%s state:%@", __func__, @(central.state)); | |||
| if (central.state == CBManagerStatePoweredOn) { | |||
| [self scanBle]; | |||
| } else if (central.state == CBManagerStatePoweredOff) { | |||
| self.bleConnectState = NELBleManagerConnectStateCentralPowerOff; | |||
| [self updateBleStatusView:NELBleManagerConnectStateCentralPowerOff]; | |||
| } | |||
| } | |||
| - (void)managerScanState:(BOOL)scanning { | |||
| NSLog(@"%s scanning:%@", __func__, @(scanning)); | |||
| if (scanning) { | |||
| [self updateBleStatusView:NELBleManagerConnectStateCentralScanning]; | |||
| } | |||
| } | |||
| // 扫描到设备 | |||
| - (void)managerDidDiscoverPeripheral:(ELAILinkPeripheral *)peripheral { | |||
| NSLog(@"managerDidDiscoverPeripheral cid:%02x vid:%02x pid:%02x mac:%@", peripheral.cid, peripheral.vid, peripheral.pid, peripheral.macAddressString); | |||
| if ([peripheral.macAddressString isEqualToString:self.per.macAddressString]) { | |||
| [self.bleManager stopScan]; | |||
| [self connect:peripheral]; | |||
| } | |||
| } | |||
| - (void)managerDidUpdateConnect:(NELBleManagerConnectState)state { | |||
| NSLog(@"%s NELBleManagerConnectState:%@", __func__, @(state)); | |||
| [self updateBleStatusView:state]; | |||
| } | |||
| //A7数据 | |||
| - (void)aiLinkBleReceiveA7Data:(NSData *)payload { | |||
| NSLog(@"%s #### payload:%@", __func__, payload.elHexString); | |||
| NSString *log = [NSString stringWithFormat:@"收: payload: %@", payload.elHexString]; | |||
| [self addLog:log]; | |||
| [self.bleParser parseA7Payload:payload]; | |||
| } | |||
| //A6数据 | |||
| - (void)aiLinkBleReceiveA6Data:(NSData *)packet { | |||
| NSLog(@"%s ##### packet:%@", __func__, packet.elHexString); | |||
| Byte *bytes = (Byte *)packet.bytes; | |||
| Byte cmd = bytes[2]; | |||
| if (cmd == ELInetGetCmdTypeGetBMVersion) { | |||
| NSString *bmVersion = self.bleManager.bmVersion; | |||
| NSLog(@"##### bmVersion: %@", bmVersion); | |||
| } | |||
| if (cmd == ELInetGetCmdTypeGetBMVersionPro) { | |||
| NSString *bmVersionPro = self.bleManager.bmVersionPro; | |||
| NSLog(@"##### bmVersionPro: %@", bmVersionPro); | |||
| } | |||
| } | |||
| #pragma mark - ELMeatProbeBoxMultipleBleParserDelegate | |||
| //MCU 上报设备状态数据(Type:02) | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser boxStatus:(ELMeatProbeBoxMultipleStatusModel *)boxStatus { NSLog(@"##### boxStatus(): %@ ", boxStatus.debugDescription); | |||
| self.boxStatus = boxStatus; | |||
| NSString *log = [NSString stringWithFormat:@"boxStatus: { mac:%@, probeMaxCount:%hhu, probeCount:%hhu, batteryStatus:%hhu batteryPercent:%hhu, unit:%ld }", parser.mac, boxStatus.probeMaxCount, boxStatus.probeCount, boxStatus.batteryStatus, boxStatus.batteryPercent, boxStatus.unit]; | |||
| [self addLog:log]; | |||
| ELMeatProbeBoxMultipleProbeStatusModel *probeStatus = boxStatus.probeStatusArray.firstObject; | |||
| NSString *log2 = [NSString stringWithFormat:@"probeStatus: { connectState:%@ macString:%hhu, internalTemperature_C:%ld ℃, ambientTemperature_C:%ld ℃ batteryStatus:%hhu batteryPercent:%hhu }", probeStatus.macString, probeStatus.connectState, probeStatus.internalTemperature_C, probeStatus.ambientTemperature_C, probeStatus.batteryStatus, probeStatus.batteryPercent]; | |||
| [self addLog:log2]; | |||
| } | |||
| //切换单位(Type:04) | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser unit:(ELDeviceTemperatureUnit)unit { | |||
| NSLog(@"##### unit(): %@", @(unit)); | |||
| NSString *log = [NSString stringWithFormat:@"unit(): %@", unit == 0 ? @"℃" : @"°F"]; | |||
| [self addLog:log]; | |||
| } | |||
| //报警状态数据(Type:06)//state: bit0:食物目标温度到达时置1 | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser startAlarmWithState:(Byte)state macData:(NSData *)macData mac:(NSString *)mac { | |||
| NSLog(@"##### box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state)); | |||
| NSString *log = [NSString stringWithFormat:@"box:%@ startAlarmWithState(): probe:%@ state:%@", parser.mac, mac, @(state)]; | |||
| [self addLog:log]; | |||
| } | |||
| //取消报警(Type:08) | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser stopAlarmWithMacData:(NSData *)macData mac:(NSString *)mac { | |||
| NSLog(@"##### box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac); | |||
| NSString *log = [NSString stringWithFormat:@"box:%@ stopAlarmWithMacData(): probe:%@ ", parser.mac, mac]; | |||
| [self addLog:log]; | |||
| } | |||
| //烧烤任务参数(Type:0A)//Op:0x00:设置任务参数(设置时需要一个针一个针设定任务) 0x01:获取任务参数(返回所有的针任务,如果包长度超MTU时,会分包发) 0x02:结束烧烤任务 | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser option:(UInt8)option supportProbe:(UInt8)supportProbe param:(ELMeatProbeBoxMultipleBleBBQParamModel *)param { | |||
| NSLog(@"##### box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe)); | |||
| NSLog(@"##### param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType)); | |||
| self.paramDic[param.macString] = param; | |||
| NSString *log = [NSString stringWithFormat:@"box:%@ meatProbeScreenBoxBleParser(): option:%@ supportProbe:%@ ", parser.mac, @(option), @(supportProbe)]; | |||
| NSString *log2 = [NSString stringWithFormat:@"param:%@ state:%@ foodType:%@", param.macString, @(param.state), @(param.foodType)]; | |||
| [self addLog:log]; | |||
| [self addLog:log2]; | |||
| } | |||
| //color info | |||
| - (void)meatProbeBoxMultipleBleParser:(ELMeatProbeBoxMultipleBleParser *)parser colorInfos:(NSArray<ELMeatProbeBoxMultipleColorInfoModel *> *)colorInfos { | |||
| NSLog(@"##### colorInfos(): %@", colorInfos); | |||
| if (colorInfos == nil || colorInfos.count == 0) { | |||
| return; | |||
| } | |||
| ELMeatProbeBoxMultipleColorInfoModel *colorInfo = colorInfos.firstObject; | |||
| NSString *log = [NSString stringWithFormat:@"colorInfos(): count:%@ probeIndex:%@ colorIndex:%@", @(colorInfos.count), @(colorInfo.probeIndex), @(colorInfo.colorIndex)]; | |||
| [self addLog:log]; | |||
| } | |||
| - (void)peripheralDidReadRSSI:(nonnull NSNumber *)RSSI { | |||
| NSLog(@"%s peripheralDidReadRSSI: %@", __FUNCTION__, RSSI); | |||
| // main thread | |||
| dispatch_async(dispatch_get_main_queue(), ^{ | |||
| [self addLog:[NSString stringWithFormat:@"peripheralDidReadRSSI: %@", RSSI]]; | |||
| }); | |||
| [self readRSSI]; | |||
| } | |||
| - (void)readRSSI { | |||
| // delay | |||
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |||
| // Read RSSI | |||
| [self.bleManager readRSSI]; | |||
| }); | |||
| } | |||
| #pragma mark - addLog | |||
| - (void)addLog:(NSString *)log { | |||
| [self addLog:log newline:YES]; | |||
| } | |||
| - (void)addWithoutNewlineLog:(NSString *)log { | |||
| [self addLog:log newline:NO]; | |||
| } | |||
| - (void)addLog:(NSString *)log newline:(BOOL)newline { | |||
| NSDateFormatter *format = [[NSDateFormatter alloc] init]; | |||
| format.dateFormat = @"HH:mm:ss.SSS"; | |||
| NSString *time = [format stringFromDate:[NSDate date]]; | |||
| if (newline) { | |||
| self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log]; | |||
| } else { | |||
| self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log]; | |||
| } | |||
| [self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)]; | |||
| } | |||
| @end | |||
| @@ -0,0 +1,133 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | |||
| <device id="retina6_12" orientation="portrait" appearance="light"/> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> | |||
| <capability name="Safe area layout guides" minToolsVersion="9.0"/> | |||
| <capability name="System colors in document resources" minToolsVersion="11.0"/> | |||
| <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | |||
| </dependencies> | |||
| <objects> | |||
| <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MultipleMeatProbeBoxVC"> | |||
| <connections> | |||
| <outlet property="bleStatusLabel" destination="avL-mp-uk3" id="FYb-Yh-SLT"/> | |||
| <outlet property="logTextView" destination="E0x-Fa-KTj" id="N8a-h2-SB1"/> | |||
| <outlet property="view" destination="x2W-jW-2kg" id="rLz-wJ-z8g"/> | |||
| </connections> | |||
| </placeholder> | |||
| <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | |||
| <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="x2W-jW-2kg"> | |||
| <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <subviews> | |||
| <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="avL-mp-uk3"> | |||
| <rect key="frame" x="0.0" y="59" width="393" height="21"/> | |||
| <fontDescription key="fontDescription" type="system" pointSize="17"/> | |||
| <nil key="textColor"/> | |||
| <nil key="highlightedColor"/> | |||
| </label> | |||
| <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vOk-FU-BQ9"> | |||
| <rect key="frame" x="8" y="380" width="377" height="438"/> | |||
| <subviews> | |||
| <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E0x-Fa-KTj"> | |||
| <rect key="frame" x="0.0" y="0.0" width="377" height="438"/> | |||
| <color key="backgroundColor" systemColor="labelColor"/> | |||
| <color key="textColor" systemColor="opaqueSeparatorColor"/> | |||
| <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="10"/> | |||
| <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> | |||
| </textView> | |||
| </subviews> | |||
| <color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
| <constraints> | |||
| <constraint firstAttribute="bottom" secondItem="E0x-Fa-KTj" secondAttribute="bottom" id="BHm-cI-1oG"/> | |||
| <constraint firstItem="E0x-Fa-KTj" firstAttribute="leading" secondItem="vOk-FU-BQ9" secondAttribute="leading" id="KII-WE-P1m"/> | |||
| <constraint firstAttribute="trailing" secondItem="E0x-Fa-KTj" secondAttribute="trailing" id="nXv-F2-TLg"/> | |||
| <constraint firstItem="E0x-Fa-KTj" firstAttribute="top" secondItem="vOk-FU-BQ9" secondAttribute="top" id="uJZ-ST-1Ce"/> | |||
| </constraints> | |||
| <userDefinedRuntimeAttributes> | |||
| <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> | |||
| <real key="value" value="8"/> | |||
| </userDefinedRuntimeAttribute> | |||
| </userDefinedRuntimeAttributes> | |||
| </view> | |||
| <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="e5p-j3-6VG"> | |||
| <rect key="frame" x="354" y="342" width="31" height="30"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="分享"/> | |||
| <connections> | |||
| <action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="8YU-6o-y8q"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FoH-SE-VcD"> | |||
| <rect key="frame" x="8" y="342" width="31" height="30"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="清空"/> | |||
| <connections> | |||
| <action selector="clearAction:" destination="-1" eventType="touchUpInside" id="sBW-gD-CZ4"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="d8W-4g-bv9"> | |||
| <rect key="frame" x="16" y="96" width="128" height="35"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="Button"/> | |||
| <buttonConfiguration key="configuration" style="filled" title="请求烧烤参数"/> | |||
| <connections> | |||
| <action selector="requestParamsAction:" destination="-1" eventType="touchUpInside" id="zbL-Pf-OOz"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RUX-kg-vMY"> | |||
| <rect key="frame" x="176" y="96" width="128" height="35"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="Button"/> | |||
| <buttonConfiguration key="configuration" style="filled" title="设置烧烤参数"/> | |||
| <connections> | |||
| <action selector="setupParamsAction:" destination="-1" eventType="touchUpInside" id="acG-k5-idT"/> | |||
| </connections> | |||
| </button> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dXn-E2-KcZ"> | |||
| <rect key="frame" x="16" y="147" width="93.333333333333329" height="35"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| <state key="normal" title="Button"/> | |||
| <buttonConfiguration key="configuration" style="filled" title="单位切换"/> | |||
| <connections> | |||
| <action selector="switchUnitAction:" destination="-1" eventType="touchUpInside" id="rCn-DM-9bK"/> | |||
| </connections> | |||
| </button> | |||
| </subviews> | |||
| <viewLayoutGuide key="safeArea" id="NHq-9v-F7t"/> | |||
| <color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
| <constraints> | |||
| <constraint firstItem="d8W-4g-bv9" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" constant="16" id="0mG-RC-xTr"/> | |||
| <constraint firstItem="RUX-kg-vMY" firstAttribute="leading" secondItem="d8W-4g-bv9" secondAttribute="trailing" constant="32" id="4o1-mG-nyl"/> | |||
| <constraint firstItem="avL-mp-uk3" firstAttribute="top" secondItem="NHq-9v-F7t" secondAttribute="top" id="65b-CM-qWc"/> | |||
| <constraint firstItem="dXn-E2-KcZ" firstAttribute="leading" secondItem="d8W-4g-bv9" secondAttribute="leading" id="MnL-Ml-ELh"/> | |||
| <constraint firstItem="NHq-9v-F7t" firstAttribute="bottom" secondItem="vOk-FU-BQ9" secondAttribute="bottom" id="P3H-ha-my3"/> | |||
| <constraint firstItem="vOk-FU-BQ9" firstAttribute="top" secondItem="avL-mp-uk3" secondAttribute="bottom" constant="300" id="XgC-e8-meq"/> | |||
| <constraint firstItem="dXn-E2-KcZ" firstAttribute="top" secondItem="d8W-4g-bv9" secondAttribute="bottom" constant="16" id="Y47-BO-ap7"/> | |||
| <constraint firstItem="vOk-FU-BQ9" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" constant="8" id="bI5-zC-F3I"/> | |||
| <constraint firstItem="RUX-kg-vMY" firstAttribute="top" secondItem="d8W-4g-bv9" secondAttribute="top" id="hGc-8d-bah"/> | |||
| <constraint firstItem="avL-mp-uk3" firstAttribute="trailing" secondItem="NHq-9v-F7t" secondAttribute="trailing" id="jgI-X6-rF6"/> | |||
| <constraint firstItem="FoH-SE-VcD" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" constant="8" id="ldB-hE-eps"/> | |||
| <constraint firstItem="d8W-4g-bv9" firstAttribute="top" secondItem="avL-mp-uk3" secondAttribute="bottom" constant="16" id="mb7-bS-812"/> | |||
| <constraint firstItem="vOk-FU-BQ9" firstAttribute="top" secondItem="e5p-j3-6VG" secondAttribute="bottom" constant="8" symbolic="YES" id="mlJ-Mg-6V6"/> | |||
| <constraint firstItem="vOk-FU-BQ9" firstAttribute="top" secondItem="FoH-SE-VcD" secondAttribute="bottom" constant="8" symbolic="YES" id="szh-L5-bcY"/> | |||
| <constraint firstItem="avL-mp-uk3" firstAttribute="leading" secondItem="NHq-9v-F7t" secondAttribute="leading" id="uaL-lJ-HX1"/> | |||
| <constraint firstItem="NHq-9v-F7t" firstAttribute="trailing" secondItem="vOk-FU-BQ9" secondAttribute="trailing" constant="8" id="wtV-so-dPb"/> | |||
| <constraint firstItem="NHq-9v-F7t" firstAttribute="trailing" secondItem="e5p-j3-6VG" secondAttribute="trailing" constant="8" id="z2L-Bc-1Rc"/> | |||
| </constraints> | |||
| <point key="canvasLocation" x="139" y="21"/> | |||
| </view> | |||
| </objects> | |||
| <resources> | |||
| <systemColor name="labelColor"> | |||
| <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
| </systemColor> | |||
| <systemColor name="opaqueSeparatorColor"> | |||
| <color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
| </systemColor> | |||
| <systemColor name="systemBackgroundColor"> | |||
| <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
| </systemColor> | |||
| </resources> | |||
| </document> | |||
| @@ -0,0 +1,71 @@ | |||
| // | |||
| // ELMeatProbeBoxCustomDataModel.h | |||
| // AILinkBleSDK | |||
| // | |||
| #import <Foundation/Foundation.h> | |||
| #import <AILinkBleSDK/ELMeatProbeBleTool.h> | |||
| NS_ASSUME_NONNULL_BEGIN | |||
| #pragma pack(1) | |||
| typedef struct ELMeatProbeBoxCustomDataStruct { | |||
| UInt8 ver; //数据版本 (当前版本 0x02) //数据总长度: modelSize() | |||
| UInt32 cookingId; //烧烤id (选择食物的时间戳) | |||
| UInt8 foodType; //食物类型 | |||
| UInt8 foodRawness; //食物熟度 | |||
| SInt16 targetTemperature_C; //食物目标温度:℃ | |||
| SInt16 targetTemperature_F; //食物目标温度:℉ | |||
| SInt16 ambientMinTemperature_C; //炉温目标下限:℃ | |||
| SInt16 ambientMinTemperature_F; //炉温目标下限:℉ | |||
| SInt16 ambientMaxTemperature_C; //炉温目标上限:℃ | |||
| SInt16 ambientMaxTemperature_F; //炉温目标上限:℉ | |||
| double alarmTemperaturePercent; //提醒温度对目标温度百分比 0~1.0 | |||
| UInt32 timerStart;//计时开始时间戳 | |||
| UInt32 timerEnd;//计时结束时间戳 | |||
| UInt8 currentUnit; //当前温度单位 | |||
| SInt16 alarmTemperature_C; //食物提醒温度:℃ | |||
| SInt16 alarmTemperature_F; //食物提醒温度:℉ | |||
| UInt8 remarkBytes[32]; //UTF-8 编码 | |||
| } ELMeatProbeBoxCustomDataStruct; | |||
| #pragma pack() | |||
| /// | |||
| @interface ELMeatProbeBoxCustomDataModel : NSObject | |||
| @property (nonatomic, assign) ELMeatProbeBoxCustomDataStruct customStruct; | |||
| @property (nonatomic, assign, readonly) NSInteger ver; | |||
| @property (nonatomic, assign, readonly) NSInteger cookingId; | |||
| @property (nonatomic, assign, readonly) NSInteger foodType; | |||
| @property (nonatomic, assign, readonly) NSInteger foodRawness; | |||
| @property (nonatomic, assign) double alarmTemperaturePercent; | |||
| @property (nonatomic, assign, readonly) ELDeviceTemperatureUnit currentUnit; | |||
| + (instancetype _Nullable)modelWithData:(NSData *)data; | |||
| - (NSData *)dataValue; | |||
| - (NSString *)remark; | |||
| - (NSString *)setRemark:(NSString *)remark; | |||
| + (Byte)version; | |||
| + (Byte)modelSize; | |||
| @end | |||
| NS_ASSUME_NONNULL_END | |||
| @@ -0,0 +1,154 @@ | |||
| // | |||
| // ELMeatProbeCustomDataModel.m | |||
| // AILinkBleSDK | |||
| // | |||
| #import "ELMeatProbeBoxCustomDataModel.h" | |||
| #import <AILinkBleSDK/NSData+AILinkBle.h> | |||
| #import <objc/runtime.h> | |||
| @implementation ELMeatProbeBoxCustomDataModel | |||
| + (Byte)version { | |||
| return 0x02; | |||
| } | |||
| + (Byte)modelSize { | |||
| return 128;//sizeof(ELMeatProbeCustomDataStruct); | |||
| } | |||
| + (instancetype)modelWithData:(NSData *)data { | |||
| if (data.length != self.modelSize) { | |||
| NSLog(@"数据长度错误 data:%@", data); | |||
| return nil; | |||
| } | |||
| ELMeatProbeBoxCustomDataModel *model = [[ELMeatProbeBoxCustomDataModel alloc] initWithData:data]; | |||
| if (model.customStruct.ver != self.version) { | |||
| NSLog(@"数据版本错误 data:%@", data); | |||
| return nil; | |||
| } | |||
| return model; | |||
| } | |||
| - (instancetype)init { | |||
| if (self = [super init]) { | |||
| memset(&_customStruct, 0, sizeof(_customStruct)); | |||
| _customStruct.ver = self.class.version; | |||
| } | |||
| return self; | |||
| } | |||
| - (instancetype)initWithData:(NSData *)data { | |||
| if (self = [self init]) { | |||
| memcpy(&_customStruct, data.bytes, sizeof(_customStruct)); | |||
| } | |||
| return self; | |||
| } | |||
| - (NSData *)dataValue { | |||
| Byte bytes[128] = {0x00}; | |||
| memcpy(bytes, &_customStruct, sizeof(_customStruct)); | |||
| return [NSData dataWithBytes:bytes length:sizeof(bytes)]; | |||
| } | |||
| - (NSString *)remark { | |||
| char *remarkBytes[64] = {0}; | |||
| memcpy(remarkBytes, _customStruct.remarkBytes, sizeof(_customStruct.remarkBytes)); | |||
| NSString *str = [NSString stringWithUTF8String:(const char *)remarkBytes]; | |||
| return str; | |||
| } | |||
| - (NSString *)setRemark:(NSString *)remark { | |||
| NSData *data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| if (data.length > 32) { | |||
| while (data.length > 29) { | |||
| remark = [remark substringToIndex:(remark.length - 1)]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| remark = [remark stringByAppendingString:@"..."]; | |||
| data = [remark dataUsingEncoding:NSUTF8StringEncoding]; | |||
| } | |||
| Byte *bytes = (Byte *)data.bytes; | |||
| NSUInteger total = data.length > 32 ? 32 : data.length; | |||
| memset(_customStruct.remarkBytes, 0, 32); | |||
| memcpy(_customStruct.remarkBytes, bytes, total); | |||
| return [self remark]; | |||
| } | |||
| #pragma mark setter getter | |||
| - (NSInteger)ver { | |||
| return self.customStruct.ver; | |||
| } | |||
| - (NSInteger)cookingId { | |||
| return self.customStruct.cookingId; | |||
| } | |||
| - (NSInteger)foodType { | |||
| return self.customStruct.foodType; | |||
| } | |||
| - (NSInteger)foodRawness { | |||
| return self.customStruct.foodRawness; | |||
| } | |||
| - (double)alarmTemperaturePercent { | |||
| return self.customStruct.alarmTemperaturePercent; | |||
| } | |||
| - (NSInteger)alarmTemperature_C { | |||
| return self.customStruct.alarmTemperature_C; | |||
| } | |||
| - (NSInteger)alarmTemperature_F { | |||
| return self.customStruct.alarmTemperature_F; | |||
| } | |||
| - (void)setAlarmTemperaturePercent:(double)alarmTemperaturePercent { | |||
| _customStruct.alarmTemperaturePercent = alarmTemperaturePercent; | |||
| _customStruct.alarmTemperature_C = _customStruct.targetTemperature_C * alarmTemperaturePercent; | |||
| _customStruct.alarmTemperature_F = _customStruct.targetTemperature_F * alarmTemperaturePercent; | |||
| } | |||
| - (ELDeviceTemperatureUnit)currentUnit { | |||
| return self.customStruct.currentUnit; | |||
| } | |||
| #pragma mark ---- description | |||
| - (NSString *)description { | |||
| NSMutableString *desc = [NSStringFromClass([self class]) stringByAppendingString:@": {\n"].mutableCopy; | |||
| [desc appendString:[NSString stringWithFormat:@"ver: %@,\n", @(self.customStruct.ver)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"cookingId: %@,\n", @(self.customStruct.cookingId)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"foodType: %@,\n", @(self.customStruct.foodType)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"foodRawness: %@,\n", @(self.customStruct.foodRawness)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"targetTemperature_C: %@,\n", @(self.customStruct.targetTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"targetTemperature_F: %@,\n", @(self.customStruct.targetTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_C: %@,\n", @(self.customStruct.ambientMinTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMinTemperature_F: %@,\n", @(self.customStruct.ambientMinTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_C: %@,\n", @(self.customStruct.ambientMaxTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"ambientMaxTemperature_F: %@,\n", @(self.customStruct.ambientMaxTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"alarmTemperaturePercent: %@,\n", @(self.alarmTemperaturePercent)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"timerStart: %@,\n", @(self.customStruct.timerStart)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"timerEnd: %@,\n", @(self.customStruct.timerEnd)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"alarmTemperature_C: %@,\n", @(self.customStruct.alarmTemperature_C)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"alarmTemperature_F: %@,\n", @(self.customStruct.alarmTemperature_F)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"currentUnit: %@,\n", @(self.customStruct.currentUnit)]]; | |||
| [desc appendString:[NSString stringWithFormat:@"remark: %@,\n", self.remark]]; | |||
| [desc appendString:@"}"]; | |||
| return desc; | |||
| } | |||
| - (NSString *)debugDescription { | |||
| return [self description]; | |||
| } | |||
| @end | |||
| @@ -111,8 +111,9 @@ | |||
| - (void)initBle { | |||
| self.bleManager = [[ELAILinkBleManager alloc] init]; | |||
| self.bleManager.ailinkDelegate = self; | |||
| self.bleParser = [ELMeatProbeBoxBleParser new]; | |||
| self.bleParser = [[ELMeatProbeBoxBleParser alloc] init]; | |||
| self.bleParser.delegate = self; | |||
| self.bleParser.mac = self.per.macAddressString; | |||
| } | |||
| - (void)scanBle { | |||
| @@ -169,10 +170,6 @@ | |||
| - (void)requestDeviceData { | |||
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{ | |||
| // //APP获取设备功能列表 | |||
| // [self.bleManager airDetectorRequestDeviceFunctions]; | |||
| // //APP获取设备状态 //0x03 CMD:获取设备状态 | |||
| // [self.bleManager airDetectorRequestDeviceStatus]; | |||
| }); | |||
| } | |||
| @@ -224,11 +221,6 @@ | |||
| Byte *bytes = (Byte *)packet.bytes; | |||
| Byte cmd = bytes[2]; | |||
| if (cmd == ELInetGetCmdTypeGetBatteryState) { | |||
| int power = self.bleManager.battery.power; | |||
| ELBatteryChargingState state = self.bleManager.battery.state; | |||
| NSLog(@"##### state: %lu power: %d", (unsigned long)state, power); | |||
| } | |||
| if (cmd == ELInetGetCmdTypeGetBMVersion) { | |||
| NSString *bmVersion = self.bleManager.bmVersion; | |||
| NSLog(@"##### bmVersion: %@", bmVersion); | |||
| @@ -1,9 +1,9 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | |||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | |||
| <device id="retina6_12" orientation="portrait" appearance="light"/> | |||
| <dependencies> | |||
| <deployment identifier="iOS"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/> | |||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/> | |||
| <capability name="Safe area layout guides" minToolsVersion="9.0"/> | |||
| <capability name="System colors in document resources" minToolsVersion="11.0"/> | |||
| <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | |||
| @@ -21,6 +21,12 @@ | |||
| <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | |||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
| <subviews> | |||
| <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rgz-Ge-utp"> | |||
| <rect key="frame" x="0.0" y="63" width="393" height="21"/> | |||
| <fontDescription key="fontDescription" type="system" pointSize="17"/> | |||
| <nil key="textColor"/> | |||
| <nil key="highlightedColor"/> | |||
| </label> | |||
| <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K5I-eH-HGA"> | |||
| <rect key="frame" x="16" y="346" width="31" height="30"/> | |||
| <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
| @@ -37,12 +43,6 @@ | |||
| <action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="aLe-00-nz6"/> | |||
| </connections> | |||
| </button> | |||
| <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rgz-Ge-utp"> | |||
| <rect key="frame" x="0.0" y="63" width="393" height="21"/> | |||
| <fontDescription key="fontDescription" type="system" pointSize="17"/> | |||
| <nil key="textColor"/> | |||
| <nil key="highlightedColor"/> | |||
| </label> | |||
| <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="I1h-EM-UuI"> | |||
| <rect key="frame" x="8" y="384" width="377" height="434"/> | |||
| <subviews> | |||
| @@ -133,10 +133,10 @@ | |||
| </objects> | |||
| <resources> | |||
| <systemColor name="labelColor"> | |||
| <color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
| <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
| </systemColor> | |||
| <systemColor name="opaqueSeparatorColor"> | |||
| <color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
| <color red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
| </systemColor> | |||
| <systemColor name="systemBackgroundColor"> | |||
| <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
| @@ -47,6 +47,8 @@ | |||
| #import "MeatProbeVC.h" | |||
| #import "MeatProbeBoxVC.h" | |||
| #import "HygrothermographVC.h" | |||
| #import "MeatProbeScreenBoxVC.h" | |||
| #import "MultipleMeatProbeBoxVC.h" | |||
| @interface ViewController ()<UITableViewDelegate,UITableViewDataSource> | |||
| @@ -106,6 +108,9 @@ | |||
| [ELDemoDeviceModel modelWithIndex:@"26" imageName:@"meat_probe_type_ic" title:@"MeatProbe" subtitle:@"食物探针" entryVCName:NSStringFromClass(MeatProbeVC.class) cids:@[@(ELSupportDeviceTypeMeatProbe)]], | |||
| [ELDemoDeviceModel modelWithIndex:@"27" imageName:@"ailink_cid85_type_ic" title:@"MeatProbeBox" subtitle:@"食物探针盒子" entryVCName:NSStringFromClass(MeatProbeBoxVC.class) cids:@[@(ELSupportDeviceTypeMeatProbeBox)]], | |||
| [ELDemoDeviceModel modelWithIndex:@"27B" imageName:@"ailink_cid85_banding_cid_0062_100_ic" title:@"MeatProbeScreenBox" subtitle:@"食物探针带屏盒子" entryVCName:NSStringFromClass(MeatProbeScreenBoxVC.class) cids:@[@(0x0062)]], | |||
| [ELDemoDeviceModel modelWithIndex:@"27C" imageName:@"aibbq_add_equipment_probe_100_ic" title:@"MultipleMeatProbeBox" subtitle:@"多食物探针盒子" entryVCName:NSStringFromClass(MultipleMeatProbeBoxVC.class) cids:@[@(0x006B)]], | |||
| [ELDemoDeviceModel modelWithIndex:@"28" imageName:@"Hygrothermograph_add_icon2_100" title:@"Hygrothermograph" subtitle:@"温湿度计" entryVCName:NSStringFromClass(HygrothermographVC.class) cids:@[@(ELSupportDeviceTypeHygrothermograph)]], | |||
| ]; | |||
| } | |||
| @@ -57,7 +57,7 @@ static NSString * ELSelectViewCellid = @"ELSelectViewCellid"; | |||
| _tableView = [[UITableView alloc]init]; | |||
| _tableView.delegate = self; | |||
| _tableView.dataSource = self; | |||
| _tableView.tableFooterView = [UIView new]; | |||
| _tableView.tableFooterView = [[UIView alloc] init]; | |||
| _tableView.separatorColor = [UIColor clearColor]; | |||
| } | |||
| return _tableView; | |||
| @@ -15,4 +15,5 @@ | |||
| | 1.8.0 | 20240313 | larry | 修改 增加 readRSSI 接口 | | |||
| | 1.9.0 | 20240603 | larry | 修改 增加 Privacy Manifest | | |||
| | 1.9.0(Demo v1.9.2) | 20240628 | larry | 修改 增加 温湿度计 Demo | | |||
| | 1.10.0 | 20240910 | larry | 修改 增加 带屏食物探针盒 多探针盒子 | | |||