""" Web Interface Regelmäßiges Abfragen von Daten aus dem Internet - NAVTEX - DWD - Pegelstände - pegelonline - BSH - Wetterinformationen TODO - Datenbanktabelle um feld bshid erweitern um mehrere Pegel gleichzeitig verarbeiten zu können """ import os import http.client import ssl import json import sqlite3 from datetime import datetime, timedelta from gi.repository import GLib class WebInterface(): pegeldata = { 'Amrum': '631P', 'Borkum': '101P', 'Büsum': '505P', 'Cuxhaven': '506P', 'Dagebüll': '635P', 'Dwarsgat': '737P', 'Eider Sperrwerk': '664P', 'Emden': '507P', 'Helgoland': '509A', 'Hörnum, Sylt': '624P', 'Hooksiel': '764B', 'Hooge': '636F', 'Husum': '510P', 'Leer': '806P', 'List, Sylt': '617P', 'Norderney': '111P', 'Papenburg': '814P', 'Scharhörn': '677C', 'Schulau': '714P', 'St. Pauli': '508P', 'Wangerooge': '754P' } def __init__(self, logger, cfg): self.log = logger dbpath = os.path.join(cfg['histpath'], "tidedata.db") try: self.conn = sqlite3.connect(dbpath) self.cur = self.conn.cursor() except: self.log.error(f"Failed to open tide database: {dbpath}") return # Datenbank erstellen wenn nicht vorhanden sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='station'" self.cur.execute(sql) if self.cur.fetchone() == None: sql = ("CREATE TABLE IF NOT EXISTS station (" "uuid TEXT PRIMARY KEY NOT NULL," "name TEXT)" ) self.cur.execute(sql) sql = ("CREATE TABLE IF NOT EXISTS data (" "timestamp TEXT PRIMARY KEY NOT NULL," "astro NUMBER NOT NULL," "forecast NUMBER," "measurement NUMBER)" ) self.cur.execute(sql) self.log.info(f"Created tide database: {dbpath}") self.pegelid = 'Schulau' self.bshid = self.pegeldata[self.pegelid] # Tidendaten (Rahmen) self.tide = { 'station': None, 'area': None, 'MHW': None, 'MNW': None, 'warning': None, 'forecast_ts': None } # In der Konfiguration werden Minuten angegeben #GLib.timeout_add_seconds(cfg['tide_refresh'] * 60, self.on_timer) GLib.timeout_add_seconds(60 * 60, self.on_timer) self.running = True self.log.info(f"Web interface started") def __del__(self): self.conn.close() def on_timer(self): """ Data handling """ self.refresh() self.housekeeping() return True def bsh_get_data(self, local=False): """ Webseite auslesen """ if local: # Für Tests um nicht permanent die Webseite abzufragen with open(f"/tmp/DE__{self.bshid}.json", "r") as fh: content = fh.read() else: ssl_context = ssl.create_default_context() conn = http.client.HTTPSConnection("wasserstand-nordsee.bsh.de", 443, context=ssl_context) url = f"https://wasserstand-nordsee.bsh.de/data/DE__{self.bshid}.json" try: conn.request("GET", url) response = conn.getresponse() if response.status == 200: content = response.read().decode() else: print(f"Error: {response.status}") return [] except http.client.HTTPException as e: self.log.warning(f"HTTP error occurred: {e}") return [] except ssl.SSLError as ssl_error: self.log.warning(f"SSL error occurred: {ssl_error}") return [] finally: conn.close() return content def refresh(self): self.log.info("Data refresh") data = self.bsh_get_data(False) tmpfile = open(f"/tmp/DE__{self.bshid}.json", "w") tmpfile.write(data) tmpfile.close() wvdata = json.loads(data) sql = "INSERT OR REPLACE INTO data (timestamp, astro, forecast, measurement) VALUES (?, ?, ?, ?)" for wv in wvdata["curve_forecast"]["data"]: self.cur.execute(sql, (wv['timestamp'], wv['astro'], wv['curveforecast'], wv['measurement'])) self.conn.commit() self.tide['station'] = wvdata["station_name"] self.tide['area'] = wvdata["area"] self.tide['MHW'] = wvdata["MHW"] # Mittleres Hochwasser self.tide['MNW'] = wvdata["MNW"] # Mittleres Niedrigwasser self.tide['warning'] = wvdata["warning"] self.tide['forecast_ts'] = wvdata["creation_forecast"] # Zeitpunkte und Höhen der beiden nächsten Hoch- und Niedrigwasser # wvdata["hwnwforecast"]["data"]["event"] HW / NW # wvdata["hwnwforecast"]["data"]["timestamp"] # wvdata["hwnwforecast"]["data"]["value"] def housekeeping(self): self.log.info("Housekeeping") def set_pegel(self, pegelid): self.pegelid = pegelid self.bshid = self.pegeldata[self.pegelid] def set_timestamp(self, vorlauf, dauer): """ Manuell setzen, damit verschiedene Aufrufe nacheinander auch auf den selben Zeitstempel zurückgreifen """ self.current_ts = datetime.now().replace(minute=0, second=0, microsecond=0) self.start_ts = self.current_ts - timedelta(hours=vorlauf) self.end_ts = self.current_ts + timedelta(hours=dauer) # TODO # Stationsdaten # Berechnungszeitpunkt # Warnhinweise (z.B. Sturmflut) def get_tide(self): # 12 Stunden Vorhersage ab der aktuelle Stunde # Vorlauf sind 4 Stunden # TODO Aufbereiten, daß nur eine einfache Liste zurückgeliefert wird sql = "select forecast from data where timestamp > ? and timestamp < ?" self.cur.execute(sql, (self.start_ts, self.end_ts)) return [x[0] for x in self.cur.fetchall()] def get_tide_minmax(self): # Liefert ein Tupel mit min, max zurück sql = "select min(forecast), max(forecast) from data where timestamp > ? and timestamp < ?" self.cur.execute(sql, (self.start_ts, self.end_ts)) return self.cur.fetchone()