Heizstab- und Bewaesserungsfenster im neuen Look, Bewaesserungszeilen enger
Beide Fenster lesen den Zustand live aus MQTT und uebernehmen jede Wahl sofort (bedienfenster.js). submitFormAjax entfaellt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+11
-157
@@ -443,9 +443,9 @@ var chartSettings = {
|
||||
|
||||
|
||||
function openModal(type) {
|
||||
// Das Ladefenster und die Bewaesserung uebernehmen jede Aenderung sofort -
|
||||
// "Save changes" braucht nur noch der Heizstab.
|
||||
document.getElementById("modalSaveBtn").style.display = type === "heater" ? "" : "none";
|
||||
// Alle Fenster der Solar-Seite uebernehmen jede Aenderung sofort -
|
||||
// "Save changes" braucht hier keines mehr.
|
||||
document.getElementById("modalSaveBtn").style.display = "none";
|
||||
switch(type){
|
||||
case "CarEG":
|
||||
document.getElementById("modal-title").innerHTML = "Autoladung EG";
|
||||
@@ -471,8 +471,6 @@ var chartSettings = {
|
||||
|
||||
modalBodyElement = document.getElementById('modal-body');
|
||||
modalBodyElement.innerHTML = loadingHTML("Wird geladen...");
|
||||
document.getElementById("modalSaveBtn").addEventListener("click", submitFormAjax);
|
||||
document.getElementById("modalSaveBtn").contentURL = contentURL;
|
||||
modalEV.show();
|
||||
fetch(contentURL, {
|
||||
method: 'GET',
|
||||
@@ -485,25 +483,17 @@ var chartSettings = {
|
||||
.then(html => {
|
||||
modalBodyElement.innerHTML = html;
|
||||
|
||||
var slider = document.getElementById("modal-slider");
|
||||
var span = document.getElementById("modal-slider-label");
|
||||
// Aufbau und Logik der Fenster: ladefenster.js und bedienfenster.js.
|
||||
switch(type){
|
||||
case "CarEG":
|
||||
case "CarOG":
|
||||
// Alles Weitere in js/solar/ladefenster.js.
|
||||
ladefensterStarten(modalBodyElement.querySelector(".lade"), contentURL);
|
||||
break;
|
||||
case "heater":
|
||||
bindModalSlider(slider, span, value => value == 0 ? "Automatik" : (value / 10) + " kW");
|
||||
if(mqttData["solarManager/heatMode"] == "eco"){
|
||||
document.getElementById("modal-slider").value = 0;
|
||||
}else{
|
||||
document.getElementById("modal-slider").value = Math.round(mqttData["solarManager/pHeat"]/100);
|
||||
}
|
||||
document.getElementById("modal-slider").dispatchEvent(new Event('input'));
|
||||
break;
|
||||
case "heater":
|
||||
heizfensterStarten(modalBodyElement.querySelector(".heiz"), contentURL);
|
||||
break;
|
||||
case "watering":
|
||||
initializeWateringModal();
|
||||
wasserfensterStarten(modalBodyElement.querySelector(".wasser"), contentURL);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -513,146 +503,10 @@ var chartSettings = {
|
||||
});
|
||||
}
|
||||
|
||||
function wateringCommand(topic, command, directTopic = false) {
|
||||
const feedback = document.getElementById("watering-feedback");
|
||||
const targetTopic = directTopic ? topic : topic + "/set";
|
||||
const payload = typeof command === "string" ? command : JSON.stringify(command);
|
||||
const endpoint = window.location.pathname.replace(/\/index\.php$/, "").replace(/\/$/, "") + "/ajax/watering.php";
|
||||
const showFeedback = (message, type) => {
|
||||
if (!feedback) return;
|
||||
feedback.textContent = message;
|
||||
feedback.className = "alert alert-" + type + " mb-3";
|
||||
feedback.style.whiteSpace = "pre-line";
|
||||
};
|
||||
fetch(endpoint, {
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams({ topic: targetTopic, payload: payload })
|
||||
}).then(async (response) => {
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result.success) throw new Error(result.message || "Unbekannter MQTT-Fehler.");
|
||||
showFeedback("Befehl gesendet.\nTopic: " + result.topic + "\nInhalt: " + result.payload, "success");
|
||||
}).catch((error) => {
|
||||
showFeedback("Befehl konnte nicht gesendet werden: " + error.message + "\nTopic: " + targetTopic + "\nInhalt: " + payload, "danger");
|
||||
});
|
||||
}
|
||||
|
||||
function initializeWateringModal() {
|
||||
document.querySelectorAll("[data-watering-toggle]").forEach((button) => {
|
||||
const updateButton = () => {
|
||||
const zone = wateringZones[button.dataset.wateringZone];
|
||||
setzeZonenButton(button, wateringLabel(zone.topic, zone.valve));
|
||||
};
|
||||
button.addEventListener("click", () => {
|
||||
const zone = wateringZones[button.dataset.wateringZone];
|
||||
if (wateringLabel(zone.topic, zone.valve) === "aus") {
|
||||
const timerInput = document.querySelector(`[data-watering-timer='${button.dataset.wateringZone}']`);
|
||||
const timer = Math.min(120, Math.max(1, Number(timerInput.value) || 30));
|
||||
wateringCommand(zone.topic, { valve: zone.valve, set: "open", timer: timer });
|
||||
} else {
|
||||
wateringCommand(zone.topic, { valve: zone.valve, set: "close" });
|
||||
}
|
||||
});
|
||||
updateButton();
|
||||
});
|
||||
document.querySelectorAll("[data-watering-auto-toggle]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const topics = (button.dataset.wateringTopics || button.dataset.wateringTopic).split(",");
|
||||
const modes = button.dataset.wateringAuto.split(",");
|
||||
const active = topics.every((topic, index) => wateringAutoActive(topic, modes[index]));
|
||||
topics.forEach((topic, index) => {
|
||||
wateringCommand(topic + "/auto", active ? "Stop" : modes[index], true);
|
||||
});
|
||||
});
|
||||
updateWateringAutoButton(button);
|
||||
});
|
||||
updateWateringStatus();
|
||||
}
|
||||
|
||||
// Die Zonensymbole im Bewaesserungskreis; das Fenster aktualisiert sich
|
||||
// selbst (bedienfenster.js).
|
||||
function updateWateringStatus() {
|
||||
wateringZoneList.forEach(([key, zone]) => {
|
||||
const state = wateringLabel(zone.topic, zone.valve);
|
||||
const paused = wateringIsPaused(zone.topic, zone.valve);
|
||||
const heute = wateringDayTotal(zone.topic, "WateringToday", "wateringDurationTodaySecs", zone.valve, true);
|
||||
// Die Regenautomatik laeuft zum Sonnenaufgang; wenn jemand auf die
|
||||
// Anzeige schaut, ist die Entscheidung laengst gefallen. Angezeigt wird
|
||||
// deshalb nicht mehr, was geplant ist, sondern was passiert ist. Der
|
||||
// Zaehler unterscheidet nicht, wer ausgeloest hat - eine Bewaesserung
|
||||
// von Hand zaehlt genauso, und das ist so gewollt.
|
||||
const bewaessert = state === "aus" && heute.sekunden > 0;
|
||||
|
||||
// Die Anzeigen liegen im Realtime-SVG und bleiben bestehen -> svgElement.
|
||||
const status = svgElement(zone.statusId);
|
||||
const icon = svgElement(zone.iconId);
|
||||
if (status && icon) updateWateringIndicator(status, icon, zone.label, state, paused, bewaessert);
|
||||
|
||||
// Die Modal-Elemente werden bei jedem Oeffnen neu erzeugt -> nicht cachen.
|
||||
setzeZonenButton(document.querySelector(`[data-watering-toggle][data-watering-zone='${key}']`), state);
|
||||
setzeSchild(`[data-watering-state='${key}']`, state, paused, bewaessert);
|
||||
|
||||
const history = wateringHistory(zone.topic, zone.valve);
|
||||
setzeZelle(`[data-watering-last='${key}']`, history.dateTime);
|
||||
setzeZelle(`[data-watering-duration='${key}']`, history.duration);
|
||||
const vortag = wateringDayTotal(zone.topic, "LastWateringDay", "wateringDurationDaySecs", zone.valve, false);
|
||||
setzeZelle(`[data-watering-today='${key}']`, heute.label, heute.day && "Stand: " + heute.day);
|
||||
setzeZelle(`[data-watering-prevday='${key}']`, vortag.label, vortag.day && "Bewässerungstag: " + vortag.day);
|
||||
});
|
||||
document.querySelectorAll("[data-watering-auto-toggle]").forEach(updateWateringAutoButton);
|
||||
}
|
||||
|
||||
// Der Knopf zeigt die naechste Aktion, nicht den Zustand: laeuft die Zone,
|
||||
// steht "Aus" darauf.
|
||||
function setzeZonenButton(button, state) {
|
||||
if (!button) return;
|
||||
const beschriftung = state === "aus" ? "Ein" : "Aus";
|
||||
if (button.textContent !== beschriftung) button.textContent = beschriftung;
|
||||
button.classList.toggle("btn-success", state === "aus");
|
||||
button.classList.toggle("btn-danger", state !== "aus");
|
||||
}
|
||||
|
||||
// Zustandsschild der Zonenkarte, Farben wie im SVG: laufend gruen,
|
||||
// pausiert gelb, heute schon bewaessert blau, sonst grau.
|
||||
function setzeSchild(selektor, state, paused, bewaessert) {
|
||||
const schild = document.querySelector(selektor);
|
||||
if (!schild) return;
|
||||
let text = "unbekannt";
|
||||
let farbe = "bg-secondary";
|
||||
if (state !== "--" && state !== "aus") {
|
||||
text = paused ? "pausiert " + state : "läuft " + state;
|
||||
farbe = paused ? "bg-warning text-dark" : "bg-success";
|
||||
} else if (state === "aus") {
|
||||
text = bewaessert ? "heute bewässert" : "aus";
|
||||
farbe = bewaessert ? "bg-primary" : "bg-secondary";
|
||||
}
|
||||
if (schild.textContent !== text) schild.textContent = text;
|
||||
const klasse = "badge " + farbe;
|
||||
if (schild.className !== klasse) schild.className = klasse;
|
||||
}
|
||||
|
||||
// Schreibt nur bei echter Aenderung - die Tabelle wird im Sekundentakt
|
||||
// aktualisiert, ihre Werte aendern sich aber hoechstens minuetlich.
|
||||
function setzeZelle(selektor, text, titel) {
|
||||
const element = document.querySelector(selektor);
|
||||
if (!element) return;
|
||||
if (element.textContent !== text) element.textContent = text;
|
||||
if (titel) {
|
||||
if (element.getAttribute("title") !== titel) element.setAttribute("title", titel);
|
||||
} else if (element.hasAttribute("title")) {
|
||||
element.removeAttribute("title");
|
||||
}
|
||||
}
|
||||
|
||||
function updateWateringAutoButton(button) {
|
||||
const topics = (button.dataset.wateringTopics || button.dataset.wateringTopic).split(",");
|
||||
const modes = button.dataset.wateringAuto.split(",");
|
||||
const active = topics.every((topic, index) => wateringAutoActive(topic, modes[index]));
|
||||
if (!button.dataset.wateringLabel) {
|
||||
button.dataset.wateringLabel = button.textContent.replace(/^Automatik\s+/, "");
|
||||
}
|
||||
button.textContent = (active ? "Aus: " : "Ein: ") + button.dataset.wateringLabel;
|
||||
button.classList.toggle("btn-outline-primary", !active);
|
||||
button.classList.toggle("btn-outline-danger", active);
|
||||
wateringSymboleSetzen(svgElement);
|
||||
}
|
||||
|
||||
window.setInterval(updateWateringStatus, 1000);
|
||||
|
||||
Reference in New Issue
Block a user