OBP60v/web.py

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()