Let me provide a comprehensive solution addressing all three problem areas:
Device Callback Endpoint:
Your custom /api/firmware/callback endpoint needs to bridge to ThingWorx’s job management system. You have two options:
Option 1 - Modify Custom Endpoint (if you need custom processing):
Update your custom endpoint service to call the job manager after processing:
// Your custom endpoint service
let jobId = me.GetActiveJobForDevice({deviceId: deviceId});
if (jobId) {
Things["FirmwareUpdateJobManager"].UpdateDeviceStatus({
jobId: jobId,
deviceId: deviceId,
status: status,
firmwareVersion: version
});
}
Option 2 - Use Standard Endpoint (recommended):
Reconfigure devices to POST directly to ThingWorx’s built-in endpoint:
`https://your-server/Thingworx/Things/FirmwareUpdateJobManager/Services/UpdateDeviceStatus
With this payload format:
{
"jobId": "FW_UPDATE_20250708_001",
"deviceId": "GW_001",
"status": "completed",
"firmwareVersion": "4.2.1",
"timestamp": 1720444800000
}
The jobId is critical - it’s provided to devices when they receive the update command. Ensure your update initiation service includes this in the device payload.
Status Event Processing:
Verify the event processing pipeline is functioning:
-
Check subscription status:
- Navigate to FirmwareUpdateJobManager thing in Composer
- Open the Subscriptions tab
- Verify “DeviceStatusUpdateSubscription” is Enabled
- Check that LastProcessedTime is recent (within last few minutes)
-
If subscription is disabled or missing:
// Re-enable subscription programmatically
Things["FirmwareUpdateJobManager"].EnableSubscription({
subscriptionName: "DeviceStatusUpdateSubscription"
});
- Verify the event stream isn’t backed up:
- Check System > Monitoring > Event Queue Metrics
- If queue depth > 1000, you may have a processing bottleneck
- Consider increasing event processing threads in platform settings
Dashboard Polling Interval:
The 30-second dashboard refresh is fine, but it depends on the underlying data stream being updated. The issue isn’t the polling frequency - it’s that the data source (job manager status) isn’t being updated due to the callback problems above.
However, you can optimize dashboard performance:
-
Verify the dashboard is bound to the correct data source:
- Dashboard should subscribe to FirmwareUpdateJobManager.JobStatusStream
- Refresh type should be “On Data Change” rather than “Polling”
- This makes updates near-instantaneous when status changes
-
Check for dashboard binding issues:
- Open the dashboard in Composer
- Verify all status widgets are bound to live data properties
- Test by manually updating a device status and watching for dashboard refresh
Complete Implementation Steps:
- First, identify all active jobs stuck in ‘In Progress’:
let stuckJobs = Things["FirmwareUpdateJobManager"].GetJobsByStatus({
status: "InProgress"
});
- For each stuck job, manually sync device statuses:
let devices = Things["FirmwareUpdateJobManager"].GetJobDevices({jobId: jobId});
for each (device in devices) {
let actualStatus = Things[device.thingName].GetFirmwareVersion();
// Update job with actual device state
Things["FirmwareUpdateJobManager"].UpdateDeviceStatus({
jobId: jobId,
deviceId: device.deviceId,
status: actualStatus.matches(targetVersion) ? "completed" : "failed",
firmwareVersion: actualStatus
});
}
-
Implement proper callback handling going forward (use Option 1 or 2 above)
-
Add monitoring alerts for stuck jobs:
- Create a scheduled service that checks for jobs in ‘InProgress’ > 2 hours
- Send alerts when detected
- Auto-trigger status reconciliation if needed
Testing:
After implementing these fixes, test with a small job (5-10 devices):
- Launch update job from dashboard
- Monitor callback logs to confirm proper endpoint calls
- Watch job status update in real-time as devices complete
- Verify dashboard reflects changes within 5-10 seconds
The core issue is the disconnect between your custom callback endpoint and ThingWorx’s job management system. Once callbacks properly update the job manager, both the status event processing and dashboard polling will work correctly.