Webserver_Dashboard and basic functionality #1
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
# ---> Python
 | 
			
		||||
# ---> Webserver
 | 
			
		||||
# Byte-compiled / optimized / DLL files
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.py[cod]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
from Zone import Zone
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FileIO:
 | 
			
		||||
    def __init__(self, systemSettings):
 | 
			
		||||
        self.systemSettings = systemSettings
 | 
			
		||||
 | 
			
		||||
    def loadZones(self):
 | 
			
		||||
        zones = []
 | 
			
		||||
        for i in range(12):
 | 
			
		||||
            zones.append(Zone(number=i+1, name="Zone " + str(i+1), actualHumidity=50, desiredHumidity=70, autoMode=True, state=False, setState=0, endTimeSetState=0, planedDuration=0))
 | 
			
		||||
        return zones
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class SystemSettings:
 | 
			
		||||
    def __init__(self, dataDir="/Data",  multiZoneIrrigation=False, defaultAutoIrrigationDuration=10, defaultManualIrrigationDuration=10, defaultManualOffDuration=10, webDurationOptions=[5, 10, 15, 20, 25, 30, 45, 60]):
 | 
			
		||||
        self.dataDir = dataDir
 | 
			
		||||
        self.multiZoneIrrigation = multiZoneIrrigation
 | 
			
		||||
        self.defaultAutoIrrigationDuration = defaultAutoIrrigationDuration
 | 
			
		||||
        self.defaultManualIrrigationDuration = defaultManualIrrigationDuration
 | 
			
		||||
        self.defaultManualOffDuration = defaultManualOffDuration
 | 
			
		||||
        self.webDurationOptions = webDurationOptions
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,161 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
    <link rel="stylesheet" href="{{ url_for('static', filename='Styles/dashboard.css') }}">
 | 
			
		||||
 | 
			
		||||
    <title>{{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }}</title>
 | 
			
		||||
 | 
			
		||||
    <style>
 | 
			
		||||
        @media screen and (min-width: 2051px){
 | 
			
		||||
            #zones{
 | 
			
		||||
                grid-template-rows: repeat( {{ (zoneManager.zones|length / 3) + 1  }} , minmax(150px, auto));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        @media screen and (max-width: 2050px) and (min-width: 1351px){
 | 
			
		||||
            #zones{
 | 
			
		||||
                grid-template-rows: repeat( {{ (zoneManager.zones|length / 2) + 1  }} , minmax(150px, auto));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        @media screen and (max-width: 1350px){
 | 
			
		||||
            #zones{
 | 
			
		||||
                grid-template-rows: repeat( {{ zoneManager.zones|length }} , minmax(150px, auto));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    {% include "header.html" %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
     
 | 
			
		||||
        <h2>{{ translater.getTranslation("Dashboard") }}</h2>
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        <h3>{{ translater.getTranslation("planed irrigationjobs") }}</h3>
 | 
			
		||||
        <div id="pipeline">
 | 
			
		||||
            <table>
 | 
			
		||||
                <thead>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <td>ID</td>
 | 
			
		||||
                        <td>{{ translater.getTranslation("Zone") }}</td>
 | 
			
		||||
                        <td>{{ translater.getTranslation("planed duration") }}</td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    
 | 
			
		||||
                </thead>
 | 
			
		||||
 | 
			
		||||
                <tbody>
 | 
			
		||||
                    {% for job in zoneManager.pipeLine %}
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td>{{ job.id }}</td>
 | 
			
		||||
                            <td>{{ job.zone.number|string + ": " + job.zone.name }}</td>
 | 
			
		||||
                            <td>{{ ((job.duration/60)|int)|string + " " + translater.getTranslation("minutes")}}</td>
 | 
			
		||||
                            <td><button onclick="executeAction('delete_job_by_id','{{ job.id }}', 0)">{{ translater.getTranslation("delete") }}</button></td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <h3>{{ translater.getTranslation("irrigation zones") }}</h3>
 | 
			
		||||
        <div id="zones">
 | 
			
		||||
            {% for zone in zoneManager.zones %}
 | 
			
		||||
                <div id="zone_{{ zone.number }}" class="zone">
 | 
			
		||||
                    <a href="/zones/{{ zone.number}}">
 | 
			
		||||
                        <h4>{{ zone.name }}</h3>
 | 
			
		||||
                    </a>
 | 
			
		||||
                    <table>
 | 
			
		||||
                        <tbody>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td class="icon">
 | 
			
		||||
                                    <span class="outer_dot">
 | 
			
		||||
                                        <span class="inner_icon {{ 'dot_green' if zone.state else 'dot_red' }}"></span>
 | 
			
		||||
                                    </span>
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td class="property">{{ translater.getTranslation("state") }}:</td>
 | 
			
		||||
                                <td class="value">
 | 
			
		||||
                                    {{ translater.getTranslation("switched on") if zone.state else translater.getTranslation("switched off") }}
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <select id="duration_zone_{{ zone.number }}">
 | 
			
		||||
                                        {% for option in zoneManager.systemSettings.webDurationOptions %}
 | 
			
		||||
                                            <option value="{{ option }}">{{ option }} {{ translater.getTranslation("minutes") }}</option>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
                                    </select>
 | 
			
		||||
                                    <button onclick="executeActionByValueID({{'"switch_zone_off"' if zone.state else '"switch_zone_on"'}},'{{ zone.number }}', 'duration_zone_{{ zone.number }}', 60)">{{ translater.getTranslation("turn off") if zone.state else translater.getTranslation("turn on") }}</button>
 | 
			
		||||
                                </td>
 | 
			
		||||
                                
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            {% if (zone.setState == 1 or zone.setState == 2)  %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td></td>
 | 
			
		||||
                                    <td></td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {{translater.getTranslation("until") + zone.endTimeSetState|string}}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <button onclick="executeAction({{'"switch_zone_off"' if zone.state else '"switch_zone_on"'}},'{{ zone.number }}', -1)">{{ translater.getTranslation("cancel") }}</button>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            {% if zone.planedDuration > 0  %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td></td>
 | 
			
		||||
                                    <td></td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {{translater.getTranslation("irragation is planed for") + " " + ((zone.planedDuration/60)|int)|string + " " + translater.getTranslation("minutes") + "." }}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <button onclick="executeAction('delete_jobs_for_zone','{{ zone.number }}', 0)">{{ translater.getTranslation("delete") }}</button>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <label class="toggle">
 | 
			
		||||
                                        <input type="checkbox" {{ "checked" if zone.autoMode }} onchange="switchZoneMode(this.checked, {{ zone.number }})">
 | 
			
		||||
                                        <span class="slider"></span>
 | 
			
		||||
                                        <span class="labels" data-on="A" data-off="H"></span>
 | 
			
		||||
                                    </label>
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td class="property">{{ translater.getTranslation("operating mode") }}:</td>
 | 
			
		||||
                                <td class="value">{{translater.getTranslation("automatic mode") if zone.autoMode else translater.getTranslation("manual mode")}}</td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <button onclick="executeAction('switch_zone_mode','{{ zone.number }}', '{{ 'manual' if zone.autoMode else 'automatic' }}')">{{translater.getTranslation("switch to manual mode") if zone.autoMode else translater.getTranslation("switch to automatic mode")}}</button>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td class="icon">
 | 
			
		||||
                                    <span class="outer_dot">
 | 
			
		||||
                                        <span class="inner_icon {{ 'dot_green' if(zone.actualHumidity >= zone.desiredHumidity) else 'dot_red' }}"></span>
 | 
			
		||||
                                    </span>
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td class="property">{{ translater.getTranslation("actual humidity") }}:</td>
 | 
			
		||||
                                <td class="value">{{ zone.actualHumidity}}</td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td class="icon">
 | 
			
		||||
                                
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td class="property">{{ translater.getTranslation("desired humidity") }}:</td>
 | 
			
		||||
                                <td class="value">{{ zone.desiredHumidity }}</td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        </tbody>
 | 
			
		||||
                    </table>
 | 
			
		||||
                    <p class="zone_number">{{ zone.number }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </main>
 | 
			
		||||
    <p><br><br></p>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
    <link rel="stylesheet" href="{{ url_for('static', filename='Styles/header.css') }}">
 | 
			
		||||
    <link rel="stylesheet" href="{{ url_for('static', filename='Styles/main.css') }}">
 | 
			
		||||
    <link rel="stylesheet" href="{{ url_for('static', filename='Styles/switch.css') }}">
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta http-equiv="refresh" content="5" >
 | 
			
		||||
 | 
			
		||||
    <script>
 | 
			
		||||
        function executeAction(command, index, value) {
 | 
			
		||||
            send_web_request('{{url_for("executeAction")}}/' + command + '/' + index + '/' + value, 'no', 'variable');
 | 
			
		||||
        }
 | 
			
		||||
        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'))
 | 
			
		||||
        }
 | 
			
		||||
    </script>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <header>
 | 
			
		||||
        <nav>
 | 
			
		||||
            <ul>
 | 
			
		||||
                {% for item in nav.top %}
 | 
			
		||||
                    <li {{ 'class=active' if item.is_active else '' }}>
 | 
			
		||||
                        <a href="{{ item.url }}">
 | 
			
		||||
                            {{ item.label }}
 | 
			
		||||
                        </a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </ul>
 | 
			
		||||
        </nav>
 | 
			
		||||
 | 
			
		||||
        <a class="heading" href="{{ url_for('startPage')}}">           
 | 
			
		||||
            <img src="{{ url_for('static', filename='img/header/wassertropfen.png') }}" alt="" class="title-img">
 | 
			
		||||
 | 
			
		||||
            <h1>{{ translater.getTranslation("Irrigation") }}-<br>{{ translater.getTranslation("system") }}</h1>
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
        <img src="{{ url_for('static', filename='img/header/blumenbeet.jpg') }}" alt="" class="header-img">
 | 
			
		||||
        
 | 
			
		||||
    </header>
 | 
			
		||||
    <script src="{{ url_for('static', filename='js/webhook.js') }}"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
 | 
			
		||||
    <title>{{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }}</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    {% include "header.html" %}
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
 | 
			
		||||
        <h2>Systemeinstellungen</h2>
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <title>{{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }}</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    {% include "header.html" %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
 | 
			
		||||
        <h2>Sperrzeiteneinstellung</h2>
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
 | 
			
		||||
    <title>{{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }}</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    {% include "header.html" %}
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
 | 
			
		||||
        <h2>Zone</h2>
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <title>{{ translater.getTranslation("Irrigation") }}{{ translater.getTranslation("system") }}</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    {% include "header.html" %}
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
 | 
			
		||||
        <h2>Zonen</h2>
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
 | 
			
		||||
                Sed excepturi quod dicta temporibus eveniet corporis incidunt molestiae, laborum deleniti. 
 | 
			
		||||
                Molestiae architecto quod veritatis laudantium aliquid aperiam earum quia voluptates saepe!
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
from enum import Enum
 | 
			
		||||
class Language(Enum):
 | 
			
		||||
    ENGLISH = 1
 | 
			
		||||
    GERMAN = 2
 | 
			
		||||
 | 
			
		||||
class Translater:
 | 
			
		||||
    def __init__(self, language):
 | 
			
		||||
        self.language = language
 | 
			
		||||
        self.dict_german = {    "Irrigation": "Bewässerungs",
 | 
			
		||||
                                "system": "system",
 | 
			
		||||
                                "Dashboard": "Startseite",
 | 
			
		||||
                                "irrigation zones": "Bewässerungszonen",
 | 
			
		||||
                                "blocking times": "Sperrzeiten",
 | 
			
		||||
                                "system settings": "Systemeinstellungen",
 | 
			
		||||
                                "Zone": "Zone",
 | 
			
		||||
                                "state": "Status",
 | 
			
		||||
                                "operating mode": "Betriebsmodus",
 | 
			
		||||
                                "actual humidity": "momentane Feuchtigkeit",
 | 
			
		||||
                                "desired humidity": "gewünschte Feuchtigkeit",
 | 
			
		||||
                                "switched on": "eingeschaltet",
 | 
			
		||||
                                "switched off": "ausgeschaltet",
 | 
			
		||||
                                "turn on": "einschalten",
 | 
			
		||||
                                "turn off": "ausschalten",
 | 
			
		||||
                                "manual mode": "Handbetrieb",
 | 
			
		||||
                                "automatic mode": "Automatikbetrieb",
 | 
			
		||||
                                "switch to manual mode": "Auf Handbetrieb umstellen",
 | 
			
		||||
                                "switch to automatic mode": "Auf Automatikbetrieb umstellen",
 | 
			
		||||
                                "minutes": "Minuten",
 | 
			
		||||
                                "until": "bis",
 | 
			
		||||
                                "irragation is planed for": "Bewässerung ist geplant für",
 | 
			
		||||
                                "planed irrigationjobs": "geplante Bewässerungsaufträge",
 | 
			
		||||
                                "planed duration": "geplante Dauer",
 | 
			
		||||
                                "cancel": "abbrechen",
 | 
			
		||||
                                "delete": "löschen",
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def getTranslation(self, english_String):
 | 
			
		||||
        match self.language:
 | 
			
		||||
            case Language.ENGLISH:
 | 
			
		||||
                return english_String
 | 
			
		||||
            case Language.GERMAN:
 | 
			
		||||
                return self.dict_german[english_String]
 | 
			
		||||
            case _:
 | 
			
		||||
                return "no translations for these language"
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
from flask import Flask, render_template, request, redirect, url_for
 | 
			
		||||
from flask_navigation import Navigation
 | 
			
		||||
from Webserver.Translater import Translater, Language
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Webserver:
 | 
			
		||||
    def __init__(self, zoneManager, port=80):
 | 
			
		||||
        self.port = port
 | 
			
		||||
        self.zoneManager = zoneManager
 | 
			
		||||
        self.translater_EN = Translater(Language.ENGLISH)
 | 
			
		||||
        self.translater_DE = Translater(Language.GERMAN)
 | 
			
		||||
        self.translater = self.translater_DE
 | 
			
		||||
 | 
			
		||||
    def startWebserver(self):
 | 
			
		||||
        app = Flask("Bewässerungssystem", template_folder="Webserver/templates", static_folder="Webserver/static")
 | 
			
		||||
        nav = Navigation(app)
 | 
			
		||||
 | 
			
		||||
        nav.Bar('top', [
 | 
			
		||||
            nav.Item(self.translater.getTranslation('Dashboard'), 'showDashboard'),
 | 
			
		||||
            nav.Item(self.translater.getTranslation('irrigation zones'), 'showZones'),
 | 
			
		||||
            nav.Item(self.translater.getTranslation('blocking times'), 'showTimes'),
 | 
			
		||||
            nav.Item(self.translater.getTranslation('system settings'), 'showSystem')
 | 
			
		||||
            # nav.Item('Gfg', 'gfg', {'page': 5}), #(example with pages)
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        # Example Route with pages:
 | 
			
		||||
        # @app.route('/gfg/<int:page>')
 | 
			
		||||
        # def gfg(page):
 | 
			
		||||
        #    return render_template('gfg.html', page=page)
 | 
			
		||||
 | 
			
		||||
        @app.route('/')
 | 
			
		||||
        def startPage():
 | 
			
		||||
            return redirect(url_for('showDashboard'))
 | 
			
		||||
 | 
			
		||||
        @app.route('/action', methods=['GET', 'POST'])
 | 
			
		||||
        @app.route('/action/<command>', methods=['GET', 'POST'])
 | 
			
		||||
        @app.route('/action/<command>/<index_str>', methods=['GET', 'POST'])
 | 
			
		||||
        @app.route('/action/<command>/<index_str>/<value_str>', 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
        @app.route('/zones')
 | 
			
		||||
        @app.route('/zones/<zoneNumber>')
 | 
			
		||||
        def showZones(zoneNumber=False):
 | 
			
		||||
            if (zoneNumber):
 | 
			
		||||
                return render_template('zone.html', translater=self.translater, zone=self.zoneManager.getZone(zoneNumber))
 | 
			
		||||
            else:
 | 
			
		||||
                return render_template('zones.html', translater=self.translater, zones=self.zoneManager.zones)
 | 
			
		||||
 | 
			
		||||
        @app.route('/times')
 | 
			
		||||
        def showTimes():
 | 
			
		||||
            return render_template('times.html', translater=self.translater)
 | 
			
		||||
 | 
			
		||||
        @app.route('/system')
 | 
			
		||||
        def showSystem():
 | 
			
		||||
            return render_template('system.html', translater=self.translater)
 | 
			
		||||
 | 
			
		||||
        app.run(debug=True, port=self.port)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,123 @@
 | 
			
		|||
#zones{
 | 
			
		||||
    display: grid;
 | 
			
		||||
    grid-template-columns: repeat(3, 1fr);
 | 
			
		||||
    /* grid-template-rows: repeat( {{ zones|length  }} , minmax(150px, auto)); moved to content div to use jinja */
 | 
			
		||||
    grid-gap: 30px;
 | 
			
		||||
    margin: 0 auto;
 | 
			
		||||
}
 | 
			
		||||
.zone{
 | 
			
		||||
    position: relative;
 | 
			
		||||
    background: green;
 | 
			
		||||
    color: white;
 | 
			
		||||
    font-size: larger;
 | 
			
		||||
    padding-left: 30px;
 | 
			
		||||
    padding-bottom: 30px;
 | 
			
		||||
    padding-right: 30px;
 | 
			
		||||
}
 | 
			
		||||
.zone a{
 | 
			
		||||
    color: inherit;
 | 
			
		||||
    text-decoration: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.zone:hover{
 | 
			
		||||
    animation: scale 0.5s linear 0.0s normal both;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@keyframes scale{
 | 
			
		||||
    0% {transform: scale3d(100%, 100%, 100%);}
 | 
			
		||||
    100% {transform: scale3d(107%, 120%, 100%);}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.zone h4{
 | 
			
		||||
    padding-top: 25px;
 | 
			
		||||
    padding-bottom: 25px;
 | 
			
		||||
    font-size: x-large;
 | 
			
		||||
}
 | 
			
		||||
.zone_number{
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 15px;
 | 
			
		||||
    right: 30px;;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.zone table{
 | 
			
		||||
    table-layout: auto;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
td.icon{
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    width: 30px;
 | 
			
		||||
}
 | 
			
		||||
td.property{
 | 
			
		||||
    text-align: left;
 | 
			
		||||
}
 | 
			
		||||
td.value{
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.outer_dot {
 | 
			
		||||
    height: 30px;
 | 
			
		||||
    width: 30px;
 | 
			
		||||
    background-color: #bbb;
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
.inner_icon{
 | 
			
		||||
    color: black;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
 | 
			
		||||
    height: 15px;
 | 
			
		||||
    width: 15px;
 | 
			
		||||
    margin-left: 7.5px;
 | 
			
		||||
    margin-top: 7.5px;
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    z-index: 2;
 | 
			
		||||
}
 | 
			
		||||
.dot_green {
 | 
			
		||||
    background-color: green;
 | 
			
		||||
}
 | 
			
		||||
.dot_red {
 | 
			
		||||
    background-color: red;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (min-width: 2051px){
 | 
			
		||||
    .zone:nth-child(even){
 | 
			
		||||
        background: blue;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (max-width: 2050px) and (min-width: 1351px){
 | 
			
		||||
    #zones{
 | 
			
		||||
        grid-template-columns: repeat(2, 1fr);
 | 
			
		||||
    }
 | 
			
		||||
    .zone:nth-child(4n){
 | 
			
		||||
        background: blue;
 | 
			
		||||
    }
 | 
			
		||||
    .zone:nth-child(4n+1){
 | 
			
		||||
        background: blue;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media screen and (max-width: 1350px){
 | 
			
		||||
    #zones{
 | 
			
		||||
        grid-template-columns: repeat(1, 1fr);
 | 
			
		||||
    }
 | 
			
		||||
    .zone{
 | 
			
		||||
        max-width: 600px;
 | 
			
		||||
    }
 | 
			
		||||
    .zone:nth-child(even){
 | 
			
		||||
        background: blue;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
header{
 | 
			
		||||
    font-family: "Century Gothic", CenturyGothic, AppleGothic, sans-serif;
 | 
			
		||||
    padding: 2px;
 | 
			
		||||
    position: sticky;
 | 
			
		||||
    left: 0px;
 | 
			
		||||
    top: 0px;
 | 
			
		||||
    z-index: 20;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav{
 | 
			
		||||
    z-index: 21;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0px;
 | 
			
		||||
    right: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav ul li{
 | 
			
		||||
    display: inline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav ul li a{
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    color: white;
 | 
			
		||||
    background-color: blue;
 | 
			
		||||
    padding: 5px;
 | 
			
		||||
    margin: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav ul li a:hover{
 | 
			
		||||
    background-color: darkblue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.active{
 | 
			
		||||
    font-size : 1.2em;
 | 
			
		||||
    font-weight : bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.heading{
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    color: white;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 10%;
 | 
			
		||||
    top: 2px;
 | 
			
		||||
    background-color: darkblue;
 | 
			
		||||
    padding-top: 0px;
 | 
			
		||||
    padding-bottom: 0px;
 | 
			
		||||
    margin: 0px;  
 | 
			
		||||
    text-decoration: none; 
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
.heading h1{
 | 
			
		||||
    font-size: 32;
 | 
			
		||||
    line-height: 2em;
 | 
			
		||||
    text-transform: uppercase;
 | 
			
		||||
    margin-top: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.title-img{
 | 
			
		||||
    width: 186px;
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header-img{
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 300px;
 | 
			
		||||
    object-fit: cover; 
 | 
			
		||||
    object-position: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
main{
 | 
			
		||||
    max-width: 90%;
 | 
			
		||||
    width: 1800px;
 | 
			
		||||
    margin-left: auto;
 | 
			
		||||
    margin-right: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h2{
 | 
			
		||||
    font-family: "Century Gothic", CenturyGothic, AppleGothic, sans-serif;
 | 
			
		||||
    text-transform: uppercase;
 | 
			
		||||
    margin-bottom: 2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h3{
 | 
			
		||||
    font-family: "Century Gothic", CenturyGothic, AppleGothic, sans-serif;
 | 
			
		||||
    text-transform: uppercase;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
    .toggle {
 | 
			
		||||
      --width: 60px;
 | 
			
		||||
      --height: 25px;  /*calc(var(--width) / 3); */
 | 
			
		||||
 | 
			
		||||
      position: relative;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      width: var(--width);
 | 
			
		||||
      height: var(--height);
 | 
			
		||||
      box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3);
 | 
			
		||||
      border-radius: var(--height);
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle input {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle .slider {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 0;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      border-radius: var(--height);
 | 
			
		||||
      background-color: #ccc;
 | 
			
		||||
      transition: all 0.4s ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle .slider::before {
 | 
			
		||||
      content: '';
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 0;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      width: calc(var(--height));
 | 
			
		||||
      height: calc(var(--height));
 | 
			
		||||
      border-radius: calc(var(--height) / 2);
 | 
			
		||||
      background-color: #fff;
 | 
			
		||||
      box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3);
 | 
			
		||||
      transition: all 0.4s ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle input:checked+.slider {
 | 
			
		||||
      background-color: #2196F3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle input:checked+.slider::before {
 | 
			
		||||
      transform: translateX(calc(var(--width) - var(--height)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle .labels {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 4px;
 | 
			
		||||
      left: 10px;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      font-size: 16px;
 | 
			
		||||
      font-family: sans-serif;
 | 
			
		||||
      transition: all 0.4s ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle .labels::after {
 | 
			
		||||
      content: attr(data-off);
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      right: 25px;
 | 
			
		||||
      color: #4d4d4d;
 | 
			
		||||
      opacity: 1;
 | 
			
		||||
      text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4);
 | 
			
		||||
      transition: all 0.4s ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle .labels::before {
 | 
			
		||||
      content: attr(data-on);
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      left: 5px;
 | 
			
		||||
      color: #ffffff;
 | 
			
		||||
      opacity: 0;
 | 
			
		||||
      text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.4);
 | 
			
		||||
      transition: all 0.4s ease-in-out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle input:checked~.labels::after {
 | 
			
		||||
      opacity: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .toggle input:checked~.labels::before {
 | 
			
		||||
      opacity: 1;
 | 
			
		||||
    }
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 858 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 572 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,96 @@
 | 
			
		|||
import time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Zone:
 | 
			
		||||
    def __init__(self, number=0, name="", actualHumidity="100", desiredHumidity="0", autoMode=False, state=False, setState=0, endTimeSetState=0, planedDuration=0):
 | 
			
		||||
        self.number = number
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.actualHumidity = actualHumidity
 | 
			
		||||
        self.desiredHumidity = desiredHumidity
 | 
			
		||||
        self.autoMode = autoMode #False = manual, True = automatic
 | 
			
		||||
        self.state = state #True = on, False = off
 | 
			
		||||
        self.setState = setState #0=default, 1=turned off for time, 2=turned on for time, 3=command in pipeline
 | 
			
		||||
        self.endTimeSetState = endTimeSetState
 | 
			
		||||
        self.planedDuration = planedDuration
 | 
			
		||||
 | 
			
		||||
    def setZoneManager(self, zoneManager):
 | 
			
		||||
        self.zoneManager = zoneManager
 | 
			
		||||
 | 
			
		||||
    def timeOver(self):
 | 
			
		||||
        return time.time() > self.endTimeSetState
 | 
			
		||||
 | 
			
		||||
    def refreshStateAutomode(self):
 | 
			
		||||
        match self.setState:
 | 
			
		||||
            case 0:
 | 
			
		||||
                self.state = False
 | 
			
		||||
                if(self.planedDuration > 0):
 | 
			
		||||
                    self.setState = 3
 | 
			
		||||
                elif(self.desiredHumidity > self.actualHumidity):
 | 
			
		||||
                    self.zoneManager.switchZoneState(zone=self, state=True, duration=self.zoneManager.systemSettings.defaultAutoIrrigationDuration, instant=False)
 | 
			
		||||
            case 1:
 | 
			
		||||
                if(self.timeOver()):
 | 
			
		||||
                    self.setState = 0
 | 
			
		||||
                    self.refreshStateAutomode()
 | 
			
		||||
                else:
 | 
			
		||||
                    self.state = False
 | 
			
		||||
            case 2:
 | 
			
		||||
                if (self.timeOver()):
 | 
			
		||||
                    self.setState = 0
 | 
			
		||||
                    self.refreshStateAutomode()
 | 
			
		||||
                else:
 | 
			
		||||
                    self.state = True
 | 
			
		||||
            case 3:
 | 
			
		||||
                self.state = False
 | 
			
		||||
                if (not self.planedDuration > 0):
 | 
			
		||||
                    self.setState = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def refreshStateManualmode(self):
 | 
			
		||||
        if(self.setState > 0):
 | 
			
		||||
            match self.setState:
 | 
			
		||||
                case 0:
 | 
			
		||||
                    self.state = False
 | 
			
		||||
                    if (self.planedDuration > 0):
 | 
			
		||||
                        self.setState = 3
 | 
			
		||||
                case 1:
 | 
			
		||||
                    if(self.timeOver()):
 | 
			
		||||
                        self.setState = 0
 | 
			
		||||
                    self.state = False
 | 
			
		||||
                case 2:
 | 
			
		||||
                    if (self.timeOver()):
 | 
			
		||||
                        self.setState = 0
 | 
			
		||||
                        self.state = False
 | 
			
		||||
                    else:
 | 
			
		||||
                        self.state = True
 | 
			
		||||
                case 3:
 | 
			
		||||
                    self.state = False
 | 
			
		||||
                    if (not self.planedDuration > 0):
 | 
			
		||||
                        self.setState = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def refreshState(self):
 | 
			
		||||
        self.planedDuration = self.zoneManager.getPlanedDurationForZone(zone=self)
 | 
			
		||||
        if(self.autoMode):
 | 
			
		||||
            self.refreshStateAutomode()
 | 
			
		||||
        else:
 | 
			
		||||
            self.refreshStateManualmode()
 | 
			
		||||
 | 
			
		||||
    def switchMode(self, autoMode):
 | 
			
		||||
        if(self.autoMode != autoMode):
 | 
			
		||||
            self.autoMode = autoMode
 | 
			
		||||
            self.state = False
 | 
			
		||||
            self.refreshState()
 | 
			
		||||
 | 
			
		||||
    def switchState(self, state, duration, instant):
 | 
			
		||||
        if(instant):
 | 
			
		||||
            if(not state):
 | 
			
		||||
                self.setState = 1
 | 
			
		||||
            else:
 | 
			
		||||
                self.setState = 2
 | 
			
		||||
            self.endTimeSetState = time.time() + duration
 | 
			
		||||
        else:
 | 
			
		||||
            if(state):
 | 
			
		||||
                self.setState = 3
 | 
			
		||||
        self.refreshState()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
import threading
 | 
			
		||||
from random import Random
 | 
			
		||||
from time import time
 | 
			
		||||
 | 
			
		||||
from FileIO import FileIO
 | 
			
		||||
 | 
			
		||||
class ZoneManager:
 | 
			
		||||
    def __init__(self, systemSettings, fileIO):
 | 
			
		||||
        self.random = Random()
 | 
			
		||||
        self.systemSettings = systemSettings
 | 
			
		||||
        self.fileIO = FileIO
 | 
			
		||||
        self.zones = fileIO.loadZones()
 | 
			
		||||
        for zone in self.zones:
 | 
			
		||||
            zone.setZoneManager(self)
 | 
			
		||||
        self.pipeLine = []
 | 
			
		||||
        self.piplineMutexLock = threading.Lock()
 | 
			
		||||
 | 
			
		||||
    def getZone(self, number):
 | 
			
		||||
        for zone in self.zones:
 | 
			
		||||
            if(zone.number == number):
 | 
			
		||||
                return zone
 | 
			
		||||
 | 
			
		||||
    def addIrrigationJob(self, job):
 | 
			
		||||
        with self.piplineMutexLock:
 | 
			
		||||
            self.pipeLine.append(job)
 | 
			
		||||
 | 
			
		||||
    def deleteIrrigationJobByID(self, id):
 | 
			
		||||
        i = 0
 | 
			
		||||
        with self.piplineMutexLock:
 | 
			
		||||
            while i < (len(self.pipeLine)):
 | 
			
		||||
                irrigationJob = self.pipeLine[i]
 | 
			
		||||
                if(irrigationJob.id == id):
 | 
			
		||||
                    self.pipeLine.pop(i)
 | 
			
		||||
                    break
 | 
			
		||||
                else:
 | 
			
		||||
                    i = i + 1
 | 
			
		||||
 | 
			
		||||
    def deleteIrrigationJobsForZone(self, zone):
 | 
			
		||||
        i = 0
 | 
			
		||||
        with self.piplineMutexLock:
 | 
			
		||||
            while i < (len(self.pipeLine)):
 | 
			
		||||
                irrigationJob = self.pipeLine[i]
 | 
			
		||||
                if (irrigationJob.zone == zone):
 | 
			
		||||
                    self.pipeLine.pop(i)
 | 
			
		||||
                else:
 | 
			
		||||
                    i = i + 1
 | 
			
		||||
 | 
			
		||||
    def isAnyZoneBusy(self):
 | 
			
		||||
        for zone in self.zones:
 | 
			
		||||
            if(zone.state):
 | 
			
		||||
                return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def switchZoneState(self, zone, state, duration, instant=False):
 | 
			
		||||
        if(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))
 | 
			
		||||
 | 
			
		||||
    def switchZoneIndexState(self, zoneIndex, state, duration, instant=False):
 | 
			
		||||
        zone = self.getZone(zoneIndex)
 | 
			
		||||
        self.switchZoneState(zone, state, duration, instant)
 | 
			
		||||
 | 
			
		||||
    def getPlanedDurationForZone(self, zone):
 | 
			
		||||
        totalDuration = 0
 | 
			
		||||
        with self.piplineMutexLock:
 | 
			
		||||
            for irrigationJob in self.pipeLine:
 | 
			
		||||
                if(irrigationJob.zone == zone):
 | 
			
		||||
                    totalDuration = totalDuration + irrigationJob.duration
 | 
			
		||||
        return totalDuration
 | 
			
		||||
 | 
			
		||||
    def refreshStates(self):
 | 
			
		||||
        for zone in self.zones:
 | 
			
		||||
            zone.refreshState()
 | 
			
		||||
 | 
			
		||||
    def processPipeline(self):
 | 
			
		||||
        self.piplineMutexLock.acquire()
 | 
			
		||||
        if(len(self.pipeLine) > 0 and (not self.isAnyZoneBusy())):
 | 
			
		||||
            irrigationJob = self.pipeLine[0]
 | 
			
		||||
            self.pipeLine.pop(0)
 | 
			
		||||
            self.piplineMutexLock.release()
 | 
			
		||||
            irrigationJob.process()
 | 
			
		||||
        else:
 | 
			
		||||
            self.piplineMutexLock.release()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def cronJobs(self):
 | 
			
		||||
        self.refreshStates()
 | 
			
		||||
        self.processPipeline()
 | 
			
		||||
        print("Executed Cron Jobs of ZoneManager.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IrrigationJob:
 | 
			
		||||
    def __init__(self, id, zone, duration=0):
 | 
			
		||||
        self.id = id
 | 
			
		||||
        self.zone = zone
 | 
			
		||||
        self.duration = duration
 | 
			
		||||
        self.zone.switchState(state=True, duration=duration, instant=False)
 | 
			
		||||
 | 
			
		||||
    def process(self):
 | 
			
		||||
        self.zone.switchState(state=True, duration=self.duration, instant=True)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import threading
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from Webserver import Webserver
 | 
			
		||||
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])
 | 
			
		||||
fileIO = FileIO(systemSettings)
 | 
			
		||||
zoneManager = ZoneManager(systemSettings=systemSettings, fileIO=fileIO)
 | 
			
		||||
webserver = Webserver(zoneManager=zoneManager, port=80)
 | 
			
		||||
 | 
			
		||||
def cronJobs():
 | 
			
		||||
    while True:
 | 
			
		||||
        zoneManager.cronJobs()
 | 
			
		||||
        print("Cronjobs done\nactual Time: " + str(time.time()))
 | 
			
		||||
        time.sleep(0.5)
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    cronjob_Thread = threading.Thread(target=cronJobs)
 | 
			
		||||
    cronjob_Thread.start()
 | 
			
		||||
    webserver.startWebserver()
 | 
			
		||||
		Loading…
	
		Reference in New Issue