Feature: Eigenversorgungs-Ersparnis in Perioden-Karten (v1.8.2)
- bat_discharge_total + bat_charge_total werden jetzt als Perioden-Starts getrackt - savings_kwh = Batterie-Entladung der Periode (→ Haus + Auto) - savings_eur = savings_kwh × effektiver Importpreis (Festpreis oder Börsendurchschnitt) - Neue Karte "Eigenversorgung" (lila) neben Netzbezug/Einspeisung - Preis-Refaktor: eff_price für Import, Export, Ersparnis aus einer Berechnung Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
name: ShineBridge
|
name: ShineBridge
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
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
|
||||||
|
|||||||
+14
-9
@@ -407,7 +407,8 @@ def api_period_energy():
|
|||||||
entry["label"] = f"{pd.strftime('%d.%m.%Y')} – {(end_d - datetime.timedelta(days=1)).strftime('%d.%m.%Y')}"
|
entry["label"] = f"{pd.strftime('%d.%m.%Y')} – {(end_d - datetime.timedelta(days=1)).strftime('%d.%m.%Y')}"
|
||||||
pd_start_ts = datetime.datetime.combine(pd, datetime.time.min).timestamp()
|
pd_start_ts = datetime.datetime.combine(pd, datetime.time.min).timestamp()
|
||||||
|
|
||||||
for agg_id in ("grid_import_kwh", "grid_export_kwh"):
|
for agg_id in ("grid_import_kwh", "grid_export_kwh",
|
||||||
|
"bat_discharge_total", "bat_charge_total"):
|
||||||
cur = agg.get(agg_id)
|
cur = agg.get(agg_id)
|
||||||
if cur is None:
|
if cur is None:
|
||||||
continue
|
continue
|
||||||
@@ -416,23 +417,27 @@ def api_period_energy():
|
|||||||
if val is not None:
|
if val is not None:
|
||||||
entry[agg_id] = round(val, 2)
|
entry[agg_id] = round(val, 2)
|
||||||
|
|
||||||
# Kostenberechnung: Festpreis oder Börsenpreis
|
# Effektiver Importpreis (Festpreis oder Börsenpreis)
|
||||||
if "grid_import_kwh" in entry:
|
eff_price = price_import
|
||||||
if tariff_type == "spot":
|
if tariff_type == "spot":
|
||||||
avg_ct = _get_avg_spot_price(pd_start_ts, now_ts, spot_country)
|
avg_ct = _get_avg_spot_price(pd_start_ts, now_ts, spot_country)
|
||||||
if avg_ct is not None:
|
if avg_ct is not None:
|
||||||
eff_price = (avg_ct + spot_markup) / 100 # ct/kWh → €/kWh
|
eff_price = (avg_ct + spot_markup) / 100
|
||||||
entry["import_cost"] = round(entry["grid_import_kwh"] * eff_price, 2)
|
|
||||||
entry["spot_avg_ct"] = avg_ct
|
entry["spot_avg_ct"] = avg_ct
|
||||||
entry["spot_markup_ct"] = spot_markup
|
entry["spot_markup_ct"] = spot_markup
|
||||||
entry["effective_price"]= round(eff_price, 4)
|
entry["effective_price"]= round(eff_price, 4)
|
||||||
else:
|
|
||||||
entry["import_cost"] = round(entry["grid_import_kwh"] * price_import, 2)
|
if "grid_import_kwh" in entry:
|
||||||
else:
|
entry["import_cost"] = round(entry["grid_import_kwh"] * eff_price, 2)
|
||||||
entry["import_cost"] = round(entry["grid_import_kwh"] * price_import, 2)
|
|
||||||
if "grid_export_kwh" in entry:
|
if "grid_export_kwh" in entry:
|
||||||
entry["export_revenue"] = round(entry["grid_export_kwh"] * price_export, 2)
|
entry["export_revenue"] = round(entry["grid_export_kwh"] * price_export, 2)
|
||||||
|
|
||||||
|
# Eigenverbrauch-Ersparnis: Batterie-Entladung (→ Haus + Auto) zu Importpreis
|
||||||
|
bat_dch = entry.get("bat_discharge_total")
|
||||||
|
if bat_dch is not None:
|
||||||
|
entry["savings_kwh"] = bat_dch
|
||||||
|
entry["savings_eur"] = round(bat_dch * eff_price, 2)
|
||||||
|
|
||||||
result[period_type] = entry
|
result[period_type] = entry
|
||||||
|
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|||||||
@@ -604,19 +604,26 @@ function renderEnergy(inverters, aggregates, period, spotData) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sectionCards(title, data, col) {
|
function sectionCards(title, data) {
|
||||||
const impSub = data.spot_avg_ct != null
|
const impSub = data.spot_avg_ct != null
|
||||||
? `Ø ${data.spot_avg_ct.toFixed(1)} + ${(data.spot_markup_ct||0).toFixed(1)} ct/kWh`
|
? `Ø ${data.spot_avg_ct.toFixed(1)} + ${(data.spot_markup_ct||0).toFixed(1)} ct/kWh`
|
||||||
: '';
|
: '';
|
||||||
const imp = periodCard('Netzbezug', data.grid_import_kwh, data.import_cost, C.imp, impSub);
|
const imp = periodCard('Netzbezug', data.grid_import_kwh, data.import_cost, C.imp, impSub);
|
||||||
const exp = periodCard('Einspeisung', data.grid_export_kwh, data.export_revenue, C.exp, '');
|
const exp = periodCard('Einspeisung', data.grid_export_kwh, data.export_revenue, C.exp, '');
|
||||||
if (!imp && !exp) return '';
|
const sav = data.savings_kwh != null
|
||||||
|
? `<div class="kwh-card" style="border-top:3px solid ${C.bat}">
|
||||||
|
<div class="kv" style="color:${C.bat}">${fKwh(data.savings_kwh)}</div>
|
||||||
|
${data.savings_eur != null ? `<div style="font-size:11px;font-weight:600;color:${C.bat};opacity:.8;margin:2px 0">${fEur(data.savings_eur)} gespart</div>` : ''}
|
||||||
|
<div class="kl">Eigenversorgung<br><span style="opacity:.6">Batterie → Haus & Auto</span></div>
|
||||||
|
</div>`
|
||||||
|
: '';
|
||||||
|
if (!imp && !exp && !sav) return '';
|
||||||
return `<div style="margin-bottom:8px;font-size:10px;font-weight:700;letter-spacing:.08em;color:${C.dim};text-transform:uppercase">${title}</div>
|
return `<div style="margin-bottom:8px;font-size:10px;font-weight:700;letter-spacing:.08em;color:${C.dim};text-transform:uppercase">${title}</div>
|
||||||
<div class="energy-kwh" style="margin-bottom:16px">${imp}${exp}</div>`;
|
<div class="energy-kwh" style="margin-bottom:16px">${imp}${exp}${sav}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cards = sectionCards(mon.label || 'Diesen Monat', mon, C.imp) +
|
const cards = sectionCards(mon.label || 'Diesen Monat', mon) +
|
||||||
sectionCards(yr.label || 'Dieses Jahr', yr, C.imp);
|
sectionCards(yr.label || 'Dieses Jahr', yr);
|
||||||
|
|
||||||
const spotHtml = (period.spot_chart !== false) ? renderSpotChart(spotData) : '';
|
const spotHtml = (period.spot_chart !== false) ? renderSpotChart(spotData) : '';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user