Feature: EMS-Konfiguration im Web-UI (v1.5.1)

- Modal zeigt EMS-Felder nur bei KATHREIN_WALLBOX (toggleEmsSection)
- openModal lädt ems_min_pv/timeout/target_hour/phases aus Gerätekonfig
- saveInverter speichert EMS-Parameter in inverters.json
- main.py übergibt EMS-Konfig aus inv_cfg an EmsController()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
retr0
2026-04-28 12:11:13 +02:00
parent ac965dcfa6
commit a09bbdd25d
2 changed files with 51 additions and 3 deletions
+45 -2
View File
@@ -226,6 +226,26 @@
<input type="text" id="modal-prefix" placeholder="growatt/wechselrichter1"></div>
<div class="field"><label>Abfrageintervall (Sekunden)</label>
<input type="number" id="modal-interval" value="30" min="5"></div>
<div id="ems-section" style="display:none; border-top:1px solid var(--border); margin-top:14px; padding-top:14px;">
<div style="font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--accent); margin-bottom:12px;">EMS — Ladesteuerung</div>
<div class="field"><label>Mindest-PV-Überschuss (W)</label>
<input type="number" id="ems-min-pv" value="1400" min="100" step="100">
<div style="font-size:11px;color:var(--muted);margin-top:3px">Mindestleistung für PV-Laden (min. 6A × 230V = 1380W)</div></div>
<div class="field"><label>Timeout ohne PV (Stunden)</label>
<input type="number" id="ems-timeout" value="4" min="0.5" max="24" step="0.5">
<div style="font-size:11px;color:var(--muted);margin-top:3px">Nach dieser Zeit ohne Überschuss startet Zwangsladen</div></div>
<div class="field"><label>Vollladung bis (Uhr)</label>
<input type="number" id="ems-target-hour" value="6" min="0" max="23">
<div style="font-size:11px;color:var(--muted);margin-top:3px">Zielzeit für vollgeladenes Auto (z.B. 6 = 06:00 Uhr)</div></div>
<div class="field"><label>Anzahl Phasen</label>
<select id="ems-phases">
<option value="1">1-phasig</option>
<option value="2">2-phasig</option>
<option value="3" selected>3-phasig</option>
</select></div>
</div>
<div class="modal-actions">
<button class="btn" onclick="closeModal()">Abbrechen</button>
<button class="btn btn-primary" onclick="saveInverter()">Speichern</button>
@@ -437,20 +457,31 @@ function renderInverterList() {
el.innerHTML = cards + `<div class="add-btn" onclick="openModal()"> Gerät hinzufügen</div>`;
}
function toggleEmsSection(model) {
document.getElementById("ems-section").style.display =
model === "KATHREIN_WALLBOX" ? "" : "none";
}
async function openModal(invId) {
await loadModels();
const modal = document.getElementById("modal-backdrop");
const modelSel = document.getElementById("modal-model");
modelSel.onchange = () => toggleEmsSection(modelSel.value);
if (invId) {
const inv = invertersList.find(i => i.id === invId) || {};
document.getElementById("modal-title").textContent = "Gerät bearbeiten";
document.getElementById("modal-id").value = inv.id || "";
document.getElementById("modal-name").value = inv.name || "";
document.getElementById("modal-model").value = inv.inverter_model || "MIC_1500_TL_X";
modelSel.value = inv.inverter_model || "MIC_1500_TL_X";
document.getElementById("modal-ip").value = inv.modbus_ip || "";
document.getElementById("modal-port").value = inv.modbus_port || 502;
document.getElementById("modal-addr").value = inv.modbus_address || 1;
document.getElementById("modal-prefix").value = inv.mqtt_topic_prefix || "";
document.getElementById("modal-interval").value = inv.update_interval || 30;
document.getElementById("ems-min-pv").value = inv.ems_min_pv ?? 1400;
document.getElementById("ems-timeout").value = inv.ems_timeout ?? 4;
document.getElementById("ems-target-hour").value = inv.ems_target_hour ?? 6;
document.getElementById("ems-phases").value = inv.ems_phases ?? 3;
} else {
document.getElementById("modal-title").textContent = "Gerät hinzufügen";
document.getElementById("modal-id").value = "";
@@ -460,7 +491,12 @@ async function openModal(invId) {
document.getElementById("modal-addr").value = "1";
document.getElementById("modal-prefix").value = "growatt/wechselrichter" + (invertersList.length + 1);
document.getElementById("modal-interval").value = "30";
document.getElementById("ems-min-pv").value = "1400";
document.getElementById("ems-timeout").value = "4";
document.getElementById("ems-target-hour").value = "6";
document.getElementById("ems-phases").value = "3";
}
toggleEmsSection(modelSel.value);
modal.classList.add("open");
}
@@ -477,16 +513,23 @@ async function saveInverter() {
const r = await fetchJSON(api("api/new-id"), {method: "POST"});
id = r.id;
}
const model = document.getElementById("modal-model").value;
const inv = {
id,
name: document.getElementById("modal-name").value.trim() || "Wechselrichter",
inverter_model: document.getElementById("modal-model").value,
inverter_model: model,
modbus_ip: document.getElementById("modal-ip").value.trim(),
modbus_port: parseInt(document.getElementById("modal-port").value),
modbus_address: parseInt(document.getElementById("modal-addr").value),
mqtt_topic_prefix: document.getElementById("modal-prefix").value.trim(),
update_interval: parseInt(document.getElementById("modal-interval").value),
};
if (model === "KATHREIN_WALLBOX") {
inv.ems_min_pv = parseInt(document.getElementById("ems-min-pv").value);
inv.ems_timeout = parseFloat(document.getElementById("ems-timeout").value);
inv.ems_target_hour = parseInt(document.getElementById("ems-target-hour").value);
inv.ems_phases = parseInt(document.getElementById("ems-phases").value);
}
const idx = invertersList.findIndex(i => i.id === id);
if (idx >= 0) invertersList[idx] = inv; else invertersList.push(inv);
try {