diff --git a/templates/editor.html b/templates/editor.html
index 316015c..9dcd960 100644
--- a/templates/editor.html
+++ b/templates/editor.html
@@ -168,6 +168,22 @@ body{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(
.popup .sp-btn:active{background:var(--border)}
.popup .sp-val{font-size:32px;min-width:80px;text-align:center}
.popup .close-btn{width:160px;height:44px;border:none;border-radius:8px;background:var(--blue);color:var(--text);font-size:16px;font-weight:600;cursor:pointer}
+
+/* Automation editor */
+.auto-editor{width:700px;max-width:95vw;max-height:80vh;overflow-y:auto}
+.auto-editor h3{font-size:20px;margin-bottom:12px}
+.auto-rule{display:flex;gap:8px;align-items:center;padding:10px;background:var(--bg);border-radius:8px;margin-bottom:8px}
+.auto-rule select,.auto-rule input{padding:8px 10px;border:1px solid var(--border);border-radius:6px;background:var(--card);color:var(--text);font-size:14px}
+.auto-rule select{min-width:120px}
+.auto-rule input[type=number]{width:80px}
+.auto-rule .rule-del{width:32px;height:32px;border:none;border-radius:6px;background:rgba(255,23,68,0.2);color:var(--red);font-size:16px;cursor:pointer;flex-shrink:0}
+.auto-rule .rule-del:hover{background:var(--red);color:#fff}
+.auto-add-rule{width:100%;padding:12px;border:2px dashed var(--dim);border-radius:8px;background:none;color:var(--text2);font-size:14px;cursor:pointer;margin:8px 0}
+.auto-add-rule:hover{border-color:var(--blue);color:var(--blue)}
+.auto-btns{display:flex;gap:12px;justify-content:center;margin-top:16px}
+.auto-btns button{padding:10px 24px;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer}
+.auto-btns .save-btn{background:var(--green);color:#000}
+.auto-btns .cancel-btn{background:var(--card);color:var(--text);border:1px solid var(--border)}
@@ -499,27 +515,170 @@ function createCardEl(c) {
return el;
}
+let autoEditCard = null;
+
function editAutomation(c) {
- const rulesStr = (c.rules || []).map(r => `${r.type}:${r.target}:${r.action}:${r.value||''}`).join('\n');
- const help = `Edit automation rules (one per line):
-Format: type:target:action:value
+ autoEditCard = c;
+ // Gather all available controls from the layout
+ const allControls = [];
+ layout.pages.forEach(p => p.cards.forEach(card => {
+ if (card.id !== c.id) allControls.push(card);
+ }));
-Types: temp, motor, output
-Actions: set, on, off
-Examples:
- temp:Heat Input:set:130
- motor:Hot Fan:set:65
- motor:Conveyor:on
- output:Brush:on
- output:Mill:off
+ const popup = document.getElementById('popup');
+ popup.style.borderColor = 'var(--blue)';
-Current rules:`;
- const input = prompt(help, rulesStr);
- if (input === null) return;
- c.rules = input.split('\n').filter(l => l.trim()).map(l => {
- const [type, target, action, value] = l.split(':');
- return {type: type||'output', target: target||'', label: target||'', action: action||'on', value: value ? parseInt(value) : undefined};
+ let rulesHtml = '';
+ (c.rules || []).forEach((r, i) => {
+ rulesHtml += buildRuleRow(i, r, allControls);
});
+
+ popup.innerHTML = `
+
+
Edit Automation: ${c.label}
+
+ When this automation is activated, all these actions happen at once.
+ Add what should turn on, set speeds, or adjust temperatures.
+
+
${rulesHtml}
+
+
+
+
+
+
`;
+ document.getElementById('popup-overlay').classList.add('active');
+}
+
+function buildRuleRow(idx, rule, controls) {
+ if (!controls) {
+ controls = [];
+ layout.pages.forEach(p => p.cards.forEach(c => { if(autoEditCard && c.id !== autoEditCard.id) controls.push(c); }));
+ }
+
+ // Group controls by type
+ const temps = controls.filter(c => c.type === 'temp');
+ const motors = controls.filter(c => c.type === 'motor');
+ const outputs = controls.filter(c => c.type === 'output' || c.type === 'burner');
+
+ // What to control dropdown
+ let targetOpts = '';
+ if (temps.length) {
+ targetOpts += '';
+ }
+ if (motors.length) {
+ targetOpts += '';
+ }
+ if (outputs.length) {
+ targetOpts += '';
+ }
+
+ // Action depends on type
+ const isTemp = rule.type === 'temp';
+ const isMotor = rule.type === 'motor';
+ const isOutput = rule.type === 'output' || rule.type === 'burner';
+
+ let actionOpts = '';
+ if (isTemp) {
+ actionOpts = ``;
+ } else if (isMotor) {
+ actionOpts = `
+
+
+ `;
+ } else {
+ actionOpts = `
+
+ `;
+ }
+
+ const showValue = rule.action === 'set';
+ const valueUnit = isTemp ? '°C' : '%';
+ const valueMax = isTemp ? 200 : 100;
+ const valueStep = isTemp ? 1 : 5;
+
+ return `
+
+
+
+
+
+ ${valueUnit}
+
+
+
`;
+}
+
+function autoRuleTargetChanged(sel, idx) {
+ const opt = sel.options[sel.selectedIndex];
+ const type = opt.dataset.type || 'output';
+ // Store temporarily so we can rebuild the action dropdown
+ const rules = collectAutoRules();
+ if (rules[idx]) {
+ rules[idx].target = sel.value;
+ rules[idx].type = type;
+ rules[idx].label = opt.textContent;
+ // Reset action based on type
+ if (type === 'temp') rules[idx].action = 'set';
+ else rules[idx].action = 'on';
+ }
+ autoEditCard.rules = rules;
+ editAutomation(autoEditCard); // re-render the whole popup
+}
+
+function autoRuleActionChanged(idx) {
+ const actionSel = document.querySelector(`.rule-action[data-idx="${idx}"]`);
+ const valueWrap = document.querySelectorAll('.rule-value-wrap')[idx];
+ if (actionSel && valueWrap) {
+ valueWrap.style.display = actionSel.value === 'set' ? 'flex' : 'none';
+ }
+}
+
+function addAutoRule() {
+ if (!autoEditCard) return;
+ const rules = collectAutoRules();
+ rules.push({type:'output', target:'', label:'', action:'on', value:0});
+ autoEditCard.rules = rules;
+ editAutomation(autoEditCard);
+}
+
+function removeAutoRule(idx) {
+ if (!autoEditCard) return;
+ const rules = collectAutoRules();
+ rules.splice(idx, 1);
+ autoEditCard.rules = rules;
+ editAutomation(autoEditCard);
+}
+
+function collectAutoRules() {
+ const rows = document.querySelectorAll('.auto-rule');
+ const rules = [];
+ rows.forEach(row => {
+ const targetSel = row.querySelector('select');
+ const actionSel = row.querySelector('.rule-action');
+ const valueInput = row.querySelector('.rule-value');
+ const opt = targetSel.options[targetSel.selectedIndex];
+ rules.push({
+ type: opt?.dataset?.type || 'output',
+ target: targetSel.value,
+ label: opt?.textContent || '',
+ action: actionSel?.value || 'on',
+ value: actionSel?.value === 'set' ? parseInt(valueInput?.value || 0) : undefined
+ });
+ });
+ return rules;
+}
+
+function saveAutoRules() {
+ if (!autoEditCard) return;
+ autoEditCard.rules = collectAutoRules().filter(r => r.target); // remove empty rows
+ closePopup();
renderCards();
}