diff --git a/scripts/meter_reading_dashboard.py b/scripts/meter_reading_dashboard.py
new file mode 100644
index 0000000..0a46b0c
--- /dev/null
+++ b/scripts/meter_reading_dashboard.py
@@ -0,0 +1,414 @@
+import json
+import httpx
+from datetime import datetime, timedelta
+import math
+
+# Parameter - diese können später als Eingabe konfiguriert werden
+METER_NUMBER = "Any - Als Parameter" # Wird durch tatsächliche Zählernummer ersetzt
+CHART_TYPE = "line" # line, bar, area
+GROUP_BY = "hour" # hour, day, week
+INCLUDE_STATISTICS = True
+
+# GraphQL Queries
+FIND_SENSORS_QUERY = """
+query FindSensors($meterNumber: String!) {
+ sensorsForMeterNumber(meterNumber: $meterNumber) {
+ sensorId
+ sensorName
+ sensorNameExtern
+ descr
+ measureConcept {
+ id
+ name
+ descr
+ }
+ }
+}
+"""
+
+GET_VARIABLES_QUERY = """
+query GetVariables($sensorId: ID!) {
+ availableVariableUnits(sensorId: $sensorId) {
+ variableUnitId
+ variableName
+ unitName
+ }
+}
+"""
+
+FIND_OBSERVATIONS_QUERY = """
+query FindObservations($measurementConceptId: ID!, $sensorName: String, $startTime: String, $endTime: String) {
+ findObservation(
+ measurementConceptId: $measurementConceptId
+ sensorName: $sensorName
+ startTime: $startTime
+ endTime: $endTime
+ ) {
+ id
+ moment
+ value
+ meterValue
+ observationVariableUnit {
+ observationVariable {
+ name
+ description
+ }
+ unit {
+ name
+ description
+ }
+ }
+ quality
+ }
+}
+"""
+
+def make_graphql_request(query, variables):
+ """GraphQL Request ausführen"""
+ try:
+ with httpx.Client() as client:
+ response = client.post(
+ f"{EXTERNAL_BASE_URL}/graphql",
+ headers=AUTH_HEADERS,
+ json={"query": query, "variables": variables}
+ )
+ response.raise_for_status()
+ return response.json()
+ except Exception as e:
+ return {"errors": [str(e)]}
+
+def calculate_statistics(observations):
+ """Berechnet Statistiken für die Messwerte"""
+ if not observations:
+ return {}
+
+ values = [obs.get('value', 0) for obs in observations]
+ meter_values = [obs.get('meterValue', 0) for obs in observations]
+
+ return {
+ 'total_readings': len(observations),
+ 'value_min': min(values) if values else 0,
+ 'value_max': max(values) if values else 0,
+ 'value_avg': sum(values) / len(values) if values else 0,
+ 'meter_min': min(meter_values) if meter_values else 0,
+ 'meter_max': max(meter_values) if meter_values else 0,
+ 'meter_avg': sum(meter_values) / len(meter_values) if meter_values else 0,
+ 'consumption': max(meter_values) - min(meter_values) if meter_values else 0
+ }
+
+def format_datetime(dt_string):
+ """Formatiert DateTime für Chart.js"""
+ try:
+ dt = datetime.fromisoformat(dt_string.replace('Z', '+00:00'))
+ return dt.strftime('%Y-%m-%d %H:%M:%S')
+ except:
+ return dt_string
+
+# Hauptlogik
+result_html = ""
+
+try:
+ # 1. Sensoren für Zählernummer finden
+ sensors_response = make_graphql_request(FIND_SENSORS_QUERY, {
+ "meterNumber": METER_NUMBER
+ })
+
+ if "errors" in sensors_response:
+ result = f"
Fehler beim Abrufen der Sensoren: {sensors_response['errors']}
"
+ else:
+ sensors = sensors_response.get('data', {}).get('sensorsForMeterNumber', [])
+
+ if not sensors:
+ result = f"Keine Sensoren für Zählernummer '{METER_NUMBER}' gefunden.
"
+ else:
+ sensor = sensors[0] # Ersten Sensor verwenden
+ sensor_id = sensor['sensorId']
+ sensor_name = sensor['sensorName']
+ measure_concept_id = sensor['measureConcept']['id']
+
+ # 2. Verfügbare Variablen abrufen
+ variables_response = make_graphql_request(GET_VARIABLES_QUERY, {
+ "sensorId": sensor_id
+ })
+
+ available_vars = variables_response.get('data', {}).get('availableVariableUnits', [])
+
+ # 3. Zeitraum: Letzten 3 Monate
+ end_time = datetime.now()
+ start_time = end_time - timedelta(days=90)
+
+ # 4. Messwerte abrufen
+ observations_response = make_graphql_request(FIND_OBSERVATIONS_QUERY, {
+ "measurementConceptId": measure_concept_id,
+ "sensorName": sensor_name,
+ "startTime": start_time.isoformat(),
+ "endTime": end_time.isoformat()
+ })
+
+ observations = observations_response.get('data', {}).get('findObservation', [])
+
+ # 5. Statistiken berechnen
+ stats = calculate_statistics(observations)
+
+ # 6. Daten für Chart vorbereiten
+ chart_labels = []
+ chart_values = []
+ chart_meter_values = []
+
+ for obs in observations:
+ chart_labels.append(format_datetime(obs['moment']))
+ chart_values.append(obs.get('value', 0))
+ chart_meter_values.append(obs.get('meterValue', 0))
+
+ # 7. HTML mit Chart.js generieren
+ result_html = f"""
+
+
+
+
+
+ Messwerte Dashboard - {sensor_name}
+
+
+
+
+
+
+
+
+ """
+
+ # Statistiken hinzufügen
+ if INCLUDE_STATISTICS and stats:
+ result_html += f"""
+
+
+
{stats['total_readings']:,}
+
Gesamte Messwerte
+
+
+
{stats['consumption']:,.2f}
+
Verbrauch (Differenz)
+
+
+
{stats['value_avg']:,.2f}
+
Ø Messwert
+
+
+
{stats['meter_max']:,.2f}
+
Max Zählerstand
+
+
+ """
+
+ # Chart hinzufügen
+ result_html += f"""
+
+
Messwerte Verlauf ({CHART_TYPE.title()})
+
+
+
+
+
Zählerstände
+
+
+
+
+
Verfügbare Variablen:
+
+ """
+
+ for var in available_vars:
+ result_html += f"- {var['variableName']} ({var['unitName']})
"
+
+ result_html += f"""
+
+
Anzahl Sensoren gefunden: {len(sensors)}
+
Measure Concept: {sensor['measureConcept']['name']}
+
+
+
+
+
+
+
+ """
+
+ result = result_html
+
+except Exception as e:
+ result = f"Allgemeiner Fehler: {str(e)}
"
+
+print(f"Generated HTML dashboard with {len(chart_labels) if 'chart_labels' in locals() else 0} data points")
\ No newline at end of file