如果当前设备是一个网关,且该网关下的子设备需接入云端,此时需要使用子设备管理功能。
子设备动态注册
网关子设备管理提供子设备动态注册、获取云端网关下子设备列表、添加子设备、删除子设备、子设备上线、子设备下线、监听子设备禁用和删除的消息、代理子设备数据上下行的能力。
网关本身是一个直连设备,网关使用子设备管理的功能前,需要网关已经连接到阿里云物联网平台。网关设备的开发,请参见认证与连接和基于MQTT Topic通信。
子设备的动态注册是指网关根据子设备的productKey、deviceName向云端获取子设备DeviceSecret的过程,网关获取到子设备的DeviceSecret之后,需要将其保存在网关上,即使网关reset也不能将子设备的DeviceSecret丢失。
子设备在商家后台创建时需要开启允许动态注册功能,同时子设备的deviceName需要厂商预先上传到阿里云物联网平台。
示例代码如下:
self.gatewayInterface = [[LinkKitEntry sharedKit] gatewayInterface];
NSMutableArray * subBases = @[].mutableCopy;
for (NSDictionary * subinfo in _subDevices) {
LinkkitDeviceBase * base = [[LinkkitDeviceBase alloc] init];
base.productKey = [subinfo valueForKey:@"productKey"];
base.deviceName = [subinfo valueForKey:@"deviceName"];
[subBases addObject:base];
}
[self.gatewayInterface subDeviceRegisterBatch:subBases
resultBlock:^(NSArray * _Nullable result, NSError * _Nullable error) {
LinkkitLogDebug(@"subDeviceRegisterBatch error : %@", error);
self.subDevicesReg = result;
LinkkitDeviceAuth * authSub = result[0];
/// LinkkitDeviceAuth这个类包含deviceSecret,建议用户将deviceSecret持久保存。当子设备再次上线时,使用保存的子设备证书进行身份认证。
dispatch_async(dispatch_get_main_queue(), ^{
[self ims_showHUDWithMessage:[NSString stringWithFormat:@"成功注册子设备个数 : %d",
(int)self.subDevicesReg.count]];
});
}];
添加子设备到网关
添加子设备过程实际上是子设备跟网关在本地以及IoT云端建立拓扑关系的过程,子设备要通过网关实现数据上下云,必须首先跟网关建立拓扑关系。拓扑关系建立后一直存在,直到删除拓扑关系。
添加子设备存在两种方式:
通过动态注册网关获取到了子设备的证书信息。 示例代码如下:
__weak typeof (self) weakSelf = self; LinkkitDeviceAuth * authSub = self.subDevicesReg[0]; [self.gatewayInterface addSubDevice:authSub delegate:self resultBlock:^(id<ILKSubDeviceChannel> _Nonnull subChannel, NSError * _Nullable error) { LinkkitLogDebug(@"subDevice logout add to gateway : %@", error); weakSelf.subChannel = subChannel; dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.lableSubState.text = @"已关联至网关"; [weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"添加子设备到网关 : %@", error ? @"失败":@"成功"]]; }); }];
子设备本身烧录了设备证书信息,对于此类设备,切记无需要再去调用 “子设备动态注册”方法。 示例代码如下:
__weak typeof (self) weakSelf = self; LinkkitDeviceBase * devBase = [[LinkkitDeviceBase alloc] init]; devBase.productKey = @"sub device productKey"; devBase.deviceName = @"sub device deviceName"; [self.gatewayInterface addSubDevice:devBase signer:self delegate:self resultBlock:^(id<ILKSubDeviceChannel> _Nullable subChannel, NSError * _Nullable error) { LinkkitLogDebug(@"subDevice logout add to gateway : %@", error); weakSelf.subChannel = subChannel; dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.lableSubState.text = @"已关联至网关"; [weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"添加子设备到网关 : %@", error ? @"失败":@"成功"]]; }); }]; // 其中 signer的具体实现,请参见LinkkitGateway.h 中的 protocol ILKSubDeviceSigner 声明中介绍。
从网关删除子设备
删除子设备与网关拓扑关系的示例代码如下:
__weak typeof (self) weakSelf = self;
LinkkitDeviceBase * devBase = [[LinkkitDeviceBase alloc] init];
devBase.productKey = @"sub device productKey";
devBase.deviceName = @"sub device deviceName";
[self.gatewayInterface deleteSubDevice:devBase
resultBlock:^(BOOL succeeded, NSError * _Nullable error) {
LinkkitLogDebug(@"subDevice delete from gateway error : %@", error);
weakSelf.subChannel = nil;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.lableSubState.text = @"未关联网关";
[weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"从网关删除子设备 : %@", error ? @"失败":@"成功"]];
});
}];
子设备上线
子设备必须在上线之后才可以执行子设备的订阅、发布等操作。代理子设备上线的示例代码如下:
__weak typeof (self) weakSelf = self;
[self.subChannel login:^(BOOL succeeded, NSError * _Nullable error) {
LinkkitLogDebug(@"subDevice login error : %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.lableSubState.text = @"已上线";
[weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"子设备上线 : %@", error ? @"失败":@"成功"]];
});
}];
子设备下线
子设备下线之后不可以进行子设备的发布、订阅、取消订阅等操作。子设备下线的示例代码如下:
__weak typeof (self) weakSelf = self;
[self.subChannel logout:^(BOOL succeeded, NSError * _Nullable error) {
LinkkitLogDebug(@"subDevice logout error : %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.lableSubState.text = @"未上线";
[weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"子设备下线 : %@", error ? @"失败":@"成功"]];
});
}];
监听子设备禁用、删除
网关设备可以在云端操作子设备,例如禁用子设备、启用子设备、删除和子设备的拓扑关系。目前服务端只支持禁用子设备的下行通知。服务端在禁用子设备时会对子设备进行下线处理,后续网关不能代理子设备和云端做通信。可以通过设置子设备stateListener
来侦听子设备禁用、删除消息。
self.subChannel.stateListener = self;
代理子设备基础数据上下行
使用网关的通道执行子设备的数据上下行。
上行数据示例代码:
- (IBAction)onClickUpload:(id)sender {
if (self.subChannel == nil) {
[self ims_showHUDWithMessage:@"请添加子设备并上线"];
return;
}
__weak typeof (self) weakSelf = self;
NSString * topic = self.textFieldPubTopic.text;
NSData * upData = [self.textViewPubContent.text dataUsingEncoding:NSUTF8StringEncoding];
int qos = [self.textFieldQos.text intValue];
[self.subChannel publish:topic data:upData qos:qos
resultBlock:^(BOOL succeeded, NSError * _Nullable error) {
LinkkitLogDebug(@"subDevice upload error : %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"子设备数据上行 : %@", error ? @"失败":@"成功"]];
});
}];
}
关于QoS取值,目前阿里云IoT平台只支持QoS取值为[0,1],不支持QoS=2。
如果需要能收到子设备的下推消息,首先需要订阅相关Topic示例代码如下:
- (IBAction)onClickSubscribe:(id)sender {
if (self.subChannel == nil) {
[self ims_showHUDWithMessage:@"请添加子设备并上线"];
return;
}
__weak typeof (self) weakSelf = self;
NSString * topic = self.textFieldSubTopic.text;
[self.subChannel subscribe:topic
resultBlock:^(BOOL succeeded, NSError * _Nullable error) {
LinkkitLogDebug(@"subDevice subscribe error : %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"子设备订阅 : %@", error ? @"失败":@"成功"]];
});
}];
}
同时,还需要在添加子设备到网关时传入ILKSubDeviceDelegate
的实例。 ILKSubDeviceDelegate
实现如下所示:
///侦听子设备与云端的连接状态,即上下线情况
- (void)onConnectResult:(BOOL)success
error:(NSError * _Nullable)err
subDeviceChannel:(nonnull id<ILKSubDeviceChannel>)subDeviceChannel {
if ([self.subChannel.subDeviceProfile.deviceBaseId
isEqualToString:subDeviceChannel.subDeviceProfile.deviceBaseId]) {
//__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
});
} else {
}
}
///侦听子设备的下推(从云端下推)数据
- (void)onDataPush:(nonnull NSString *)topic data:(nonnull NSData *)data {
NSString * downData = [NSString stringWithFormat:@"收到下推,topic : %@ \r\n", topic];
downData = [downData stringByAppendingString:[NSString stringWithFormat:@"\r\n数据 : %@", data]];
LinkkitLogDebug(@"subDevice recv topic : %@", topic);
dispatch_async(dispatch_get_main_queue(), ^{
self.textViewDownData.text = downData;
});
}
可以通过取消订阅方法来屏蔽某些已经订阅过的消息。 示例代码如下:
- (IBAction)onClickUnsubscribe:(id)sender {
if (self.subChannel == nil) {
[self ims_showHUDWithMessage:@"请添加子设备并上线"];
return;
}
__weak typeof (self) weakSelf = self;
NSString * topic = self.textFieldSubTopic.text;
[self.subChannel unsubscribe:topic
resultBlock:^(BOOL succeeded, NSError * _Nullable error) {
LinkkitLogDebug(@"subDevice unsubscribe error : %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf ims_showHUDWithMessage:[NSString stringWithFormat:@"子设备取消订阅 : %@", error ? @"失败":@"成功"]];
});
}];
}