Compare commits

..

8 Commits

Author SHA1 Message Date
schrom01 0135609919 implemented alert to delete all jobs
#8
2022-08-27 22:06:58 +02:00
schrom01 0ed508f70d styling of scrolling Table 2022-08-27 21:01:50 +02:00
schrom01 ab12bf7b02 implemented Text to show if there are no Jobs.
fixed Bug #11
2022-08-27 20:47:03 +02:00
schrom01 18e78b7783 implemented to cancel all jobs if all jobs are deleted. 2022-08-27 20:02:49 +02:00
schrom01 cb9a3b6830 implemented Time formating
solved #5
2022-08-27 19:41:01 +02:00
schrom01 0264375d3f implemented Buttons to switch all Operation Modes
#7
2022-08-27 19:16:23 +02:00
schrom01 d5a9e4781c Fixed Bug #2 2022-08-27 19:08:50 +02:00
schrom01 279c9e74be implemented Action to switch Mode of all Zones.
Fixed Bug #2
2022-08-27 13:54:23 +02:00
9 changed files with 136 additions and 56 deletions

View File

@ -34,6 +34,20 @@
executeAction('get_zone_info', {{ zone.number }}, '0', refreshZone);
{% endfor %}
}
function buttonDeleteJobById(jobId) {
if(jobId == "all") {
if(confirm('{{ translater.getTranslation("delete all planed and cancel running jobs?")}}')){
document.getElementById("jobListBody").innerHTML = "";
} else {
return
}
} else {
var jobToDelete = document.getElementById("job_" + jobId);
document.getElementById("jobListBody").removeChild(jobToDelete);
}
deleteJobById('delete_job_by_id',jobId);
}
</script>
</head>
@ -46,11 +60,11 @@
<div id="pipeline" class="scrollingtable">
<div>
<div id="jobListTable">
<div>
<table>
<caption>{{ translater.getTranslation("planed irrigationjobs") }}</caption>
<thead>
<thead id="jobListHead">
<tr>
<th><div label="Nr."></div></th>
<th><div label="{{ translater.getTranslation("Zone") }}"></div></th>
@ -64,13 +78,16 @@
</tbody>
</table>
</div>
Text unter Table
<p id="text_no_jobs">{{ translater.getTranslation("currently there are no planned jobs.")}}</p>
<button id="button_delete_all_jobs" onclick="buttonDeleteJobById('all')">{{ translater.getTranslation("delete and cancel all jobs")}}</button>
</div>
</div>
<h3>{{ translater.getTranslation("irrigation zones") }}</h3>
<button onclick="switchZoneMode(false, 'all')">{{ translater.getTranslation("switch all to")}} {{ translater.getTranslation("manual mode") }}</button>
<button onclick="switchZoneMode(true, 'all')">{{ translater.getTranslation("switch all to")}} {{ translater.getTranslation("automatic mode") }}</button>
<div id="zones">
{% for zone in zoneManager.zones %}
<div id="zone_{{ zone.number }}" class="zone">

View File

@ -2,6 +2,6 @@
<!-- <td>{{ job.id }}</td> -->
<td>{{ job.zone.number|string}}</td>
<td>{{job.zone.name }}</td>
<td>{{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}}</td>
<td>{{ ((job.duration/60))|string + " " + translater.getTranslation("minutes")}}</td>
<td><button onclick="buttonDeleteJobById('{{ job.id }}')">{{ translater.getTranslation("delete") }}</button></td>
</tr>

View File

@ -36,7 +36,7 @@
<td></td>
<td></td>
<td>
{{translater.getTranslation("irragation is planed for") }} <p id="planed_duration_zone{{ zone.number }}" class="planed_duration_value"></p> {{ translater.getTranslation("minutes") + "." }}
{{translater.getTranslation("irrigation is planed.") }}<br>{{ translater.getTranslation("planed duration")}}: <p id="planed_duration_zone{{ zone.number }}" class="planed_duration_value"></p> {{ translater.getTranslation("minutes") + "." }}
</td>
<td>
<button onclick="deleteJobsForZone('delete_jobs_for_zone','{{ zone.number }}')">{{ translater.getTranslation("delete") }}</button>

View File

@ -1,4 +1,6 @@
from enum import Enum
from time import strftime
from time import gmtime
class Language(Enum):
ENGLISH = 1
GERMAN = 2
@ -27,11 +29,15 @@ class Translater:
"switch to automatic mode": "Auf Automatikbetrieb umstellen",
"minutes": "Minuten",
"until": "bis",
"irragation is planed for": "Bewässerung ist geplant für",
"irrigation is planed.": "Bewässerung ist geplant.",
"planed irrigationjobs": "geplante Bewässerungsaufträge",
"planed duration": "geplante Dauer",
"cancel": "abbrechen",
"delete": "löschen",
"delete and cancel all jobs": "alle Aufträge löschen und abbrechen",
"switch all to": "stelle alle um auf",
"currently there are no planned jobs.": "momentan sind keine Aufträge geplant.",
"delete all planed and cancel running jobs?": "Sollen alle geplanten Aufträge gelöscht und laufende abgerochen werden?",
}
@ -43,3 +49,14 @@ class Translater:
return self.dict_german[english_String]
case _:
return "no translations for these language"
def formatTime(self, timeInt):
format = ""
match self.language:
case Language.ENGLISH:
format = "%I.%S %p"
case Language.GERMAN:
format = "%H:%M:%S"
case _:
format = ""
return strftime(format, gmtime(timeInt))

View File

@ -34,7 +34,6 @@ class Webserver:
@app.route('/action', methods=['GET', 'POST'])
def executeAction(command=False, index_str=False, value_str=False):
sucess = False
if(request.method == 'POST'):
try:
command = request.form['command']
@ -69,39 +68,52 @@ class Webserver:
case "switch_zone_on":
if (index and value):
self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=value)
sucess = True
return "True"
elif(index):
self.zoneManager.switchZoneIndexState(zoneIndex=index, state=True, duration=self.zoneManager.systemSettings.defaultManualIrrigationDuration)
sucess = True
return "True"
case "switch_zone_off":
if (index and value):
self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=value, instant=True)
sucess = True
return "True"
elif(index):
self.zoneManager.switchZoneIndexState(zoneIndex=index, state=False, duration=self.zoneManager.systemSettings.defaultManualOffDuration, instant=True)
sucess = True
return "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
zonesToSwitch = []
if(index_str == "all" and value_str):
zonesToSwitch = self.zoneManager.zones
elif (index and value_str):
zonesToSwitch.append(self.zoneManager.getZone(index))
else:
return "False"
automode = False
if(value_str == "automatic"):
automode = True
elif(value_str == "manual"):
automode = False
else:
return "False"
for zone in zonesToSwitch:
zone.switchMode(autoMode=automode)
return "True"
case "set_desired_humidity":
if (index and value):
zone = self.zoneManager.getZone(index)
zone.desiredHumidity = value
sucess = True
return "True"
case "delete_jobs_for_zone":
if (index):
zone = self.zoneManager.getZone(index)
self.zoneManager.deleteIrrigationJobsForZone(zone)
return "True"
case "delete_job_by_id":
if(index_str == "all"):
self.zoneManager.clearIrrigationJobs()
return "True"
if (index):
self.zoneManager.deleteIrrigationJobByID(index)
return "True"
case "get_dashboard_zone_html":
if (index):
zone = self.zoneManager.getZone(index)
@ -116,7 +128,7 @@ class Webserver:
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)
return "False"
@app.route('/dashboard')

View File

@ -4,22 +4,25 @@
vertical-align: middle;
overflow: hidden;
width: auto; /*set table width here if using fixed value*/
/*min-width: 100%;*/ /*set table width here if using %*/
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*/
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*/
line-height: 25px;
padding-top: 25px; /*this determines top caption height*/
padding-bottom: 25px; /*this determines bottom caption height*/
text-align: left;
}
#button_delete_all_jobs{
margin-top: 25px;
}
.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*/
padding-top: 25px; /*this determines column header height*/
}
.scrollingtable > div:before {
top: 0;
@ -36,7 +39,7 @@
}
.scrollingtable > div > div {
/*min-height: 43px;*/ /*if using % height, make this at least large enough to fit scrollbar arrows*/
max-height: 100%;
max-height: 200px;
overflow: scroll; /*set to auto if using fixed or % width; else scroll*/
overflow-x: hidden;
border: 1px solid black; /*border around table body*/
@ -45,22 +48,22 @@
.scrollingtable > div > div > table {
width: 100%;
border-spacing: 0;
margin-top: -20px; /*inverse of column header height*/
margin-top: -25px; /*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*/
top: -25px; /*inverse of caption height*/
margin-top: -1px; /*inverse of border-width*/
width: 100%;
font-weight: bold;
text-align: center;
text-align: left;
}
.scrollingtable > div > div > table > * > tr > * {padding: 0;}
.scrollingtable > div > div > table > thead {
vertical-align: bottom;
white-space: nowrap;
text-align: center;
text-align: left;
}
.scrollingtable > div > div > table > thead > tr > * > div {
display: inline-block;
@ -71,7 +74,7 @@
position: absolute;
top: 0;
left: 0;
height: 20px; /*match column header height*/
height: 25px; /*match column header height*/
border-left: 1px solid black; /*leftmost header border*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
@ -87,7 +90,7 @@
.scrollingtable > div > div > table > thead > tr > * + :before {
content: "";
display: block;
min-height: 20px; /*match column header height*/
min-height: 25px; /*match column header height*/
padding-top: 1px;
border-left: 1px solid black; /*borders between header cells*/
}
@ -111,7 +114,7 @@
.scrollingtable > div > div > table > tbody > tr > * {
border-bottom: 1px solid black;
padding: 0 6px 0 6px;
height: 20px; /*match column header height*/
height: 25px; /*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*/

View File

@ -4,7 +4,7 @@ function refreshZone(zone_json) {
document.getElementById("inner_icon_state_zone_" + zone.number).className = 'inner_icon ' + (zone.state ? 'dot_green' : 'dot_red');
document.getElementById("state_text_zone_" + zone.number).innerHTML = zone.state_text;
document.getElementById("end_time_row_zone_" + zone.number).style.display = ((zone.setState == 1 || zone.setState == 2) ? 'table-row' : 'none');
document.getElementById("end_time_zone_" + zone.number).innerHTML = zone.endTimeSetState;
document.getElementById("end_time_zone_" + zone.number).innerHTML = zone.endTimeSetStateFormated;
document.getElementById("planed_duration_row_zone_" + zone.number).style.display = ((zone.planedDuration > 0) ? 'table-row' : 'none');
document.getElementById("planed_duration_zone" + zone.number).innerHTML = zone.planedDuration/60;
document.getElementById("inner_icon_mode_zone_" + zone.number).innerHTML = zone.autoMode ? 'A' : 'M';
@ -15,13 +15,18 @@ function refreshZone(zone_json) {
document.getElementById("desired_humidity_zone_" + zone.number).innerHTML = zone.desiredHumidity;
}
function buttonDeleteJobById(jobId) {
deleteJobById('delete_job_by_id',jobId);
var jobToDelete = document.getElementById("job_" + jobId);
document.getElementById("jobListBody").removeChild(jobToDelete);
}
function refreshPipeline(pipeline_html) {
document.getElementById("jobListBody").innerHTML = pipeline_html;
//alert("refreshing Pipeline");
var jobListBody = document.getElementById("jobListBody");
jobListBody.innerHTML = pipeline_html;
if(!jobListBody.childElementCount){
document.getElementById("button_delete_all_jobs").style.display = 'none';
document.getElementById("text_no_jobs").style.display = 'block';
} else {
document.getElementById("button_delete_all_jobs").style.display = 'block';
document.getElementById("text_no_jobs").style.display = 'none';
}
}

17
Zone.py
View File

@ -26,6 +26,7 @@ class Zone:
"state_text": translater.getTranslation("switched on" if self.state else "switched off"),
"setState": self.setState,
"endTimeSetState" : self.endTimeSetState,
"endTimeSetStateFormated" : translater.formatTime(self.endTimeSetState),
"planedDuration": self.planedDuration,
}
@ -45,12 +46,18 @@ class Zone:
self.zoneManager.switchZoneState(zone=self, state=True, duration=self.zoneManager.systemSettings.defaultAutoIrrigationDuration, instant=False)
case 1:
if(self.timeOver()):
if (self.planedDuration > 0):
self.setState = 3
else:
self.setState = 0
self.refreshStateAutomode()
else:
self.state = False
case 2:
if (self.timeOver()):
if (self.planedDuration > 0):
self.setState = 3
else:
self.setState = 0
self.refreshStateAutomode()
else:
@ -70,10 +77,16 @@ class Zone:
self.setState = 3
case 1:
if(self.timeOver()):
if (self.planedDuration > 0):
self.setState = 3
else:
self.setState = 0
self.state = False
case 2:
if (self.timeOver()):
if (self.planedDuration > 0):
self.setState = 3
else:
self.setState = 0
self.state = False
else:
@ -105,7 +118,9 @@ class Zone:
self.setState = 2
self.endTimeSetState = time.time() + duration
else:
if(state):
if(self.setState == 1 or self.setState == 2):
pass
elif(state):
self.setState = 3
self.refreshState()

View File

@ -60,6 +60,12 @@ class ZoneManager:
else:
i = i + 1
def clearIrrigationJobs(self):
with self.piplineMutexLock:
self.pipeLine = []
for zone in self.zones:
self.switchZoneState(zone=zone, state=False, duration=-1, instant=True)
def isAnyZoneBusy(self):
for zone in self.zones:
if(zone.state):
@ -76,7 +82,7 @@ class ZoneManager:
def switchZoneIndexState(self, zoneIndex, state, duration, instant=False):
zone = self.getZone(zoneIndex)
self.switchZoneState(zone, state, duration, instant)
self.switchZoneState(zone=zone, state=state, duration=duration, instant=instant)
def getPlanedDurationForZone(self, zone):
totalDuration = 0
@ -92,12 +98,17 @@ class ZoneManager:
def processPipeline(self):
self.piplineMutexLock.acquire()
if(len(self.pipeLine) > 0 and (not self.isAnyZoneBusy())):
irrigationJob = self.pipeLine[0]
self.pipeLine.pop(0)
index = 0
while(len(self.pipeLine) > index and (not self.isAnyZoneBusy())):
irrigationJob = self.pipeLine[index]
if(not irrigationJob.zone.setState == 1):
self.pipeLine.pop(index)
self.piplineMutexLock.release()
irrigationJob.process()
return
else:
index = index + 1
self.piplineMutexLock.release()