From 5daaa84f7ed7edfb89fa8876980faf7fbe4acaed Mon Sep 17 00:00:00 2001 From: "martin.schweitzer" Date: Fri, 3 Apr 2026 16:02:28 +0000 Subject: [PATCH] deploy script: meter_reading_dashboard --- scripts/meter_reading_dashboard.py | 414 +++++++++++++++++++++++++++++ 1 file changed, 414 insertions(+) create mode 100644 scripts/meter_reading_dashboard.py 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} + + + + +
+
+

📊 Messwerte Dashboard

+
Sensor: {sensor_name} | Zählernummer: {METER_NUMBER}
+
Zeitraum: {start_time.strftime('%d.%m.%Y')} - {end_time.strftime('%d.%m.%Y')}
+
+ +
+ """ + + # 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