feat: Introduce rule configuration from the status dashboard, allowing storedDuration editing for the water button rule and improving its timer handling.
This commit is contained in:
@@ -4,7 +4,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { WebSocketServer } from 'ws';
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { getRulesStatus } from './rule_engine.js';
|
||||
import { getRulesStatus, setRuleConfig } from './rule_engine.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -588,12 +588,27 @@ const dashboardHTML = `<!DOCTYPE html>
|
||||
} else if (data.type === 'rule_update') {
|
||||
// Real-time rule status update
|
||||
handleRuleUpdate(data);
|
||||
} else if (data.type === 'rules_update') {
|
||||
// Full rules refresh (after config change)
|
||||
renderRules(data.rules);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let currentRules = [];
|
||||
|
||||
function sendConfig(ruleName, key, value) {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'set_config',
|
||||
ruleName,
|
||||
key,
|
||||
value
|
||||
}));
|
||||
showToast(ruleName, 'Saving...', key + ' = ' + value, 'event');
|
||||
}
|
||||
}
|
||||
|
||||
function handleRuleUpdate(data) {
|
||||
// Update the rule in our local cache
|
||||
const ruleIndex = currentRules.findIndex(r => r.name === data.name);
|
||||
@@ -825,10 +840,24 @@ const dashboardHTML = `<!DOCTYPE html>
|
||||
} else if (rule.status.error) {
|
||||
statusHTML = \`<span style="color: var(--accent-offline);">Error: \${rule.status.error}</span>\`;
|
||||
} else {
|
||||
// Display each status property as a badge
|
||||
// Display each status property as a badge or input
|
||||
const stats = Object.entries(rule.status).map(([key, value]) => {
|
||||
let displayValue = value;
|
||||
let isEditable = key === 'storedDuration';
|
||||
|
||||
if (key.toLowerCase().includes('duration') && typeof value === 'number') {
|
||||
const secs = Math.round(value / 1000);
|
||||
if (isEditable) {
|
||||
return \`
|
||||
<div class="rule-stat editable">
|
||||
<span class="rule-stat-label">\${key}:</span>
|
||||
<input type="number" class="rule-input" value="\${secs}" min="1" max="300"
|
||||
onchange="sendConfig('\${rule.name}', '\${key}', this.value * 1000)"
|
||||
style="width: 50px; background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 2px 4px; font-family: monospace;">
|
||||
<span style="color: var(--text-secondary);">s</span>
|
||||
</div>
|
||||
\`;
|
||||
}
|
||||
displayValue = formatDuration(value);
|
||||
} else if (typeof value === 'boolean') {
|
||||
displayValue = value ? '✓' : '✗';
|
||||
@@ -908,6 +937,29 @@ wss.on('connection', async (ws) => {
|
||||
console.error('[Status] Error fetching initial data:', err);
|
||||
}
|
||||
|
||||
ws.on('message', async (message) => {
|
||||
try {
|
||||
const data = JSON.parse(message);
|
||||
if (data.type === 'set_config') {
|
||||
const { ruleName, key, value } = data;
|
||||
const success = setRuleConfig(ruleName, key, value);
|
||||
if (success) {
|
||||
// Broadcast updated status to all clients
|
||||
const rules = await getRulesStatus();
|
||||
const updateMsg = JSON.stringify({ type: 'rules_update', rules });
|
||||
for (const client of wsClients) {
|
||||
if (client.readyState === 1) {
|
||||
client.send(updateMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.send(JSON.stringify({ type: 'set_config_result', success }));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Status] Error processing message:', err);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('[Status] Browser client disconnected');
|
||||
wsClients.delete(ws);
|
||||
|
||||
Reference in New Issue
Block a user