更新时间:2023-06-21 05:33:28下载pdf
涂鸦cube App SDK iOS 版提供了即全面又灵活的 IoT App 开发模式。您可以通过本教程在两小时内快速开发一款自己的 IoT App,并实现如下功能:
您可以点击下方按钮下载 Sample 查看本教程中的示例代码。本次教程按功能模块进行分类,您可以快速找到对应的代码参考学习。
学习完本教程,并且结合一定的面板开发后,您可以创建一个类似以下 iOS App 的 Demo。
在您开始本教程前,请先确保您已经 :
本模块中,仅演示使用手机号注册登录。除此以外,涂鸦cube App SDK 还提供了邮箱、第三方、匿名等多种注册登录方式。详情请参考 用户账户系统。
注册用户账号时:
countryCode
参数来区分注册地区,用于就近选择涂鸦 IoT 开发平台的可用区。如中国大陆为 86
,美国为 1
。有关可用区相关概念,请参考 云服务介绍。各个可用区的数据是相互独立的,因此在 中国大陆(86) 注册的账号,在 美国(1) 地区无法使用(提示用户不存在)。
ThingSmartUser
。它是一个单例,存储了当前用户的所有信息及相关的登录注册方法。详情请参考 用户数据模型。为了加强用户信息的数据安全,涂鸦优化验证码和添加了账号限制。只有验证码服务可用的地区,才可以发送验证码。您需要先查询您的开发者账号是否已经在从涂鸦 IoT 开发平台开通验证码服务的可用地区列表。
[[ThingSmartUser sharedInstance] getWhiteListWhoCanSendMobileCodeSuccess:^(NSString *regions) {
} failure:^(NSError *error) {
}];
返回值 regions
表示一个或多个国家或地区,以 ,
隔开的字符串,例如 86
和 01
。可用区编号列表,请参考 涂鸦云服务介绍。
目前中国大陆默认已开通验证码服务。若您希望自己的应用发布在其他国家或地区,则必须验证开通地区,并联系您的涂鸦客户经理或 提交工单 开通该服务。
与大部分注册流程类似,用户必须先获取验证码。无论是手机或是邮箱,您都可以使用统一的获取验证码接口在注册及后续密码修改,验证码登录,信息完善等操作中获取相应的验证码。
NSString * region = [[ThingSmartUser sharedInstance] getDefaultRegionWithCountryCode:countryCode];
[[ThingSmartUser sharedInstance] sendVerifyCodeWithUserName:userName // phone number or email address
region:region
countryCode:countryCode // NSString ,like 86 or 01
type:1 // code type , 1: verification code register,
success:^{
// request success
} failure:^(NSError *error) {
// request fail
// get error details from error.localizedDescription
}];
请确保所传参数 type
为 1
,否则将无法正常注册。
使用手机号码注册账号需要上传国家码、手机号码、密码、获取到的验证码等信息。详情请参考 手机号注册。
[[ThingSmartUser sharedInstance] registerByPhone:countryCode //country code ,like 86 or 1
phoneNumber:phone
password:password
code:code // VerifyCode
success:^{
// register success
} failure:^(NSError *error) {
// register fail
// get error details from error.localizedDescription
}
];
账号注册成功后,用户就可以使用手机号码登录账号。详情请参考 手机号登录。
[[ThingSmartUser sharedInstance] loginByPhone:countryCode
phoneNumber:phone
password:password
success:^{
// login successfully
} failure:^(NSError *error) {
// login fail
}];
家庭是cube App SDK 开发下实际场景的最大单位。IoT 设备的添加、编辑、移除、状态变化的监听基于家庭下。用户可以在用户账号下创建任意多个家庭。在指定家庭下,用户还可以添加并管理多个房间和家庭成员。
操作家庭模块时,您将频繁调用对象 ThingSmartHomeModel
和 ThingSmartHome
。
对象 | 说明 |
---|---|
ThingSmartHomeModel | 存放了家庭的基本信息,如 ID、名字、位置等。 |
ThingSmartHome | 存放了家庭相关的所有功能,如单个家庭信息管理、家庭下的家庭成员管理、房间管理等。ThingSmartHome 需要使用正确的 homeId 进行初始化。 |
请确保 ViewController
或其他对象中持有的 ThingSmartHome
为全局变量(@property)。临时的 ThingSmartHome
变量可能会因为作用域问题,在初始化时被提前释放,而返回 nil
。
在登录状态下,用户可以创建家庭。之后对于房间、成员、设备等对象的管理都基于家庭下。创建家庭需要通过 ThingSmartHomeManager
调用 创建家庭 接口。
[self.homeManager addHomeWithName:name
geoName:city
rooms:@[@""] // we could add rooms after creating the home , so pass a null list firstly .
latitude:self.latitude
longitude:self.longitude
success:^(long long result) {
//add success, result here is the homeID .
} failure:^(NSError *error) {
//add failed
}];
在登录状态下您可以直接获取家庭列表。如还没创建过家庭,将返回 空数组。
//get home list and refresh the TableView
[self.homeManager getHomeListWithSuccess:^(NSArray<ThingSmartHomeModel *> *homes) {
// get success , refresh UI
// [self.tableView reloadData];
} failure:^(NSError *error) {
// get failed
}];
在创建了家庭后,后续房间成员、用户配网等相关操作均需要基于某一特定家庭。因此建议您将这一特定家庭作为 App 的全局变量存储。当然,您也可以在本地随时切换当前家庭,涂鸦 IoT 开发平台并不会记录这一信息。在本教程和配套的 Sample 中,默认将列表的第一个家庭设为当前家庭。
+ (ThingSmartHomeModel *)getCurrentHome {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults valueForKey:@"CurrentHome"]) {
return nil;
}
long long homeId = [[defaults valueForKey:@"CurrentHome"] longLongValue];
if (![ThingSmartHome homeWithHomeId:homeId]) {
return nil;
}
return [ThingSmartHome homeWithHomeId:homeId].homeModel;
}
+ (void)setCurrentHome:(ThingSmartHomeModel *)homeModel {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:[NSString stringWithFormat:@"%lld", homeModel.homeId] forKey:@"CurrentHome"];
}
实现以上逻辑后,家庭的操作就可以被简化:
查询家庭列表:
self.home = [ThingSmartHome homeWithHomeId:[ThingHome getCurrentHome].homeId];
设置家庭 ID:
[ThingHome setCurrentHome:xxx];
简单来说,配网就是将设备连接并注册到云端,使其拥有与云端远程通信的能力。涂鸦cube App SDK 提供了丰富的配网方式以支持大部分智能设备。如 Wi-Fi 连接,蓝牙连接等。详情请参考 设备配网 和 蓝牙体系。
本模块以 Wi-Fi 配网为例介绍如何使用SDK 将设备配置到云端。
Wi-Fi 配网方式包括 EZ、AP、扫 App 二维码三种方式。在之后的 iOS 版本 SDK 中,推荐使用 AP 热点模式 代替 Wi-Fi 快连模式(即EZ模式)。主要原因如下:
开始配网之前,SDK 需要在联网状态下从涂鸦 获取配网 Token,然后才可以开始热点模式配网。Token 的有效期为 10 分钟,且配置成功后就会失效,再次配网需要重新获取。获取 Token 需要上传当前的 homeId
,因此您需要确保处于登录状态并至少创建了一个家庭。
[[ThingSmartActivator sharedInstance] getTokenWithHomeId:homeId
success:^(NSString *token) {
// NSLog(@"getToken success: %@", token);
// you could start ConfigWiFi now
} failure:^(NSError *error) {
//NSLog(@"getToken failure: %@", error.localizedDescription);
}
];
iOS 14 版本适配
从 iOS 14 版本开始,在设备配网、局域网本地控制时会触发 本地网络 权限弹窗。
目前苹果没有提供任何 API 对此权限进行判断,建议您在相关功能无法正常使用时提示、引导用户检查 系统设置 中的 app设置,确认是否开启了 本地网络 权限。
iOS 13 版本适配
从 iOS 13 版本开始,如果用户没有开启地理位置权限,在已开启 Wi-Fi 权限的前提下,[[ThingSmartActivator sharedInstance] currentWifiSSID]
将获取不到有效的 Wi-Fi SSID 或 BSSID。在此情况下,iOS 会返回下列默认值:
开始配网前,请确保设备处于待配网状态。操作方法可参考设备的使用说明书。
调用 配网接口,需要提供路由器的 SSID(即 Wi-Fi 名称)、密码、从云端获取的 Token 等。
[ThingSmartActivator sharedInstance].delegate = self;
[[ThingSmartActivator sharedInstance] startConfigWiFi:ThingActivatorModeAP
ssid:ssid
password:password
token:token
timeout:100];
timeout
单位为秒,默认为 100
,您可以设置为任意值。但不建议将此值设置得过小,否则将影响配网结果。
使用 AP 模式配网时,您需要实现 ThingSmartActivatorDelegate
协议,以监听配网结果的回调。
@interface xxxViewController () <ThingSmartActivatorDelegate>
- (void)activator:(ThingSmartActivator *)activator didReceiveDevice:(ThingSmartDeviceModel *)deviceModel error:(NSError *)error {
if (deviceModel && error == nil) {
//success
// NSLog(@"connected success: %@", deviceModel.name);
}
if (error) {
//fail
}
// stop config
}
开始配网操作后,App 会持续广播配网信息,直到配网成功或是超时才停止。如果需要中途取消操作或配网完成,需要调用 停止配网 接口。
[ThingSmartActivator sharedInstance].delegate = nil;
[[ThingSmartActivator sharedInstance] stopConfigWiFi];
本章节主要操作对象包含 ThingSmartDeviceModel
和 ThingSmartDevice
。
对象 | 说明 | 参考链接 |
---|---|---|
ThingSmartDeviceModel |
|
设备功能点 |
ThingSmartDevice | ThingSmartDevice 存放了设备相关的所有功能,如功能控制,设备固件管理等。您需要用正确的 deviceId 初始化一个 ThingSmartDevice 。 |
设备管理 |
请确保 ViewController
或其他对象中持有的 ThingSmartDevice
为全局变量(@property)。临时的 ThingSmartDevice
变量可能会因为作用域问题,在初始化时被提前释放,而返回 nil
。
设备成功配网后,您可以在对应的家庭下查看对应的设备列表。
self.home = [ThingSmartHome homeWithHomeId:#your homeId];
self.deviceList = [self.home.deviceList copy];
您必须先调用 获取家庭详细信息 接口。否则即使配网成功也无法成功获取。
设备的功能点信息存放在 deviceModel
的 schemaArray
中。
ThingSmartDevice *device = self.device;
NSArray *schemas = device.deviceModel.schemaArray;
schemaArray
存放了该设备的所有功能点信息,每个功能点被封装成一个ThingSmartSchemaModel
对象。
对于部分功能点信息复杂的设备,涂鸦将功能点再一次封装在 ThingSmartSchemaModel
的 property
属性中。具体判断方式如下:
NSString *type = [schema.type isEqualToString:@"obj"] ? schema.property.type : schema.type;
if ([type isEqualToString:@"bool"]) {
SwitchTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"switchCell"];
if (cell == nil){
cell = [[[NSBundle mainBundle] loadNibNamed:@"SwitchTableViewCell" owner:self options:nil] lastObject];
cell.label.text = schema.name;
[cell.switchButton setOn:[dps[schema.dpId] boolValue]];
};
}
return cell;
}
else if ([type isEqualToString:@"value"]) {
SliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"valueCell"];
if (cell == nil){
cell = [[[NSBundle mainBundle] loadNibNamed:@"SliderTableViewCell" owner:self options:nil] lastObject];
cell.label.text = schema.name;
cell.detailLabel.text = [dps[schema.dpId] stringValue];
cell.slider.minimumValue = schema.property.min;
cell.slider.maximumValue = schema.property.max;
cell.slider.value = [dps[schema.dpId] floatValue];
};
};
return cell;
}
else if ([type isEqualToString:@"enum"]) {
//...
}
//...
在上述代码中,以智能灯泡为例将其功能点信息展示在 TableView
上。其中:
type
为 bool 的 cell
展示了开关的信息。type
为 value 的 cell
展示了其亮度的信息。控制设备需要将对应的 DP 以 NSDictionary
形式通过 设备控制 接口改变设备状态或功能。
参数 dps
中可以包含多个功能点,您可以一次同时改变设备的多个状态。
同样以灯泡为例,以下代码分别修改了其开关状态和亮度值。
if ([type isEqualToString:@"bool"]) {
SwitchTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"switchCell"];
if (cell == nil){
cell = [[[NSBundle mainBundle] loadNibNamed:@"SwitchTableViewCell" owner:self options:nil] lastObject];
cell.label.text = schema.name;
[cell.switchButton setOn:[dps[schema.dpId] boolValue]];
cell.isReadOnly = isReadOnly;
// turn on/off when click the UISwitch
cell.switchAction = ^(UISwitch *switchButton) {
[weakSelf publishMessage:@{schema.dpId: [NSNumber numberWithBool:switchButton.isOn]}];
};
}
return cell;
}
else if ([type isEqualToString:@"value"]) {
SliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"valueCell"];
if (cell == nil){
cell = [[[NSBundle mainBundle] loadNibNamed:@"SliderTableViewCell" owner:self options:nil] lastObject];
cell.label.text = schema.name;
cell.detailLabel.text = [dps[schema.dpId] stringValue];
cell.slider.minimumValue = schema.property.min;
cell.slider.maximumValue = schema.property.max;
[cell.slider setContinuous:NO];
cell.slider.value = [dps[schema.dpId] floatValue];
// change the value when tap the UISlider
cell.sliderAction = ^(UISlider * _Nonnull slider) {
float step = schema.property.step;
float roundedValue = round(slider.value / step) * step;
[weakSelf publishMessage:@{schema.dpId : [NSNumber numberWithInt:(int)roundedValue]}];
};
};
return cell;
}
- (void)publishMessage:(NSDictionary *) dps {
[self.device publishDps:dps success:^{
// change success
}
failure:^(NSError *error) {
// change failed
}];
}
如果您需要监听设备状态的改变,如在线状态、移除通知、功能点状态改变等。需要实现 ThingSmartDeviceDelegate
协议。
self.device = [ThingSmartDevice deviceWithDeviceId:## your deviceId];
self.device.delegate = self;
#pragma mark - ThingSmartDeviceDelegate
/// Device information updates, such as the name and online status.
/// @param device The device instance.
- (void)deviceInfoUpdate:(ThingSmartDevice *)device;
/// Device online status updates
/// @param device The device instance.
- (void)deviceOnlineUpdate:(ThingSmartDevice *)device;
/// Indicates whether the device is removed.
/// @param device The device instance.
- (void)deviceRemoved:(ThingSmartDevice *)device;
/// The DP data updates.
/// @param device The device instance.
/// @param dps The command dictionary.
- (void)device:(ThingSmartDevice *)device dpsUpdate:(NSDictionary *)dps;
/// The DP data updates.
/// @param device The device instance.
/// @param dpCodes The DP codes.
- (void)device:(ThingSmartDevice *)device dpCommandsUpdate:(NSDictionary *)dpCodes;
/// The group OTA task progress.
/// @param device The gateway instance.
/// @param groupId group OTA task id.
/// @param type The firmware type.
/// @param progress The update progress.
- (void)device:(ThingSmartDevice *)device groupOTAId:(long)groupId firmwareType:(NSInteger)type progress:(double)progress;
/// The group OTA task status.
/// @param device The gateway device instance.
/// @param upgradeStatusModel The model of the update status.
- (void)device:(ThingSmartDevice *)device
groupOTAStatusModel:(ThingSmartFirmwareUpgradeStatusModel *)upgradeStatusModel;
/// The callback of Wi-Fi signal strength.
/// @param device The device instance.
/// @param signal The signal strength.
- (void)device:(ThingSmartDevice *)device signal:(NSString *)signal;
/// Receives MQTT custom messages.
/// @param device The device instance.
/// @param message The custom message.
- (void)device:(ThingSmartDevice *)device didReceiveCustomMessage:(ThingSmartMQTTMessageModel *)message;
/// Receives LAN custom messages.
- (void)device:(ThingSmartDevice *)device didReceiveLanMessage:(ThingSmartLanMessageModel *)message;
/// The delegate of warning information updates.
/// @param device The device instance.
/// @param warningInfo The warning information.
- (void)device:(ThingSmartDevice *)device warningInfoUpdate:(NSDictionary *)warningInfo;
/// The delegate of changes in device normal firmware/pid version update's status/progress
/// Notice: sometimes the progress may <0, when it occured please ignore the progress.
/// @param device The device instance.
/// @param statusModel status/progress model.
- (void)device:(ThingSmartDevice *)device otaUpdateStatusChanged:(ThingSmartFirmwareUpgradeStatusModel *)statusModel;
/// The Tuya message data update.
/// Example:
/// type == property:
/// payload = {
/// "code_name1": {
/// "value": "code_value1",
/// "time": 1234567890
/// },
/// "code_name2": {
/// "value": 50,
/// "time": 1234567890
/// }
/// }
/// type == action:
/// payload = {
/// "actionCode": "testAction",
/// "outputParams": {
/// "outputParam1":"outputValue1",
/// "outputParam2":50
/// }
/// }
/// type == event:
/// payload = {
/// "eventCode": "testEvent",
/// "outputParams": {
/// "outputParam1":["outputValue1", "outputValue2"],
/// "outputParam2":false
/// }
/// }
/// @param device The device instance.
/// @param thingMessageType The message type.
/// @param payload The message payload.
- (void)device:(ThingSmartDevice *)device didReceiveThingMessageWithType:(ThingSmartThingMessageType)thingMessageType payload:(NSDictionary *)payload;
调用 移除设备 接口,可以将当前设备从对应家庭下移除。
[self.device remove:^{
NSLog(@"remove success");
} failure:^(NSError *error) {
NSLog(@"remove failure: %@", error);
}];
学习完本次教程,相信您已经创建了一个自己的 App,它可以进行用户账号注册、家庭创建、家庭查询、设备配网、设备控制等。
为了降低您的开发成本,涂鸦将 SDK 中按模块进行了功能抽离和 UI 封装,为您提供了一整套一键接入的 UI 业务包。您可以根据需要自由选择需要的业务模块。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈