Add device telemetry history + per-device graph view
activity_log only ever recorded state transitions/overrides/sync/errors — useful for "what did the app decide" but not "what was the room actually doing" when a guest/staff complaint comes in after the fact. Backend: - New device_readings time-series table (room_temp, target_temp, mode, power_state, fan_speed, valve_position, heating_active), throttled to ~1 row/device/minute via recordReading() in db.js regardless of how often a caller fires — MQTT pushes can be far chattier than that. - Wired into every place zone_devices' live-status cache already gets updated: ac-poller.js, daikin-poller.js, and mqtt.js's TRV (handleStatusBlob/handleInfo) and native-MQTT MHI (writeMhiState) handlers. - 4-day retention by default (config key history_retention_days), pruned every 6h by the new history-pruner.js. - GET /api/devices/:id/history?hours=96 to read it back. Frontend: - DeviceHistoryModal: room temp vs setpoint line chart, plus a valve-position chart (TRVs) or fan-speed step chart (AC), plus a diffed mode/power/fan change log — 24h/4d/7d range picker. Uses recharts, matching the convention already established in utilities/wages/reports. - Graph icon on each device in both ZoneDetailModal and the Devices page opens it. Side effect: frontend/package-lock.json was missing vite-plugin-pwa's whole dependency tree even though package.json already declared it — pre-existing drift, reconciled by this npm install (unrelated to the recharts addition, just came along with it).
This commit is contained in:
parent
59a3e091d8
commit
f0b4ac3c41
15 changed files with 5172 additions and 38 deletions
|
|
@ -82,6 +82,22 @@ export async function deviceRoutes(app, opts) {
|
|||
return { ok: true, found: result.devices.length, new: discoveredCount, note: result.note }
|
||||
})
|
||||
|
||||
// GET /api/devices/:id/history — time-series readings for the debugging chart.
|
||||
// ?hours=96 (default, matches the default 4-day retention — see history_retention_days
|
||||
// in config); hard-capped at 14 days regardless of what's asked, since that's the
|
||||
// longest anyone could plausibly have kept via config even if retention was raised.
|
||||
app.get('/api/devices/:id/history', { preHandler: requireCap('view') }, async (req) => {
|
||||
const hours = Math.min(parseInt(req.query?.hours) || 96, 24 * 14)
|
||||
const { rows } = await pool.query(
|
||||
`SELECT room_temp, target_temp, mode, power_state, fan_speed, valve_position, heating_active, recorded_at
|
||||
FROM device_readings
|
||||
WHERE device_id = $1 AND recorded_at > NOW() - ($2 || ' hours')::interval
|
||||
ORDER BY recorded_at ASC`,
|
||||
[req.params.id, hours]
|
||||
)
|
||||
return rows
|
||||
})
|
||||
|
||||
// PATCH /api/devices/:id — zone/location assignment (a living mapping, not a one-time wizard)
|
||||
app.patch('/api/devices/:id', { preHandler: requireCap('manage_devices') }, async (req, reply) => {
|
||||
const { rows: existing } = await pool.query('SELECT * FROM zone_devices WHERE id = $1', [req.params.id])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue