From 2747b79f37b223674ca826df644a65384c592b8a Mon Sep 17 00:00:00 2001 From: schrom01 Date: Tue, 23 Aug 2022 23:47:43 +0200 Subject: [PATCH 1/6] implemented actions get_zone_list and get_pipeline --- Webserver/Templates/header.html | 20 +++--- Webserver/__init__.py | 104 +++++++++++++++++--------------- Webserver/static/js/action.js | 8 +++ Zone.py | 14 +++++ ZoneManager.py | 24 +++++++- 5 files changed, 109 insertions(+), 61 deletions(-) create mode 100644 Webserver/static/js/action.js diff --git a/Webserver/Templates/header.html b/Webserver/Templates/header.html index d1eb6a4..e275f31 100644 --- a/Webserver/Templates/header.html +++ b/Webserver/Templates/header.html @@ -6,19 +6,12 @@ - - + +
@@ -43,6 +36,7 @@
+ diff --git a/Webserver/__init__.py b/Webserver/__init__.py index 417866b..319b6a3 100644 --- a/Webserver/__init__.py +++ b/Webserver/__init__.py @@ -33,57 +33,67 @@ class Webserver: return redirect(url_for('showDashboard')) @app.route('/action', methods=['GET', 'POST']) - @app.route('/action/', methods=['GET', 'POST']) - @app.route('/action//', methods=['GET', 'POST']) - @app.route('/action///', methods=['GET', 'POST']) def executeAction(command=False, index_str=False, value_str=False): sucess = False - try: - index = int(index_str) - value = int(value_str) - except: - pass - match command: - case "switch_zone_on": - if (index and value): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=value) - sucess = True - elif(index): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=self.zoneManager.systemSettings.defaultManualIrrigationDuration) - sucess = True - case "switch_zone_off": - if (index and value): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=value, instant=True) - sucess = True - elif(index): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=self.zoneManager.systemSettings.defaultManualOffDuration, instant=True) - sucess = True - case "switch_zone_mode": - if (index and value_str): - zone = self.zoneManager.getZone(index) - match value_str: - case "automatic": - zone.switchMode(autoMode=True) - sucess = True - case "manual": - zone.switchMode(autoMode=False) - sucess = True - case "set_desired_humidity": - if (index and value): - zone = self.zoneManager.getZone(index) - zone.desiredHumidity = value - sucess = True - case "delete_jobs_for_zone": - if (index): - zone = self.zoneManager.getZone(index) - self.zoneManager.deleteIrrigationJobsForZone(zone) - case "delete_job_by_id": - if (index): - self.zoneManager.deleteIrrigationJobByID(index) - - + if(request.method == 'POST'): + try: + command = request.form['command'] + except: + pass + try: + index_str = request.form['index'] + index = int(index_str) + except: + pass + try: + value_str = request.form['value'] + value = int(value_str) + except: + pass + match command: + case "switch_zone_on": + if (index and value): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=value) + sucess = True + elif(index): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=self.zoneManager.systemSettings.defaultManualIrrigationDuration) + sucess = True + case "switch_zone_off": + if (index and value): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=value, instant=True) + sucess = True + elif(index): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=self.zoneManager.systemSettings.defaultManualOffDuration, instant=True) + sucess = True + case "switch_zone_mode": + if (index and value_str): + zone = self.zoneManager.getZone(index) + match value_str: + case "automatic": + zone.switchMode(autoMode=True) + sucess = True + case "manual": + zone.switchMode(autoMode=False) + sucess = True + case "set_desired_humidity": + if (index and value): + zone = self.zoneManager.getZone(index) + zone.desiredHumidity = value + sucess = True + case "delete_jobs_for_zone": + if (index): + zone = self.zoneManager.getZone(index) + self.zoneManager.deleteIrrigationJobsForZone(zone) + case "delete_job_by_id": + if (index): + self.zoneManager.deleteIrrigationJobByID(index) + case "get_zone_list": + return self.zoneManager.zonesToJSON() + case "get_pipeline": + return self.zoneManager.pipelineToJSON() return render_template('action.html', translater=self.translater, zones=self.zoneManager.zones, sucess=sucess) + @app.route('/dashboard') def showDashboard(): return render_template('dashboard.html', translater=self.translater, zoneManager=self.zoneManager) diff --git a/Webserver/static/js/action.js b/Webserver/static/js/action.js new file mode 100644 index 0000000..e6d8b62 --- /dev/null +++ b/Webserver/static/js/action.js @@ -0,0 +1,8 @@ + +function executeActionByValueID(command, index, valueID, valueFactor){ + var value = document.getElementById(valueID).value * valueFactor; + executeAction(command, index, value); +} +function switchZoneMode(autoMode, zone) { + executeAction('switch_zone_mode' ,zone, (autoMode ? 'automatic' : 'manual')) +} \ No newline at end of file diff --git a/Zone.py b/Zone.py index b86da92..f802cf8 100644 --- a/Zone.py +++ b/Zone.py @@ -1,4 +1,5 @@ import time +import json class Zone: @@ -13,6 +14,19 @@ class Zone: self.endTimeSetState = endTimeSetState self.planedDuration = planedDuration + def toJSON(self): + return { + "number": self.number, + "name": self.name, + "actualHumidity": self.actualHumidity, + "desiredHumidity": self.desiredHumidity, + "autoMode": self.autoMode, + "state": self.state, + "setState": self.setState, + "endTimeSetState" : self.endTimeSetState, + "planedDuration": self.planedDuration, + } + def setZoneManager(self, zoneManager): self.zoneManager = zoneManager diff --git a/ZoneManager.py b/ZoneManager.py index 2ef1d91..119e7cc 100644 --- a/ZoneManager.py +++ b/ZoneManager.py @@ -1,6 +1,7 @@ +import json import threading from random import Random -from time import time + from FileIO import FileIO @@ -15,6 +16,20 @@ class ZoneManager: self.pipeLine = [] self.piplineMutexLock = threading.Lock() + def zonesToJSON(self): + zoneList = [] + for zone in self.zones: + zoneList.append(zone.toJSON()) + return zoneList + + def pipelineToJSON(self): + jobList = [] + with self.piplineMutexLock: + for irrigationJob in self.pipeLine: + jobList.append(irrigationJob.toJSON()) + return jobList + + def getZone(self, number): for zone in self.zones: if(zone.number == number): @@ -100,5 +115,12 @@ class IrrigationJob: self.duration = duration self.zone.switchState(state=True, duration=duration, instant=False) + def toJSON(self): + return { + "id": self.id, + "zone": self.zone.toJSON(), + "duration": self.duration, + } + def process(self): self.zone.switchState(state=True, duration=self.duration, instant=True) \ No newline at end of file From 442a7a8b110a2d1a9713bbcbba64ce06be0103b6 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Wed, 24 Aug 2022 21:20:54 +0200 Subject: [PATCH 2/6] implemented action get_zone_info --- Webserver/Templates/action.html | 23 +++++++++++ Webserver/__init__.py | 4 ++ Webserver/static/js/webhook.js | 67 +++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 Webserver/Templates/action.html create mode 100644 Webserver/static/js/webhook.js diff --git a/Webserver/Templates/action.html b/Webserver/Templates/action.html new file mode 100644 index 0000000..2e5ea85 --- /dev/null +++ b/Webserver/Templates/action.html @@ -0,0 +1,23 @@ + + + + + {{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }} + + + {% include "header.html" %} + +
+ +

Action

+ +
+

+ Sucess: {{ sucess }} +

+
+ +
+ + + \ No newline at end of file diff --git a/Webserver/__init__.py b/Webserver/__init__.py index 319b6a3..e126274 100644 --- a/Webserver/__init__.py +++ b/Webserver/__init__.py @@ -89,6 +89,10 @@ class Webserver: self.zoneManager.deleteIrrigationJobByID(index) case "get_zone_list": return self.zoneManager.zonesToJSON() + case "get_zone_info": + if (index): + zone = self.zoneManager.getZone(index) + return zone.toJSON() case "get_pipeline": return self.zoneManager.pipelineToJSON() return render_template('action.html', translater=self.translater, zones=self.zoneManager.zones, sucess=sucess) diff --git a/Webserver/static/js/webhook.js b/Webserver/static/js/webhook.js new file mode 100644 index 0000000..11b5f58 --- /dev/null +++ b/Webserver/static/js/webhook.js @@ -0,0 +1,67 @@ +function sleep (time) { + + return new Promise((resolve) => setTimeout(resolve, time)); + +} + + + +function send_web_request($url, $messagestring, $varstring) { + // Browserkompatibles Request-Objekt erzeugen: + r = null; + + if(window.XMLHttpRequest) + { + + r = new XMLHttpRequest(); + } + else if(window.ActiveXObject) + { + + try + { + r = new ActiveXObject('Msxml2.XMLHTTP'); + } + catch(e1) + { + try + { + r = new ActiveXObject('Microsoft.XMLHTTP'); + } + catch(e2) + { + alert("Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut."); + } + } + } + + // Wenn Request-Objekt vorhanden, dann Anfrage senden: + if(r != null) + { + + + // HTTP-POST + r.open('POST', $url, true); + + r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + + r.send($varstring); + if($messagestring != 'no') + { + alert($messagestring); + } + sleep(500).then(() => { + + //window.location.href = window.location.href; + //document.location.reload(); + + }); + + } + else + { + alert("Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut."); + } + + +} \ No newline at end of file From 1066adcc637cf4a85109e672b881ba88fbd1d0fd Mon Sep 17 00:00:00 2001 From: schrom01 Date: Thu, 25 Aug 2022 13:24:11 +0200 Subject: [PATCH 3/6] moved Dashboard.html in speparate folder to refresh zones. --- Webserver/Templates/dashboard.html | 161 ----------------------------- Webserver/Templates/header.html | 11 +- Webserver/__init__.py | 9 +- Webserver/static/js/action.js | 2 +- Webserver/static/js/webhook.js | 18 +++- 5 files changed, 29 insertions(+), 172 deletions(-) delete mode 100644 Webserver/Templates/dashboard.html diff --git a/Webserver/Templates/dashboard.html b/Webserver/Templates/dashboard.html deleted file mode 100644 index 502b46c..0000000 --- a/Webserver/Templates/dashboard.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - {{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }} - - - - - - {% include "header.html" %} - - -
- -

{{ translater.getTranslation("Dashboard") }}

- - -

{{ translater.getTranslation("planed irrigationjobs") }}

-
- - - - - - - - - - - - {% for job in zoneManager.pipeLine %} - - - - - - - {% endfor %} - -
ID{{ translater.getTranslation("Zone") }}{{ translater.getTranslation("planed duration") }}
{{ job.id }}{{ job.zone.number|string + ": " + job.zone.name }}{{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}}
-
- - -

{{ translater.getTranslation("irrigation zones") }}

-
- {% for zone in zoneManager.zones %} -
- -

{{ zone.name }}

-
- - - - - - - - - - {% if (zone.setState == 1 or zone.setState == 2) %} - - - - - - - {% endif %} - {% if zone.planedDuration > 0 %} - - - - - - - {% endif %} - - - - - - - - - - - - - - - - - -
- - - - {{ translater.getTranslation("state") }}: - {{ translater.getTranslation("switched on") if zone.state else translater.getTranslation("switched off") }} - - - -
- {{translater.getTranslation("until") + zone.endTimeSetState|string}} - - -
- {{translater.getTranslation("irragation is planed for") + " " + ((zone.planedDuration/60)|int)|string + " " + translater.getTranslation("minutes") + "." }} - - -
- - {{ translater.getTranslation("operating mode") }}:{{translater.getTranslation("automatic mode") if zone.autoMode else translater.getTranslation("manual mode")}} - -
- - - - {{ translater.getTranslation("actual humidity") }}:{{ zone.actualHumidity}}
- - {{ translater.getTranslation("desired humidity") }}:{{ zone.desiredHumidity }}
-

{{ zone.number }}

-
- - {% endfor %} - - - -
- -
-



- - - \ No newline at end of file diff --git a/Webserver/Templates/header.html b/Webserver/Templates/header.html index e275f31..9751ae0 100644 --- a/Webserver/Templates/header.html +++ b/Webserver/Templates/header.html @@ -5,11 +5,16 @@ - + diff --git a/Webserver/__init__.py b/Webserver/__init__.py index e126274..92e15c9 100644 --- a/Webserver/__init__.py +++ b/Webserver/__init__.py @@ -87,6 +87,10 @@ class Webserver: case "delete_job_by_id": if (index): self.zoneManager.deleteIrrigationJobByID(index) + case "get_dashboard_zone_html": + if (index): + zone = self.zoneManager.getZone(index) + return render_template('dashboard/zone.html', translater=self.translater, zoneManager=self.zoneManager, zone=zone) case "get_zone_list": return self.zoneManager.zonesToJSON() case "get_zone_info": @@ -95,12 +99,13 @@ class Webserver: return zone.toJSON() case "get_pipeline": return self.zoneManager.pipelineToJSON() - return render_template('action.html', translater=self.translater, zones=self.zoneManager.zones, sucess=sucess) + return self.zoneManager.zonesToJSON() + #return render_template('action.html', translater=self.translater, zones=self.zoneManager.zones, sucess=sucess) @app.route('/dashboard') def showDashboard(): - return render_template('dashboard.html', translater=self.translater, zoneManager=self.zoneManager) + return render_template('dashboard/dashboard.html', translater=self.translater, zoneManager=self.zoneManager) @app.route('/zones') @app.route('/zones/') diff --git a/Webserver/static/js/action.js b/Webserver/static/js/action.js index e6d8b62..5ce0379 100644 --- a/Webserver/static/js/action.js +++ b/Webserver/static/js/action.js @@ -5,4 +5,4 @@ function executeActionByValueID(command, index, valueID, valueFactor){ } function switchZoneMode(autoMode, zone) { executeAction('switch_zone_mode' ,zone, (autoMode ? 'automatic' : 'manual')) -} \ No newline at end of file +} diff --git a/Webserver/static/js/webhook.js b/Webserver/static/js/webhook.js index 11b5f58..eebdd88 100644 --- a/Webserver/static/js/webhook.js +++ b/Webserver/static/js/webhook.js @@ -6,7 +6,7 @@ function sleep (time) { -function send_web_request($url, $messagestring, $varstring) { +function send_web_request(url, messageString, varString, elementId) { // Browserkompatibles Request-Objekt erzeugen: r = null; @@ -41,14 +41,22 @@ function send_web_request($url, $messagestring, $varstring) { // HTTP-POST - r.open('POST', $url, true); + r.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + document.getElementById(elementId).innerHTML = this.responseText; + } + }; + + r.open('POST', url, true); r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - r.send($varstring); - if($messagestring != 'no') + r.send(varString); + + + if(messageString != 'no') { - alert($messagestring); + alert(messageString); } sleep(500).then(() => { From ba3afd129dfbcc3db757905ab8998d10f77cc587 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Thu, 25 Aug 2022 15:37:54 +0200 Subject: [PATCH 4/6] implemented autorefresh --- SystemSettings.py | 3 +- Webserver/Templates/dashboard/dashboard.html | 82 +++++++++++++++++ Webserver/Templates/dashboard/pipeline.html | 21 +++++ Webserver/Templates/dashboard/zone.html | 92 ++++++++++++++++++++ Webserver/Templates/header.html | 46 ++++++++++ Webserver/Templates/{ => zones}/zone.html | 0 Webserver/Templates/{ => zones}/zones.html | 0 Webserver/__init__.py | 6 +- Webserver/static/js/action.js | 7 -- main.py | 4 +- 10 files changed, 249 insertions(+), 12 deletions(-) create mode 100644 Webserver/Templates/dashboard/dashboard.html create mode 100644 Webserver/Templates/dashboard/pipeline.html create mode 100644 Webserver/Templates/dashboard/zone.html rename Webserver/Templates/{ => zones}/zone.html (100%) rename Webserver/Templates/{ => zones}/zones.html (100%) diff --git a/SystemSettings.py b/SystemSettings.py index 1891b36..211f132 100644 --- a/SystemSettings.py +++ b/SystemSettings.py @@ -1,7 +1,8 @@ class SystemSettings: - def __init__(self, dataDir="/Data", multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[5, 10, 15, 20, 25, 30, 45, 60]): + def __init__(self, cronJobFrequency=2, dataDir="/Data", multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[5, 10, 15, 20, 25, 30, 45, 60]): + self.cronJobFrequency = cronJobFrequency self.dataDir = dataDir self.multiZoneIrrigation = multiZoneIrrigation self.defaultAutoIrrigationDuration = defaultAutoIrrigationDuration diff --git a/Webserver/Templates/dashboard/dashboard.html b/Webserver/Templates/dashboard/dashboard.html new file mode 100644 index 0000000..18c83ec --- /dev/null +++ b/Webserver/Templates/dashboard/dashboard.html @@ -0,0 +1,82 @@ + + + + + + {{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }} + + + + + + + + {% include "header.html" %} + + +
+ +

{{ translater.getTranslation("Dashboard") }}

+ + +

{{ translater.getTranslation("planed irrigationjobs") }}

+

+ +

+
+ {% include "dashboard/pipeline.html" %} +
+ + +

{{ translater.getTranslation("irrigation zones") }}

+
+ {% for zone in zoneManager.zones %} +
+ {% include "dashboard/zone.html" %} +
+ + {% endfor %} + + + +
+ +
+



+ + + \ No newline at end of file diff --git a/Webserver/Templates/dashboard/pipeline.html b/Webserver/Templates/dashboard/pipeline.html new file mode 100644 index 0000000..8d852c4 --- /dev/null +++ b/Webserver/Templates/dashboard/pipeline.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + {% for job in zoneManager.pipeLine %} + + + + + + + {% endfor %} + +
ID{{ translater.getTranslation("Zone") }}{{ translater.getTranslation("planed duration") }}
{{ job.id }}{{ job.zone.number|string + ": " + job.zone.name }}{{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}}
\ No newline at end of file diff --git a/Webserver/Templates/dashboard/zone.html b/Webserver/Templates/dashboard/zone.html new file mode 100644 index 0000000..14f40cb --- /dev/null +++ b/Webserver/Templates/dashboard/zone.html @@ -0,0 +1,92 @@ + +

{{ zone.name }}

+
+ + + + + + + + + + + {% if (zone.setState == 1 or zone.setState == 2) %} + + + + + + + {% endif %} + {% if zone.planedDuration > 0 %} + + + + + + + {% endif %} + + + + + + + + + + + + + + + + + + +
+ + + + {{ translater.getTranslation("state") }}: + {{ translater.getTranslation("switched on") if zone.state else translater.getTranslation("switched off") }} + + + + + +
+ {{translater.getTranslation("until") + zone.endTimeSetState|string}} + + +
+ {{translater.getTranslation("irragation is planed for") + " " + ((zone.planedDuration/60)|int)|string + " " + translater.getTranslation("minutes") + "." }} + + +
+ +

{{'A' if zone.autoMode else 'M'}}

+
+
+ + {{ translater.getTranslation("operating mode") }}:{{translater.getTranslation("automatic mode") if zone.autoMode else translater.getTranslation("manual mode")}} + +
+ + + + {{ translater.getTranslation("actual humidity") }}:{{ zone.actualHumidity}}
+ + {{ translater.getTranslation("desired humidity") }}:{{ zone.desiredHumidity }}
+

{{ zone.number }}

\ No newline at end of file diff --git a/Webserver/Templates/header.html b/Webserver/Templates/header.html index 9751ae0..ecb074d 100644 --- a/Webserver/Templates/header.html +++ b/Webserver/Templates/header.html @@ -14,6 +14,52 @@ var varString = 'command=' + command + '&index=' + index + '&value=' + value; send_web_request(url, messageString, varString, elementId); } + function executeActionByValueID(command, index, valueID, valueFactor){ + var value = document.getElementById(valueID).value * valueFactor; + executeAction(command, index, value); + } + + + + function switchZoneMode(autoMode, zone) { + executeAction('switch_zone_mode' ,zone, (autoMode ? 'automatic' : 'manual')); + sleep(1000).then(() => { + refreshZone(zone); + refreshPipeline(); + }); + + } + function switchZoneState(command, zone, value) { + executeAction(command, zone, value); + sleep(1000).then(() => { + refreshZone(zone); + refreshPipeline(); + }); + } + function switchZoneStateByValueID(command, zone, valueID, valueFactor) { + executeActionByValueID(command, zone, valueID, valueFactor); + sleep(1000).then(() => { + refreshZone(zone); + refreshPipeline(); + }); + } + function deleteJobsForZone(command, zone) { + executeAction(command, zone); + sleep(1000).then(() => { + refreshZone(zone); + refreshPipeline(); + }); + } + function deleteJobById(command, jobId, zone) { + executeAction(command, jobId); + sleep(1000).then(() => { + refreshZone(zone); + refreshPipeline(); + }); + } + + +setInterval(refreshContent, {{ (zoneManager.systemSettings.cronJobFrequency + 1) * 1000 }}) diff --git a/Webserver/Templates/zone.html b/Webserver/Templates/zones/zone.html similarity index 100% rename from Webserver/Templates/zone.html rename to Webserver/Templates/zones/zone.html diff --git a/Webserver/Templates/zones.html b/Webserver/Templates/zones/zones.html similarity index 100% rename from Webserver/Templates/zones.html rename to Webserver/Templates/zones/zones.html diff --git a/Webserver/__init__.py b/Webserver/__init__.py index 92e15c9..c638980 100644 --- a/Webserver/__init__.py +++ b/Webserver/__init__.py @@ -91,6 +91,8 @@ class Webserver: if (index): zone = self.zoneManager.getZone(index) return render_template('dashboard/zone.html', translater=self.translater, zoneManager=self.zoneManager, zone=zone) + case "get_dashboard_pipeline_html": + return render_template("dashboard/pipeline.html", translater=self.translater, zoneManager=self.zoneManager) case "get_zone_list": return self.zoneManager.zonesToJSON() case "get_zone_info": @@ -111,9 +113,9 @@ class Webserver: @app.route('/zones/') def showZones(zoneNumber=False): if (zoneNumber): - return render_template('zone.html', translater=self.translater, zone=self.zoneManager.getZone(zoneNumber)) + return render_template('zones/zone.html', translater=self.translater, zone=self.zoneManager.getZone(zoneNumber)) else: - return render_template('zones.html', translater=self.translater, zones=self.zoneManager.zones) + return render_template('zones/zones.html', translater=self.translater, zones=self.zoneManager.zones) @app.route('/times') def showTimes(): diff --git a/Webserver/static/js/action.js b/Webserver/static/js/action.js index 5ce0379..8b13789 100644 --- a/Webserver/static/js/action.js +++ b/Webserver/static/js/action.js @@ -1,8 +1 @@ -function executeActionByValueID(command, index, valueID, valueFactor){ - var value = document.getElementById(valueID).value * valueFactor; - executeAction(command, index, value); -} -function switchZoneMode(autoMode, zone) { - executeAction('switch_zone_mode' ,zone, (autoMode ? 'automatic' : 'manual')) -} diff --git a/main.py b/main.py index 8645ade..fce8609 100644 --- a/main.py +++ b/main.py @@ -6,7 +6,7 @@ from ZoneManager import ZoneManager from SystemSettings import SystemSettings from FileIO import FileIO -systemSettings = SystemSettings(dataDir="/Data", multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[1, 5, 10, 15, 20, 25, 30, 45, 60]) +systemSettings = SystemSettings(cronJobFrequency=2, dataDir="/Data", multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[1, 5, 10, 15, 20, 25, 30, 45, 60]) fileIO = FileIO(systemSettings) zoneManager = ZoneManager(systemSettings=systemSettings, fileIO=fileIO) webserver = Webserver(zoneManager=zoneManager, port=80) @@ -15,7 +15,7 @@ def cronJobs(): while True: zoneManager.cronJobs() print("Cronjobs done\nactual Time: " + str(time.time())) - time.sleep(0.5) + time.sleep(systemSettings.cronJobFrequency) if __name__ == "__main__": cronjob_Thread = threading.Thread(target=cronJobs) From b1c371d9b3095e035c0c890f0a6f1cfc1d6218e5 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Sat, 27 Aug 2022 00:29:22 +0200 Subject: [PATCH 5/6] implemented autorefresh --- Webserver/Templates/dashboard/dashboard.html | 91 +++++++---- Webserver/Templates/dashboard/pipeline.html | 28 +--- Webserver/Templates/dashboard/zone.html | 145 ++++++++--------- Webserver/Templates/header.html | 161 +++++++++---------- Webserver/__init__.py | 130 ++++++++------- Webserver/static/Styles/dashboard.css | 6 + Webserver/static/js/webhook.js | 27 ++-- Zone.py | 4 +- ZoneManager.py | 16 +- main.py | 2 +- 10 files changed, 319 insertions(+), 291 deletions(-) diff --git a/Webserver/Templates/dashboard/dashboard.html b/Webserver/Templates/dashboard/dashboard.html index 18c83ec..8047d81 100644 --- a/Webserver/Templates/dashboard/dashboard.html +++ b/Webserver/Templates/dashboard/dashboard.html @@ -1,7 +1,9 @@ +{% include "header.html" %} + {{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }} @@ -24,26 +26,8 @@ - - - {% include "header.html" %}
@@ -51,15 +35,29 @@

{{ translater.getTranslation("Dashboard") }}

-

{{ translater.getTranslation("planed irrigationjobs") }}

-

- -

-
- {% include "dashboard/pipeline.html" %} +
+
+
+ + + + + + + + + + + + + {% include "dashboard/pipeline.html" %} + +
{{ translater.getTranslation("planed irrigationjobs") }}
+
+ Text unter Table +
+

{{ translater.getTranslation("irrigation zones") }}

@@ -79,4 +77,43 @@



- \ No newline at end of file + + +{% include "footer.html" %} \ No newline at end of file diff --git a/Webserver/Templates/dashboard/pipeline.html b/Webserver/Templates/dashboard/pipeline.html index 8d852c4..9492586 100644 --- a/Webserver/Templates/dashboard/pipeline.html +++ b/Webserver/Templates/dashboard/pipeline.html @@ -1,21 +1,9 @@ - - - - - - - - - - - {% for job in zoneManager.pipeLine %} - - - - - - - {% endfor %} - -
ID{{ translater.getTranslation("Zone") }}{{ translater.getTranslation("planed duration") }}
{{ job.id }}{{ job.zone.number|string + ": " + job.zone.name }}{{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}}
\ No newline at end of file +{% for job in zoneManager.pipeLine %} + {% include "dashboard/irrigationJob.html" %} +{% endfor %} + + + + + diff --git a/Webserver/Templates/dashboard/zone.html b/Webserver/Templates/dashboard/zone.html index 14f40cb..6c4c044 100644 --- a/Webserver/Templates/dashboard/zone.html +++ b/Webserver/Templates/dashboard/zone.html @@ -1,92 +1,79 @@ - -

{{ zone.name }}

-
- - - - - - - - - - - {% if (zone.setState == 1 or zone.setState == 2) %} + + +

{{ zone.name }}

+
+
- - - - {{ translater.getTranslation("state") }}: - {{ translater.getTranslation("switched on") if zone.state else translater.getTranslation("switched off") }} - - - - - -
+ - - - + + - {% endif %} - {% if zone.planedDuration > 0 %} - + + + + + + + - {% endif %} - - - - - - - - - - - - - - - - - - -
- {{translater.getTranslation("until") + zone.endTimeSetState|string}} + + + + {{ translater.getTranslation("state") }}: - + + +
- {{translater.getTranslation("irragation is planed for") + " " + ((zone.planedDuration/60)|int)|string + " " + translater.getTranslation("minutes") + "." }} + {{translater.getTranslation("until") }}

+
+ +
+ {{translater.getTranslation("irragation is planed for") }}

{{ translater.getTranslation("minutes") + "." }}
- -

{{'A' if zone.autoMode else 'M'}}

-
-
- - {{ translater.getTranslation("operating mode") }}:{{translater.getTranslation("automatic mode") if zone.autoMode else translater.getTranslation("manual mode")}} - -
- - - - {{ translater.getTranslation("actual humidity") }}:{{ zone.actualHumidity}}
- - {{ translater.getTranslation("desired humidity") }}:{{ zone.desiredHumidity }}
-

{{ zone.number }}

\ No newline at end of file + + + +

+
+ + + + + {{ translater.getTranslation("operating mode") }}: + + + + + + + + + {{ translater.getTranslation("actual humidity") }}: + + + + + + + {{ translater.getTranslation("desired humidity") }}: + + + + +

{{ zone.number }}

\ No newline at end of file diff --git a/Webserver/Templates/header.html b/Webserver/Templates/header.html index ecb074d..5af1b2b 100644 --- a/Webserver/Templates/header.html +++ b/Webserver/Templates/header.html @@ -1,94 +1,91 @@ - - - - - - - - } - function switchZoneState(command, zone, value) { - executeAction(command, zone, value); - sleep(1000).then(() => { - refreshZone(zone); - refreshPipeline(); - }); - } - function switchZoneStateByValueID(command, zone, valueID, valueFactor) { - executeActionByValueID(command, zone, valueID, valueFactor); - sleep(1000).then(() => { - refreshZone(zone); - refreshPipeline(); - }); - } - function deleteJobsForZone(command, zone) { - executeAction(command, zone); - sleep(1000).then(() => { - refreshZone(zone); - refreshPipeline(); - }); - } - function deleteJobById(command, jobId, zone) { - executeAction(command, jobId); - sleep(1000).then(() => { - refreshZone(zone); - refreshPipeline(); - }); - } + + +
+ + + -setInterval(refreshContent, {{ (zoneManager.systemSettings.cronJobFrequency + 1) * 1000 }}) +

{{ translater.getTranslation("Irrigation") }}-
{{ translater.getTranslation("system") }}

+
- - - - -
- - - - - -

{{ translater.getTranslation("Irrigation") }}-
{{ translater.getTranslation("system") }}

-
- - - -
- - - + + +
+ + + diff --git a/Webserver/__init__.py b/Webserver/__init__.py index c638980..bc90e53 100644 --- a/Webserver/__init__.py +++ b/Webserver/__init__.py @@ -50,59 +50,73 @@ class Webserver: value = int(value_str) except: pass - match command: - case "switch_zone_on": - if (index and value): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=value) - sucess = True - elif(index): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=self.zoneManager.systemSettings.defaultManualIrrigationDuration) - sucess = True - case "switch_zone_off": - if (index and value): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=value, instant=True) - sucess = True - elif(index): - self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=self.zoneManager.systemSettings.defaultManualOffDuration, instant=True) - sucess = True - case "switch_zone_mode": - if (index and value_str): - zone = self.zoneManager.getZone(index) - match value_str: - case "automatic": - zone.switchMode(autoMode=True) - sucess = True - case "manual": - zone.switchMode(autoMode=False) - sucess = True - case "set_desired_humidity": - if (index and value): - zone = self.zoneManager.getZone(index) - zone.desiredHumidity = value - sucess = True - case "delete_jobs_for_zone": - if (index): - zone = self.zoneManager.getZone(index) - self.zoneManager.deleteIrrigationJobsForZone(zone) - case "delete_job_by_id": - if (index): - self.zoneManager.deleteIrrigationJobByID(index) - case "get_dashboard_zone_html": - if (index): - zone = self.zoneManager.getZone(index) - return render_template('dashboard/zone.html', translater=self.translater, zoneManager=self.zoneManager, zone=zone) - case "get_dashboard_pipeline_html": - return render_template("dashboard/pipeline.html", translater=self.translater, zoneManager=self.zoneManager) - case "get_zone_list": - return self.zoneManager.zonesToJSON() - case "get_zone_info": - if (index): - zone = self.zoneManager.getZone(index) - return zone.toJSON() - case "get_pipeline": - return self.zoneManager.pipelineToJSON() - return self.zoneManager.zonesToJSON() - #return render_template('action.html', translater=self.translater, zones=self.zoneManager.zones, sucess=sucess) + elif(request.method == 'GET'): + try: + command = request.args['command'] + except: + pass + try: + index_str = request.args['index'] + index = int(index_str) + except: + pass + try: + value_str = request.args['value'] + value = int(value_str) + except: + pass + match command: + case "switch_zone_on": + if (index and value): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=value) + sucess = True + elif(index): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=self.zoneManager.systemSettings.defaultManualIrrigationDuration) + sucess = True + case "switch_zone_off": + if (index and value): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=value, instant=True) + sucess = True + elif(index): + self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=self.zoneManager.systemSettings.defaultManualOffDuration, instant=True) + sucess = True + case "switch_zone_mode": + if (index and value_str): + zone = self.zoneManager.getZone(index) + match value_str: + case "automatic": + zone.switchMode(autoMode=True) + sucess = True + case "manual": + zone.switchMode(autoMode=False) + sucess = True + case "set_desired_humidity": + if (index and value): + zone = self.zoneManager.getZone(index) + zone.desiredHumidity = value + sucess = True + case "delete_jobs_for_zone": + if (index): + zone = self.zoneManager.getZone(index) + self.zoneManager.deleteIrrigationJobsForZone(zone) + case "delete_job_by_id": + if (index): + self.zoneManager.deleteIrrigationJobByID(index) + case "get_dashboard_zone_html": + if (index): + zone = self.zoneManager.getZone(index) + return render_template('dashboard/zone.html', translater=self.translater, zoneManager=self.zoneManager, zone=zone) + case "get_dashboard_pipeline_html": + return render_template("dashboard/pipeline.html", translater=self.translater, zoneManager=self.zoneManager) + case "get_zone_list": + return self.zoneManager.zonesToJSON(self.translater) + case "get_zone_info": + if (index): + zone = self.zoneManager.getZone(index) + return zone.toJSON(self.translater) + case "get_pipeline": + return self.zoneManager.pipelineToJSON(self.translater) + return render_template('action.html', translater=self.translater, zones=self.zoneManager.zones, sucess=sucess) @app.route('/dashboard') @@ -113,16 +127,16 @@ class Webserver: @app.route('/zones/') def showZones(zoneNumber=False): if (zoneNumber): - return render_template('zones/zone.html', translater=self.translater, zone=self.zoneManager.getZone(zoneNumber)) + return render_template('zones/zone.html', translater=self.translater, zoneManager=self.zoneManager, zone=self.zoneManager.getZone(zoneNumber)) else: - return render_template('zones/zones.html', translater=self.translater, zones=self.zoneManager.zones) + return render_template('zones/zones.html', translater=self.translater, zoneManager=self.zoneManager) @app.route('/times') def showTimes(): - return render_template('times.html', translater=self.translater) + return render_template('times.html', translater=self.translater, zoneManager=self.zoneManager) @app.route('/system') def showSystem(): - return render_template('system.html', translater=self.translater) + return render_template('system.html', translater=self.translater, zoneManager=self.zoneManager) - app.run(debug=True, port=self.port) + app.run(debug=True, host="0.0.0.0", port=self.port) diff --git a/Webserver/static/Styles/dashboard.css b/Webserver/static/Styles/dashboard.css index 7fd7357..b8df3be 100644 --- a/Webserver/static/Styles/dashboard.css +++ b/Webserver/static/Styles/dashboard.css @@ -1,3 +1,5 @@ + + #zones{ display: grid; grid-template-columns: repeat(3, 1fr); @@ -57,6 +59,10 @@ td.value{ text-align: left; position: relative; } +p.end_time_value, p.planed_duration_value { + display: inline; +} + diff --git a/Webserver/static/js/webhook.js b/Webserver/static/js/webhook.js index eebdd88..4cc0853 100644 --- a/Webserver/static/js/webhook.js +++ b/Webserver/static/js/webhook.js @@ -1,12 +1,4 @@ -function sleep (time) { - - return new Promise((resolve) => setTimeout(resolve, time)); - -} - - - -function send_web_request(url, messageString, varString, elementId) { +function send_web_request(url, messageString, varString, handleResponse) { // Browserkompatibles Request-Objekt erzeugen: r = null; @@ -40,16 +32,19 @@ function send_web_request(url, messageString, varString, elementId) { { - // HTTP-POST - r.onreadystatechange = function() { - if (this.readyState == 4 && this.status == 200) { - document.getElementById(elementId).innerHTML = this.responseText; - } - }; - + r.open('POST', url, true); r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + + if(handleResponse != null) { + // HTTP-POST + r.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + handleResponse(this.responseText); + } + }; + } r.send(varString); diff --git a/Zone.py b/Zone.py index f802cf8..5694dd8 100644 --- a/Zone.py +++ b/Zone.py @@ -14,14 +14,16 @@ class Zone: self.endTimeSetState = endTimeSetState self.planedDuration = planedDuration - def toJSON(self): + def toJSON(self, translater): return { "number": self.number, "name": self.name, "actualHumidity": self.actualHumidity, "desiredHumidity": self.desiredHumidity, "autoMode": self.autoMode, + "operationMode_text": translater.getTranslation("automatic mode" if self.autoMode else "manual mode"), "state": self.state, + "state_text": translater.getTranslation("switched on" if self.state else "switched off"), "setState": self.setState, "endTimeSetState" : self.endTimeSetState, "planedDuration": self.planedDuration, diff --git a/ZoneManager.py b/ZoneManager.py index 119e7cc..114567a 100644 --- a/ZoneManager.py +++ b/ZoneManager.py @@ -16,17 +16,17 @@ class ZoneManager: self.pipeLine = [] self.piplineMutexLock = threading.Lock() - def zonesToJSON(self): + def zonesToJSON(self, translater): zoneList = [] for zone in self.zones: - zoneList.append(zone.toJSON()) + zoneList.append(zone.toJSON(translater)) return zoneList - def pipelineToJSON(self): + def pipelineToJSON(self, translater): jobList = [] with self.piplineMutexLock: for irrigationJob in self.pipeLine: - jobList.append(irrigationJob.toJSON()) + jobList.append(irrigationJob.toJSON(translater)) return jobList @@ -67,7 +67,9 @@ class ZoneManager: return False def switchZoneState(self, zone, state, duration, instant=False): - if(instant or self.systemSettings.multiZoneIrrigation or state==False ): #or (not self.isAnyZoneBusy()) + if(not duration > 0): + zone.switchState(state=False, duration=0, instant=True) + elif(instant or self.systemSettings.multiZoneIrrigation or state==False ): #or (not self.isAnyZoneBusy()) zone.switchState(state=state, duration=duration, instant=True) else: self.addIrrigationJob(IrrigationJob(id=self.random.randint(a=100000000, b=999999999), zone=zone, duration=duration)) @@ -115,10 +117,10 @@ class IrrigationJob: self.duration = duration self.zone.switchState(state=True, duration=duration, instant=False) - def toJSON(self): + def toJSON(self, translater): return { "id": self.id, - "zone": self.zone.toJSON(), + "zone": self.zone.toJSON(translater), "duration": self.duration, } diff --git a/main.py b/main.py index fce8609..60a44cc 100644 --- a/main.py +++ b/main.py @@ -6,7 +6,7 @@ from ZoneManager import ZoneManager from SystemSettings import SystemSettings from FileIO import FileIO -systemSettings = SystemSettings(cronJobFrequency=2, dataDir="/Data", multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[1, 5, 10, 15, 20, 25, 30, 45, 60]) +systemSettings = SystemSettings(cronJobFrequency=0.5, dataDir="/Data", multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[1, 5, 10, 15, 20, 25, 30, 45, 60]) fileIO = FileIO(systemSettings) zoneManager = ZoneManager(systemSettings=systemSettings, fileIO=fileIO) webserver = Webserver(zoneManager=zoneManager, port=80) From ca294dd17ae7d4968d2f3ee5183620f727c07a5f Mon Sep 17 00:00:00 2001 From: schrom01 Date: Sat, 27 Aug 2022 00:29:46 +0200 Subject: [PATCH 6/6] implemented autorefresh --- .../Templates/dashboard/irrigationJob.html | 7 ++ Webserver/Templates/footer.html | 15 +++ Webserver/static/Styles/scrollingTable.css | 118 ++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 Webserver/Templates/dashboard/irrigationJob.html create mode 100644 Webserver/Templates/footer.html create mode 100644 Webserver/static/Styles/scrollingTable.css diff --git a/Webserver/Templates/dashboard/irrigationJob.html b/Webserver/Templates/dashboard/irrigationJob.html new file mode 100644 index 0000000..d552487 --- /dev/null +++ b/Webserver/Templates/dashboard/irrigationJob.html @@ -0,0 +1,7 @@ + + + {{ job.zone.number|string}} + {{job.zone.name }} + {{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}} + + \ No newline at end of file diff --git a/Webserver/Templates/footer.html b/Webserver/Templates/footer.html new file mode 100644 index 0000000..149a04d --- /dev/null +++ b/Webserver/Templates/footer.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Webserver/static/Styles/scrollingTable.css b/Webserver/static/Styles/scrollingTable.css new file mode 100644 index 0000000..c43fba8 --- /dev/null +++ b/Webserver/static/Styles/scrollingTable.css @@ -0,0 +1,118 @@ +.scrollingtable { + box-sizing: border-box; + display: inline-block; + vertical-align: middle; + overflow: hidden; + width: auto; /*set table width here if using fixed value*/ + /*min-width: 100%;*/ /*set table width here if using %*/ + height: 300px; /*set table height here; can be fixed value or %*/ + /*min-height: 104px;*/ /*if using % height, make this at least large enough to fit scrollbar arrows + captions + thead*/ + font-family: Verdana, Tahoma, sans-serif; + font-size: 15px; + line-height: 20px; + padding-top: 20px; /*this determines top caption height*/ + padding-bottom: 20px; /*this determines bottom caption height*/ + text-align: left; +} +.scrollingtable * {box-sizing: border-box;} +.scrollingtable > div { + position: relative; + border-top: 1px solid black; /*top table border*/ + height: 100%; + padding-top: 20px; /*this determines column header height*/ +} +.scrollingtable > div:before { + top: 0; + background: cornflowerblue; /*column header background color*/ +} +.scrollingtable > div:before, +.scrollingtable > div > div:after { + content: ""; + position: absolute; + z-index: -1; + width: 100%; + height: 50%; + left: 0; +} +.scrollingtable > div > div { + /*min-height: 43px;*/ /*if using % height, make this at least large enough to fit scrollbar arrows*/ + max-height: 100%; + overflow: scroll; /*set to auto if using fixed or % width; else scroll*/ + overflow-x: hidden; + border: 1px solid black; /*border around table body*/ +} +.scrollingtable > div > div:after {background: white;} /*match page background color*/ +.scrollingtable > div > div > table { + width: 100%; + border-spacing: 0; + margin-top: -20px; /*inverse of column header height*/ + /*margin-right: 17px;*/ /*uncomment if using % width*/ +} +.scrollingtable > div > div > table > caption { + position: absolute; + top: -20px; /*inverse of caption height*/ + margin-top: -1px; /*inverse of border-width*/ + width: 100%; + font-weight: bold; + text-align: center; +} +.scrollingtable > div > div > table > * > tr > * {padding: 0;} +.scrollingtable > div > div > table > thead { + vertical-align: bottom; + white-space: nowrap; + text-align: center; +} +.scrollingtable > div > div > table > thead > tr > * > div { + display: inline-block; + padding: 0 6px 0 6px; /*header cell padding*/ +} +.scrollingtable > div > div > table > thead > tr > :first-child:before { + content: ""; + position: absolute; + top: 0; + left: 0; + height: 20px; /*match column header height*/ + border-left: 1px solid black; /*leftmost header border*/ +} +.scrollingtable > div > div > table > thead > tr > * > div[label]:before, +.scrollingtable > div > div > table > thead > tr > * > div > div:first-child, +.scrollingtable > div > div > table > thead > tr > * + :before { + position: absolute; + top: 0; + white-space: pre-wrap; + color: white; /*header row font color*/ +} +.scrollingtable > div > div > table > thead > tr > * > div[label]:before, +.scrollingtable > div > div > table > thead > tr > * > div[label]:after {content: attr(label);} +.scrollingtable > div > div > table > thead > tr > * + :before { + content: ""; + display: block; + min-height: 20px; /*match column header height*/ + padding-top: 1px; + border-left: 1px solid black; /*borders between header cells*/ +} +.scrollingtable .scrollbarhead {float: right;} +.scrollingtable .scrollbarhead:before { + position: absolute; + width: 100px; + top: -1px; /*inverse border-width*/ + background: white; /*match page background color*/ +} +.scrollingtable > div > div > table > tbody > tr:after { + content: ""; + display: table-cell; + position: relative; + padding: 0; + border-top: 1px solid black; + top: -1px; /*inverse of border width*/ +} +.scrollingtable > div > div > table > tbody {vertical-align: top;} +.scrollingtable > div > div > table > tbody > tr {background: white;} +.scrollingtable > div > div > table > tbody > tr > * { + border-bottom: 1px solid black; + padding: 0 6px 0 6px; + height: 20px; /*match column header height*/ +} +.scrollingtable > div > div > table > tbody:last-of-type > tr:last-child > * {border-bottom: none;} +.scrollingtable > div > div > table > tbody > tr:nth-child(even) {background: gainsboro;} /*alternate row color*/ +.scrollingtable > div > div > table > tbody > tr > * + * {border-left: 1px solid black;} /*borders between body cells*/ \ No newline at end of file