Visualisierung der Zählerwerte


Allgemeine Informationen

Auf dieser Seite wird eine Beispielvisualisierung von Zählerdaten präsentiert. Die gezeigten Werte sind bewusst eingeschränkt, repräsentieren jedoch realitätsnahe Datenstrukturen. Ziel ist es, eine intuitive Darstellung und Auswertung von Verbrauchs- und Einspeisewerten zu ermöglichen. Der vollständige Quellcode der Visualisierung steht im Anschluss zur Verfügung. Die zugrundeliegende Datenbasis wird aus einer MySQL-Datenbank geladen, welche idealerweise von einem Mikrocontroller (z.B. Arduino) regelmäßig gespeist wird.

Inhaltsverzeichnis

Beispiel Visualisierung

Die Zählerwerte sind für die Öffentlichkeit stark eingeschränkt und nicht zu 100 % realistisch. Sie sollen als Beispiel dienen und Möglichkeiten aufzeigen. Der aktuell verfügbare Datensatz umfasst den Zeitraum vom 24.09.2025 bis zum 03.11.2025.

Aktuelle Zählerwerte

Werte in der Tabelle werden nach Auswahl eines Datums neu eingelesen. Alle 60 Sekunden wird der aktuelle Verbrauch in Watt aktualisiert. Werte die sich ändern, leuchten kurz Rot auf.

Datum:
Werte für OBIS 1.8.0
Zählerstand: kWh
Tagesverbrauch: kWh
Werte für OBIS 2.8.0
Zählerstand: kWh
Tageseinspeisung: kWh
Werte für OBIS 10.07.0 60 Sek. bis Update
Aktuelle Leistung: Watt
Zeitstempel: Uhr

Graph Stromzähler Daten

Kurze Funktionsbeschreibung

Die Visualisierung besteht aus dynamischen Diagrammen, Tabellen und interaktiven Steuerelementen. Daten werden per AJAX von der Datenbank geladen und mit JavaScript aktualisiert. In der Tabelle werden die aktuellen Zählerstände und Verbrauchswerte zum jeweiligen Datum angezeigt. Werte aus der Vergangenheit können über das Datum ausgewählt werden. Der Wert für den aktuellen Verbrauch oder die Einspeisung wird alle 60 Sekunden automatisch aktualisiert.

  • Donut-Diagramm: Aktuelle Leitung in Watt
    Die aktuelle Leistung in Watt wird in einem Donut-Diagramm angezeigt. Ist der Donut grün (negativer Wert), liegt eine Einspeisung vor. Bei den Farben Gelb und Rot liegt ein Netzbezug vor. Die automatische Aktualisierung erfolgt alle 60 Sekunden.
  • Balkendiagramme: Übersicht Tagesverbräuche in KWH
    In diesem Diagramm sind die summierten Tagesverbräuche (blau) und Einspeisewerte (grün) des jeweils ausgewählten Monats in kWh dargestellt.
  • Balkendiagramme: Übersicht Monatsverbräuche in KWH
    In diesem Diagramm sind die summierten Monatsverbräuche (blau) und Einspeisewerte (grün) des jeweils ausgewählten Jahres in kWh dargestellt.
  • Balkendiagramme: Übersicht Jahresverbräuche in KWH
    In diesem Diagramm sind die summierten Jahresverbräuche (blau) und Einspeisewerte (grün) in kWh dargestellt.
  • Liniendiagramm: Tagesauswertung
    In diesem Liniendiagramm wird die verbrauchte und eingespeiste Leistung in Watt in Abhängigkeit zur Zeitachse für den jeweiligen Tag angezeigt. Wird diese Ansicht ausgewählt, können über die Datumsauswahl in der Tabelle die Daten für den jeweiligen Tag angezeigt werden.

Quellcode Visualisierung

Die Visualisierung basiert auf der Bibliothek graph.js (Version 2.5.0). Zur Anzeige von Meldungen wird sweetalert2 verwendet. Die Daten werden über PHP aus einer MySQL-Datenbank geladen. Voraussetzung sind vorbereitete SQL-Tabellen mit OBIS-Daten.
Weitere Infos findest du unter: `Vom Arduino in die MySql Datenbank.`


PHP Quellcode Grenzwertermittlung

Dieser PHP-Code ermittelt das Start- und Enddatum der verfügbaren Messwerte. Daraus werden z.B. Dropdown-Auswahlfelder für Jahre und gültige Datumsbereiche generiert. Es erfolgt eine SQL-Abfrage auf die Tabelle obis_180_280, um das erste und letzte Messdatum zu laden. Danach werden die Daten als Array aufbereitet und für die HTML-Ausgabe vorbereitet.

			/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben
/* Abfrage Steuerdaten */
	$parts_end_ymd = "";
	$parts_start_ymd = "";
	$option_yyyy_kwh = "";
	$statement = "SELECT * FROM obis_180_280 WHERE date = (SELECT MIN(date) FROM obis_180_280) 
				  UNION 
				  SELECT * FROM obis_180_280 WHERE date = (SELECT MAX(date) FROM obis_180_280);"; 
	$sqlresult = mysqli_query($dbcon, $statement);
	if ($sqlresult && mysqli_num_rows($sqlresult) > 0) {
		// Schleife für alle Datensätze (in diesem Fall 2)
		while ($dsatzSQL = mysqli_fetch_assoc($sqlresult)) {
			// Speichern der Daten im Array
			$DBdataArray[] = $dsatzSQL;
		}
		// Sicherstellen, dass genau 2 Datensätze im Array sind
		if (count($DBdataArray) == 2) {
			$parts_start_ymd = explode("-", $DBdataArray[0]["date"]);
			$parts_end_ymd = explode("-", $DBdataArray[1]["date"]);			
		} elseif(count($DBdataArray) == 1) {
			$parts_start_ymd = explode("-", $DBdataArray[0]["date"]);
			$parts_end_ymd = explode("-", $DBdataArray[0]["date"]);		
		}else{
			echo "kein Datensatz";
		}
		// Dropdown Jahresauswahl
		for ($i = $parts_start_ymd[0]; $i <= $parts_end_ymd[0]; $i++) {
				$selected = ($i == $parts_end_ymd[0]) ? " selected" : "";
				$option_yyyy_kwh .= "<option value=\"$i\"$selected>$i</option>";
		}
	}

	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);
			
		

Datenbankverbindung dbcon.php

Diese Datenbankverbindung wird für alle SQL-Abfragen benötigt und muss daher in jede PHP-Datei eingebunden include "dbcon.php"; werden.

			/* Datenbankverbindung */
	$username = "XXX";
	$password = "AAA";
	$database = "YYY";
	$hostname = "CCC"; 
//connection to the database
	$dbcon_sh = mysqli_connect($hostname, $username, $password, $database);
	mysqli_set_charset($dbcon, 'utf8');	

			
		

HTML Seitenaufbau

Das HTML stellt den strukturellen Rahmen der Seite bereit. Inhalte wie Datumsauswahl, Tabellenstruktur und Platzhalter für Diagramme sind hier definiert. Dynamische Inhalte werden per JavaScript und PHP befüllt.

			<section>
	<h2>Visualisierung</h2>
	<div class="row gtr-200">
		<div class="col-6 col-12-medium">
			<h3>Aktuelle Zählerwerte</h3>
			<p>
				Werte in der Tabelle werden nach Auswahl eines Datums neu eingelesen. 
				Alle 60 Sekunden wird der aktuelle Verbrauch in Watt aktualisiert.
				Werte die sich ändern, leuchten kurz Rot auf.
			</p>
			<div class="table-wrapper">
				<table class="alt">
					<tbody>
						<tr>
							<td>Datum:</td>
							<td>
								<input id="input_date_180_280" type="date" value="'.$parts_end_ymd[0].'-'.$parts_end_ymd[1].'-'.$parts_end_ymd[2].'" min="'.$parts_start_ymd[0].'-'.$parts_start_ymd[1].'-'.$parts_start_ymd[2].'" max="'.$parts_end_ymd[0].'-'.$parts_end_ymd[1].'-'.$parts_end_ymd[2].'" onchange="get_180_280(this.value)">
							</td>
						</tr>
						<tr>
							<td colspan="2">
								<b>Werte für OBIS 1.8.0</b>
							</td>									
						</tr>
						<tr>
							<td>Zählerstand:</td>
							<td id="value_obis180"> kWh</td>
						</tr>
						<tr>
							<td>Tagesverbrauch:</td>
							<td id="used_obis180"> kWh</td>
						</tr>
						<tr>
							<td colspan="2">
								<b>Werte für OBIS 2.8.0</b>
							</td>									
						</tr>
						<tr>
							<td>Zählerstand:</td>
							<td id="value_obis280"> kWh</td>
						</tr>
						<tr>
							<td>Tageseinspeisung:</td>
							<td id="used_obis280"> kWh</td>
						</tr>
						<tr>
							<td><b>Werte für OBIS 10.07.0</b></td>
							<td><span id="sek_to_update_10070">60</span> Sek. bis Update</td>
						</tr>
						<tr>
							<td>Aktuelle Leistung:</td>
							<td id="value_obis10070"> Watt</td>
						</tr>
						<tr>
							<td>Zeitstempel:</td>
							<td id="time_obis10070"> Uhr</td>
						</tr>			
					</tbody>	
				</table>
			</div>					
		</div>
		<div class="col-6 col-12-medium">
			<h3>Graph Stromzähler Daten</h3>
			<select id="drop_Funktion" onchange="loadDynamicContent()">
				<option value="option1" selected>Aktuelle Leistung in Watt</option>
				<option value="option2" >Übersicht Tagesverbräuche in KWH</option>
				<option value="option3" >Übersicht Monatsverbräuche in KWH</option>
				<option value="option4" >Übersicht Jahresverbräuche in KWH</option>
				<option value="option5" >Tagesauswertung</option>
			</select>
			<div id="dynamicContent">
			  <!-- Der dynamisch geladene Inhalt erscheint hier -->
			</div>
			  <canvas id="Option_Graph" width="100%" height="100%"></canvas>
		</div>
	</div>
<section>
			
		

Variablenübergabe php --> JS

Zur Übergabe von PHP-Daten an JavaScript werden <script>-Blöcke verwendet, in denen PHP-Werte als Konstanten definiert werden. Dies erlaubt z.B. die Steuerung der Datumsauswahl.

			<!-- Übergabe von PHP-Daten an JavaScript -->
	<script>
		const partsEndYmd = "<?php echo implode('-', $parts_end_ymd); ?>"; // z.B. "2025-09-29"
		const partsEndYm = "<?php echo $parts_end_ymd[0] . '-' . $parts_end_ymd[1]; ?>"; // z.B. "2025-09"
		const partsStartYm = "<?php echo $parts_start_ymd[0] . '-' . $parts_start_ymd[1]; ?>"; // z.B. "2023-01"
		const partsStartYear = "<?php echo $parts_start_ymd[0]; ?>"; // z.B. "2023"
		const partsEndYear = "<?php echo $parts_end_ymd[0]; ?>"; // z.B. "2025"
		const optionYyyyKwh = `<?php echo $option_yyyy_kwh; ?>`; // HTML <option>-Elemente für das Jahr
	</script>
			
		

Javasqript Start Umgebung

Beim Laden der Seite werden mittels DOMContentLoaded initiale Daten geladen und Event-Handler gesetzt. Dadurch ist die Anwendung sofort nutzbar. Zusätzlich werden hier die dynamischen Elemente angesteuert, nachdem die jeweilige Funktionsart ausgewählt wurde.

			//Skripte Laden nach Seitenaufbau-->
	document.addEventListener("DOMContentLoaded", function () {
		loadDynamicContent(); //Laden der Daten für die Funktionsauswahl
		get_180_280(partsEndYmd); // Laden der Daten für Tabelle obis 180 und 280
	});

	
//Load Dynamik Content
	// JSON-Daten mit HTML-Struktur und optionalem AJAX-Aufruf (ohne Chart)
	const contentData = {
		"option1": `<br>
					<p>
						Anzeige der aktuellen Leistung der Einspeisung oder Bezug in Watt.
					</p>`,
		"option2": `<br>
					<p>
						Es werden die Verbräuche in kWh für den jeweiligen Tag des ausgewählten Zeitraums angezeigt.
					</p>
					<div class="table-wrapper">
						<table class="alt">
							<tbody>
								<tr>
									<td>Bitte Monat wählen:</td>
									<td><input type="month" id="yyyy_mm_kwh" value="${partsEndYm}" min="${partsStartYm}" max="${partsEndYm}"  onchange="load_graph_option_2_3_4(this.value)"></td>
								</tr>			
							</tbody>	
						</table>
					</div>`,
		"option3": `<br>
					<p>
						Es werden die Verbräuche in kWh für den jeweiligen Monat des ausgewählten Jahres angezeigt.
					</p>
					<label for="yyyy_kwh">Jahr auswählen:</label>
					<select id="yyyy_kwh" onchange="load_graph_option_2_3_4(this.value)">
						${optionYyyyKwh}
					</select>`,
		"option4": `<br>
					<p>
						Es werden die Verbräuche der letzten Jahre angezeigt. Aktueller Anzeigzeitraum ${partsStartYear} - ${partsEndYear}
					</p>`,
		"option5": `<br>
					<p>
						Detailierte Tagesansicht zum Bezug und zur Einspeisung für das ausgewählte Datum (siehe Tabelle).
					</p>`,
	};

	function loadDynamicContent() {
		const selectedValue = document.getElementById("drop_Funktion").value;
		if (selectedValue === "") {
			document.getElementById("dynamicContent").innerHTML = "";
			return;
		}
		const content = contentData[selectedValue];
		if (content) {
			if (typeof content === "string") {
				document.getElementById("dynamicContent").innerHTML = content;
			} else if (typeof content === "object") {
				document.getElementById("dynamicContent").innerHTML = content.html;
				if (content.fetchData) {
					content.fetchData();
				}
			}
		}
		const selectedOption = document.getElementById("drop_Funktion").value; // Die ausgewählte Option im Dropdown-Menü
		switch (selectedOption) {
			case "option1":
					get_10070();
					countdown(true);
				break;
			case "option2":
				load_graph_option_2_3_4();
				break;
			case "option3":
				load_graph_option_2_3_4();
				break;
			case "option4":
				load_graph_option_2_3_4();
				break;
			case "option5":
				load_graph_option5();
				break;
		}
	}
			
		

JS AJAX Tabellenfunktionen

Mittels AJAX werden aktuelle Verbrauchs- und Einspeisewerte geladen. Die zugehörigen PHP-Skripte liefern JSON-Daten, die dann in die Tabelle eingefügt werden. Die Zuordnung erfolgt über den Dateinamen.

Tabellendaten OBIS 10.07.00

Diese Daten repräsentieren die aktuelle elektrische Leistung in Watt, die vom Stromzähler alle 60 Sekunden gemessen wird. Sie wird live in der Oberfläche angezeigt und ist Grundlage für die Darstellung im Donut-Diagramm (Option 1).

			//Get 10.07.0 last value
	function get_10070() {
		var xmlhttp = new XMLHttpRequest();
		xmlhttp.onreadystatechange = function() {
			if (this.readyState == 4 && this.status == 200) {
				var ajaxresultsplit = this.responseText.split("|");
				changeCellContent("value_obis10070", ajaxresultsplit[0] + " Watt");
				changeCellContent("time_obis10070", ajaxresultsplit[1]);
				checkTimestamp(ajaxresultsplit[1]);
				// Den Graph nur laden wenn option1 aktiv
				const selectedOption = document.getElementById("drop_Funktion").value; // Die ausgewählte Option im Dropdown-Menü
				if (selectedOption == "option1") {
					load_graph_option1(ajaxresultsplit[0]);
				}	
			}
		};
		xmlhttp.open("GET", "get_10070.php", true);
		xmlhttp.send();			
	}
			
		

Aufbau der Datei get_10070.php

Diese PHP-Datei liest den zuletzt gespeicherten Wert des OBIS-Kennwerts 10.07.0 aus der Datenbanktabelle obis_10070. Die Daten (Leistung in Watt und Zeitstempel) werden als JSON zurückgegeben, sodass sie per AJAX in die Seite eingebunden werden können.

			<?php
	/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben

	$ajaxresult = "";

	// SQL-Abfrage (bereinigter Variablenname!)
	$statement = "SELECT obis_10070, TimeStamp 
	              FROM obis_10070 
	              ORDER by TimeStamp DESC 
				  LIMIT 1";

	$sqlresult = mysqli_query($dbcon, $statement);

	if ($sqlresult && mysqli_num_rows($sqlresult) > 0) {
		$dsatzSQL = mysqli_fetch_assoc($sqlresult);

		$ajaxresult = implode('|', [
			$dsatzSQL["obis_10070"],
			$dsatzSQL["TimeStamp"]
		]);
	}

	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);

	/* Rückgabewert */
	echo $ajaxresult;
?>
			
		

Tabellendaten OBIS 1.8.0 und 2.8.0

Diese Daten zeigen den Stromverbrauch (1.8.0) und die Einspeisung (2.8.0) pro Tag an. In der Tabelle werden der Gesamtzählerstand und der Tageswert angezeigt. Die Datenbasis dient außerdem für die Grafiken (Option 2–5).

			//Get 180 280 by Date
	function get_180_280(ymd_obis_180_280) {
		if (ymd_obis_180_280.length == 0) {
			return;
		} else {
			var xmlhttp = new XMLHttpRequest();
			xmlhttp.onreadystatechange = function() {
				if (this.readyState == 4 && this.status == 200) {
					var ajaxresultsplit = this.responseText.split("|");
					changeCellContent("used_obis180", ajaxresultsplit[0] + " KWh");
					changeCellContent("value_obis180", ajaxresultsplit[1] + " KWh");
					changeCellContent("used_obis280", ajaxresultsplit[2] + " KWh");
					changeCellContent("value_obis280", ajaxresultsplit[3] + " KWh");
				}
			};
		xmlhttp.open("GET", "get_180_280.php?ymd_obis_180_280=" + ymd_obis_180_280, true);
		xmlhttp.send();
	  }
		// Den Graph nur laden wenn option5 aktiv
		const selectedOption = document.getElementById("drop_Funktion").value; // Die ausgewählte Option im Dropdown-Menü
		if (selectedOption == "option5") {
			load_graph_option5();
		}	
	}
			
		

Aufbau der Datei get_180_280.php

Diese PHP-Datei lädt die Tageswerte zu einem bestimmten Datum für die OBIS-Codes 1.8.0 (Verbrauch) und 2.8.0 (Einspeisung). Die Werte werden aus der Tabelle obis_180_280 selektiert und als JSON an JavaScript zurückgegeben, um die Tabelle auf der Webseite zu befüllen.

			<?php
	/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben

	// Eingabewert holen und absichern
	$ymd_obis_180_280 = $_REQUEST["ymd_obis_180_280"] ?? '';

	// Frühzeitiger Abbruch, wenn kein Datum übergeben wurde
	if (empty($ymd_obis_180_280)) {
		echo 'Kein Wert|Kein Wert|Kein Wert|Kein Wert';
		mysqli_close($dbcon);
		exit;
	}

	$ajaxresult = "";

	// SQL-Abfrage (bereinigter Variablenname!)
	$statement = "SELECT used_180, used_280, obis_180, obis_280 
	              FROM obis_180_280 
	              WHERE date = '" . $ymd_obis_180_280 . "' 
	              LIMIT 1";

	$sqlresult = mysqli_query($dbcon, $statement);

	if ($sqlresult && mysqli_num_rows($sqlresult) > 0) {
		$dsatzSQL = mysqli_fetch_assoc($sqlresult);

		$ajaxresult = implode('|', [
			$dsatzSQL["used_180"],
			$dsatzSQL["obis_180"],
			$dsatzSQL["used_280"],
			$dsatzSQL["obis_280"]
		]);
	}

	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);

	/* Rückgabewert */
	echo $ajaxresult;
?>
			
		

Allgemeine Unterstützungfunktionen

Diese JavaScript-Hilfsfunktionen kümmern sich um die Aktualisierung von Zellen, Zeitmessung und Plausibilitätsprüfungen:

  • changeCellContent(...): Aktualisiert dynamisch den Zellinhalt und markiert Änderungen visuell.
  • countdown(...): Zählt die verbleibenden Sekunden bis zur nächsten Datenaktualisierung herunter.
  • checkTimestamp(...): Prüft, ob der letzte Zeitstempel zu alt ist, und zeigt ggf. eine Warnung an.
			//Werte an id der Tabelle übergeben
	function changeCellContent(cellId, newContent) {
		var cell = document.getElementById(cellId);

		// Ändern Sie den Zelleninhalt
		const [intPart, decPart] = newContent.toString().split('.');
		cell.innerHTML = `<strong>${intPart}</strong>${decPart ? ',' + decPart : ''}`;

		// Ändern Sie die Hintergrundfarbe allmählich
		var originalBackgroundColor = getComputedStyle(cell).backgroundColor || "";
		cell.style.transition = "background-color 1s ease"; // Übergangseffekt für die Farbänderung
		cell.style.backgroundColor = "#ff9680";

		// Setzen Sie nach einer Verzögerung die Hintergrundfarbe zurück
		setTimeout(function() {
			cell.style.backgroundColor = originalBackgroundColor;
			cell.style.transition = ""; // Zurücksetzen der Übergangseigenschaft
		}, 1000); // 1000 Millisekunden = 1 Sekunde
	}

	
//Interval zum Laden der Werte für Option 1
	let countdownInterval;
	let timeLeft = 60;

	function countdown(reset = false) {
		if (reset) {
			clearInterval(countdownInterval);
			timeLeft = 60;
		}
		if (!countdownInterval || reset) {
			countdownInterval = setInterval(() => {
				document.getElementById("sek_to_update_10070").innerText = timeLeft;
				timeLeft--;
				if (timeLeft < 0) {
					timeLeft = 60; // automatisch wieder von vorn
					get_10070(); // Nach ablauf der Zeit die aktuellen Werte für Tabelle abrufen
				}
			}, 1000);
		}
	}
	
//Check Timestamp
    function checkTimestamp(timestampString) {
        // Timestamp in ISO-Format umwandeln
        const timestamp = new Date(timestampString.replace(" ", "T"));
        const now = new Date();

        const diffMs = now - timestamp; // Differenz in Millisekunden
        const maxDiffMs = 5 * 1000 * 60;

        if (diffMs > maxDiffMs) {
            Swal.fire({
                icon: 'error',
                title: 'Zeitüberschreitung',
                text: `Der angegebene Timestamp liegt mehr als ${maxDiffMs / 1000} Sekunden in der Vergangenheit.`,
            });
        }
    }
			
		

JS AJAX Graphfunktionen

Diese Funktionen erzeugen die verschiedenen Diagrammtypen (Donut, Balken, Linien) auf Basis dynamisch geladener Daten aus der Datenbank. Die Daten werden per AJAX von zugehörigen PHP-Dateien im JSON-Format bezogen. Die jeweilige Option (1–5) bestimmt dabei den Diagrammtyp und die Quelle der Auswertung.

Option 1: Donut

Zeigt die aktuelle Leistung (Verbrauch oder Einspeisung) als Donut-Diagramm. Die Farbgebung signalisiert den Zustand:

  • Grün: Einspeisung (negativer Wert)
  • Gelb oder Rot: Stromverbrauch
Die Werte werden alle 60 Sekunden aktualisiert.

			//Graph Funktionen
	function load_graph_option1(obis_10070_value) {
		obis_10070_value = Number(obis_10070_value);
		
		if (window.myChart) {
			window.myChart.destroy();
		}
		//Daten für Graph erzeugen
		let graph_labels = [];
		let graph_colors = [];
		let graph_values = [];
		let hidden_values = [10000, 1000];
		if (obis_10070_value >= 0){
			if (obis_10070_value <= hidden_values[0]/2){
				graph_values[0] = obis_10070_value;
				graph_values[1] = hidden_values[0] - obis_10070_value;
				graph_labels = [obis_10070_value + " Watt Bezug"];
				graph_colors = ["yellow", "#FFFFFF"];					
			}else{
				graph_values[0] = hidden_values[0]/2;
				graph_values[1] = obis_10070_value - hidden_values[0]/2;
				graph_values[2] = hidden_values[0]/2 - graph_values[1];
				graph_labels = ["",obis_10070_value + " Watt Bezug",""];
				graph_colors = ["yellow", "red", "#FFFFFF"];	
			}
		}else{
			graph_values[0] = obis_10070_value * -1;
			graph_values[1] = hidden_values[1] + obis_10070_value;
			graph_labels = [obis_10070_value + " Watt Einspeisung"];
			graph_colors = ["green", "#FFFFFF"];
		}
		//Graph erzeugen		
		var ctx = document.getElementById("Option_Graph").getContext("2d");
		window.myChart = new Chart(ctx, {
			type: "doughnut",
			data: {
				labels: graph_labels,
				datasets: [{
					data: graph_values, // Beispielwerte
					backgroundColor: graph_colors,
					borderWidth: 0
				}]
			},
			options: {
				rotation: -Math.PI,       // Start links
				circumference: Math.PI,   // nur oberer Halbkreis
				cutoutPercentage: 50,     // Donut-Loch (2.x Syntax)
				responsive: true,
				maintainAspectRatio: true, // Höhe kontrollieren
				legend: {position: "top"},
				tooltips: {enabled: false},
				hover: {mode: null},
				animation: {duration: 3000}
			}
		});
	}
			
		

Option 2, 3 und 4: Balkendiagramme

Diese Optionen zeigen historische Verbrauchs- und Einspeisewerte als Balkendiagramm:

  • Option 2: Tageswerte eines Monats
  • Option 3: Monatswerte eines Jahres
  • Option 4: Jahresübersicht über mehrere Jahre
Die Daten werden aggregiert über zugehörige PHP-Dateien geladen.

			//Graph Funktionen	
	function load_graph_option_2_3_4(date_for_query = "") {
		// Alten Graph löschen
		if (window.myChart) {
			window.myChart.destroy();
		}
		// Wenn leer oder undefined, aktuelles Datum nehmen
		let now = new Date();
		let year = now.getFullYear();
		let month = String(now.getMonth() + 1).padStart(2, "0");
		const selectedOption = document.getElementById("drop_Funktion").value; // Die ausgewählte Option im Dropdown-Menü
		let ajaxurl = "";
		
		if (selectedOption == "option2") {
			ajaxurl = "get_180_280_days.php";
			if (!date_for_query) {
				date_for_query = `${year}-${month}`;
			}			
			
		}else if (selectedOption == "option3"){
			ajaxurl = "get_180_280_months.php";
			if (!date_for_query) {
				date_for_query = `${year}`;
			}
		}else if (selectedOption == "option4"){
			ajaxurl = "get_180_280_years.php";
		}
		// AJAX-Anfrage
		$.ajax({
			url: ajaxurl,
			type: "GET",
			dataType: "json",
			data: {
				setdate_for_query: date_for_query
			},		
			success: function(data) {
				// Zugriff auf die Arrays
				let graph_data_used280 = data.arr_used280;
				let graph_data_used180 = data.arr_used180;
				let graph_labels = data.arr_date;

				// Neuen Graph erstellen – JETZT SIND DIE DATEN DA
				var ctx = document.getElementById("Option_Graph").getContext("2d");

				window.myChart = new Chart(ctx, {
					type: "bar",
					data: {
						labels: graph_labels,
						datasets: [
							{
								label: "Einspeisung in KWH",
								data: graph_data_used280,
								backgroundColor: "green",
								stack: "Stack 0"
							},
							{
								label: "Bezug in KWH",
								data: graph_data_used180,
								backgroundColor: "blue",
								stack: "Stack 0"
							}
						]
					},
					options: {
						scales: {
							x: {
								stacked: true
							},
							y: {
								stacked: true
							}
						}
					}
				});
			},
			error: function(xhr, status, error) {
				console.error("Fehler bei der AJAX-Anfrage:", error);
			}
		});
	}
			
		

Aufbau der Datei get_180_280_days.php

Diese Datei berechnet die Tagesverbräuche und Einspeisungen eines ausgewählten Monats. Sie führt eine SQL-Abfrage durch, summiert die Werte je Tag und gibt das Ergebnis als JSON zurück. Basis für Option 2 (Tagesdiagramm).

			<?php
	/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben
	$date_for_query = $_GET['setdate_for_query'];
	$statement = "SELECT used_180, used_280, date
	              FROM obis_180_280  
				  WHERE date like '$date_for_query%' 
				  ORDER by date ASC";
	$sqlresult = mysqli_query($dbcon, $statement);
	
	// Arrays vorbereiten
	$arr_used180 = []; // Für used_180
	$arr_used280 = []; // Für used_280
	$arr_date = []; // Für date
	// Ergebnis durchlaufen und Werte in Arrays speichern
	while ($row = mysqli_fetch_assoc($sqlresult)) {
		$arr_used180[] = $row['used_180'];
		$arr_used280[] = "-".$row['used_280'];
		$dateObj = new DateTime($row['date']);
		$arr_date[] = $dateObj->format('d');
	}	
	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);
	
	// Die Arrays in ein assoziatives Array packen
	$data = [
		'arr_used180' => $arr_used180,
		'arr_used280' => $arr_used280,
		'arr_date' => $arr_date
	];

	// Setze den Content-Type auf JSON
	header('Content-Type: application/json');

	// Sende die Daten als JSON zurück
	echo json_encode($data);
?>
			
		

Aufbau der Datei get_180_280_months.php

Diese PHP-Datei aggregiert die Monatsdaten eines ausgewählten Jahres. Sie summiert Verbrauch und Einspeisung je Monat und liefert die Daten als JSON für die Balkendiagramm-Darstellung in Option 3.

			<?php
	/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben
	$date_for_query = $_GET['setdate_for_query'];
	$statement = "
		SELECT 
			MONTH(`date`) AS monat,
			SUM(used_180) AS sum_used_180,
			SUM(used_280) AS sum_used_280
		FROM 
			obis_180_280
		WHERE 
			YEAR(`date`) = $date_for_query
		GROUP BY 
			MONTH(`date`)
		ORDER BY 
			monat
	";
	$sqlresult = mysqli_query($dbcon, $statement);
	
	// Arrays vorbereiten
	$arr_used180 = []; // Für used_180
	$arr_used280 = []; // Für used_280
	$arr_date = []; // Für date
	$monatsnamen = array( 1=>"Jan", 2=>"Feb", 3=>"Mrz", 4=>"Apr", 5=>"Mai", 6=>"Jun", 7=>"Jul", 8=>"Aug", 9=>"Sep", 10=>"Okt", 11=>"Nov", 12=>"Dez");
	// Ergebnis durchlaufen und Werte in Arrays speichern
	if ($sqlresult) {
		while ($row = mysqli_fetch_assoc($sqlresult)) {
			$arr_date[] = $monatsnamen[(int)$row['monat']];
			$arr_used180[] = (float)$row['sum_used_180'];
			$arr_used280[] = (float)$row['sum_used_280']*-1;
		}
		mysqli_free_result($sqlresult);
	}
	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);
	
	// Die Arrays in ein assoziatives Array packen
	$data = [
		'arr_used180' => $arr_used180,
		'arr_used280' => $arr_used280,
		'arr_date' => $arr_date
	];

	// Setze den Content-Type auf JSON
	header('Content-Type: application/json');

	// Sende die Daten als JSON zurück
	echo json_encode($data);
?>
			
		

Aufbau der Datei get_180_280_years.php

Diese Datei stellt Jahresverbräuche über mehrere Jahre hinweg zusammen. Die Daten werden pro Jahr aus der Datenbank geladen, aufsummiert und per JSON für Option 4 bereitgestellt.

			<?php
	/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben
	$date_for_query = $_GET['setdate_for_query'];
	$statement = "
		SELECT 
			YEAR(`date`) AS year,
			SUM(used_180) AS sum_used_180,
			SUM(used_280) AS sum_used_280
		FROM 
			obis_180_280
		GROUP BY 
			YEAR(`date`)
		ORDER BY 
			YEAR
	";
	$sqlresult = mysqli_query($dbcon, $statement);
	
	// Arrays vorbereiten
	$arr_used180 = []; // Für used_180
	$arr_used280 = []; // Für used_280
	$arr_date = []; // Für date
	// Ergebnis durchlaufen und Werte in Arrays speichern
	if ($sqlresult) {
		while ($row = mysqli_fetch_assoc($sqlresult)) {
			$arr_date[] = (int)$row['year'];
			$arr_used180[] = (float)$row['sum_used_180'];
			$arr_used280[] = (float)$row['sum_used_280']*-1;
		}
		mysqli_free_result($sqlresult);
	}
	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);
	
	// Die Arrays in ein assoziatives Array packen
	$data = [
		'arr_used180' => $arr_used180,
		'arr_used280' => $arr_used280,
		'arr_date' => $arr_date
	];

	// Setze den Content-Type auf JSON
	header('Content-Type: application/json');

	// Sende die Daten als JSON zurück
	echo json_encode($data);
?>
			
		

Option 5: Liniendiagramm

Diese Option visualisiert die zeitliche Entwicklung von Verbrauch und Einspeisung innerhalb eines einzelnen Tages. Die Messwerte werden in kurzen Abständen (z. B. 5 Minuten) aus der Datenbank geladen und als Linienverlauf angezeigt.

			//Graph Funktionen
	function load_graph_option5() {
		// Alten Graph löschen
		if (window.myChart) {
			window.myChart.destroy();
		}
		// Datum aus Datumsauswahl beziehen
		let date_for_query = document.getElementById("input_date_180_280").value;

		// AJAX-Anfrage
		$.ajax({
			url: "ajax/sh_get_10070_date.php",
			type: "GET",
			dataType: "json",
			data: {
				setdate_for_query: date_for_query
			},
			success: function(data) {
				let graph_data_v10070 = data.arr_v10070;
				let graph_labels = data.arr_date;

				var ctx = document.getElementById("Option_Graph").getContext("2d");
				window.myChart = new Chart(ctx, {
					type: 'line',
					data: {
						labels: graph_labels,
						datasets: [{
							label: 'Energie in kwh',
							data: graph_data_v10070,
							borderColor: 'blue',   // nur eine Farbe
							borderWidth: 1,
							pointRadius: 0,
							spanGaps: false,
							fill: false,
						}]
					},
					options: {
						animation: false,
						responsive: true,
						elements: {
							line: {
								tension: 0
							}
						},
						tooltips: {
							enabled: false
						},
						hover: {
							mode: null
						},
						scales: {
							xAxes: [{
								display: true,
								gridLines: {
									display: false
								},
								ticks: {
									autoSkip: false,
									maxTicksLimit: 25
								}
							}],
							yAxes: [{
								display: true
							}]
						}
					}
				});
			},
			error: function(xhr, status, error) {
				console.error("Fehler bei der AJAX-Anfrage:", error);
			}
		});
	}
			
		

Aufbau der Datei get_10070_date.php

Diese Datei liefert alle historischen Leistungsdaten (OBIS 10.07.0) eines bestimmten Tages. Die Werte werden in regelmäßigen Abständen (z. B. jede Minute) ausgelesen und für die Tagesauswertung (Option 5) als JSON an das Liniendiagramm übergeben.

			<?php
	/* Datenbankverbindung */
	include 'dbcon.php'; // Siehe SQL Verbindung oben
	
	$date_for_query = $_GET['setdate_for_query'];
	$statement = "SELECT obis_10070, TimeStamp 
	              FROM obis_10070 
				  WHERE TimeStamp like '$date_for_query%' 
	              ORDER by TimeStamp";
	$sqlresult = mysqli_query($dbcon, $statement);
	
	// Arrays vorbereiten
	$arr_v10070 = []; // Für Tageswerte aus obis 10070
	$arr_date = []; // Für date
	// Ergebnis durchlaufen und Werte in Arrays speichern
	while ($row = mysqli_fetch_assoc($sqlresult)) {
		$arr_v10070[] = $row['obis_10070'];
		$dateObj = new DateTime($row['TimeStamp']);
		$arr_date[] = $dateObj->format('H:i');
	}	
	/* Verbindung zur SQL-DB trennen */
	mysqli_close($dbcon);
	
	
	// 1. Schritt: Werte auf nächste volle Stunde aufrunden (außer erster Wert bleibt unverändert)
	for ($i = 0; $i < count($arr_date); $i++) {
		// Erster Wert bleibt so, wie er ist
		if ($i == 0) continue;

		if ($arr_date[$i] === "") continue;

		list($h, $m) = explode(":", $arr_date[$i]);
		$h = (int)$h;
		$m = (int)$m;

		if ($m > 0) {
			$h++;
			if ($h == 24) $h = 0;
		}

		$arr_date[$i] = str_pad($h, 2, "0", STR_PAD_LEFT) . ":00";
	}
	// 2. Schritt: Nur letzten Eintrag pro voller Stunde behalten (ausgenommen erster und letzter Wert im Array)
	$last_per_hour = [];

	// Alle Stunden mit dem letzten Index merken (inklusive erster und letzter Werte, die wir später schützen)
	for ($i = 0; $i < count($arr_date); $i++) {
		if ($arr_date[$i] !== "") {
			$last_per_hour[$arr_date[$i]] = $i;
		}
	}

	// 3. Schritt: Werte entfernen, die nicht letzter Eintrag ihrer vollen Stunde sind, außer erste & letzte Position
	for ($i = 0; $i < count($arr_date); $i++) {
		if ($i == 0 || $i == count($arr_date) - 1) {
			// Erster und letzter Wert bleiben erhalten, egal was
			continue;
		}

		if ($arr_date[$i] !== "") {
			// Wenn dieser Index nicht der letzte für diese Stunde ist → leer machen
			if ($last_per_hour[$arr_date[$i]] !== $i) {
				$arr_date[$i] = "";
			}
		}
	}	
	
	
	// Die Arrays in ein assoziatives Array packen
	$data = [
		'arr_v10070' => $arr_v10070,
		'arr_date' => $arr_date
	];

// Setze den Content-Type auf JSON
header('Content-Type: application/json');

// Sende die Daten als JSON zurück
echo json_encode($data);
?>
			
		

Donation

Euch hat es bei uns gefallen oder die Beiträge haben euch weitergeholfen, dann könnt uns gerne mit einer kleinen Spende für weitere Projekte unterstützen. Vielen Dank

Letzte Änderung: 04.11.2025