@@ -22,6 +22,14 @@ | |||
78DC4DF62990FB40006C6911 /* ELDeviceScanCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78DC4DF02990FB40006C6911 /* ELDeviceScanCell.xib */; }; | |||
78DC4DFB2990FC10006C6911 /* HeightBodyFatScaleVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78DC4DF82990FC10006C6911 /* HeightBodyFatScaleVC.m */; }; | |||
78DC4DFC2990FC10006C6911 /* HeightBodyFatScaleVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78DC4DF92990FC10006C6911 /* HeightBodyFatScaleVC.xib */; }; | |||
78E07B6A2A49664000E68200 /* BroadcastHeightGuage.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E07B672A49663F00E68200 /* BroadcastHeightGuage.m */; }; | |||
78E07B6B2A49664000E68200 /* BroadcastHeightGuage.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78E07B682A49663F00E68200 /* BroadcastHeightGuage.xib */; }; | |||
78E07B7A2A49664E00E68200 /* MeatProbeVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E07B6D2A49664D00E68200 /* MeatProbeVC.m */; }; | |||
78E07B7B2A49664E00E68200 /* MeatProbeVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */; }; | |||
78E07B7C2A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E07B742A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.m */; }; | |||
78E07B7D2A49664E00E68200 /* ELMeatProbeBoxBleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */; }; | |||
78E07B7E2A49664E00E68200 /* MeatProbeBoxVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78E07B782A49664E00E68200 /* MeatProbeBoxVC.xib */; }; | |||
78E07B7F2A49664E00E68200 /* MeatProbeBoxVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 78E07B792A49664E00E68200 /* MeatProbeBoxVC.m */; }; | |||
952D19A1276DE80000ED3A0C /* AILinkBleSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 952D19A0276DE80000ED3A0C /* AILinkBleSDK.framework */; }; | |||
95565B792786EA3B000265EA /* SkipConnectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95565B752786EA3B000265EA /* SkipConnectionViewController.m */; }; | |||
95565B7A2786EA3B000265EA /* SkipScanViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95565B782786EA3B000265EA /* SkipScanViewController.m */; }; | |||
@@ -133,6 +141,20 @@ | |||
78DC4DF82990FC10006C6911 /* HeightBodyFatScaleVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HeightBodyFatScaleVC.m; sourceTree = "<group>"; }; | |||
78DC4DF92990FC10006C6911 /* HeightBodyFatScaleVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeightBodyFatScaleVC.xib; sourceTree = "<group>"; }; | |||
78DC4DFA2990FC10006C6911 /* HeightBodyFatScaleVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeightBodyFatScaleVC.h; sourceTree = "<group>"; }; | |||
78E07B672A49663F00E68200 /* BroadcastHeightGuage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BroadcastHeightGuage.m; sourceTree = "<group>"; }; | |||
78E07B682A49663F00E68200 /* BroadcastHeightGuage.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BroadcastHeightGuage.xib; sourceTree = "<group>"; }; | |||
78E07B692A49663F00E68200 /* BroadcastHeightGuage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BroadcastHeightGuage.h; sourceTree = "<group>"; }; | |||
78E07B6D2A49664D00E68200 /* MeatProbeVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeatProbeVC.m; sourceTree = "<group>"; }; | |||
78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MeatProbeVC.xib; sourceTree = "<group>"; }; | |||
78E07B6F2A49664D00E68200 /* MeatProbeVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeatProbeVC.h; sourceTree = "<group>"; }; | |||
78E07B722A49664E00E68200 /* ELMeatProbeBoxBleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxBleParser.h; sourceTree = "<group>"; }; | |||
78E07B732A49664E00E68200 /* ELMeatProbeBoxBleHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELMeatProbeBoxBleHeader.h; sourceTree = "<group>"; }; | |||
78E07B742A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ELAILinkBleManager+MeatProbeBox.m"; sourceTree = "<group>"; }; | |||
78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELMeatProbeBoxBleParser.m; sourceTree = "<group>"; }; | |||
78E07B762A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ELAILinkBleManager+MeatProbeBox.h"; sourceTree = "<group>"; }; | |||
78E07B772A49664E00E68200 /* MeatProbeBoxVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeatProbeBoxVC.h; sourceTree = "<group>"; }; | |||
78E07B782A49664E00E68200 /* MeatProbeBoxVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MeatProbeBoxVC.xib; sourceTree = "<group>"; }; | |||
78E07B792A49664E00E68200 /* MeatProbeBoxVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeatProbeBoxVC.m; sourceTree = "<group>"; }; | |||
952D19A0276DE80000ED3A0C /* AILinkBleSDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AILinkBleSDK.framework; sourceTree = "<group>"; }; | |||
95565B752786EA3B000265EA /* SkipConnectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SkipConnectionViewController.m; sourceTree = "<group>"; }; | |||
95565B762786EA3B000265EA /* SkipScanViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkipScanViewController.h; sourceTree = "<group>"; }; | |||
@@ -309,6 +331,7 @@ | |||
955725E0276C38CC003BFBAC /* 04_BabyScale */, | |||
955725BA276C38CC003BFBAC /* 05_BloodPressure */, | |||
955725E5276C38CC003BFBAC /* 06_HeightGuage */, | |||
78E07B662A49663F00E68200 /* 06B_BroadcastHeightGuage */, | |||
9557261E276C38CD003BFBAC /* 07_RemoteControl */, | |||
95572614276C38CD003BFBAC /* 08_Forehead */, | |||
955725F9276C38CD003BFBAC /* 09_Thermometer */, | |||
@@ -329,6 +352,8 @@ | |||
955725F4276C38CD003BFBAC /* 23_FaceMask */, | |||
95565B742786EA3B000265EA /* 24_Skip */, | |||
78DC4DF72990FC10006C6911 /* 25_HeightBodyFatScale */, | |||
78E07B6C2A49664D00E68200 /* 26_MeatProbe */, | |||
78E07B702A49664E00E68200 /* 27_MeatProbeBox */, | |||
); | |||
path = DeviceDemo; | |||
sourceTree = "<group>"; | |||
@@ -395,6 +420,49 @@ | |||
path = AILinkBleSDKDemo/DeviceDemo/25_HeightBodyFatScale; | |||
sourceTree = SOURCE_ROOT; | |||
}; | |||
78E07B662A49663F00E68200 /* 06B_BroadcastHeightGuage */ = { | |||
isa = PBXGroup; | |||
children = ( | |||
78E07B692A49663F00E68200 /* BroadcastHeightGuage.h */, | |||
78E07B672A49663F00E68200 /* BroadcastHeightGuage.m */, | |||
78E07B682A49663F00E68200 /* BroadcastHeightGuage.xib */, | |||
); | |||
path = 06B_BroadcastHeightGuage; | |||
sourceTree = "<group>"; | |||
}; | |||
78E07B6C2A49664D00E68200 /* 26_MeatProbe */ = { | |||
isa = PBXGroup; | |||
children = ( | |||
78E07B6F2A49664D00E68200 /* MeatProbeVC.h */, | |||
78E07B6D2A49664D00E68200 /* MeatProbeVC.m */, | |||
78E07B6E2A49664D00E68200 /* MeatProbeVC.xib */, | |||
); | |||
path = 26_MeatProbe; | |||
sourceTree = "<group>"; | |||
}; | |||
78E07B702A49664E00E68200 /* 27_MeatProbeBox */ = { | |||
isa = PBXGroup; | |||
children = ( | |||
78E07B712A49664E00E68200 /* Ble */, | |||
78E07B772A49664E00E68200 /* MeatProbeBoxVC.h */, | |||
78E07B792A49664E00E68200 /* MeatProbeBoxVC.m */, | |||
78E07B782A49664E00E68200 /* MeatProbeBoxVC.xib */, | |||
); | |||
path = 27_MeatProbeBox; | |||
sourceTree = "<group>"; | |||
}; | |||
78E07B712A49664E00E68200 /* Ble */ = { | |||
isa = PBXGroup; | |||
children = ( | |||
78E07B762A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.h */, | |||
78E07B742A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.m */, | |||
78E07B732A49664E00E68200 /* ELMeatProbeBoxBleHeader.h */, | |||
78E07B722A49664E00E68200 /* ELMeatProbeBoxBleParser.h */, | |||
78E07B752A49664E00E68200 /* ELMeatProbeBoxBleParser.m */, | |||
); | |||
path = Ble; | |||
sourceTree = "<group>"; | |||
}; | |||
95565B742786EA3B000265EA /* 24_Skip */ = { | |||
isa = PBXGroup; | |||
children = ( | |||
@@ -869,11 +937,14 @@ | |||
files = ( | |||
78DC4DF42990FB40006C6911 /* ELDeviceTypeCell.xib in Resources */, | |||
E622E287243F1D41003C319D /* Main.storyboard in Resources */, | |||
78E07B7E2A49664E00E68200 /* MeatProbeBoxVC.xib in Resources */, | |||
78DC4DE32990FB2E006C6911 /* ELDemoScanVC.xib in Resources */, | |||
E622E26E243F1CA6003C319D /* LaunchScreen.storyboard in Resources */, | |||
E622E26B243F1CA6003C319D /* Assets.xcassets in Resources */, | |||
78DC4DF62990FB40006C6911 /* ELDeviceScanCell.xib in Resources */, | |||
78E07B7B2A49664E00E68200 /* MeatProbeVC.xib in Resources */, | |||
78350BBD29D28C1200456695 /* README.md in Resources */, | |||
78E07B6B2A49664000E68200 /* BroadcastHeightGuage.xib in Resources */, | |||
78DC4DFC2990FC10006C6911 /* HeightBodyFatScaleVC.xib in Resources */, | |||
); | |||
runOnlyForDeploymentPostprocessing = 0; | |||
@@ -930,16 +1001,19 @@ | |||
95572641276C38CD003BFBAC /* ELEightScaleRecordModel.m in Sources */, | |||
95572634276C38CD003BFBAC /* HeightGuageScanViewController.m in Sources */, | |||
958B4EA7263C08E800476BAE /* UIButton+WZAdd.m in Sources */, | |||
78E07B7C2A49664E00E68200 /* ELAILinkBleManager+MeatProbeBox.m in Sources */, | |||
958B4EA9263C08E800476BAE /* ELSelectView.m in Sources */, | |||
95572635276C38CD003BFBAC /* HeightGuageConnectViewController.m in Sources */, | |||
95572639276C38CD003BFBAC /* NutritionScaleConnectionViewController.m in Sources */, | |||
95572647276C38CD003BFBAC /* ForeheadConnectViewController.m in Sources */, | |||
E622E271243F1CA6003C319D /* main.m in Sources */, | |||
78E07B7F2A49664E00E68200 /* MeatProbeBoxVC.m in Sources */, | |||
95572646276C38CD003BFBAC /* FoodThermometerConnectionViewController.m in Sources */, | |||
958B4EB3263C08E800476BAE /* View+MASAdditions.m in Sources */, | |||
958B4EB1263C08E800476BAE /* MASLayoutConstraint.m in Sources */, | |||
9557263A276C38CD003BFBAC /* FaceMaskConnectionViewController.m in Sources */, | |||
95572636276C38CD003BFBAC /* OximeterScanViewController.m in Sources */, | |||
78E07B7D2A49664E00E68200 /* ELMeatProbeBoxBleParser.m in Sources */, | |||
958B4EA4263C08E800476BAE /* ELInputAlertView.m in Sources */, | |||
958B4EB6263C08E800476BAE /* MASViewAttribute.m in Sources */, | |||
958B4EBB263C08E800476BAE /* InheritConnectViewController.m in Sources */, | |||
@@ -949,6 +1023,7 @@ | |||
78DC4DF32990FB40006C6911 /* ELDemoButton.m in Sources */, | |||
958B4EA5263C08E800476BAE /* UILabel+WZAdd.m in Sources */, | |||
95572633276C38CD003BFBAC /* BabyScaleConnectViewController.m in Sources */, | |||
78E07B7A2A49664E00E68200 /* MeatProbeVC.m in Sources */, | |||
95572644276C38CD003BFBAC /* BodyFatScaleConnectViewController.m in Sources */, | |||
958B4EBD263C08E800476BAE /* AppDelegate.m in Sources */, | |||
95565B792786EA3B000265EA /* SkipConnectionViewController.m in Sources */, | |||
@@ -962,6 +1037,7 @@ | |||
95572645276C38CD003BFBAC /* FoodThermometerScanViewController.m in Sources */, | |||
958B4EA6263C08E800476BAE /* UIView+Category.m in Sources */, | |||
9557262E276C38CD003BFBAC /* ELBfsWifiConnectVC.m in Sources */, | |||
78E07B6A2A49664000E68200 /* BroadcastHeightGuage.m in Sources */, | |||
95572637276C38CD003BFBAC /* OximeterConnectionViewController.m in Sources */, | |||
95572648276C38CD003BFBAC /* ForeheadScanViewController.m in Sources */, | |||
78DC4DE42990FB2E006C6911 /* ELDemoDeviceModel.m in Sources */, | |||
@@ -1117,7 +1193,7 @@ | |||
buildSettings = { | |||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
CODE_SIGN_STYLE = Automatic; | |||
CURRENT_PROJECT_VERSION = 20230427; | |||
CURRENT_PROJECT_VERSION = 20230626; | |||
DEVELOPMENT_TEAM = YEU77XR292; | |||
FRAMEWORK_SEARCH_PATHS = ( | |||
"$(inherited)", | |||
@@ -1139,7 +1215,7 @@ | |||
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | |||
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | |||
); | |||
MARKETING_VERSION = 1.5.15; | |||
MARKETING_VERSION = 1.6.1; | |||
OTHER_LDFLAGS = "-ObjC"; | |||
PRODUCT_BUNDLE_IDENTIFIER = com.pingwang.AILinkBleSDKDemo; | |||
PRODUCT_NAME = "$(TARGET_NAME)"; | |||
@@ -1152,7 +1228,7 @@ | |||
buildSettings = { | |||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
CODE_SIGN_STYLE = Automatic; | |||
CURRENT_PROJECT_VERSION = 20230427; | |||
CURRENT_PROJECT_VERSION = 20230626; | |||
DEVELOPMENT_TEAM = YEU77XR292; | |||
FRAMEWORK_SEARCH_PATHS = ( | |||
"$(inherited)", | |||
@@ -1174,7 +1250,7 @@ | |||
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/HTBodyfat_SDK", | |||
"$(PROJECT_DIR)/AILinkBleSDKDemo/DeviceDemo/13_EightScale/BhBodyComposition120", | |||
); | |||
MARKETING_VERSION = 1.5.15; | |||
MARKETING_VERSION = 1.6.1; | |||
OTHER_LDFLAGS = "-ObjC"; | |||
PRODUCT_BUNDLE_IDENTIFIER = com.pingwang.AILinkBleSDKDemo; | |||
PRODUCT_NAME = "$(TARGET_NAME)"; |
@@ -18,12 +18,6 @@ | |||
#import "NELBleManagerHeader.h" | |||
#import "NELMacAddressModel.h" | |||
#import "NSData+AILinkBle.h" | |||
#import "ELAILinkBleManager+AirDetector.h" | |||
#import "ELAirDetectorBleFunctionModel.h" | |||
#import "ELAirDetectorBleHeader.h" | |||
#import "ELAirDetectorBleParser.h" | |||
#import "ELAirDetectorBleSettingModel.h" | |||
#import "ELAirDetectorBleStatusModel.h" | |||
#import "ELAILinkBleManager+MeatProbe.h" | |||
#import "ELMeatProbeBleTool.h" | |||
#import "ELAILinkBleManager+NoiseMeter.h" | |||
@@ -71,6 +65,7 @@ | |||
#import "ELBroadcastNutritionFoodScaleBleManager.h" | |||
#import "ELBroadcastNutritionFoodScaleDataModel.h" | |||
#import "ELBfsLeaOneDataModel.h" | |||
#import "ELBfsOKOKDataModel.h" | |||
#import "ELBroadcastScaleBleHeader.h" | |||
#import "ELBroadcastScaleBleManager.h" | |||
#import "ELBroadcastScaleDataModel.h" |
@@ -9,6 +9,6 @@ | |||
#define AILinkBleSDK_Version_h | |||
//版本号 SDK Version | |||
#define AiLinkBleSDKVersion @"1.5.15" //20230427 增加 蓝牙牙刷防飞溅 | |||
#define AiLinkBleSDKVersion @"1.6.1" //20230626 增加 广播身高仪 食物探针 食物探针盒子 支持 | |||
#endif /* AILinkBleSDK_Version_h */ |
@@ -1,136 +0,0 @@ | |||
// | |||
// ELAILinkBleManager+AirDetector.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2022/12/12. | |||
// | |||
#import "ELAILinkBleManager.h" | |||
#import "ELTLVModel.h" | |||
#import "ELAirDetectorBleHeader.h" | |||
#import "ELAirDetectorBleParser.h" | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAILinkBleManager (AirDetector) | |||
- (NSData *)xxx; | |||
//APP获取设备功能列表 //0x01 CMD:获取设备支持的功能列表 | |||
- (void)airDetectorRequestDeviceFunctions; | |||
- (NSData *)airDetectorRequestDeviceFunctionsPacketWithMac:(NSData *)mac withCid:(UInt16)cid; | |||
//APP获取设备状态 //0x03 CMD:获取设备状态 | |||
- (void)airDetectorRequestDeviceStatus; | |||
- (NSData *)airDetectorRequestDeviceStatusPacketWithMac:(NSData *)mac withCid:(UInt16)cid; | |||
//APP设置/获取参数 //0x05 CMD:设置/获取参数功能 | |||
- (void)airDetectorRequestDeviceSettings; | |||
- (NSData *)airDetectorRequestDeviceSettingsPacketWithMac:(NSData *)mac withCid:(UInt16)cid; | |||
- (void)airDetectorSendDeviceSettings:(NSArray<ELTLVModel *> *)tlvs; | |||
- (NSData *)airDetectorSendDeviceSettingsData:(NSArray<ELTLVModel *> *)tlvs; | |||
//APP与模块心跳 //0x07 CMD:定时心跳包 | |||
- (void)airDetectorSendHeartbeat; | |||
- (NSData *)airDetectorSendHeartbeatPacketWithMac:(NSData *)mac withCid:(UInt16)cid; | |||
@end | |||
@interface ELAILinkBleManager (AirDetectorTLV) | |||
//0x01:甲醛 | |||
+ (ELTLVModel *)tlvHCHOAlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x02:温度 //参数: scale(标度) 表示小数点位数 | |||
+ (ELTLVModel *)tlvTemperatureMinValue:(NSInteger)minValue maxValue:(NSInteger)maxValue uint:(ELDeviceTemperatureUnit)unit scale:(NSInteger)scale; | |||
//0x03:湿度 | |||
+ (ELTLVModel *)tlvHumidityMinValue:(NSInteger)minValue maxValue:(NSInteger)maxValue; | |||
//0x04:PM2.5 | |||
+ (ELTLVModel *)tlvPM2_5AlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x05:PM1.0 | |||
+ (ELTLVModel *)tlvPM1AlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x06:PM10 | |||
+ (ELTLVModel *)tlvPM10AlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x07:VOC | |||
+ (ELTLVModel *)tlvVOCAlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x08:CO2 | |||
+ (ELTLVModel *)tlvCO2AlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x09:AQI | |||
+ (ELTLVModel *)tlvAQIAlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x0B:音量状态 | |||
+ (ELTLVModel *)tlvVolumeOn:(BOOL)on value:(NSInteger)value; | |||
//0x0C:报警时长 | |||
+ (ELTLVModel *)tlvAlertDuration:(NSInteger)duration; | |||
//0x0D:报警铃声 | |||
+ (ELTLVModel *)tlvAlertRing:(NSInteger)ringValue; | |||
//0x0F:设备自检 ?? | |||
+ (ELTLVModel *)tlvSelf:(NSInteger)ringValue; | |||
//0x10:TVOC | |||
+ (ELTLVModel *)tlvTVOCAlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x11:单位切换 | |||
+ (ELTLVModel *)tlvTemperatureUint:(ELDeviceTemperatureUnit)unit; | |||
//0x13:设备绑定 | |||
+ (ELTLVModel *)tlvBindDeviceRequest; | |||
+ (ELTLVModel *)tlvBindDeviceCancel; | |||
//0x15:CO | |||
+ (ELTLVModel *)tlvCOAlarmOn:(BOOL)on value:(NSInteger)value; | |||
//0x16:闹钟设置 | |||
+ (ELTLVModel *)tlvSetupAlarms:(NSArray *)alarms; | |||
//0x17:恢复出厂设置 | |||
+ (ELTLVModel *)tlvRestoreFactory; | |||
//0x18:参数校准设置 | |||
+ (ELTLVModel *)tlvCalibrationUp:(ELAirDetectorTLVType)type; | |||
+ (ELTLVModel *)tlvCalibrationDown:(ELAirDetectorTLVType)type; | |||
+ (ELTLVModel *)tlvCalibrationReset:(ELAirDetectorTLVType)type; | |||
//0x19:时间格式设置 | |||
+ (ELTLVModel *)tlvTimeFormat12H; | |||
+ (ELTLVModel *)tlvTimeFormat24H; | |||
//0x1A:设备亮度设置 | |||
+ (ELTLVModel *)tlvDeviceBrightnessOn:(BOOL)on value:(NSInteger)value; | |||
//0x1B:按键音效设置 | |||
+ (ELTLVModel *)tlvKeySoundOn; | |||
+ (ELTLVModel *)tlvKeySoundOff; | |||
//0x1C:报警音效设置 | |||
+ (ELTLVModel *)tlvAlertSoundOn; | |||
+ (ELTLVModel *)tlvAlertSoundOff; | |||
//0x1D:图标显示设置 | |||
+ (ELTLVModel *)tlvIconDisplayOn; | |||
+ (ELTLVModel *)tlvIconDisplayOff; | |||
//0x1E:监控显示数据 | |||
+ (ELTLVModel *)tlvMonitoringDisplayDataOn; | |||
+ (ELTLVModel *)tlvMonitoringDisplayDataOff; | |||
//0x1F:数据显示模式设置 | |||
+ (ELTLVModel *)tlvDataDisplayMode:(NSInteger)mode; | |||
@end | |||
NS_ASSUME_NONNULL_END | |||
@@ -13,17 +13,13 @@ NS_ASSUME_NONNULL_BEGIN | |||
//MeatProbe 设置设备信息 0x35 | |||
- (void)meatProbeSendCustomData:(NSData *_Nonnull)customData; | |||
- (NSData *)meatProbeSendCustomData:(NSData *)customData; | |||
//MeatProbe 设置设备信息 0x35 请求 | |||
- (void)meatProbeRequestCustomData; | |||
- (NSData *)meatProbeRequestCustomData; | |||
//切换温度单位 A7 | |||
- (void)meatProbeSwitchUint:(ELDeviceTemperatureUnit)unit; | |||
//关闭自动关机指令 A7 **调试阶段使用 | |||
//- (void)meatProbeCloseAutoOff; | |||
- (NSData *)meatProbeSwitchUint:(ELDeviceTemperatureUnit)unit; | |||
@end |
@@ -11,11 +11,6 @@ NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAILinkBleManager (WIFI) | |||
//============ wifi 分包数据 ============== | |||
@property(nonatomic, strong) ELBleWifiDetailModel *_Nullable wifiDetailModel; | |||
@property(nonatomic, strong) NSMutableData *_Nullable urlBuffData; | |||
@property(nonatomic, strong) NSMutableData *_Nullable pathBuffData; | |||
//============ Bluetooth WIFI calling method(蓝牙WIFI 设置命令) ============== | |||
/** |
@@ -31,9 +31,13 @@ NS_ASSUME_NONNULL_BEGIN | |||
- (NSString *)sdkVersion; | |||
+ (NSString *)sdkVersion; | |||
//扫描状态 | |||
@property(nonatomic, assign, readonly, getter=isScanning) BOOL scanning; | |||
@property(nonatomic, assign, readonly) CBCentralManager *central; | |||
//BLE设备连接状态 | |||
@property(nonatomic, assign, readonly) NELBleManagerConnectState connectState; | |||
@property(nonatomic, strong, readonly) CBCentralManager *central; | |||
@property (nonatomic, weak) id<ELAILinkBleManagerDelegate> delegate DEPRECATED_MSG_ATTRIBUTE("use 'ailinkDelegate' only"); | |||
@@ -150,12 +154,4 @@ NS_ASSUME_NONNULL_BEGIN | |||
@end | |||
#pragma mark - specialMethod | |||
@interface ELAILinkBleManager (specialMethod) | |||
- (BOOL)specialMethodWithCid:(UInt16)cid vid:(UInt16)vid pid:(UInt16)pid secret:(NSString *)secret; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -1,126 +0,0 @@ | |||
// | |||
// ELAirDetectorBleFunctionModel.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2022/12/13. | |||
// | |||
#import <Foundation/Foundation.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAirDetectorBleFunctionModel : NSObject | |||
//0x01:甲醛 | |||
@property (nonatomic, assign) NSInteger HCHOScale; | |||
@property (nonatomic, assign) NSInteger HCHOMin; | |||
@property (nonatomic, assign) NSInteger HCHOMax; | |||
//0x02:温度 | |||
@property (nonatomic, assign) NSInteger temperatureScale; | |||
@property (nonatomic, assign) NSInteger temperatureMin; | |||
@property (nonatomic, assign) NSInteger temperatureMax; | |||
//0x03:湿度 | |||
@property (nonatomic, assign) NSInteger humidityScale; | |||
@property (nonatomic, assign) NSInteger humidityMin; | |||
@property (nonatomic, assign) NSInteger humidityMax; | |||
//0x04:PM2.5 | |||
@property (nonatomic, assign) NSInteger PM2_5Scale; | |||
@property (nonatomic, assign) NSInteger PM2_5Min; | |||
@property (nonatomic, assign) NSInteger PM2_5Max; | |||
//0x05:PM1.0 | |||
@property (nonatomic, assign) NSInteger PM1Scale; | |||
@property (nonatomic, assign) NSInteger PM1Min; | |||
@property (nonatomic, assign) NSInteger PM1Max; | |||
//0x06:PM10 | |||
@property (nonatomic, assign) NSInteger PM10Scale; | |||
@property (nonatomic, assign) NSInteger PM10Min; | |||
@property (nonatomic, assign) NSInteger PM10Max; | |||
//0x07:VOC | |||
@property (nonatomic, assign) NSInteger VOCScale; | |||
@property (nonatomic, assign) NSInteger VOCMin; | |||
@property (nonatomic, assign) NSInteger VOCMax; | |||
//0x08:CO2 | |||
@property (nonatomic, assign) NSInteger CO2Scale; | |||
@property (nonatomic, assign) NSInteger CO2Min; | |||
@property (nonatomic, assign) NSInteger CO2Max; | |||
//0x09:AQI | |||
@property (nonatomic, assign) NSInteger AQIScale; | |||
@property (nonatomic, assign) NSInteger AQIMin; | |||
@property (nonatomic, assign) NSInteger AQIMax; | |||
//0x0A:报警功能 | |||
@property (nonatomic, assign) BOOL alertSupport; | |||
//0x0B:音量 | |||
@property (nonatomic, assign) NSInteger volumeMax; | |||
//0x0C:报警时长 | |||
@property (nonatomic, assign) NSInteger alertDurationMax; | |||
//0x0D:报警铃声 | |||
@property (nonatomic, assign) NSInteger alertRingCount; | |||
//0x0E:设备故障 | |||
@property (nonatomic, assign) BOOL deviceErrorSupport; | |||
//0x0F:设备自检 | |||
@property (nonatomic, assign) BOOL deviceSelfTestSupport; | |||
//0x10:TVOC | |||
@property (nonatomic, assign) NSInteger TVOCScale; | |||
@property (nonatomic, assign) NSInteger TVOCMin; | |||
@property (nonatomic, assign) NSInteger TVOCMax; | |||
//0x15:CO(一氧化碳) | |||
@property (nonatomic, assign) NSInteger COScale; | |||
@property (nonatomic, assign) NSInteger COMin; | |||
@property (nonatomic, assign) NSInteger COMax; | |||
//0x16:闹钟功能声明 | |||
@property (nonatomic, assign) BOOL alarmAllSupport; | |||
@property (nonatomic, assign) NSInteger alarmCount; | |||
@property (nonatomic, assign) NSInteger alarmMode; | |||
//0x17:恢复出厂设置声明 | |||
@property (nonatomic, assign) BOOL restoreFactorySupport; | |||
//0x18:参数校准声明 | |||
@property (nonatomic, strong) NSArray<NSNumber *> *_Nullable calibrationSupportArray; | |||
//0x19:时间格式声明 | |||
@property (nonatomic, assign) BOOL timeFormat12HSupport; | |||
@property (nonatomic, assign) BOOL timeFormat24HSupport; | |||
//0x1A:设备亮度声明 | |||
@property (nonatomic, assign) BOOL deviceBrightnessAuto; | |||
@property (nonatomic, assign) BOOL deviceBrightnessManual; | |||
@property (nonatomic, assign) BOOL deviceBrightnessGearMode; | |||
@property (nonatomic, assign) BOOL deviceBrightnessGearMax; | |||
//0x1B:按键音效声明 | |||
@property (nonatomic, assign) BOOL keySoundSupport; | |||
//0x1C:报警音效声明 | |||
@property (nonatomic, assign) BOOL alertSoundSupport; | |||
//0x1D:图标显示声明 | |||
@property (nonatomic, assign) BOOL iconDisplaySupport; | |||
//0x1E:监控显示数据声明 | |||
@property (nonatomic, assign) BOOL monitoringDisplayDataSupport; | |||
//0x1F:数据显示模式声明 | |||
@property (nonatomic, assign) NSInteger dataDisplayModeCount; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -1,63 +0,0 @@ | |||
// | |||
// ELAirDetectorBleHeader.h | |||
// Pods | |||
// | |||
// Created by LarryZhang on 2022/12/13. | |||
// | |||
#ifndef ELAirDetectorBleHeader_h | |||
#define ELAirDetectorBleHeader_h | |||
typedef NS_ENUM(NSUInteger, ELAirDetectorBleCMD) { | |||
ELAirDetectorBleCMD01 = 0x01, //CMD:获取设备支持的功能列表 | |||
ELAirDetectorBleCMD02 = 0x02, //CMD:设备返回功能 | |||
ELAirDetectorBleCMD03 = 0x03, //CMD:获取设备状态 | |||
ELAirDetectorBleCMD04 = 0x04, //CMD:设备返回状态 | |||
ELAirDetectorBleCMD05 = 0x05, //CMD:设置/获取参数功能 | |||
ELAirDetectorBleCMD06 = 0x06, //CMD:MCU返回参数功能 | |||
ELAirDetectorBleCMD07 = 0x07, //CMD:定时心跳包 | |||
ELAirDetectorBleCMD08 = 0x08, //CMD:模块返回心跳包 | |||
ELAirDetectorBleCMDF1 = 0xF1, //CMD:上发保存数据 | |||
}; | |||
typedef NS_ENUM(NSUInteger, ELAirDetectorTLVType) { | |||
ELAirDetectorTLVTypeHCHO = 0x01, //甲醛 | |||
ELAirDetectorTLVTypeTemperature = 0x02, //温度 | |||
ELAirDetectorTLVTypeHumidity = 0x03, //湿度 | |||
ELAirDetectorTLVTypePM2_5 = 0x04, //PM2.5 | |||
ELAirDetectorTLVTypePM1 = 0x05, //PM1 | |||
ELAirDetectorTLVTypePM10 = 0x06, //PM10 | |||
ELAirDetectorTLVTypeVOC = 0x07, | |||
ELAirDetectorTLVTypeCO2 = 0x08, | |||
ELAirDetectorTLVTypeAQI = 0x09, | |||
ELAirDetectorTLVTypeSettingAlert = 0x0A, //报警功能 | |||
ELAirDetectorTLVTypeSettingVolume = 0x0B, //音量 | |||
ELAirDetectorTLVTypeSettingAlertDuration = 0x0C, //报警时长 | |||
ELAirDetectorTLVTypeSettingAlertRing = 0x0D, //报警铃声 | |||
ELAirDetectorTLVTypeSettingDeviceError = 0x0E, //设备故障 | |||
ELAirDetectorTLVTypeSettingDeviceSelfTest = 0x0F, //设备自检 | |||
ELAirDetectorTLVTypeTVOC = 0x10, //TVOC | |||
ELAirDetectorTLVTypeSwitchTemperatureUnit = 0x11, //单位切换 | |||
ELAirDetectorTLVTypeBatteryState = 0x12, //电池状态 | |||
ELAirDetectorTLVTypeBindDevice = 0x13, //设备绑定 | |||
ELAirDetectorTLVTypeHeartbeat = 0x14, //心跳包(用以模块和APP通信) | |||
ELAirDetectorTLVTypeCO = 0x15, //CO(一氧化碳) | |||
ELAirDetectorTLVTypeAlarm = 0x16, //闹钟功能 | |||
ELAirDetectorTLVTypeRestoreFactorySetting = 0x17, //恢复出厂设置 | |||
ELAirDetectorTLVTypeCalibrationParameter = 0x18, //参数校准 | |||
ELAirDetectorTLVTypeTimeFormat = 0x19, //时间格式 | |||
ELAirDetectorTLVTypeDeviceBrightness = 0x1A, //设备亮度 | |||
ELAirDetectorTLVTypeKeySound = 0x1B, //按键音效 | |||
ELAirDetectorTLVTypeAlertSound = 0x1C, //报警音效 | |||
ELAirDetectorTLVTypeIconDisplay = 0x1D, //图标显示 | |||
ELAirDetectorTLVTypeMonitoringDisplayData = 0x1E, //监控显示数据 | |||
ELAirDetectorTLVTypeDataDisplayMode = 0x1F, //数据显示模式 | |||
}; | |||
#endif /* ELAirDetectorBleHeader_h */ |
@@ -1,32 +0,0 @@ | |||
// | |||
// ELAirDetectorBleParser.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2022/12/12. | |||
// | |||
#import <Foundation/Foundation.h> | |||
#import "ELSDKHeader.h" | |||
#import "ELAirDetectorBleHeader.h" | |||
#import "ELAirDetectorBleFunctionModel.h" | |||
#import "ELAirDetectorBleStatusModel.h" | |||
#import "ELAirDetectorBleSettingModel.h" | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAirDetectorBleParser : NSObject | |||
typedef void(^airDetectorDidUpdateDataBlock)(ELAirDetectorBleCMD cmd, ELAirDetectorTLVType type); | |||
@property (nonatomic, strong) ELAirDetectorBleFunctionModel *functionModel; | |||
@property (nonatomic, strong) ELAirDetectorBleStatusModel *statusModel; | |||
@property (nonatomic, strong) ELAirDetectorBleSettingModel *settingModel; | |||
- (void)parseData:(NSData *)payload callback:(airDetectorDidUpdateDataBlock)calllback; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -1,113 +0,0 @@ | |||
// | |||
// ELAirDetectorBleSettingModel.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2022/12/14. | |||
// | |||
#import <Foundation/Foundation.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAirDetectorBleSettingModel : NSObject | |||
//0x01:甲醛 | |||
@property (nonatomic, assign) BOOL HCHOAlertOn; | |||
@property (nonatomic, assign) NSInteger HCHOAlertValue; | |||
//0x02:温度 | |||
@property (nonatomic, assign) NSInteger temperatureScale; | |||
@property (nonatomic, assign) NSInteger temperatureUint; | |||
@property (nonatomic, assign) NSInteger temperatureAlertMinValue; | |||
@property (nonatomic, assign) NSInteger temperatureAlertMaxValue; | |||
//0x03:湿度 | |||
@property (nonatomic, assign) NSInteger humidityAlertMinValue; | |||
@property (nonatomic, assign) NSInteger humidityAlertMaxValue; | |||
//0x04:PM2.5 | |||
@property (nonatomic, assign) BOOL PM2_5AlertOn; | |||
@property (nonatomic, assign) NSInteger PM2_5AlertValue; | |||
//0x05:PM1.0 | |||
@property (nonatomic, assign) BOOL PM1AlertOn; | |||
@property (nonatomic, assign) NSInteger PM1AlertValue; | |||
//0x06:PM10 | |||
@property (nonatomic, assign) BOOL PM10AlertOn; | |||
@property (nonatomic, assign) NSInteger PM10AlertValue; | |||
//0x07:VOC | |||
@property (nonatomic, assign) BOOL VOCAlertOn; | |||
@property (nonatomic, assign) NSInteger VOCAlertValue; | |||
//0x08:CO2 | |||
@property (nonatomic, assign) BOOL CO2AlertOn; | |||
@property (nonatomic, assign) NSInteger CO2AlertValue; | |||
//0x09:AQI | |||
@property (nonatomic, assign) BOOL AQIAlertOn; | |||
@property (nonatomic, assign) NSInteger AQIAlertValue; | |||
//0x0B:音量状态 | |||
@property (nonatomic, assign) BOOL volumeAlertOn; | |||
@property (nonatomic, assign) NSInteger volumeValue; | |||
//0x0C:报警时长 | |||
@property (nonatomic, assign) NSInteger alertDurationValue; | |||
//0x0D:报警铃声 | |||
@property (nonatomic, assign) NSInteger alertRingValue; | |||
//0x0F:设备自检 | |||
@property (nonatomic, assign) BOOL deviceSelfTesting; | |||
//0x10:TVOC | |||
@property (nonatomic, assign) BOOL TVOCAlertOn; | |||
@property (nonatomic, assign) NSInteger TVOCAlertValue; | |||
//0x11:单位切换 | |||
@property (nonatomic, assign) NSInteger switchTemperatureUint; | |||
//0x13:设备绑定 | |||
@property (nonatomic, assign) NSInteger bandState; | |||
//0x15:CO | |||
@property (nonatomic, assign) BOOL COAlertOn; | |||
@property (nonatomic, assign) NSInteger COAlertValue; | |||
//0x16:闹钟设置 //原始数据未解析 | |||
@property (nonatomic, strong) NSData *alarmSettingData; | |||
//0x17:恢复出厂设置 | |||
@property (nonatomic, assign) NSInteger restoreFactoryState; | |||
//0x18:参数校准设置 //原始数据未解析 | |||
@property (nonatomic, strong) NSData *calibrationSettingData; | |||
//0x19:时间格式设置 | |||
@property (nonatomic, assign) NSInteger timeFormatValue; | |||
//0x1A:设备亮度状态 | |||
@property (nonatomic, assign) BOOL deviceBrightnessOn; | |||
@property (nonatomic, assign) NSInteger deviceBrightnessValue; | |||
//按键音效设置 | |||
@property (nonatomic, assign) BOOL keySoundOn; | |||
//0x1C:报警音效设置 | |||
@property (nonatomic, assign) BOOL alertSoundOn; | |||
//0x1D:图标显示设置 | |||
@property (nonatomic, assign) BOOL iconDisplayOn; | |||
//0x1E:监控显示数据 | |||
@property (nonatomic, assign) BOOL monitoringDisplayDataOn; | |||
//0x1F:数据显示模式设置 | |||
@property (nonatomic, assign) NSInteger dataDisplayModeValue; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -1,117 +0,0 @@ | |||
// | |||
// ELAirDetectorBleStatusModel.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2022/12/13. | |||
// | |||
#import <Foundation/Foundation.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAirDetectorBleStatusModel : NSObject | |||
//0x01:甲醛 | |||
@property (nonatomic, assign) NSInteger HCHOValue; | |||
//0x02:温度 | |||
@property (nonatomic, assign) NSInteger temperatureScale; | |||
@property (nonatomic, assign) NSInteger temperatureValue; | |||
@property (nonatomic, assign) NSInteger temperatureUint; | |||
//0x03:湿度 | |||
@property (nonatomic, assign) NSInteger humidityValue; | |||
//0x04:PM2.5 | |||
@property (nonatomic, assign) NSInteger PM2_5Value; | |||
//0x05:PM1.0 | |||
@property (nonatomic, assign) NSInteger PM1Value; | |||
//0x06:PM10 | |||
@property (nonatomic, assign) NSInteger PM10Value; | |||
//0x07:VOC | |||
@property (nonatomic, assign) NSInteger VOCValue; | |||
//0x08:CO2 | |||
@property (nonatomic, assign) NSInteger CO2Value; | |||
//0x09:AQI | |||
@property (nonatomic, assign) NSInteger AQIValue; | |||
//0x0A:报警状态 | |||
@property (nonatomic, assign) BOOL alertOn; | |||
@property (nonatomic, assign) BOOL alertHCHOHigh; | |||
@property (nonatomic, assign) BOOL alertTemperatureLow; | |||
@property (nonatomic, assign) BOOL alertTemperatureHigh; | |||
@property (nonatomic, assign) BOOL alertHumidityLow; | |||
@property (nonatomic, assign) BOOL alertHumidityHigh; | |||
@property (nonatomic, assign) BOOL alertPM2_5; | |||
@property (nonatomic, assign) BOOL alertPM1; | |||
@property (nonatomic, assign) BOOL alertPM10; | |||
@property (nonatomic, assign) BOOL alertVOC; | |||
@property (nonatomic, assign) BOOL alertCO2; | |||
@property (nonatomic, assign) BOOL alertAQI; | |||
@property (nonatomic, assign) BOOL alertTVOC; | |||
//0x0B:音量状态 | |||
@property (nonatomic, assign) BOOL volumeOn; | |||
@property (nonatomic, assign) NSInteger volumeValue; | |||
//0x0C:报警时长 | |||
@property (nonatomic, assign) NSInteger alertDurationValue; | |||
//0x0D:报警铃声 | |||
@property (nonatomic, assign) NSInteger alertRingValue; | |||
//0x0E:设备故障 | |||
@property (nonatomic, assign) BOOL deviceError; | |||
//0x0F:设备自检 | |||
@property (nonatomic, assign) BOOL deviceSelfTesting; | |||
//0x10:TVOC | |||
@property (nonatomic, assign) NSInteger TVOCValue; | |||
//0x12:电池状态 | |||
@property (nonatomic, assign) NSInteger batteryState; | |||
@property (nonatomic, assign) NSInteger batteryPercentValue; | |||
//0x15:CO | |||
@property (nonatomic, assign) NSInteger COValue; | |||
//0x16:闹钟状态 //原始数据未解析 | |||
@property (nonatomic, strong) NSData *alarmStatusData; | |||
//0x17:恢复出厂设置 | |||
@property (nonatomic, assign) NSInteger restoreFactoryState; | |||
//0x18:参数校准状态 //原始数据未解析 | |||
@property (nonatomic, strong) NSData *calibrationStatusData; | |||
//0x19:时间格式状态 | |||
@property (nonatomic, assign) NSInteger timeFormatValue; | |||
//0x1A:设备亮度状态 | |||
@property (nonatomic, assign) BOOL deviceBrightnessOn; | |||
@property (nonatomic, assign) NSInteger deviceBrightnessValue; | |||
//0x1B:按键音效状态 | |||
@property (nonatomic, assign) BOOL keySoundOn; | |||
//0x1C:报警音效状态 | |||
@property (nonatomic, assign) BOOL alertSoundOn; | |||
//0x1D:图标显示状态 | |||
@property (nonatomic, assign) BOOL iconDisplayOn; | |||
//0x1E:监控显示数据状态 | |||
@property (nonatomic, assign) BOOL monitoringDisplayDataOn; | |||
//0x1F:数据显示模式 | |||
@property (nonatomic, assign) NSInteger dataDisplayModeValue; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -0,0 +1,49 @@ | |||
// | |||
// ELBfsOKOKDataModel.h | |||
// Elink | |||
// | |||
#import <Foundation/Foundation.h> | |||
#import "ELBroadcastScaleBleHeader.h" | |||
NS_ASSUME_NONNULL_BEGIN | |||
typedef NS_ENUM(NSInteger, OKOKDevType) { | |||
/** 体重秤 */ | |||
OKOKDevType_Weight = 0 , | |||
/** 体脂秤 */ | |||
OKOKDevType_Bfr , | |||
}; | |||
/** lea one 秤返回的数据对象模型 */ | |||
@interface ELBfsOKOKDataModel : NSObject | |||
/** 流水号 */ | |||
@property (nonatomic, assign) NSInteger serialNumber; | |||
/** 设备返回的原始体重 */ | |||
@property (nonatomic, assign) NSInteger rawWeight; | |||
/** 电阻 */ | |||
@property (nonatomic, assign) NSInteger adc; | |||
/** mac地址 */ | |||
@property (nonatomic, copy) NSString *mac; | |||
/** 设备类型 */ | |||
@property (nonatomic, assign) OKOKDevType devType; | |||
/** 单位 (仅支持 kg,斤,lb和st:lb) */ | |||
@property (nonatomic, assign) ELDeviceWeightUnit unit; | |||
/** 小数位 */ | |||
@property (nonatomic, assign) NSInteger weightPoint; | |||
/** 是否为锁定数据 */ | |||
@property (nonatomic, assign) BOOL isLockData; | |||
+ (instancetype)initWithDevBleData:(NSData *)data; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -73,6 +73,8 @@ NS_ASSUME_NONNULL_BEGIN | |||
@return 结构体 | |||
*/ | |||
+(struct ELMCUStateStruct)analysisBluetoothConnectState:(NSData *)data; | |||
//重构 | |||
+ (ELWMStateStruct)analysisWMState:(NSData *)data; | |||
/** | |||
解析模块通信模式 |
@@ -72,6 +72,9 @@ NS_ASSUME_NONNULL_BEGIN | |||
@param stateStrct 结构体 | |||
*/ | |||
-(void)bluetoothManagerReceiveMCUConnectedState:(struct ELMCUStateStruct)stateStrct; | |||
//重构 | |||
-(void)bluetoothManagerReceiveWMState:(ELWMStateStruct)stateStrct; | |||
-(void)bluetoothManagerReceiveWMStateFailCode:(WMWorkStateFailCode)failCode; | |||
/** | |||
Query the callback of the communication mode(查询通信模式的回调) |
@@ -68,6 +68,7 @@ static NSString *GET_A7_DECRYPT_BLE_PAYLOAD_DATA_CONTENT = @"GET_A7_DECRYPT_BLE_ | |||
//Start scanning(开始扫描)- 过滤 cid 列表 | |||
-(void)startScanFilterCids:(NSArray<NSNumber *> *)cids; | |||
-(void)startScanAllFilterCids:(NSArray<NSNumber *> *)cids; | |||
//Start scanning all (开始扫描附近所有设备) | |||
- (void)startScanAll; |
@@ -41,6 +41,8 @@ NS_ASSUME_NONNULL_BEGIN | |||
@property (nonatomic, assign) long long createTime; | |||
@property (nonatomic, assign) BOOL weightNegative; | |||
@property (nonatomic, assign) UInt16 weightValue; | |||
@end | |||
@@ -106,10 +106,16 @@ typedef NS_ENUM(NSUInteger, ELSupportDeviceType) { | |||
ELSupportDeviceTypeBroadcastScale = (0xffff +0x01) , //655536 Broadcast body fat scale(AILink广播体脂秤) | |||
ELSupportDeviceTypeBroadcastHeightGauge = (0xffff + 0x03) , //65538 广播身高仪 | |||
ELSupportDeviceTypeBroadcastScale_Lingyang = (0xffff +0x16) , //65557 凌阳广播体脂秤 | |||
ELSupportDeviceTypeBroadcastBodyScale = (0xFFFF + 0x07) , //Broadcast body scale AILink广播体重秤(无体脂) | |||
ELSupportDeviceTypeBroadcastNutritionFoodScale = (0xffff + 0x04) , //65539 广播营养秤 | |||
ELSupportDeviceTypeBroadcastHygrothermograph = (0xffff + 0x06) , //65541 广播温湿度计 | |||
ELSupportDeviceTypeBroadcastScale_OKOK = (0xffff + 0x08) , //65543 OKOK广播体脂秤 | |||
ELSupportDeviceTypeBroadcastScale_T6 = (0xffff + 0x09) , //65544 T6广播体脂秤 | |||
}; | |||
#endif /* ELExSupportDeviceType_h */ |
@@ -37,6 +37,7 @@ typedef NS_ENUM(NSUInteger, ELInetGetCmdType) { | |||
ELInetGetCmdTypeGetC_V_P_ID = 0x1E, //-Read CID, VID, PID(读取CID、VID、PID) | |||
ELInetGetCmdTypeGetCommunicationMode = 0x20, //-Get module communication mode(获取模块通信模式) | |||
ELInetGetCmdTypeGetConnectState = 0x26, //-Get Bluetooth and WiFi connection status(获取蓝牙和WiFi连接状态) | |||
ELInetGetCmdTypeGetConnectStateFailCode = 0xAB, //- WiFi connection error 连接失败原因 | |||
ELInetGetCmdTypeGetBatteryState = 0x28, //-Get MCU battery status(获取MCU电池状态) | |||
ELInetGetCmdTypeGetHandshake = 0x24, //-Encrypted handshake data returned by Bluetooth(蓝牙返回的加密的握手数据) | |||
ELInetGetCmdTypeGetBodyFatScaleA6Data = 0x2B, //-Body fat scale 0x2B stands for A6 protocol (see the second level instructions for details)(体脂秤0x2B代表A6协议(具体见二级指令)) | |||
@@ -195,6 +196,50 @@ struct ELMCUStateStruct { | |||
BleWiFiConnectServerFaildState faildState; | |||
}; | |||
//重构 | |||
//BLE状态 | |||
typedef NS_ENUM(Byte, WMBleState) { | |||
WMBleStateNoConnection = 0,//无连接 | |||
WMBleStateConnected = 1,//已连接 | |||
WMBleStatePaired = 2,//配对完成 | |||
WMBleStateMax = 0xFF | |||
}; | |||
//wifi状态 | |||
typedef NS_ENUM(Byte, WMWiFiState) { | |||
WMWiFiStateNotSetAP = 0, //未配置AP | |||
WMWiFiStateConnectAPFail = 1, //连接AP失败,连接时密码错误、AP信号不好、主动断开都会是这个状态 | |||
WMWiFiStateConnectServerFail = 2, //与服务器通讯失败 | |||
WMWiFiStateConnectedAP = 3, //成功连接上AP | |||
WMWiFiStateConnectingAP = 4, //正在连接AP | |||
WMWiFiStateMax = 0xFF | |||
}; | |||
//工作状态 | |||
typedef NS_ENUM(Byte, WMWorkState) { | |||
WMWorkStateWakeup = 0,//唤醒 | |||
WMWorkStateSleep = 1,//进入休眠 | |||
WMWorkStateReady = 2,//模块准备就绪 | |||
WMWorkStateMax = 0xFF | |||
}; | |||
//WiFi连接失败原因 | |||
typedef NS_ENUM(Byte, WMWorkStateFailCode) { | |||
WMWorkStateFailCodeUnknow = 0,//未知原因 | |||
WMWorkStateFailCodeAPSignalBad = 1,//AP信号差 | |||
WMWorkStateFailCodeWrongPassword = 2,//密码错误 | |||
WMWorkStateFailCodeNoIP = 3,//获取不到IP | |||
WMWorkStateFailCodeMax = 0xFF | |||
}; | |||
typedef struct { | |||
WMBleState bleState; | |||
WMWiFiState wifiState; | |||
WMWorkState workState; | |||
WMWorkStateFailCode failCode; | |||
} ELWMStateStruct, ELWMStateStruct; | |||
#pragma mark - AiLink unit | |||
/** Type key of unit string(单位字符串的type key) */ |
@@ -120,6 +120,9 @@ NS_ASSUME_NONNULL_BEGIN | |||
///获取设备连接的wifi名称 | |||
- (void)getBleLinkWifiName; | |||
/** 设置设备的IP地址,端口号和访问路径 */ | |||
- (void)setDevAccessIpAddress:(NSString *)address portNumber:(NSUInteger)portNum path:(NSString *)path; | |||
@property (nonatomic,assign) ELBluetoothState bleWifiBloodStatus; | |||
@@ -0,0 +1,17 @@ | |||
// | |||
// BroadcastHeightGuage.h | |||
// AILinkBleSDK_Example | |||
// | |||
// Created by LarryZhang on 2023/6/26. | |||
// Copyright © 2023 zhengzida. All rights reserved. | |||
// | |||
#import <UIKit/UIKit.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface BroadcastHeightGuage : UIViewController | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -0,0 +1,127 @@ | |||
// | |||
// BroadcastHeightGuage.m | |||
// AILinkBleSDK_Example | |||
// | |||
// Created by LarryZhang on 2023/6/26. | |||
// Copyright © 2023 zhengzida. All rights reserved. | |||
// | |||
#import "BroadcastHeightGuage.h" | |||
#import <AILinkBleSDK/ELBroadcastHeightGaugeBleManager.h> | |||
@interface BroadcastHeightGuage () | |||
@property (weak, nonatomic) IBOutlet UILabel *bleStatusLabel; | |||
@property (weak, nonatomic) IBOutlet UITextView *logTextView; | |||
@end | |||
@interface BroadcastHeightGuage () <ELBroadcastHeightGaugeBleDelegate> | |||
@end | |||
@implementation BroadcastHeightGuage | |||
- (void)viewDidLoad { | |||
[super viewDidLoad]; | |||
// Do any additional setup after loading the view from its nib. | |||
[self initBle]; | |||
[self scanBle]; | |||
} | |||
- (void)dealloc { | |||
[self deinitBle]; | |||
} | |||
#pragma mark - Action | |||
- (IBAction)clearAction:(id)sender { | |||
self.logTextView.text = @""; | |||
} | |||
- (IBAction)shareLogAction:(id)sender { | |||
} | |||
#pragma mark - BLE | |||
- (void)initBle { | |||
[ELBroadcastHeightGaugeBleManager shareManager].heightGaugeDelegate = self; | |||
} | |||
- (void)scanBle { | |||
[[ELBroadcastHeightGaugeBleManager shareManager] startScanAll]; | |||
} | |||
- (void)deinitBle { | |||
[[ELBroadcastHeightGaugeBleManager shareManager] stopScan]; | |||
[ELBroadcastHeightGaugeBleManager shareManager].heightGaugeDelegate = nil; | |||
} | |||
/**Callback Bluetooth status(回调蓝牙状态) */ | |||
- (void)broadcastHeightGaugeUpdateState:(ELBluetoothState)state { | |||
if (state == ELBluetoothStateAvailable) { | |||
self.bleStatusLabel.text = @"Connecting"; | |||
} else if (state == ELBluetoothStateUnavailable) { | |||
self.bleStatusLabel.text = @"Bluetooth is disconnected"; | |||
} | |||
} | |||
/** 回调身高数据 */ | |||
- (void)broadcastHeightGaugeDeviceDataModel:(ELBroadcastHeightGaugeBleDataModel *)model { | |||
static NSInteger serialNumber = 0; | |||
if (serialNumber == model.serialNumber) { | |||
return; | |||
} | |||
serialNumber = model.serialNumber; | |||
switch (model.testState) { | |||
case BroadcastHeightGuage_TestState_Measuring: { | |||
self.bleStatusLabel.text = @"Measuring"; | |||
break; | |||
} | |||
case BroadcastHeightGuage_TestState_Stable: { | |||
self.bleStatusLabel.text = @"Stabl"; | |||
break; | |||
} | |||
case BroadcastHeightGuage_TestState_Failed: { | |||
self.bleStatusLabel.text = @"Failed"; | |||
break; | |||
} | |||
} | |||
NSString *log = [NSString stringWithFormat:@"MAC:%@ cid = %d--vid=%d--pid=%d\n Height = %@%@", model.mac, model.cid - 0xFFFF, model.vid, model.pid, @(model.height), AiLinkBleHeightUnitDic[@(model.heightUnit)]]; | |||
[self addLog:log]; | |||
} | |||
#pragma mark - Log | |||
- (void)addLog:(NSString *)log { | |||
[self addLog:log newline:YES]; | |||
} | |||
- (void)addWithoutNewlineLog:(NSString *)log { | |||
[self addLog:log newline:NO]; | |||
} | |||
- (void)addLog:(NSString *)log newline:(BOOL)newline { | |||
NSDateFormatter *format = [[NSDateFormatter alloc] init]; | |||
format.dateFormat = @"HH:mm:ss.SSS"; | |||
NSString *time = [format stringFromDate:[NSDate date]]; | |||
if (newline) { | |||
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log]; | |||
} else { | |||
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log]; | |||
} | |||
[self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)]; | |||
} | |||
@end |
@@ -0,0 +1,101 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" 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="21679"/> | |||
<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="BroadcastHeightGuage"> | |||
<connections> | |||
<outlet property="bleStatusLabel" destination="0xz-aS-XmR" id="M4X-Dn-08b"/> | |||
<outlet property="logTextView" destination="7mG-7x-9xa" id="8Vr-ji-Eql"/> | |||
<outlet property="view" destination="Cra-or-dGB" id="NoP-us-drS"/> | |||
</connections> | |||
</placeholder> | |||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | |||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="Cra-or-dGB"> | |||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | |||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
<subviews> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="y1w-g7-Gwh"> | |||
<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"/> | |||
<state key="normal" title="清空"/> | |||
<connections> | |||
<action selector="clearAction:" destination="-1" eventType="touchUpInside" id="k92-gw-3vb"/> | |||
</connections> | |||
</button> | |||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PGm-jo-Ko3"> | |||
<rect key="frame" x="346" y="346" 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="jj3-ON-0lh"/> | |||
</connections> | |||
</button> | |||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0xz-aS-XmR"> | |||
<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="itb-kd-JGo"> | |||
<rect key="frame" x="8" y="384" width="377" height="434"/> | |||
<subviews> | |||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7mG-7x-9xa"> | |||
<rect key="frame" x="0.0" y="0.0" width="377" height="434"/> | |||
<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 firstItem="7mG-7x-9xa" firstAttribute="leading" secondItem="itb-kd-JGo" secondAttribute="leading" id="0NM-af-zvv"/> | |||
<constraint firstAttribute="trailing" secondItem="7mG-7x-9xa" secondAttribute="trailing" id="Q09-hB-QcZ"/> | |||
<constraint firstItem="7mG-7x-9xa" firstAttribute="top" secondItem="itb-kd-JGo" secondAttribute="top" id="kkV-Sm-BVr"/> | |||
<constraint firstAttribute="bottom" secondItem="7mG-7x-9xa" secondAttribute="bottom" id="nqD-ZR-BY0"/> | |||
</constraints> | |||
<userDefinedRuntimeAttributes> | |||
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> | |||
<real key="value" value="8"/> | |||
</userDefinedRuntimeAttribute> | |||
</userDefinedRuntimeAttributes> | |||
</view> | |||
</subviews> | |||
<viewLayoutGuide key="safeArea" id="k4u-s9-7Su"/> | |||
<color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
<constraints> | |||
<constraint firstItem="k4u-s9-7Su" firstAttribute="bottom" secondItem="itb-kd-JGo" secondAttribute="bottom" id="031-Ze-0Uo"/> | |||
<constraint firstItem="itb-kd-JGo" firstAttribute="top" secondItem="PGm-jo-Ko3" secondAttribute="bottom" constant="8" symbolic="YES" id="49Z-8f-jcF"/> | |||
<constraint firstItem="itb-kd-JGo" firstAttribute="top" secondItem="0xz-aS-XmR" secondAttribute="bottom" constant="300" id="6aU-tq-ex8"/> | |||
<constraint firstItem="itb-kd-JGo" firstAttribute="top" secondItem="y1w-g7-Gwh" secondAttribute="bottom" constant="8" symbolic="YES" id="MZu-VN-DtI"/> | |||
<constraint firstItem="y1w-g7-Gwh" firstAttribute="leading" secondItem="k4u-s9-7Su" secondAttribute="leading" constant="16" id="O7j-eL-y7n"/> | |||
<constraint firstItem="k4u-s9-7Su" firstAttribute="trailing" secondItem="itb-kd-JGo" secondAttribute="trailing" constant="8" id="Spc-n1-4dx"/> | |||
<constraint firstItem="k4u-s9-7Su" firstAttribute="trailing" secondItem="0xz-aS-XmR" secondAttribute="trailing" id="bLk-Ca-9bf"/> | |||
<constraint firstItem="k4u-s9-7Su" firstAttribute="trailing" secondItem="PGm-jo-Ko3" secondAttribute="trailing" constant="16" id="h1Q-kO-zCq"/> | |||
<constraint firstItem="itb-kd-JGo" firstAttribute="leading" secondItem="k4u-s9-7Su" secondAttribute="leading" constant="8" id="mGb-E5-W9q"/> | |||
<constraint firstItem="0xz-aS-XmR" firstAttribute="leading" secondItem="k4u-s9-7Su" secondAttribute="leading" id="qzH-Hp-bXE"/> | |||
<constraint firstItem="0xz-aS-XmR" firstAttribute="top" secondItem="k4u-s9-7Su" secondAttribute="top" constant="4" id="sdm-yW-Wjg"/> | |||
<constraint firstItem="0xz-aS-XmR" firstAttribute="centerX" secondItem="Cra-or-dGB" secondAttribute="centerX" id="zhi-rs-RXG"/> | |||
</constraints> | |||
<point key="canvasLocation" x="53" y="20"/> | |||
</view> | |||
</objects> | |||
<resources> | |||
<systemColor name="labelColor"> | |||
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
</systemColor> | |||
<systemColor name="opaqueSeparatorColor"> | |||
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
</systemColor> | |||
<systemColor name="systemBackgroundColor"> | |||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
</systemColor> | |||
</resources> | |||
</document> |
@@ -0,0 +1,21 @@ | |||
// | |||
// MeatProbeVC.h | |||
// AILinkBleSDK_Example | |||
// | |||
// Created by LarryZhang on 2023/3/7. | |||
// Copyright © 2023 zhengzida. All rights reserved. | |||
// | |||
#import <UIKit/UIKit.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@class ELAILinkPeripheral; | |||
@interface MeatProbeVC : UIViewController | |||
@property (nonatomic, strong) ELAILinkPeripheral *per; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -0,0 +1,261 @@ | |||
// | |||
// MeatProbeVC.m | |||
// AILinkBleSDK_Example | |||
// | |||
// Created by LarryZhang on 2023/3/7. | |||
// Copyright © 2023 zhengzida. All rights reserved. | |||
// | |||
#import "MeatProbeVC.h" | |||
#import <AILinkBleSDK/ELAILinkBleManager+MeatProbe.h> | |||
#import <AILinkBleSDK/ELMeatProbeBleTool.h> | |||
#import "ELDemoButton.h" | |||
@interface MeatProbeVC () <ELAILinkBleManagerDelegate> | |||
@property (weak, nonatomic) IBOutlet UILabel *bleStatusLabel; | |||
@property (weak, nonatomic) IBOutlet UITextView *logTextView; | |||
@property (nonatomic, strong) ELAILinkBleManager *bleManager; | |||
@end | |||
@implementation MeatProbeVC | |||
- (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)customDataRequestAction:(id)sender { | |||
NSData *payload = [self.bleManager meatProbeRequestCustomData]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
- (IBAction)customDataSendAction:(id)sender { | |||
NSData *payload = [self.bleManager meatProbeSendCustomData:[self coustumData]]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
- (IBAction)customDataClearAction:(id)sender { | |||
NSData *payload = [self.bleManager meatProbeSendCustomData:[NSData data]]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
- (IBAction)switchUnitAction:(id)sender { | |||
static BOOL flag = NO; | |||
if (flag) { | |||
NSData *payload = [self.bleManager meatProbeSwitchUint:0]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to C", payload]; | |||
[self addLog:log]; | |||
} else { | |||
NSData *payload = [self.bleManager meatProbeSwitchUint:1]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to F", payload]; | |||
[self addLog:log]; | |||
} | |||
flag = !flag; | |||
} | |||
- (IBAction)clearAction:(id)sender { | |||
self.logTextView.text = @""; | |||
} | |||
- (IBAction)shareLogAction:(id)sender { | |||
} | |||
- (NSData *)coustumData { | |||
//自定义数据 | |||
//*******要验证设备必须支持128字节payload | |||
Byte bytes[128]; | |||
memset(bytes, 0, sizeof(bytes)); | |||
for (int i=0; i<sizeof(bytes); i++) { | |||
bytes[i] = 0x80 + i; | |||
} | |||
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)]; | |||
NSLog(@"meatProbeAction() data:%@", data); | |||
return data; | |||
} | |||
#pragma mark - ELAILinkBleManagerDelegate | |||
- (void)initBle { | |||
self.bleManager = [[ELAILinkBleManager alloc] init]; | |||
self.bleManager.ailinkDelegate = self; | |||
} | |||
- (void)scanBle { | |||
[self.bleManager scanFilterWithCidArray:@[@(self.per.cid)]]; | |||
} | |||
- (void)connect { | |||
[self.bleManager connectAILinkPeripheral:self.per]; | |||
[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 = @"蓝牙关闭"; | |||
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.bleManager getBluetoothInfoWithELInetGetCmdType:ELInetGetCmdTypeGetBatteryState]; | |||
//获取 设置设备信息 | |||
// [self requestDeviceData]; | |||
break; | |||
case NELBleManagerConnectStateCentralScanning: | |||
self.bleStatusLabel.text = @"正在扫描..."; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
- (void)requestDeviceData { | |||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0), dispatch_get_main_queue(), ^{ | |||
//获取 设置设备信息 | |||
[self.bleManager meatProbeRequestCustomData]; | |||
}); | |||
} | |||
// 设备状态变更 | |||
- (void)managerDidUpdateState:(CBCentralManager *)central { | |||
NSLog(@"%s state:%@", __func__, @(central.state)); | |||
if (central.state == CBManagerStatePoweredOn) { | |||
[self scanBle]; | |||
} else if (central.state == CBManagerStatePoweredOff) { | |||
[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.bleManager connectAILinkPeripheral:peripheral]; | |||
} | |||
} | |||
- (void)managerDidUpdateConnect:(NELBleManagerConnectState)state { | |||
NSLog(@"%s NELBleManagerConnectState:%@", __func__, @(state)); | |||
[self updateBleStatusView:state]; | |||
} | |||
//A7数据 | |||
- (void)aiLinkBleReceiveA7Data:(NSData *)payload { | |||
NSLog(@"%s #### payload:%@", __func__, payload); | |||
ELMeatProbeBleTemperatureStruct temperatureInfo = [ELMeatProbeBleTool parseTemperature:payload]; | |||
if (temperatureInfo.createTime == 0) { | |||
return; | |||
} | |||
NSString *log = [NSString stringWithFormat:@"internalTemprature:%@%@ (%@%@) ambientTemperature:%@%@ (%@%@)", @(temperatureInfo.internalTemperature_C), @"℃", @(temperatureInfo.internalTemperature_F), @"℉", @(temperatureInfo.ambientTemperature_C), @"℃", @(temperatureInfo.ambientTemperature_F), @"℉"]; | |||
[self addLog:log]; | |||
} | |||
//A6数据 | |||
- (void)aiLinkBleReceiveA6Data:(NSData *)packet { | |||
NSLog(@"%s ##### packet:%@", __func__, packet); | |||
Byte *bytes = (Byte *)packet.bytes; | |||
Byte cmd = bytes[2]; | |||
if (cmd == ELInetGetCmdTypeGetBatteryState) { | |||
int power = self.bleManager.battery.power; | |||
ELBatteryChargingState state = self.bleManager.battery.state; | |||
NSLog(@"##### state: %lu power: %d", (unsigned long)state, power); | |||
NSString *log = [NSString stringWithFormat:@"battery state:%@ power:%@", @(state), @(power)]; | |||
[self addLog:log]; | |||
return; | |||
} | |||
if (cmd == ELInetGetCmdTypeGetBMVersion) { | |||
NSString *bmVersion = self.bleManager.bmVersion; | |||
NSLog(@"##### bmVersion: %@", bmVersion); | |||
return; | |||
} | |||
if (cmd == ELInetGetCmdTypeGetBMVersionPro) { | |||
NSString *bmVersionPro = self.bleManager.bmVersionPro; | |||
NSLog(@"##### bmVersionPro: %@", bmVersionPro); | |||
return; | |||
} | |||
if (cmd == ELInetGetCmdTypeGetBasicInfoData) { | |||
NSLog(@"##### ELInetGetCmdTypeGetBasicInfoData: %@", packet); | |||
Byte state = bytes[3]; | |||
if (state == 0x01) { | |||
NSData *custom = [packet subdataWithRange:NSMakeRange(4, 128)]; | |||
NSString *log = [NSString stringWithFormat:@"custom:%@", custom]; | |||
[self addLog:log]; | |||
} else { | |||
NSString *log = [NSString stringWithFormat:@"custom:nil"]; | |||
[self addLog:log]; | |||
} | |||
return; | |||
} | |||
} | |||
#pragma mark - addLog | |||
- (void)addLog:(NSString *)log { | |||
[self addLog:log newline:YES]; | |||
} | |||
- (void)addWithoutNewlineLog:(NSString *)log { | |||
[self addLog:log newline:NO]; | |||
} | |||
- (void)addLog:(NSString *)log newline:(BOOL)newline { | |||
NSDateFormatter *format = [[NSDateFormatter alloc] init]; | |||
format.dateFormat = @"HH:mm:ss.SSS"; | |||
NSString *time = [format stringFromDate:[NSDate date]]; | |||
if (newline) { | |||
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log]; | |||
} else { | |||
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log]; | |||
} | |||
[self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)]; | |||
} | |||
@end |
@@ -0,0 +1,145 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" 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="21679"/> | |||
<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="MeatProbeVC"> | |||
<connections> | |||
<outlet property="bleStatusLabel" destination="7bY-Ib-xgF" id="obS-2C-ezt"/> | |||
<outlet property="logTextView" destination="aWC-up-Yoe" id="Gad-hB-MrW"/> | |||
<outlet property="view" destination="thx-ue-KPs" id="ozx-bW-w8j"/> | |||
</connections> | |||
</placeholder> | |||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="thx-ue-KPs"> | |||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | |||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |||
<subviews> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LZQ-xA-pA3"> | |||
<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"/> | |||
<state key="normal" title="清空"/> | |||
<connections> | |||
<action selector="clearAction:" destination="-1" eventType="touchUpInside" id="P6P-KV-phg"/> | |||
</connections> | |||
</button> | |||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Wia-UQ-8Ld"> | |||
<rect key="frame" x="346" y="346" 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="e5W-ic-RSS"/> | |||
</connections> | |||
</button> | |||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7bY-Ib-xgF"> | |||
<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="2cp-J6-wNZ"> | |||
<rect key="frame" x="8" y="384" width="377" height="434"/> | |||
<subviews> | |||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aWC-up-Yoe"> | |||
<rect key="frame" x="0.0" y="0.0" width="377" height="434"/> | |||
<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 firstItem="aWC-up-Yoe" firstAttribute="leading" secondItem="2cp-J6-wNZ" secondAttribute="leading" id="IA2-sg-ktX"/> | |||
<constraint firstAttribute="bottom" secondItem="aWC-up-Yoe" secondAttribute="bottom" id="NbF-DQ-HWh"/> | |||
<constraint firstItem="aWC-up-Yoe" firstAttribute="top" secondItem="2cp-J6-wNZ" secondAttribute="top" id="UvG-mE-0OI"/> | |||
<constraint firstAttribute="trailing" secondItem="aWC-up-Yoe" secondAttribute="trailing" id="YF8-Au-gGR"/> | |||
</constraints> | |||
<userDefinedRuntimeAttributes> | |||
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> | |||
<real key="value" value="8"/> | |||
</userDefinedRuntimeAttribute> | |||
</userDefinedRuntimeAttributes> | |||
</view> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RjY-Pb-1ZL"> | |||
<rect key="frame" x="24" y="108" width="145.33333333333334" 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="customDataRequestAction:" destination="-1" eventType="touchUpInside" id="4HT-gK-erD"/> | |||
</connections> | |||
</button> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6wU-4y-kXJ"> | |||
<rect key="frame" x="201.33333333333337" y="108" width="146" 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="customDataSendAction:" destination="-1" eventType="touchUpInside" id="Tgn-0e-wxU"/> | |||
</connections> | |||
</button> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Vm8-VU-scN"> | |||
<rect key="frame" x="202" y="159" width="93.333333333333314" 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="Gkc-6R-oeG"/> | |||
</connections> | |||
</button> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ttt-Hs-Tk9"> | |||
<rect key="frame" x="24" y="159" width="146" 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="customDataClearAction:" destination="-1" eventType="touchUpInside" id="VKf-Yq-l4b"/> | |||
</connections> | |||
</button> | |||
</subviews> | |||
<viewLayoutGuide key="safeArea" id="5ya-cg-e71"/> | |||
<color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
<constraints> | |||
<constraint firstItem="Vm8-VU-scN" firstAttribute="top" secondItem="RjY-Pb-1ZL" secondAttribute="bottom" constant="16" id="3fa-3k-CsR"/> | |||
<constraint firstItem="7bY-Ib-xgF" firstAttribute="leading" secondItem="5ya-cg-e71" secondAttribute="leading" id="5iU-uI-ukE"/> | |||
<constraint firstItem="5ya-cg-e71" firstAttribute="trailing" secondItem="Wia-UQ-8Ld" secondAttribute="trailing" constant="16" id="GYI-zv-QJU"/> | |||
<constraint firstItem="7bY-Ib-xgF" firstAttribute="top" secondItem="5ya-cg-e71" secondAttribute="top" constant="4" id="KNZ-xZ-Kvy"/> | |||
<constraint firstItem="ttt-Hs-Tk9" firstAttribute="top" secondItem="RjY-Pb-1ZL" secondAttribute="bottom" constant="16" id="Lji-qd-u0z"/> | |||
<constraint firstItem="6wU-4y-kXJ" firstAttribute="leading" secondItem="RjY-Pb-1ZL" secondAttribute="trailing" constant="32" id="M6a-6u-DWR"/> | |||
<constraint firstItem="RjY-Pb-1ZL" firstAttribute="leading" secondItem="5ya-cg-e71" secondAttribute="leading" constant="24" id="NYf-r3-SxK"/> | |||
<constraint firstItem="2cp-J6-wNZ" firstAttribute="top" secondItem="7bY-Ib-xgF" secondAttribute="bottom" constant="300" id="P0n-PG-LvG"/> | |||
<constraint firstItem="2cp-J6-wNZ" firstAttribute="top" secondItem="Wia-UQ-8Ld" secondAttribute="bottom" constant="8" symbolic="YES" id="SWO-jj-7Mh"/> | |||
<constraint firstItem="RjY-Pb-1ZL" firstAttribute="top" secondItem="7bY-Ib-xgF" secondAttribute="bottom" constant="24" id="TRo-Mu-UVA"/> | |||
<constraint firstItem="6wU-4y-kXJ" firstAttribute="top" secondItem="7bY-Ib-xgF" secondAttribute="bottom" constant="24" id="Uqf-4D-Cl0"/> | |||
<constraint firstItem="2cp-J6-wNZ" firstAttribute="leading" secondItem="5ya-cg-e71" secondAttribute="leading" constant="8" id="Z3M-US-a9v"/> | |||
<constraint firstItem="5ya-cg-e71" firstAttribute="trailing" secondItem="2cp-J6-wNZ" secondAttribute="trailing" constant="8" id="ZSu-l2-kID"/> | |||
<constraint firstItem="Vm8-VU-scN" firstAttribute="leading" secondItem="ttt-Hs-Tk9" secondAttribute="trailing" constant="32" id="gT6-xc-gTY"/> | |||
<constraint firstItem="ttt-Hs-Tk9" firstAttribute="leading" secondItem="5ya-cg-e71" secondAttribute="leading" constant="24" id="hNT-dX-rsK"/> | |||
<constraint firstItem="LZQ-xA-pA3" firstAttribute="leading" secondItem="5ya-cg-e71" secondAttribute="leading" constant="16" id="kW2-mg-yKj"/> | |||
<constraint firstItem="5ya-cg-e71" firstAttribute="bottom" secondItem="2cp-J6-wNZ" secondAttribute="bottom" id="ms3-vm-VMQ"/> | |||
<constraint firstItem="5ya-cg-e71" firstAttribute="trailing" secondItem="7bY-Ib-xgF" secondAttribute="trailing" id="qVb-2b-hVU"/> | |||
<constraint firstItem="2cp-J6-wNZ" firstAttribute="top" secondItem="LZQ-xA-pA3" secondAttribute="bottom" constant="8" symbolic="YES" id="tY0-Fc-dAj"/> | |||
<constraint firstItem="7bY-Ib-xgF" firstAttribute="centerX" secondItem="thx-ue-KPs" secondAttribute="centerX" id="yRU-ks-IeE"/> | |||
</constraints> | |||
<point key="canvasLocation" x="53" y="20"/> | |||
</view> | |||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | |||
</objects> | |||
<resources> | |||
<systemColor name="labelColor"> | |||
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
</systemColor> | |||
<systemColor name="opaqueSeparatorColor"> | |||
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
</systemColor> | |||
<systemColor name="systemBackgroundColor"> | |||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |||
</systemColor> | |||
</resources> | |||
</document> |
@@ -0,0 +1,27 @@ | |||
// | |||
// ELAILinkBleManager+MeatProbeBox.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2023/2/16. | |||
// | |||
#import <AILinkBleSDK/ELAILinkBleManager.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@interface ELAILinkBleManager (MeatProbeBox) | |||
//APP 获取设备状态数据(Type:01) | |||
- (NSData *)meatProbeBoxGetStatus; | |||
//设置、获取设备参数数据(Type:03) | |||
- (NSData *)meatProbeBoxSendCustomData:(NSData *)customData withMac:(NSData *)macData; | |||
- (NSData *)meatProbeBoxRequestCustomDataWithMac:(NSData *)macData; | |||
- (NSData *)meatProbeBoxClearCustomDataWithMac:(NSData *)macData; | |||
//切换单位(Type:04) | |||
- (NSData *)meatProbeBoxSwitchUint:(ELDeviceTemperatureUnit)unit; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -0,0 +1,55 @@ | |||
// | |||
// ELAILinkBleManager+MeatProbeBox.m | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2023/2/16. | |||
// | |||
#import "ELAILinkBleManager+MeatProbeBox.h" | |||
#import "ELMeatProbeBoxBleHeader.h" | |||
@implementation ELAILinkBleManager (MeatProbeBox) | |||
//APP 获取设备状态数据(Type:01) | |||
- (NSData *)meatProbeBoxGetStatus { | |||
Byte bytes[] = { ELMeatProbeBoxCMDAppGetStatus_Type01, 0x01 }; | |||
NSData *payload = [[NSData alloc] initWithBytes:bytes length:sizeof(bytes)]; | |||
[self sendA7PayloadNoEncryption:payload]; | |||
return payload; | |||
} | |||
//设置、获取设备参数数据(Type:03) | |||
- (NSData *)meatProbeBoxSendCustomData:(NSData *)customData withMac:(NSData *)macData { | |||
Byte bytes[] = { ELMeatProbeBoxCMDCustomData_Type03, 0x01 }; | |||
NSMutableData *payload = [[NSMutableData alloc] initWithBytes:bytes length:sizeof(bytes)]; | |||
[payload appendData:macData]; | |||
[payload appendData:customData]; | |||
[self sendA7PayloadNoEncryption:payload.copy]; | |||
return payload.copy; | |||
} | |||
- (NSData *)meatProbeBoxRequestCustomDataWithMac:(NSData *)macData { | |||
Byte bytes[] = { ELMeatProbeBoxCMDCustomData_Type03, 0x02 }; | |||
NSMutableData *payload = [[NSMutableData alloc] initWithBytes:bytes length:sizeof(bytes)]; | |||
[payload appendData:macData]; | |||
[self sendA7PayloadNoEncryption:payload.copy]; | |||
return payload.copy; | |||
} | |||
- (NSData *)meatProbeBoxClearCustomDataWithMac:(NSData *)macData { | |||
Byte bytes[] = { ELMeatProbeBoxCMDCustomData_Type03, 0x00 }; | |||
NSMutableData *payload = [[NSMutableData alloc] initWithBytes:bytes length:sizeof(bytes)]; | |||
[payload appendData:macData]; | |||
[self sendA7PayloadNoEncryption:payload.copy]; | |||
return payload.copy; | |||
} | |||
//切换单位(Type:04) | |||
- (NSData *)meatProbeBoxSwitchUint:(ELDeviceTemperatureUnit)unit { | |||
Byte bytes[] = { ELMeatProbeBoxCMDAppSwitchUnit_Type04, unit }; | |||
NSData *payload = [[NSData alloc] initWithBytes:bytes length:sizeof(bytes)]; | |||
[self sendA7PayloadNoEncryption:payload]; | |||
return payload; | |||
} | |||
@end |
@@ -0,0 +1,23 @@ | |||
// | |||
// ELMeatProbeBoxBleHeader.h | |||
// Pods | |||
// | |||
// Created by LarryZhang on 2023/3/30. | |||
// | |||
#ifndef ELMeatProbeBoxBleHeader_h | |||
#define ELMeatProbeBoxBleHeader_h | |||
typedef NS_ENUM(UInt8, ELMeatProbeBoxCMD) { | |||
ELMeatProbeBoxCMDAppGetStatus_Type01 = 0x01, //0x01:Type:APP 获取设备状态 | |||
ELMeatProbeBoxCMDMcuUploadStatus_Type02 = 0x02, //0x02:Type:设备上报设备数据 | |||
ELMeatProbeBoxCMDCustomData_Type03 = 0x03, //0x03:Type:APP 设置、获取设备相关信息 | |||
ELMeatProbeBoxCMDAppSwitchUnit_Type04 = 0x04, //0x04:Type:切换单位 | |||
}; | |||
#endif /* ELMeatProbeBoxBleHeader_h */ |
@@ -0,0 +1,102 @@ | |||
// | |||
// ELMeatProbeBoxBleParser.h | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2023/2/16. | |||
// | |||
#import <Foundation/Foundation.h> | |||
#import <AILinkBleSDK/ELSDKHeader.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@class ELMeatProbeBoxBleBoxStatusModel, ELMeatProbeBoxBleProbeStatusModel; | |||
@protocol ELMeatProbeBoxBleParserDelegate; | |||
@interface ELMeatProbeBoxBleParser : NSObject | |||
@property (nonatomic, copy) NSString *mac; | |||
@property (nonatomic, weak) id<ELMeatProbeBoxBleParserDelegate> delegate; | |||
- (void)parseA7Payload:(NSData *)payloadData; | |||
@end | |||
@protocol ELMeatProbeBoxBleParserDelegate <NSObject> | |||
@optional | |||
//MCU 上报设备状态数据(Type:02) | |||
- (void)meatProbeBoxBleParser:(ELMeatProbeBoxBleParser *)bleParser boxStatus:(ELMeatProbeBoxBleBoxStatusModel *)boxStatus probeStatus:(ELMeatProbeBoxBleProbeStatusModel *)probeStatus offline:(BOOL)offline; | |||
//设置、获取设备参数数据(Type:03) | |||
- (void)meatProbeBoxBleParser:(ELMeatProbeBoxBleParser *)bleParser state:(BOOL)state mac:(NSString *)mac customData:(NSData *)customData; | |||
//切换单位(Type:04) | |||
- (void)meatProbeBoxBleParser:(ELMeatProbeBoxBleParser *)bleParser unit:(ELDeviceTemperatureUnit)unit; | |||
@end | |||
@interface ELMeatProbeBoxBleBoxStatusModel : 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) double timestamp; | |||
@end | |||
@interface ELMeatProbeBoxBleProbeStatusModel : NSObject | |||
//探针编号(1-M) 无法确定可连 接探针的编号,上报 0xFF | |||
@property (nonatomic, assign) UInt8 index; | |||
@property (nonatomic, assign) UInt8 dataIndex; | |||
//探针 mac 地址 | |||
@property (nonatomic, strong) NSData *macData; | |||
@property (nonatomic, copy) NSString *macString; | |||
//食物温度 | |||
@property (nonatomic, assign) NSInteger internalTemperature; | |||
@property (nonatomic, assign) ELDeviceTemperatureUnit internalTemperatureUnit; | |||
@property (nonatomic, assign) UInt16 internalTemperatureRaw; | |||
//环境温度 | |||
@property (nonatomic, assign) NSInteger ambientTemperature; | |||
@property (nonatomic, assign) 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) double timestamp; | |||
//数据无效 | |||
- (void)makeInvalid; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -0,0 +1,228 @@ | |||
// | |||
// ELMeatProbeBoxBleParser.m | |||
// AILinkBleSDK | |||
// | |||
// Created by LarryZhang on 2023/2/16. | |||
// | |||
#import "ELMeatProbeBoxBleParser.h" | |||
#import "ELMeatProbeBoxBleHeader.h" | |||
#import <objc/runtime.h> | |||
@interface ELMeatProbeBoxBleParser () | |||
@property (nonatomic, strong) ELMeatProbeBoxBleBoxStatusModel *boxStatusModel; | |||
@property (nonatomic, strong) ELMeatProbeBoxBleProbeStatusModel *probeStatusModel; | |||
@end | |||
@implementation ELMeatProbeBoxBleParser | |||
- (instancetype)init { | |||
if (self = [super init]) { | |||
_boxStatusModel = [[ELMeatProbeBoxBleBoxStatusModel alloc] init]; | |||
_probeStatusModel = [[ELMeatProbeBoxBleProbeStatusModel alloc] init]; | |||
} | |||
return self; | |||
} | |||
#pragma mark - 解析包 | |||
- (void)parseA7Payload:(NSData *)payloadData { | |||
UInt8 *bytes = (Byte *)payloadData.bytes; | |||
// UInt8 len = payloadData.length; | |||
UInt8 cmd = bytes[0]; | |||
switch (cmd) { | |||
case ELMeatProbeBoxCMDMcuUploadStatus_Type02: | |||
[self handelMcuUploadStatus:payloadData]; | |||
break; | |||
case ELMeatProbeBoxCMDCustomData_Type03: | |||
[self handelCustomData:payloadData]; | |||
break; | |||
case ELMeatProbeBoxCMDAppSwitchUnit_Type04: | |||
[self handelSwitchUnit:payloadData]; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
- (void)handelCustomData:(NSData *)payloadData { | |||
UInt8 *bytes = (Byte *)payloadData.bytes; | |||
UInt8 len = payloadData.length; | |||
BOOL state = bytes[1]; | |||
NSData *mac = [payloadData subdataWithRange:NSMakeRange(2, 6)]; | |||
Byte *bytesMac = (Byte *)mac.bytes; | |||
NSString *macString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytesMac[5], bytesMac[4], bytesMac[3], bytesMac[2], bytesMac[1], bytesMac[0]]; | |||
NSData *customData = [payloadData subdataWithRange:NSMakeRange(8, len - 8)]; | |||
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxBleParser:state:mac:customData:)]) { | |||
[self.delegate meatProbeBoxBleParser:self state:state mac:macString customData:customData]; | |||
} | |||
} | |||
- (void)handelSwitchUnit:(NSData *)payloadData { | |||
UInt8 *bytes = (Byte *)payloadData.bytes; | |||
//UInt8 len = payloadData.length; | |||
ELDeviceTemperatureUnit unit = bytes[1]; | |||
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxBleParser:unit:)]) { | |||
[self.delegate meatProbeBoxBleParser:self unit:unit]; | |||
} | |||
} | |||
- (void)handelMcuUploadStatus:(NSData *)payloadData { | |||
UInt8 *bytes = (Byte *)payloadData.bytes; | |||
UInt8 len = payloadData.length; | |||
_boxStatusModel.version = bytes[1]; | |||
UInt8 probeMaxCount = bytes[2]; | |||
if (probeMaxCount < 1) { | |||
return;//探针数排错 | |||
} | |||
_boxStatusModel.probeMaxCount = probeMaxCount; | |||
UInt8 probeCount = bytes[3]; | |||
if (probeCount > probeMaxCount) { | |||
return;//探针数排错 | |||
} | |||
if (len < 1 + 8 + 16 * probeCount) { | |||
return;//数据长度排错 | |||
} | |||
_boxStatusModel.probeCount = probeCount; | |||
_boxStatusModel.batteryStatus = bytes[4] >> 7; | |||
_boxStatusModel.batteryPercent = bytes[4] & 0x7F; | |||
_boxStatusModel.unit = bytes[5]; | |||
UInt8 reserved = bytes[6]; | |||
reserved = bytes[7]; | |||
reserved = bytes[8]; | |||
double timestamp = [[NSDate date] timeIntervalSince1970]; | |||
_boxStatusModel.timestamp = timestamp; | |||
// for (int i=0, j=9+16*i; i<probeCount; i++) { | |||
for (int i=0, j=9+16*i; i<probeMaxCount; i++) { | |||
_probeStatusModel.index = bytes[j+0]; | |||
_probeStatusModel.dataIndex = i; | |||
_probeStatusModel.macData = [payloadData subdataWithRange:NSMakeRange(j+1, 6)]; | |||
_probeStatusModel.macString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", bytes[j+6], bytes[j+5], bytes[j+4], bytes[j+3], bytes[j+2], bytes[j+1]]; | |||
if ([_probeStatusModel.macString isEqualToString:@"00:00:00:00:00:00"] || [_probeStatusModel.macString isEqualToString:@"FF:FF:FF:FF:FF:FF"]) { | |||
continue; | |||
} | |||
if (i >= probeCount) { | |||
[_probeStatusModel makeInvalid]; | |||
_probeStatusModel.timestamp = timestamp; | |||
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxBleParser:boxStatus:probeStatus:offline:)]) { | |||
[self.delegate meatProbeBoxBleParser:self boxStatus:_boxStatusModel probeStatus:_probeStatusModel offline:YES]; | |||
} | |||
continue; | |||
} | |||
UInt16 temp = (bytes[j+7] << 8) | bytes[j+8]; | |||
_probeStatusModel.internalTemperatureRaw = temp; | |||
_probeStatusModel.internalTemperatureUnit = (temp & 0x8000) ? ELDeviceTemperatureUnit_F : ELDeviceTemperatureUnit_C; | |||
_probeStatusModel.internalTemperature = (temp & 0x4000) ? -(temp & 0x3FFF) : (temp & 0x3FFF); | |||
temp = (bytes[j+9] << 8) | bytes[j+10]; | |||
_probeStatusModel.ambientTemperatureRaw = temp; | |||
_probeStatusModel.ambientTemperatureUnit = (temp & 0x8000) ? ELDeviceTemperatureUnit_F : ELDeviceTemperatureUnit_C; | |||
_probeStatusModel.ambientTemperature = (temp & 0x4000) ? -(temp & 0x3FFF) : (temp & 0x3FFF); | |||
#if DEBUG | |||
// _probeStatusModel.ambientTemperature += 40; | |||
#endif | |||
UInt8 battery = bytes[j+11]; | |||
_probeStatusModel.batteryRaw = battery; | |||
_probeStatusModel.batteryStatus = battery >> 7; | |||
_probeStatusModel.batteryPercent = battery & 0x7F; | |||
_probeStatusModel.insertStatusRaw = bytes[j+12]; | |||
_probeStatusModel.timestamp = timestamp; | |||
if (self.delegate && [self.delegate respondsToSelector:@selector(meatProbeBoxBleParser:boxStatus:probeStatus:offline:)]) { | |||
[self.delegate meatProbeBoxBleParser:self boxStatus:_boxStatusModel probeStatus:_probeStatusModel offline:NO]; | |||
} | |||
} | |||
} | |||
@end | |||
@implementation ELMeatProbeBoxBleBoxStatusModel | |||
#pragma mark ---- description | |||
- (NSString *)description { | |||
NSMutableString *desc = [NSStringFromClass([self class]) stringByAppendingString:@": {\n"].mutableCopy; | |||
unsigned int count; | |||
Ivar *ivar = class_copyIvarList([self class], &count); | |||
for (int i = 0 ; i < count ; i++) { | |||
Ivar iv = ivar[i]; | |||
const char *name = ivar_getName(iv); | |||
NSString *strName = [NSString stringWithUTF8String:name]; | |||
//利用KVC取值 | |||
id value = [self valueForKey:strName]; | |||
[desc appendString:[NSString stringWithFormat:@" %@: %@,\n", strName, value]]; | |||
} | |||
free(ivar); | |||
[desc appendString:@"}"]; | |||
return desc; | |||
} | |||
- (NSString *)debugDescription { | |||
return [self description]; | |||
} | |||
@end | |||
@implementation ELMeatProbeBoxBleProbeStatusModel | |||
//数据无效 | |||
- (void)makeInvalid { | |||
self.internalTemperatureRaw = 0xFFFF; | |||
self.internalTemperatureUnit = 0; | |||
self.internalTemperature = 0; | |||
self.ambientTemperatureRaw = 0xFFFF; | |||
self.ambientTemperatureUnit = 0; | |||
self.ambientTemperature = 0; | |||
self.batteryRaw = 0xFF; | |||
self.batteryStatus = 0; | |||
self.batteryPercent = 0; | |||
self.insertStatusRaw = 0; | |||
} | |||
#pragma mark ---- description | |||
- (NSString *)description { | |||
NSMutableString *desc = [NSStringFromClass([self class]) stringByAppendingString:@": {\n"].mutableCopy; | |||
unsigned int count; | |||
Ivar *ivar = class_copyIvarList([self class], &count); | |||
for (int i = 0 ; i < count ; i++) { | |||
Ivar iv = ivar[i]; | |||
const char *name = ivar_getName(iv); | |||
NSString *strName = [NSString stringWithUTF8String:name]; | |||
//利用KVC取值 | |||
id value = [self valueForKey:strName]; | |||
[desc appendString:[NSString stringWithFormat:@" %@: %@,\n", strName, value]]; | |||
} | |||
free(ivar); | |||
[desc appendString:@"}"]; | |||
return desc; | |||
} | |||
- (NSString *)debugDescription { | |||
return [self description]; | |||
} | |||
@end | |||
@@ -0,0 +1,21 @@ | |||
// | |||
// MeatProbeBoxVC.h | |||
// AILinkBleSDK_Example | |||
// | |||
// Created by LarryZhang on 2023/3/7. | |||
// Copyright © 2023 zhengzida. All rights reserved. | |||
// | |||
#import <UIKit/UIKit.h> | |||
NS_ASSUME_NONNULL_BEGIN | |||
@class ELAILinkPeripheral; | |||
@interface MeatProbeBoxVC : UIViewController | |||
@property (nonatomic, strong) ELAILinkPeripheral *per; | |||
@end | |||
NS_ASSUME_NONNULL_END |
@@ -0,0 +1,292 @@ | |||
// | |||
// MeatProbeBoxVC.m | |||
// AILinkBleSDK_Example | |||
// | |||
// Created by LarryZhang on 2023/3/7. | |||
// Copyright © 2023 zhengzida. All rights reserved. | |||
// | |||
#import "MeatProbeBoxVC.h" | |||
#import <AILinkBleSDK/ELAILinkBleManager.h> | |||
#import <AILinkBleSDK/NSData+AILinkBle.h> | |||
#import "ELAILinkBleManager+MeatProbeBox.h" | |||
#import "ELMeatProbeBoxBleParser.h" | |||
@interface MeatProbeBoxVC () <ELAILinkBleManagerDelegate, ELMeatProbeBoxBleParserDelegate> | |||
@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) ELMeatProbeBoxBleParser *bleParser; | |||
@property (nonatomic, strong) NSMutableSet *probeList; | |||
@property (nonatomic, strong) ELMeatProbeBoxBleBoxStatusModel *boxStatus; | |||
@end | |||
@implementation MeatProbeBoxVC | |||
- (void)viewDidLoad { | |||
[super viewDidLoad]; | |||
// Do any additional setup after loading the view from its nib. | |||
self.probeList = [NSMutableSet set]; | |||
[self initBle]; | |||
} | |||
- (void)dealloc { | |||
[self deinitBle]; | |||
} | |||
#pragma mark - Action | |||
- (IBAction)customDataRequestAction:(id)sender { | |||
for (NSData *mac in self.probeList) { | |||
NSData *payload = [self.bleManager meatProbeBoxRequestCustomDataWithMac:mac]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
} | |||
- (IBAction)customDataSendAction:(id)sender { | |||
for (NSData *mac in self.probeList) { | |||
NSData *payload = [self.bleManager meatProbeBoxSendCustomData:[self coustumData] withMac:mac]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
} | |||
- (IBAction)customDataClearAction:(id)sender { | |||
for (NSData *mac in self.probeList) { | |||
NSData *payload = [self.bleManager meatProbeBoxClearCustomDataWithMac:mac]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
} | |||
- (IBAction)switchUnitAction:(id)sender { | |||
if (self.boxStatus.unit == 1) { | |||
NSData *payload = [self.bleManager meatProbeBoxSwitchUint:0]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to C", payload]; | |||
[self addLog:log]; | |||
} else { | |||
NSData *payload = [self.bleManager meatProbeBoxSwitchUint:1]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@ -- 切换单位 to F", payload]; | |||
[self addLog:log]; | |||
} | |||
} | |||
- (IBAction)clearAction:(id)sender { | |||
self.logTextView.text = @""; | |||
} | |||
- (IBAction)shareLogAction:(id)sender { | |||
} | |||
- (NSData *)coustumData { | |||
//自定义数据 | |||
//*******要验证设备必须支持128字节payload | |||
Byte bytes[128]; | |||
memset(bytes, 0, sizeof(bytes)); | |||
for (int i=0; i<sizeof(bytes); i++) { | |||
bytes[i] = 0x80 + i; | |||
} | |||
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)]; | |||
NSLog(@"meatProbeAction() data:%@", data); | |||
return data; | |||
} | |||
#pragma mark - ELAILinkBleManagerDelegate | |||
- (void)initBle { | |||
self.bleManager = [[ELAILinkBleManager alloc] init]; | |||
self.bleManager.ailinkDelegate = self; | |||
self.bleParser = [ELMeatProbeBoxBleParser new]; | |||
self.bleParser.delegate = self; | |||
} | |||
- (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]; | |||
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(), ^{ | |||
// //APP获取设备功能列表 | |||
// [self.bleManager airDetectorRequestDeviceFunctions]; | |||
// //APP获取设备状态 //0x03 CMD:获取设备状态 | |||
// [self.bleManager airDetectorRequestDeviceStatus]; | |||
}); | |||
} | |||
// 设备状态变更 | |||
- (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 == ELInetGetCmdTypeGetBatteryState) { | |||
int power = self.bleManager.battery.power; | |||
ELBatteryChargingState state = self.bleManager.battery.state; | |||
NSLog(@"##### state: %lu power: %d", (unsigned long)state, power); | |||
} | |||
if (cmd == ELInetGetCmdTypeGetBMVersion) { | |||
NSString *bmVersion = self.bleManager.bmVersion; | |||
NSLog(@"##### bmVersion: %@", bmVersion); | |||
} | |||
if (cmd == ELInetGetCmdTypeGetBMVersionPro) { | |||
NSString *bmVersionPro = self.bleManager.bmVersionPro; | |||
NSLog(@"##### bmVersionPro: %@", bmVersionPro); | |||
} | |||
} | |||
#pragma mark - ELMeatProbeBoxBleParserDelegate | |||
//MCU 上报设备状态数据(Type:02) | |||
- (void)meatProbeBoxBleParser:(ELMeatProbeBoxBleParser *)bleParser boxStatus:(ELMeatProbeBoxBleBoxStatusModel *)boxStatus probeStatus:(ELMeatProbeBoxBleProbeStatusModel *)probeStatus offline:(BOOL)offline { | |||
NSLog(@"##### boxStatus(): %@ probeStatus(): %@", boxStatus, probeStatus); | |||
self.boxStatus = boxStatus; | |||
NSString *log = [NSString stringWithFormat:@"boxStatus: { probeMaxCount:%hhu, probeCount:%hhu, batteryStatus:%hhu batteryPercent:%hhu, unit:%ld }", boxStatus.probeMaxCount, boxStatus.probeCount, boxStatus.batteryStatus, boxStatus.batteryPercent, boxStatus.unit]; | |||
[self addLog:log]; | |||
NSString *log2 = [NSString stringWithFormat:@"probeStatus: { index:%hhu, macString:%@, internalTemperature:%ld, ambientTemperature:%ld, batteryPercent:%hhu }", probeStatus.index, probeStatus.macString, probeStatus.internalTemperature, probeStatus.ambientTemperature, probeStatus.batteryPercent]; | |||
[self addLog:log2]; | |||
if (![self.probeList containsObject:probeStatus.macData]) { | |||
[self.probeList addObject:probeStatus.macData]; | |||
NSData *payload = [self.bleManager meatProbeBoxRequestCustomDataWithMac:probeStatus.macData]; | |||
NSString *log = [NSString stringWithFormat:@"发: payload: %@", payload]; | |||
[self addLog:log]; | |||
} | |||
} | |||
//设置、获取设备参数数据(Type:03) | |||
- (void)meatProbeBoxBleParser:(ELMeatProbeBoxBleParser *)bleParser state:(BOOL)state mac:(NSData *)mac customData:(NSData *)customData { | |||
NSLog(@"##### state:%d mac:%@ customData():%@", state, mac, customData); | |||
NSLog(@""); | |||
} | |||
//切换单位(Type:04) | |||
- (void)meatProbeBoxBleParser:(ELMeatProbeBoxBleParser *)bleParser unit:(ELDeviceTemperatureUnit)unit { | |||
NSLog(@"##### unit(): %@", @(unit)); | |||
NSLog(@""); | |||
} | |||
#pragma mark - addLog | |||
- (void)addLog:(NSString *)log { | |||
[self addLog:log newline:YES]; | |||
} | |||
- (void)addWithoutNewlineLog:(NSString *)log { | |||
[self addLog:log newline:NO]; | |||
} | |||
- (void)addLog:(NSString *)log newline:(BOOL)newline { | |||
NSDateFormatter *format = [[NSDateFormatter alloc] init]; | |||
format.dateFormat = @"HH:mm:ss.SSS"; | |||
NSString *time = [format stringFromDate:[NSDate date]]; | |||
if (newline) { | |||
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@\n", time, log]; | |||
} else { | |||
self.logTextView.text = [self.logTextView.text stringByAppendingFormat:@"%@ %@", time, log]; | |||
} | |||
[self.logTextView scrollRangeToVisible:NSMakeRange(self.logTextView.text.length, 1)]; | |||
} | |||
@end |
@@ -0,0 +1,145 @@ | |||
<?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"> | |||
<device id="retina6_12" orientation="portrait" appearance="light"/> | |||
<dependencies> | |||
<deployment identifier="iOS"/> | |||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/> | |||
<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="MeatProbeBoxVC"> | |||
<connections> | |||
<outlet property="bleStatusLabel" destination="Rgz-Ge-utp" id="h2s-vl-sLn"/> | |||
<outlet property="logTextView" destination="Txf-5E-hsN" id="Azk-gu-Gls"/> | |||
<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> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K5I-eH-HGA"> | |||
<rect key="frame" x="16" y="346" width="31" height="30"/> | |||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> | |||
<state key="normal" title="清空"/> | |||
<connections> | |||
<action selector="clearAction:" destination="-1" eventType="touchUpInside" id="o2p-MO-8pL"/> | |||
</connections> | |||
</button> | |||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lwE-Xr-Dac"> | |||
<rect key="frame" x="346" y="346" 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="aLe-00-nz6"/> | |||
</connections> | |||
</button> | |||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stauts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rgz-Ge-utp"> | |||
<rect key="frame" x="0.0" y="63" width="393" height="21"/> | |||
<fontDescription key="fontDescription" type="system" pointSize="17"/> | |||
<nil key="textColor"/> | |||
<nil key="highlightedColor"/> | |||
</label> | |||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="I1h-EM-UuI"> | |||
<rect key="frame" x="8" y="384" width="377" height="434"/> | |||
<subviews> | |||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" indicatorStyle="white" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Txf-5E-hsN"> | |||
<rect key="frame" x="0.0" y="0.0" width="377" height="434"/> | |||
<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="Txf-5E-hsN" secondAttribute="bottom" id="CLf-PD-bjv"/> | |||
<constraint firstItem="Txf-5E-hsN" firstAttribute="top" secondItem="I1h-EM-UuI" secondAttribute="top" id="RHZ-uy-ZqR"/> | |||
<constraint firstItem="Txf-5E-hsN" firstAttribute="leading" secondItem="I1h-EM-UuI" secondAttribute="leading" id="vQ7-WS-tPk"/> | |||
<constraint firstAttribute="trailing" secondItem="Txf-5E-hsN" secondAttribute="trailing" id="wsr-Tq-aYp"/> | |||
</constraints> | |||
<userDefinedRuntimeAttributes> | |||
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> | |||
<real key="value" value="8"/> | |||
</userDefinedRuntimeAttribute> | |||
</userDefinedRuntimeAttributes> | |||
</view> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="U2W-tX-U1Z"> | |||
<rect key="frame" x="24" y="108" width="145.33333333333334" 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="customDataRequestAction:" destination="-1" eventType="touchUpInside" id="EbK-Xt-NEO"/> | |||
</connections> | |||
</button> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MNm-EU-G2q"> | |||
<rect key="frame" x="201.33333333333337" y="108" width="146" 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="customDataSendAction:" destination="-1" eventType="touchUpInside" id="Bgd-AX-0sf"/> | |||
</connections> | |||
</button> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AXK-mu-b6T"> | |||
<rect key="frame" x="202" y="159" width="93.333333333333314" 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="qmW-aP-fla"/> | |||
</connections> | |||
</button> | |||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7tV-LN-Ahy"> | |||
<rect key="frame" x="24" y="159" width="146" 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="customDataClearAction:" destination="-1" eventType="touchUpInside" id="ZWX-NO-7e6"/> | |||
</connections> | |||
</button> | |||
</subviews> | |||
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/> | |||
<color key="backgroundColor" systemColor="systemBackgroundColor"/> | |||
<constraints> | |||
<constraint firstItem="Rgz-Ge-utp" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" id="26n-ac-URx"/> | |||
<constraint firstItem="K5I-eH-HGA" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="16" id="43e-FM-L00"/> | |||
<constraint firstItem="U2W-tX-U1Z" firstAttribute="top" secondItem="Rgz-Ge-utp" secondAttribute="bottom" constant="24" id="76S-le-4CY"/> | |||
<constraint firstItem="Rgz-Ge-utp" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="Cuz-sL-8PR"/> | |||
<constraint firstItem="7tV-LN-Ahy" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="24" id="DGY-hw-cQW"/> | |||
<constraint firstItem="Q5M-cg-NOt" firstAttribute="bottom" secondItem="I1h-EM-UuI" secondAttribute="bottom" id="Dfj-32-bLS"/> | |||
<constraint firstItem="I1h-EM-UuI" firstAttribute="top" secondItem="K5I-eH-HGA" secondAttribute="bottom" constant="8" symbolic="YES" id="EN4-TU-LIY"/> | |||
<constraint firstItem="Rgz-Ge-utp" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" constant="4" id="NV7-q2-a68"/> | |||
<constraint firstItem="AXK-mu-b6T" firstAttribute="top" secondItem="U2W-tX-U1Z" secondAttribute="bottom" constant="16" id="Nnq-dz-fTJ"/> | |||
<constraint firstItem="7tV-LN-Ahy" firstAttribute="top" secondItem="U2W-tX-U1Z" secondAttribute="bottom" constant="16" id="RA0-gL-Ljv"/> | |||
<constraint firstItem="AXK-mu-b6T" firstAttribute="leading" secondItem="7tV-LN-Ahy" secondAttribute="trailing" constant="32" id="RAL-eu-lGT"/> | |||
<constraint firstItem="MNm-EU-G2q" firstAttribute="leading" secondItem="U2W-tX-U1Z" secondAttribute="trailing" constant="32" id="Su5-Ls-bvw"/> | |||
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="I1h-EM-UuI" secondAttribute="trailing" constant="8" id="Ulp-Zv-w68"/> | |||
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="lwE-Xr-Dac" secondAttribute="trailing" constant="16" id="aZt-4V-Vkm"/> | |||
<constraint firstItem="I1h-EM-UuI" firstAttribute="top" secondItem="Rgz-Ge-utp" secondAttribute="bottom" constant="300" id="gWj-B6-gld"/> | |||
<constraint firstItem="I1h-EM-UuI" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="8" id="mGL-Xz-QuQ"/> | |||
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="Rgz-Ge-utp" secondAttribute="trailing" id="myT-ol-sQQ"/> | |||
<constraint firstItem="U2W-tX-U1Z" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="24" id="onE-X9-raT"/> | |||
<constraint firstItem="MNm-EU-G2q" firstAttribute="top" secondItem="Rgz-Ge-utp" secondAttribute="bottom" constant="24" id="vx8-hV-lTa"/> | |||
<constraint firstItem="I1h-EM-UuI" firstAttribute="top" secondItem="lwE-Xr-Dac" secondAttribute="bottom" constant="8" symbolic="YES" id="zvN-yN-K2g"/> | |||
</constraints> | |||
<point key="canvasLocation" x="53" y="20"/> | |||
</view> | |||
</objects> | |||
<resources> | |||
<systemColor name="labelColor"> | |||
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |||
</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> |
@@ -20,6 +20,7 @@ | |||
#import "BabyScaleViewController.h" | |||
#import "BloodScanViewController.h" | |||
#import "HeightGuageScanViewController.h" | |||
#import "BroadcastHeightGuage.h" | |||
#import "RemoteControlScanViewController.h" | |||
#import "ForeheadScanViewController.h" | |||
#import "ThermometerScanViewController.h" | |||
@@ -42,6 +43,8 @@ | |||
#import "HeightBodyFatScaleVC.h" | |||
#import "MeatProbeVC.h" | |||
#import "MeatProbeBoxVC.h" | |||
@@ -69,6 +72,9 @@ | |||
[ELDemoDeviceModel modelWithIndex:@"04" imageName:@"type_baby_scale_ic" title:@"Baby Scale" subtitle:@"婴儿秤" entryVCName:NSStringFromClass(BabyScaleViewController.class)], | |||
[ELDemoDeviceModel modelWithIndex:@"05" imageName:@"type_sphygmomanometer_ic" title:@"Sphygmomanometer" subtitle:@"血压计" entryVCName:NSStringFromClass(BloodScanViewController.class)], | |||
[ELDemoDeviceModel modelWithIndex:@"06" imageName:@"type_height_measuring_instrument" title:@"Height Guage" subtitle:@"身高仪" entryVCName:NSStringFromClass(HeightGuageScanViewController.class)], | |||
[ELDemoDeviceModel modelWithIndex:@"06B" imageName:@"type_height_measuring_instrument" title:@"Broadcast Height Guage" subtitle:@"广播身高仪" entryVCName:NSStringFromClass(BroadcastHeightGuage.class)], | |||
[ELDemoDeviceModel modelWithIndex:@"07" imageName:@"type_control_vision_ic" title:@"Remote Control" subtitle:@"遥控器" entryVCName:NSStringFromClass(RemoteControlScanViewController.class)], | |||
[ELDemoDeviceModel modelWithIndex:@"08" imageName:@"type_temperature_gun_ic" title:@"Forehead thermometer" subtitle:@"额温枪" entryVCName:NSStringFromClass(ForeheadScanViewController.class)], | |||
[ELDemoDeviceModel modelWithIndex:@"09" imageName:@"type_thermometer_ic" title:@"Digital thermometer" subtitle:@"温度计" entryVCName:NSStringFromClass(ThermometerScanViewController.class)], | |||
@@ -93,6 +99,9 @@ | |||
[ELDemoDeviceModel modelWithIndex:@"25" imageName:@"height_body_fat_scale_type_ic" title:@"HeightBodyFatScale" subtitle:@"身高体脂秤" entryVCName:NSStringFromClass(HeightBodyFatScaleVC.class) cids:@[@(ELSupportDeviceTypeHeightBodyFatScale)]], | |||
[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)]], | |||
]; | |||
} | |||
@@ -10,4 +10,5 @@ | |||
| 1.5.9| 20230206 | larry | 增加 身高体脂秤,优化 Demo UI | | |||
| 1.5.11 | 20230327 | larry | 增加 蓝牙体重秤 | | |||
| 1.5.15 | 20230427 | larry | 修改 增加 蓝牙牙刷防飞溅 | | |||
| 1.6.1 | 20230626 | larry | 修改 增加 广播身高仪 食物探针 食物探针盒子 支持 | | |||