//This is code for the modification of rules (function(){ const APPLY_URL = 'https://control.richardjolley.co.uk/cgi-bin/automation-rules-apply.cgi'; const GET_URL = 'https://control.richardjolley.co.uk/cgi-bin/automation-rules-get.cgi'; const el = id => document.getElementById(id); const modeEl = el('mode'); const nameEl = el('rule-name'); const idEl = el('match-id'); const stEl = el('match-state'); const cdEl = el('cooldown'); const typeEl = el('action-type'); const topicEl= el('action-topic'); const payloadEl = el('action-payload'); const deviceEl = el('action-device'); const statusEl = el('rule-status'); function setStatus(msg, ok=true) { statusEl.textContent = msg || ''; statusEl.style.color = ok ? 'inherit' : 'crimson'; } function showActionFields() { const t = typeEl.value; document.querySelectorAll('#rule-editor-card .action-row').forEach(row => { const forList = (row.getAttribute('data-for') || '').split(/\s+/); row.style.display = forList.includes(t) ? '' : 'none'; }); } typeEl.addEventListener('change', showActionFields); showActionFields(); // Presets document.querySelectorAll('#rule-editor-card .preset').forEach(btn => { btn.addEventListener('click', () => { const p = btn.getAttribute('data-preset'); // Common defaults nameEl.value = ''; idEl.value = 0; stEl.value = '1'; cdEl.value = 200; if (p === 'plug-toggle') { nameEl.value = 's2_toggle_plug_on_press'; idEl.value = 1; typeEl.value = 'mqtt_publish'; topicEl.value = 'office-plug-lamp/rpc'; payloadEl.value = JSON.stringify({ method: "Switch.Toggle", params: { id: 0 } }, null, 2); } else if (p === 'bulb-toggle-legacy') { nameEl.value = 's1_toggle_legacy_bulb'; idEl.value = 0; typeEl.value = 'mqtt_publish_toggle_legacy'; deviceEl.value = 'office-bulb'; } else if (p === 'bulb-brightness-up') { nameEl.value = 's1_brightness_up'; idEl.value = 0; typeEl.value = 'mqtt_publish_json'; // Adjust to your device’s topic. For legacy Gen1 bulbs, often: shellies//color/0/set topicEl.value = 'shellies/office-bulb/color/0/set'; payloadEl.value = JSON.stringify({ brightness_change: +10 }, null, 2); } else if (p === 'bulb-brightness-down') { nameEl.value = 's1_brightness_down'; idEl.value = 0; typeEl.value = 'mqtt_publish_json'; topicEl.value = 'shellies/office-bulb/color/0/set'; payloadEl.value = JSON.stringify({ brightness_change: -10 }, null, 2); } showActionFields(); }); }); // Submit el('rule-form').addEventListener('submit', async (ev) => { ev.preventDefault(); setStatus('Applying…'); // Build rule const rule = { name: String(nameEl.value || '').trim(), match: { method: "NotifyInput", params: { id: Number(idEl.value), state: Number(stEl.value) } }, actions: [], }; const cd = Number(cdEl.value); if (!isNaN(cd) && cd > 0) rule.cooldown_ms = cd; const t = typeEl.value; if (t === 'mqtt_publish' || t === 'mqtt_publish_json') { const topic = String(topicEl.value || '').trim(); if (!topic) { setStatus('Topic is required for mqtt_publish*', false); return; } let payload = payloadEl.value.trim(); let payloadVal = null; // For mqtt_publish_json we require valid JSON if (t === 'mqtt_publish_json') { try { payloadVal = payload ? JSON.parse(payload) : null; } catch (e) { setStatus('Payload must be valid JSON', false); return; } rule.actions.push({ type: t, topic, payload: payloadVal }); } else { // allow raw strings OR JSON (we won’t parse) try { payloadVal = payload ? JSON.parse(payload) : null; } catch { payloadVal = payload; } // keep as string rule.actions.push({ type: t, topic, payload: payloadVal }); } } else if (t === 'mqtt_publish_toggle_legacy') { const device = String(deviceEl.value || '').trim(); if (!device) { setStatus('Legacy device name is required', false); return; } rule.actions.push({ type: t, device }); } else { setStatus('Unsupported action type', false); return; } const payloadOut = (modeEl.value === 'replace') ? { mode: 'replace', rules: [ rule ] } // start simple: replace with one rule : { mode: 'append', rule }; try { const res = await fetch(APPLY_URL, { method: 'POST', cache: 'no-store', credentials: 'omit', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payloadOut) }); const ct = res.headers.get('content-type') || ''; const text = await res.text(); if (!ct.includes('application/json')) throw new Error(`Expected JSON, got ${ct}: ${text}`); const data = JSON.parse(text); if (!res.ok || !data.ok) throw new Error(data.error || `HTTP ${res.status}`); setStatus(`Applied ✔ (backup: ${data.backup})`); // Refresh the read-only card if you kept my function name; else call your own reload try { if (typeof loadBridgeRules === 'function') await loadBridgeRules(); } catch {} } catch (e) { setStatus(`Failed: ${e.message}`, false); console.error(e); } }); })();