From bc29d29a5fad5f3289cedb5b6f66d0091eecb961 Mon Sep 17 00:00:00 2001
From: Christian Elberfeld <elberfeld@web.de>
Date: Sun, 8 Jan 2023 22:07:35 +0100
Subject: [PATCH] Removed MQTT dependency, implemented http Update

---
 Dockerfile                  |   1 -
 docker-compose.yml          |   2 +
 mqtt/publish_zoneclosed.bat |   1 -
 mqtt/publish_zoneopen.bat   |   1 -
 mqtt/subscribe.bat          |   2 -
 warpapi/warpapi.py          | 107 ++++++++++++++++++++++++------------
 6 files changed, 75 insertions(+), 39 deletions(-)
 delete mode 100644 mqtt/publish_zoneclosed.bat
 delete mode 100644 mqtt/publish_zoneopen.bat
 delete mode 100644 mqtt/subscribe.bat

diff --git a/Dockerfile b/Dockerfile
index 5e38869..074c48c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,6 @@ RUN pip3 install --upgrade pip
 # pip Packages
 RUN pip3 install \     
     flask==1.0.2 \     
-    flask_mqtt \
     gunicorn \
     --upgrade 
     
diff --git a/docker-compose.yml b/docker-compose.yml
index b69f2af..874e262 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,3 +9,5 @@ services:
       - 0.0.0.0:5000:5000
     volumes:
       - ./warpapi/:/opt/warpapi
+    environment:
+      - UPDATE_KEY=somesecretkey
diff --git a/mqtt/publish_zoneclosed.bat b/mqtt/publish_zoneclosed.bat
deleted file mode 100644
index 47ad7d3..0000000
--- a/mqtt/publish_zoneclosed.bat
+++ /dev/null
@@ -1 +0,0 @@
- docker run --rm -it ruimarinho/mosquitto mosquitto_pub -h 192.168.0.201 -t warpzone/door/status -m "CLOSED" --retain
diff --git a/mqtt/publish_zoneopen.bat b/mqtt/publish_zoneopen.bat
deleted file mode 100644
index b2be594..0000000
--- a/mqtt/publish_zoneopen.bat
+++ /dev/null
@@ -1 +0,0 @@
- docker run --rm -it ruimarinho/mosquitto mosquitto_pub -h 192.168.0.201 -t warpzone/door/status -m "OPEN" --retain
diff --git a/mqtt/subscribe.bat b/mqtt/subscribe.bat
deleted file mode 100644
index a07b68e..0000000
--- a/mqtt/subscribe.bat
+++ /dev/null
@@ -1,2 +0,0 @@
- docker run --rm -it ruimarinho/mosquitto mosquitto_sub -h 192.168.0.201 -t warpzone/door/status
-  
\ No newline at end of file
diff --git a/warpapi/warpapi.py b/warpapi/warpapi.py
index 4209936..abb17b6 100644
--- a/warpapi/warpapi.py
+++ b/warpapi/warpapi.py
@@ -1,11 +1,27 @@
+from datetime import datetime
 from flask import Flask
 from flask import request
 from flask import redirect
 from flask import render_template
 from flask import send_from_directory
 from flask import json
-from flask_mqtt import Mqtt
 from logging.config import dictConfig
+import os
+
+# Erlaubte Werte für zone_door_status 
+zone_door_status_values = ['OPEN', 'CLOSED', 'UNKNOWN']
+
+# Aktueller Status
+zone_door_status = 'UNKNOWN'
+
+# Letztes Update des Status 
+zone_door_lastupdate = datetime.min
+
+# Maximales ALter (in Minuten) für den Status 
+zone_door_maxage = 15
+
+# Update Key für Updates 
+setstatus_update_key = os.getenv("UPDATE_KEY", "development_key")
 
 dictConfig({
     'version': 1,
@@ -24,35 +40,25 @@ dictConfig({
 })
 
 app = Flask(__name__, static_url_path='')
-app.config['MQTT_BROKER_URL'] = '192.168.0.201'  
-app.config['MQTT_BROKER_PORT'] = 1883 
-app.config['MQTT_USERNAME'] = ''  
-app.config['MQTT_PASSWORD'] = ''  
-app.config['MQTT_KEEPALIVE'] = 5  
-app.config['MQTT_TLS_ENABLED'] = False  
-zone_door_status = "UNKNOWN"
-mqtt = Mqtt(app)
 
 if __name__ == "__main__":
     app.run()
 
-@mqtt.on_connect()
-def handle_connect(client, userdata, flags, rc):
-    app.logger.info("mqtt connected")
-    mqtt.subscribe("warpzone/door/status")
-    app.logger.info("mqtt subscribed: warpzone/door/status")
+# Effektiver Status 
+# Wenn der letzte Status zu alt ist wird UNKNOWN zurückgegeben 
+def effective_zone_door_status(app):
+    global zone_door_status   
+    global zone_door_lastupdate
 
-@mqtt.on_message()
-def handle_mqtt_message(client, userdata, message):
-    global zone_door_status
-    data = dict(
-        topic=message.topic,
-        payload=message.payload.decode()
-    )
-    app.logger.info("mqtt message")
-    app.logger.info(data)
-    zone_door_status = message.payload.decode()
-    app.logger.info("zone_door_status = " + zone_door_status)
+    zone_door_alter = (datetime.now() - zone_door_lastupdate).total_seconds() / 60 
+    zone_door_alter_str = str(round(zone_door_alter,2))
+
+    app.logger.info("Current Zone Status = " + zone_door_status + " / Age = " + zone_door_alter_str + " Minutes / MaxAge = " + str(zone_door_maxage))
+
+    if zone_door_alter > zone_door_maxage:
+      return 'UNKNOWN'
+    else:
+      return zone_door_status  
 
 # CORS Header setzen     
 @app.after_request 
@@ -64,29 +70,63 @@ def after_request(response):
 # Startseite
 @app.route('/')
 def view_index():
-    global zone_door_status   
-    return 'Warpzone API, current status = ' + zone_door_status + ', see: https://gitlab.warpzone.ms/infrastruktur/warpapi/blob/master/README.md'
+    global zone_door_lastupdate
+    zone_door_status = effective_zone_door_status(app)
+    zone_door_alter = (datetime.now() - zone_door_lastupdate).total_seconds() / 60 
+    zone_door_alter_str = str(round(zone_door_alter,2))
+    return 'Warpzone API, Status = ' + zone_door_status + ', letztes Update vor ' + zone_door_alter_str + ' Minuten, see: https://gitlab.warpzone.ms/infrastruktur/warpapi/blob/master/README.md'
 
 # Statische Dateien 
 @app.route('/files/<path:path>')
-def send_file(path):
+def file(path):
     return send_from_directory('files', path)
 
+# Statische Dateien 
+@app.route('/setstatus')
+def setstatus():
+    global zone_door_status
+    global zone_door_status_values
+    global zone_door_lastupdate
+    global setstatus_update_key
+
+    if not 'newstatus' in request.args: 
+      return "Error: Parameter 'newstatus' is missing", 500
+
+    if not 'update_key' in request.args: 
+      return "Error: Parameter 'update_key' is missing", 500
+
+    newstatus = request.args['newstatus']
+    update_key = request.args['update_key']
+
+    app.logger.info("Update Request, newstatus = " + newstatus + " / update_key = " + update_key)
+
+    if newstatus not in zone_door_status_values:
+      app.logger.error("Update Request, newstatus is invalid")  
+      return "Error: Parameter 'newstatus' is invalid", 500
+
+    if update_key != setstatus_update_key:
+      app.logger.error("Update Request, update_key is invalid, expected = " + setstatus_update_key)  
+      return "Error: 'update_key' is invalid", 500 
+
+    app.logger.info("Update Request is valid, Update Zone Status to " + newstatus)
+
+    zone_door_status = newstatus
+    zone_door_lastupdate = datetime.now();
+    return "OK, Status is set to: " + zone_door_status
+
 # Statusanzeige auf der Webseite 
 @app.route('/static')
 def view_static():
-    global zone_door_status   
     spaceopen = False
-    if zone_door_status == "OPEN":
+    if effective_zone_door_status(app) == "OPEN":
       spaceopen = True
     return render_template('static.html', open = spaceopen)
 
 # Statusanzeige auf der Webseite 
 @app.route('/status')
 def data_status():
-    global zone_door_status   
     spaceopen = 0
-    if zone_door_status == "OPEN":
+    if effective_zone_door_status(app) == "OPEN":
       spaceopen = 1
     data = {
         "tuerOffen": spaceopen,
@@ -116,9 +156,8 @@ def data_spaceapiphp():
 # Implementierung der SpaceAPI
 @app.route('/spaceapi')
 def data_spaceapi():
-    global zone_door_status   
     spaceopen = False
-    if zone_door_status == "OPEN":
+    if effective_zone_door_status(app) == "OPEN":
       spaceopen = True
     data = {
         "api": "0.13",
-- 
GitLab