From 489e07d4e18d3b68c9e93382d8e88b278e116295 Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Thu, 25 Dec 2025 03:52:09 +0100 Subject: [PATCH] u --- agents/tapo/src/main.rs | 2 ++ uiserver/webpack.config.js | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/agents/tapo/src/main.rs b/agents/tapo/src/main.rs index 70a148c..9459950 100644 --- a/agents/tapo/src/main.rs +++ b/agents/tapo/src/main.rs @@ -60,6 +60,8 @@ struct Config { server_url: String, api_key: String, poll_interval_secs: u64, + #[serde(default)] + command_url: Option, // HTTP URL for command polling (e.g., http://localhost:3905/api/outputs/commands) devices: Vec, } diff --git a/uiserver/webpack.config.js b/uiserver/webpack.config.js index 89214de..e04e4d9 100644 --- a/uiserver/webpack.config.js +++ b/uiserver/webpack.config.js @@ -266,6 +266,49 @@ module.exports = { } }); + // Output bindings: map virtual outputs to physical devices + // Format: outputChannel -> { device, channel, type } + const OUTPUT_BINDINGS = { + 'BigDehumid': { device: 'tapo', channel: 'r0', type: 'switch' }, + 'CO2Valve': { device: 'tapo', channel: 'c', type: 'switch' }, + }; + + // 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 }); + } + }); + // GET /api/rules - List all rules app.get('/api/rules', (req, res) => { try {