140 lines
5.3 KiB
JavaScript
140 lines
5.3 KiB
JavaScript
//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/<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 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);
|
||
}
|
||
});
|
||
})(); |