tutoring/app/Http/Controllers/Home Server/create-new-automation-rule.js

140 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//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 devices topic. For legacy Gen1 bulbs, often: shellies/<dev>/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 wont 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);
}
});
})();