AutoAction-Runner: Automatiken ausfuehren
Bisher konnte man Automatiken nur anlegen - ausgefuehrt hat sie niemand. restricted/autoActions/autoaction_runner.py holt das nach. Dauerlaeufer statt Cronjob, aus zwei Gruenden: Schwellwert-Ausloeser sollen greifen, wenn die MQTT-Nachricht hereinkommt, und actor_states.current_value wird sonst von niemandem fortgeschrieben - beim Discovery einmal gesetzt und danach nie wieder. Ein zustandsloser Lauf haette gar nichts, womit er vergleichen koennte. Der Runner pflegt den Wert nebenbei mit, wovon auch der Editor profitiert: er zeigt neben jedem Messwert den aktuellen Stand. Ausgeloest wird nur auf der steigenden Flanke (automations.cond_met), sonst wuerde "Temperatur ueber 22 Grad" bei jedem Takt erneut feuern. Aus demselben Grund heissen Zeit-Ausloeser jetzt "ab 16:30" statt "gleich 16:30": ein Gleichheitsvergleich waere nur in einer einzigen Minute wahr, und ein Ausfall in genau dieser Minute kostet den ganzen Tag. Dieselbe Ueberlegung steht hinter den breiten Zeitfenstern in auto_watering.py. Der Weg zum Geraet haengt an der URL des Aktors: mqtt:// abonniert und publiziert, http:// pollt und haengt Parameter an, io:// spricht mit der Tahoma-Box, Logic rechnet Uhrzeit, Datum und Sonnenzeiten. Alle vier stehen in transports.py; eine fuenfte Geraeteart ist eine weitere Klasse mit passt(), zustaende_lesen() und senden(). fetch_calendar.py fuellt calendar_days aus openholidaysapi.org, damit "in den Ferien" und "an Feiertagen" eine Grundlage haben. Einmal jaehrlich per Cron. Die Uebersicht blendet auf schmalen Schirmen Spalten aus, statt sie wegzuschieben - auf dem Handy waren die Knoepfe zum Pausieren und Loeschen sonst nur per Seitwaertsscrollen erreichbar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,8 +42,9 @@ function commandById(id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function operatorLabel(op) {
|
||||
return op === "!=" ? "≠" : op;
|
||||
function operatorLabel(state, op) {
|
||||
const treffer = state.operators.find(o => o.value === op);
|
||||
return treffer ? treffer.label : op;
|
||||
}
|
||||
|
||||
/** Erster Messwert des ersten Geraets - Vorbelegung fuer eine neue Zeile. */
|
||||
@@ -150,7 +151,7 @@ function renderConditions() {
|
||||
add.onclick = () => {
|
||||
const state = ersterMesswert();
|
||||
if (!state) return;
|
||||
gruppe.push({ state_id: state.id, operator: state.operators[0], value: standardWert(state) });
|
||||
gruppe.push({ state_id: state.id, operator: state.operators[0].value, value: standardWert(state) });
|
||||
renderConditions();
|
||||
};
|
||||
block.appendChild(add);
|
||||
@@ -179,7 +180,7 @@ function conditionRow(gi, bi, bed) {
|
||||
const neu = deviceById(Number(geraet.value));
|
||||
if (!neu || !neu.states.length) return;
|
||||
bed.state_id = neu.states[0].id;
|
||||
bed.operator = neu.states[0].operators[0];
|
||||
bed.operator = neu.states[0].operators[0].value;
|
||||
bed.value = standardWert(neu.states[0]);
|
||||
renderConditions();
|
||||
};
|
||||
@@ -196,7 +197,7 @@ function conditionRow(gi, bi, bed) {
|
||||
messwert.onchange = () => {
|
||||
const neu = dev.states.find(s => s.id === Number(messwert.value));
|
||||
bed.state_id = neu.id;
|
||||
bed.operator = neu.operators[0];
|
||||
bed.operator = neu.operators[0].value;
|
||||
bed.value = standardWert(neu);
|
||||
renderConditions();
|
||||
};
|
||||
@@ -212,7 +213,7 @@ function conditionRow(gi, bi, bed) {
|
||||
// schiebt er das Zeichen aus dem sichtbaren Bereich.
|
||||
op.className = "form-select flex-grow-0 text-center";
|
||||
op.style.flex = "0 0 5.5rem";
|
||||
optionListe(op, state.operators.map(o => ({ value: o, text: operatorLabel(o) })), bed.operator);
|
||||
optionListe(op, state.operators.map(o => ({ value: o.value, text: o.label })), bed.operator);
|
||||
op.onchange = () => { bed.operator = op.value; renderSummary(); };
|
||||
zeile.appendChild(op);
|
||||
|
||||
@@ -254,7 +255,7 @@ function conditionText() {
|
||||
const treffer = stateById(bed.state_id);
|
||||
if (!treffer) return "?";
|
||||
let s = treffer.device.name + ": " + treffer.state.name + " "
|
||||
+ operatorLabel(bed.operator) + " " + bed.value;
|
||||
+ operatorLabel(treffer.state, bed.operator) + " " + bed.value;
|
||||
if (treffer.state.unit) s += " " + treffer.state.unit;
|
||||
return s;
|
||||
}).join(" und ");
|
||||
@@ -426,7 +427,7 @@ function loadAutomatic() {
|
||||
if (!autoModel.groups.length) {
|
||||
const state = ersterMesswert();
|
||||
if (state) {
|
||||
autoModel.groups = [[{ state_id: state.id, operator: state.operators[0], value: standardWert(state) }]];
|
||||
autoModel.groups = [[{ state_id: state.id, operator: state.operators[0].value, value: standardWert(state) }]];
|
||||
}
|
||||
}
|
||||
if (!autoModel.actions.length) {
|
||||
@@ -472,7 +473,7 @@ function openAutoActionModal(params) {
|
||||
document.getElementById("btnAddGroup").addEventListener("click", () => {
|
||||
const state = ersterMesswert();
|
||||
if (!state) return;
|
||||
autoModel.groups.push([{ state_id: state.id, operator: state.operators[0], value: standardWert(state) }]);
|
||||
autoModel.groups.push([{ state_id: state.id, operator: state.operators[0].value, value: standardWert(state) }]);
|
||||
renderConditions();
|
||||
});
|
||||
document.getElementById("btnAddActor").addEventListener("click", () => {
|
||||
|
||||
Reference in New Issue
Block a user