Docs / iglooworks / 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? = null,
): FirmwareUpdateInfo
func checkFirmwareUpdate(
deviceId: String,
key: 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? |
OAuth only |
OAuth 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. |
Both checkFirmwareUpdate and performDfu start by scanning for the device, which on
iOS throws LockManagerError.noLockFound (device did not respond) or
.bluetoothIsTurnedOff before anything else runs.
Example
try {
val info = sdk.checkFirmwareUpdate(
deviceId = "IGM4-XXXX",
key = guestKey,
)
if (info.hasUpdate) {
println("Update available: ${info.firmwareUid}")
println("Size: ${info.firmwareSizeBytes} bytes")
} else {
println("Firmware is up to date (${info.currentVersion})")
}
} catch (e: IglooWorksException.ConnectionException) {
showError("unexpected bluetooth connection issue")
} catch (e: IglooWorksException.TimeoutException) {
showError("Timed out reading firmware version")
} catch (e: IglooWorksException) {
showError("Check failed: ${e.message}")
}
do {
let info = try await sdk.checkFirmwareUpdate(
deviceId: "IGM4-XXXX",
key: guestKey)
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)")
}
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? = null,
): Flow<DfuEvent>
func performDfu(
deviceId: String,
key: String,
firmwareUid: String
) async throws -> AsyncThrowingStream<DfuEvent, Error>
There's also a public performDfuJob(deviceId:) for driving a Bridge's server-job DFU flow directly — performDfu calls into it automatically when the target device is a Bridge.
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? |
OAuth only |
OAuth 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: IglooWorksException) : DfuEvent()
}
public enum DfuEvent {
case progress(Int)
case complete(newFirmwareVersion: String)
}
Example
try {
val info = sdk.checkFirmwareUpdate(deviceId, key)
if (!info.hasUpdate) return
sdk.performDfu(
deviceId = deviceId,
key = key,
firmwareUid = info.firmwareUid!!,
).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 IglooWorksException.BatteryLowException ->
showError("Battery too low for update")
is IglooWorksException.DfuStateNotReadyException ->
showError("Lock not ready — close the door and try again")
else -> showError("DFU failed: ${event.error.message}")
}
}
}
}
} catch (e: IglooWorksException.ConnectionException) {
showError("unexpected bluetooth connection issue")
} catch (e: IglooWorksException.ApiException) {
showError("Server error: ${e.message}")
} catch (e: IglooWorksException) {
showError("Firmware update failed: ${e.message}")
}
let info = try await sdk.checkFirmwareUpdate(deviceId: deviceId, key: key)
guard info.hasUpdate, let firmwareUid = info.firmwareUid else { return }
do {
let events = try await sdk.performDfu(deviceId: deviceId, key: key, firmwareUid: firmwareUid)
for try await event in events {
switch event {
case let .progress(percent):
updateProgressBar(percent)
case let .complete(newFirmwareVersion):
showSuccess(message: "Updated to \(newFirmwareVersion)")
}
}
} catch IglooworksError.batteryLow {
showError(message: "Battery too low for update")
} catch IglooworksError.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.