Bulk OTA Updates

Last Updated on : 2024-08-22 03:22:37download

Firmware update is the process to flash new firmware to a chip and update the earlier firmware. Tuya supports regular firmware updates to meet your development and market requirements. Tuya also deactivates certain legacy firmware versions in line with chip iterations of the manufacturers. In the case of device feature updates, a firmware update is also required.

  • Starting from App SDK v3.35.5, you can update firmware that matches a specified product ID (PID). The new API method for starting firmware update supports Bluetooth devices, and multiple firmware versions can be selected in the same update task.
  • Only home creators and home administrators are authorized to update firmware. Common home members can only get information about the firmware update based on the API method memberCheckFirmwareStatus.

Update process

Query device
update information
Send command
to update module
Module updated successfully
Send command to
update device control module
Device control module
updated successfully

Single Device OTA Upgrade

Get firmware update information

Returns the device update information. The firmware update model ThingSmartFirmwareUpgradeModel provides the following properties:

  • upgradeMode: indicates the firmware update mode.
  • type: indicates the firmware update channel type.
  • typeDesc: describes the firmware type.

Firmware update modes

When you query the list of firmware updates, the upgradeMode field of ThingSmartFirmwareUpgradeModel includes the following valid values:

  • Generic firmware update: The field value is 0. This is the traditional mode to update device firmware.

  • Update PID-specific firmware: The field value is 1. This update mode applies the philosophy of products. Update versions can be differentiated by functional modules, such as product schema, panel settings, multilingual settings, and shortcut control.

    The new API methods are required to get the firmware list and trigger a PID-specific firmware update.

Firmware update channel types

For the generic firmware update mode, when you query a list of firmware updates, the type field of ThingSmartFirmwareUpgradeModel indicates the type of firmware update channel and typeDesc describes each channel.

The following table lists descriptions of type for a generic firmware update. We recommend that you describe the firmware by using the value of typeDesc that is actually returned by the model.

Channel type of generic firmware update Description of firmware update channel
0 Main module, Wi-Fi module, and Bluetooth module.
1 Bluetooth module
2 GPRS module
3 Zigbee module
5 433 MHz module
6 NB-IoT module
9 MCU module
10 to 19 Extension module

API description

// Returns firmware information required by generic update and PID-specific update.
- (void)checkFirmwareUpgrade:(void (^)(NSArray<ThingSmartFirmwareUpgradeModel *> *firmwares))success failure:(nullable ThingFailureError)failure;

// (Deprecated) Returns only the firmware information required by generic update.
- (void)getFirmwareUpgradeInfo:(nullable void (^)(NSArray <ThingSmartFirmwareUpgradeModel *> *upgradeModelList))success failure:(nullable ThingFailureError)failure;

Parameters

Parameter Description
success The success callback. A list of firmware update information is returned.
failure The failure callback.

Data model of ThingSmartFirmwareUpgradeModel

Field Type Description
desc NSString The description of the update.
type NSInteger The type of firmware update channel. The value is used for a generic firmware update.
typeDesc NSString The description of the firmware update channel.
upgradeStatus NSInteger
  • 0: no update available
  • 1: update available
  • 2: updating
  • 5: wait for the device to wake up
version NSString The target firmware version.
currentVersion NSString The current firmware version.
devType NSInteger
  • 0: generic type of device
  • 1: low-power device
upgradeType NSInteger
  • 0: app notification of update
  • 2: forced app update
  • 3: check for update
url NSString The URL at which the firmware update package of the Bluetooth device can be downloaded.
fileSize NSString The size of the firmware update package. Unit: bytes. FileSize only supports Bluetooth devices.
md5 NSString The MD5 (Message-Digest algorithm 5) hash value of the firmware.
controlType BOOL
  • YES: controllable during the update
  • NO: non-controllable during the update
waitingDesc NSString The description of waiting for the device to wake up.
upgradingDesc NSString The description of the firmware update in progress.
canUpgrade NSNumber
  • nil: runs the update task without a pre-validation.
  • 0: If a pre-validation is required but not passed, an update is not allowed. Therefore, remind can be used to return the description that an update is not allowed.
  • 1: pre-validation is passed and update is allowed.
remind NSString The description of a failed pre-validation.
upgradeMode ThingSmartDeviceUpgradeMode The type of firmware update. Valid values:
  • 0: generic firmware update
  • 1: PID-specific firmware update, available starting from v3.35.5

Example

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)")
        }
    })
}

Query status of firmware being updated

Returns information about the firmware being updated. If no firmware is being updated, a failure is returned.

API description

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

Parameters

Parameter Description
success The success callback. An update status model is returned.
failure The failure callback. An error message is returned to indicate that the device is not being updated or the API request failed.

Data model of ThingSmartFirmwareUpgradeStatusModel

Field Type Description
upgradeStatus ThingSmartDeviceUpgradeStatus The update status. Valid values:
  • 2: updating
  • 3: updated successfully
  • 4: update failed
  • 5: wait for the device to wake up
  • 6: firmware downloaded to the device
  • 7: update timeout
  • 100: the app is preparing, for example, connecting to a Bluetooth device, or downloading the firmware package
statusText NSString The description of the firmware status.
statusTitle NSString The title of the firmware status.
progress NSInteger The update progress. In certain conditions, the value might be less than 0. If any, ignore this type of value.
type NSInteger The type of firmware update channel. The value is used when users confirm the update.
upgradeMode ThingSmartDeviceUpgradeMode
  • 0: generic firmware update
  • 1: PID-specific firmware update, available starting from v3.35.5
error NSError The error message.

Example

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)")
        }
    })
}

Start update

API description

// Both generic update and PID-specific update are supported.
// Based on the original update capabilities, Bluetooth LE devices, Bluetooth mesh devices, and dynamic gateways are also supported.
// Multiple firmware versions can be sequentially updated for the same device.
// firmewares: The firmware to be updated. Get it through checkFirmwareUpgrade.
- (void)startFirmwareUpgrade:(NSArray<ThingSmartFirmwareUpgradeModel *> *)firmwares

// Deprecated. - startFirmwareUpgrade: is recommended to initiate an update.
// Only generic firmware update is supported. Only Wi-Fi devices and Zigbee sub-devices are supported. Bluetooth LE devices, Bluetooth mesh devices, and dynamic gateways are not supported.
// Only a single firmware update task can be initiated.
// type: the type of update. You can call getFirmwareUpgradeInfo to get the value.
- (void)upgradeFirmware:(NSInteger)type success:(nullable ThingSuccessHandler)success failure:(nullable ThingFailureError)failure;

Parameters

  • -startFirmwareUpgrade

    Parameter Description
    firmwares You can call getFirmwareUpgradeInfo to get a list of firmware versions.
  • -upgradeFirmware:success:failure:

    Parameter Description
    type The type of update. You can call getFirmwareUpgradeInfo to get the value.
    success The success callback.
    failure The failure callback.
  • The list returned by getFirmwareUpgradeInfo includes the following status information:

    • No new version (upgradeStatus: 0)

    • New version available (upgradeStatus: 1)

    • Updating (upgradeStatus: 2)

    • Wait for the device to wake up (upgradeStatus: 5)

      The status information is displayed to indicate the update status. An update can be initiated only when a new version is available (upgradeStatus: 1). You need to implement the features to filter and display the status information on your own.

  • Certain devices require a pre-validation before an update. Call getFirmwareUpgradeInfo to get a list of firmware information. Check the value of canUpgrade in the object of the update information model ThingSmartFirmwareUpgradeModel.

    Value result Value
    Pre-validation is not required and update is allowed. canUpgrade == nil
    Pre-validation is required and failed, so update is not allowed. canUpgrade != nil && canUpgrade.integerValue == 0
    Pre-validation is required and passed, so update is allowed. canUpgrade != nil && canUpgrade.integerValue == 1

    If a pre-validation is required and an update is allowed only after the pre-validation is passed, remind can be used to return the description that an update is not allowed.

Example

Objective-C:

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

    // firmwares: the list of firmware information returned by checkFirmwareUpgrade.
  // Note that the firmware versions that do not meet the preceding rules must be filtered out.
    [self.device startFirmwareUpgrade:firmwares];
}

Swift:

func upgradeFirmware() {
      // firmwares: the list of firmware information returned by getFirmwareUpgradeInfo.
      // Note that the firmware versions that do not meet the preceding rules must be filtered out.
    device?.startFirmwareUpgrade(firmwares);
}

Continue with update

Currently, this API method applies only when the callback for the update progress is returned and when error.code is ThingOTAErrorCodeSignalStrengthNotSatisfy.

For Wi-Fi devices and combo devices, to ensure a successful update, the minimum signal strength level is specified. When an update is triggered, the system checks whether the device signal strength is within an acceptable range. If not, the callback for the update progress is returned, including error.code = ThingOTAErrorCodeSignalStrengthNotSatisfy. Then, you can implement a UI interaction for users to make decisions. For example, a dialog box appears, saying “The device signal is weak, and the update might fail. Continue to update?”

API description

- (void)confirmWarningUpgradeTask:(BOOL)isContinue;

Parameters

Parameter Description
isContinue Indicates whether to continue the update task.

This process applies to only the update that is initiated by -startFirmwareUpgrade:.

Example

Objective-C:

- (void)continueFirmware {
    // isContinue: asks users whether to continue the update.
    [self.device confirmWarningUpgradeTask:isContinue];
}

Swift:

func continueFirmware() {
    // isContinue: asks users whether to continue the update.
    device?.confirmWarningUpgradeTask(isContinue)
}

Cancel update

Currently, only low-power devices are supported. After an update is initiated for this type of device, the update task might get into the status of waiting for the device to wake up (upgradeStatus = 5). This API method can be called to cancel the update task in this status.

API description

// Currently, an update task can be canceled only for low-power devices.
- (void)cancelFirmwareUpgrade:(ThingSuccessHandler)success
                      failure:(nullable ThingFailureError)failure;

// Deprecated. -cancelFirmwareUpgrade:failure: is recommended.
// type: the type of update. You can call getFirmwareUpgradeInfo to get the value.
- (void)cancelUpgradeFirmware:(NSInteger)type success:(nullable ThingSuccessHandler)success failure:(nullable ThingFailureError)failure;

Parameters

Parameter Description
success The success callback.
failure The failure callback.

This API method applies only in the status of waiting for the device to wake up. upgradeStatus is 5 to indicate this status.

Example

Objective-C:

- (void)cancelFirmware {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    // This API method applies only in the status of waiting for the device to wake up. upgradeStatus is 5 to indicate this status.
    [self.device cancelFirmwareUpgrade:^{
        NSLog(@"cancel success");
    } failure:^(NSError *error) {
        NSLog(@"cancel failure: %@", error);
    }];
}

Swift:

func cancelFirmware() {
    // This API method applies only in the status of waiting for the device to wake up. upgradeStatus is 5 to indicate this status.
    device?.cancelFirmwareUpgrade(success: {
        print("cancel success")
    }, failure: { (error) in
        if let e = error {
            print("cancel failure: \(e)")
        }
    })
}

Listen for callback

For the update initiated by -startFirmwareUpgrade:, we recommend that you use -device:otaUpdateStatusChanged: to register the callback. This callback includes the status changes, progress, and failure reason. Other deprecated callback methods can still be executed. To prevent repeated callback listeners, we recommend that you port respective logic to this callback.

Example

Objective-C:

- (void)device:(ThingSmartDevice *)device otaUpdateStatusChanged:(ThingSmartFirmwareUpgradeStatusModel *)statusModel {
        // The callback for the status and progress of the firmware update.
        // Recommended for use with -startFirmwareUpgrade:
}

- (void)device:(ThingSmartDevice *)device firmwareUpgradeStatusModel:(ThingSmartFirmwareUpgradeStatusModel *)upgradeStatusModel {
    // The callback for the device update status.
    // Recommended for use with -upgradeFirmware:success:failure:.
}

- (void)deviceFirmwareUpgradeSuccess:(ThingSmartDevice *)device type:(NSInteger)type {
    // The firmware is updated. Deprecated. - device:firmwareUpgradeStatusModel: is recommended.
}

- (void)deviceFirmwareUpgradeFailure:(ThingSmartDevice *)device type:(NSInteger)type {
    // Failed to update the firmware. Deprecated. - device:firmwareUpgradeStatusModel: is recommended.
}

- (void)device:(ThingSmartDevice *)device firmwareUpgradeProgress:(NSInteger)type progress:(double)progress {
    // The progress of the firmware update.
    // Recommended for use with -upgradeFirmware:success:failure:.
}

Swift:

func device(_ device: ThingSmartDevice, firmwareUpgradeStatusModel upgradeStatusModel: ThingSmartFirmwareUpgradeStatusModel) {
    // The callback for the device update status.
}

func deviceFirmwareUpgradeSuccess(_ device: ThingSmartDevice!, type: Int) {
    // The firmware is updated. This API method will be deprecated soon. device(_:, firmwareUpgradeStatusModel:) is recommended.
}

func deviceFirmwareUpgradeFailure(_ device: ThingSmartDevice!, type: Int) {
    // Failed to update the firmware. This API method will be deprecated soon. device(_:, firmwareUpgradeStatusModel:) is recommended.
}

func device(_ device: ThingSmartDevice!, firmwareUpgradeProgress type: Int, progress: Double) {
    // The progress of the firmware update.
}

Automatically update to the latest version

Certain devices support an automatic update to the latest version. You can use ThingSmartDeviceModel - supportAuto to check whether a device supports this feature.

API description

// Checks whether this feature is enabled.
- (void)getAutoUpgradeSwitchInfoWithSuccess:(nullable ThingSuccessID)success
                                    failure:(nullable ThingFailureError)failure;

// Saves the switch states.
- (void)saveUpgradeInfoWithSwitchValue:(NSInteger)switchValue
                               success:(nullable ThingSuccessHandler)success
                               failure:(nullable ThingFailureError)failure;

Parameters

Parameter Description
switchValue The status of the feature. Valid values:
  • 0: disabled
  • 1: enabled
success The success callback.
failure The failure callback.

Example

Objective-C:

- (void)getAutoUpgradeSwitchInfo {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    // Note that ThingSmartDeviceModel - supportAuto must be used to check whether the device supports this feature first.

    [self.device getAutoUpgradeSwitchInfoWithSuccess:^(NSNumber *status) {
        // 0: disabled. 1: enabled. This feature is disabled in this example.
        NSLog(@"current switch status: %@", status);
    } failure:^(NSError *error) {
        NSLog(@"get current switch status fail. %@", error);
    }];
}

- (void)saveAutoUpgradeSwitchInfo {
    // self.device = [ThingSmartDevice deviceWithDeviceId:@"your_device_id"];
    // Note that ThingSmartDeviceModel - supportAuto must be used to check whether the device supports this feature first.

    NSInteger status = 0; // 0: disabled. 1: enabled. This feature is disabled in this example.
    [self.device saveUpgradeInfoWithSwitchValue:status success:^{
        NSLog(@"save auto switch status success.");
    } failure:^(NSError *error) {
        NSLog(@"save auto switch status fail. %@", error);
    }];
}

Swift:

func getAutoUpgradeSwitchInfo() {
    // Note that ThingSmartDeviceModel - supportAuto must be used to check whether the device supports this feature first.
    device?.getAutoUpgradeSwitchInfo(success: { status in
        // 0: disabled. 1: enabled.
        if let value = status {
            print("get current status: \(value)")
        }
    }, failure: { error in
        if let e = error {
            print("get status failure: \(e)")
        }
    })
}

func saveAutoUpgradeSwitchInfo() {
    // Note that ThingSmartDeviceModel - supportAuto must be used to check whether the device supports this feature first.
    let status = 0 // 0: disabled. 1: enabled. This feature is disabled in this example.
    device?.saveUpgradeInfo(withSwitchValue: 0, success: {
        print("save success")
    }, failure: { error in
        if let e = error {
            print("success failure: \(e)")
        }
    })
}

Common home members get information about firmware update

This API method is only applicable to apps developed based on App SDK v5.1.0 or later.

API description

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

Parameters

Parameter Description
success The success callback.
failure The failure callback.

Data model of ThingSmartMemberCheckFirmwareInfo

Field Type Description
type NSInteger The type of firmware update channel.
upgradeStatus NSInteger
  • 0: no update available
  • 1: update available
  • 2: updating
  • 5: wait for the device to wake up
version NSString The target firmware version.
upgradeText NSString The information about firmware update.

Example

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)")
        }
    })
}

Error codes

The error codes in ThingSmartFirmwareUpgradeStatusModel only apply to the API methods for a PID-specific firmware update.

Error code Description Remarks
5000 All device firmware versions are up to date and do not need to be updated. None.
5001 No firmware update found. None.
5002 For Bluetooth devices, only one device can be updated in the same task. In most cases, this error code applies to Bluetooth LE devices, Tuya mesh sub-devices, and Bluetooth mesh sub-devices.
5003 Failed to download the firmware package by using the app. None.
5004 Failed to check whether the signal strength detection is required. None.
5005 The signal strength level of the device does not meet firmware update requirements. We recommend that you implement a UI interaction to ask users whether to continue the update. The update might fail if it is continued. You can call - confirmWarningUpgradeTask: to continue or cancel the update.
5006 Failed to connect to the device. In most cases, this error code applies to Bluetooth LE devices, Tuya mesh sub-devices, and Bluetooth mesh sub-devices.
5007 A timeout error has occurred while unbinding the Bluetooth LE device from the gateway. None.
5009 The MD5 check failed while downloading firmware by using the app. None.
5010 Failed to send the firmware by using the app. In most cases, this error code applies to Bluetooth LE devices, Tuya mesh sub-devices, and Bluetooth mesh sub-devices.
5012 The device is offline. None.
5013 The pre-validation prior to the update is not passed. None.
5014 The Bluetooth feature is not enabled on the mobile phone. None.
5015 Failed to get the list of available firmware updates. None.
5016 The device update timed out. None.
5017 Failed to call the API to initiate an OTA update. The server returns an error in API calls. You can get the detailed error code through error.localizedFailureReason and the detailed description through error.localizedDescription.
5018 The device failed to upgrade and reported the reason for the failure. You can get the detailed description through error.localizedDescription.
5099 A global error code. None.

Mapping between new and old interfaces

Feature New interface Old interface
Get firmware update information checkFirmwareUpgrade:failure: getFirmwareUpgradeInfo:failure:
Query status of firmware being updated getFirmwareUpgradingStatus:failure: None
Start update startFirmwareUpgrade: upgradeFirmware:success:failure:
Cancel update cancelFirmwareUpgrade:failure: cancelUpgradeFirmware:succes:failure:
Listen for callback device:otaUpdateStatusChanged: device:firmwareUpgradeStatusModel:
deviceFirmwareUpgradeSuccess:type:
deviceFirmwareUpgradeFailure:type:
device:firmwareUpgradeProgress:

Bulk OTA Updates

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

target 'Your_Project_Name' do
    pod "ThingSmartBusinessExtensionKit"
end

API description

Model

Device firmware information

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

Bulk OTA update manager

open class ThingUpgradeListManager : NSObject {
    ...
}

Method

Check the information about the device firmware to be updated in a home.

Request parameters Type Description
familyId Int64 The home ID.
success [ThingUpgradeListInfo] The device firmware information.
failure error The error message.

true: Support. false: Not support.

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

Examples

For more information, see 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()
    }

}

Implementing a User Interface

First, implement a simple user interface.

  1. When initializing the interface, pass in the current home’s homeId.

  2. The interface will display a UITableView. Initially, the number of sections and rows in the UITableView will be 0. Additionally, the cell will be created in the value1 style.

    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)
        }
    
    }
    

Loading Devices for Upgrade

Next, load the devices in the current home that need an upgrade. Implement a loadData method that calls ThingUpgradeListManager’s getUpgradeDevices(inFamily:success:failure:) method.

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")
        }
    }
}

Storing Firmware Information of Devices

Retrieve the firmware information of the devices in the current home that need OTA upgrades. Use infos to store the firmware information of the devices that need OTA upgrades, and render the tableview based on infos.

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
    }
}

Storing Devices for OTA

Now that we have displayed the list of devices that need OTA, we need to use ThingSmartDevice for the OTA process. Therefore, create a devices dictionary to store these devices and listen for device callbacks.

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) {

    }

}

Starting Upgrades and Maintaining Status

Now, clicking on a device in the tableview will start the OTA process. However, not all devices that need OTA can be upgraded immediately; some might not meet the upgrade conditions (e.g., low battery). Additionally, some devices might already be in the process of upgrading. Therefore, we can use a status dictionary to maintain these states.

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 upgrade", 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()
    }

}

The OTA information returned by the ThingUpgradeListManager.getUpgradeDevices(inFamily:success:failure:) method does not include the local OTA status of Bluetooth devices. This means that if your Bluetooth device is already in the upgrading state during OTA, the status returned by calling the getUpgradeDevices method will be “need upgrade”.

Therefore, it is recommended not to close the upgrade interface while the Bluetooth device is performing an OTA upgrade to avoid inconsistent states when re-entering the interface.