igloo
Docs / igloohome / BLE SDK / Firmware Updates

Firmware Updates (DFU)

Check for and perform firmware updates on Igloohome devices.


checkFirmwareUpdate

Checks whether a firmware update is available for the device.

For BLE locks: reads the current firmware version from the lock over BLE, patches the server, then queries the DFU service.

For EB1 bridges: reads the server-stored firmware version (no BLE) and queries the DFU service.

Signature

suspend fun checkFirmwareUpdate(
    deviceId: String,
    key: String,
    accessToken: String,
): FirmwareUpdateInfo
func checkFirmwareUpdate(
    deviceId: String,
    key: String,
    accessToken token: String
) async throws -> FirmwareUpdateInfo

Parameters

Name Type Required Description
deviceId String Yes Bluetooth device name (or device ID for EB1).
key String Yes Guest key with GET_FIRMWARE_VERSION permission.
accessToken String Yes Access token for server calls.

Return Type

data class FirmwareUpdateInfo(
    val hasUpdate: Boolean,
    val currentVersion: String,
    val firmwareUid: String?,
    val latestFirmwareUid: String?,
    val firmwareSizeBytes: Long?,
)
public struct FirmwareUpdateInfo {
    public let hasUpdate: Bool
    public let currentVersion: String
    public let firmwareUid: String?
    public let firmwareSizeBytes: Int?
}
Field Description
hasUpdate true if a newer firmware is available.
currentVersion Current firmware version string on the device.
firmwareUid UID of the available firmware package. null if no update.
latestFirmwareUid UID of the latest available firmware. null if no update.
firmwareSizeBytes Size of the firmware binary in bytes. null if no update.

Error Codes

Exception Code Description
BluetoothException 708 Bluetooth is off or unavailable (BLE path).
ConnectionException 12 Device disconnected during version read.
TimeoutException 703 BLE operation exceeded the timeout.
ApiException varies Server API call failed.

Example

try {
    val info = sdk.checkFirmwareUpdate(
        deviceId = "IGM4-XXXX",
        key = guestKey,
        accessToken = accessToken,
    )
    if (info.hasUpdate) {
        println("Update available: ${info.firmwareUid}")
        println("Size: ${info.firmwareSizeBytes} bytes")
    } else {
        println("Firmware is up to date (${info.currentVersion})")
    }
} catch (e: IglooHomeException.ConnectionException) {
    showError("unexpected bluetooth connection issue")
} catch (e: IglooHomeException.TimeoutException) {
    showError("Timed out reading firmware version")
} catch (e: IglooHomeException) {
    showError("Check failed: ${e.message}")
}
do {
    let info = try await sdk.checkFirmwareUpdate(
        deviceId: "IGM4-XXXX",
        key: guestKey,
        accessToken: accessToken)
    if info.hasUpdate {
        print("Update available: \(info.firmwareUid ?? "")")
        print("Size: \(info.firmwareSizeBytes ?? 0) bytes")
    } else {
        print("Firmware is up to date (\(info.currentVersion))")
    }
} catch {
    showError(message: "Check failed: \(error.localizedDescription)")
}

performDfu

Performs a firmware update on the device. Returns a Flow of DfuEvent — progress updates then complete or failed.

For BLE locks: downloads firmware, executes BLE DFU transfer with progress (0–100%), then patches the server with the new version.

For SP2 devices: perform a Lock followed by an Unlock on the lock, then ensure the shackle is fully released (open position) before calling performDfu.

For EB1 bridges: creates a cloud firmware update job and polls until completion.

Signature

fun performDfu(
    deviceId: String,
    key: String,
    firmwareUid: String,
    accessToken: String,
): Flow<DfuEvent>
func performDfu(
    deviceId: String,
    key: String,
    firmwareUid: String,
    accessToken token: String
) -> AsyncThrowingStream<DfuEvent, Error>

Parameters

Name Type Required Description
deviceId String Yes Bluetooth device name (or device ID for EB1).
key String Yes Guest key with ENABLE_DFU permission.
firmwareUid String Yes Firmware package UID from FirmwareUpdateInfo.firmwareUid.
accessToken String Yes Access token for server calls.

Return Type — DfuEvent

sealed class DfuEvent {
    /** Firmware transfer progress (0–100). */
    data class Progress(val percent: Int) : DfuEvent()

    /** Firmware update completed. */
    data class Complete(val newFirmwareVersion: String) : DfuEvent()

    /** Firmware update failed. */
    data class Failed(val error: IglooHomeException) : DfuEvent()
}
public enum DfuEvent {
    /// Firmware transfer progress, in percent (0–100).
    case progress(Int)
    /// Firmware update finished and the new version has been reported to the server.
    case complete(newFirmwareVersion: String)
}

Example

val info = sdk.checkFirmwareUpdate(deviceId, key, accessToken)
if (!info.hasUpdate) return

try {
    sdk.performDfu(
        deviceId = deviceId,
        key = key,
        firmwareUid = info.firmwareUid!!,
        accessToken = accessToken,
    ).collect { event ->
        when (event) {
            is DfuEvent.Progress -> updateProgressBar(event.percent)
            is DfuEvent.Complete -> showSuccess("Updated to ${event.newFirmwareVersion}")
            is DfuEvent.Failed -> {
                when (event.error) {
                    is IglooHomeException.BatteryLowException ->
                        showError("Battery too low for update")
                    is IglooHomeException.DfuStateNotReadyException ->
                        showError("Lock not ready — close the door and try again")
                    else -> showError("DFU failed: ${event.error.message}")
                }
            }
        }
    }
} catch (e: IglooHomeException.ConnectionException) {
    showError("unexpected bluetooth connection issue")
} catch (e: IglooHomeException) {
    showError("Firmware update failed: ${e.message}")
}
let info = try await sdk.checkFirmwareUpdate(deviceId: deviceId, key: key, accessToken: accessToken)
guard info.hasUpdate, let firmwareUid = info.firmwareUid else { return }

do {
    for try await event in sdk.performDfu(
        deviceId: deviceId,
        key: key,
        firmwareUid: firmwareUid,
        accessToken: accessToken)
    {
        switch event {
        case let .progress(percent):
            updateProgressBar(percent)
        case let .complete(newFirmwareVersion):
            showSuccess(message: "Updated to \(newFirmwareVersion)")
        }
    }
} catch IgloohomeError.batteryLow {
    showError(message: "Battery too low for update")
} catch IgloohomeError.dfuStateNotReady {
    showError(message: "Lock not ready — close the door and try again")
} catch {
    showError(message: "Firmware update failed: \(error.localizedDescription)")
}

Notes

  • Battery check: The SDK checks battery before starting DFU. Minimum 20% (31% for SP2X/SP2E). Throws BatteryLowException if too low.
  • DFU state check: The SDK verifies the lock is ready for DFU (door closed, not in active state). Throws DfuStateNotReadyException if not ready. Skipped for EK and IEF SKU.
  • SP2 SKU: Before performing DFU on an SP2 device, perform a Lock followed by an Unlock on the lock, then ensure the shackle is fully released (open position) before starting the update.
  • Progress: BLE DFU emits progress 0–90% during transfer, animates 90–99% during reconnect, then 100% after reading the new firmware version.
  • Bridge DFU: For EB1 bridges, the SDK creates a cloud job and polls every 3 seconds. DfuJobFailedException if the job fails, DfuJobExpiredException if it expires.
  • Keep screen alive: DFU can take several minutes. Keep the screen on and display a progress indicator.
  • Errors are emitted as DfuEvent.Failed rather than thrown as exceptions.