赞
踩
涂鸦智能生活 App SDK iOS 版提供了即全面又灵活的 IoT App 开发模式,大家可以通过本教程在两小时内快速开发出一款自己的 IoT App,并实现如下功能:
大家可以前往App 开发平台查看本教程中的示例代码,本次教程按功能模块进行分类,方便大家可以快速找到对应的代码参考学习。
学习完本教程,并且结合一定的面板开发后,大家可以创建出一个类似以下 iOS App 的 Demo。
在开始本教程前,请先确保已经完成以下内容 :
本模块中,仅演示使用手机号注册登录。除此以外,涂鸦智能生活 App SDK 还提供了邮箱、第三方、匿名等多种注册登录方式。
注册用户账号时:
countryCode
参数来区分注册地区,用于就近选择涂鸦 IoT 开发平台的可用区。如中国大陆为 86
,美国为 1
。TuyaSmartUser
。它是一个单例,存储了当前用户的所有信息及相关的登录注册方法。为了加强用户信息的数据安全,涂鸦优化了验证码和添加了账号限制。只有验证码服务可用的地区,才可以发送验证码。大家需要先查询自己的开发者账号是否已经在从涂鸦 IoT 开发平台开通验证码服务的可用地区列表。
- [[TuyaSmartUser sharedInstance] getWhiteListWhoCanSendMobileCodeSuccess:^(NSString *regions) {
-
- } failure:^(NSError *error) {
-
- }];
返回值 regions
表示一个或多个国家或地区,以 ,
隔开的字符串,例如 86
和 01
。可用区编号列表。
与大部分注册流程类似,用户必须先获取验证码。无论是手机或是邮箱,都可以使用统一的获取验证码接口在注册及后续密码修改,验证码登录,信息完善等操作中获取相应的验证码。
- NSString * region = [[TuyaSmartUser sharedInstance] getDefaultRegionWithCountryCode:countryCode];
-
- [[TuyaSmartUser 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
- }];
使用手机号码注册账号需要上传国家码、手机号码、密码、获取到的验证码等信息。
- [[TuyaSmartUser 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
- }
-
- ];
账号注册成功后,用户就可以使用手机号码登录账号。
- [[TuyaSmartUser sharedInstance] loginByPhone:countryCode
- phoneNumber:phone
- password:password
- success:^{
-
- // login successfully
-
- } failure:^(NSError *error) {
-
- // login fail
-
- }];
家庭是智能生活 App SDK 开发下实际场景的最大单位。IoT 设备的添加、编辑、移除、状态变化的监听基于家庭下。用户可以在用户账号下创建任意多个家庭,在指定家庭下,用户还可以添加并管理多个房间和家庭成员。
操作家庭模块时,大家将频繁调用对象 TuyaSmartHomeModel
和 TuyaSmartHome
。
对象 | 说明 |
---|---|
TuyaSmartHomeModel | 存放了家庭的基本信息,如 ID、名字、位置等。 |
TuyaSmartHome | 存放了家庭相关的所有功能,如单个家庭信息管理、家庭下的家庭成员管理、房间管理等。TuyaSmartHome 需要使用正确的 homeId 进行初始化。 |
在登录状态下,用户可以创建家庭,之后对于房间、成员、设备等对象的管理都基于家庭下。创建家庭需要通过 TuyaSmartHomeManager
调用 创建家庭 接口。
- [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
-
- }];
创建家庭接口说明
- - (void)addHomeWithName:(NSString *)homeName
- geoName:(NSString *)geoName
- rooms:(NSArray <NSString *>*)rooms
- latitude:(double)latitude
- longitude:(double)longitude
- success:(TYSuccessLongLong)success
- failure:(TYFailureError)failure;
参数说明
参数 | 说明 |
---|---|
homeName | 家庭的名称 |
geoName | 家庭的地址 |
rooms | 家庭下房间的名称列表 |
latitude | 家庭地址纬度 |
longitude | 家庭地址经度 |
success | 成功回调 |
failure | 失败回调 |
示例代码
Objc:
- - (void)addHome {
- [self.homeManager addHomeWithName:@"you_home_name"
- geoName:@"city_name"
- rooms:@[@"room_name"]
- latitude:lat
- longitude:lon
- success:^(double homeId) {
-
- // homeId 创建的家庭的 homeId
- NSLog(@"add home success");
- } failure:^(NSError *error) {
- NSLog(@"add home failure: %@", error);
- }];
- }
Swift:
- func addHome() {
- homeManager.addHome(withName: "you_home_name",
- geoName: "city_name",
- rooms: ["room_name"],
- latitude: lat,
- longitude: lon,
- success: { (homeId) in
- // homeId 创建的家庭的 homeId
- print("add home success")
- }) { (error) in
- if let e = error {
- print("add home failure: \(e)")
- }
- }
- }
在登录状态下大家可以直接获取家庭列表。如还没创建过家庭,将返回 空数组。
- //get home list and refresh the TableView
- [self.homeManager getHomeListWithSuccess:^(NSArray<TuyaSmartHomeModel *> *homes) {
- // get success , refresh UI
- // [self.tableView reloadData];
- } failure:^(NSError *error) {
-
- // get failed
-
- }];
在创建了家庭后,后续房间成员、用户配网等相关操作均需要基于某一特定家庭,因此建议将这一特定家庭作为 App 的全局变量存储。当然,大家也可以在本地随时切换当前家庭,涂鸦 IoT 开发平台并不会记录这一信息。
在本教程和配套的 Sample 中,默认将列表的第一个家庭设为当前家庭。
- + (TuyaSmartHomeModel *)getCurrentHome {
-
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- if (![defaults valueForKey:@"CurrentHome"]) {
- return nil;
- }
-
- long long homeId = [[defaults valueForKey:@"CurrentHome"] longLongValue];
-
- if (![TuyaSmartHome homeWithHomeId:homeId]) {
- return nil;
- }
-
- return [TuyaSmartHome homeWithHomeId:homeId].homeModel;
- }
-
- + (void)setCurrentHome:(TuyaSmartHomeModel *)homeModel {
-
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- [defaults setValue:[NSString stringWithFormat:@"%lld", homeModel.homeId] forKey:@"CurrentHome"];
-
- }

实现以上逻辑后,家庭的操作就可以被简化:
查询家庭列表:
self.home = [TuyaSmartHome homeWithHomeId:[TYHome getCurrentHome].homeId];
设置家庭 ID:
[TYHome setCurrentHome:xxx];
简单来说,配网就是将设备连接并注册到云端,使其拥有与云端远程通信的能力。涂鸦智能生活 App SDK 提供了丰富的配网方式以支持大部分智能设备。如 Wi-Fi 连接,蓝牙连接等。
本模块以 Wi-Fi 配网为例介绍如何使用SDK 将设备配置到云端。
Wi-Fi 配网方式包括 EZ、AP、扫 App 二维码三种方式,在之后的 iOS 版本 SDK 中,推荐使用 AP 热点模式 代替 Wi-Fi 快连模式(即EZ模式)。主要原因如下:
开始配网之前,SDK 需要在联网状态下从涂鸦 获取配网Token,然后才可以开始热点模式配网。Token 的有效期为 10 分钟,且配置成功后就会失效,再次配网需要重新获取。
获取 Token 需要上传当前的 homeId
,因此大家需要确保处于登录状态并至少创建了一个家庭。
- [[TuyaSmartActivator sharedInstance] getTokenWithHomeId:homeId
- success:^(NSString *token) {
- // NSLog(@"getToken success: %@", token);
- // you could start ConfigWiFi now
- } failure:^(NSError *error) {
- //NSLog(@"getToken failure: %@", error.localizedDescription);
- }
- ];
获取Token 接口说明
- - (void)getTokenWithHomeId:(long long)homeId
- success:(TYSuccessString)success
- failure:(TYFailureError)failure;
参数说明
参数 | 说明 |
---|---|
homeId | 设备将要绑定到的家庭的 ID |
success | 成功回调,返回配网 Token |
failure | 失败回调,返回失败原因 |
示例代码
Objc:
- - (void)getToken {
- [[TuyaSmartActivator sharedInstance] getTokenWithHomeId:homeId success:^(NSString *token) {
- NSLog(@"getToken success: %@", token);
- // TODO: startConfigWiFi
- } failure:^(NSError *error) {
- NSLog(@"getToken failure: %@", error.localizedDescription);
- }];
- }
Swift:
- func getToken() {
- TuyaSmartActivator.sharedInstance()?.getTokenWithHomeId(homeId, success: { (token) in
- print("getToken success: \(token)")
- // TODO: startConfigWiFi
- }, failure: { (error) in
- if let e = error {
- print("getToken failure: \(e)")
- }
- })
- }
iOS 14 版本适配
从 iOS 14 版本开始,在设备配网、局域网本地控制时会触发 本地网络 权限弹窗。
目前苹果没有提供任何 API 对此权限进行判断,建议在相关功能无法正常使用时提示、引导用户检查 系统设置 中的 app设置,确认是否开启了 本地网络 权限。
iOS 13 版本适配
从 iOS 13 版本开始,如果用户没有开启地理位置权限,在已开启 Wi-Fi 权限的前提下,[[TuyaSmartActivator sharedInstance] currentWifiSSID]
将获取不到有效的 Wi-Fi SSID 或 BSSID。
在此情况下,iOS 会返回下列默认值:
开始配网前,请确保设备处于待配网状态。操作方法可参考设备的使用说明书。
调用 配网接口,需要提供路由器的 SSID(即 Wi-Fi 名称)、密码、从云端获取的 Token 等。
- [TuyaSmartActivator sharedInstance].delegate = self;
- [[TuyaSmartActivator sharedInstance] startConfigWiFi:TYActivatorModeAP
- ssid:ssid
- password:password
- token:token
- timeout:100];
-
开始配网 接口说明
- - (void)startConfigWiFi:(TYActivatorMode)mode
- ssid:(NSString *)ssid
- password:(NSString *)password
- token:(NSString *)token
- timeout:(NSTimeInterval)timeout;
参数说明
参数 | 说明 |
---|---|
mode | 配网模式 |
ssid | Wi-Fi 名称 |
password | Wi-Fi 密码 |
token | 配网 Token |
timeout | 超时时间,默认 100 秒 |
示例代码
Objc :
- - (void)startConfigWiFi:(NSString *)ssid password:(NSString *)password token:(NSString *)token {
- // 设置 TuyaSmartActivator 的 delegate,并实现 delegate 方法
- [TuyaSmartActivator sharedInstance].delegate = self;
-
- // 开始配网,快连模式对应 mode 为 TYActivatorModeEZ
- [[TuyaSmartActivator sharedInstance] startConfigWiFi:TYActivatorModeEZ ssid:ssid password:password token:token timeout:100];
- }
-
- #pragma mark - TuyaSmartActivatorDelegate
- - (void)activator:(TuyaSmartActivator *)activator didReceiveDevice:(TuyaSmartDeviceModel *)deviceModel error:(NSError *)error {
-
- if (!error && deviceModel) {
- //配网成功
- }
-
- if (error) {
- //配网失败
- }
- }

Swift :
- func startConfigWiFi(withSsid ssid: String, password: String, token: String) {
- // 设置 TuyaSmartActivator 的 delegate,并实现 delegate 方法
- TuyaSmartActivator.sharedInstance()?.delegate = self
-
- // 开始配网
- TuyaSmartActivator.sharedInstance()?.startConfigWiFi(TYActivatorModeEZ, ssid: ssid, password: password, token: token, timeout: 100)
- }
-
- #pragma mark - TuyaSmartActivatorDelegate
- func activator(_ activator: TuyaSmartActivator!, didReceiveDevice deviceModel: TuyaSmartDeviceModel!, error: Error!) {
- if deviceModel != nil && error == nil {
- //配网成功
- }
-
- if let e = error {
- //配网失败
- print("\(e)")
- }
- }

使用 AP 模式配网时,需要先实现 TuyaSmartActivatorDelegate
协议,以监听配网结果的回调。
@interface xxxViewController () <TuyaSmartActivatorDelegate>
- - (void)activator:(TuyaSmartActivator *)activator didReceiveDevice:(TuyaSmartDeviceModel *)deviceModel error:(NSError *)error {
- if (deviceModel && error == nil) {
- //success
- // NSLog(@"connected success: %@", deviceModel.name);
- }
-
- if (error) {
- //fail
- }
-
- // stop config
- }
开始配网操作后,App 会持续广播配网信息,直到配网成功或是超时才停止。如果需要中途取消操作或配网完成,需要调用 停止配网 接口。
- [TuyaSmartActivator sharedInstance].delegate = nil;
- [[TuyaSmartActivator sharedInstance] stopConfigWiFi];
停止配网接口说明
- (void)stopConfigWiFi;
示例代码
Objc:
- - (void)stopConfigWifi {
- [TuyaSmartActivator sharedInstance].delegate = nil;
- [[TuyaSmartActivator sharedInstance] stopConfigWiFi];
- }
Swift:
- func stopConfigWifi() {
- TuyaSmartActivator.sharedInstance()?.delegate = nil
- TuyaSmartActivator.sharedInstance()?.stopConfigWiFi()
- }
本章节主要操作对象包含 TuyaSmartDeviceModel
和 TuyaSmartDevice
。
对象 | 说明 |
---|---|
TuyaSmartDeviceModel |
|
TuyaSmartDevice | TuyaSmartDevice 存放了设备相关的所有功能,如功能控制,设备固件管理等。您需要用正确的 deviceId 初始化一个 TuyaSmartDevice 。 |
设备成功配网后,可以在对应的家庭下查看对应的设备列表。
- self.home = [TuyaSmartHome homeWithHomeId:#your homeId];
- self.deviceList = [self.home.deviceList copy];
设备的功能点信息存放在 deviceModel
的 schemaArray
中。
- TuyaSmartDevice *device = self.device;
- NSArray *schemas = device.deviceModel.schemaArray;
schemaArray
存放了该设备的所有功能点信息,每个功能点被封装成一个TuyaSmartSchemaModel
对象。
对于部分功能点信息复杂的设备,涂鸦将功能点再一次封装在 TuyaSmartSchemaModel
的 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
形式通过 设备控制 接口改变设备状态或功能。
同样以灯泡为例,以下代码分别修改了其开关状态和亮度值。
- 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
- }];
- }
如果需要监听设备状态的改变,如在线状态、移除通知、功能点状态改变等。需要实现 TuyaSmartDeviceDelegate
协议。
- self.device = [TuyaSmartDevice deviceWithDeviceId:## your deviceId];
- self.device.delegate = self;
- #pragma mark - TuyaSmartDeviceDelegate
-
- /// Device information updates, such as the name and online status.
- /// @param device The device instance.
- - (void)deviceInfoUpdate:(TuyaSmartDevice *)device;
-
- /// Device online status updates
- /// @param device The device instance.
- - (void)deviceOnlineUpdate:(TuyaSmartDevice *)device;
-
- /// Indicates whether the device is removed.
- /// @param device The device instance.
- - (void)deviceRemoved:(TuyaSmartDevice *)device;
-
- /// The DP data updates.
- /// @param device The device instance.
- /// @param dps The command dictionary.
- - (void)device:(TuyaSmartDevice *)device dpsUpdate:(NSDictionary *)dps;
-
- /// The DP data updates.
- /// @param device The device instance.
- /// @param dpCodes The DP codes.
- - (void)device:(TuyaSmartDevice *)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:(TuyaSmartDevice *)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:(TuyaSmartDevice *)device
- groupOTAStatusModel:(TuyaSmartFirmwareUpgradeStatusModel *)upgradeStatusModel;
-
- /// The callback of Wi-Fi signal strength.
- /// @param device The device instance.
- /// @param signal The signal strength.
- - (void)device:(TuyaSmartDevice *)device signal:(NSString *)signal;
-
- /// Receives MQTT custom messages.
- /// @param device The device instance.
- /// @param message The custom message.
- - (void)device:(TuyaSmartDevice *)device didReceiveCustomMessage:(TuyaSmartMQTTMessageModel *)message;
-
- /// Receives LAN custom messages.
- - (void)device:(TuyaSmartDevice *)device didReceiveLanMessage:(TuyaSmartLanMessageModel *)message;
-
- /// The delegate of warning information updates.
- /// @param device The device instance.
- /// @param warningInfo The warning information.
- - (void)device:(TuyaSmartDevice *)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:(TuyaSmartDevice *)device otaUpdateStatusChanged:(TuyaSmartFirmwareUpgradeStatusModel *)statusModel;
- /// The thing 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:(TuyaSmartDevice *)device didReceiveThingMessageWithType:(TuyaSmartThingMessageType)thingMessageType payload:(NSDictionary *)payload;

调用 移除设备 接口,可以将当前设备从对应家庭下移除。
- [self.device remove:^{
- NSLog(@"remove success");
- } failure:^(NSError *error) {
- NSLog(@"remove failure: %@", error);
- }];
移除设备 接口说明
- (void)remove:(nullable TYSuccessHandler)success failure:(nullable TYFailureError)failure;
参数说明
参数 | 说明 |
---|---|
success | 成功回调 |
failure | 失败回调 |
示例代码
Objective-C:
- - (void)removeDevice {
- // self.device = [TuyaSmartDevice deviceWithDeviceId:@"your_device_id"];
-
- [self.device remove:^{
- NSLog(@"remove success");
- } failure:^(NSError *error) {
- NSLog(@"remove failure: %@", error);
- }];
- }
Swift:
- func removeDevice() {
- device?.remove({
- print("remove success")
- }, failure: { (error) in
- if let e = error {
- print("remove failure: \(e)")
- }
- })
- }
为了减少客户的开发成本,涂鸦针对特定设备提供了一系列的设备控制面板。详情请登录 涂鸦IoT开发平台 参考 全品类控制面板 和 空调品类控制面板。
学习完本次教程,相信大家已经创建出了一个自己的 App,它可以进行用户账号注册、家庭创建、家庭查询、设备配网、设备控制等。
为了降低开发者的开发成本,涂鸦将 SDK 中按模块进行了功能抽离和 UI 封装,为大家提供了一整套一键接入的 UI业务包,可以根据需要自由选择需要的业务模块。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。