Fix: Stromtarif-Einstellungen nach Neustart verloren + Finanzen-Layout (v1.8.22)

State.mqtt_cfg wurde beim Start nur mit 4 MQTT-Keys initialisiert — alle
Tarif/Billing-Keys fehlten, wurden nach Neustart auf Defaults zurückgesetzt.
Fix: alle persistenten Keys aus load_config() in State.mqtt_cfg übernehmen.

Finanzen-Tab: mehr Abstände, größere Karten (22px Wert), Abschnittsüberschriften,
Trennlinie vor dem Chart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
retr0
2026-05-06 08:26:52 +02:00
parent cb5f23d486
commit 512b743b16
4 changed files with 37 additions and 26 deletions
+1
View File
@@ -96,3 +96,4 @@
| v1.8.19 | 2026-05-05 | Fix: Mobile-Layout — Tabs scrollen, kein horizontaler Overflow | | v1.8.19 | 2026-05-05 | Fix: Mobile-Layout — Tabs scrollen, kein horizontaler Overflow |
| v1.8.20 | 2026-05-05 | Fix: Eigenversorgungskarte bei PV offline, Stromtarif-Einstellungen gehen nicht verloren | | v1.8.20 | 2026-05-05 | Fix: Eigenversorgungskarte bei PV offline, Stromtarif-Einstellungen gehen nicht verloren |
| v1.8.21 | 2026-05-05 | Fix: Finanzen-Tab bleibt nicht bei "Lade..." hängen (fEur/fKwh Scope-Bug) | | v1.8.21 | 2026-05-05 | Fix: Finanzen-Tab bleibt nicht bei "Lade..." hängen (fEur/fKwh Scope-Bug) |
| v1.8.22 | 2026-05-06 | Fix: Stromtarif-Einstellungen bleiben nach Neustart erhalten; Finanzen-Tab Layout |
+1 -1
View File
@@ -1,5 +1,5 @@
name: ShineBridge name: ShineBridge
version: "1.8.21" version: "1.8.22"
slug: shinebridge slug: shinebridge
description: Growatt Wechselrichter lokal in Home Assistant — Modbus TCP via ShineLAN-X, MQTT Discovery, Web UI description: Growatt Wechselrichter lokal in Home Assistant — Modbus TCP via ShineLAN-X, MQTT Discovery, Web UI
url: https://gitea.bitfire.work/retr0/shinebridge url: https://gitea.bitfire.work/retr0/shinebridge
+6 -2
View File
@@ -952,8 +952,12 @@ if __name__ == "__main__":
history.init_db() history.init_db()
cfg = load_config() cfg = load_config()
with State.lock: with State.lock:
State.mqtt_cfg = {k: cfg[k] for k in State.mqtt_cfg = {k: cfg[k] for k in (
("mqtt_broker", "mqtt_port", "mqtt_user", "mqtt_pass")} "mqtt_broker", "mqtt_port", "mqtt_user", "mqtt_pass",
"price_import", "price_export", "billing_day", "billing_month",
"tariff_type", "spot_country", "spot_markup", "spot_chart",
"billing_tracker_enabled", "monthly_rate_eur", "grundpreis_eur_per_month",
) if k in cfg}
State.inverters_cfg = cfg.get("inverters", []) State.inverters_cfg = cfg.get("inverters", [])
State.surplus_devices_cfg = cfg.get("surplus_devices", []) State.surplus_devices_cfg = cfg.get("surplus_devices", [])
State.z2m_base = cfg.get("z2m_base", "zigbee2mqtt") State.z2m_base = cfg.get("z2m_base", "zigbee2mqtt")
+22 -16
View File
@@ -957,34 +957,36 @@ async function loadFinance() {
const lohnt = savings_eur > 0; const lohnt = savings_eur > 0;
const col = lohnt ? C.green : C.red; const col = lohnt ? C.green : C.red;
const icon = lohnt ? '✓' : '✗'; const icon = lohnt ? '✓' : '✗';
empfehlung = `<div style="display:flex;align-items:center;justify-content:space-between;background:var(--surface);border:1px solid ${col};border-radius:var(--radius);padding:14px 18px;margin-bottom:16px"> empfehlung = `<div style="display:flex;align-items:center;justify-content:space-between;background:var(--surface);border:1px solid ${col};border-radius:var(--radius);padding:18px 22px;margin-bottom:24px">
<div> <div>
<div style="font-weight:700;font-size:15px;color:${col}">${icon} Flexibler Tarif würde sich ${lohnt ? 'lohnen' : 'nicht lohnen'}</div> <div style="font-weight:700;font-size:15px;color:${col}">${icon} Flexibler Tarif würde sich ${lohnt ? 'lohnen' : 'nicht lohnen'}</div>
<div style="font-size:12px;color:var(--text-dim);margin-top:3px">Basierend auf ${spot_days} Tagen im Abrechnungsjahr</div> <div style="font-size:12px;color:var(--text-dim);margin-top:5px">Basierend auf ${spot_days} Tagen im Abrechnungsjahr</div>
</div> </div>
<div style="font-size:22px;font-weight:700;color:${col}">${lohnt ? '' : '+'}${fEur(Math.abs(savings_eur))}</div> <div style="font-size:26px;font-weight:700;color:${col};margin-left:20px;white-space:nowrap">${lohnt ? '' : '+'}${fEur(Math.abs(savings_eur))}</div>
</div>`; </div>`;
} }
// ── Summary-Karten ─────────────────────────────────────────── // ── Summary-Karten ───────────────────────────────────────────
const spotCard = spot_total_eur !== null const spotCard = spot_total_eur !== null
? `<div class="kwh-card" style="border-top:3px solid ${C.spot}"> ? `<div class="kwh-card" style="border-top:3px solid ${C.spot};padding:18px 10px">
<div class="kv" style="color:${C.spot}">${fEur(spot_total_eur)}</div> <div class="kv" style="color:${C.spot};font-size:22px">${fEur(spot_total_eur)}</div>
<div class="kl">Spot-Tarif (hypothetisch)<br><span style="opacity:.6">${spot_days} Tage mit Preisdaten</span></div> <div class="kl" style="font-size:11px;margin-top:6px">Spot-Tarif (hypothetisch)<br><span style="opacity:.6">${spot_days} Tage mit Preisdaten</span></div>
</div>` </div>`
: `<div class="kwh-card"><div class="kv" style="color:var(--text-dim)"></div><div class="kl">Spot-Tarif<br><span style="opacity:.6">Noch keine Daten</span></div></div>`; : `<div class="kwh-card" style="padding:18px 10px"><div class="kv" style="color:var(--text-dim);font-size:22px"></div><div class="kl" style="font-size:11px;margin-top:6px">Spot-Tarif<br><span style="opacity:.6">Noch keine Daten</span></div></div>`;
const savCard = savings_eur !== null const savCard = savings_eur !== null
? `<div class="kwh-card" style="border-top:3px solid ${savings_eur > 0 ? C.green : C.red}"> ? `<div class="kwh-card" style="border-top:3px solid ${savings_eur > 0 ? C.green : C.red};padding:18px 10px">
<div class="kv" style="color:${savings_eur > 0 ? C.green : C.red}">${savings_eur > 0 ? '' : '+'}${fEur(Math.abs(savings_eur))}</div> <div class="kv" style="color:${savings_eur > 0 ? C.green : C.red};font-size:22px">${savings_eur > 0 ? '' : '+'}${fEur(Math.abs(savings_eur))}</div>
<div class="kl">${savings_eur > 0 ? 'Ersparnis mit Spot' : 'Mehrkosten mit Spot'}<br><span style="opacity:.6">gegenüber Festpreis</span></div> <div class="kl" style="font-size:11px;margin-top:6px">${savings_eur > 0 ? 'Ersparnis mit Spot' : 'Mehrkosten mit Spot'}<br><span style="opacity:.6">gegenüber Festpreis</span></div>
</div>` </div>`
: ''; : '';
const cards = `<div class="energy-kwh" style="margin-bottom:20px"> const cards = `
<div class="kwh-card" style="border-top:3px solid ${C.fixed}"> <div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin-bottom:10px">Kostenübersicht · Abrechnungsjahr</div>
<div class="kv" style="color:${C.fixed}">${fEur(fixed_total_eur)}</div> <div class="energy-kwh" style="margin-bottom:28px;gap:12px">
<div class="kl">Festpreis-Kosten<br><span style="opacity:.6">${total_days} Tage</span></div> <div class="kwh-card" style="border-top:3px solid ${C.fixed};padding:18px 10px">
<div class="kv" style="color:${C.fixed};font-size:22px">${fEur(fixed_total_eur)}</div>
<div class="kl" style="font-size:11px;margin-top:6px">Festpreis-Kosten<br><span style="opacity:.6">${total_days} Tage</span></div>
</div> </div>
${spotCard}${savCard} ${spotCard}${savCard}
</div>`; </div>`;
@@ -1027,13 +1029,17 @@ async function loadFinance() {
const legend = `<text x="${PL}" y="${H+18}" font-size="10" fill="${C.fixed}">■ Festpreis</text> const legend = `<text x="${PL}" y="${H+18}" font-size="10" fill="${C.fixed}">■ Festpreis</text>
<text x="${PL+70}" y="${H+18}" font-size="10" fill="${C.spot}">■ Spot (hypothetisch)</text>`; <text x="${PL+70}" y="${H+18}" font-size="10" fill="${C.spot}">■ Spot (hypothetisch)</text>`;
const chart = `<div style="margin-bottom:20px;overflow-x:auto"> const chartSection = `
<div style="border-top:1px solid var(--border);padding-top:24px;margin-top:4px">
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin-bottom:14px">Kosten je Tag</div>
<div style="overflow-x:auto">
<svg viewBox="0 0 ${W} ${H+24}" style="width:100%;max-width:${W}px;display:block"> <svg viewBox="0 0 ${W} ${H+24}" style="width:100%;max-width:${W}px;display:block">
${gridLines}${yLabels}${bars}${xLabels}${legend} ${gridLines}${yLabels}${bars}${xLabels}${legend}
</svg> </svg>
</div>
</div>`; </div>`;
el.innerHTML = empfehlung + cards + chart; el.innerHTML = `<div style="padding:4px 0">${empfehlung}${cards}${chartSection}</div>`;
} catch(e) { el.innerHTML = '<div class="no-data">Fehler beim Laden</div>'; console.error('loadFinance:', e); } } catch(e) { el.innerHTML = '<div class="no-data">Fehler beim Laden</div>'; console.error('loadFinance:', e); }
} }