From 608f6bfd427949c0d222dd9eb9aedc36d4fb750e Mon Sep 17 00:00:00 2001 From: retr0 <42kdesigners@gmail.com> Date: Tue, 25 Aug 2026 14:02:33 +0200 Subject: [PATCH] =?UTF-8?q?ShineBridge=20v1.9.3=20=E2=80=94=20Finanzen-Cha?= =?UTF-8?q?rt:=20auf=20letzte=2014=20Perioden=20begrenzt,=20echt=20respons?= =?UTF-8?q?iv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Zeigt nur noch die letzten 14 Balken (statt aller Perioden im Abrechnungsjahr) — genug für einen Trend, ohne dass Labels sich gegenseitig überlappen - SVG jetzt echt responsiv (viewBox + width:100%, Mobile-first dimensioniert) statt fester Pixelbreite mit Scrollbalken — passt sich Handy wie Desktop an - X-Achsen-Labels um -40° gedreht statt horizontal — brauchen dadurch deutlich weniger Platz pro Balken, alle 14 bleiben lesbar Co-Authored-By: Claude Sonnet 5 --- haos-addon/config.yaml | 2 +- haos-addon/src/web/index.html | 47 ++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/haos-addon/config.yaml b/haos-addon/config.yaml index 3b57775..363a7ca 100644 --- a/haos-addon/config.yaml +++ b/haos-addon/config.yaml @@ -1,5 +1,5 @@ name: ShineBridge -version: "1.9.2" +version: "1.9.3" 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 1971683..5cf278e 100644 --- a/haos-addon/src/web/index.html +++ b/haos-addon/src/web/index.html @@ -798,11 +798,11 @@ async function loadFinance() { // ── SVG-Balkendiagramm ──────────────────────────────────────── // 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". + // Tagesbalken lieber Wochenbalken, und davon nur die letzten 14 (reicht für + // einen Trend, bleibt aber lesbar statt endlos zu scrollen). const rawN = days.length; const bucket = rawN > 180 ? 14 : rawN > 21 ? 7 : 1; - const periods = bucket === 1 ? days.map(d => ({ + const allPeriods = bucket === 1 ? days.map(d => ({ fixed_eur: d.fixed_eur, spot_eur: d.spot_eur, label: d.date.slice(5), })) : (() => { const out = []; @@ -811,21 +811,24 @@ async function loadFinance() { 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}` }); + out.push({ fixed_eur: fixed, spot_eur: spot, label: chunk[0].date.slice(5) }); } return out; })(); + const MAX_PERIODS = 14; + const periods = allPeriods.slice(-MAX_PERIODS); + // Echtes responsives SVG: viewBox in einer "Mobile-first"-Breite, skaliert per + // width:100% frei auf den Container (Handy wie PC) — kein Scrollen mehr nötig, + // da wir uns auf max. 14 Balken begrenzen. 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 W = 380, H = 220, PL = 40, PR = 8, PT = 14, PB = 46; const cW = W - PL - PR; const cH = H - PT - PB; const slot = cW / n; - const barW = Math.min(22, Math.max(4, slot * 0.34)); - const barGap = Math.max(2, slot * 0.06); + const barW = Math.max(3, slot * 0.36); + const barGap = Math.max(1.5, slot * 0.08); + const rBar = Math.min(2.5, barW / 2); const allVals = periods.flatMap(d => [d.fixed_eur, d.spot_eur].filter(v => v != null)); const maxVal = Math.max(...allVals, 0.01); @@ -835,12 +838,12 @@ async function loadFinance() { for (let i = 0; i <= yTicks; i++) { const v = maxVal * i / yTicks; const y = PT + cH - (cH * i / yTicks); - gridLines += ``; - yLabels += `${fEur(v)}`; + gridLines += ``; + yLabels += `${fEur(v)}`; } - const labelStep = Math.max(1, Math.round(50 / slot)); - const rBar = Math.min(3, barW / 2); + // Labels schräg statt horizontal — braucht bei 14 Balken pro Label deutlich + // weniger Breite, dadurch bleiben trotz Platzmangel alle 14 lesbar. let bars = '', xLabels = ''; periods.forEach((d, i) => { const cx = PL + i * slot + slot / 2; @@ -850,9 +853,8 @@ async function loadFinance() { const hSpot = Math.max(0, cH * d.spot_eur / maxVal); bars += ``; } - if (i % labelStep === 0) { - xLabels += `${d.label}`; - } + const ly = H - PB + 12; + xLabels += `${d.label}`; }); const defs = ` @@ -867,6 +869,7 @@ async function loadFinance() { `; const bucketLabel = bucket === 1 ? 'Tag' : bucket === 7 ? 'Woche' : '2 Wochen'; + const rangeNote = allPeriods.length > MAX_PERIODS ? ` · letzte ${n}` : ''; const legend = `
Festpreis Spot (hypothetisch) @@ -874,12 +877,10 @@ async function loadFinance() { const chartSection = `
-
Kosten je ${bucketLabel}
-
- - ${defs}${gridLines}${yLabels}${bars}${xLabels} - -
+
Kosten je ${bucketLabel}${rangeNote}
+ + ${defs}${gridLines}${yLabels}${bars}${xLabels} + ${legend}
`;