Solar: Speicher-Ansicht mit Modulen und Zellen der BYD-Batterie

Dritte Ansicht der Realtime-Karte (auch per Klick auf die Batterie):
Kennzahlen (Ladestand mit kWh und voll/leer um, Leistung, SOH,
Zellspreizung, Temperaturen, Spannung, Durchsatz, Zustand), der Turm aus
BMU und acht Modulen mit je 16 Zellbalken, das gewaehlte Modul mit
Zellspannungen und Temperaturfuehlern, dazu Verlaeufe fuer Ladestand und
Leistung (EnergyFlow) sowie Spreizung und Temperatur (byd) mit Zeitraum-
wahl und Anzeige beim Ueberfahren. Neu: ajax/speicher.php.

Nebenbei: Icon-Zeichen in energiefluss.js und im Katalog stehen als
Escape (\uF...) statt als unsichtbare Zeichen im Quelltext.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 18:11:22 +02:00
co-authored by Claude Opus 5
parent de2892f7a7
commit ba5622f9b2
9 changed files with 790 additions and 14 deletions
+135
View File
@@ -3733,6 +3733,141 @@ svg.anl-plan.anl-hoch { max-height: none; }
.anl *, .anl-plan * { animation: none !important; transition: none !important; }
}
/* ==========================================================================
Speicher-Ansicht (js/solar/speicher.js). Töne wie Übersicht und Anlage.
========================================================================== */
.spk { display: grid; gap: .9rem; }
.spk .spk-meldung { border-radius: .7rem; padding: .55rem .9rem; font-size: .88rem; background: rgba(232, 163, 58, .12); color: #e8a33a; }
.spk .spk-meldung.spk-fehler { background: rgba(229, 98, 79, .14); color: #e5624f; }
.spk .spk-fach { background: var(--ton-grund, #191d20); border-radius: .9rem; padding: .9rem 1rem; display: grid; gap: .7rem; align-content: start; min-width: 0; }
.spk .spk-kopf { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: .4rem .8rem; }
.spk .spk-ueberschrift { font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--ton-schrift-2, #9aa6ad); margin: 0; }
.spk .spk-stand { font-size: .78rem; color: var(--ton-schrift-3, #6f7a81); }
.spk .spk-leer { color: var(--ton-schrift-3, #6f7a81); font-size: .85rem; margin: .5rem 0; }
/* Kennzahlen */
.spk .spk-kennzahlen { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .6rem; }
@media (max-width: 991.98px) { .spk .spk-kennzahlen { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.spk .spk-notiz { font-size: .74rem; color: var(--ton-schrift-3, #6f7a81); margin-top: -.4rem; }
.spk .spk-einheit { font-weight: 600; }
.spk .spk-kachel { position: relative; background: var(--ton-grund, #191d20); border-radius: .8rem; padding: .65rem .85rem .7rem; display: grid; gap: .1rem; overflow: hidden; }
.spk .spk-kachel-titel { font-size: .74rem; color: var(--ton-schrift-2, #9aa6ad); display: flex; align-items: center; gap: .4rem; }
.spk .spk-kachel-titel i { font-family: bootstrap-icons; font-style: normal; font-size: .9rem; color: #6cbe58; }
.spk .spk-kachel b { font-size: 1.45rem; font-weight: 600; color: var(--ton-schrift, #e8ede9); font-variant-numeric: tabular-nums; line-height: 1.25; }
.spk .spk-kachel small { font-size: .74rem; color: var(--ton-schrift-3, #6f7a81); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.spk .spk-kachel.spk-gut b { color: #8fd37a; }
.spk .spk-kachel.spk-warm b { color: #e8a33a; }
.spk .spk-kachel.spk-warm .spk-kachel-titel i { color: #e8a33a; }
.spk .spk-kachel.spk-krit b,
.spk .spk-kachel.spk-krit .spk-kachel-titel i { color: #e5624f; }
.spk .spk-kachel-balken { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: #2b3139; }
.spk .spk-kachel-balken span { display: block; height: 100%; background: linear-gradient(90deg, #4f8fb8, #6cbe58); transition: width 1.2s cubic-bezier(.2, .7, .2, 1); }
/* Turm und Detail */
.spk .spk-mitte { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: .9rem; align-items: start; }
@media (max-width: 991.98px) { .spk .spk-mitte { grid-template-columns: minmax(0, 1fr); } }
.spk .spk-turm { position: relative; display: grid; gap: 4px; }
.spk .spk-bmu {
display: flex; align-items: center; gap: .6rem; padding: .5rem .8rem; font-size: .82rem;
background: linear-gradient(#2c3238, #24292f); border: 1px solid #333a42; border-radius: .7rem .7rem .25rem .25rem;
}
.spk .spk-bmu b { font-weight: 600; }
.spk .spk-bmu span:last-child { color: var(--ton-schrift-3, #6f7a81); font-size: .74rem; margin-left: auto; }
.spk .spk-led { width: .55rem; height: .55rem; border-radius: 50%; background: #444b53; }
.spk .spk-led.an { background: #6cbe58; box-shadow: 0 0 .5rem #6cbe58; animation: spk-led 2.4s ease-in-out infinite; }
@keyframes spk-led { 50% { opacity: .55; } }
.spk .spk-modul {
all: unset; box-sizing: border-box; cursor: pointer; position: relative; overflow: hidden;
display: grid; grid-template-columns: 2.2rem minmax(0, 1fr) 7.4rem; gap: .7rem; align-items: center;
padding: .45rem .7rem; border-radius: .25rem; border: 1px solid #2f363d;
background: linear-gradient(90deg, #1f2429, #262c32 50%, #1f2429);
transition: border-color .2s, box-shadow .2s;
animation: spk-modul-auf .5s cubic-bezier(.2, .9, .3, 1.1) backwards; animation-delay: calc(var(--i) * 45ms);
}
@keyframes spk-modul-auf { from { opacity: 0; transform: translateY(8px); } }
.spk .spk-modul:hover { border-color: #46505a; }
.spk .spk-modul:focus-visible { outline: 2px solid #8fb8de; outline-offset: 1px; }
.spk .spk-modul.gewaehlt { border-color: #6cbe58; box-shadow: inset 3px 0 0 #6cbe58; }
.spk .spk-nr { font-weight: 600; color: var(--ton-schrift-2, #9aa6ad); font-size: .9rem; }
.spk .spk-zellen { display: grid; grid-template-columns: repeat(16, minmax(0, 1fr)); gap: 2px; height: 2rem; align-items: end; }
.spk .spk-zellen i { display: block; height: var(--h); background: var(--f); border-radius: 2px 2px 1px 1px; transition: height 1s cubic-bezier(.2, .7, .2, 1), background 1s; }
.spk .spk-werte { text-align: right; line-height: 1.25; font-variant-numeric: tabular-nums; }
.spk .spk-werte b { display: block; font-size: .8rem; font-weight: 600; }
.spk .spk-werte small { font-size: .74rem; color: var(--ton-schrift-3, #6f7a81); }
.spk .spk-sockel { height: .6rem; margin-top: -2px; border-radius: 0 0 .6rem .6rem; background: #1a1e22; border: 1px solid #2f363d; border-top: 0; }
/* Laden: Licht wandert nach oben durch die Module, Entladen nach unten. */
.spk .spk-turm.spk-laden .spk-modul::after,
.spk .spk-turm.spk-entladen .spk-modul::after {
content: ""; position: absolute; inset: 0; pointer-events: none;
background: linear-gradient(90deg, transparent, rgba(108, 190, 88, .16), transparent);
transform: translateX(-100%); animation: spk-strom 2.6s ease-in-out infinite;
}
.spk .spk-turm.spk-laden .spk-modul::after { animation-delay: calc((8 - var(--i)) * 180ms); }
.spk .spk-turm.spk-entladen .spk-modul::after { background: linear-gradient(90deg, transparent, rgba(232, 163, 58, .14), transparent); animation-delay: calc(var(--i) * 180ms); }
@keyframes spk-strom { 0% { transform: translateX(-100%); } 60%, 100% { transform: translateX(100%); } }
.spk.spk-pause *,
.spk-pause .spk * { animation-play-state: paused !important; }
.spk .spk-legende { display: flex; flex-wrap: wrap; gap: .3rem 1rem; font-size: .76rem; color: var(--ton-schrift-2, #9aa6ad); }
.spk .spk-legende i { display: inline-block; width: .7rem; height: .7rem; border-radius: 2px; margin-right: .35rem; vertical-align: -.08rem; }
.spk .spk-pille { font-size: .72rem; font-weight: 600; border-radius: 99px; padding: .1rem .55rem; }
.spk .spk-pille.ok { color: #6cbe58; background: rgba(108, 190, 88, .13); }
.spk .spk-pille.warn { color: #e8a33a; background: rgba(232, 163, 58, .14); }
.spk .spk-gross { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem; }
.spk .spk-gross b { font-size: 2rem; font-weight: 600; line-height: 1.1; color: var(--ton-schrift, #e8ede9); font-variant-numeric: tabular-nums; }
.spk .spk-gross span { color: var(--ton-schrift-2, #9aa6ad); font-size: .88rem; }
.spk .spk-zellchart { width: 100%; height: auto; display: block; }
.spk .spk-zelle { transform-box: fill-box; transform-origin: 50% 100%; animation: spk-zelle .6s cubic-bezier(.2, .9, .3, 1.1) backwards; animation-delay: calc(var(--j) * 25ms); }
.spk .spk-zelle.markiert { stroke: #fff; stroke-width: 1.5; }
@keyframes spk-zelle { from { transform: scaleY(0); } }
.spk .spk-gitter { stroke: #2a3037; stroke-width: 1; }
.spk .spk-mittel { stroke: #8d96a0; stroke-dasharray: 3 3; }
.spk .spk-achse { fill: var(--ton-schrift-3, #6f7a81); font-size: 10px; font-family: "Poppins", system-ui, sans-serif; }
.spk .spk-fuehler { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 3px; }
.spk .spk-fuehler span { text-align: center; font-size: .74rem; font-weight: 600; padding: .2rem 0; border-radius: .3rem; color: #15181c; background: var(--f); font-variant-numeric: tabular-nums; }
.spk .spk-paare { display: grid; grid-template-columns: auto 1fr; gap: .25rem .9rem; font-size: .86rem; margin: 0; }
.spk .spk-paare dt { color: var(--ton-schrift-2, #9aa6ad); font-weight: 400; }
.spk .spk-paare dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
/* Verlauf */
.spk .spk-diagramme { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: .9rem; }
@media (max-width: 991.98px) { .spk .spk-diagramme { grid-template-columns: minmax(0, 1fr); } }
.spk .spk-zeitraum { display: inline-flex; gap: 2px; padding: 2px; border-radius: .5rem; background: #23282c; }
.spk .spk-zeitraum button { all: unset; cursor: pointer; font-size: .76rem; font-weight: 600; padding: .15rem .6rem; border-radius: .4rem; color: var(--ton-schrift-2, #9aa6ad); }
.spk .spk-zeitraum button.aktiv { background: #2f363d; color: var(--ton-schrift, #e8ede9); }
.spk .spk-zeitraum button:focus-visible { outline: 2px solid #8fb8de; }
.spk .spk-diagramm { position: relative; min-height: 190px; }
.spk .spk-verlauf { display: block; width: 100%; height: auto; touch-action: pan-y; }
.spk .spk-flaeche { opacity: .22; }
.spk .spk-linie { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.spk .spk-verlauf.neu .spk-linie { stroke-dasharray: 1 1; stroke-dashoffset: 1; animation: spk-zeichnen 1.2s cubic-bezier(.5, 0, .2, 1) forwards; animation-delay: calc(var(--i) * 150ms); }
.spk .spk-verlauf.neu .spk-flaeche { animation: spk-einblenden 1.2s backwards; }
@keyframes spk-zeichnen { to { stroke-dashoffset: 0; } }
@keyframes spk-einblenden { from { opacity: 0; } }
.spk .spk-zeiger { stroke: rgba(255, 255, 255, .35); stroke-width: 1; pointer-events: none; }
.spk .spk-tipp {
position: absolute; top: .4rem; z-index: 2; pointer-events: none; min-width: 9rem;
background: rgba(21, 24, 28, .94); border: 1px solid #333a42; border-radius: .55rem; padding: .4rem .6rem;
font-size: .76rem; display: grid; gap: .1rem; box-shadow: 0 .4rem 1rem rgba(0, 0, 0, .35);
}
.spk .spk-tipp b { font-weight: 600; margin-bottom: .1rem; }
.spk .spk-tipp span { display: flex; align-items: center; gap: .4rem; color: var(--ton-schrift-2, #9aa6ad); }
.spk .spk-tipp em { font-style: normal; margin-left: auto; padding-left: .8rem; color: var(--ton-schrift, #e8ede9); font-variant-numeric: tabular-nums; }
.spk .spk-tipp i,
.spk .spk-reihen i { display: inline-block; width: .6rem; height: .6rem; border-radius: 2px; }
.spk .spk-reihen { display: flex; flex-wrap: wrap; gap: .2rem 1rem; font-size: .76rem; color: var(--ton-schrift-2, #9aa6ad); margin-top: .2rem; }
.spk .spk-reihen span { display: inline-flex; align-items: center; gap: .35rem; }
@media (max-width: 575.98px) {
.spk .spk-kennzahlen { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .45rem; }
.spk .spk-kachel b { font-size: 1.2rem; }
.spk .spk-modul { grid-template-columns: 1.8rem minmax(0, 1fr) 5.6rem; gap: .45rem; padding: .4rem .5rem; }
.spk .spk-werte b { font-size: .72rem; }
}
@media (prefers-reduced-motion: reduce) {
.spk *, .spk *::after { animation: none !important; transition: none !important; }
.spk .spk-verlauf.neu .spk-linie { stroke-dashoffset: 0; }
}
/* --- Karte: Umschalter Übersicht / Anlage -------------------------------- */
.enf-umschalter { display: inline-flex; gap: 2px; padding: 3px; border-radius: var(--ton-radius-klein, .5rem); background: var(--ton-grund, #191d20); }
.enf-umschalter .btn.btn-sm { border: 0; color: var(--ton-schrift-2, #9aa6ad); font-weight: 600; padding: .2rem .8rem; }