From c3614ebab0514f5ecf76e77dfc3479490f2e8b9a Mon Sep 17 00:00:00 2001 From: rxf Date: Mon, 26 Jan 2026 16:38:50 +0100 Subject: [PATCH] erster test mit http --- wetterstation.py | 205 ++++++++++++++--------------------------------- 1 file changed, 58 insertions(+), 147 deletions(-) diff --git a/wetterstation.py b/wetterstation.py index a76c614..737c5e2 100644 --- a/wetterstation.py +++ b/wetterstation.py @@ -1,28 +1,21 @@ #!/usr/bin/env python3 """ -Wetterstation - MQTT Datenempfang und Web-Visualisierung +Wetterstation - HTTP-POST Datenempfang und Web-Visualisierung """ import sqlite3 import json -import threading import os from datetime import datetime, timedelta -from flask import Flask, render_template, jsonify -import paho.mqtt.client as mqtt +from flask import Flask, render_template, jsonify, request from dotenv import load_dotenv # Lade Umgebungsvariablen aus .env Datei load_dotenv() # Konfiguration aus Umgebungsvariablen -MQTT_HOST = os.getenv("MQTT_HOST", "rexfue.de") -MQTT_PORT = int(os.getenv("MQTT_PORT", 1883)) -MQTT_TOPIC = os.getenv("MQTT_TOPIC", "vantage/live") -MQTT_USER = os.getenv("MQTT_USER", "") -MQTT_PASSWORD = os.getenv("MQTT_PASSWORD", "") - DB_FILE = os.getenv("DB_FILE", "wetterdaten.db") +HTTP_PORT = int(os.getenv("HTTP_PORT", 5003)) app = Flask(__name__) @@ -41,19 +34,19 @@ class WetterDB: cursor.execute(''' CREATE TABLE IF NOT EXISTS wetterdaten ( id INTEGER PRIMARY KEY AUTOINCREMENT, - datetime TEXT NOT NULL, - pressure REAL, - wind_gust REAL, - wind_speed REAL, - wind_dir REAL, - rain_rate REAL, - rain REAL, - humidity INTEGER, - temperature REAL + dateTime TEXT NOT NULL, + barometer REAL, + outTemp REAL, + outHumidity INTEGER, + windSpeed REAL, + windDir REAL, + windGust REAL, + rainRate REAL, + rain REAL ) ''') cursor.execute(''' - CREATE INDEX IF NOT EXISTS idx_datetime ON wetterdaten(datetime) + CREATE INDEX IF NOT EXISTS idx_dateTime ON wetterdaten(dateTime) ''') conn.commit() conn.close() @@ -64,23 +57,22 @@ class WetterDB: cursor = conn.cursor() cursor.execute(''' INSERT INTO wetterdaten - (datetime, pressure, wind_gust, wind_speed, wind_dir, - rain_rate, rain, humidity, temperature) + (dateTime, barometer, outTemp, outHumidity, windSpeed, windDir, windGust, rainRate, rain) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( - data['datetime'], - data.get('pressure'), - data.get('wind_gust'), - data.get('wind_speed'), - data.get('wind_dir'), - data.get('rain_rate'), - data.get('rain'), - data.get('humidity'), - data.get('temperature') + data.get('dateTime'), + data.get('barometer'), + data.get('outTemp'), + data.get('outHumidity'), + data.get('windSpeed'), + data.get('windDir'), + data.get('windGust'), + data.get('rainRate'), + data.get('rain') )) conn.commit() conn.close() - print(f"Daten gespeichert: {data['datetime']}") + print(f"Daten gespeichert: {data.get('dateTime')}") def get_data(self, hours=24): """Daten der letzten X Stunden abrufen""" @@ -92,8 +84,8 @@ class WetterDB: cursor.execute(''' SELECT * FROM wetterdaten - WHERE datetime >= ? - ORDER BY datetime ASC + WHERE dateTime >= ? + ORDER BY dateTime ASC ''', (time_threshold,)) rows = cursor.fetchall() @@ -110,10 +102,10 @@ class WetterDB: cursor.execute(''' SELECT - strftime('%Y-%m-%d %H:00:00', datetime) as hour, - SUM(rain_rate) as total_rain + strftime('%Y-%m-%d %H:00:00', dateTime) as hour, + SUM(rainRate) as total_rain FROM wetterdaten - WHERE datetime >= ? + WHERE dateTime >= ? GROUP BY hour ORDER BY hour ASC ''', (time_threshold,)) @@ -128,104 +120,6 @@ class WetterDB: db = WetterDB(DB_FILE) -class MQTTClient: - """MQTT Client für Datenempfang""" - - def __init__(self): - self.client = mqtt.Client() - self.client.username_pw_set(MQTT_USER, MQTT_PASSWORD) - # Stabilere Verbindungen bei Abbrüchen - try: - self.client.reconnect_delay_set(min_delay=1, max_delay=120) - except Exception: - pass - # Optionale Protokollierung (hilfreich beim Debuggen) - try: - self.client.enable_logger() - except Exception: - pass - self.client.on_connect = self.on_connect - self.client.on_message = self.on_message - - @staticmethod - def _sanitize_data(payload: dict) -> dict: - """Payload robust in erwartetes Format wandeln. - - Fehlende `datetime` wird mit aktueller Zeit ergänzt - - Felder in richtige Typen konvertieren - """ - def to_float(x): - try: - return float(x) if x is not None else None - except Exception: - return None - - def to_int(x): - try: - return int(x) if x is not None else None - except Exception: - return None - - # Zeitstempel: wenn vorhanden, in akzeptables Format bringen, sonst jetzt - dt = payload.get('datetime') - if isinstance(dt, (int, float)): - # Epoch -> ISO - dt = datetime.fromtimestamp(dt).strftime('%Y-%m-%d %H:%M:%S') - elif isinstance(dt, str): - # Versuchen, ISO-Varianten in 'YYYY-MM-DD HH:MM:SS' zu überführen - # Entferne ggf. 'T' oder 'Z' - dt_clean = dt.replace('T', ' ').replace('Z', '').strip() - # Falls Millisekunden enthalten, abschneiden - if '.' in dt_clean: - dt_clean = dt_clean.split('.')[0] - # Bei zu kurzem String: fallback auf jetzt - if len(dt_clean) < 16: - dt = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - else: - dt = dt_clean - else: - dt = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - - return { - 'datetime': dt, - 'pressure': to_float(payload.get('pressure')), - 'wind_gust': to_float(payload.get('wind_gust')), - 'wind_speed': to_float(payload.get('wind_speed')), - 'wind_dir': to_float(payload.get('wind_dir')), - 'rain_rate': to_float(payload.get('rain_rate')), - 'rain': to_float(payload.get('rain')), - 'humidity': to_int(payload.get('humidity')), - 'temperature': to_float(payload.get('temperature')), - } - - def on_connect(self, client, userdata, flags, rc): - """Callback bei Verbindung""" - if rc == 0: - print(f"Mit MQTT Broker verbunden: {MQTT_HOST}") - client.subscribe(MQTT_TOPIC) - print(f"Topic abonniert: {MQTT_TOPIC}") - else: - print(f"Verbindungsfehler: {rc}") - - def on_message(self, client, userdata, msg): - """Callback bei empfangener Nachricht""" - try: - raw = json.loads(msg.payload.decode()) - data = self._sanitize_data(raw) - print(f"Empfangen und gespeichert: {data}") - db.save_data(data) - except Exception as e: - print(f"Fehler beim Verarbeiten der Nachricht: {e}") - - def start(self): - """MQTT Client starten""" - try: - self.client.connect(MQTT_HOST, MQTT_PORT, 60) - self.client.loop_start() - print("MQTT Client gestartet") - except Exception as e: - print(f"MQTT Verbindungsfehler: {e}") - - # Flask Routes @app.route('/') def index(): @@ -233,9 +127,34 @@ def index(): return render_template('index.html') +@app.route('/api/data/upload', methods=['POST']) +def upload_data(): + """HTTP-POST Endpoint für Wetterdaten""" + try: + data = request.get_json() + + if not data: + return jsonify({'error': 'Keine Daten empfangen'}), 400 + + # Daten speichern (unverändert) + db.save_data(data) + + return jsonify({ + 'status': 'success', + 'message': 'Daten empfangen und gespeichert' + }), 200 + + except Exception as e: + print(f"Fehler beim Verarbeiten der POST-Anfrage: {e}") + return jsonify({'error': str(e)}), 400 + + + + + @app.route('/api/data/') -def get_data(period): - """API Endpoint für Wetterdaten""" +def get_historical_data(period): + """API Endpoint für historische Wetterdaten""" hours = 24 if period == 'day' else 168 # 168h = 1 Woche data = db.get_data(hours) rain_data = db.get_hourly_rain(hours) @@ -246,21 +165,13 @@ def get_data(period): }) - - - def main(): """Hauptprogramm""" print("Wetterstation wird gestartet...") - - # MQTT Client starten - mqtt_client = MQTTClient() - mqtt_client.start() - - # Flask Server starten print("\nWeb-Interface verfügbar unter: http://localhost:5003") + print("HTTP-POST Endpoint: http://localhost:5003/api/data/upload") print("Drücke CTRL+C zum Beenden\n") - app.run(host='0.0.0.0', port=5003, debug=False) + app.run(host='0.0.0.0', port=HTTP_PORT, debug=False) if __name__ == '__main__':