197 lines
6.4 KiB
Python
197 lines
6.4 KiB
Python
"""
|
|
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()
|