网关与子设备

如果当前设备是一个网关,且该网关下的子设备需接入云端,此时需要使用子设备管理功能。

子设备动态注册

网关子设备管理提供子设备动态注册、获取云端网关下子设备列表、添加子设备、删除子设备、子设备上线、子设备下线、监听子设备禁用和删除的消息、代理子设备数据上下行的能力。

网关本身是一个直连设备,网关使用子设备管理的功能前,需要网关已经连接到阿里云物联网平台。网关设备的开发,请参见认证与连接基于MQTT Topic通信

子设备的动态注册是指网关根据子设备的productKeydeviceName向云端获取子设备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 ? @"失败":@"成功"]];
                       });
                   }];
}