Sammelstand der offenen Arbeit im Dashboard. Einstellungen: eine Eingabemaske fuer die Energiepreise unter restricted/settings.php, dazu costs.php als Modell und ajax/settings.php als Endpunkt. Die Preise stehen jetzt in gridCosts, gasCosts und fuelCosts statt fest im Quelltext von getStats.php; die Jahresstatistik rechnet ueber LEAD() den jeweils gueltigen Zeitraum aus und schlaegt den anteiligen Grundpreis auf. Eingegeben wird in gewohnten Einheiten - l/100 km, kWh/100 km, Euro je Liter -, umgerechnet wird beim Speichern. solarLog_costs.sql beschreibt die Umstellung der Tabellen. Skoda: eigene Seite mit Live-Werten ueber MQTT und Historie ueber ajax/skoda.php, Kommandos ueber ajax/skodaCmd.php. Karte: Leaflet mit einem eigenen Kachel-Zwischenspeicher (ajax/tile.php), dessen Ablage unter tiles/ nicht ins Repository gehoert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
384 lines
19 KiB
PHP
384 lines
19 KiB
PHP
<?php
|
||
/*
|
||
* Škoda-Dashboard.
|
||
*
|
||
* Zwei Datenwege, bewusst getrennt:
|
||
*
|
||
* MQTT Ladestand, Verriegelung, Reichweite, Ladezustand und
|
||
* Wallbox-Leistung kommen laufend über solarManager/# und
|
||
* aktualisieren die Anzeige sofort.
|
||
*
|
||
* ajax/skoda.php Alles, was nur in der Tabelle steht - Türen, Fenster,
|
||
* Licht, Kilometerstand, Position, Klima - sowie sämtliche
|
||
* Auswertungen. Wird beim Laden und danach minütlich geholt.
|
||
*
|
||
* Solange kein Fahrzeug angebunden ist, bleibt die Tabelle leer; jeder
|
||
* Abschnitt zeigt dann seinen Leerzustand statt eines Fehlers.
|
||
*
|
||
* Die Fahrzeuggrafik ist eine maßstäbliche Draufsicht des Enyaq:
|
||
* 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.
|
||
*/
|
||
?>
|
||
<!--begin::App Main-->
|
||
<main class="app-main">
|
||
<div class="app-content-header">
|
||
<div class="container-fluid">
|
||
<div class="row align-items-center">
|
||
<div class="col-sm-8">
|
||
<h3 class="mb-0">Škoda</h3>
|
||
</div>
|
||
<div class="col-sm-4 text-sm-end">
|
||
<span class="skoda-alter text-secondary" id="skodaAlter"> </span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="app-content">
|
||
<div class="container-fluid">
|
||
|
||
<!-- ================================================ Zustand ===== -->
|
||
<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="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">
|
||
<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">
|
||
|
||
<!-- 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>
|
||
</svg>
|
||
</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>
|
||
</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>
|
||
</div>
|
||
<div class="skoda-balken">
|
||
<div class="teil-pv" id="skodaAnteilPV"></div>
|
||
<div class="teil-netz" id="skodaAnteilNetz"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================================================= Ladungen ==== -->
|
||
<div class="row">
|
||
<div class="col-xl-8 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">Verlauf der letzten Ladung</h3>
|
||
<div class="card-tools">
|
||
<span class="text-secondary small" id="skodaKurveZeit"></span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body pb-1">
|
||
<div class="chart-container"><canvas id="skodaKurve"></canvas></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-xl-4 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">Ladungen</h3>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<div class="skoda-liste" id="skodaLadungen"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================== Akkugesundheit == -->
|
||
<div class="row">
|
||
<div class="col-xl-8 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">Akkugesundheit</h3>
|
||
<div class="card-tools">
|
||
<span class="text-secondary small">kWh je 100 Ladestandsprozent</span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body pb-1">
|
||
<div class="chart-container"><canvas id="skodaGesundheit"></canvas></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-xl-4 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">Befund</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="skoda-befund" id="skodaBefund">
|
||
<p class="text-secondary mb-0">Noch keine ausgewerteten Ladungen.</p>
|
||
</div>
|
||
<p class="text-secondary small mb-0 mt-3">
|
||
Gemessen wird am Wallbox-Zähler, die Ladeverluste sind also
|
||
enthalten. Absolut ist der Wert eine Näherung — aussagekräftig
|
||
ist sein Verlauf, denn ein alternder Akku bräuchte <em>weniger</em>
|
||
Kilowattstunden für dieselben hundert Prozentpunkte.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================ Standort ========== -->
|
||
<!-- Karte auf Leaflet. Die Kacheln kommen nicht unmittelbar von
|
||
OpenStreetMap, sondern über ajax/tile.php aus dem eigenen
|
||
Zwischenspeicher auf dem NAS - siehe dort, warum.
|
||
|
||
Die Fahrtlinien sind gestrichelt, und zwar mit Absicht: die API
|
||
liefert nur die letzte bekannte PARKposition, also Start und Ziel.
|
||
Die tatsächlich gefahrene Strecke kennt niemand, und eine
|
||
durchgezogene Linie auf einer Straßenkarte würde genau das
|
||
behaupten. -->
|
||
<div class="row">
|
||
<div class="col-xl-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">Standort und Fahrten</h3>
|
||
<div class="card-tools">
|
||
<button type="button" id="skodaKarteHeim" class="btn btn-tool"
|
||
title="Auf den aktuellen Standort zentrieren">
|
||
<i class="bi bi-geo-alt"></i>
|
||
</button>
|
||
<a href="#" id="skodaOsmLink" class="btn btn-tool" target="_blank"
|
||
rel="noopener noreferrer" title="Auf OpenStreetMap öffnen">
|
||
<i class="bi bi-box-arrow-up-right"></i>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-2">
|
||
<div id="skodaKarte" class="skoda-karte"></div>
|
||
<div class="d-flex justify-content-between align-items-center px-2 pt-2 pb-1">
|
||
<span class="text-secondary small" id="skodaStandort">–</span>
|
||
<span class="skoda-kartenlegende">
|
||
<span><i class="pkt jetzt"></i>jetzt</span>
|
||
<span><i class="pkt halt"></i>Halt</span>
|
||
<span><i class="lin"></i>Luftlinie</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-xl-5 d-flex align-items-stretch">
|
||
<!-- Die Klasse zeigt-kwh / zeigt-prozent auf dieser Kachel
|
||
entscheidet, welche Einheit die Zeilen anzeigen. Beide Werte
|
||
stehen im Markup, umgeschaltet wird allein per CSS - so muss
|
||
beim Wechsel nichts neu gebaut werden, und die Auswahl
|
||
übersteht auch die minütliche Auffrischung der Liste. -->
|
||
<div class="card mb-4 p-0 w-100 zeigt-kwh" id="skodaFahrtenKachel">
|
||
<div class="card-header pb-0 pt-1">
|
||
<h3 class="card-title">Fahrten</h3>
|
||
<div class="card-tools d-flex align-items-center gap-2">
|
||
<!-- Text kommt aus zeigeVerbrauchsbasis(): der Verbrauch in
|
||
Kilowattstunden ist nicht gemessen, sondern aus dem
|
||
Ladestand und der eigenen Ladehistorie gerechnet. Womit
|
||
genau, steht im Titel dieses Elements. -->
|
||
<span class="text-secondary small" id="skodaVerbrauchBasis"></span>
|
||
<span class="skoda-umschalter" id="skodaEinheit" role="group"
|
||
aria-label="Einheit des Verbrauchs">
|
||
<button type="button" data-einheit="kwh" aria-pressed="true">kWh</button>
|
||
<button type="button" data-einheit="prozent" aria-pressed="false">%</button>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<div class="skoda-liste" id="skodaFahrten"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</main>
|
||
<!--end::App Main-->
|