import { requireAuth, requireCap } from '../auth.js' import { pool } from '../db.js' import { isConnected as mqttConnected } from '../lib/mqtt.js' export async function statusRoutes(app) { app.addHook('preHandler', requireAuth) // GET /api/status — live status per zone: room state, target temp, device health/battery app.get('/api/status', { preHandler: requireCap('view') }, async () => { const { rows: zones } = await pool.query(` SELECT z.*, zs.room_state, zs.last_transition_at, zs.last_booking_status FROM zones z LEFT JOIN zone_state zs ON zs.zone_id = z.id WHERE z.active = TRUE ORDER BY z.zone_type, z.name `) const { rows: devices } = await pool.query(` SELECT id, zone_id, device_type, external_ref, discovered_name, location, health_state, battery_pct, wifi_rssi, current_target_temp, target_origin, last_seen FROM zone_devices WHERE zone_id IS NOT NULL `) const devicesByZone = new Map() for (const d of devices) { if (!devicesByZone.has(d.zone_id)) devicesByZone.set(d.zone_id, []) devicesByZone.get(d.zone_id).push(d) } return { mqtt_connected: mqttConnected(), zones: zones.map(z => ({ ...z, room_state: z.room_state || 'vacant', target_temp: ['heating_up', 'occupied'].includes(z.room_state) ? z.occupied_temp : z.vacant_temp, devices: devicesByZone.get(z.id) || [], })), } }) // GET /api/status/activity — recent activity log, optionally filtered by zone app.get('/api/status/activity', { preHandler: requireCap('view') }, async (req) => { const { zone_id, limit } = req.query || {} const lim = Math.min(parseInt(limit) || 100, 500) if (zone_id) { const { rows } = await pool.query( `SELECT al.*, z.name AS zone_name FROM activity_log al LEFT JOIN zones z ON z.id = al.zone_id WHERE al.zone_id = $1 ORDER BY al.created_at DESC LIMIT $2`, [zone_id, lim] ) return rows } const { rows } = await pool.query( `SELECT al.*, z.name AS zone_name FROM activity_log al LEFT JOIN zones z ON z.id = al.zone_id ORDER BY al.created_at DESC LIMIT $1`, [lim] ) return rows }) }