Wi-Fi 快连模式

更新时间:2023-09-27 03:32:39下载pdf

Wi-Fi 快连配网又称快连模式(Easy-Connect)、SmartConfig 或 EZ 配网。用户将手机连接到路由器后,利用路由器广播报文与设备进行通信配对。用户操作简单,但对手机和路由器有兼容性要求,成功率低于 热点配网

在 iOS 14.5 及以上版本中,推荐您使用热点模式(即 AP 模式)代替 Wi-Fi 快连模式(即 EZ 模式)。主要原因如下:

  • 相比快连模式,热点模式成功率高、可靠性好,对手机和路由器兼容性要求小。
  • 当 Xcode 升级至 12.5 版本后,编译出来的 App 无法在大于等于 14.5 版本的 iOS 系统的设备上发出快连配网的数据包。此时,App 需要额外开启一个 com.apple.developer.networking.multicast 的权限。该权限需要向苹果额外申请,等审批通过后才能够使用。

配网流程

App业务拓展 SDK云端设备路由器进入配网模式Wi-Fi LED指示灯快闪连接到路由器1请求配网 Token2请求配网 Token3返回配网 Token4返回配网 Token5用户输入路由器密码发送路由器 SSID、密码、配网Token6发送路由器 SSID、密码、配网 Token7指示灯灭连接到路由器8连接成功9指示灯常亮激活设备10设备激活成功11返回设备列表12返回设备列表13App业务拓展 SDK云端设备路由器Wi-Fi 快连模式配网流程

注册配网类型

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

接口说明

/// 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 失败回调,返回失败原因

开始搜索

开始搜索时,需要传入已注册的 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 是否清空当前搜索设备缓存

搜索设备回调

Wi-Fi 快连设备配网成功之后,会通过搜索回调返回设备信息。如果失败,则会返回对应的失败信息。

发现设备回调

接口说明

/// 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 配网类型,此处返回 ThingSmartActivatorTypeEZModel
device 发现设备,返回此次配网的设备模型,失败时返回 nil
errorModel 如果配网失败或者超时,返回此模型,成功时返回 nil

设备信息更新回调

接口说明

/// Device information update or device rediscovered on a different channel
/// @param service Search instance
/// @param type Network configuration type
/// @param device Device model
- (void)activatorService:(id<ThingSmartActivatorSearchProtocol>)service
            activatorType:(ThingSmartActivatorTypeModel *)type
           didUpdateDevice:(ThingSmartActivatorDeviceModel *)device;

参数说明

参数 说明
service 配网服务
type 配网类型,此处返回 ThingSmartActivatorTypeEZModel
device 返回变化的设备信息

错误码说明

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

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

其中 error 对应的错误码,定义在 ThingSmartActivatorDiscoveryError 中。下表展示了常见错误与说明。

错误码 配网错误
ThingSmartActivatorDiscoveryErrorTimeout 配网超时。
ThingSmartActivatorDiscoveryErrorDeviceAlreadyBound 设备强绑定错误。该设备已经被用户绑定,无法被第二个用户绑定,需要第一个用户解绑才能完成配网操作。
ThingSmartActivatorDiscoveryErrorAPPUnsupportProduct 配网账号的 App 和产品没有绑定关系。
ThingSmartActivatorDiscoveryErrorTokenExpired Token 失效。
ThingSmartActivatorDiscoveryErrorGuestNotSupportStrongBind 游客模式无法对强绑定设备进行配网。
ThingSmartActivatorDiscoveryErrorRemoteApiParamIllegal 接口参数不合法。

示例代码

Swift

class EZmodeConfigurationVC: UITableViewController {
    @IBOutlet weak var ssidTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!

    private var token: String = ""
    private var ezType:ThingSmartActivatorTypeEZModel = {
        let type = ThingSmartActivatorTypeEZModel()
        type.type = ThingSmartActivatorType.ezSearch
        type.typeName = NSStringFromThingSmartActivatorType(ThingSmartActivatorType.ezSearch)
        type.timeout = 120
        return type
    }()

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

    private func startConfiguration() {
        guard let homeID = Home.current?.homeId else { return }
        SVProgressHUD.show(withStatus: NSLocalizedString("Requesting for Token", comment: ""))

        ThingSmartActivator.sharedInstance()?.getTokenWithHomeId(homeID, success: { [weak self] (token) in
            guard let self = self else { return }
            self.token = token ?? ""
            self.startConfiguration(with: self.token)
        }, failure: { (error) in
            let errorMessage = error?.localizedDescription ?? ""
            SVProgressHUD.showError(withStatus: errorMessage)
        })
    }

    private func startConfiguration(with token: String) {
        SVProgressHUD.show(withStatus: NSLocalizedString("Configuring", comment: ""))
        guard let homeID = Home.current?.homeId else { return }
        let ssid = ssidTextField.text ?? ""
        let password = passwordTextField.text ?? ""
        ezType.ssid = ssid
        ezType.password = password
        ezType.token = self.token
        ezType.spaceId = homeID
        discovery.startSearch([ezType])
    }

    func stopConfigWifi() {
        if !isSuccess {
            SVProgressHUD.dismiss()
        }
        discovery.stopSearch([self.typeModel], clearCache: true)
        discovery.removeDelegate(self)
    }
}

extension EZmodeConfigurationVC: ThingSmartActivatorSearchDelegate {

    func activatorService(_ service: ThingSmartActivatorSearchProtocol, activatorType type: ThingSmartActivatorTypeModel, didFindDevice device: ThingSmartActivatorDeviceModel?, error errorModel: ThingSmartActivatorErrorModel?) {
        if (errorModel != nil) {
            // Error
            SVProgressHUD.showError(withStatus: errorModel?.error.localizedDescription)
            return
        }

        if (device != nil) {
            if device?.step == ThingActivatorStep.found {
                // device find
            }
        }
    }

    func activatorService(_ service: ThingSmartActivatorSearchProtocol, activatorType type: ThingSmartActivatorTypeModel, didUpdateDevice device: ThingSmartActivatorDeviceModel) {
        if device.step == ThingActivatorStep.intialized {
            // Success
            let name = device.name
            SVProgressHUD.showSuccess(withStatus: NSLocalizedString("Successfully Added \(name)", comment: "Successfully added one device."))
            isSuccess = true
            navigationController?.popViewController(animated: true)
        }
    }

}

Objective-C

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

/// Start config
- (void)startConfigWifi:(NSString *)token {
  ThingSmartActivatorTypeEZModel *ezModel  = [[ThingSmartActivatorTypeEZModel alloc] init];
  ezModel.type = ThingSmartActivatorTypeEZSearch;
  ezModel.typeName = NSStringFromThingSmartActivatorType(ThingSmartActivatorTypeEZSearch);
  ezModel.timeout = 120;
  ezModel.ssid = ssid;
  ezModel.password = password;
  ezModel.token = token;
  ezModel.spaceId = homeId;

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

  [self.discovery startSearch:@[ezModel]];
}

- (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)activatorService:(id<ThingSmartActivatorSearchProtocol>)service activatorType:(ThingSmartActivatorTypeModel *)type didUpdateDevice:(ThingSmartActivatorDeviceModel *)device {
    if (device) {
        [self _handleDevice:device];
    }
}

- (void)_handleDevice:(ThingSmartActivatorDeviceModel *)device {
    ThingActivatorStep step = device.step;

    if (step == ThingActivatorStepFound) {
      // discovery device
    } else if (step == ThingActivatorStepRegisted) {
      // device registe
    } else if (step == ThingActivatorStepIntialized) {
        /// activated successfully
    }
}

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