Over-The-Air (OTA) Updates
Over-The-Air (OTA) updates allow you to update the firmware of your nodes remotely without physical access to the device.
Overview
OTA updates in RainMaker enable you to:
- Check for available firmware updates
- Initiate OTA updates for nodes
- Monitor the status of OTA update jobs
Check OTA Update
Check if there is an Over-The-Air (OTA) update available for the node using the checkOTAUpdate method.
try {
const otaResponse = await node.checkOTAUpdate();
console.log("OTA update availability:", otaResponse);
if (otaResponse.otaAvailable) {
console.log("Update available:", otaResponse.fwVersion);
console.log("OTA Job ID:", otaResponse.otaJobId);
console.log("File size:", otaResponse.fileSize);
}
} catch (error) {
console.error("Error checking OTA update:", error);
}
ESPOTAUpdateResponse Interface
The checkOTAUpdate method returns an ESPOTAUpdateResponse object containing detailed information about the OTA update availability and details.
| Property | Type | Required | Description |
|---|---|---|---|
| otaAvailable | boolean | Yes | Indicates whether an OTA update is available for the device. |
| status | string | Yes | The status of the OTA update. |
| description | string | Yes | Description of the OTA update. |
| fwVersion | string | Yes | The firmware version available for update. |
| otaJobId | string | Yes | The OTA job ID that can be used to initiate the update. |
| fileSize | number | Yes | The size of the firmware file in bytes. |
| url | string | No | The URL from which the firmware can be downloaded. |
| fileMD5 | string | No | The MD5 hash of the firmware file for verification. |
| streamId | string | No | The stream ID for the OTA update. |
| metadata | Record<string, any> | No | Additional metadata associated with the OTA update. |
Example: Check OTA Update and Use Response
try {
const otaResponse = await node.checkOTAUpdate();
if (otaResponse.otaAvailable) {
console.log("OTA Update Available!");
console.log("Firmware Version:", otaResponse.fwVersion);
console.log("Description:", otaResponse.description);
console.log("File Size:", otaResponse.fileSize, "bytes");
console.log("Status:", otaResponse.status);
console.log("OTA Job ID:", otaResponse.otaJobId);
// Use otaJobId to initiate the update
if (otaResponse.otaJobId) {
// You can use this job ID with pushOTAUpdate
console.log("Ready to update with job ID:", otaResponse.otaJobId);
}
// Optional fields
if (otaResponse.url) {
console.log("Firmware URL:", otaResponse.url);
}
if (otaResponse.fileMD5) {
console.log("File MD5:", otaResponse.fileMD5);
}
if (otaResponse.metadata) {
console.log("Metadata:", otaResponse.metadata);
}
} else {
console.log("No OTA update available. Current status:", otaResponse.status);
}
} catch (error) {
console.error("Error checking OTA update:", error);
}
Example: Validate OTA Update Before Proceeding
try {
const otaResponse = await node.checkOTAUpdate();
if (otaResponse.otaAvailable) {
// Validate that we have all required information
if (!otaResponse.otaJobId) {
console.error("OTA Job ID is missing");
return;
}
// Check file size if needed
if (otaResponse.fileSize > 0) {
console.log(`Update size: ${(otaResponse.fileSize / 1024 / 1024).toFixed(2)} MB`);
}
// Verify MD5 if available
if (otaResponse.fileMD5) {
console.log("File verification hash:", otaResponse.fileMD5);
}
// Proceed with update using the job ID
console.log("Proceeding with OTA update...");
// Use otaResponse.otaJobId with pushOTAUpdate
}
} catch (error) {
console.error("Error checking OTA update:", error);
}
Push OTA Update
Initiate an Over-The-Air (OTA) update for the node using a specified OTA job ID with the pushOTAUpdate method. The OTA job ID is obtained from the checkOTAUpdate response.
try {
// First, check for available OTA updates
const otaResponse = await node.checkOTAUpdate();
if (otaResponse.otaAvailable && otaResponse.otaJobId) {
// Use the otaJobId from checkOTAUpdate response
const response = await node.pushOTAUpdate(otaResponse.otaJobId);
console.log("OTA update initiated:", response);
} else {
console.log("No OTA update available or job ID missing");
}
} catch (error) {
console.error("Error initiating OTA update:", error);
}
Example: Complete OTA Update Flow
try {
// Step 1: Check for available OTA updates
const otaResponse = await node.checkOTAUpdate();
if (!otaResponse.otaAvailable) {
console.log("No OTA update available. Status:", otaResponse.status);
return;
}
console.log("OTA Update Available!");
console.log("Firmware Version:", otaResponse.fwVersion);
console.log("File Size:", otaResponse.fileSize, "bytes");
// Step 2: Initiate the OTA update using the job ID
if (otaResponse.otaJobId) {
const pushResponse = await node.pushOTAUpdate(otaResponse.otaJobId);
console.log("OTA update initiated:", pushResponse);
// Step 3: Monitor the update status
const status = await node.getOTAUpdateStatus(otaResponse.otaJobId);
console.log("OTA update status:", status);
}
} catch (error) {
console.error("Error in OTA update flow:", error);
}
Get OTA Update Status
Check the status of a previously initiated Over-The-Air (OTA) update for the node using the getOTAUpdateStatus method. Use the OTA job ID obtained from checkOTAUpdate or pushOTAUpdate.
try {
// Get the OTA job ID from checkOTAUpdate response
const otaResponse = await node.checkOTAUpdate();
if (otaResponse.otaJobId) {
const statusResponse = await node.getOTAUpdateStatus(otaResponse.otaJobId);
console.log("OTA update status:", statusResponse.status);
console.log("Node ID:", statusResponse.nodeId);
console.log("Timestamp:", new Date(statusResponse.timestamp));
console.log("Additional Info:", statusResponse.additionalInfo);
}
} catch (error) {
console.error("Error fetching OTA update status:", error);
}
ESPOTAUpdateStatusResponse Interface
The getOTAUpdateStatus method returns an ESPOTAUpdateStatusResponse object containing the current status of the OTA update.
| Property | Type | Required | Description |
|---|---|---|---|
| status | string | Yes | The current status of the OTA update (e.g., "in_progress", "completed", "failed"). |
| nodeId | string | Yes | The ID of the node for which the OTA update status is being checked. |
| timestamp | number | Yes | The timestamp when the status was last updated (Unix timestamp in milliseconds). |
| additionalInfo | string | Yes | Additional information about the OTA update status. |
Example: Monitor OTA Update Progress
async function monitorOTAUpdate(node, otaJobId) {
try {
const maxAttempts = 30; // Maximum number of status checks
let attempts = 0;
while (attempts < maxAttempts) {
const statusResponse = await node.getOTAUpdateStatus(otaJobId);
const status = statusResponse.status;
console.log(`OTA Update Status (Attempt ${attempts + 1}):`, status);
console.log("Node ID:", statusResponse.nodeId);
console.log("Last Updated:", new Date(statusResponse.timestamp).toLocaleString());
if (statusResponse.additionalInfo) {
console.log("Additional Info:", statusResponse.additionalInfo);
}
// Check if update is complete or failed
if (status === "completed" || status === "failed") {
console.log("OTA update finished with status:", status);
return statusResponse;
}
// Wait before next check (e.g., 5 seconds)
await new Promise(resolve => setTimeout(resolve, 5000));
attempts++;
}
console.log("Status check timeout");
return null;
} catch (error) {
console.error("Error monitoring OTA update:", error);
throw error;
}
}
// Usage
try {
const otaResponse = await node.checkOTAUpdate();
if (otaResponse.otaAvailable && otaResponse.otaJobId) {
await node.pushOTAUpdate(otaResponse.otaJobId);
const finalStatus = await monitorOTAUpdate(node, otaResponse.otaJobId);
if (finalStatus) {
console.log("Final Status:", finalStatus.status);
console.log("Completed at:", new Date(finalStatus.timestamp).toLocaleString());
}
}
} catch (error) {
console.error("Error in OTA update process:", error);
}
Example: Handle Different OTA Status Values
try {
const otaResponse = await node.checkOTAUpdate();
if (otaResponse.otaAvailable && otaResponse.otaJobId) {
await node.pushOTAUpdate(otaResponse.otaJobId);
const statusResponse = await node.getOTAUpdateStatus(otaResponse.otaJobId);
switch (statusResponse.status) {
case "in_progress":
console.log("OTA update is in progress...");
console.log("Additional Info:", statusResponse.additionalInfo);
break;
case "completed":
console.log("OTA update completed successfully!");
console.log("Completed at:", new Date(statusResponse.timestamp).toLocaleString());
break;
case "failed":
console.error("OTA update failed!");
console.error("Error details:", statusResponse.additionalInfo);
break;
case "pending":
console.log("OTA update is pending...");
break;
default:
console.log("Unknown status:", statusResponse.status);
}
}
} catch (error) {
console.error("Error in OTA update process:", error);
}
OTA Update Flow
The following sequence diagram illustrates the complete OTA update flow between the SDK, App, and Node: