Einstellungsseite, Skoda-Seite und Karte
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>
This commit is contained in:
+82
-15
@@ -4,30 +4,94 @@
|
||||
* Ersetzt getConsData_{month,year,decade}.php und getProdData_{month,year,decade}.php.
|
||||
*
|
||||
* Aufruf: energyHistory.php?series=prod|cons&range=month|year|decade
|
||||
*
|
||||
* Zwei Quellen, je nach Zeitraum:
|
||||
*
|
||||
* month EnergyFlow, also die Rohdaten. Nach Tagen gruppiert, und der
|
||||
* heutige Balken soll bis zur letzten Messung reichen. Das
|
||||
* Stundenarchiv wird nur einmal nachts fortgeschrieben und wäre
|
||||
* für den laufenden Tag noch leer.
|
||||
*
|
||||
* year EnergyFlow_hourly, das Stundenarchiv. Die Rohdaten werden nach
|
||||
* decade zwölf Monaten ausgedünnt (solarlog-rollup.sh thin), diese
|
||||
* Diagramme fänden dort also nichts mehr. Bei Gruppierung nach
|
||||
* Monat bzw. Jahr fällt der fehlende laufende Tag nicht auf.
|
||||
*
|
||||
* Beide Quellen liefern Wattstunden, weil die Diagramme auf der y-Achse durch
|
||||
* 1.000.000 teilen und MWh beschriften. Die Rohdaten sind Momentanleistungen
|
||||
* alle fünf Minuten, daher /12; im Archiv stehen bereits kWh, daher *1000.
|
||||
* Gegengerechnet über einen kompletten Monat stimmen beide auf 0,003 % überein
|
||||
* - der Rest ist die /12-Näherung gegenüber der echten Zeitgewichtung.
|
||||
*
|
||||
* pv_fehlbetrag_kwh wird zur erfassten Erzeugung addiert. Wenn Wechselrichter
|
||||
* die Verbindung zur OpenDTU verlieren, fehlt ihr Beitrag in pvP bzw. pv_kwh -
|
||||
* die Einspeisung, die Batterieladung und der Heizstab sind aber vollständig
|
||||
* gemessen. Ohne die Korrektur wird der Direktverbrauch als Differenz negativ
|
||||
* und der Balken bleibt leer, etwa am 03.08.2026 mit -0,5 statt +15,9 kWh.
|
||||
* Im Rohdaten-Zweig kommt der Betrag über eine nach Tagen vorverdichtete
|
||||
* abgeleitete Tabelle dazu, sonst zählte ihn jede der zwölf Zeilen je Stunde
|
||||
* erneut; für den laufenden Tag gibt es ihn noch nicht, dann bleibt er 0.
|
||||
*
|
||||
* Die Einspeisung kommt in beiden Fällen aus gridPfeed, dem Zählerregister,
|
||||
* nicht aus dem Saldo gridP. Innerhalb eines Fensters kommen Bezug und
|
||||
* Einspeisung gleichzeitig vor; der Saldo löscht kurze Bezüge weg und passt
|
||||
* dann nicht mehr zu dem, was abgerechnet wird.
|
||||
*/
|
||||
|
||||
require_once("../helper.php");
|
||||
require_once("./chartData.php");
|
||||
|
||||
// Zeitraum -> [Format der Beschriftung, Filter auf datetime]
|
||||
// Zeitraum -> [Format der Beschriftung, Quelle]
|
||||
$ranges = [
|
||||
"month" => ["'%d.%b.'", "DATE_FORMAT(datetime,'%Y%m%d') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 1 MONTH),'%Y%m%d')"],
|
||||
"year" => ["'%b. %y'", "DATE_FORMAT(datetime,'%Y%m') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 13 MONTH),'%Y%m')"],
|
||||
"decade" => ["'%Y'", "DATE_FORMAT(datetime,'%Y') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 10 YEAR),'%Y')"],
|
||||
"month" => ["'%d.%b.'", "roh"],
|
||||
"year" => ["'%b. %y'", "archiv"],
|
||||
"decade" => ["'%Y'", "archiv"],
|
||||
];
|
||||
|
||||
// Filter auf die Zeitspalte. ZEIT wird durch den Spaltennamen der Quelle ersetzt.
|
||||
$filters = [
|
||||
"month" => "DATE_FORMAT(ZEIT,'%Y%m%d') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 1 MONTH),'%Y%m%d')",
|
||||
"year" => "DATE_FORMAT(ZEIT,'%Y%m') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 13 MONTH),'%Y%m')",
|
||||
"decade" => "DATE_FORMAT(ZEIT,'%Y') >= DATE_FORMAT(DATE_SUB(now(),INTERVAL 10 YEAR),'%Y')",
|
||||
];
|
||||
|
||||
// Datenreihen je Diagramm. Beide teilen sich Solarertrag und Heizstab und
|
||||
// unterscheiden sich nur darin, wohin die Energie fließt (prod) bzw. woher
|
||||
// sie kommt (cons).
|
||||
$series = [
|
||||
"prod" => "SUM(pvP/12 - IF(gridP < 0, -gridP/12,0) - IF(battP < 0, -battP/12,0) - heaterPwr/12) AS Direktverbrauch,
|
||||
$quellen = [
|
||||
"roh" => [
|
||||
"tabelle" => "EnergyFlow",
|
||||
"zeit" => "datetime",
|
||||
"join" => "LEFT JOIN (SELECT DATE(stunde) AS tag, SUM(pv_fehlbetrag_kwh) AS fehlt
|
||||
FROM EnergyFlow_hourly GROUP BY 1) f ON f.tag = DATE(datetime)",
|
||||
"reihen" => [
|
||||
"prod" => "SUM(pvP/12 - gridPfeed/12 - IF(battP < 0, -battP/12,0) - heaterPwr/12)
|
||||
+ IFNULL(MAX(f.fehlt),0)*1000 AS Direktverbrauch,
|
||||
SUM(heaterPwr/12) AS Heizstab,
|
||||
SUM(IF(battP < 0, -battP/12,0)) AS Batterieladung,
|
||||
SUM(gridPfeed/12) AS Einspeisung",
|
||||
"cons" => "SUM(pvP/12 - IF(gridP < 0, -gridP/12,0) - IF(battP < 0, -battP/12,0) - heaterPwr/12) AS Solarverbrauch,
|
||||
"cons" => "SUM(pvP/12 - gridPfeed/12 - IF(battP < 0, -battP/12,0) - heaterPwr/12)
|
||||
+ IFNULL(MAX(f.fehlt),0)*1000 AS Solarverbrauch,
|
||||
SUM(heaterPwr/12) AS Heizstab,
|
||||
SUM(IF(battP > 0, battP/12,0)) AS Batteriebezug,
|
||||
SUM(gridPcons/12) AS Netzbezug",
|
||||
],
|
||||
],
|
||||
"archiv" => [
|
||||
"tabelle" => "EnergyFlow_hourly",
|
||||
"zeit" => "stunde",
|
||||
"join" => "",
|
||||
"reihen" => [
|
||||
"prod" => "SUM((pv_kwh + IFNULL(pv_fehlbetrag_kwh,0) - netz_einsp_kwh - batt_laden_kwh - heizstab_kwh)*1000) AS Direktverbrauch,
|
||||
SUM(heizstab_kwh*1000) AS Heizstab,
|
||||
SUM(batt_laden_kwh*1000) AS Batterieladung,
|
||||
SUM(netz_einsp_kwh*1000) AS Einspeisung",
|
||||
"cons" => "SUM((pv_kwh + IFNULL(pv_fehlbetrag_kwh,0) - netz_einsp_kwh - batt_laden_kwh - heizstab_kwh)*1000) AS Solarverbrauch,
|
||||
SUM(heizstab_kwh*1000) AS Heizstab,
|
||||
SUM(batt_entladen_kwh*1000) AS Batteriebezug,
|
||||
SUM(netz_bezug_kwh*1000) AS Netzbezug",
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$linecolors["Direktverbrauch"] = "#FFFF00";
|
||||
@@ -46,14 +110,17 @@ $linecolors["Vorhersage"] = "#2222FF";
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$rangeKey = isset($_GET["range"]) ? $_GET["range"] : "";
|
||||
$rangeKey = isset($_GET["range"]) ? $_GET["range"] : "";
|
||||
$seriesKey = isset($_GET["series"]) ? $_GET["series"] : "";
|
||||
if (!isset($ranges[$rangeKey]) || !isset($series[$seriesKey])) {
|
||||
if (!isset($ranges[$rangeKey]) || !in_array($seriesKey, ["prod", "cons"], true)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(["error" => "Unbekannter Zeitraum oder Datenreihe."]);
|
||||
exit;
|
||||
}
|
||||
list($labelFormat, $filter) = $ranges[$rangeKey];
|
||||
list($labelFormat, $quelleKey) = $ranges[$rangeKey];
|
||||
$quelle = $quellen[$quelleKey];
|
||||
$zeit = $quelle["zeit"];
|
||||
$filter = str_replace("ZEIT", $zeit, $filters[$rangeKey]);
|
||||
|
||||
$obj = (object)[];
|
||||
$obj->labels = [];
|
||||
@@ -62,12 +129,12 @@ $obj->datasets = [];
|
||||
if (checkLogin()) {
|
||||
$mysql = new mysqli($mysql_server, $mysql_solarUser, $mysql_solarPass, $mysql_solarDB);
|
||||
$query = "SELECT
|
||||
DATE_FORMAT(datetime, $labelFormat) AS 'time',
|
||||
" . $series[$seriesKey] . "
|
||||
FROM EnergyFlow
|
||||
DATE_FORMAT($zeit, $labelFormat) AS 'time',
|
||||
" . $quelle["reihen"][$seriesKey] . "
|
||||
FROM " . $quelle["tabelle"] . " " . $quelle["join"] . "
|
||||
WHERE $filter
|
||||
GROUP BY DATE_FORMAT(datetime, $labelFormat)
|
||||
ORDER BY datetime ; ";
|
||||
GROUP BY DATE_FORMAT($zeit, $labelFormat)
|
||||
ORDER BY MIN($zeit) ; ";
|
||||
$obj = buildChartData($mysql, $query, $linecolors);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user