| objects = { | objects = { | ||||
| /* Begin PBXBuildFile section */ | /* 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 */; }; | 78350BB329D2826900456695 /* libHTBodyfat_SDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB129D2826900456695 /* libHTBodyfat_SDK.a */; }; | ||||
| 78350BB729D282A300456695 /* libBhBodyComposition120.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB429D282A300456695 /* libBhBodyComposition120.a */; }; | 78350BB729D282A300456695 /* libBhBodyComposition120.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78350BB429D282A300456695 /* libBhBodyComposition120.a */; }; | ||||
| 78350BBB29D2869000456695 /* BodyScaleVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78350BB929D2869000456695 /* BodyScaleVC.m */; }; | 78350BBB29D2869000456695 /* BodyScaleVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78350BB929D2869000456695 /* BodyScaleVC.m */; }; | ||||
| /* End PBXContainerItemProxy section */ | /* End PBXContainerItemProxy section */ | ||||
| /* Begin PBXFileReference 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>"; }; | 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>"; }; | 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>"; }; | 78350BB429D282A300456695 /* libBhBodyComposition120.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libBhBodyComposition120.a; sourceTree = "<group>"; }; | ||||
| /* End PBXFrameworksBuildPhase section */ | /* End PBXFrameworksBuildPhase section */ | ||||
| /* Begin PBXGroup 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 */ = { | 78350BB029D280D300456695 /* DeviceDemo */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 78DC4DF72990FC10006C6911 /* 25_HeightBodyFatScale */, | 78DC4DF72990FC10006C6911 /* 25_HeightBodyFatScale */, | ||||
| 78E07B6C2A49664D00E68200 /* 26_MeatProbe */, | 78E07B6C2A49664D00E68200 /* 26_MeatProbe */, | ||||
| 78E07B702A49664E00E68200 /* 27_MeatProbeBox */, | 78E07B702A49664E00E68200 /* 27_MeatProbeBox */, | ||||
| 7802C5C82C903A7F008D7616 /* 27B_MeatProbeScreenBox */, | |||||
| 7802C5BC2C903A7F008D7616 /* 27C_MultipleMeatProbeBox */, | |||||
| 78CF43522C2EC21A00D0A165 /* 28_Hygrothermograph */, | 78CF43522C2EC21A00D0A165 /* 28_Hygrothermograph */, | ||||
| ); | ); | ||||
| path = DeviceDemo; | path = DeviceDemo; | ||||
| 78E07B6F2A49664D00E68200 /* MeatProbeVC.h */, | 78E07B6F2A49664D00E68200 /* MeatProbeVC.h */, | ||||
| 78E07B6D2A49664D00E68200 /* MeatProbeVC.m */, | 78E07B6D2A49664D00E68200 /* MeatProbeVC.m */, | ||||
| 78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */, | 78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */, | ||||
| 7802C5AB2C903A31008D7616 /* ELMeatProbeCustomDataModel.h */, | |||||
| 7802C5AC2C903A31008D7616 /* ELMeatProbeCustomDataModel.m */, | |||||
| ); | ); | ||||
| path = 26_MeatProbe; | path = 26_MeatProbe; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| 78E07B732A49664E00E68200 /* ELMeatProbeBoxBleHeader.h */, | 78E07B732A49664E00E68200 /* ELMeatProbeBoxBleHeader.h */, | ||||
| 78E07B722A49664E00E68200 /* ELMeatProbeBoxBleParser.h */, | 78E07B722A49664E00E68200 /* ELMeatProbeBoxBleParser.h */, | ||||
| 78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */, | 78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */, | ||||
| 7802C5AE2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.h */, | |||||
| 7802C5AF2C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m */, | |||||
| ); | ); | ||||
| path = Ble; | path = Ble; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| 78DC4DE32990FB2E006C6911 /* ELDemoScanVC.xib in Resources */, | 78DC4DE32990FB2E006C6911 /* ELDemoScanVC.xib in Resources */, | ||||
| E622E26E243F1CA6003C319D /* LaunchScreen.storyboard in Resources */, | E622E26E243F1CA6003C319D /* LaunchScreen.storyboard in Resources */, | ||||
| 78CF43542C2EC21B00D0A165 /* HygrothermographVC.xib in Resources */, | 78CF43542C2EC21B00D0A165 /* HygrothermographVC.xib in Resources */, | ||||
| 7802C5CD2C903A7F008D7616 /* MultipleMeatProbeBoxVC.xib in Resources */, | |||||
| E622E26B243F1CA6003C319D /* Assets.xcassets in Resources */, | E622E26B243F1CA6003C319D /* Assets.xcassets in Resources */, | ||||
| 78AC3B2B2C0D994900A62C61 /* PrivacyInfo.bundle in Resources */, | 78AC3B2B2C0D994900A62C61 /* PrivacyInfo.bundle in Resources */, | ||||
| 7802C5D22C903A7F008D7616 /* MeatProbeScreenBoxVC.xib in Resources */, | |||||
| 78DC4DF62990FB40006C6911 /* ELDeviceScanCell.xib in Resources */, | 78DC4DF62990FB40006C6911 /* ELDeviceScanCell.xib in Resources */, | ||||
| 78E07B7B2A49664E00E68200 /* MeatProbeVC.xib in Resources */, | 78E07B7B2A49664E00E68200 /* MeatProbeVC.xib in Resources */, | ||||
| 78350BBD29D28C1200456695 /* README.md in Resources */, | 78350BBD29D28C1200456695 /* README.md in Resources */, | ||||
| 95565B7A2786EA3B000265EA /* SkipScanViewController.m in Sources */, | 95565B7A2786EA3B000265EA /* SkipScanViewController.m in Sources */, | ||||
| 78DC4DFB2990FC10006C6911 /* HeightBodyFatScaleVC.m in Sources */, | 78DC4DFB2990FC10006C6911 /* HeightBodyFatScaleVC.m in Sources */, | ||||
| 9557263B276C38CD003BFBAC /* FaceMaskScanViewController.m in Sources */, | 9557263B276C38CD003BFBAC /* FaceMaskScanViewController.m in Sources */, | ||||
| 7802C5D02C903A7F008D7616 /* ELMeatProbeScreenBoxBleParser.m in Sources */, | |||||
| 9557263F276C38CD003BFBAC /* EightScaleConnectionViewController.m in Sources */, | 9557263F276C38CD003BFBAC /* EightScaleConnectionViewController.m in Sources */, | ||||
| 9557262B276C38CD003BFBAC /* WheelMonitorScanViewController.m in Sources */, | 9557262B276C38CD003BFBAC /* WheelMonitorScanViewController.m in Sources */, | ||||
| 7802C5AD2C903A31008D7616 /* ELMeatProbeCustomDataModel.m in Sources */, | |||||
| 95572629276C38CD003BFBAC /* BloodSugarConnectionViewController.m in Sources */, | 95572629276C38CD003BFBAC /* BloodSugarConnectionViewController.m in Sources */, | ||||
| 78DC4DE52990FB2E006C6911 /* ELDemoScanVC.m in Sources */, | 78DC4DE52990FB2E006C6911 /* ELDemoScanVC.m in Sources */, | ||||
| 9557262A276C38CD003BFBAC /* WheelMonitorConnectViewController.m in Sources */, | 9557262A276C38CD003BFBAC /* WheelMonitorConnectViewController.m in Sources */, | ||||
| 78DC4DF22990FB40006C6911 /* ELDeviceTypeCell.m in Sources */, | 78DC4DF22990FB40006C6911 /* ELDeviceTypeCell.m in Sources */, | ||||
| 78DC4DF52990FB40006C6911 /* ELDeviceScanCell.m in Sources */, | 78DC4DF52990FB40006C6911 /* ELDeviceScanCell.m in Sources */, | ||||
| 78DC4DF12990FB40006C6911 /* ELDemoView.m in Sources */, | 78DC4DF12990FB40006C6911 /* ELDemoView.m in Sources */, | ||||
| 7802C5B02C903A4B008D7616 /* ELMeatProbeBoxCustomDataModel.m in Sources */, | |||||
| 95572626276C38CD003BFBAC /* AiFreshNutritionScaleConnectionViewController.m in Sources */, | 95572626276C38CD003BFBAC /* AiFreshNutritionScaleConnectionViewController.m in Sources */, | ||||
| 9557262F276C38CD003BFBAC /* ELBfsWifiConnectTableViewCell.m in Sources */, | 9557262F276C38CD003BFBAC /* ELBfsWifiConnectTableViewCell.m in Sources */, | ||||
| 95572623276C38CD003BFBAC /* BloodConnectViewController.m in Sources */, | 95572623276C38CD003BFBAC /* BloodConnectViewController.m in Sources */, | ||||
| 958B4EB5263C08E800476BAE /* MASViewConstraint.m in Sources */, | 958B4EB5263C08E800476BAE /* MASViewConstraint.m in Sources */, | ||||
| 95572628276C38CD003BFBAC /* BloodSugarScanViewController.m in Sources */, | 95572628276C38CD003BFBAC /* BloodSugarScanViewController.m in Sources */, | ||||
| 9557264C276C38CD003BFBAC /* RemoteControlScanViewController.m in Sources */, | 9557264C276C38CD003BFBAC /* RemoteControlScanViewController.m in Sources */, | ||||
| 7802C5D12C903A7F008D7616 /* MeatProbeScreenBoxVC.m in Sources */, | |||||
| 9557262C276C38CD003BFBAC /* BroadcastNutritionScaleViewController.m in Sources */, | 9557262C276C38CD003BFBAC /* BroadcastNutritionScaleViewController.m in Sources */, | ||||
| 95572643276C38CD003BFBAC /* BodyFatScaleScanViewController.m in Sources */, | 95572643276C38CD003BFBAC /* BodyFatScaleScanViewController.m in Sources */, | ||||
| 95572627276C38CD003BFBAC /* BroadcastScaleViewController.m in Sources */, | 95572627276C38CD003BFBAC /* BroadcastScaleViewController.m in Sources */, | ||||
| E622E271243F1CA6003C319D /* main.m in Sources */, | E622E271243F1CA6003C319D /* main.m in Sources */, | ||||
| 78E07B7F2A49664E00E68200 /* MeatProbeBoxVC.m in Sources */, | 78E07B7F2A49664E00E68200 /* MeatProbeBoxVC.m in Sources */, | ||||
| 95572646276C38CD003BFBAC /* FoodThermometerConnectionViewController.m in Sources */, | 95572646276C38CD003BFBAC /* FoodThermometerConnectionViewController.m in Sources */, | ||||
| 7802C5CE2C903A7F008D7616 /* ELMeatProbeScreenBoxBleBBQParamModel.m in Sources */, | |||||
| 958B4EB3263C08E800476BAE /* View+MASAdditions.m in Sources */, | 958B4EB3263C08E800476BAE /* View+MASAdditions.m in Sources */, | ||||
| 958B4EB1263C08E800476BAE /* MASLayoutConstraint.m in Sources */, | 958B4EB1263C08E800476BAE /* MASLayoutConstraint.m in Sources */, | ||||
| 9557263A276C38CD003BFBAC /* FaceMaskConnectionViewController.m in Sources */, | 9557263A276C38CD003BFBAC /* FaceMaskConnectionViewController.m in Sources */, | ||||
| 95572636276C38CD003BFBAC /* OximeterScanViewController.m in Sources */, | 95572636276C38CD003BFBAC /* OximeterScanViewController.m in Sources */, | ||||
| 78E07B7D2A49664E00E68200 /* ELMeatProbeBoxBleParser.m in Sources */, | 78E07B7D2A49664E00E68200 /* ELMeatProbeBoxBleParser.m in Sources */, | ||||
| 958B4EA4263C08E800476BAE /* ELInputAlertView.m in Sources */, | 958B4EA4263C08E800476BAE /* ELInputAlertView.m in Sources */, | ||||
| 7802C5CC2C903A7F008D7616 /* MultipleMeatProbeBoxVC.m in Sources */, | |||||
| 958B4EB6263C08E800476BAE /* MASViewAttribute.m in Sources */, | 958B4EB6263C08E800476BAE /* MASViewAttribute.m in Sources */, | ||||
| 958B4EBB263C08E800476BAE /* InheritConnectViewController.m in Sources */, | 958B4EBB263C08E800476BAE /* InheritConnectViewController.m in Sources */, | ||||
| 958B4EB4263C08E800476BAE /* MASConstraint.m in Sources */, | 958B4EB4263C08E800476BAE /* MASConstraint.m in Sources */, | ||||
| 7802C5CA2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleCmdTool.m in Sources */, | |||||
| 958B4EAA263C08E800476BAE /* AilinkSuperConnectViewController.m in Sources */, | 958B4EAA263C08E800476BAE /* AilinkSuperConnectViewController.m in Sources */, | ||||
| 958B4EB0263C08E800476BAE /* MASConstraintMaker.m in Sources */, | 958B4EB0263C08E800476BAE /* MASConstraintMaker.m in Sources */, | ||||
| 7802C5C92C903A7F008D7616 /* ELMeatProbeBoxMultipleBleBBQParamModel.m in Sources */, | |||||
| 78DC4DF32990FB40006C6911 /* ELDemoButton.m in Sources */, | 78DC4DF32990FB40006C6911 /* ELDemoButton.m in Sources */, | ||||
| 7802C5CF2C903A7F008D7616 /* ELMeatProbeScreenBoxBleCmdTool.m in Sources */, | |||||
| 958B4EA5263C08E800476BAE /* UILabel+WZAdd.m in Sources */, | 958B4EA5263C08E800476BAE /* UILabel+WZAdd.m in Sources */, | ||||
| 95572633276C38CD003BFBAC /* BabyScaleConnectViewController.m in Sources */, | 95572633276C38CD003BFBAC /* BabyScaleConnectViewController.m in Sources */, | ||||
| 78E07B7A2A49664E00E68200 /* MeatProbeVC.m in Sources */, | 78E07B7A2A49664E00E68200 /* MeatProbeVC.m in Sources */, | ||||
| 95572644276C38CD003BFBAC /* BodyFatScaleConnectViewController.m in Sources */, | 95572644276C38CD003BFBAC /* BodyFatScaleConnectViewController.m in Sources */, | ||||
| 958B4EBD263C08E800476BAE /* AppDelegate.m in Sources */, | 958B4EBD263C08E800476BAE /* AppDelegate.m in Sources */, | ||||
| 7802C5CB2C903A7F008D7616 /* ELMeatProbeBoxMultipleBleParser.m in Sources */, | |||||
| 95565B792786EA3B000265EA /* SkipConnectionViewController.m in Sources */, | 95565B792786EA3B000265EA /* SkipConnectionViewController.m in Sources */, | ||||
| 958B4EAE263C08E800476BAE /* ViewController+MASAdditions.m in Sources */, | 958B4EAE263C08E800476BAE /* ViewController+MASAdditions.m in Sources */, | ||||
| 958B4EA8263C08E800476BAE /* CABasicAnimation+ELAnimation.m in Sources */, | 958B4EA8263C08E800476BAE /* CABasicAnimation+ELAnimation.m in Sources */, | ||||
| buildSettings = { | buildSettings = { | ||||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = 20240628; | |||||
| CURRENT_PROJECT_VERSION = 20240910; | |||||
| DEVELOPMENT_TEAM = YEU77XR292; | DEVELOPMENT_TEAM = YEU77XR292; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | ||||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | ||||
| ); | ); | ||||
| MARKETING_VERSION = 1.9.2; | |||||
| MARKETING_VERSION = 1.10.0; | |||||
| OTHER_LDFLAGS = "-ObjC"; | OTHER_LDFLAGS = "-ObjC"; | ||||
| PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app; | PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app; | ||||
| PRODUCT_NAME = "$(TARGET_NAME)"; | PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| buildSettings = { | buildSettings = { | ||||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = 20240628; | |||||
| CURRENT_PROJECT_VERSION = 20240910; | |||||
| DEVELOPMENT_TEAM = YEU77XR292; | DEVELOPMENT_TEAM = YEU77XR292; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | ||||
| "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | "$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | ||||
| ); | ); | ||||
| MARKETING_VERSION = 1.9.2; | |||||
| MARKETING_VERSION = 1.10.0; | |||||
| OTHER_LDFLAGS = "-ObjC"; | OTHER_LDFLAGS = "-ObjC"; | ||||
| PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app; | PRODUCT_BUNDLE_IDENTIFIER = com.ellinkthings.ailinksdk.app; | ||||
| PRODUCT_NAME = "$(TARGET_NAME)"; | PRODUCT_NAME = "$(TARGET_NAME)"; |
| #define AILinkBleSDK_Version_h | #define AILinkBleSDK_Version_h | ||||
| //版本号 SDK Version | //版本号 SDK Version | ||||
| #define AiLinkBleSDKVersion @"1.9.0" // 20240603 | |||||
| #define AiLinkBleSDKVersion @"1.10.0" // 20240910 | |||||
| #endif /* AILinkBleSDK_Version_h */ | #endif /* AILinkBleSDK_Version_h */ |
| ELBodyFatScaleBleDataType_MCUBackHeartRateMeasureFailed = 0x0D , | ELBodyFatScaleBleDataType_MCUBackHeartRateMeasureFailed = 0x0D , | ||||
| /** MCU->APP:MCU请求补全体脂数据 */ | /** MCU->APP:MCU请求补全体脂数据 */ | ||||
| ELBodyFatScaleBleDataType_MCUBackNeedsFillBodyData = 0x0E , | ELBodyFatScaleBleDataType_MCUBackNeedsFillBodyData = 0x0E , | ||||
| /** MCU->APP:设备上报模式 */ | |||||
| ELBodyFatScaleBleDataType_MCUReportingMode = 0x0F , | |||||
| /** App发送指令切换单位 */ | /** App发送指令切换单位 */ | ||||
| ELBodyFatScaleBleDataType_AppSendUnit = 0x81 , | ELBodyFatScaleBleDataType_AppSendUnit = 0x81 , |
| */ | */ | ||||
| - (void)bodyFatScaleManagerCallBackSetIPAddressResult:(NSInteger)result; | - (void)bodyFatScaleManagerCallBackSetIPAddressResult:(NSInteger)result; | ||||
| /** MCU reporting mode (MCU上报模式 ) | |||||
| * @param mode : 0:体脂模式(默认),1:体重模式(没有阻抗和体脂数据) | |||||
| */ | |||||
| - (void)bodyFatScaleManagerCallBackReportingMode:(NSInteger)mode; | |||||
| @end | @end | ||||
| PHSex_MALE = 1,//男 | PHSex_MALE = 1,//男 | ||||
| }; | }; | ||||
| //矿物质重量 | |||||
| typedef NS_ENUM(NSUInteger, AILinkMineralWeightType) { | |||||
| AILinkMineralWeightTypeUnknown = 0,//未知 | |||||
| AILinkMineralWeightTypeInsufficient = 1,//不足 | |||||
| AILinkMineralWeightTypeStandard = 2,//标准 | |||||
| AILinkMineralWeightTypeExcellent = 3,//偏高 | |||||
| }; | |||||
| //四肢肌肉指数 | //四肢肌肉指数 | ||||
| typedef NS_ENUM(NSUInteger, AILinkLimbMuscleIndexType) { | typedef NS_ENUM(NSUInteger, AILinkLimbMuscleIndexType) { | ||||
| @interface ELBodyIndexAlgorithmModel : NSObject | @interface ELBodyIndexAlgorithmModel : NSObject | ||||
| #pragma mark ============ 标准判断临界值数组 ============== | #pragma mark ============ 标准判断临界值数组 ============== | ||||
| /// 四肢肌肉指数 标准判断 | /// 四肢肌肉指数 标准判断 | ||||
| /// @param sex 性别 | /// @param sex 性别 | ||||
| /// @param cmHeight 身高(cm) | /// @param cmHeight 身高(cm) | ||||
| /// @param age 年龄 | /// @param age 年龄 | ||||
| + (NSArray<NSString *> *)getSkeletalMuscleMassScopeSex:(PHSex)sex age:(float)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 ============ 判断标准数组 ============== | #pragma mark ============ 判断标准数组 ============== | ||||
| //四肢肌肉指数标准词条 | //四肢肌肉指数标准词条 | ||||
| +(NSArray<NSNumber *> *)getLimbMuscleIndexStandardArray; | +(NSArray<NSNumber *> *)getLimbMuscleIndexStandardArray; | ||||
| /// @param weight 体重(KG) | /// @param weight 体重(KG) | ||||
| /// @param standardWeight 标准体重(KG) | /// @param standardWeight 标准体重(KG) | ||||
| + (AILinkFatLevelType)getfatLevelWithweight:(double)weight andStandardWeight:(double)standardWeight; | + (AILinkFatLevelType)getfatLevelWithweight:(double)weight andStandardWeight:(double)standardWeight; | ||||
| #pragma mark - 新增指标 | |||||
| /** | |||||
| * 矿物质重量 标准判断 | |||||
| * | |||||
| * @param mineralWeightValue 体重(KG) | |||||
| * @param weight 体重 | |||||
| * | |||||
| * @return 不足、标准、偏高、Null | |||||
| */ | |||||
| + (AILinkMineralWeightType)getMineralWeightType:(float)mineralWeightValue; | |||||
| @end | @end | ||||
| NS_ASSUME_NONNULL_END | NS_ASSUME_NONNULL_END |
| BroadcastScaleTestStatusADCTestSuccess, | BroadcastScaleTestStatusADCTestSuccess, | ||||
| //Impedance measurement failed (the impedance value is 0xFFFF at this time)(阻抗测量失败(此时阻抗数值为 0xFFFF)) | //Impedance measurement failed (the impedance value is 0xFFFF at this time)(阻抗测量失败(此时阻抗数值为 0xFFFF)) | ||||
| BroadcastScaleTestStatusADCTestFailed, | 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(测试结束) | //End of test(测试结束) | ||||
| BroadcastScaleTestStatusTestEnd = 0xFF, | BroadcastScaleTestStatusTestEnd = 0xFF, | ||||
| }; | }; |
| ///MCU返回设备测量信息(测量完成的时候才会回调该方法) | ///MCU返回设备测量信息(测量完成的时候才会回调该方法) | ||||
| - (void)eightScaleBleReportTestData:(ELEightScaleBleDataModel *)dataModle bfrData:(ELEightScaleBleBfrDataModel *)bfrDataModle; | - (void)eightScaleBleReportTestData:(ELEightScaleBleDataModel *)dataModle bfrData:(ELEightScaleBleBfrDataModel *)bfrDataModle; | ||||
| // 双频八电极mcu测量完成返回数据(测量完成的时候才会回调该方法) | |||||
| - (void)eightScaleBle0x0068ReportTestData:(ELEightScaleBleDataModel *)dataModel bodyFatData:(ELEightScaleBleBfrDataModel *)bfrDataModel; | |||||
| /** MCU返回操作结果 */ | /** MCU返回操作结果 */ | ||||
| - (void)eightScaleBleReportOperationWithType:(enum EightScaleOperationType)type unitResult:(enum EightScaleSwitchUnitResult)result; | - (void)eightScaleBleReportOperationWithType:(enum EightScaleOperationType)type unitResult:(enum EightScaleSwitchUnitResult)result; | ||||
| ///MCU请求补全体脂数据 | ///MCU请求补全体脂数据 | ||||
| - (void)eightScaleBleCompletionBodyFatData; | - (void)eightScaleBleCompletionBodyFatData; | ||||
| - (void)eightScaleBleCompletionBodyFatData:(ELEightScaleBleDataModel *)dataModel; | |||||
| /** | |||||
| Back to impedance measurement(返回阻抗测量) | |||||
| @param status 阻抗测量状态 | |||||
| */ | |||||
| -(void)eightScaleScaleManagerCallBackAdcMeasureStatus:(DualBandEightScaleAdcMeasureStatus)status; | |||||
| @end | @end | ||||
| @interface ELEightScaleBleManager : ELBluetoothManager | @interface ELEightScaleBleManager : ELBluetoothManager |
| EightScaleErrorCodeType_Overweight = 0x01 , | 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 */ | #endif /* ELEightScaleSDKHeader_h */ |
| ELSupportDeviceTypeBroadcastScale_T6 = (0xffff + 0x09) , //65544 T6广播体脂秤 | ELSupportDeviceTypeBroadcastScale_T6 = (0xffff + 0x09) , //65544 T6广播体脂秤 | ||||
| ELSupportDeviceType_dualFrequencyEightelectrodeMCUAlgorithm = 0x0068, //双频八电极MCU算法秤 | |||||
| }; | }; | ||||
| #endif /* ELExSupportDeviceType_h */ | #endif /* ELExSupportDeviceType_h */ |
| TH_Dev_SupportFeatures_GroundLight = 5 , | TH_Dev_SupportFeatures_GroundLight = 5 , | ||||
| /// 寻物功能(APP点击寻物时,设备蜂鸣器会响) | /// 寻物功能(APP点击寻物时,设备蜂鸣器会响) | ||||
| TH_Dev_SupportFeatures_Lost = 6 , | 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 , | |||||
| }; | }; | ||||
| /// @param unit 温度单位 | /// @param unit 温度单位 | ||||
| /// @param hum 湿度,单位% | /// @param hum 湿度,单位% | ||||
| /// @param state 当前设备状态,详细内容见枚举注释 | /// @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; | |||||
| /** 设备返回阀值 | /** 设备返回阀值 | ||||
| // App绑定设备 完成 | // App绑定设备 完成 | ||||
| - (void)bindDeviceDone; | - (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 | @end | ||||
| { | |||||
| "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 | |||||
| } | |||||
| } |
| { | |||||
| "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 | |||||
| } | |||||
| } |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 */ |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| <?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> |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 */ |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| <?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> |
| // | |||||
| // 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 |
| // | |||||
| // 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 |
| - (void)initBle { | - (void)initBle { | ||||
| self.bleManager = [[ELAILinkBleManager alloc] init]; | self.bleManager = [[ELAILinkBleManager alloc] init]; | ||||
| self.bleManager.ailinkDelegate = self; | self.bleManager.ailinkDelegate = self; | ||||
| self.bleParser = [ELMeatProbeBoxBleParser new]; | |||||
| self.bleParser = [[ELMeatProbeBoxBleParser alloc] init]; | |||||
| self.bleParser.delegate = self; | self.bleParser.delegate = self; | ||||
| self.bleParser.mac = self.per.macAddressString; | |||||
| } | } | ||||
| - (void)scanBle { | - (void)scanBle { | ||||
| - (void)requestDeviceData { | - (void)requestDeviceData { | ||||
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{ | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{ | ||||
| // //APP获取设备功能列表 | |||||
| // [self.bleManager airDetectorRequestDeviceFunctions]; | |||||
| // //APP获取设备状态 //0x03 CMD:获取设备状态 | |||||
| // [self.bleManager airDetectorRequestDeviceStatus]; | |||||
| }); | }); | ||||
| } | } | ||||
| Byte *bytes = (Byte *)packet.bytes; | Byte *bytes = (Byte *)packet.bytes; | ||||
| Byte cmd = bytes[2]; | 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) { | if (cmd == ELInetGetCmdTypeGetBMVersion) { | ||||
| NSString *bmVersion = self.bleManager.bmVersion; | NSString *bmVersion = self.bleManager.bmVersion; | ||||
| NSLog(@"##### bmVersion: %@", bmVersion); | NSLog(@"##### bmVersion: %@", bmVersion); |
| <?xml version="1.0" encoding="UTF-8"?> | <?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"/> | <device id="retina6_12" orientation="portrait" appearance="light"/> | ||||
| <dependencies> | <dependencies> | ||||
| <deployment identifier="iOS"/> | <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="Safe area layout guides" minToolsVersion="9.0"/> | ||||
| <capability name="System colors in document resources" minToolsVersion="11.0"/> | <capability name="System colors in document resources" minToolsVersion="11.0"/> | ||||
| <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||||
| <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | ||||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||
| <subviews> | <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"> | <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"/> | <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"/> | <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | ||||
| <action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="aLe-00-nz6"/> | <action selector="shareLogAction:" destination="-1" eventType="touchUpInside" id="aLe-00-nz6"/> | ||||
| </connections> | </connections> | ||||
| </button> | </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"> | <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="I1h-EM-UuI"> | ||||
| <rect key="frame" x="8" y="384" width="377" height="434"/> | <rect key="frame" x="8" y="384" width="377" height="434"/> | ||||
| <subviews> | <subviews> | ||||
| </objects> | </objects> | ||||
| <resources> | <resources> | ||||
| <systemColor name="labelColor"> | <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> | ||||
| <systemColor name="opaqueSeparatorColor"> | <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> | ||||
| <systemColor name="systemBackgroundColor"> | <systemColor name="systemBackgroundColor"> | ||||
| <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
| #import "MeatProbeVC.h" | #import "MeatProbeVC.h" | ||||
| #import "MeatProbeBoxVC.h" | #import "MeatProbeBoxVC.h" | ||||
| #import "HygrothermographVC.h" | #import "HygrothermographVC.h" | ||||
| #import "MeatProbeScreenBoxVC.h" | |||||
| #import "MultipleMeatProbeBoxVC.h" | |||||
| @interface ViewController ()<UITableViewDelegate,UITableViewDataSource> | @interface ViewController ()<UITableViewDelegate,UITableViewDataSource> | ||||
| [ELDemoDeviceModel modelWithIndex:@"26" imageName:@"meat_probe_type_ic" title:@"MeatProbe" subtitle:@"食物探针" entryVCName:NSStringFromClass(MeatProbeVC.class) cids:@[@(ELSupportDeviceTypeMeatProbe)]], | [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:@"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)]], | [ELDemoDeviceModel modelWithIndex:@"28" imageName:@"Hygrothermograph_add_icon2_100" title:@"Hygrothermograph" subtitle:@"温湿度计" entryVCName:NSStringFromClass(HygrothermographVC.class) cids:@[@(ELSupportDeviceTypeHygrothermograph)]], | ||||
| ]; | ]; | ||||
| } | } |
| _tableView = [[UITableView alloc]init]; | _tableView = [[UITableView alloc]init]; | ||||
| _tableView.delegate = self; | _tableView.delegate = self; | ||||
| _tableView.dataSource = self; | _tableView.dataSource = self; | ||||
| _tableView.tableFooterView = [UIView new]; | |||||
| _tableView.tableFooterView = [[UIView alloc] init]; | |||||
| _tableView.separatorColor = [UIColor clearColor]; | _tableView.separatorColor = [UIColor clearColor]; | ||||
| } | } | ||||
| return _tableView; | return _tableView; |
| | 1.8.0 | 20240313 | larry | 修改 增加 readRSSI 接口 | | | 1.8.0 | 20240313 | larry | 修改 增加 readRSSI 接口 | | ||||
| | 1.9.0 | 20240603 | larry | 修改 增加 Privacy Manifest | | | 1.9.0 | 20240603 | larry | 修改 增加 Privacy Manifest | | ||||
| | 1.9.0(Demo v1.9.2) | 20240628 | larry | 修改 增加 温湿度计 Demo | | | 1.9.0(Demo v1.9.2) | 20240628 | larry | 修改 增加 温湿度计 Demo | | ||||
| | 1.10.0 | 20240910 | larry | 修改 增加 带屏食物探针盒 多探针盒子 | | |||||