批量 OTA 升级

更新时间:2024-08-22 03:21:17下载pdf

固件升级是指把新的固件写入芯片中,代替原有的固件的过程。随着市场变化、开发方案升级等原因,涂鸦会定期进行固件更新。同时,涂鸦会根据原厂芯片迭代等情况,对部分旧版本进行停用。另外,设备本身也可以进行功能更新迭代,从而升级固件。

  • 从 v3.35.5 版本 SDK 开始,新增产品 ID(PID)级别升级固件,新的 开始升级 接口支持升级蓝牙设备的固件,并且可以选择多个固件升级。
  • 固件升级相关接口需要是 家庭创建者家庭管理员 才有权限使用,对于 家庭普通成员 仅能使用 家庭普通成员获取设备固件升级信息 中的接口进行获取固件升级信息。

升级流程

查询设备升级信息
下发模组升级指令
模组升级成功
下发设备控制模组升级指令
设备控制模组升级成功

OTA 升级

查询升级信息

使用查询设备升级信息接口,可以查询到 ThingSmartFirmwareUpgradeModel 固件升级模型。其中:

  • upgradeMode 属性表示固件类别。
  • type 属性表示固件通道类型。
  • typeDesc 属性表示固件类型的描述。

固件类别

查询固件列表时,ThingSmartFirmwareUpgradeModel 中有 upgradeMode 字段:

  • 普通固件:字段取值为 0。传统意义上的固件,用于升级设备内固件。

  • PID 版本升级固件:字段取值为 1。引入产品版本的概念,不同版本间的数据能在指定功能模组范围内(即产品 schema 配置、面板配置、多语言配置、快捷控制配置)进行隔离配置。

    需要使用新接口获取固件列表、发起升级。

固件通道类型

作为标识 普通固件 的固件通道,在查询固件列表时,ThingSmartFirmwareUpgradeModeltype 字段为固件通道类型,可以通过typeDesc 来获取到具体的描述。

通常通道 type 含义如下表所示,推荐根据实际情况使用模型返回的 typeDesc 来标识实际固件描述。

普通固件通道 type 固件通道含义
0 主联网模组、Wi-Fi 模组、蓝牙模组
1 蓝牙模组
2 GPRS 模组
3 Zigbee 模组
5 433 模组
6 NB-IoT 模组
9 MCU 模组
10 ~ 19 扩展通道

接口说明

// 获取普通固件及 PID 版本升级固件
- (void)checkFirmwareUpgrade:(void (^)(NSArray<ThingSmartFirmwareUpgradeModel *> *firmwares))success failure:(nullable ThingFailureError)failure;

// 废弃,仅获取普通固件
- (void)getFirmwareUpgradeInfo:(nullable void (^)(NSArray <ThingSmartFirmwareUpgradeModel *> *upgradeModelList))success failure:(nullable ThingFailureError)failure;

参数说明

参数 说明
success 成功回调,设备的固件升级信息列表
failure 失败回调

ThingSmartFirmwareUpgradeModel 数据模型

字段 类型 说明
desc NSString 升级文案
type NSInteger 固件通道类型,普通固件使用
typeDesc NSString 固件通道描述
upgradeStatus NSInteger
  • 0:无新版本
  • 1:有新版本
  • 2:在升级中
  • 5:等待设备唤醒
version NSString 新版本使用的固件版本
currentVersion NSString 当前的固件版本
devType NSInteger
  • 0:普通设备
  • 1:低功耗类型设备
upgradeType NSInteger
  • 0:App 提醒升级
  • 2:App 强制升级
  • 3:检测升级
url NSString 蓝牙设备的升级固件包下载 URL
fileSize NSString 固件包的大小,单位为字节 (byte),fileSize 仅支持蓝牙设备
md5 NSString 固件的 MD5
controlType BOOL
  • YES:升级中可控制
  • NO:升级中不可控制
waitingDesc NSString 固件等待设备唤醒的文案
upgradingDesc NSString 固件升级中的提示文案
canUpgrade NSNumber
  • nil:无前置校验规则,可直接升级
  • 0:有前置校验,不可升级,建议展示 remind 文案
  • 1:有前置校验,可以升级
remind NSString 前置校验规则未通过的提示文案
upgradeMode ThingSmartDeviceUpgradeMode 固件类别,从 3.35.5 版本开始支持
  • 0:普通固件
  • 1:PID 版本升级固件

示例代码

Objective C:

- (void)getFirmwareUpgradeInfo {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];

    [self.device checkFirmwareUpgrade:^(NSArray<ThingSmartFirmwareUpgradeModel *> *upgradeModelList) {
        NSLog(@"getFirmwareUpgradeInfo success");
    } failure:^(NSError *error) {
        NSLog(@"getFirmwareUpgradeInfo failure: %@", error);
    }];
}

Swift:

func getFirmwareUpgradeInfo() {
    device?.checkFirmwareUpgrade({ (upgradeModelList) in
        print("getFirmwareUpgradeInfo success")
    }, failure: { (error) in
        if let e = error {
            print("getFirmwareUpgradeInfo failure: \(e)")
        }
    })
}

查询设备正在升级的固件状态

这里只会返回正在升级的固件。若没有正在升级的固件,则回调 failure

接口说明

- (void)getFirmwareUpgradingStatus:(void (^)(ThingSmartFirmwareUpgradeStatusModel *status))success failure:(nullable ThingFailureError)failure

参数说明

参数 说明
success 成功回调,升级状态模型
failure 失败回调,表示设备没有在升级或查询失败

ThingSmartFirmwareUpgradeStatusModel 数据模型

字段 类型 说明
upgradeStatus ThingSmartDeviceUpgradeStatus 升级状态枚举:
  • 2:在升级中
  • 3:升级成功
  • 4:升级失败
  • 5:等待设备唤醒
  • 6:设备已下载固件
  • 7:升级超时
  • 100:App 正在准备中(例如:正在连接蓝牙设备、App 正在下载固件包)
statusText NSString 状态描述
statusTitle NSString 状态标题
progress NSInteger 进度,部分情况下,参数值可能小于 0,请忽略此种小于 0 的进度
type NSInteger 固件通道类型,在确认升级时需要使用到
upgradeMode ThingSmartDeviceUpgradeMode
  • 0:普通固件
  • 1:PID 版本升级,从 3.35.5 版本 SDK 开始支持
error NSError 失败信息

示例代码

Objective C:

- (void)getFirmwareUpgradeInfo {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];

    [self.device getFirmwareUpgradingStatus:^(ThingSmartFirmwareUpgradeStatusModel *status) {
        NSLog(@"getFirmwareUpgradingStatus success");
    } failure:^(NSError *error) {
        NSLog(@"getFirmwareUpgradingStatus failure: %@", error);
    }];
}

Swift:

func getFirmwareUpgradeInfo() {
    device?.getFirmwareUpgradingStatus({ (status) in
        print("getFirmwareUpgradingStatus success")
    }, failure: { (error) in
        if let e = error {
            print("getFirmwareUpgradingStatus failure: \(e)")
        }
    })
}

开始升级

接口说明

// 支持普通固件以及 PID 版本升级固件
// 在原本的基础上支持单点蓝牙、蓝牙 Mesh、动态网关等设备的升级
// 同一设备的多个固件可以依次升级
// firmewares: 需要升级的固件,通过接口 checkFirmwareUpgrade 获取
- (void)startFirmwareUpgrade:(NSArray<ThingSmartFirmwareUpgradeModel *> *)firmwares

// 废弃,推荐使用通过 - startFirmwareUpgrade: 发起升级
// 仅支持普通固件,仅支持有 Wi-Fi 能力的设备以及 Zigbee 子设备,无法支持单点蓝牙、蓝牙 Mesh、动态网关等设备
// 仅能发起单个固件升级
// type:需要升级的类型,从设备升级信息接口 getFirmwareUpgradeInfo 获取
- (void)upgradeFirmware:(NSInteger)type success:(nullable ThingSuccessHandler)success failure:(nullable ThingFailureError)failure;

参数说明

  • -startFirmwareUpgrade: 接口参数说明

    参数 说明
    firmwares 从设备升级信息接口 getFirmwareUpgradeInfo 获取的固件列表
  • -upgradeFirmware:success:failure: 接口参数说明

    参数 说明
    type type: 需要升级的类型,从设备升级信息接口 getFirmwareUpgradeInfo 获取
    success 成功回调
    failure 失败回调
  • 通过设备升级信息接口 getFirmwareUpgradeInfo 获取的信息列表中会包含下列状态信息:

    • 无新版本(upgradeStatus: 0)

    • 有新版本(upgradeStatus: 1)

    • 在升级中(upgradeStatus: 2)

    • 等待设备唤醒(upgradeStatus: 5)

      上述状态信息用于展示升级相关状态。仅 有新版本(upgradeStatus: 1)状态时,可以发起升级。请在使用做好相关过滤和状态展示。

  • 部分设备有前置校验限制。通过 getFirmwareUpgradeInfo 获取信息列表,采用其中任何一个升级信息模型 ThingSmartFirmwareUpgradeModel 对象中的 canUpgrade 进行判断。

    判断说明 判断依据
    无需前置校验,可以升级 canUpgrade == nil
    有前置校验,且校验未通过,无法升级 canUpgrade != nil && canUpgrade.integerValue == 0
    有前置校验,且校验通过,可以升级 canUpgrade != nil && canUpgrade.integerValue == 1

    若有前置校验,且校验未通过,无法升级的,可以通过 remind 获取不可升级的说明文案。

示例代码

Objective C:

- (void)upgradeFirmwareNew {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];

    // firmwares:从获取设备升级信息接口 checkFirmwareUpgrade 返回的固件列表
  // 注意,根据上述注意事项过滤掉不符合规则的固件
    [self.device startFirmwareUpgrade:firmwares];
}

Swift:

func upgradeFirmware() {
      // firmwares:从获取设备升级信息接口 getFirmwareUpgradeInfo 返回的固件列表
      // 注意,根据上述注意事项过滤掉不符合规则的固件
    device?.startFirmwareUpgrade(firmwares);
}

继续升级

目前仅支持在升级中收到进度回调时,error.codeThingOTAErrorCodeSignalStrengthNotSatisfy 的场景。

在升级 Wi-Fi 和双模类设备时,为保证升级通畅,通常会设置一个最低的信号强度。当发起升级时,会检查设备的信号强度是否符合。不符合,则会回调进度状态 error.code = ThingOTAErrorCodeSignalStrengthNotSatisfy。此时,可以通过 UI 交互,让用户做选择。例如,弹窗提示 设备信号弱,升级可能失败,是否继续升级? 等文案。

接口说明

- (void)confirmWarningUpgradeTask:(BOOL)isContinue;

参数说明

参数 说明
isContinue 是否继续升级

仅通过 -startFirmwareUpgrade: 发起的升级,才有此流程。

示例代码

Objective C:

- (void)continueFirmware {
    // isContinue 由用户选择是否继续
    [self.device confirmWarningUpgradeTask:isContinue];
}

Swift:

func continueFirmware() {
    // isContinue 由用户选择是否继续
    device?.confirmWarningUpgradeTask(isContinue)
}

取消升级

目前仅支持低功耗类型的设备。此类设备在发起升级后,可能处于 待唤醒状态upgradeStatus = 5)。通过此方法,可以取消待唤醒的升级任务。

接口说明

// 目前仅支持取消低功耗类型的设备
- (void)cancelFirmwareUpgrade:(ThingSuccessHandler)success
                      failure:(nullable ThingFailureError)failure;

// 方法废弃,推荐使用 -cancelFirmwareUpgrade:failure:
// type:从设备升级信息接口 getFirmwareUpgradeInfo 获取
- (void)cancelUpgradeFirmware:(NSInteger)type success:(nullable ThingSuccessHandler)success failure:(nullable ThingFailureError)failure;

参数说明

参数 说明
success 成功回调
failure 失败回调

取消待唤醒的升级,仅适用于 upgradeStatus5(等待设备唤醒)的设备。

示例代码

Objective C:

- (void)cancelFirmware {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    // 仅适用于 upgradeStatus 为 5(等待设备唤醒)的设备
    [self.device cancelFirmwareUpgrade:^{
        NSLog(@"cancel success");
    } failure:^(NSError *error) {
        NSLog(@"cancel failure: %@", error);
    }];
}

Swift:

func cancelFirmware() {
    // 仅适用于 upgradeStatus 为 5(等待设备唤醒)的设备
    device?.cancelFirmwareUpgrade(success: {
        print("cancel success")
    }, failure: { (error) in
        if let e = error {
            print("cancel failure: \(e)")
        }
    })
}

回调监听

通过 -startFirmwareUpgrade: 发起的升级,推荐使用 -device:otaUpdateStatusChanged: 回调。此回调包含状态变化、进度更新、失败原因。其他废弃回调方法,也依然会回调。为了防止重复的回调监听,建议将相关逻辑迁移至此回调。

示例代码

Objective C:

- (void)device:(ThingSmartDevice *)device otaUpdateStatusChanged:(ThingSmartFirmwareUpgradeStatusModel *)statusModel {
        // 固件升级状态和升级进度回调。
        // 推荐与 -startFirmwareUpgrade: 配套使用
}

- (void)device:(ThingSmartDevice *)device firmwareUpgradeStatusModel:(ThingSmartFirmwareUpgradeStatusModel *)upgradeStatusModel {
    // 固件升级状态回调。
    // 推荐与 -upgradeFirmware:success:failure: 配套使用
}

- (void)deviceFirmwareUpgradeSuccess:(ThingSmartDevice *)device type:(NSInteger)type {
    // 固件升级成功。方法废弃,推荐使用 - device:firmwareUpgradeStatusModel:
}

- (void)deviceFirmwareUpgradeFailure:(ThingSmartDevice *)device type:(NSInteger)type {
    // 固件升级失败。方法废弃,推荐使用 - device:firmwareUpgradeStatusModel:
}

- (void)device:(ThingSmartDevice *)device firmwareUpgradeProgress:(NSInteger)type progress:(double)progress {
    // 固件升级的进度。
    // 推荐与 -upgradeFirmware:success:failure: 配套使用
}

Swift:

func device(_ device: ThingSmartDevice, firmwareUpgradeStatusModel upgradeStatusModel: ThingSmartFirmwareUpgradeStatusModel) {
    //设备升级状态变更
}

func deviceFirmwareUpgradeSuccess(_ device: ThingSmartDevice!, type: Int) {
    //固件升级成功,即将弃用,推荐使用 device(_:, firmwareUpgradeStatusModel:)
}

func deviceFirmwareUpgradeFailure(_ device: ThingSmartDevice!, type: Int) {
    //固件升级失败,即将弃用,推荐使用 device(_:, firmwareUpgradeStatusModel:)
}

func device(_ device: ThingSmartDevice!, firmwareUpgradeProgress type: Int, progress: Double) {
    //固件升级的进度
}

自动保持最新版本

部分设备支持自动保持最新版本。通过 ThingSmartDeviceModel - supportAuto 来判断设备是否支持该功能。

接口说明

// 获取开关状态
- (void)getAutoUpgradeSwitchInfoWithSuccess:(nullable ThingSuccessID)success
                                    failure:(nullable ThingFailureError)failure;

// 保存开关状态
- (void)saveUpgradeInfoWithSwitchValue:(NSInteger)switchValue
                               success:(nullable ThingSuccessHandler)success
                               failure:(nullable ThingFailureError)failure;

参数说明

参数 说明
switchValue 功能状态:
  • 0:关闭
  • 1:开启
success 成功回调
failure 失败回调

示例代码

Objective C:

- (void)getAutoUpgradeSwitchInfo {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    // 注意需先通过 ThingSmartDeviceModel - supportAuto 来判断是否设备支持

    [self.device getAutoUpgradeSwitchInfoWithSuccess:^(NSNumber *status) {
        // 0:关闭, 1:开启。此处样例为:关闭
        NSLog(@"current switch status: %@", status);
    } failure:^(NSError *error) {
        NSLog(@"get current switch status fail. %@", error);
    }];
}

- (void)saveAutoUpgradeSwitchInfo {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    // 注意,需先通过 ThingSmartDeviceModel - supportAuto 来判断设备是否支持

    NSInteger status = 0; // 0:关闭, 1:开启。此处样例为:关闭
    [self.device saveUpgradeInfoWithSwitchValue:status success:^{
        NSLog(@"save auto switch status success.");
    } failure:^(NSError *error) {
        NSLog(@"save auto switch status fail. %@", error);
    }];
}

Swift:

func getAutoUpgradeSwitchInfo() {
    // 注意,需先通过 ThingSmartDeviceModel - supportAuto 来判断设备是否支持
    device?.getAutoUpgradeSwitchInfo(success: { status in
        // 0:关闭, 1:开启。
        if let value = status {
            print("get current status: \(value)")
        }
    }, failure: { error in
        if let e = error {
            print("get status failure: \(e)")
        }
    })
}

func saveAutoUpgradeSwitchInfo() {
    // 注意,需先通过 ThingSmartDeviceModel - supportAuto 来判断设备是否支持
    let status = 0 // 0:关闭, 1:开启。此处样例为:关闭
    device?.saveUpgradeInfo(withSwitchValue: 0, success: {
        print("save success")
    }, failure: { error in
        if let e = error {
            print("success failure: \(e)")
        }
    })
}

家庭普通成员获取设备固件升级信息

此接口从 5.1.0 版本 SDK 开始提供。

接口说明

- (void)memberCheckFirmwareStatus:(void (^)(NSArray<ThingSmartMemberCheckFirmwareInfo *> *infos))success
                          failure:(ThingFailureError)failure;

参数说明

参数 说明
success 成功回调
failure 失败回调

ThingSmartMemberCheckFirmwareInfo 数据模型

字段 类型 说明
type NSInteger 固件通道类型
upgradeStatus NSInteger
  • 0:无新版本
  • 1:有新版本
  • 2:在升级中
  • 5:等待设备唤醒
version NSString 新版本使用的固件版本
upgradeText NSString 固件升级信息

示例代码

Objective C:

- (void)memberCheckFirmwareStatus {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    [self.device memberCheckFirmwareStatus:^(NSArray<ThingSmartMemberCheckFirmwareInfo *> * _Nonnull infos) {
        NSLog(@"member check success");
    } failure:^(NSError *error) {
        NSLog(@"member check failure");
    }];
}

Swift:

func memberCheckFirmwareStatus() {
    device?.memberCheckFirmwareStatus(success: { infos in
            print("member check success: \(infos)")
    }, failure: { error in
        if let e = error {
            print("member check failure: \(e)")
        }
    })
}

错误码

ThingSmartFirmwareUpgradeStatusModel 中的 error 属性 的 code,仅在通过新升级接口时可用。

错误码 说明 备注
5000 设备所有固件已是最新,无需升级 -
5001 未检查到固件 -
5002 蓝牙类设备同时只能有一个设备在升级 通常为蓝牙单点、涂鸦 Mesh 子设备、蓝牙 Mesh 子设备
5003 App 下载固件包失败 -
5004 获取是否需要信号强度检测失败 -
5005 设备信号强度不满足升级条件 建议询问用户是否继续,继续可能会失败,可通过接口 - confirmWarningUpgradeTask: 继续或者取消
5006 连接设备失败 通常为蓝牙单点、涂鸦 Mesh 子设备、蓝牙 Mesh 子设备
5007 将蓝牙单点设备从网关下解绑超时 -
5009 App 下载固件 MD5 校验不通过 -
5010 App 发送固件失败 通常为蓝牙单点、涂鸦 Mesh 子设备、蓝牙 Mesh 子设备
5012 设备不在线 -
5013 升级前置校验不通过 -
5014 手机蓝牙未打开 -
5015 可升级固件列表接口请求失败 -
5016 设备升级超时 -
5017 调用发起 OTA 升级接口失败 服务端返回接口调用错误,详细错误码通过 error.localizedFailureReason 获取,详细描述通过 error.localizedDescription 获取
5018 设备升级中失败,设备上报失败原因 详细描述通过 error.localizedDescription 获取
5099 通用错误码 -

新老接口对应关系

功能 新接口 老接口
查询升级信息 checkFirmwareUpgrade:failure: getFirmwareUpgradeInfo:failure:
查询设备正在升级的固件状态 getFirmwareUpgradingStatus:failure:
开始升级 startFirmwareUpgrade: upgradeFirmware:success:failure:
取消升级 cancelFirmwareUpgrade:failure: cancelUpgradeFirmware:succes:failure:
回调监听 device:otaUpdateStatusChanged: device:firmwareUpgradeStatusModel:
deviceFirmwareUpgradeSuccess:type:
deviceFirmwareUpgradeFailure:type:
device:firmwareUpgradeProgress:

批量 OTA 升级

source 'https://github.com/tuya/tuya-pod-specs.git'
platform :ios, '11.0'

target 'Your_Project_Name' do
    pod "ThingSmartBusinessExtensionKit"
end

API 说明

模型

设备固件信息

open class ThingUpgradeListInfo : NSObject {
    open var devId: String
    open var icon: String
    open var name: String
    open var upgradeList: [ThingSmartFirmwareUpgradeModel]
}

批量 OTA 管理类

open class ThingUpgradeListManager : NSObject {
    ...
}

批量 OTA 管理接口

查询家庭下需要进行 OTA 升级的设备及固件信息。

入参 类型 说明
familyId Int64 家庭 ID
success [ThingUpgradeListInfo] 设备固件信息
failure error 错误信息

返回值:true 表示支持,false 表示不支持。

func getUpgradeDevices(
    inFamily familyId: Int64,
    success: @escaping ([ThingUpgradeListInfo]) -> Void,
    failure: @escaping (Error) -> Void
)

使用示例

更多信息,参考 Demo

import UIKit

class BatchOtaVC: UITableViewController {

    var home: ThingSmartHome

    init(home: ThingSmartHome) {
        self.home = home
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationController?.navigationBar.prefersLargeTitles = false

        if #available(iOS 13.0, *) {
            let appearance = UINavigationBarAppearance()
            appearance.configureWithOpaqueBackground()
            appearance.backgroundColor = UIColor.white
            self.navigationController?.navigationBar.standardAppearance = appearance;
            self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance
        } else {
            // Fallback on earlier versions
        }

        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        self.title = "batch ota"

        self.loadData()
    }

    var infos: [ThingUpgradeListInfo] = []
    var devices: [String: ThingSmartDevice] = [:]
    var status: [String: String] = [:]

    func loadData() {
        SVProgressHUD.show()
        ThingUpgradeListManager.sharedInstance().getUpgradeDevices(inFamily: self.home.homeId) { infos in
            SVProgressHUD.dismiss()
            self.infos = infos

            infos.forEach { info in
                let device = ThingSmartDevice(deviceId: info.devId)
                device?.delegate = self
                if (device != nil) {
                    self.devices.updateValue(device!, forKey: info.devId)
                }

                let upgradeInfo = ThingUpgradeInfo(firmwares: info.upgradeList)
                if (upgradeInfo.matchUpgradeCondition == false) {
                    self.status.updateValue("can't grade", forKey: info.devId)
                }else if (upgradeInfo.isUpgrading == false) {
                    self.status.updateValue("need upgrade", forKey: info.devId)
                }else{
                    self.status.updateValue("upgrading", forKey: info.devId)
                }
            }

            self.tableView.reloadData()
        } failure: { error in
            SVProgressHUD.dismiss()
            SVProgressHUD.showError(withStatus: "error")
        }
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.infos.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .value1, reuseIdentifier: "cell")
        let info = self.infos[indexPath.row]
        cell.textLabel?.text = info.name

        cell.detailTextLabel?.text = self.status[info.devId]
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let info = self.infos[indexPath.row]

        let upgradeInfo = ThingUpgradeInfo(firmwares: info.upgradeList)
        if (upgradeInfo.matchUpgradeCondition == false) {return}

        if (self.status[info.devId] != nil && self.status[info.devId]! == "upgrading") {return}

        let device = self.devices[info.devId]
        device?.startFirmwareUpgrade(info.upgradeList)
    }

}

extension BatchOtaVC: ThingSmartDeviceDelegate {
    func device(_ device: ThingSmartDevice, otaUpdateStatusChanged statusModel: ThingSmartFirmwareUpgradeStatusModel) {
        if (statusModel.upgradeStatus == ThingSmartDeviceUpgradeStatusTimeout || statusModel.upgradeStatus == ThingSmartDeviceUpgradeStatusFailure) {
            self.status.updateValue("fail", forKey: device.deviceModel.devId)
        }else{
            self.status.updateValue("upgrading", forKey: device.deviceModel.devId)
        }
        self.tableView.reloadData()
    }

}

展示如何实现简单的批量 OTA 升级功能。

实现一个界面

首先,简单实现一个界面。

  1. 界面初始化的时候,传入当前的家庭的 homeId

  2. 界面会展示一个 UITableView,此时 UITableView 的分组数和行数都为 0。另外,将会以 value1 的样式来创建 cell

    import UIKit
    
    class BatchOtaVC: UITableViewController {
    
        var home: ThingSmartHome
    
        init(home: ThingSmartHome) {
            self.home = home
            super.init(nibName: nil, bundle: nil)
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.navigationController?.navigationBar.prefersLargeTitles = false
    
            if #available(iOS 13.0, *) {
                let appearance = UINavigationBarAppearance()
                appearance.configureWithOpaqueBackground()
                appearance.backgroundColor = UIColor.white
                self.navigationController?.navigationBar.standardAppearance = appearance;
                self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance
            } else {
                // Fallback on earlier versions
            }
    
            self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
            self.title = "batch ota"
        }
    
        override func numberOfSections(in tableView: UITableView) -> Int {
            return 0
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 0
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = UITableViewCell(style: .value1, reuseIdentifier: "cell")
            return cell
        }
    
        override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            tableView.deselectRow(at: indexPath, animated: true)
        }
    
    }
    

加载需要升级的设备

接下来,开始加载当前家庭下需要升级的设备。实现一个 loadData 方法,在其中调用 ThingUpgradeListManagergetUpgradeDevices(inFamily:success:failure:) 方法。

import UIKit

class BatchOtaVC: UITableViewController {

    override func viewDidLoad() {
        ...
        self.loadData()
    }

    func loadData() {
        SVProgressHUD.show()
        ThingUpgradeListManager.sharedInstance().getUpgradeDevices(inFamily: self.home.homeId) { infos in
            SVProgressHUD.dismiss()

        } failure: { error in
            SVProgressHUD.dismiss()
            SVProgressHUD.showError(withStatus: "error")
        }
    }
}

存储设备的固件信息

获取到当前家庭下需要 OTA 的设备的固件信息。用 infos 来存储需要 OTA 的设备的固件信息,同时根据 infos 来渲染 tableview

import UIKit

class BatchOtaVC: UITableViewController {

    var infos: [ThingUpgradeListInfo] = []

    func loadData() {
        SVProgressHUD.show()
        ThingUpgradeListManager.sharedInstance().getUpgradeDevices(inFamily: self.home.homeId) { infos in
            SVProgressHUD.dismiss()
            self.infos = infos

            self.tableView.reloadData()
        } failure: { error in
            SVProgressHUD.dismiss()
            SVProgressHUD.showError(withStatus: "error")
        }
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.infos.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .value1, reuseIdentifier: "cell")
        let info = self.infos[indexPath.row]
        cell.textLabel?.text = info.name
        return cell
    }
}

存储需要 OTA 的设备列表

现在,已经展示出了需要 OTA 的设备列表。要对设备进行 OTA,需要用到 ThingSmartDevice。因此,创建一个 devices 字典来存储这些设备,并监听设备的回调。

import UIKit

class BatchOtaVC: UITableViewController {

    var devices: [String: ThingSmartDevice] = [:]

    func loadData() {
        SVProgressHUD.show()
        ThingUpgradeListManager.sharedInstance().getUpgradeDevices(inFamily: self.home.homeId) { infos in
            SVProgressHUD.dismiss()
            self.infos = infos

            infos.forEach { info in
                let device = ThingSmartDevice(deviceId: info.devId)
                device?.delegate = self
                if (device != nil) {
                    self.devices.updateValue(device!, forKey: info.devId)
                }
            }

            self.tableView.reloadData()
        } failure: { error in
            SVProgressHUD.dismiss()
            SVProgressHUD.showError(withStatus: "error")
        }
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let info = self.infos[indexPath.row]

        let device = self.devices[info.devId]
        device?.startFirmwareUpgrade(info.upgradeList)
    }

}

extension BatchOtaVC: ThingSmartDeviceDelegate {
    func device(_ device: ThingSmartDevice, otaUpdateStatusChanged statusModel: ThingSmartFirmwareUpgradeStatusModel) {

    }

}

开始升级和维护状态

好了,现在单击 tableview 上的设备就可以进行 OTA 了。但是并不是需要 OTA 的设备就可以进行升级,可能设备当前不满足升级条件,例如电量。也可能是因为设备当前正在升级中。因此,可以用一个 status 来维护这些状态。

import UIKit

class BatchOtaVC: UITableViewController {

    var status: [String: String] = [:]

    func loadData() {
        SVProgressHUD.show()
        ThingUpgradeListManager.sharedInstance().getUpgradeDevices(inFamily: self.home.homeId) { infos in
            SVProgressHUD.dismiss()
            self.infos = infos

            infos.forEach { info in
                ...

                let upgradeInfo = ThingUpgradeInfo(firmwares: info.upgradeList)
                if (upgradeInfo.matchUpgradeCondition == false) {
                    self.status.updateValue("can't grade", forKey: info.devId)
                }else if (upgradeInfo.isUpgrading == false) {
                    self.status.updateValue("need upgrade", forKey: info.devId)
                }else{
                    self.status.updateValue("upgrading", forKey: info.devId)
                }
            }

            self.tableView.reloadData()
        } failure: { error in
            SVProgressHUD.dismiss()
            SVProgressHUD.showError(withStatus: "error")
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        ...
        cell.detailTextLabel?.text = self.status[info.devId]
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        ...
        let upgradeInfo = ThingUpgradeInfo(firmwares: info.upgradeList)
        if (upgradeInfo.matchUpgradeCondition == false) {return}
        if (self.status[info.devId] != nil && self.status[info.devId]! == "upgrading") {return}

        let device = self.devices[info.devId]
        device?.startFirmwareUpgrade(info.upgradeList)
    }

}

extension BatchOtaVC: ThingSmartDeviceDelegate {
    func device(_ device: ThingSmartDevice, otaUpdateStatusChanged statusModel: ThingSmartFirmwareUpgradeStatusModel) {
        if (statusModel.upgradeStatus == ThingSmartDeviceUpgradeStatusTimeout || statusModel.upgradeStatus == ThingSmartDeviceUpgradeStatusFailure) {
            self.status.updateValue("fail", forKey: device.deviceModel.devId)
        }else{
            self.status.updateValue("upgrading", forKey: device.deviceModel.devId)
        }
        self.tableView.reloadData()
    }

}

获取家庭下需升级设备的 OTA 信息所返回的数据并没有包含蓝牙本地的 OTA 状态。也就是说,如果您的蓝牙设备在进行 OTA 升级时状态已经变成 升级中 了,但是此时您调 ThingUpgradeListManager.getUpgradeDevices(inFamily:success:failure:) 接口会发现,返回的状态是 待升级

因此,建议在蓝牙设备进行 OTA 升级的时候,不要关闭升级界面,以避免界面重入时状态不一致的问题。

Demo

更多信息,参考 Demo