Matter 设备配网

更新时间:2023-10-10 10:22:06下载pdf

Matter 设备配网支持 Tuya Matter 设备和三方 Matter 设备配网,两者接入的 API 调用一致,但配网表现有不同。Matter 设备配网类型包含自发现、扫描二维码和手动输入编码配网三种方式。

配网流程

App业务拓展 SDKMatter 设备云端设备进入配网模式解析设备二维码1解析设备二维码返回设备信息2alt[扫描设备二维码]解析设备码3解析设备码返回设备信息4alt[输入设备码]开启搜索功能,搜索 Matter 设备5搜索设备返回搜索到的设备列表6alt[搜索设备]请求配网 Token7返回配网 Token8开始激活设备9开始激活设备,并连接设备10完成连接11完成连接12根据设备信息激活设备13根据设备信息激活设备14在云端激活设备15激活成功16完成激活17完成激活18alt[激活设备]App业务拓展 SDKMatter 设备云端Matter 设备配网流程

注册配网类型

基座初始化时,需要注册一下配网的类型,Matter 配网对应为 ThingSmartActivatorTypeMatterModel

接口说明

/// Initialize network configuration types
/// @param typeList Network configuration types
- (void)registerWithActivatorList:(NSArray<ThingSmartActivatorTypeModel *>*)typeList;

参数说明

参数 说明
typeList 配网类型列表

查询 Token

使用涂鸦 SDK 获取配网 Token,然后进行配网。Token 的有效期为 10 分钟,且配置成功后就会失效。再次配网时,需要重新查询 Token。

接口说明

- (void)getTokenWithHomeId:(long long)homeId
                   success:(ThingSuccessString)success
                   failure:(ThingFailureError)failure;

参数说明

参数 说明
homeId 设备将要绑定到的家庭的 ID
success 成功回调,返回配网 Token
failure 失败回调,返回失败原因

二维码/手动码解析

二维码和手动码解析接口,若二维码和手动码错误,则不会返回设备模型。

/// Check the matter code is legal or not. Returns nil if it is invalid.
/// @param qrString The matter QRCode string or Manual Code string.
- (ThingSmartActivatorDeviceModel *)parseSetupCode:(NSString *)qrString;

参数说明

参数 说明
qrString 二维码或者手动设备码信息

开始搜索

开始搜索时,需要传入已注册的 typeList

接口说明

/// Start searching
/// @param typeList Network configuration types
- (void)startSearch:(NSArray <ThingSmartActivatorTypeModel *>*)typeList;

参数说明

参数 说明
typeList 配网类型列表

停止搜索

接口说明

/// Stop searching
/// @param typeList Network configuration types
/// @param clearCache Whether to clear the cache
- (void)stopSearch:(NSArray <ThingSmartActivatorTypeModel *>*)typeList clearCache:(BOOL)clearCache;

参数说明

参数 说明
typeList 配网类型列表
clearCache 是否清空当前搜索设备缓存

搜索设备回调

搜到设备之后,会通过搜索回调返回设备信息。如果失败,则会返回对应的失败信息。

接口说明

/// Device search callback
/// @param service Search instance
/// @param type Network configuration type
/// @param device Discovered device
/// @param errorModel Error callback
- (void)activatorService:(id<ThingSmartActivatorSearchProtocol>)service
            activatorType:(ThingSmartActivatorTypeModel *)type
             didFindDevice:(nullable ThingSmartActivatorDeviceModel *)device
                     error:(nullable ThingSmartActivatorErrorModel *)errorModel;

参数说明

参数 说明
service 配网服务。
type 配网类型。
device 发现设备,返回此次配网的设备模型。
errorModel 如果配网失败或者超时,返回此模型,成功时返回 nil

设备激活

接口说明

/// Activate devices with a single network configuration type
/// @param type Network configuration type
/// @param deviceList Devices to be activated
- (void)startActive:(ThingSmartActivatorTypeModel *)type deviceList:(NSArray<ThingSmartActivatorDeviceModel *>*)deviceList;

参数说明

参数 说明
type 配网类型
deviceList 待激活设备列表,目前仅支持单个设备

Matter 配网链路回调

SDK 会根据设备广播包自动选择最合适的配网链路,并回调给业务层进行相应的页面显示。共有三种类型,涂鸦链路、分享配网链路以及 MatterSupport 链路。
如您需要支持 MatterSupport 链路配网,请参考 Matter 设备接入准备。只有配置完成后,才可以进行 MatterSupport 配网。

/// Discoveryed Matter Device.
/// @param typeModel device type model
- (void)matterDeviceDiscoveryed:(ThingMatterDeviceDiscoveriedType *)typeModel;

参数说明

参数 说明
typeModel 具体配网链路

设备 PASE 会话建立成功回调

Matter 设备连接后建立完成密码认证会话建立(Password Authenticated Session Establishment,PASE)会话,将触发 PASE 会话成功回调。

/// Matter commission establish complete, model's type will be known.
/// @param deviceModel Device model.
- (void)matterCommissioningSessionEstablishmentComplete:(ThingSmartActivatorDeviceModel *)deviceModel;

参数说明

参数 说明
deviceModel PASE 会话阶段的 Matter 设备模型,可用于部分数据提取

建立完整 CASE 会话并激活设备上云

完成 PASE 会话后,部分关键信息如设备类型等已经可以拿到,如您确认需要激活设备上云,且完成了证书认证会话建立(Certificate Authenticated Session Establishment,CASE)会话的必要参数组装,则需要继续调用该接口完成 CASE 会话建立。

/// @param deviceModel the matter devicemodel.
/// @param typeModel Network configuration type.
- (void)continueCommissionDevice:(ThingSmartActivatorDeviceModel *)deviceModel typeModel:(ThingSmartActivatorTypeMatterModel *)typeModel;

参数说明

参数 说明
deviceModel PASE 会话阶段完成后回调的 Matter 设备模型,从 matterCommissioningSessionEstablishmentComplete 回调中获取。
typeModel 组装的 CASE 会话参数模型,双模设备需要 ssidpassword,thread 设备需要 gwDevId

设备认证回调

如果您配对的是一个未经过官方认证的 Matter 设备,那么会触发认证回调。如果触发了该回调,在您选择继续或放弃前,配网流程将会暂停。

// Matter device attestation fails, use `-continueCommissioningDevice:ignoreAttestationFailure:error:` to continue OR interrupt.
///
/// **Notice**
/// Usually app will display an alert view to the user, allowing the user to judge whether to ignore the attestation failure.
///
/// @param device the failure device.
/// @param error the failure error info.
- (void)matterDeviceAttestation:(void *)device error:(NSError * _Nonnull)error;

参数说明

参数 说明
device 配网链路中的设备对象地址指针
error 未通过认证的错误信息

信任未认证的设备并继续配网

如您认为该设备证书可以被信任,可以调用该接口继续配网。

/// Continue the pairing or NOT.
///
/// **Notice:** Can only be used after the `-matterDeviceAttestation:error:` delegate callback.
///
/// @param device It MUST BE the device object through the `-matterDeviceAttestation:error:` delegate callback.
/// @param ignoreAttestationFailure Ignore the attestation fail or NOT. `YES` - continue pairing, `NO` - interrupt pairing.
/// @param error the error info.
- (void)continueCommissioningDevice:(void *)device
           ignoreAttestationFailure:(BOOL)ignoreAttestationFailure
                              error:(NSError * __autoreleasing *)error;

参数说明

参数 说明
device Attestation 回调方法中的设备对象地址指针,必须透传回调上来的参数,不可进行其他操作
ignoreAttestationFailure 是否忽略未认证信息
error 该操作执行后可能的错误信息

设备激活结果回调

// Device network configuration result callback
/// @param service Device network configuration implementation object
/// @param type Network configuration type
/// @param devices Devices being configured
/// @param errorModel Error encountered during network configuration
- (void)activatorService:(id<ThingSmartActivatorActiveProtocol>)service
           activatorType:(ThingSmartActivatorTypeModel *)type
       didReceiveDevices:(nullable NSArray<ThingSmartActivatorDeviceModel *> *)devices
                   error:(nullable ThingSmartActivatorErrorModel *)errorModel;

参数说明

参数 说明
service 配网服务
type 配网类型
devices 激活成功设备
errorModel 如果配网失败或者超时,返回此模型,成功时返回 nil

停止激活

接口说明

/// Stop activating devices
/// @param typeList Array of network configuration types
/// @param clearCache Whether to clear the cache
- (void)stopActive:(NSArray <ThingSmartActivatorTypeModel *>*)typeList clearCache:(BOOL)clearCache;

参数说明

参数 说明
typeList 配网类型
clearCache 清空缓存设备信息

错误码说明

配网失败或者配网超时的情况下,会返回 ThingSmartActivatorErrorModel

@interface ThingSmartActivatorErrorModel : NSObject
@property (nonatomic, strong) ThingSmartActivatorDeviceModel *deviceModel;
@property (nonatomic) NSError *error;
@end

其中 error 对应的错误码,定义在 ThingSmartActivatorDiscoveryError 中。

示例代码

搜索配网

Swift

class BEMatterTableViewController: UITableViewController {

    private var typeModel: ThingSmartActivatorTypeMatterModel = {
        let type = ThingSmartActivatorTypeMatterModel()
        type.type = ThingSmartActivatorType.matter
        type.typeName = NSStringFromThingSmartActivatorType(ThingSmartActivatorType.matter)
        type.timeout = 180
        return type
    }()

    lazy var discovery: ThingSmartActivatorDiscovery = {
        let discovery = ThingSmartActivatorDiscovery()
        discovery.register(withActivatorList: [self.typeModel])
        discovery.setupDelegate(self)
        discovery.loadConfig()
        return discovery
    }()

    func bindMatter(qrcode codeStr: String?) -> Void {
        let homeId = (Home.current?.homeId)!
        let payload = self.discovery.parseSetupCode(codeStr ?? "")
        let activator = ThingSmartActivator()
        activator.getTokenWithHomeId(homeId, success: { [weak self] (token) in
            guard let self = self else { return }
            self.typeModel.token = token ?? ""
            self.typeModel.spaceId = homeId
            self.discovery.startActive(self.typeModel, deviceList:[payload])
            SVProgressHUD.show(withStatus: NSLocalizedString("Configuring", comment: ""))
        }, failure: { (error) in
            let errorMessage = error?.localizedDescription ?? ""
            SVProgressHUD.showError(withStatus: errorMessage)
        })
    }

    private func stopConfigWifi() {
        SVProgressHUD.dismiss()
        discovery.stopActive([typeModel], clearCache: true)
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        discovery.startSearch([self.typeModel])
    }

}

extension BEMatterTableViewController: ThingSmartActivatorSearchDelegate {
    func activatorService(_ service: ThingSmartActivatorSearchProtocol, activatorType type: ThingSmartActivatorTypeModel, didFindDevice device: ThingSmartActivatorDeviceModel?, error errorModel: ThingSmartActivatorErrorModel?) {
        if let device = device {
            if device.deviceModelType == ThingSearchDeviceModelTypeMatterWifi {
                self.typeModel.ssid = ssid.text ?? ""
                self.typeModel.password = password.text ?? ""
                print("Please use Dual Mode to pair: %@", device.uniqueID)

            }

            if device.deviceModelType == ThingSearchDeviceModelTypeMatterThread {
                self.typeModel.gwDevId = "gateway deivce id"
            }
            let homeId = (Home.current?.homeId)!
            let payload = self.discovery.parseSetupCode(codeStr ?? "")
            let activator = ThingSmartActivator()
            activator.getTokenWithHomeId(homeId, success: { [weak self] (token) in
                guard let self = self else { return }
                self.typeModel.token = token ?? ""
                self.typeModel.spaceId = homeId
                self.discovery.startActive(self.typeModel, deviceList:[payload])
                SVProgressHUD.show(withStatus: NSLocalizedString("Configuring", comment: ""))
            }, failure: { (error) in
                let errorMessage = error?.localizedDescription ?? ""
                SVProgressHUD.showError(withStatus: errorMessage)
            })
        }

        if let errorModel = errorModel {
            // Error
            SVProgressHUD.showError(withStatus: errorModel.error.localizedDescription)
        }
    }
}

extension BEMatterTableViewController: ThingSmartActivatorActiveDelegate {
    func activatorService(_ service: ThingSmartActivatorActiveProtocol, activatorType type: ThingSmartActivatorTypeModel, didReceiveDevices devices: [ThingSmartActivatorDeviceModel]?, error errorModel: ThingSmartActivatorErrorModel?) {
        if errorModel != nil {
            SVProgressHUD.showError(withStatus: "Bind Failure. (\(errorModel?.error.localizedDescription ?? ""))")
            return
        }

        let device = devices?.first
        isSuccess = true
        SVProgressHUD.show(withStatus: "Bind Success. \n devId: \(device?.uniqueID ?? "") \n name: \(device?.name ?? "")")
    }
}

extension BEMatterTableViewController: ThingSmartActivatorDeviceExpandDelegate {
    func matterDeviceDiscoveryed(_ typeModel: ThingMatterDeviceDiscoveriedType) {
        deviceTypeModel = typeModel
    }

    func matterCommissioningSessionEstablishmentComplete(_ deviceModel: ThingSmartActivatorDeviceModel) {
        if deviceTypeModel?.deviceType == .wifi {
            self.typeModel.ssid = ssid.text ?? ""
            self.typeModel.password = password.text ?? ""
            self.discovery.continueCommissionDevice(deviceModel, typeModel: self.typeModel)
        }

        ///
        if deviceTypeModel?.deviceType == .thread {
            self.typeModel.gwDevId = "your gateway id"
            self.discovery.continueCommissionDevice(deviceModel, typeModel: self.typeModel)
        }

    }

    func matterDeviceAttestation(_ device: UnsafeMutableRawPointer, error: Error) {
        let alertControl = UIAlertController(title: "Attestation", message: "Should Continue?", preferredStyle: .alert)
        let alertAction = UIAlertAction(title: "Continue", style: .default) { _ in
            self.discovery.continueCommissioningDevice(device, ignoreAttestationFailure: true, error: nil)
        }
        let canAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
            self.discovery.continueCommissioningDevice(device, ignoreAttestationFailure: false, error: nil)
        }

        alertControl.addAction(alertAction)
        alertControl.addAction(canAction)

        self.present(alertControl, animated: true)
    }

}

Objective-C


- (void)getToken {
    ThingSmartActivator *wiredActivator = [[ThingSmartActivator alloc] init];
    [wiredActivator getTokenWithHomeId:homeId success:^(NSString *token) {
        NSLog(@"getToken success: %@", token);
        [self startConfigWiFi:token];
    } failure:^(NSError *error) {
        NSLog(@"getToken failure: %@", error.localizedDescription);
    }];
}

- (void)startConfigWiFi:(NSString *)token {
  ThingSmartActivatorTypeMatterModel *matterType  = [[ThingSmartActivatorTypeMatterModel alloc] init];
  matterType.type = ThingSmartActivatorTypeMatter;
  matterType.typeName = NSStringFromThingSmartActivatorType(ThingSmartActivatorTypeMatter);
  matterType.timeout = 120;
  matterType.spaceId = homeId;
  matterType.token = token;

  [self.discovery registerWithActivatorList:@[matterType]];
  [self.discovery setupDelegate:self];
  [self.discovery startSearch:@[matterType]];
}

- (void)activatorService:(id<ThingSmartActivatorSearchProtocol>)service activatorType:(ThingSmartActivatorTypeModel *)type didFindDevice:(ThingSmartActivatorDeviceModel *)device error:(ThingSmartActivatorErrorModel *)errorModel {

    if (errorModel) {
        [self _connectWifiError:errorModel];
        return;
    }

    if (device) {
        [self _handleDevice:device];
    }
}

- (void)_handleDevice:(ThingSmartActivatorDeviceModel *)deviceModel {
     ThingSmartActivatorTypeMatterModel *matterType = (ThingSmartActivatorTypeMatterModel *)[self.discovery activatorTypeModelWith:ThingSmartActivatorTypeMatter];
    [self.discovery startActive:matterType deviceList:@[deviceModel]];
}


- (void)matterRoutingComplete:(ThingSmartMatterRoutingType)routingType {
    self.routingType = routingType;
    if (routingType == ThingSmartMatterRoutingTypeSupport) {

    }
}

-(void)matterCommissioningSessionEstablishmentComplete:(ThingSmartActivatorDeviceModel *)deviceModel {


    if (self.routingType == ThingSmartMatterRoutingTypeThing && deviceModel.deviceModelType == ThingSearchDeviceModelTypeMatterThread) {
        self.matterType..gwDevId = gatewayID;
        [self.discovery continueCommissionDevice:deviceModel typeModel:self.matterType];
    }

    if (self.routingType == ThingSmartMatterRoutingTypeThing && deviceModel.deviceModelType == ThingSearchDeviceModelTypeMatterWifi) {

        self.matterType.ssid = factoryConfig.ssid;
        self.matterType.password = factoryConfig.password;
        [self.discovery continueCommissionDevice:deviceModel typeModel:self.matterType];

    }

}

- (void)activatorService:(id<ThingSmartActivatorActiveProtocol>)service
           activatorType:(ThingSmartActivatorTypeModel *)type
       didReceiveDevices:(nullable NSArray<ThingSmartActivatorDeviceModel *> *)devices
                   error:(nullable ThingSmartActivatorErrorModel *)errorModel {
     if (devices && devices.count > 0) {

     }
}

- (ThingSmartActivatorDiscovery *)discovery {
    if (!_discovery) {
        _discovery = [[ThingSmartActivatorDiscovery alloc] init];
    }
    return _discovery;
}

二维码/手动输入码配网

Swift

class BEMatterTableViewController: UITableViewController {
   /// UI 部分

    private var typeModel: ThingSmartActivatorTypeMatterModel = {
        let type = ThingSmartActivatorTypeMatterModel()
        type.type = ThingSmartActivatorType.matter
        type.typeName = NSStringFromThingSmartActivatorType(ThingSmartActivatorType.matter)
        type.timeout = 180
        return type
    }()

    lazy var discovery: ThingSmartActivatorDiscovery = {
        let discovery = ThingSmartActivatorDiscovery()
        discovery.register(withActivatorList: [self.typeModel])
        discovery.setupDelegate(self)
        discovery.loadConfig()
        return discovery
    }()

    func bindMatter(qrcode codeStr: String?) -> Void {
        let homeId = (Home.current?.homeId)!
        let payload = self.discovery.parseSetupCode(codeStr ?? "")
        let activator = ThingSmartActivator()
        activator.getTokenWithHomeId(homeId, success: { [weak self] (token) in
            guard let self = self else { return }
            self.typeModel.token = token ?? ""
            self.typeModel.spaceId = homeId
            self.discovery.startActive(self.typeModel, deviceList:[payload])
            SVProgressHUD.show(withStatus: NSLocalizedString("Configuring", comment: ""))
        }, failure: { (error) in
            let errorMessage = error?.localizedDescription ?? ""
            SVProgressHUD.showError(withStatus: errorMessage)
        })
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let vc = QRCodeScanerViewController()
        vc.scanCallback = { [weak self] codeStr in
            self?.bindMatter(qrcode: codeStr)
        }
        navigationController?.pushViewController(vc, animated: true)
    }

}

extension BEMatterTableViewController: ThingSmartActivatorActiveDelegate {
    func activatorService(_ service: ThingSmartActivatorActiveProtocol, activatorType type: ThingSmartActivatorTypeModel, didReceiveDevices devices: [ThingSmartActivatorDeviceModel]?, error errorModel: ThingSmartActivatorErrorModel?) {
        if errorModel != nil {
            SVProgressHUD.showError(withStatus: "Bind Failure. (\(errorModel?.error.localizedDescription ?? ""))")
            return
        }

        let device = devices?.first
        isSuccess = true
        SVProgressHUD.show(withStatus: "Bind Success. \n devId: \(device?.uniqueID ?? "") \n name: \(device?.name ?? "")")
    }
}

extension BEMatterTableViewController: ThingSmartActivatorDeviceExpandDelegate {
    func matterDeviceDiscoveryed(_ typeModel: ThingMatterDeviceDiscoveriedType) {
        deviceTypeModel = typeModel
    }

    func matterCommissioningSessionEstablishmentComplete(_ deviceModel: ThingSmartActivatorDeviceModel) {
        if deviceTypeModel?.deviceType == .wifi {
            self.typeModel.ssid = ssid.text ?? ""
            self.typeModel.password = password.text ?? ""
            self.discovery.continueCommissionDevice(deviceModel, typeModel: self.typeModel)
        }

        ///
        if deviceTypeModel?.deviceType == .thread {
            self.typeModel.gwDevId = "your gateway id"
            self.discovery.continueCommissionDevice(deviceModel, typeModel: self.typeModel)
        }

    }

    func matterDeviceAttestation(_ device: UnsafeMutableRawPointer, error: Error) {
        let alertControl = UIAlertController(title: "Attestation", message: "Should Continue?", preferredStyle: .alert)
        let alertAction = UIAlertAction(title: "Continue", style: .default) { _ in
            self.discovery.continueCommissioningDevice(device, ignoreAttestationFailure: true, error: nil)
        }
        let canAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
            self.discovery.continueCommissioningDevice(device, ignoreAttestationFailure: false, error: nil)
        }

        alertControl.addAction(alertAction)
        alertControl.addAction(canAction)

        self.present(alertControl, animated: true)
    }

}

Objective-C


- (void)getToken {
    ThingSmartActivator *wiredActivator = [[ThingSmartActivator alloc] init];
    [wiredActivator getTokenWithHomeId:homeId success:^(NSString *token) {
        NSLog(@"getToken success: %@", token);
        [self startConfigWiFi:token];
    } failure:^(NSError *error) {
        NSLog(@"getToken failure: %@", error.localizedDescription);
    }];
}

- (void)startConfigWiFi:(NSString *)token {
  ThingSmartActivatorTypeMatterModel *matterType  = [[ThingSmartActivatorTypeMatterModel alloc] init];
  matterType.type = ThingSmartActivatorTypeMatter;
  matterType.typeName = NSStringFromThingSmartActivatorType(ThingSmartActivatorTypeMatter);
  matterType.timeout = 120;
  matterType.spaceId = homeId;
  matterType.token = token;

  [self.discovery registerWithActivatorList:@[matterType]];
  [self.discovery setupDelegate:self];

  ThingSmartActivatorDeviceModel *deviceModel = [self.discovery parseSetupCode:code];
  if (deviceModel) {
    [self.discovery startActive:matterType deviceList:@[deviceModel]];
  } else {

  }
}
//Follow-up processing logic is consistent with search activate