Ladestand ins Fahrzeugbild, Jahresstatistik als Vergleichstabelle

Drei Teile, die zusammengehoeren: die Fahrzeugkachel zeigt den Ladestand
jetzt dort, wo er im Auto auch sitzt, die Jahresstatistik wird zur
Tabelle, und beide holen ihr Aussehen aus einer gemeinsamen Farbwelt.

Fahrzeugkachel. Die Kachel "Batterie und Ladung" ist aufgeloest; ihr
Inhalt steckt in der Draufsicht. Die Akkuplatte im Fahrzeugboden fuellt
sich von hinten nach vorn, traegt die Zielmarke und nennt Prozent und
Reichweite in ihrer Mitte - auf dem Goldenen Schnitt der Plattenhoehe,
damit sie nicht am Klimaknopf klebt. Beim Laden wandern Streifen im
gefuellten Teil nach vorn, und vom Anschluss fliesst eine Linie in die
Platte. Was sich nicht zeichnen laesst - Ladeleistung, Restzeit,
Kilometerstand, Klima, Herkunft des Ladestroms - steht als Kennzahl
daneben, dazu die gefahrenen Kilometer aus Woche, Monat und Jahr
(?was=strecke, gerechnet aus dem Zaehlerstand statt aus erkannten
Fahrten - faellt eine Zeile aus, fehlt die Fahrt in der Liste, im
Zaehlerstand aber nicht).

Am Fahrzeug selbst: kein Panoramadach, das hat unseres nicht. Reling und
Spoiler neu gezeichnet, weil sie mit der Dachplatte bisher ein
geschlossenes Rechteck bildeten und das wie ein Fensterrahmen aussah. Die
Blende am Bug ist schmaler und heller - von oben sieht man von einer
senkrechten Flaeche nur einen Streifen, und fast schwarz las sie sich wie
ein Loch. Die Ladeklappe ist gebaut wie der Klimaknopf; den Ladezustand
traegt jetzt die Farbe statt einer zweiten Form.

Jahresvergleich. getStats.php liefert kein fertiges HTML mehr, sondern
JSON, das sich selbst beschreibt: Einheit, Nachkommastellen, Richtung und
Zugehoerigkeit stehen als Metadaten je Kennzahl, nicht mehr als zwei
Arrays, die ueber den Spaltenindex mit der Abfrage synchron gehalten
werden mussten. Zwei Abfragen holen alles - Monatswerte, daraus die
ganzen Jahre und die Summe seit Aufzeichnungsbeginn, dazu der gleiche
Zeitraum jedes Jahres bis zum heutigen Kalendertag. Verhaeltniszahlen
sind auf jeder Ebene neu aus den Summen gerechnet; ein Jahresmittel ist
nicht das Mittel der Monatsmittel.

Gezeichnet wird das von einem Baustein, den sich Historien- und
Solarseite teilen: dort drei Jahresspalten mit verschiebbarem Fenster bis
zurueck zum ersten Jahr, hier eine Spalte ohne Werkzeuge. Zeilen, die
sich aufteilen, stehen als "davon" darunter; was bereits in einer anderen
Zeile steckt, als "darin" samt Namen der Zeile - der Beitrag der Batterie
und die Solarladung des Autos duerfen nicht zum Ertrag addiert werden.
Zusammengefasste Zeilen sind zunaechst zu, damit die Tabelle mit zehn
Zeilen aufmacht statt mit siebzehn.

Farbwelt. Statt Regel fuer Regel zu ueberschreiben, ist die Palette
ausgetauscht: ein paar Variablen am Wurzelelement, an die Bootstrap und
AdminLTE ohnehin gehen. Anthrazit als Grund, eine Spur hellere Flaeche
fuer Karten, Haarlinien statt Rahmen und Schatten, Bernstein als einzige
Akzentfarbe - auf dieser Seite die Farbe der eigenen Anlage. Gruen, Rot
und Gelb bleiben den Zustaenden vorbehalten.

Erklaerungen, die bisher nur im title standen, sind antippbar: am Finger
gibt es kein Ueberfahren, und ein title erscheint dort nie.

Die beiden neuen Abfragen sind gegen solarLog gelaufen, die Monatswerte
decken sich mit einer unabhaengigen Zerlegung. grundpreisImJahr() ruft
jetzt grundpreisImZeitraum() auf - dieselbe Rechnung, nur auch fuer einen
Teil des Jahres.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 20:08:51 +02:00
co-authored by Claude Opus 5
parent 22ecc1b50f
commit 0cb0d4c3ae
13 changed files with 2691 additions and 587 deletions
+26 -3
View File
@@ -325,9 +325,32 @@ function preiseSpeichern($key, $zeilen)
function grundpreisImJahr($jahr)
{
$jahr = intval($jahr);
$von = $jahr . "-01-01";
$bis = $jahr . "-12-31";
$tage = intval((new DateTimeImmutable($bis))->format("z")) + 1; // 365 oder 366
return grundpreisImZeitraum($jahr . "-01-01", $jahr . "-12-31");
}
/**
* Grundpreis Strom fuer einen Zeitraum innerhalb eines Kalenderjahres.
*
* Der Grundpreis steht als Jahresbetrag in der Tarifzeile; auf einen Zeitraum
* kommt er anteilig nach Tagen. Geteilt wird deshalb durch die Tage des
* JAHRES, nicht durch die des Zeitraums - sonst zahlte ein halbes Jahr den
* vollen Betrag.
*
* Von und bis muessen im selben Jahr liegen: die Statistik fragt entweder ein
* ganzes Jahr oder dessen Anfang bis zu einem Stichtag, und ein Zeitraum ueber
* den Jahreswechsel haette zwei verschiedene Nenner.
*/
function grundpreisImZeitraum($von, $bis)
{
$vonTag = new DateTimeImmutable($von);
$bisTag = new DateTimeImmutable($bis);
if ($vonTag->format("Y") !== $bisTag->format("Y")) {
throw new InvalidArgumentException("Zeitraum muss innerhalb eines Jahres liegen.");
}
$von = $vonTag->format("Y-m-d");
$bis = $bisTag->format("Y-m-d");
// Tage des Jahres, 365 oder 366 - der Nenner des Jahresbetrags.
$tage = intval((new DateTimeImmutable($vonTag->format("Y") . "-12-31"))->format("z")) + 1;
$stmt = solarDb()->prepare(
"WITH p AS (SELECT active_date, base,
+34 -19
View File
@@ -1,16 +1,24 @@
<?php
/*
* Seite "Vergangenheit": sechs Diagramme und darunter der Jahresvergleich.
*
* Der Vergleich war einmal drei Karten mit je achtzehn Kacheln - dieselben
* Beschriftungen dreimal untereinander, und wer zwei Jahre vergleichen wollte,
* musste scrollen. Jetzt ist es eine Tabelle: Kennzahlen in Zeilen, Jahre in
* Spalten, der Vergleich also eine Zeile weit.
*
* Gebaut wird der Vergleich von baueJahresstatistik() in
* js/solar/jahresstatistik.js, aus einer einzigen Antwort von
* ajax/getStats.php - auch die Zeilen selbst, denn welche Kennzahlen es gibt,
* weiss der Server. Denselben Baustein zeigt die Solarseite einspaltig.
*/
// Die sechs Diagramme unterscheiden sich nur in Zeitraum und Canvas-ID.
$periods = [
"month" => "Letzter Monat",
"year" => "Letzte 13 Monate",
"decade" => "Letzte 10 Jahre",
];
// Die Jahresstatistiken darunter folgen demselben Muster.
$statistics = [
"Stats-Year" => "Statistik dieses Jahr",
"Stats-Lastyear" => "Statistik letztes Jahr",
"Stats-Prelastyear" => "Statistik vorletztes Jahr",
];
?>
<!--begin::App Main-->
<main class="app-main">
@@ -63,23 +71,30 @@ $statistics = [
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<?php foreach ($statistics as $id => $title): ?>
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
<div class="card p-0 mb-4" style="width:100%">
<div class="card-header pb-0 pt-1">
<h3 class="card-title"><?= $title ?></h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
<i class="bi bi-x-lg"></i>
</button>
<!-- ============================================ Jahresvergleich ==== -->
<!-- Der Inhalt kommt aus baueJahresstatistik() in
js/solar/jahresstatistik.js: Leitzahlen, Werkzeugleiste, Tabelle und
Fussnoten baut der Baustein selbst, damit die Solarseite dieselbe
Statistik in schmaler Form zeigen kann, ohne dass es sie zweimal
gaebe. -->
<div class="row">
<div class="col-12">
<div class="card mb-4 p-0 w-100">
<div class="card-header pb-0 pt-1">
<h3 class="card-title">Jahresvergleich</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
<div class="card-body statistik">
<div id="jahresvergleich"></div>
</div>
</div>
<div class="row-cols-1 row-cols-md-1 g-4 p-2" id="<?= $id ?>">
</div>
</div>
<!-- /.card -->
</div>
<?php endforeach; ?>
<!-- /.row (main row) -->
</div>
<!--end::Container-->
+375 -180
View File
@@ -19,10 +19,16 @@
* 4648 x 1877 mm, Radstand 2765 mm, daraus das Seitenverhältnis 2,48:1 und
* die Lage der Achsen. Kein Foto, sondern ein Schema - die Teile sollen
* ihren Zustand zeigen, nicht das Auto porträtieren.
*
* Der Ladestand ist Teil dieser Grafik und nicht mehr eine Anzeige daneben:
* die Akkuplatte im Fahrzeugboden füllt sich von hinten nach vorn, trägt die
* Zielmarke und nennt Prozent und Reichweite in ihrer Mitte. Alles, was sich
* nicht zeichnen lässt - Ladeleistung, Restzeit, Kilometerstand, Klima und
* die Herkunft des Ladestroms - steht als Kennzahl daneben.
*/
?>
<!--begin::App Main-->
<main class="app-main">
<main class="app-main skoda-seite">
<div class="app-content-header">
<div class="container-fluid">
<div class="row align-items-center">
@@ -39,208 +45,397 @@
<div class="app-content">
<div class="container-fluid">
<!-- ================================================ Zustand ===== -->
<!-- ================================================ Übersicht ==== -->
<!-- Eine Kachel, zwei Hälften: links das Fahrzeug, rechts die Zahlen
dazu. Der Ladestand steht nicht mehr als eigener Ring daneben,
sondern dort, wo er im Auto auch sitzt - in der Akkuplatte im
Fahrzeugboden. Ladestand, Ladeziel und Reichweite gehören zusammen
und stehen deshalb beieinander im Bild; alles, was sich nicht
zeichnen lässt, steht als Kennzahl daneben. -->
<div class="row">
<!-- Das Fahrzeug als Anzeigeelement. Jedes Teil trägt seinen Zustand
selbst: offene Türen und Klappen leuchten bernstein, Licht gelb,
der Ladeanschluss pulst beim Laden. Die Zeile darunter benennt
dasselbe in Worten und hebt beim Überfahren das zugehörige Teil
hervor - Farbe allein soll die Aussage nicht tragen. -->
<div class="col-xxl-4 col-lg-5 d-flex align-items-stretch">
<div class="col-12">
<div class="card mb-4 p-0 w-100">
<div class="card-header pb-0 pt-1">
<h3 class="card-title">Fahrzeug</h3>
<div class="card-tools">
<h3 class="card-title">Übersicht</h3>
<div class="card-tools d-flex align-items-center gap-2">
<span class="badge text-bg-secondary" id="skodaSchloss"></span>
</div>
</div>
<div class="card-body d-flex flex-column align-items-center pt-2">
<svg id="skodaAuto" class="skoda-auto" viewBox="0 0 224 500"
role="img" aria-label="Draufsicht des Fahrzeugs mit dem Zustand von Türen, Fenstern, Klappen und Licht">
<!-- Räder. Vorderachse bei 860 mm, Hinterachse bei 3625 mm
ab Bugspitze - der Radstand von 2765 mm maßstäblich. -->
<g class="rad">
<rect x="5" y="68" width="21" height="58" rx="9"/>
<rect x="198" y="68" width="21" height="58" rx="9"/>
<rect x="5" y="362" width="21" height="58" rx="9"/>
<rect x="198" y="362" width="21" height="58" rx="9"/>
</g>
<!-- Karosserie -->
<path class="karosserie" d="M112 6
C150 6, 198 16, 204 50 C209 90, 212 160, 212 250
C212 340, 209 410, 204 450 C198 484, 150 494, 112 494
C74 494, 26 484, 20 450 C15 410, 12 340, 12 250
C12 160, 15 90, 20 50 C26 16, 74 6, 112 6 Z"/>
<!-- Tech-Deck Face: die glänzend schwarze Blende über die
volle Bugbreite, die den Enyaq seit der Überarbeitung
kennzeichnet. Von oben sieht man ihre Oberkante. -->
<path class="techdeck" d="M112 7
C150 7, 197 17, 203 49 L194 53 C187 27, 148 18, 112 18
C76 18, 37 27, 30 53 L21 49 C27 17, 74 7, 112 7 Z"/>
<!-- Scheinwerfer, vier Elemente je Seite wie beim Original,
dazu die Rückleuchten als durchgehendes Band. -->
<g id="teil-lights" class="teil teil-licht" data-teil="lights">
<rect x="36" y="30" width="9" height="13" rx="4"/>
<rect x="48" y="27" width="9" height="13" rx="4"/>
<rect x="60" y="25" width="9" height="13" rx="4"/>
<rect x="72" y="24" width="9" height="13" rx="4"/>
<rect x="143" y="24" width="9" height="13" rx="4"/>
<rect x="155" y="25" width="9" height="13" rx="4"/>
<rect x="167" y="27" width="9" height="13" rx="4"/>
<rect x="179" y="30" width="9" height="13" rx="4"/>
<rect class="rueck" x="40" y="462" width="52" height="13" rx="6"/>
<rect class="rueck" x="132" y="462" width="52" height="13" rx="6"/>
<title>Beleuchtung</title>
</g>
<!-- Motorhaube mit angedeuteter Mittelsicke -->
<g id="teil-bonnet" class="teil" tabindex="0" data-teil="bonnet">
<path d="M31 152 L193 152 L189 62 C169 40, 55 40, 35 62 Z"/>
<title>Motorhaube</title>
</g>
<path class="sicke" d="M112 60 L112 150"/>
<!-- Windschutzscheibe, Dach, Heckscheibe -->
<path class="scheibe" d="M34 156 L190 156 L178 200 L46 200 Z"/>
<rect class="dach" x="46" y="200" width="132" height="136" rx="10"/>
<!-- Panoramaglasdach: beim Enyaq nahezu die ganze Dachfläche -->
<g id="teil-sunroof" class="teil" tabindex="0" data-teil="sunroof">
<rect x="62" y="206" width="100" height="120" rx="8"/>
<title>Panoramadach</title>
</g>
<!-- Dachreling -->
<g class="reling">
<rect x="48" y="204" width="9" height="134" rx="4"/>
<rect x="167" y="204" width="9" height="134" rx="4"/>
</g>
<path class="scheibe" d="M46 336 L178 336 L190 382 L34 382 Z"/>
<rect class="spoiler" x="52" y="330" width="120" height="9" rx="4"/>
<!-- Heckklappe -->
<g id="teil-trunk" class="teil" tabindex="0" data-teil="trunk">
<path d="M34 384 L190 384 L186 458 C168 478, 56 478, 38 458 Z"/>
<title>Heckklappe</title>
</g>
<!-- Türen, jeweils mit Seitenfenster darin -->
<g id="teil-doors" class="teil" data-teil="doors">
<g tabindex="0"><rect x="12" y="202" width="23" height="74" rx="6"/><title>Tür vorn links</title></g>
<g tabindex="0"><rect x="12" y="278" width="23" height="66" rx="6"/><title>Tür hinten links</title></g>
<g tabindex="0"><rect x="189" y="202" width="23" height="74" rx="6"/><title>Tür vorn rechts</title></g>
<g tabindex="0"><rect x="189" y="278" width="23" height="66" rx="6"/><title>Tür hinten rechts</title></g>
</g>
<g id="teil-windows" class="teil teil-fenster" data-teil="windows">
<rect x="17" y="210" width="13" height="58" rx="4"/>
<rect x="17" y="286" width="13" height="50" rx="4"/>
<rect x="194" y="210" width="13" height="58" rx="4"/>
<rect x="194" y="286" width="13" height="50" rx="4"/>
<title>Seitenfenster</title>
</g>
<!-- Außenspiegel -->
<g class="spiegel">
<rect x="1" y="194" width="16" height="10" rx="5"/>
<rect x="207" y="194" width="16" height="10" rx="5"/>
</g>
<!-- ============================================================
Steuerung: die beiden anklickbaren Stellen.
Bedient wird das, worum es geht - der Ladeanschluss für
das Laden, der Innenraum für Klima und Lüftung. Beide
tragen einen Ring als Hinweis darauf, dass sie mehr sind
als Anzeige, und öffnen ein Modal mit den Befehlen.
Sie stehen bewusst NICHT in der Zustandszeile darunter:
die zeigt, was ist. Diese beiden ändern etwas.
============================================================ -->
<!-- Ladeanschluss. Beim Enyaq sitzt die Klappe am hinteren
rechten Kotflügel; sie pulst, solange geladen wird. -->
<g id="teil-plug" class="teil teil-stecker steuer" tabindex="0"
role="button" data-steuer="laden" aria-label="Laden steuern">
<circle class="ring" cx="203" cy="408" r="17"/>
<circle cx="203" cy="408" r="11"/>
<path d="M198 402 l6 5 -4 0 4 6"/>
<title>Laden steuern</title>
</g>
<!-- Innenraum, auf der Windschutzscheibe: Klima und Lüftung. -->
<g id="steuer-klima" class="steuer" tabindex="0"
role="button" data-steuer="klima" aria-label="Klima und Lüftung steuern">
<circle class="ring" cx="112" cy="178" r="19"/>
<circle class="flaeche" cx="112" cy="178" r="13"/>
<path class="glyphe" d="M112 170 v10 M108 174 h8 M105 185 q7 -4 14 0"/>
<title>Klima und Lüftung steuern</title>
</g>
</svg>
<div class="skoda-teile" id="skodaTeile"></div>
<p class="skoda-hinweis">
Ladeanschluss und Innenraum antippen, um zu steuern
</p>
</div>
</div>
</div>
<!-- Batterie, Reichweite, Ladung -->
<div class="col-xxl-8 col-lg-7 d-flex align-items-stretch">
<div class="card mb-4 p-0 w-100">
<div class="card-header pb-0 pt-1">
<h3 class="card-title">Batterie und Ladung</h3>
<div class="card-tools">
<span class="badge text-bg-secondary" id="skodaLadeZustand"></span>
</div>
</div>
<div class="card-body">
<div class="row align-items-center g-3">
<div class="skoda-fahrzeug">
<!-- Ladestandsring. Der Strich auf dem Ring ist das
eingestellte Ladeziel - eine Ladung, die dort endet, ist
kein Fehler, sondern Absicht. -->
<div class="col-md-5 text-center">
<svg class="skoda-ring" viewBox="0 0 200 200" role="img" aria-labelledby="skodaSocText">
<circle class="ring-bahn" cx="100" cy="100" r="82"/>
<circle class="ring-wert" id="skodaRing" cx="100" cy="100" r="82"/>
<line class="ring-ziel" id="skodaZiel" x1="100" y1="8" x2="100" y2="26"/>
<text id="skodaSocText" class="ring-zahl" x="100" y="98"></text>
<text class="ring-einheit" x="100" y="126">Prozent</text>
<!-- Das Fahrzeug als Anzeigeelement. Jedes Teil trägt seinen
Zustand selbst: offene Türen und Klappen leuchten
bernstein, Licht gelb, der Ladeanschluss pulst beim Laden.
Die Zustandszeile rechts benennt dasselbe in Worten und
hebt beim Überfahren das zugehörige Teil hervor - Farbe
allein soll die Aussage nicht tragen. -->
<div class="skoda-bild">
<svg id="skodaAuto" class="skoda-auto" viewBox="-16 -8 256 516"
role="img" aria-label="Draufsicht des Fahrzeugs mit Ladestand der Batterie und dem Zustand von Türen, Fenstern, Klappen und Licht">
<defs>
<!-- Der Verlauf der Füllung ist am Fahrzeug verankert,
nicht am Füllrechteck: sonst wanderte er mit dem
Ladestand mit und die Farbe unten änderte sich bei
jedem Prozentpunkt. Die Farbe selbst kommt aus
--akku-farbe, gesetzt von der Zustandsklasse am
SVG-Element; das Attribut hier ist der Rückfall. -->
<linearGradient id="akkuVerlauf" gradientUnits="userSpaceOnUse"
x1="0" y1="378" x2="0" y2="108">
<stop offset="0" stop-color="#7aa7c7" stop-opacity=".75"/>
<stop offset="1" stop-color="#7aa7c7" stop-opacity=".3"/>
</linearGradient>
<!-- Innenkontur der Platte: schneidet Füllung, Strömung
und Pegelkante auf die abgerundete Form zu. -->
<clipPath id="akkuInnen">
<rect x="50" y="108" width="124" height="270" rx="7"/>
</clipPath>
<!-- Auf Höhe des Ladestands beschnitten, damit die
Strömung nur im gefüllten Teil läuft. Das Rechteck
darin setzt zeichneAkku() mit. -->
<clipPath id="akkuPegel">
<rect id="akkuPegelFeld" x="50" y="378" width="124" height="0"/>
</clipPath>
<!-- Ein Flügel des Lüfterrads, um die eigene Nabe im
Ursprung gezeichnet. Das Rad setzt ihn dreimal um je
120 Grad versetzt ein. -->
<path id="klimaFluegel"
d="M0 0 C 1.5 -4, 5 -7, 7.5 -5 C 9.6 -3.2, 5.5 -0.8, 0 0 Z"/>
</defs>
<!-- Räder. Vorderachse bei 860 mm, Hinterachse bei 3625 mm
ab Bugspitze - der Radstand von 2765 mm maßstäblich. -->
<g class="rad">
<rect x="5" y="68" width="21" height="58" rx="9"/>
<rect x="198" y="68" width="21" height="58" rx="9"/>
<rect x="5" y="362" width="21" height="58" rx="9"/>
<rect x="198" y="362" width="21" height="58" rx="9"/>
</g>
<!-- Karosserie -->
<path class="karosserie" d="M112 6
C150 6, 198 16, 204 50 C209 90, 212 160, 212 250
C212 340, 209 410, 204 450 C198 484, 150 494, 112 494
C74 494, 26 484, 20 450 C15 410, 12 340, 12 250
C12 160, 15 90, 20 50 C26 16, 74 6, 112 6 Z"/>
<!-- Tech-Deck Face: die glänzend schwarze Blende über die
volle Bugbreite, die den Enyaq seit der Überarbeitung
kennzeichnet. Von oben sieht man ihre Oberkante, also
ein schmales Band, das zu den Kotflügeln hin ausläuft.
Schmal und nur halb deckend: die Blende steht am Auto
fast senkrecht, von oben ist sie deshalb ein Streifen
und keine Fläche. Als breites, fast schwarzes Band war
sie das dunkelste im Bild und las sich wie ein Loch.
Die Enden laufen spitz zusammen statt gerade abzu-
schneiden - ein gerader Schnitt ergab dort zwei Stufen,
die es am Auto nicht gibt. -->
<path class="techdeck" d="M22 46
C 30 17, 74 7, 112 7 C 150 7, 194 17, 202 46
C 196 24, 156 17, 112 17 C 68 17, 28 24, 22 46 Z"/>
<!-- Scheinwerfer, vier Elemente je Seite wie beim Original,
dazu die Rückleuchten als durchgehendes Band. -->
<g id="teil-lights" class="teil teil-licht" data-teil="lights">
<rect x="36" y="30" width="9" height="13" rx="4"/>
<rect x="48" y="27" width="9" height="13" rx="4"/>
<rect x="60" y="25" width="9" height="13" rx="4"/>
<rect x="72" y="24" width="9" height="13" rx="4"/>
<rect x="143" y="24" width="9" height="13" rx="4"/>
<rect x="155" y="25" width="9" height="13" rx="4"/>
<rect x="167" y="27" width="9" height="13" rx="4"/>
<rect x="179" y="30" width="9" height="13" rx="4"/>
<rect class="rueck" x="40" y="462" width="52" height="13" rx="6"/>
<rect class="rueck" x="132" y="462" width="52" height="13" rx="6"/>
<title>Beleuchtung</title>
</g>
<!-- Motorhaube mit angedeuteter Mittelsicke -->
<g id="teil-bonnet" class="teil" tabindex="0" data-teil="bonnet">
<path d="M31 152 L193 152 L189 62 C169 40, 55 40, 35 62 Z"/>
<title>Motorhaube</title>
</g>
<path class="sicke" d="M112 60 L112 150"/>
<!-- Windschutzscheibe, Dach, Heckscheibe -->
<path class="scheibe" d="M34 156 L190 156 L178 200 L46 200 Z"/>
<rect class="dach" x="46" y="200" width="132" height="136" rx="10"/>
<!-- Kein Panoramadach: dieses Fahrzeug hat ein geschlossenes
Blechdach. Die Zustandszeile kennt es deshalb auch nicht
(TEILE in js/solar/skodaMQTT.js). -->
<!-- Dachreling. Sie liegt AUF dem Dach: nach innen gerückt,
nicht auf dessen Seitenkanten, und sie bleibt zwischen
den Scheiben - vorn hinter der Windschutzscheibe, hinten
am Spoiler. Läge sie bündig auf den Kanten, ergäbe sie
mit Platte und Spoiler ein geschlossenes Rechteck, und
das sieht aus wie ein Fensterrahmen. Die runden Enden
kommen von der Strichstärke, nicht aus der Geometrie. -->
<g class="reling">
<line x1="54" y1="206" x2="54" y2="330"/>
<line x1="170" y1="206" x2="170" y2="330"/>
</g>
<path class="scheibe" d="M46 336 L178 336 L190 382 L34 382 Z"/>
<!-- Heckspoiler: ein Blatt über der Dachkante, in der Mitte
breit, zu den Enden auslaufend - so sitzt er auf der
Kante statt sie nachzuzeichnen. -->
<path class="spoiler" d="M44 336 C 74 327, 150 327, 180 336
C 150 344, 74 344, 44 336 Z"/>
<!-- ========================================================
Antriebsbatterie - der Ladestand selbst.
Beim MEB liegt der Akku als flache Platte zwischen den
Achsen; genau dort sitzt sie hier auch, von 108 bis 378
über den Radstand. Gefüllt wird von hinten nach vorn,
also im Bild von unten nach oben - dieselbe Richtung,
in der jeder einen Füllstand liest.
Die Reihenfolge ist Absicht und gilt ohne Ausnahme:
die Platte liegt ÜBER allem, was nur Blech und Glas ist
- Karosserie, Haube, beide Scheiben, Dach, Reling,
Spoiler -, und UNTER allem, was Zustand zeigt oder
bedient wird: Türen, Fenster, Klappen, Licht und die
beiden Knöpfe. Eine offene Klappe muss sichtbar
bleiben, auch wenn die Batterie darunter liegt - der
Zustand hat Vorrang vor der Anzeige.
Deshalb steht dieser Block hier und nicht weiter oben:
stand er vor der Heckscheibe, lag die Platte über der
Windschutzscheibe, aber unter der Heckscheibe, und die
Relingstäbe schwammen auf ihr.
Alles Veränderliche - Füllhöhe, Zielmarke, Zahlen und
Farbe - setzt zeichneAkku().
======================================================== -->
<g id="akku" class="akku">
<rect class="huelle" x="46" y="104" width="132" height="278" rx="10"/>
<g clip-path="url(#akkuInnen)">
<rect class="fuellung" id="akkuFuellung" x="50" y="378" width="124" height="0"/>
<!-- Ladestrom: Streifen, die im gefüllten Teil nach
vorn wandern. Nur beim Laden sichtbar. -->
<g class="stroemung" clip-path="url(#akkuPegel)">
<g class="bahn">
<rect x="50" y="430" width="124" height="3" rx="1.5"/>
<rect x="50" y="376" width="124" height="3" rx="1.5"/>
<rect x="50" y="322" width="124" height="3" rx="1.5"/>
<rect x="50" y="268" width="124" height="3" rx="1.5"/>
<rect x="50" y="214" width="124" height="3" rx="1.5"/>
<rect x="50" y="160" width="124" height="3" rx="1.5"/>
<rect x="50" y="106" width="124" height="3" rx="1.5"/>
<rect x="50" y="52" width="124" height="3" rx="1.5"/>
</g>
</g>
<!-- Pegelkante: die helle Linie oben auf der Füllung.
Sie macht den Stand auch dann ablesbar, wenn die
Fläche darunter nur schwach getönt ist. -->
<rect class="kante" id="akkuKante" x="50" y="377" width="124" height="2.5"/>
</g>
<!-- Modulfugen. Sie liegen auf 20, 40, 60 und 80 Prozent
und dienen damit zugleich als Skala. -->
<g class="fugen">
<line x1="50" y1="324" x2="174" y2="324"/>
<line x1="50" y1="270" x2="174" y2="270"/>
<line x1="50" y1="216" x2="174" y2="216"/>
<line x1="50" y1="162" x2="174" y2="162"/>
</g>
<!-- Ladeziel. Eine Ladung, die dort endet, ist kein
Fehler, sondern Absicht - deshalb steht die Marke im
Bild und nicht nur als Zahl daneben. -->
<g class="zielmarke" id="akkuZielMarke">
<line id="akkuZiel" x1="44" y1="108" x2="180" y2="108"/>
<text id="akkuZielText" x="174" y="102">Ziel </text>
</g>
<!-- Ladestand und Reichweite in der Platte. Der dunkle
Umriss der Zahlen (paint-order) hält sie lesbar,
gleich ob Füllung oder leere Zelle dahinter liegt.
Die Höhe ist gerechnet, nicht geschätzt: die Platte
reicht von 108 bis 378, ist also 270 hoch; der
Goldene Schnitt von oben liegt bei 108 + 270 × 0,618
= 274,9. Dort sitzt die optische Mitte der beiden
Zeilen (Oberkante der Ziffern 246, Grundlinie der
Reichweite 303). Weiter oben klebten sie am
Klimaknopf, dessen Ring bei 205 endet - jetzt liegen
41 Einheiten Luft dazwischen. -->
<text class="wert" x="112" y="275">
<tspan id="akkuText"></tspan><tspan class="einheit" dx="3">%</tspan>
</text>
<text class="neben" id="akkuNeben" x="112" y="303"></text>
<title id="akkuTitel">Antriebsbatterie</title>
</g>
<!-- Heckklappe -->
<g id="teil-trunk" class="teil" tabindex="0" data-teil="trunk">
<path d="M34 384 L190 384 L186 458 C168 478, 56 478, 38 458 Z"/>
<title>Heckklappe</title>
</g>
<!-- Türen, jeweils mit Seitenfenster darin -->
<g id="teil-doors" class="teil" data-teil="doors">
<g tabindex="0"><rect x="12" y="202" width="23" height="74" rx="6"/><title>Tür vorn links</title></g>
<g tabindex="0"><rect x="12" y="278" width="23" height="66" rx="6"/><title>Tür hinten links</title></g>
<g tabindex="0"><rect x="189" y="202" width="23" height="74" rx="6"/><title>Tür vorn rechts</title></g>
<g tabindex="0"><rect x="189" y="278" width="23" height="66" rx="6"/><title>Tür hinten rechts</title></g>
</g>
<g id="teil-windows" class="teil teil-fenster" data-teil="windows">
<rect x="17" y="210" width="13" height="58" rx="4"/>
<rect x="17" y="286" width="13" height="50" rx="4"/>
<rect x="194" y="210" width="13" height="58" rx="4"/>
<rect x="194" y="286" width="13" height="50" rx="4"/>
<title>Seitenfenster</title>
</g>
<!-- Außenspiegel -->
<g class="spiegel">
<rect x="1" y="194" width="16" height="10" rx="5"/>
<rect x="207" y="194" width="16" height="10" rx="5"/>
</g>
<!-- ========================================================
Steuerung: die beiden anklickbaren Stellen.
Bedient wird das, worum es geht - der Ladeanschluss für
das Laden, der Innenraum für Klima und Lüftung. Beide
tragen einen Ring als Hinweis darauf, dass sie mehr
sind als Anzeige, und öffnen ein Modal mit den
Befehlen.
Sie stehen bewusst NICHT in der Zustandszeile: die
zeigt, was ist. Diese beiden ändern etwas.
======================================================== -->
<!-- Der Weg des Ladestroms vom Anschluss in die Platte.
Läuft nur, solange geladen wird. -->
<path class="ladefluss" d="M199 399 C 192 393, 183 386, 176 376"/>
<!-- Ladeanschluss. Beim Enyaq sitzt die Klappe am hinteren
rechten Kotflügel.
Gleiche Bauweise wie der Klimaknopf: gestrichelter Ring,
deckende Scheibe, Zeichen darauf. Beides sind Knöpfe, also
sehen beide gleich aus - was sie unterscheidet, ist das
Zeichen und beim Laden die Farbe. Die deckende Scheibe ist
auch hier nötig, weil darunter die Akkuplatte durchläuft. -->
<g id="teil-plug" class="teil-stecker steuer" tabindex="0"
role="button" data-steuer="laden" aria-label="Laden steuern">
<circle class="ring" cx="203" cy="408" r="19"/>
<circle class="flaeche" cx="203" cy="408" r="13"/>
<path class="glyphe" d="M199 401 L207 407 L202 407 L207 415"/>
<title>Laden steuern</title>
</g>
<!-- Innenraum, auf der Windschutzscheibe: Klima und Lüftung. -->
<g id="steuer-klima" class="steuer" tabindex="0"
role="button" data-steuer="klima" aria-label="Klima und Lüftung steuern">
<circle class="ring" cx="112" cy="186" r="19"/>
<circle class="flaeche" cx="112" cy="186" r="13"/>
<!-- Lüfterrad. Es dreht sich, solange die Klimatisierung
läuft; die Drehung sitzt in der inneren Gruppe, damit
sie die Verschiebung nach aussen nicht überschreibt. -->
<g class="luefter" transform="translate(112 186)">
<g class="blaetter">
<use href="#klimaFluegel"/>
<use href="#klimaFluegel" transform="rotate(120)"/>
<use href="#klimaFluegel" transform="rotate(240)"/>
</g>
<circle class="nabe" r="1.9"/>
</g>
<title>Klima und Lüftung steuern</title>
</g>
</svg>
<p class="skoda-hinweis">
Ladeanschluss und Innenraum antippen, um zu steuern
</p>
</div>
<div class="col-md-7">
<div class="skoda-werte">
<div class="skoda-wert"><span class="k">Reichweite</span><span class="v" id="skodaReichweite"></span></div>
<div class="skoda-wert"><span class="k">Ladeleistung</span><span class="v" id="skodaLeistung"></span></div>
<div class="skoda-wert"><span class="k">Restzeit</span><span class="v" id="skodaRestzeit"></span></div>
<div class="skoda-wert"><span class="k">Ladeziel</span><span class="v" id="skodaZielText"></span></div>
<div class="skoda-wert"><span class="k">Kilometerstand</span><span class="v" id="skodaKm"></span></div>
<div class="skoda-wert"><span class="k">Klima</span><span class="v" id="skodaKlima"></span></div>
<!-- Alles, was sich nicht ins Bild zeichnen lässt. -->
<div class="skoda-daten">
<div class="skoda-kennzahlen">
<div class="kennzahl" id="skodaKachelLeistung">
<span class="zeichen"><i class="bi bi-lightning-charge-fill" aria-hidden="true"></i></span>
<span class="text">
<span class="k">Ladeleistung</span>
<span class="v" id="skodaLeistung"></span>
<span class="neben" id="skodaLadeart"></span>
</span>
</div>
<div class="kennzahl" id="skodaKachelRestzeit">
<span class="zeichen"><i class="bi bi-hourglass-split" aria-hidden="true"></i></span>
<span class="text">
<span class="k">Restzeit</span>
<span class="v" id="skodaRestzeit"></span>
<span class="neben" id="skodaFertig"></span>
</span>
</div>
<div class="kennzahl">
<span class="zeichen"><i class="bi bi-speedometer2" aria-hidden="true"></i></span>
<span class="text">
<span class="k">Kilometer&shy;stand</span>
<span class="v" id="skodaKm"></span>
</span>
</div>
<div class="kennzahl" id="skodaKachelKlima">
<span class="zeichen"><i class="bi bi-fan" id="skodaKlimaZeichen" aria-hidden="true"></i></span>
<span class="text">
<span class="k">Klima</span>
<span class="v" id="skodaKlima"></span>
</span>
</div>
</div>
<!-- Woher der Strom gerade kommt: der Balken teilt die
Ladeleistung in den Anteil, den die PV deckt, und den
Rest aus dem Netz. -->
<div class="skoda-quelle mt-3" id="skodaQuelleBox">
<div class="d-flex justify-content-between small text-secondary mb-1">
<span>Ladestrom</span><span id="skodaQuelleText"></span>
Rest aus dem Netz. Steht nur da, solange geladen wird -
ohne Ladung hat der Balken nichts zu teilen. -->
<div class="skoda-quelle" id="skodaQuelleBox" hidden>
<div class="kopf">
<span class="skoda-marke">Ladestrom</span>
<span class="summe" id="skodaQuelleText"></span>
</div>
<div class="skoda-balken">
<div class="teil-pv" id="skodaAnteilPV"></div>
<div class="teil-netz" id="skodaAnteilNetz"></div>
</div>
<div class="legende">
<span><i class="pkt pv"></i>Sonne <b id="skodaQuellePV"></b></span>
<span><i class="pkt netz"></i>Netz <b id="skodaQuelleNetz"></b></span>
</div>
</div>
</div>
<!-- Was das Fahrzeug in Woche, Monat und Jahr gefahren ist.
Gerechnet aus dem Kilometerstand, nicht aus den erkannten
Fahrten - siehe ajax/skoda.php, ?was=strecke. -->
<div class="skoda-fahrleistung">
<span class="skoda-marke">Fahrleistung</span>
<div class="reihe" id="skodaFahrleistung"></div>
</div>
<!-- Zustandszeile: benennt in Worten, was die Farben im Bild
zeigen, und hebt beim Überfahren das Teil hervor. -->
<div class="skoda-zustand">
<span class="skoda-marke">Zustand</span>
<div class="skoda-teile" id="skodaTeile"></div>
</div>
</div>
</div>
</div>
</div>
+16 -17
View File
@@ -64,23 +64,6 @@
<!-- /.card -->
</div>
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
<div class="card p-0 mb-4" style="width:100%">
<div class="card-header pb-0 pt-1">
<h3 class="card-title">Statistik dieses Jahr</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
<div class="row-cols-1 row-cols-md-1 g-4 p-2" id="Stats-Year">
</div>
</div>
<!-- /.card -->
</div>
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
<div class="card p-0 mb-4" style="width:100%">
<div class="card-header pb-0 pt-1">
@@ -118,6 +101,22 @@
</div>
</div>
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
<div class="card p-0 mb-4" style="width:100%">
<div class="card-header pb-0 pt-1">
<h3 class="card-title">Statistik dieses Jahr</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
<!-- Gefuellt von baueJahresstatistik() aus js/solar/jahresstatistik.js -
dieselbe Tabelle wie auf der Historienseite, nur einspaltig. -->
<div class="card-body statistik" id="Stats-Year"></div>
</div>
<!-- /.card -->
</div>
<!-- Consumption Graph-->
<div class="col-lg-12">