Fix TRV control routes hanging up to ~93min, causing Gateway Time-out
Both POST /api/devices/:id/trv-control and POST /api/zones/:id/override awaited the full setTargetWithRetry()/batchSetZoneTemperature() retry/backoff pipeline before responding -- up to ~93 minutes (RETRY_DELAYS_MS sums to 5610s across 10 attempts). Any reverse proxy times out its own connection long before that finishes, surfacing as a real Gateway Time-out (hit 2026-08-26 testing a TRV setpoint change -- confirmed not kiosk-specific, same code path every TRV control UI goes through, including kiosk's bulk edit which compounded it per selected device). Fix: both routes now fire the retry pipeline in the background and respond immediately (ok:true, pending:true) -- same eventually-consistent pattern the MHI/Daikin panels already use (fire command, poll status separately for the real outcome). Updated callers that read the old synchronous successful/total counts (ZoneDetailModal's override message, KioskBulkPanel's TRV result message) to reflect 'sent, applying in the background' instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
f3a73ad273
commit
55afcaa786
5 changed files with 50 additions and 22 deletions
|
|
@ -29,16 +29,26 @@ export async function overrideRoutes(app) {
|
|||
return reply.status(200).send({ ok: false, error: 'No TRVs mapped to this zone', auto_mode: false })
|
||||
}
|
||||
|
||||
const results = await batchSetZoneTemperature(devices, tempC)
|
||||
const successful = Object.values(results).filter(Boolean).length
|
||||
// Don't block the HTTP response on the full per-device retry/backoff pipeline —
|
||||
// setTargetWithRetry() (called per-device inside batchSetZoneTemperature, plus
|
||||
// a ~10s stagger between devices) can run up to ~93 minutes before giving up
|
||||
// (RETRY_DELAYS_MS in lib/mqtt.js: 30s->30min x10 attempts), which any reverse
|
||||
// proxy will time out on long before that finishes — hit as a real Gateway
|
||||
// Time-out 2026-08-26. Fire it in the background instead; the frontend already
|
||||
// reads the eventual outcome back via GET .../trv-status (setpoint/health_state)
|
||||
// per device, same eventually-consistent pattern the MHI/Daikin panels use.
|
||||
batchSetZoneTemperature(devices, tempC)
|
||||
.then(results => {
|
||||
const successful = Object.values(results).filter(Boolean).length
|
||||
return logActivity(zone.id, 'override', {
|
||||
note: `Manual override to ${tempC}°C (${successful}/${devices.length} devices, auto mode disabled)`,
|
||||
source: 'manual',
|
||||
userEmail: req.user.email,
|
||||
})
|
||||
})
|
||||
.catch(err => req.log.error(`[override] background retry failed: ${err.message}`))
|
||||
|
||||
await logActivity(zone.id, 'override', {
|
||||
note: `Manual override to ${tempC}°C (${successful}/${devices.length} devices, auto mode disabled)`,
|
||||
source: 'manual',
|
||||
userEmail: req.user.email,
|
||||
})
|
||||
|
||||
return { ok: successful > 0, successful, total: devices.length, auto_mode: false }
|
||||
return { ok: true, pending: true, total: devices.length, auto_mode: false }
|
||||
})
|
||||
|
||||
// GET /api/devices/:id/mhi-status — live status for one assigned MHI aircon unit.
|
||||
|
|
@ -181,14 +191,19 @@ export async function overrideRoutes(app) {
|
|||
const tempC = Number(req.body?.tempC)
|
||||
if (!Number.isFinite(tempC)) return reply.status(400).send({ error: 'tempC must be a number' })
|
||||
|
||||
const ok = await setTargetWithRetry({ external_ref: device.external_ref, device_ip: device.device_ip }, tempC)
|
||||
// Don't block the HTTP response on the full retry/backoff pipeline — it can run
|
||||
// up to ~93 minutes (see the same note on /api/zones/:id/override above) before
|
||||
// giving up, which any reverse proxy will time out on long before that finishes.
|
||||
// Fire it in the background; GET .../trv-status already reflects the eventual
|
||||
// setpoint/health_state once it resolves, same as every other device panel.
|
||||
setTargetWithRetry({ external_ref: device.external_ref, device_ip: device.device_ip }, tempC)
|
||||
.then(ok => logActivity(device.zone_id, 'device_command', {
|
||||
note: `TRV manual setpoint on ${device.location || device.discovered_name || device.external_ref}: ${tempC}°C${ok ? '' : ' — FAILED to acknowledge'}`,
|
||||
source: 'manual',
|
||||
userEmail: req.user.email,
|
||||
}))
|
||||
.catch(err => req.log.error(`[trv-control] background retry failed: ${err.message}`))
|
||||
|
||||
await logActivity(device.zone_id, 'device_command', {
|
||||
note: `TRV manual setpoint on ${device.location || device.discovered_name || device.external_ref}: ${tempC}°C${ok ? '' : ' — FAILED'}`,
|
||||
source: 'manual',
|
||||
userEmail: req.user.email,
|
||||
})
|
||||
|
||||
return { ok }
|
||||
return { ok: true, pending: true }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue