diff --git a/haos-addon/config.yaml b/haos-addon/config.yaml
index af26c6c..3b57775 100644
--- a/haos-addon/config.yaml
+++ b/haos-addon/config.yaml
@@ -1,5 +1,5 @@
name: ShineBridge
-version: "1.9.1"
+version: "1.9.2"
slug: shinebridge
description: Energiemanagement für Growatt/Goodwe-Wechselrichter — PV-Überschussladen, Wallbox-EMS, Tarifvergleich, Zigbee2MQTT-Überschussgeräte
url: https://gitea.bitfire.work/retr0/shinebridge
diff --git a/haos-addon/src/web/index.html b/haos-addon/src/web/index.html
index ceb99b7..1971683 100644
--- a/haos-addon/src/web/index.html
+++ b/haos-addon/src/web/index.html
@@ -797,19 +797,37 @@ async function loadFinance() {
`;
// ── SVG-Balkendiagramm ────────────────────────────────────────
- // Breite skaliert mit der Anzahl Tage (Mindestplatz pro Tag), statt bei vielen
- // Tagen alles in eine feste Breite zu quetschen — sonst werden Balken bei
- // langen Abrechnungsjahren (>60 Tage) zu Haarlinien und unlesbar.
- const H = 180, PL = 44, PR = 12, PT = 10, PB = 28;
- const n = days.length;
- const MIN_SLOT = 10; // px pro Tag (Balkenpaar + Abstand), unterhalb dessen nichts mehr lesbar ist
+ // Bei langen Abrechnungsjahren nicht jeden einzelnen Tag zeigen — statt 112
+ // Tagesbalken lieber ~16 Wochenbalken, deutlich lesbarer und trotzdem
+ // aussagekräftig für "würde sich Spot lohnen".
+ const rawN = days.length;
+ const bucket = rawN > 180 ? 14 : rawN > 21 ? 7 : 1;
+ const periods = bucket === 1 ? days.map(d => ({
+ fixed_eur: d.fixed_eur, spot_eur: d.spot_eur, label: d.date.slice(5),
+ })) : (() => {
+ const out = [];
+ for (let i = 0; i < days.length; i += bucket) {
+ const chunk = days.slice(i, i + bucket);
+ const fixed = chunk.reduce((s, d) => s + (d.fixed_eur || 0), 0);
+ const spotVals = chunk.map(d => d.spot_eur).filter(v => v != null);
+ const spot = spotVals.length ? spotVals.reduce((a, b) => a + b, 0) : null;
+ const from = chunk[0].date.slice(5), to = chunk[chunk.length - 1].date.slice(5);
+ out.push({ fixed_eur: fixed, spot_eur: spot, label: from === to ? from : `${from}–${to}` });
+ }
+ return out;
+ })();
+
+ const n = periods.length;
+ const H = 190, PL = 44, PR = 12, PT = 14, PB = 30;
+ const MIN_SLOT = 34; // px pro Balkenpaar — bewusst großzügig, jetzt wo wenige Balken übrig sind
const W = Math.max(600, PL + PR + n * MIN_SLOT);
const cW = W - PL - PR;
const cH = H - PT - PB;
- const barW = Math.max(3, Math.floor(cW / n) - 2);
- const gap = Math.max(1, Math.floor(cW / n) - barW);
+ const slot = cW / n;
+ const barW = Math.min(22, Math.max(4, slot * 0.34));
+ const barGap = Math.max(2, slot * 0.06);
- const allVals = days.flatMap(d => [d.fixed_eur, d.spot_eur].filter(v => v != null));
+ const allVals = periods.flatMap(d => [d.fixed_eur, d.spot_eur].filter(v => v != null));
const maxVal = Math.max(...allVals, 0.01);
const yTicks = 4;
@@ -817,40 +835,52 @@ async function loadFinance() {
for (let i = 0; i <= yTicks; i++) {
const v = maxVal * i / yTicks;
const y = PT + cH - (cH * i / yTicks);
- gridLines += `