/** * Outputs API - Output channel definitions and values */ module.exports = function setupOutputsApi(app, { db, OUTPUT_CHANNELS, OUTPUT_BINDINGS }) { // GET /api/outputs - List output channel definitions app.get('/api/outputs', (req, res) => { res.json(OUTPUT_CHANNELS); }); // GET /api/outputs/values - Get current output values app.get('/api/outputs/values', (req, res) => { try { if (!db) throw new Error('Database not connected'); const result = {}; const stmt = db.prepare(` SELECT channel, value FROM output_events WHERE id IN ( SELECT MAX(id) FROM output_events GROUP BY channel ) `); const rows = stmt.all(); rows.forEach(row => { result[row.channel] = row.value; }); // Fill in defaults for missing channels OUTPUT_CHANNELS.forEach(ch => { if (result[ch.channel] === undefined) { result[ch.channel] = 0; } }); res.json(result); } catch (err) { res.status(500).json({ error: err.message }); } }); // GET /api/outputs/commands - Get desired states for bound devices // Agents poll this to get commands. Returns { "device:channel": { state: 0|1 } } app.get('/api/outputs/commands', (req, res) => { try { if (!db) throw new Error('Database not connected'); // Get current output values const stmt = db.prepare(` SELECT channel, value FROM output_events WHERE id IN ( SELECT MAX(id) FROM output_events GROUP BY channel ) `); const rows = stmt.all(); const outputValues = {}; rows.forEach(row => { outputValues[row.channel] = row.value; }); // Map to device commands const commands = {}; for (const [outputChannel, binding] of Object.entries(OUTPUT_BINDINGS)) { const value = outputValues[outputChannel] ?? 0; const deviceKey = `${binding.device}:${binding.channel}`; commands[deviceKey] = { state: value > 0 ? 1 : 0, source: outputChannel }; } res.json(commands); } catch (err) { res.status(500).json({ error: err.message }); } }); };