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/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/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") }}
-
-
-
-
- ID
- {{ translater.getTranslation("Zone") }}
- {{ translater.getTranslation("planed duration") }}
-
-
-
-
-
- {% for job in zoneManager.pipeLine %}
-
- {{ job.id }}
- {{ job.zone.number|string + ": " + job.zone.name }}
- {{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}}
- {{ translater.getTranslation("delete") }}
-
- {% endfor %}
-
-
-
-
-
- {{ translater.getTranslation("irrigation zones") }}
-
- {% for zone in zoneManager.zones %}
-
-
- {% endfor %}
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Webserver/Templates/dashboard/dashboard.html b/Webserver/Templates/dashboard/dashboard.html
new file mode 100644
index 0000000..8047d81
--- /dev/null
+++ b/Webserver/Templates/dashboard/dashboard.html
@@ -0,0 +1,119 @@
+{% include "header.html" %}
+
+
+
+
+
+
+ {{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }}
+
+
+
+
+
+
+
+
+
+ {{ translater.getTranslation("Dashboard") }}
+
+
+
+
+
+
+ {{ translater.getTranslation("irrigation zones") }}
+
+ {% for zone in zoneManager.zones %}
+
+ {% include "dashboard/zone.html" %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+{% include "footer.html" %}
\ No newline at end of file
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")}}
+ {{ translater.getTranslation("delete") }}
+
\ 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..9492586
--- /dev/null
+++ b/Webserver/Templates/dashboard/pipeline.html
@@ -0,0 +1,9 @@
+
+{% for job in zoneManager.pipeLine %}
+ {% include "dashboard/irrigationJob.html" %}
+{% endfor %}
+
+
+
+
+
diff --git a/Webserver/Templates/dashboard/zone.html b/Webserver/Templates/dashboard/zone.html
new file mode 100644
index 0000000..6c4c044
--- /dev/null
+++ b/Webserver/Templates/dashboard/zone.html
@@ -0,0 +1,79 @@
+
+
+ {{ zone.name }}
+
+
+
+
+
+
+
+
+
+ {{ translater.getTranslation("state") }}:
+
+
+
+ {% for option in zoneManager.systemSettings.webDurationOptions %}
+ {{ option }} {{ translater.getTranslation("minutes") }}
+ {% endfor %}
+
+ {{ translater.getTranslation("turn on") }}
+ {{ translater.getTranslation("turn off") }}
+
+
+
+
+
+
+ {{translater.getTranslation("until") }}
+
+
+ {{ translater.getTranslation("cancel") }}
+
+
+
+
+
+
+ {{translater.getTranslation("irragation is planed for") }}
{{ translater.getTranslation("minutes") + "." }}
+
+
+ {{ translater.getTranslation("delete") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 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/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/Templates/header.html b/Webserver/Templates/header.html
index d1eb6a4..5af1b2b 100644
--- a/Webserver/Templates/header.html
+++ b/Webserver/Templates/header.html
@@ -1,49 +1,91 @@
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
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 417866b..bc90e53 100644
--- a/Webserver/__init__.py
+++ b/Webserver/__init__.py
@@ -33,16 +33,38 @@ 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
+ 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
+ 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):
@@ -80,28 +102,41 @@ 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_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')
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/')
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, zoneManager=self.zoneManager, 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, 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/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
diff --git a/Webserver/static/js/action.js b/Webserver/static/js/action.js
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Webserver/static/js/action.js
@@ -0,0 +1 @@
+
diff --git a/Webserver/static/js/webhook.js b/Webserver/static/js/webhook.js
new file mode 100644
index 0000000..4cc0853
--- /dev/null
+++ b/Webserver/static/js/webhook.js
@@ -0,0 +1,70 @@
+function send_web_request(url, messageString, varString, handleResponse) {
+ // 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)
+ {
+
+
+
+ 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);
+
+
+ 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
diff --git a/Zone.py b/Zone.py
index b86da92..5694dd8 100644
--- a/Zone.py
+++ b/Zone.py
@@ -1,4 +1,5 @@
import time
+import json
class Zone:
@@ -13,6 +14,21 @@ class Zone:
self.endTimeSetState = endTimeSetState
self.planedDuration = planedDuration
+ 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,
+ }
+
def setZoneManager(self, zoneManager):
self.zoneManager = zoneManager
diff --git a/ZoneManager.py b/ZoneManager.py
index 2ef1d91..114567a 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, translater):
+ zoneList = []
+ for zone in self.zones:
+ zoneList.append(zone.toJSON(translater))
+ return zoneList
+
+ def pipelineToJSON(self, translater):
+ jobList = []
+ with self.piplineMutexLock:
+ for irrigationJob in self.pipeLine:
+ jobList.append(irrigationJob.toJSON(translater))
+ return jobList
+
+
def getZone(self, number):
for zone in self.zones:
if(zone.number == number):
@@ -52,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))
@@ -100,5 +117,12 @@ class IrrigationJob:
self.duration = duration
self.zone.switchState(state=True, duration=duration, instant=False)
+ def toJSON(self, translater):
+ return {
+ "id": self.id,
+ "zone": self.zone.toJSON(translater),
+ "duration": self.duration,
+ }
+
def process(self):
self.zone.switchState(state=True, duration=self.duration, instant=True)
\ No newline at end of file
diff --git a/main.py b/main.py
index 8645ade..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(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)
@@ -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)