Compare commits

...

97 Commits

Author SHA1 Message Date
5193580d13 chore: sync local changes 2026-02-19 22:22:27 +01:00
Atilla
563f11eb28 Final versie! 2024-04-14 12:53:41 +02:00
Atilla
d32883509f Final Versie! 2024-04-14 12:17:21 +02:00
Atilla
95b81a7016 Start vanaf hier 2024-04-12 21:51:37 +02:00
Atilla
88424cf30c SCHOON 2024-04-12 21:23:08 +02:00
Atilla
18fae2f870 Final versie 2024-04-10 14:02:22 +02:00
Atilla
ac40b781e4 Final versie! 2024-04-10 13:59:00 +02:00
Atilla
c6143effc6 errors gefixed | modal | form | dynamische paginas 2024-04-07 19:18:53 +02:00
Atilla
b8ab58bb53 Doc gemaakt voor scripts 2024-04-03 14:08:30 +02:00
Atilla
417dacbe73 Weer erbij! 2024-04-02 11:35:43 +02:00
Atilla
6f391d01f6 devices GEEN IDEE! 2024-04-02 11:23:40 +02:00
Atilla
34b4f9ef89 Beide werkend! 2024-03-27 13:03:41 +01:00
Atilla
dacb37a22d Homepage 1 sensor doet eht met data 2024-03-27 12:06:46 +01:00
Atilla
4df73bd862 Alles werkend nu!!! 2024-03-26 12:15:25 +01:00
Renzo
f4aee97a48 sensor 2024-03-26 11:01:03 +01:00
Renzo
0eb3890e73 Planten OOP V1.2 2024-03-26 10:59:44 +01:00
Renzo
4e7fae5943 planten class V1.1 2024-03-20 13:26:00 +01:00
Atilla
35717dad89 Alles werkend, punt vanaf waar iedereen verder moet!!!! 2024-03-20 10:39:34 +01:00
Renzo
d9504f5e7c plantenClass OOP V1.0 2024-03-20 09:35:18 +01:00
Atilla
1fd3a21ae3 Volledig samengevoegd 2024-03-20 08:55:47 +01:00
Atilla
d38d61950c Even een tussen update! 2024-03-19 18:58:43 +01:00
Atilla
a813bd6678 Merge branch 'Renzo' into fix 2024-03-19 11:52:29 +01:00
Burak
9b2e429496 het werkt wel, Atilla 2024-03-19 11:18:04 +01:00
Atilla
9307640750 Duplicaten verwijderd 2024-03-19 11:06:15 +01:00
Atilla
8e0eb0b4d4 Merge branch 'tijdelijke-branch' into fix 2024-03-15 20:30:21 +01:00
Atilla
2c01e21941 FIX: Flask 2024-03-15 20:25:45 +01:00
Atilla
cc1cf01b21 Niet helemaal af 2024-03-13 16:08:10 +01:00
Atilla
fd2eec9a4a undefined is teruggekeert in Flask 2024-03-13 16:04:59 +01:00
Atilla
0d1be6f463 Klein change 2024-03-13 13:58:37 +01:00
Atilla
04b7e62e45 Kleine verandering 2024-03-13 13:57:08 +01:00
6028570
bf51b595b7 HETWERKT 2024-03-13 13:48:36 +01:00
Renzo
ea67ed89e5 plantenClass OOP werkt volledig met sample data en if statement fix 2024-03-13 13:33:39 +01:00
Renzo
c3ca61f82d plantenClass OOP werkt volledig met sample data 2024-03-13 10:34:50 +01:00
Renzo
40947a7c46 plantenClass OOP bijna werkend 2024-03-13 09:27:20 +01:00
Renzo
cf36f53b68 plantenClass OOP bijna werkend 2024-03-13 09:23:41 +01:00
Atilla
fee3d4a564 Kleine veranderingen mqtt 2024-03-13 09:19:15 +01:00
Atilla
9de5db355c De echte 'clean' versie!!!! 2024-03-12 20:57:53 +01:00
Atilla
261427519f Clean versie mqtt 2024-03-12 19:56:49 +01:00
Atilla
c30cedc9e0 Merge branch 'burak' of https://github.com/6028570/GoodGarden into atilla 2024-03-12 12:15:08 +01:00
Atilla
82d7f127f4 MQTT wkerned, files worden opgeschoond 2024-03-12 12:13:35 +01:00
Atilla
01e2c5f1b2 MQTT wkerned, files worden opgeschoond 2024-03-12 12:12:38 +01:00
Burak
280a0d37dd Merge branch 'burak' of https://github.com/6028570/GoodGarden into burak 2024-03-12 12:11:01 +01:00
Burak
4441de32be update, duplicate 2024-03-12 12:01:42 +01:00
mohammedcifci78
19a256a251 gestuurdd 2024-03-12 11:03:45 +01:00
Atilla
69935ecbd9 MQTT Werkend gekregen 2024-03-10 22:21:01 +01:00
mohammedcifci78
b711d72b3a pythonwebsitewerkt 2024-03-06 14:27:23 +01:00
Atilla
5d74a371c5 Kleine comment 2024-03-06 12:08:40 +01:00
Atilla
3540630769 Classe versie 1.0 2024-03-06 11:46:11 +01:00
Atilla
6e1e410f2a Merge branch 'Renzo' of https://github.com/6028570/GoodGarden 2024-03-06 11:16:05 +01:00
Atilla
5c5d12e76e Geen idee 2024-03-06 11:15:20 +01:00
Renzo
e510e4f245 Class 2024-03-06 11:14:09 +01:00
Burak
0df6b09b6d test 2024-03-06 09:54:25 +01:00
Burak
f946d3ab95 0.2 2024-03-06 09:53:21 +01:00
Burak
f2974e1f47 weet ik veel 2024-03-06 09:51:21 +01:00
mohammedcifci78
da77a52b3b a 2024-03-06 09:41:11 +01:00
Renzo
4a70648a98 N/A 2024-03-06 09:11:01 +01:00
Atilla
49f3739f7e Bijgewerkte versie, html <body> nog niet juiste groote! 2024-03-05 21:05:57 +01:00
Atilla
e60518fa77 Gedeeltelijk opgelost 2024-03-05 19:39:41 +01:00
Atilla
c3fc78a598 NIET GOED!!! 2024-03-05 19:01:50 +01:00
Atilla
1fe35e62ef Merge branch 'Atilla', remote-tracking branch 'origin' 2024-03-05 16:18:35 +01:00
Atilla
9291757112 hoi 2024-03-05 16:17:54 +01:00
mohammedcifci78
8267cd114e Merge branch 'Atilla' of https://github.com/6028570/GoodGarden into mohammed 2024-03-05 11:30:43 +01:00
mohammedcifci78
261784a645 mqtt begin 2024-03-05 11:25:34 +01:00
Atilla
f592a13be4 Up to date 2024-03-05 11:10:23 +01:00
Renzo
4e9c8f415c Update Front-End 2024-03-05 10:49:25 +01:00
Renzo
24875bf7c6 Merge remote-tracking branch 'origin' into Renzo 2024-03-01 11:09:49 +01:00
Renzo
a5a38fbbca lalalal 2024-03-01 11:06:49 +01:00
mohammedcifci78
e51a718d2a week3Flask 2024-02-28 12:09:36 +01:00
Atilla
1bfc76abb3 Merge remote-tracking branch 'origin/Renzo' into Atilla 2024-02-28 12:06:12 +01:00
Atilla
378f972de2 niks 2024-02-28 12:05:26 +01:00
Renzo
57e54f460d hoi 2024-02-28 12:01:33 +01:00
mohammedcifci78
a6cf98519a week3.2 2024-02-28 11:06:23 +01:00
Burak Diker
eeec74407c Rename goodGarden.sql to goodgarden.sql 2024-02-27 12:14:48 +01:00
Burak
91a733ed3b update sql bestand 2024-02-27 11:56:57 +01:00
Burak
a56539cc9d update api 2024-02-27 11:45:31 +01:00
mohammedcifci
960c8a2f9e Merge pull request #2 from 6028570/revert-1-mohammed
Revert "Mohammed"
2024-02-27 10:50:31 +01:00
mohammedcifci
bf226e93d7 Revert "Mohammed" 2024-02-27 10:49:58 +01:00
mohammedcifci
2321982b15 Merge pull request #1 from 6028570/mohammed
Mohammed
2024-02-27 10:48:32 +01:00
mohammedcifci
7a3858c8ab Merge branch 'main' into mohammed 2024-02-27 10:48:17 +01:00
Atilla
5d6c492cfb Input Velden Electron 2024-02-16 17:19:52 +01:00
Burak
4d55f8a366 Update API 2024-02-16 11:11:24 +01:00
Atilla
7d9937bd92 Clean Version 2024-02-16 11:05:07 +01:00
Atilla
e10d54077f Yeah 2024-02-14 15:29:04 +01:00
Burak
54139a803e Update voor API 2024-02-14 11:20:30 +01:00
Burak
4446d11229 Fect Aanpassen 2024-02-13 14:23:30 +01:00
Atilla
51e645016e Database Connection 2024-02-13 11:08:21 +01:00
Atilla
0a4e9681c5 Database Connection 2024-02-13 11:04:24 +01:00
Atilla
54ccdd280c Electron Basic Template 2024-02-11 13:42:48 +01:00
mohammedcifci78
b953225a4b nietswerkt(nogfixed) 2024-02-09 11:46:55 +01:00
mohammedcifci78
be3beb5fc3 databaseupdate 2024-02-09 11:13:10 +01:00
mohammedcifci78
eaf5d2eb6e Merge branch 'main' of https://github.com/6028570/GoodGarden 2024-02-09 10:39:28 +01:00
Znooptokkie
01db72395a Create package.json 2024-02-07 13:46:05 +01:00
Znooptokkie
4596d5446a Create package-lock.json 2024-02-07 13:45:20 +01:00
Znooptokkie
8434cead76 Create main.js 2024-02-07 13:44:41 +01:00
Znooptokkie
fb62982307 Create index.html 2024-02-07 13:35:09 +01:00
Znooptokkie
0544192214 Create app.js 2024-02-07 13:34:06 +01:00
Znooptokkie
80f1b0f034 Create .gitignore 2024-02-07 13:32:01 +01:00
47 changed files with 7099 additions and 78 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
__pycache__/
venv/
.vscode/

13
.hintrc Normal file
View File

@@ -0,0 +1,13 @@
{
"extends": [
"development"
],
"hints": {
"axe/text-alternatives": [
"default",
{
"image-alt": "off"
}
]
}
}

56
ReadMe.md Normal file
View File

@@ -0,0 +1,56 @@
# GoodGarden
Welkom bij ons project genaamd "GoodGarden". Wij hebben besloten om er een semi-monolithic project van te maken. Alles is te vinden binnen deze repository, inclusief de "mqtt" publisher.
### Vereisten
* Python
* Node.Js
* XAMPP (of andere naar keuze)
* MQTT Geinstalleerd en toegevoegd aan je PATH variable
### Installeren
Zorg dat je in de hoofdmap "GoodGarden" zit. Kijk in je path: "../GoodGarden". Als je in de correcte map zit moet je de volgende items installeren:
npm install electron
npm install express
npm install body-parser
npm install python-shell
npm install --save-dev npm-run-all
npm install wait-on --save-dev
npm install concurrently --save-dev
pip install mysql-connector-python
pip install requests
pip install flask-cors
## Gebruik
- De data wordt gefetched via MQTT, waarnaar het in topics wordt verdeeld en dan kan je met een script op he gewenste onderwerp "subscriben".
- Op het Dashboard worden de planten getoond die aanwezig zijn in de database. Je kan doormiddel een modal de planten invullen die je getoond wilt hebben op het Dashboard. Voor nu moet je dan even het "planten.py" script handmatig uitvoeren. Ook worden de 2 sensoren getoond op het Dashboard en hebben we voor nu een "placeholder" neergezet voor de pomp.
- Als je op een plant klikt ga je naar de pagina van de plant toe. Die is dynamisch gemaakt, alleen nog niet helemaal ingevuld met data. Er zit een switch-knop in waarmee je kan aangeven of de plant aanwezig is in de kas of niet, ook die werkt helaas niet helemaal naar behoren.
- Je kan ook op de sensoren klikken die op de homepage staan. Dan ga je door naar de pagina van die desbetreffende sensor. De bedoeling was om hier alle errors en waardes te tonen de we doorkrijgen van de sensoren.
- Als laatste hebben we een grafiek toegevoegd aan het project die laat zien wat de temperatuur is voor de aankomende 5 dagen. De bedoeling was om voor elke plant een unieke grafiek te maken waar dan data in getoond zou worden.
## Versiebeheer
We gebruiken [Github](https://github.com) voor versiebeheer. Voor de beschikbare versies, zie de [GoodGarden](https://github.com/Znooptokkie/GoodGarden).
## Auteurs
* **Atilla Oomen** - *Projectleider | Back end Programmeur* - [Znooptokkie](https://github.com/Znooptokkie)
* **Mohammed Çifçi** - *Back end Programmeur* - [6028570](https://github.com/6028570)
* **Burak Diker** - *Back end Programmeur* - [6028083](https://github.com/6028083)
* **Justin Doekhi** - *Front end Programmeur* - [6027529](https://github.com/6027529)
* **Renzo van Putten** - *Front end Programmeur* - [6025850](https://github.com/6025850)

Binary file not shown.

177
app.js Normal file
View File

@@ -0,0 +1,177 @@
// Importeert de benodigde Electron modules om een desktopapplicatie te creëren.
const { app, BrowserWindow, ipcMain } = require("electron");
// Express framework om een HTTP-server op te zetten.
const express = require("express");
// Body-parser middleware om inkomende request bodies te parsen.
const bodyParser = require("body-parser");
// PythonShell om Python scripts binnen een NodeJS applicatie uit te voeren.
const { PythonShell } = require("python-shell");
// Path module voor het werken met bestandspaden.
const path = require("path");
// Definieert het pad naar de index.html van de Electron app.
// const urlElectron = path.join(__dirname, "src/index.html");
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
// Initialiseert de Express app.
const server = express();
// Configureert de express server om URL-encoded data te accepteren.
server.use(bodyParser.urlencoded({ extended: true }));
// Handler voor het "/submit-form" endpoint. Verwerkt formulierinzendingen.
server.post("/submit-form", (req, res) =>
{
// Extracts form data from the request body.
const { plant_naam, plantensoort } = req.body;
const plant_geteelt = req.body.plant_geteelt == "true" ? "true" : "false";
// Configuratieopties voor het uitvoeren van het Python script.
let options = {
mode: "text",
args: [plant_naam, plantensoort, plant_geteelt],
};
// Voert een Python script uit en handelt de response af.
PythonShell.run("src/py/script/db_connect_form.py", options, (err, results) =>
{
if (err)
{
console.error(err);
res.send("Er is een fout opgetreden");
}
else
{
console.log("Python script uitvoering resultaten:", results);
res.send("Formulier succesvol verwerkt");
}
});
});
// Definieert de poort waarop de server luistert.
// const PORT = 5000;
const PORT = 3000;
server.listen(PORT, () =>
{
console.log(`Server is listening on port ${PORT}`);
});
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
let mainWindow; // Referentie naar het hoofdvenster van de Electron app.
// Creëert het hoofdvenster van de Electron app.
function createWindow()
{
mainWindow = new BrowserWindow({
width: 1280,
height: 800,
frame: false,
icon: path.join(__dirname, "src/py/static/images/logo.png"),
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
webSecurity: true,
},
});
mainWindow.loadFile(path.join(__dirname, "src", "py", "templates", "index.html"));
// Stelt IPC Main Listeners in voor communicatie tussen hoofdproces en renderproces.
setupIpcMainListeners();
}
// Wanneer de app klaar is, wordt het hoofdvenster gecreëerd.
app.whenReady().then(createWindow);
// Sluit de app af wanneer alle vensters gesloten zijn (behalve op macOS).
app.on("window-all-closed", () =>
{
if (process.platform !== "darwin")
{
app.quit();
}
});
// Creëert het hoofdvenster opnieuw wanneer het app icoon wordt aangeklikt en er zijn geen vensters open.
app.on("activate", () =>
{
if (BrowserWindow.getAllWindows().length === 0)
{
createWindow();
}
});
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
// Configureert IPC (Inter-Process Communication) luisteraars.
function setupIpcMainListeners()
{
// IPC luisteraar voor het uitvoeren van een Python script.
ipcMain.on("run-python-script", (event, args) =>
{
let options = {
mode: "text",
args: args,
};
// Hier zou je PythonShell.run met deze opties aanroepen.
});
// IPC luisteraar voor het aanvragen van data update.
ipcMain.on("request-update-data", (event, args) =>
{
const databaseData = {
timestamp: "2022-01-01",
gateway_receive_time: "2022-01-01",
device: "Device1",
value: 50,
};
event.reply("update-data-result", { databaseData });
});
// IPC luisteraar voor het bijwerken van HTML data via het renderproces.
ipcMain.on("update-html-data", (event, data) =>
{
mainWindow.webContents.send("update-html-data", data);
});
}

102
app.py Normal file
View File

@@ -0,0 +1,102 @@
from flask import Flask, jsonify
import mysql.connector
import requests
app = Flask(__name__)
def database_connect():
try:
connection = mysql.connector.connect(
host="localhost",
user="root",
password="root",
database="goodgarden"
)
return connection
except Exception as e:
print("Database connection failed:", e)
#Throw error
return None
# Function to get data from the MySQL database
def get_database_data():
#Throw error
mydb = database_connect()
if mydb and mydb.is_connected():
cursor = mydb.cursor(dictionary=True) # Enable dictionary result
# Query to retrieve the latest battery voltage data
query = "SELECT label, last_seen, last_battery_voltage, device_id FROM devices"
cursor.execute(query)
battery_data = cursor.fetchall() # Fetch all rows
mydb.close()
return battery_data
@app.route('/', methods=['GET'])
def get_data():
battery_data = get_database_data()
if battery_data is None or len(battery_data) == 0:
return jsonify({"error": "Failed to fetch data from database"})
return jsonify(battery_data) # Directly return the list of dictionaries as JSON
def get_weather_data():
# Api key in a file
api_key = "05ddd06644"
location = "Leiden"
url = f"https://weerlive.nl/api/weerlive_api_v2.php?key={api_key}&locatie={location}"
response = requests.get(url).json()
return response
@app.route('/weather', methods=['GET'])
def get_weather():
weather_response = get_weather_data()
if 'error' in weather_response:
return jsonify({"error": "Kon weerdata niet ophalen"})
live_weather = weather_response.get('liveweer', [])
weather_forecast = weather_response.get('wk_verw', [])
day_forecast = weather_response.get('wk_verw', []) # Dagverwachtingen
weather_data = {
"live_weather": live_weather[0] if live_weather else {},
"weather_forecast": weather_forecast,
"day_forecast": day_forecast # Voeg de dagverwachtingen toe aan de weerdata
}
return jsonify(weather_data)
def get_planten_data():
mydb = database_connect()
if mydb and mydb.is_connected():
try:
cursor = mydb.cursor(dictionary=True)
query = "SELECT id, plant_naam, plantensoort, plant_geteelt FROM planten"
cursor.execute(query)
planten_data = cursor.fetchall()
mydb.close()
return planten_data
except Exception as e:
print("Failed to fetch planten data:", e)
return {"error": "Kon plantendata niet ophalen"}
else:
return {"error": "Database connection failed"}
@app.route("/planten", methods=["GET"])
def get_planten():
planten_response = get_planten_data()
if "error" in planten_response:
return jsonify(planten_response)
return jsonify(planten_response)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)

Binary file not shown.

Binary file not shown.

View File

@@ -1,16 +0,0 @@
DROP DATABASE IF EXISTS goodgarden;
CREATE DATABASE goodgarden;
CREATE TABLE goodgarden.sensor_data (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
timestamp INT,
gateway_receive_time VARCHAR(50),
device INT,
value DECIMAL(10, 5),
PRIMARY KEY (id)
);
-- Invoegen van gegevens in de 'sensor_data'-tabel
INSERT INTO goodgarden.sensor_data (timestamp, gateway_receive_time, device, value)
VALUES (1707295162, '2024-02-07T08:39:22Z', 256, 4.107448101043701),
(1707261284, '2024-02-06T23:14:44Z', 322, 4.111111164093018);

417
goodgarden.sql Normal file
View File

@@ -0,0 +1,417 @@
-- phpMyAdmin SQL Dump
-- version 5.2.1
-- https://www.phpmyadmin.net/
--
-- Host: 127.0.0.1
-- Gegenereerd op: 09 apr 2024 om 21:22
-- Serverversie: 10.4.32-MariaDB
-- PHP-versie: 8.2.12
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `goodgarden`
--
-- --------------------------------------------------------
DROP DATABASE IF EXISTS goodgarden;
CREATE DATABASE goodgarden;
USE goodgarden;
--
-- Tabelstructuur voor tabel `battery_voltage_events`
--
CREATE TABLE `battery_voltage_events` (
`id` int(10) UNSIGNED NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Gegevens worden geëxporteerd voor tabel `battery_voltage_events`
--
INSERT INTO `battery_voltage_events` (`id`, `timestamp`, `gateway_receive_time`, `device`, `value`) VALUES
(2185, 1710839863, '2024-03-19T09:17:43Z', 256, 4.03663),
(2186, 1710842346, '2024-03-19T09:59:06Z', 322, 4.08547);
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `care_schedules`
--
CREATE TABLE `care_schedules` (
`plant_id` int(10) UNSIGNED NOT NULL,
`water` int(11) DEFAULT NULL,
`bemesting` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Gegevens worden geëxporteerd voor tabel `care_schedules`
--
INSERT INTO `care_schedules` (`plant_id`, `water`, `bemesting`) VALUES
(1, 2, 15),
(2, 3, 10),
(3, 2, 10),
(4, 2, 15),
(5, 2, 15),
(6, 3, 15),
(7, 3, 10),
(8, 2, 15),
(9, 2, 10),
(10, 2, 10),
(11, 2, 15),
(12, 3, 15),
(13, 2, 10),
(14, 3, 15),
(15, 2, 10);
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `devices`
--
CREATE TABLE `devices` (
`id` int(10) UNSIGNED NOT NULL,
`serial_number` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
`last_seen` int(11) DEFAULT NULL,
`last_battery_voltage` float DEFAULT NULL,
`device_id` int(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Gegevens worden geëxporteerd voor tabel `devices`
--
INSERT INTO `devices` (`id`, `serial_number`, `name`, `label`, `last_seen`, `last_battery_voltage`, `device_id`) VALUES
(15, '0033889B1BAB1169', 'firefly2_0051', 'The Field', 1712297000, 3.92796, 256),
(16, '006FE1FC316ED7D8', 'firefly2_0111', 'The Field', 1712297257, 4.07448, 322);
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `fetch`
--
CREATE TABLE `fetch` (
`id` int(10) UNSIGNED NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `par_events`
--
CREATE TABLE `par_events` (
`id` int(10) UNSIGNED NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `planten`
--
CREATE TABLE `planten` (
`id` int(11) NOT NULL,
`plant_naam` varchar(50) DEFAULT NULL,
`plantensoort` varchar(50) NOT NULL,
`plant_geteelt` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Gegevens worden geëxporteerd voor tabel `planten`
--
INSERT INTO `planten` (`id`, `plant_naam`, `plantensoort`, `plant_geteelt`) VALUES
(1, 'Tomatenplant', 'Groente', 1),
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `plants`
--
CREATE TABLE `plants` (
`id` int(10) UNSIGNED NOT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`beschrijving` text DEFAULT NULL,
`licht` varchar(50) DEFAULT NULL,
`vochtigheid` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Gegevens worden geëxporteerd voor tabel `plants`
--
INSERT INTO `plants` (`id`, `name`, `type`, `beschrijving`, `licht`, `vochtigheid`) VALUES
(1, 'Tomaat', 'Groente', 'Tomaat (Solanum lycopersicum) is een veelgeteelde groenteplant in de nachtschadefamilie.', 'Volle Zon', '70'),
(2, 'Sla', 'Groente', 'Sla (Lactuca sativa) is een bladgroente die vaak wordt gebruikt in salades.', 'Gedeeltelijke Schaduw', '80'),
(3, 'Wortel', 'Groente', 'Wortel (Daucus carota) is een wortelgroente meestal oranje van kleur.', 'Volle Zon', '60'),
(4, 'Komkommer', 'Groente', 'Komkommer (Cucumis sativus) is een veel gekweekte kruipende wijnplant in de familie Cucurbitaceae.', 'Volle Zon', '65'),
(5, 'Aardappel', 'Groente', 'Aardappel (Solanum tuberosum) is een zetmeelhoudend knolgewas van de vaste plant Solanum tuberosum.', 'Gedeeltelijke Schaduw', '75'),
(6, 'Courgette', 'Groente', 'Courgette (Cucurbita pepo) is een zomerpompoen die bijna een meter lang kan worden, maar meestal onrijp wordt geoogst op 15 tot 25 cm.', 'Volle Zon', '70'),
(7, 'Spinazie', 'Groente', 'Spinazie (Spinacia oleracea) is een bladgroente afkomstig uit Centraal- en West-Azië.', 'Gedeeltelijke Schaduw', '70'),
(8, 'Paprika', 'Groente', 'Paprika (Capsicum annuum) is een fruit- en groenteplant gekweekt om zijn vruchten.', 'Volle Zon', '80'),
(9, 'Ui', 'Groente', 'Ui (Allium cepa) is een groente die de meest geteelde soort is van het geslacht Allium.', 'Volle Zon', '50'),
(10, 'Radijs', 'Groente', 'Radijs (Raphanus raphanistrum subsp. sativus) is een eetbare wortelgroente.', 'Volle Zon', '70'),
(11, 'Groene Boon', 'Groente', 'Groene bonen zijn de onrijpe, jonge vruchten en beschermende peulen van verschillende cultivars van de gewone boon.', 'Volle Zon', '75'),
(12, 'Broccoli', 'Groente', 'Broccoli (Brassica oleracea var. italica) is een koelgewas dat in het voorjaar of de herfst kan worden gekweekt.', 'Volle Zon', '75'),
(13, 'Kool', 'Groente', 'Kool (Brassica oleracea of varianten) is een bladgroente, rood of wit, een tweejarige plant die wordt geteeld als eenjarige groente.', 'Gedeeltelijke Schaduw', '80'),
(14, 'Aubergine', 'Groente', 'Aubergine (Solanum melongena) is een soort in de nachtschadefamilie die wordt gekweekt om zijn eetbare vruchten.', 'Volle Zon', '85'),
(15, 'Boerenkool', 'Groente', 'Boerenkool of bladkool (Brassica oleracea var. acephala) is een groenteplant met groene of paarse bladeren.', 'Gedeeltelijke Schaduw', '70');
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `relative_humidity_events`
--
CREATE TABLE `relative_humidity_events` (
`id` int(10) UNSIGNED NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `soil_electric_conductivity_events`
--
CREATE TABLE `soil_electric_conductivity_events` (
`id` int(10) UNSIGNED NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `soil_relative_permittivity_events`
--
CREATE TABLE `soil_relative_permittivity_events` (
`id` int(10) UNSIGNED NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Tabelstructuur voor tabel `soil_temperature_events`
--
CREATE TABLE `soil_temperature_events` (
`id` int(10) NOT NULL,
`timestamp` int(11) DEFAULT NULL,
`gateway_receive_time` varchar(50) DEFAULT NULL,
`device` int(11) DEFAULT NULL,
`value` decimal(10,5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Indexen voor geëxporteerde tabellen
--
--
-- Indexen voor tabel `battery_voltage_events`
--
ALTER TABLE `battery_voltage_events`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`);
--
-- Indexen voor tabel `care_schedules`
--
ALTER TABLE `care_schedules`
ADD PRIMARY KEY (`plant_id`);
--
-- Indexen voor tabel `devices`
--
ALTER TABLE `devices`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `last_seen` (`last_seen`),
ADD UNIQUE KEY `last_battery_voltage` (`last_battery_voltage`);
--
-- Indexen voor tabel `fetch`
--
ALTER TABLE `fetch`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`),
ADD UNIQUE KEY `value` (`value`);
--
-- Indexen voor tabel `par_events`
--
ALTER TABLE `par_events`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`),
ADD UNIQUE KEY `value` (`value`);
--
-- Indexen voor tabel `planten`
--
ALTER TABLE `planten`
ADD PRIMARY KEY (`id`);
--
-- Indexen voor tabel `plants`
--
ALTER TABLE `plants`
ADD PRIMARY KEY (`id`);
--
-- Indexen voor tabel `relative_humidity_events`
--
ALTER TABLE `relative_humidity_events`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`),
ADD UNIQUE KEY `value` (`value`);
--
-- Indexen voor tabel `soil_electric_conductivity_events`
--
ALTER TABLE `soil_electric_conductivity_events`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`),
ADD UNIQUE KEY `value` (`value`);
--
-- Indexen voor tabel `soil_relative_permittivity_events`
--
ALTER TABLE `soil_relative_permittivity_events`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`),
ADD UNIQUE KEY `value` (`value`);
--
-- Indexen voor tabel `soil_temperature_events`
--
ALTER TABLE `soil_temperature_events`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `timestamp` (`timestamp`),
ADD UNIQUE KEY `gateway_receive_time` (`gateway_receive_time`),
ADD UNIQUE KEY `value` (`value`);
--
-- AUTO_INCREMENT voor geëxporteerde tabellen
--
--
-- AUTO_INCREMENT voor een tabel `battery_voltage_events`
--
ALTER TABLE `battery_voltage_events`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2189;
--
-- AUTO_INCREMENT voor een tabel `devices`
--
ALTER TABLE `devices`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21;
--
-- AUTO_INCREMENT voor een tabel `fetch`
--
ALTER TABLE `fetch`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=100;
--
-- AUTO_INCREMENT voor een tabel `par_events`
--
ALTER TABLE `par_events`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
--
-- AUTO_INCREMENT voor een tabel `planten`
--
ALTER TABLE `planten`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=15;
--
-- AUTO_INCREMENT voor een tabel `plants`
--
ALTER TABLE `plants`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=16;
--
-- AUTO_INCREMENT voor een tabel `relative_humidity_events`
--
ALTER TABLE `relative_humidity_events`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
--
-- AUTO_INCREMENT voor een tabel `soil_electric_conductivity_events`
--
ALTER TABLE `soil_electric_conductivity_events`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT voor een tabel `soil_relative_permittivity_events`
--
ALTER TABLE `soil_relative_permittivity_events`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT voor een tabel `soil_temperature_events`
--
ALTER TABLE `soil_temperature_events`
MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
--
-- Beperkingen voor geëxporteerde tabellen
--
--
-- Beperkingen voor tabel `care_schedules`
--
ALTER TABLE `care_schedules`
ADD CONSTRAINT `fk_plant_id` FOREIGN KEY (`plant_id`) REFERENCES `plants` (`id`);
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@@ -1,62 +0,0 @@
import mysql.connector
import requests
# Verbinding maken met de database
mydb = mysql.connector.connect(
host="localhost",
user="root",
password="",
database="goodgarden"
)
# Controleren of de verbinding succesvol is
if mydb.is_connected():
print("Connected to the database")
else:
print("Failed to connect to the database")
try:
# Maak een cursor aan
mycursor = mydb.cursor()
# Voer de query uit om gegevens op te halen
mycursor.execute("SELECT * FROM goodgarden.sensor_data")
# Haal de resultaten op
myresult = mycursor.fetchall()
# Print de resultaten
for x in myresult:
print(x)
except mysql.connector.Error as err:
print(f"Error: {err}")
finally:
# Sluit de cursor en de databaseverbinding
mycursor.close()
mydb.close(
)
def fetch_data():
url = "https://garden.inajar.nl/api/battery_voltage_events/?format=json"
headers = {
"Authorization": "Token 33bb3b42452306c58ecedc3c86cfae28ba22329c"
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # Raise an exception for 4xx and 5xx status codes
data = response.json()
load_data(data)
except requests.exceptions.RequestException as e:
print(f"Error fetching data: {e}")
def load_data(data):
api_data = data
print("Data loaded:", api_data)
if __name__ == "__main__":
fetch_data()

59
mqtt/mqtt_client.py Normal file
View File

@@ -0,0 +1,59 @@
# Importeer de paho.mqtt.client module die MQTT-client functionaliteiten biedt.
import paho.mqtt.client as mqtt
def create_client(client_id, on_connect, on_message, broker="localhost", port=1883):
"""
Creëert en configureert een MQTT-client.
Deze functie initialiseert een MQTT-client met een unieke client_id,
definieert callback functies voor connect- en message-events, en
maakt vervolgens verbinding met de MQTT-broker.
Parameters:
client_id (str): Een unieke identifier voor de MQTT-client.
on_connect (function): Callback functie die wordt aangeroepen
wanneer de client verbinding maakt met de broker.
on_message (function): Callback functie die wordt aangeroepen
wanneer een bericht wordt ontvangen.
broker (str, optional): Het adres van de MQTT-broker.
Standaard is dit "localhost".
port (int, optional): De poort waarop de MQTT-broker luistert.
Standaard is dit 1883.
Returns:
mqtt.Client: Een geconfigureerde MQTT-clientobject.
"""
# Initialiseert een MQTT-client met de opgegeven client_id.
client = mqtt.Client(client_id)
# Stelt de callback functies in voor connect- en message-events.
client.on_connect = on_connect
client.on_message = on_message
# Maakt verbinding met de opgegeven MQTT-broker en poort.
client.connect(broker, port, 60) # De keepalive is ingesteld op 60 seconden.
# Retourneert het geïnitialiseerde en geconfigureerde client-object.
return client
def start_loop(client):
"""
Start de netwerkloop van de MQTT-client.
Deze functie start de oneindige loop van de client, waardoor deze
continu luistert naar berichten van de broker. De loop wordt onderbroken
bij het ontvangen van een KeyboardInterrupt (Ctrl+C).
Parameters:
client (mqtt.Client): De MQTT-client die de loop zal uitvoeren.
Returns:
None
"""
try:
# Start de oneindige loop die berichten verwerkt.
client.loop_forever()
except KeyboardInterrupt:
# Print een bericht en maakt de verbinding met de broker los
# als de gebruiker de loop onderbreekt.
print("Disconnecting from broker")

87
mqtt/publisher.py Normal file
View File

@@ -0,0 +1,87 @@
# Importeer benodigde modules voor het uitvoeren van HTTP-verzoeken, tijdbeheer, en JSON-operaties.
import requests
import time
import json
# Importeer MQTT-client functies van een aangepaste module.
from mqtt_client import create_client, start_loop
# Stel het interval in seconden in voor het periodiek ophalen en publiceren van data.
publish_interval = 900 # MOET ~900 ZIJN. -- 15min
# Definieer API-eindpunten en de corresponderende MQTT topics.
api_endpoints = [
{"url": "https://garden.inajar.nl/api/devices/", "topic": "goodgarden/devices"},
{"url": "https://garden.inajar.nl/api/relative_humidity_events/", "topic": "goodgarden/relative_humidity"},
{"url": "https://garden.inajar.nl/api/battery_voltage_events/", "topic": "goodgarden/battery_voltage"},
{"url": "https://garden.inajar.nl/api/soil_electric_conductivity_events/", "topic": "goodgarden/soil_electric_conductivity"},
{"url": "https://garden.inajar.nl/api/soil_relative_permittivity_events/", "topic": "goodgarden/soil_relative_permittivity"},
{"url": "https://garden.inajar.nl/api/soil_temperature_events/", "topic": "goodgarden/soil_temperature"},
{"url": "https://garden.inajar.nl/api/par_events/", "topic": "goodgarden/par_events"}
]
def on_connect(client, userdata, flags, rc):
"""
Callback functie voor het afhandelen van de connectie-event met de MQTT broker.
Parameters:
client: De MQTT-client instantie.
userdata: De private user-specifieke data zoals ingesteld in Client() of user_data_set().
flags: Reactie vlaggen van de broker.
rc: De connectie resultaat code.
"""
print("Geconnect met code "+str(rc))
def on_message(client, userdata, msg):
"""
Callback functie voor het afhandelen van inkomende berichten voor geabonneerde topics.
Parameters:
client: De MQTT-client instantie.
userdata: De private user-specifieke data.
msg: Het bericht instantie.
"""
print(f"Message: {msg.topic} {str(msg.payload)}")
# Initialiseer de MQTT-client met een unieke client ID en callback functies.
client = create_client("publisher1", on_connect, on_message)
def publish_to_mqtt(topic, data):
"""
Publiceert data naar een MQTT topic.
Parameters:
topic (str): Het MQTT topic waarop de data wordt gepubliceerd.
data (dict): De data die gepubliceerd moet worden, geconverteerd naar JSON formaat.
"""
json_data = json.dumps(data) # Serialiseer de data naar een JSON-string.
client.publish(topic, json_data)
print(f"\033[92mData gepubliceerd naar MQTT topic {topic}.\033[0m")
def fetch_and_publish_data():
"""
Haalt data op van API-eindpunten en publiceert deze naar MQTT.
"""
for endpoint in api_endpoints:
url = endpoint["url"]
mqtt_topic = endpoint["topic"]
access_token = "33bb3b42452306c58ecedc3c86cfae28ba22329c"
try:
headers = {"Authorization": f"Token {access_token}"}
response = requests.get(url, headers=headers)
response.raise_for_status() # Verifieert respons status.
data = response.json()
print(f"Data van {url}: {data}")
publish_to_mqtt(mqtt_topic, data)
except requests.exceptions.RequestException as e:
print(f"Error met data ophalen {url}: {e}")
if __name__ == "__main__":
client.loop_start() # Start de niet-blokkerende MQTT-client loop.
while True:
fetch_and_publish_data() # Haal data op en publiceer.
print(f"\033[91mWachten, wachten en nog eens wachten... {publish_interval} secondes!\033[0m\n")
time.sleep(publish_interval) # Wacht voor het ingestelde interval.
client.loop_stop()

3307
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "goodgarden",
"description": "",
"main": "app.js",
"scripts": {
"start": "npm-run-all --parallel start-flask start-electron",
"start-electron": "wait-on http://localhost:5000 && electron app.js",
"start-flask": "py app.py"
},
"author": "",
"license": "ISC",
"devDependencies": {
"concurrently": "^8.2.2",
"electron": "^23.3.13",
"npm-run-all": "^4.1.5",
"wait-on": "^7.2.0"
},
"dependencies": {
"axios": "^1.6.7",
"body-parser": "^1.20.2",
"elctron": "^0.0.1-security",
"express": "^4.19.2",
"mysql2": "^3.9.1",
"python-shell": "^5.0.0"
}
}

0
src/py/__init__.py Normal file
View File

Binary file not shown.

View File

@@ -0,0 +1,216 @@
import mysql.connector
import requests
import time
from db_connect import database_connect
# Functie voor het aanmaken van gegevens in de database
def create_data(url, access_token, repeat_count=5):
for _ in range(repeat_count):
try:
headers = {"Authorization": f"Token {access_token}"}
response = requests.get(url, headers=headers)
response.raise_for_status()
data = response.json()
print(f"Data from {url}:\n")
# Check if data is a list (records directly under the root)
if isinstance(data, list):
records = data
elif isinstance(data, dict) and 'results' in data:
records = data['results']
else:
print(f"Unexpected data format received: {data}")
continue
for record in records:
# Now, record is assumed to be a dictionary
timestamp = record.get('timestamp', '')
gateway_receive_time = record.get('gateway_receive_time', '')
device = record.get('device', '')
value = record.get('value', '')
print(f"\nInserted data: Timestamp: {timestamp}, Device: {device}, Battery Voltage: {value}V\n")
if float(value) < 5.0:
print("Waarschuwing: Batterijspanning is lager dan 3.0 volt. Opladen aanbevolen.\n")
# Controleer of de batterijspanning hoger is dan 4.2 volt en geef een melding
elif float(value) > 6.2:
print("Melding: Batterijspanning is hoger dan 4.2 volt. Batterij is vol.\n")
else:
print("Melding: Batterijspanning is binnen het gewenste bereik.\n\n")
# Insert data into the database
insert_data(record)
except requests.exceptions.RequestException as e:
print(f"Error fetching data from {url}: {e}")
print("Waiting for the next create action...\n")
time.sleep(1)
# Functie voor het invoegen van gegevens in de database
def insert_data(record):
mydb = database_connect()
if mydb.is_connected():
mycursor = mydb.cursor()
# Hier moet je de juiste kolomnamen en gegevensindeling aanpassen op basis van de API-respons
insert_query = """
INSERT INTO goodgarden.battery_voltage_events (timestamp, gateway_receive_time, device, value)
VALUES (%s, %s, %s, %s)
"""
# Pas dit aan op basis van de werkelijke structuur van de JSON
timestamp = record.get('timestamp', '')
gateway_receive_time = record.get('gateway_receive_time', '')
device = record.get('device', '')
value = record.get('value', '')
print(f"Inserting data: timestamp={timestamp}, gateway_receive_time={gateway_receive_time}, device={device}, value={value}") # Print de ingevoerde gegevens
# Voer de query uit
mycursor.execute(insert_query, (timestamp, gateway_receive_time, device, value))
# Bevestig de wijzigingen
mydb.commit()
# Sluit cursor en verbinding
mycursor.close()
mydb.close()
print("Data inserted into the database.")
# Functie voor het lezen van gegevens uit de database
def read_data(url, access_token, repeat_count=5):
for _ in range(repeat_count):
try:
headers = {"Authorization": f"Token {access_token}"}
response = requests.get(url, headers=headers)
response.raise_for_status()
data = response.json()
print(f"Data from {url}:\n")
for record in data['results']:
timestamp = record.get('timestamp', '')
device = record.get('device', '')
value = record.get('value', '')
print(f"Timestamp: {timestamp}, Device: {device}, Battery Voltage: {value}V\n")
if float(value) < 3.0:
print("Waarschuwing: Batterijspanning is lager dan 3.0 volt. Opladen aanbevolen.\n")
# Controleer of de batterijspanning hoger is dan 4.2 volt en geef een melding
elif float(value) > 4.2:
print("Melding: Batterijspanning is hoger dan 4.2 volt. Batterij is vol.\n")
else:
print("Melding: Batterijspanning is binnen het gewenste bereik.\n\n")
except requests.exceptions.RequestException as e:
print(f"Error fetching data from {url}: {e}")
print("Waiting for the next read action...\n")
time.sleep(300)
# Functie voor het bijwerken van gegevens in de database
def update_data(record_id):
try:
mydb = database_connect()
if mydb.is_connected():
mycursor = mydb.cursor()
# Controleer of het record bestaat voordat je het bijwerkt
mycursor.execute("SELECT * FROM goodgarden.battery_voltage_events WHERE id = %s", (record_id,))
existing_record = mycursor.fetchone()
if not existing_record:
print(f"Record with ID {record_id} not found. Update operation aborted.")
return
# Vraag de gebruiker om nieuwe waarden voor de andere velden
new_timestamp = input("Enter new timestamp: ")
new_gateway_receive_time = input("Enter new gateway_receive_time: ")
new_device = input("Enter new device: ")
new_value = input("Enter new value: ")
# Hier moet je de juiste kolomnamen aanpassen op basis van de structuur van je database
update_query = """
UPDATE goodgarden.battery_voltage_events
SET timestamp = %s, gateway_receive_time = %s, device = %s, value = %s
WHERE id = %s
"""
# Voer de query uit
print(f"Executing update query: {update_query}")
print(f"Updating record with ID {record_id} to new values - timestamp: {new_timestamp}, gateway_receive_time: {new_gateway_receive_time}, device: {new_device}, value: {new_value}")
mycursor.execute(update_query, (new_timestamp, new_gateway_receive_time, new_device, new_value, record_id))
# Bevestig de wijzigingen
mydb.commit()
print(f"Update executed. Rowcount: {mycursor.rowcount}")
except mysql.connector.Error as update_err:
print(f"Error updating data: {update_err}")
finally:
# Zorg ervoor dat je altijd de cursor en de databaseverbinding sluit
if 'mycursor' in locals() and mycursor is not None:
mycursor.close()
if 'mydb' in locals() and mydb.is_connected():
mydb.close()
# Functie voor het verwijderen van gegevens uit de database
def delete_data(record_id):
mydb = database_connect()
if mydb.is_connected():
mycursor = mydb.cursor()
# Hier moet je de juiste kolomnamen aanpassen op basis van de structuur van je database
delete_query = """
DELETE FROM goodgarden.battery_voltage_events
WHERE id = %s
"""
# Voer de query uit
mycursor.execute(delete_query, (record_id,))
# Bevestig de wijzigingen
mydb.commit()
# Sluit cursor en verbinding
mycursor.close()
mydb.close()
print(f"Data with ID {record_id} deleted.")
# Functie voor het aanmaken van gegevens in de database op basis van batterijspanningsinformatie
if __name__ == "__main__":
url = "https://garden.inajar.nl/api/battery_voltage_events/?format=json"
access_token = "33bb3b42452306c58ecedc3c86cfae28ba22329c"
# Je kunt repeat_count wijzigen om te bepalen hoe vaak je de bewerking wilt herhalen
repeat_count = 10
# Keuze voor de bewerking
operation_choice = input("Choose operation (C for Create, R for Read, U for Update, D for Delete): ").upper()
if operation_choice == "C":
# Maak gegevens aan
create_data(url, access_token, repeat_count)
elif operation_choice == "R":
# Lees gegevens
read_data(url, access_token, repeat_count)
elif operation_choice == "U":
# Update gegevens
record_id = int(input("Enter record ID to update: "))
# Call the update_data function without additional arguments
# Roep de update_data function aan zonder arguments
update_data(record_id)
elif operation_choice == "D":
# Verwijder gegevens
record_id = int(input("Enter record ID to delete: "))
delete_data(record_id)
else:
print("Invalid operation choice. Please choose C, R, U, or D.")

View File

@@ -0,0 +1,26 @@
import mysql.connector
from mysql.connector import Error
# Definieer een functie genaamd `database_connect` om verbinding te maken met de database.
def database_connect():
try:
# Maak een verbinding met de MySQL database.
# Specificeer de database credentials en de database naam.
connection = mysql.connector.connect(
host="localhost", # De hostnaam waar de database draait, in dit geval lokaal.
user="root", # De gebruikersnaam voor de database.
password="root", # Het wachtwoord voor de gebruiker, leeg in dit geval.
database="goodgarden" # De naam van de database waarmee je wilt verbinden.
)
# Controleer of de verbinding succesvol is opgezet.
if connection.is_connected():
# Als de verbinding succesvol is, retourneer het verbinding object.
# De print statement is uitgecommentarieerd, maar kan worden gebruikt voor debuggen.
# print("Connection gelukt!")
return connection
except Error as e:
# Als er een fout optreedt bij het maken van de verbinding, vang deze dan op en print een foutbericht.
print(f"Connection NIET gelukt! ${e}")
# Als de verbinding niet succesvol was, retourneer None.
return None

View File

@@ -0,0 +1,41 @@
import sys
import json
from db_connect import database_connect
from mysql.connector import Error
# Voeg de data uit het formulier toe aan de database
def insert_plant_name(plant_naam, plantensoort, plant_geteelt):
# Als er "true" is meegegeven als waarde dan komt in de database 1 anders 0 (false)
plant_geteelt_value = 1 if plant_geteelt.lower() == "true" else 0
try:
# Gebruik de verbinding die teruggegeven wordt door de database_connect functie uit de db_connect module
connection = database_connect()
# Je kunt de controle of de verbinding succesvol is rechtstreeks in de database_connect functie uitvoeren
# De cursor() zorgt ervoor dat er een verbinding met de database gelegd kan worden en de data gemanipuleerd
cursor = connection.cursor()
query = "INSERT INTO goodgarden.planten (plant_naam, plantensoort, plant_geteelt) VALUES (%s, %s, %s)"
cursor.execute(query, (plant_naam, plantensoort, plant_geteelt_value)) # "%s" wordt hier ingevuld doormiddel van de variable (parameter)
connection.commit()
print(json.dumps({"success": True}))
except Error as e:
print(json.dumps({"success": False, "error": str(e)}))
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
# Wordt alleen uitgevoerd als het een standalone script is (geen import!!!)
if __name__ == "__main__":
# Dit zijn de variables die door het JavaScript bestand (app.js) worden meegegeven --- NOTE: sys.argv[0] is altijd de naam van het script!
plant_naam = sys.argv[1]
plantensoort = sys.argv[2]
plant_geteelt = sys.argv[3]
# Call de function met de variables
insert_plant_name(plant_naam, plantensoort, plant_geteelt)

55
src/py/script/devices.py Normal file
View File

@@ -0,0 +1,55 @@
import json
from db_connect import database_connect
from paho.mqtt import subscribe
def on_message(client, userdata, message):
payload_str = message.payload.decode("utf-8")
data = json.loads(payload_str)
# Verbinding maken met de database
mydb = database_connect()
if mydb.is_connected():
# Creëren van een cursor object
mycursor = mydb.cursor()
# SQL query voor het invoegen van de gegevens
insert_query = """
INSERT INTO goodgarden.devices
(serial_number, name, label, last_seen, last_battery_voltage, device_id)
VALUES (%s, %s, %s, %s, %s, %s)
"""
# Verwerken van de gegevens uit het bericht
for record in data['results']:
serial_number = record.get('serial_number', '')
name = record.get('name', '')
label = record.get('label', '')
last_seen = record.get('last_seen', '')
last_battery_voltage = record.get('last_battery_voltage', '')
device_id = record.get("id", "")
print(f"Inserting data: serial_number={serial_number}, name={name}, label={label}, last_seen={last_seen}, last_battery_voltage={last_battery_voltage}, device_id={device_id}")
# Uitvoeren van de SQL query
mycursor.execute(insert_query, (serial_number, name, label, last_seen, last_battery_voltage, device_id))
# Commit de transacties naar de database
mydb.commit()
# Sluiten van de cursor en de databaseconnectie
mycursor.close()
mydb.close()
print("Data inserted into the database.")
else:
print("Failed to connect to the database.")
print(f"\033[92mMessage received on topic\033[0m {message.topic}: {data}")
if __name__ == "__main__":
topic = "goodgarden/devices"
subscribe.callback(on_message, topic)

View File

@@ -0,0 +1,8 @@
[
{
"id": 1,
"plant_naam": "Tomatenplant",
"plantensoort": "Groente",
"plant_geteelt": 1
}
]

View File

@@ -0,0 +1,20 @@
import json
# Importeer de subscribe module van paho.mqtt om te abonneren op MQTT topics.
from paho.mqtt import subscribe
# Definieer een callback functie die wordt aangeroepen wanneer een bericht wordt ontvangen.
def on_message(client, userdata, message):
# Decodeer het bericht payload van bytes naar een string met UTF-8 encoding.
payload_str = message.payload.decode("utf-8")
# Laad de JSON string in een Python dictionary.
data = json.loads(payload_str)
print(f"Message received on topic {message.topic}: {data}")
# Dit blok zorgt ervoor dat de code alleen wordt uitgevoerd als dit script rechtstreeks wordt uitgevoerd.
if __name__ == "__main__":
# Definieer het topic waarop geabonneerd wordt.
topic = "goodgarden/par_events"
# Roep de subscribe.callback functie aan met de on_message functie als callback om te luisteren naar berichten op het gespecificeerde topic.
subscribe.callback(on_message, topic)

45
src/py/script/planten.py Normal file
View File

@@ -0,0 +1,45 @@
import json
import mysql.connector
import os
from db_connect import database_connect # Gebruik deze geïmporteerde functie
# Functie om het absolute pad van de huidige directory te krijgen.
def get_current_directory():
return os.path.dirname(os.path.abspath(__file__))
# Functie om plantgegevens uit de database te halen en naar een JSON-bestand te schrijven.
def fetch_plant_and_write_to_json():
# Maak verbinding met de database.
connection = database_connect()
try:
# Maak een cursorobject aan met dictionary=True om rijen als woordenboeken op te halen.
cursor = connection.cursor(dictionary=True)
# Voer de SQL-query uit om gegevens op te halen.
cursor.execute("SELECT id, plant_naam, plantensoort, plant_geteelt FROM planten")
# Haal alle rijen op.
plants = cursor.fetchall()
# Verkrijg het absolute pad van de huidige directory.
current_directory = get_current_directory()
# Construeer het absolute pad voor het JSON-bestand.
json_file_path = os.path.join(current_directory, './json/plants.json')
# Schrijf de opgehaalde gegevens naar een JSON-bestand.
with open(json_file_path, 'w') as json_file:
json.dump(plants, json_file, indent=4)
except mysql.connector.Error as error:
# Print de fout als er iets misgaat bij het ophalen van de gegevens.
print(f"Error fetching data from MySQL table: {error}")
finally:
# Sluit de cursor en de verbinding, indien ze bestaan.
if 'cursor' in locals():
cursor.close()
if connection and connection.is_connected():
connection.close()
# Roept de functie aan om gegevens op te halen en naar JSON te schrijven.
fetch_plant_and_write_to_json()

View File

@@ -0,0 +1,21 @@
import json
# Importeer de subscribe functie uit de paho.mqtt module voor MQTT-communicatie.
from paho.mqtt import subscribe
# Definieer een callback functie die wordt aangeroepen wanneer een bericht wordt ontvangen.
def on_message(client, userdata, message):
# Decodeer de berichtpayload van bytes naar een UTF-8 gecodeerde string.
payload_str = message.payload.decode("utf-8")
# Converteer de JSON string naar een Python dictionary.
data = json.loads(payload_str)
# Print een bericht uit met de ontvangen data en het topic waarop het bericht is ontvangen.
print(f"Message received on topic {message.topic}: {data}")
# Het hoofdgedeelte van het script wordt alleen uitgevoerd als dit script als hoofdscript wordt gedraaid.
if __name__ == "__main__":
# Specificeer het MQTT-topic waarop geabonneerd moet worden.
topic = "goodgarden/relative_humidity"
# Abonneer op het opgegeven topic en roep de on_message functie aan als callback voor ontvangen berichten.
subscribe.callback(on_message, topic)

View File

@@ -0,0 +1,23 @@
import json
# Van de paho.mqtt bibliotheek wordt de subscribe module geïmporteerd.
# Deze module stelt ons in staat om ons te abonneren op MQTT-topics en berichten te ontvangen.
from paho.mqtt import subscribe
# Definitie van de functie on_message die wordt aangeroepen wanneer een bericht wordt ontvangen.
def on_message(client, userdata, message):
# De payload van het bericht, dat in bytes is, wordt gedecodeerd naar een UTF-8 string.
payload_str = message.payload.decode("utf-8")
# De gedecodeerde string, die in JSON-formaat is, wordt omgezet naar een Python dictionary.
data = json.loads(payload_str)
# Een bericht wordt geprint naar de console met de informatie over het ontvangen bericht.
print(f"Message received on topic {message.topic}: {data}")
# Dit blok zorgt ervoor dat de volgende code alleen uitgevoerd wordt als dit script direct wordt uitgevoerd.
if __name__ == "__main__":
# Het MQTT-topic waarop het script zich abonneert, gerelateerd aan de elektrische geleidbaarheid van de bodem.
topic = "goodgarden/soil_electric_conductivity"
# De subscribe.callback functie wordt aangeroepen met de on_message functie als callback.
# Dit start het proces van luisteren naar berichten op het gespecificeerde topic.
subscribe.callback(on_message, topic)

View File

@@ -0,0 +1,21 @@
import json
# Importeer de subscribe functie van paho.mqtt om te abonneren op MQTT berichten.
from paho.mqtt import subscribe
# Definieer een functie die wordt aangeroepen wanneer een bericht wordt ontvangen op het gespecificeerde MQTT topic.
def on_message(client, userdata, message):
# Decodeer de berichtpayload van bytes naar een UTF-8 string.
payload_str = message.payload.decode("utf-8")
# Laad de JSON string in een Python dictionary om gemakkelijk met de data te kunnen werken.
data = json.loads(payload_str)
# Print een bericht uit met de topicnaam en de ontvangen data.
print(f"Message received on topic {message.topic}: {data}")
# Het hoofdscript dat wordt uitgevoerd wanneer dit bestand direct wordt gerund.
if __name__ == "__main__":
# Specificeer het MQTT-topic waarop we willen abonneren. In dit geval luisteren we naar data over de relatieve permittiviteit van de bodem.
topic = "goodgarden/soil_relative_permittivity"
# Roep de subscribe.callback functie aan met de on_message functie als argument. Dit zorgt ervoor dat we voortdurend luisteren naar berichten op het gegeven topic.
subscribe.callback(on_message, topic)

View File

@@ -0,0 +1,21 @@
import json
# Importeer de subscribe functie van de paho.mqtt bibliotheek voor MQTT communicatie.
from paho.mqtt import subscribe
# Definieer de on_message functie die wordt uitgevoerd wanneer een bericht wordt ontvangen.
def on_message(client, userdata, message):
# Decodeer de payload van het bericht van bytes naar een UTF-8 gecodeerde string.
payload_str = message.payload.decode("utf-8")
# Converteer de JSON string naar een Python dictionary om het gemakkelijker te verwerken.
data = json.loads(payload_str)
# Print de ontvangen data samen met het topic waarop het bericht is ontvangen.
print(f"Message received on topic {message.topic}: {data}")
# Het hoofdgedeelte van het script dat wordt uitgevoerd als het script direct wordt aangeroepen.
if __name__ == "__main__":
# Definieer het MQTT-topic waarop we willen abonneren, in dit geval bodemtemperatuur.
topic = "goodgarden/soil_temperature"
# Start het abonneren op het opgegeven topic met de on_message functie als de callback.
subscribe.callback(on_message, topic)

460
src/py/static/css/style.css Normal file
View File

@@ -0,0 +1,460 @@
@import url("https://fonts.googleapis.com/css2?family=Akaya+Kanadaka&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Afacad:ital,wght@0,400..700;1,400..700&display=swap");
h1,
h2,
h3,
h4,
h5 {
font-family: "Akaya Kanadaka", system-ui;
margin: 0;
}
p,
td {
font-family: "Afacad", sans-serif;
color: black;
}
a {
text-decoration: none;
}
body {
background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)), url("../images/achtergrond.webp");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
font-family: "Afacad", sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
body .mainContainer {
width: 85vw;
height: 38rem;
background-color: rgba(255, 255, 255, 0.85);
border-radius: 40px;
padding: 2rem;
}
body .mainContainer .goodgarden-logo {
position: absolute;
width: 10vw;
left: 50%;
top: 4.1rem;
transform: translateX(-50%);
}
body .mainContainer .informatie-kas-main-container {
display: grid;
grid-template-columns: 5fr 7fr;
}
body .mainContainer .mainBorder {
padding: 1.25rem 1.5rem;
padding: 1rem 0;
height: 35rem;
border: solid 5px rgb(171, 211, 174);
border-radius: 40px;
}
body .mainContainer .mainBorder .pagina-titel {
font-size: 2rem;
margin-left: 1.5rem;
margin-bottom: 1rem;
}
body .mainContainer .mainBorder #sectie-1 {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 0 2.5rem 0 1rem;
position: relative;
}
body .mainContainer .mainBorder #sectie-1 h1 {
background-color: green;
}
body .mainContainer .mainBorder #sectie-1 .parent-algemeen-overzicht {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
border-radius: 40px;
padding: 1rem;
background-color: white;
}
body .mainContainer .mainBorder #sectie-1 .parent-algemeen-overzicht .algemeen-overzicht {
border: solid 2px rgb(171, 211, 174);
border-radius: 35px;
font-size: 1.25rem;
padding: 0.5rem 1rem;
}
body .mainContainer .mainBorder #sectie-1 .parent-algemeen-overzicht .algemeen-overzicht .table-informatie-kas {
width: 100%;
}
body .mainContainer .mainBorder #sectie-1 .parent-algemeen-overzicht .algemeen-overzicht .table-informatie-kas .tr-informatie-kas {
display: flex;
justify-content: space-between;
text-align: left;
}
body .mainContainer .mainBorder #sectie-1 .grafiek {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
border-radius: 40px;
padding: 1rem;
position: relative;
background-color: white;
}
body .mainContainer .mainBorder #sectie-1 .grafiek .grafiek-innerbox {
border: solid 2px rgb(171, 211, 174);
border-radius: 35px;
font-size: 1.25rem;
padding: 0 1rem 2.5rem;
height: 225px;
position: relative;
}
body .mainContainer .mainBorder #sectie-1 .grafiek .grafiek-innerbox h2 {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
body .mainContainer .mainBorder #sectie-1 .grafiek .grafiek-innerbox canvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
body .mainContainer .mainBorder .content {
display: grid;
grid-template-columns: 3fr 1fr 3fr;
height: 100%;
}
body .mainContainer .mainBorder .content .kant-links {
grid-column: 1;
}
body .mainContainer .mainBorder .content .kant-links #planten {
width: 100%;
border-collapse: collapse;
}
body .mainContainer .mainBorder .content .kant-links #planten td article {
height: 7rem;
width: 10rem;
padding: 0.6rem;
margin: 0.1rem;
margin-left: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: solid 3px rgb(171, 211, 174);
border-radius: 40px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
body .mainContainer .mainBorder .content .kant-links #planten td article #toevoegen {
height: 5rem;
width: 5rem;
}
body .mainContainer .mainBorder .content .kant-links #planten td article h2 {
color: gray;
}
body .mainContainer .mainBorder .content .kant-links #planten td article:hover {
background-color: lightgray;
cursor: pointer;
}
body .mainContainer .mainBorder .content .kant-rechts {
grid-column: 3;
}
body .mainContainer .mainBorder .content .kant-rechts #metingen {
border: solid 3px rgb(171, 211, 174);
border-radius: 40px;
}
body .mainContainer .mainBorder .content .kant-rechts #metingen #main-waardes {
display: flex;
justify-content: space-between;
padding: 0.5rem;
padding-bottom: 0;
width: 100%;
}
body .mainContainer .mainBorder .content .kant-rechts #metingen #main-waardes table {
display: flex;
justify-content: space-between;
width: 100%;
}
body .mainContainer .mainBorder .grid-column-2 {
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
border-radius: 40px;
padding: 1rem;
margin: 3.25rem 1rem 1.25rem 1rem;
background-color: white;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child {
border: solid 2px rgb(171, 211, 174);
border-radius: 35px;
height: 100%;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table {
padding: 1.5rem 2rem;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 2.5rem;
justify-content: space-around;
height: 90%;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table table tr td {
font-size: 1.05rem;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table .kas-table-1 tr td {
font-size: 1.25rem;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table .kas-table-2 {
position: relative;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table .kas-table-2::after, body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table .kas-table-2::before {
content: "";
position: absolute;
left: 0;
right: 0;
height: 1px;
width: 90%;
background: black;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table .kas-table-2::after {
bottom: -20px;
}
body .mainContainer .mainBorder .grid-column-2 .grid-2-child .parent-table .kas-table-2::before {
top: -20px;
}
.divider {
display: flex;
justify-content: center;
border: solid 1px rgb(171, 211, 174);
border-radius: 5px;
width: 80%;
margin: 0 auto;
}
.modal {
display: none;
position: fixed;
z-index: 999;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 1.25rem;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 30vw;
height: auto;
border: solid 2px #abd3ae;
border-radius: 10px;
}
.modal #plant-id {
font-size: 1.5rem;
}
.modal .close {
color: #aaa;
float: right;
font-size: 2rem;
font-weight: bold;
margin-top: -35px;
}
.modal .close:hover,
.modal .close:active {
color: black;
text-decoration: none;
cursor: pointer;
}
#myModal select,
#myModal input {
width: calc(100% - 20px);
padding: 10px;
margin-bottom: 15px;
border: 2px solid #abd3ae;
border-radius: 5px;
box-sizing: border-box;
}
#myModal .knop {
display: flex;
justify-content: space-between;
}
#myModal button {
width: 48%;
padding: 10px;
box-sizing: border-box;
height: 40px;
}
#myModal .knop-container {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
#myModal .knop-container input,
#myModal .knop-container button {
flex: 1;
margin: 0 5px;
padding: 10px;
box-sizing: border-box;
}
#myModal input[type=text],
#myModal select {
border: 2px solid #abd3ae;
padding: 10px;
border-radius: 17px;
box-sizing: border-box;
}
#myModal input[type=submit],
#myModal button {
color: #fff;
border: none;
border-radius: 15px;
cursor: pointer;
}
#myModal input[type=submit] {
background: linear-gradient(45deg, #abd3ae, #6ebf7d);
}
#myModal button {
background-color: white;
color: black;
}
.toevoeging {
background: linear-gradient(45deg, #abd3ae, #2ecc71);
}
.annulatie-knop {
background: linear-gradient(45deg, #ffcc00, #ff6600);
}
.switch-container {
display: flex;
align-items: center;
justify-content: flex-end;
}
.switch-wrapper {
margin-left: 20vw;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 25px;
}
.switch input[type=checkbox] {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 34px;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 3px;
bottom: 2px;
background-color: white;
border-radius: 50%;
transition: 0.4s;
}
input:checked + .slider {
background-color: rgb(171, 211, 174);
}
input:checked + .slider:before {
transform: translateX(23px);
}
.rechterkant {
display: flex;
justify-content: end;
}
.plant-container,
#modalButton {
background-color: white;
}
.switch-container {
position: absolute;
right: 75px;
}
.pagina-container {
position: relative;
align-items: end;
}
.close-button:hover {
background-color: rgb(171, 211, 174); /* Aangepaste hover kleur */
color: white; /* Tekstkleur wit op hover voor betere zichtbaarheid */
}
.container {
position: relative;
align-items: end;
}
.close-button {
position: absolute;
top: -90px;
right: -35px;
transform: translate(100%, 0%);
width: 50px;
height: 50px;
background-color: white;
color: rgb(171, 211, 174);
border-radius: 50%; /* Aangepast voor perfecte cirkel */
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 50px;
border: solid rgb(171, 211, 174) 2px;
font-weight: bold;
}
.back-button {
position: absolute;
top: -90px;
right: -25px;
width: 50px;
height: 50px;
background-color: white;
color: rgb(171, 211, 174);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 35px;
border: solid rgb(171, 211, 174) 2px;
font-weight: bold;
}
.back-button:hover {
background-color: rgb(171, 211, 174);
color: white;
}/*# sourceMappingURL=style.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"AAGQ,mFAAA;AACA,2GAAA;AAgCR;;;;;EAME,wCApCY;EAqCZ,SAAA;AClCF;;ADqCA;;EAGE,iCAzCU;EA0CV,YAAA;ACnCF;;ADsCA;EAEE,qBAAA;ACpCF;;ADuCA;EAEE,kHAAA;EACA,4BAAA;EACA,sBAAA;EACA,2BAAA;EACA,iCAxDU;EAyDV,aAAA;EACA,uBAAA;EACA,mBAAA;EACA,aAAA;EACA,SAAA;ACrCF;ADuCE;EAEE,WAAA;EACA,aAAA;EACA,2CAAA;EACA,mBAAA;EACA,aAAA;ACtCJ;ADwCI;EAEE,kBAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,2BAAA;ACvCN;AD0CI;EAEE,aAAA;EACA,8BAAA;ACzCN;AD4CI;EAEE,uBAAA;EACA,eAAA;EACA,aAAA;EACA,oCAAA;EACA,mBAAA;AC3CN;AD6CM;EAEE,eAAA;EACA,mBAAA;EACA,mBAAA;AC5CR;AD+CM;EAEE,aAAA;EACA,sBAAA;EACA,YAAA;EACA,wBAAA;EACA,kBAAA;AC9CR;ADgDQ;EAEE,uBAAA;AC/CV;ADkDQ;EAnGN,2CAAA;EAKA,mBAAA;EAkGQ,aAAA;EAEA,uBAAA;AClDV;ADoDU;EAhHR,oCAAA;EAeA,mBAAA;EAqGU,kBAAA;EACA,oBAAA;ACnDZ;ADqDY;EAEE,WAAA;ACpDd;ADsDc;EAEE,aAAA;EACA,8BAAA;EACA,gBAAA;ACrDhB;AD2DQ;EAhIN,2CAAA;EAKA,mBAAA;EA+HQ,aAAA;EACA,kBAAA;EACA,uBAAA;AC1DV;AD4DU;EA7IR,oCAAA;EAeA,mBAAA;EAkIU,kBAAA;EACA,sBAAA;EACA,aAAA;EACA,kBAAA;AC3DZ;AD6DY;EAEE,kBAAA;EACA,SAAA;EACA,2BAAA;AC5Dd;AD+DY;EAGE,kBAAA;EACA,QAAA;EACA,SAAA;EACA,gCAAA;AC/Dd;ADqEM;EAEE,aAAA;EACA,kCAAA;EACA,YAAA;ACpER;ADwEU;EAEE,cAAA;ACvEZ;ADyEY;EAEE,WAAA;EACA,yBAAA;ACxEd;AD4EgB;EAEE,YAAA;EACA,YAAA;EACA,eAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EACA,sBAAA;EACA,uBAAA;EACA,mBAAA;EACA,oCAAA;EACA,mBAAA;EACA,wCAAA;AC3ElB;AD6EkB;EAEE,YAAA;EACA,WAAA;AC5EpB;AD+EkB;EAEE,WAAA;AC9EpB;ADiFkB;EAEE,2BAAA;EACA,eAAA;AChFpB;ADuFU;EAEE,cAAA;ACtFZ;ADyFY;EAEE,oCAAA;EACA,mBAAA;ACxFd;AD0Fc;EAEE,aAAA;EACA,8BAAA;EACA,eAAA;EACA,iBAAA;EACA,WAAA;ACzFhB;AD2FgB;EAEE,aAAA;EACA,8BAAA;EACA,WAAA;AC1FlB;ADkGM;EAzPJ,2CAAA;EAKA,mBAAA;EAwPM,aAAA;EACA,iCAAA;EACA,uBAAA;ACjGR;ADmGQ;EAtQN,oCAAA;EAeA,mBAAA;EA2PQ,YAAA;AClGV;ADoGU;EAEE,oBAAA;EACA,aAAA;EACA,sBAAA;EACA,oBAAA;EACA,WAAA;EACA,6BAAA;EACA,WAAA;ACnGZ;ADyGgB;EAEE,kBAAA;ACxGlB;ADiHgB;EAEE,kBAAA;AChHlB;ADqHY;EAEE,kBAAA;ACpHd;ADsHc;EAGE,WAAA;EACA,kBAAA;EACA,OAAA;EACA,QAAA;EACA,WAAA;EACA,UAAA;EACA,iBAAA;ACtHhB;ADyHc;EAEE,aAAA;ACxHhB;AD2Hc;EAEE,UAAA;AC1HhB;;ADoIA;EACE,aAAA;EACA,uBAAA;EACA,oCAAA;EACA,kBAAA;EACA,UAAA;EACA,cAAA;ACjIF;;ADoIA;EACE,aAAA;EACA,eAAA;EACA,YAAA;EACA,SAAA;EACA,QAAA;EACA,gCAAA;EACA,iBAAA;EACA,gBAAA;EACA,wCAAA;EACA,WAAA;EACA,YAAA;EACA,yBAAA;EACA,mBAAA;ACjIF;;ADoIA;EAEE,iBAAA;AClIF;;ADqIA;EACE,WAAA;EACA,YAAA;EACA,eAAA;EACA,iBAAA;EACA,iBAAA;AClIF;;ADoIA;;EAEE,YAAA;EACA,qBAAA;EACA,eAAA;ACjIF;;ADoIA;;EAEE,wBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EACA,sBAAA;ACjIF;;ADoIA;EACE,aAAA;EACA,8BAAA;ACjIF;;ADoIA;EACE,UAAA;EACA,aAAA;EACA,sBAAA;EACA,YAAA;ACjIF;;ADoIA;EACE,aAAA;EACA,8BAAA;EACA,gBAAA;ACjIF;;ADoIA;;EAEE,OAAA;EACA,aAAA;EACA,aAAA;EACA,sBAAA;ACjIF;;ADoIA;;EAEE,yBAAA;EACA,aAAA;EACA,mBAAA;EACA,sBAAA;ACjIF;;ADoIA;;EAEE,WAAA;EACA,YAAA;EACA,mBAAA;EACA,eAAA;ACjIF;;ADoIA;EACE,oDAAA;ACjIF;;ADoIA;EACE,uBAAA;EACA,YAAA;ACjIF;;ADoIA;EACE,oDAAA;ACjIF;;ADoIA;EACE,oDAAA;ACjIF;;ADoIA;EACE,aAAA;EACA,mBAAA;EACA,yBAAA;ACjIF;;ADoIA;EACE,iBAAA;ACjIF;;ADoIA;EACE,kBAAA;EACA,qBAAA;EACA,WAAA;EACA,YAAA;ACjIF;;ADoIA;EACE,UAAA;EACA,QAAA;EACA,SAAA;ACjIF;;ADoIA;EACE,kBAAA;EACA,eAAA;EACA,MAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,sBAAA;EACA,mBAAA;EACA,gBAAA;ACjIF;;ADoIA;EACE,kBAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,uBAAA;EACA,kBAAA;EACA,gBAAA;ACjIF;;ADoIA;EACE,oCAhgBc;AC+XhB;;ADoIA;EACE,2BAAA;ACjIF;;ADmIA;EACE,aAAA;EACA,oBAAA;AChIF;;ADmIE;;EAGE,uBAAA;ACjIJ;;ADoIE;EACE,kBAAA;EACA,WAAA;ACjIJ;;ADmIA;EACE,kBAAA;EACA,gBAAA;AChIF;;ADmIA;EACE,oCA3hBc,EA2hBoB,2BAAA;EAClC,YAAA,EAAA,sDAAA;AChIF;;ADmIA;EACE,kBAAA;EACA,gBAAA;AChIF;;ADmIA;EAEE,kBAAA;EACA,UAAA;EACA,YAAA;EACA,8BAAA;EACA,WAAA;EACA,YAAA;EACA,uBAAA;EACA,yBA7iBc;EA8iBd,kBAAA,EAAA,mCAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;EACA,eAAA;EACA,eAAA;EACA,oCAAA;EACA,iBAAA;ACjIF;;ADoIA;EACE,kBAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,YAAA;EACA,uBAAA;EACA,yBA/jBc;EAgkBd,kBAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;EACA,eAAA;EACA,eAAA;EACA,oCAAA;EACA,iBAAA;ACjIF;;ADoIA;EACE,oCA3kBc;EA4kBd,YAAA;ACjIF","file":"style.css"}

View File

@@ -0,0 +1,592 @@
$primary-color: rgb(171, 211, 174);
$secondary-color: rgb(143, 188, 143);
$titel-color: rgb(46, 86, 81);
@import url("https://fonts.googleapis.com/css2?family=Akaya+Kanadaka&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Afacad:ital,wght@0,400..700;1,400..700&display=swap");
$font-titels: "Akaya Kanadaka", system-ui;
$font-text: "Afacad", sans-serif;
@mixin flexbox-center
{
display: flex;
justify-content: center;
}
@mixin groene-border
{
border: solid 2px $primary-color;
}
@mixin box-shadow
{
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
}
@mixin border-radius
{
border-radius: 40px;
}
@mixin inner-border-radius
{
border-radius: 35px;
}
h1,
h2,
h3,
h4,
h5
{
font-family: $font-titels;
margin: 0;
}
p,
td
{
font-family: $font-text;
color: black;
}
a
{
text-decoration: none;
}
body
{
background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)), url("../images/achtergrond.webp");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
font-family: $font-text;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
.mainContainer
{
width: 85vw;
height: 38rem;
background-color: rgb(255, 255, 255, 85%);
border-radius: 40px;
padding: 2rem;
.goodgarden-logo
{
position: absolute;
width: 10vw;
left: 50%;
top: 4.1rem;
transform: translateX(-50%);
}
.informatie-kas-main-container
{
display: grid;
grid-template-columns: 5fr 7fr;
}
.mainBorder
{
padding: 1.25rem 1.5rem;
padding: 1rem 0;
height: 35rem;
border: solid 5px $primary-color;
border-radius: 40px;
.pagina-titel
{
font-size: 2rem;
margin-left: 1.5rem;
margin-bottom: 1rem;
}
#sectie-1
{
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 0 2.5rem 0 1rem;
position: relative;
h1
{
background-color: green;
}
.parent-algemeen-overzicht
{
@include box-shadow;
@include border-radius;
padding: 1rem;
// margin-top: 1rem;
background-color: white;
.algemeen-overzicht
{
@include groene-border;
@include inner-border-radius;
font-size: 1.25rem;
padding: 0.5rem 1rem;
.table-informatie-kas
{
width: 100%;
.tr-informatie-kas
{
display: flex;
justify-content: space-between;
text-align: left;
}
}
}
}
.grafiek
{
@include box-shadow;
@include border-radius;
padding: 1rem;
position: relative;
background-color: white;
.grafiek-innerbox
{
@include groene-border;
@include inner-border-radius;
font-size: 1.25rem;
padding: 0 1rem 2.5rem;
height: 225px;
position: relative;
h2
{
position: absolute;
left: 50%;
transform: translateX(-50%);
}
canvas
{
// Zorgt ervoor dat de grafiek precies in eht midden komt
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
}
.content
{
display: grid;
grid-template-columns: 3fr 1fr 3fr;
height: 100%;
.kant
{
&-links
{
grid-column: 1;
#planten
{
width: 100%;
border-collapse: collapse;
td
{
article
{
height: 7rem;
width: 10rem;
padding: 0.6rem;
margin: 0.1rem;
margin-left: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: solid 3px $primary-color;
border-radius: 40px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
#toevoegen
{
height: 5rem;
width: 5rem;
}
h2
{
color: gray;
}
&:hover
{
background-color: lightgray;
cursor: pointer;
}
}
}
}
}
&-rechts
{
grid-column: 3;
// margin-right: 2rem;
#metingen
{
border: solid 3px $primary-color;
border-radius: 40px;
#main-waardes
{
display: flex;
justify-content: space-between;
padding: 0.5rem;
padding-bottom: 0;
width: 100%;
table
{
display: flex;
justify-content: space-between;
width: 100%;
}
}
}
}
}
}
.grid-column-2
{
@include box-shadow;
@include border-radius;
padding: 1rem;
margin: 3.25rem 1rem 1.25rem 1rem;
background-color: white;
.grid-2-child
{
@include groene-border;
@include inner-border-radius;
height: 100%;
.parent-table
{
padding: 1.5rem 2rem;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 2.5rem;
justify-content: space-around;
height: 90%;
table
{
tr
{
td
{
font-size: 1.05rem;
}
}
}
.kas-table-1
{
tr
{
td
{
font-size: 1.25rem;
}
}
}
.kas-table-2
{
position: relative;
&::after,
&::before
{
content: "";
position: absolute;
left: 0;
right: 0;
height: 1px;
width: 90%;
background: black;
}
&::after
{
bottom: -20px;
}
&::before
{
top: -20px;
}
}
}
}
}
}
}
}
.divider {
display: flex;
justify-content: center;
border: solid 1px $primary-color;
border-radius: 5px;
width: 80%;
margin: 0 auto;
}
.modal {
display: none;
position: fixed;
z-index: 999;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 1.25rem;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 30vw;
height: auto;
border: solid 2px #abd3ae;
border-radius: 10px;
}
.modal #plant-id
{
font-size: 1.5rem;
}
.modal .close {
color: #aaa;
float: right;
font-size: 2rem;
font-weight: bold;
margin-top: -35px;
}
.modal .close:hover,
.modal .close:active {
color: black;
text-decoration: none;
cursor: pointer;
}
#myModal select,
#myModal input {
width: calc(100% - 20px);
padding: 10px;
margin-bottom: 15px;
border: 2px solid #abd3ae;
border-radius: 5px;
box-sizing: border-box;
}
#myModal .knop {
display: flex;
justify-content: space-between;
}
#myModal button {
width: 48%;
padding: 10px;
box-sizing: border-box;
height: 40px;
}
#myModal .knop-container {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
#myModal .knop-container input,
#myModal .knop-container button {
flex: 1;
margin: 0 5px;
padding: 10px;
box-sizing: border-box;
}
#myModal input[type="text"],
#myModal select {
border: 2px solid #abd3ae;
padding: 10px;
border-radius: 17px;
box-sizing: border-box;
}
#myModal input[type="submit"],
#myModal button {
color: #fff;
border: none;
border-radius: 15px;
cursor: pointer;
}
#myModal input[type="submit"] {
background: linear-gradient(45deg, #abd3ae, #6ebf7d);
}
#myModal button {
background-color: white;
color: black;
}
.toevoeging {
background: linear-gradient(45deg, #abd3ae, #2ecc71);
}
.annulatie-knop {
background: linear-gradient(45deg, #ffcc00, #ff6600);
}
.switch-container {
display: flex;
align-items: center;
justify-content: flex-end;
}
.switch-wrapper {
margin-left: 20vw; // slider aan rechterkant
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 25px;
}
.switch input[type="checkbox"] {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 34px;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 3px;
bottom: 2px;
background-color: white;
border-radius: 50%;
transition: 0.4s;
}
input:checked + .slider {
background-color: $primary-color;
}
input:checked + .slider:before {
transform: translateX(23px);
}
.rechterkant {
display: flex;
justify-content: end;
}
.plant-container,
#modalButton
{
background-color: white;
}
.switch-container {
position: absolute;
right: 75px;
}
.pagina-container {
position: relative;
align-items: end;
}
.close-button:hover {
background-color: $primary-color; /* Aangepaste hover kleur */
color: white; /* Tekstkleur wit op hover voor betere zichtbaarheid */
}
.container {
position: relative;
align-items: end;
}
.close-button
{
position: absolute;
top: -90px;
right: -35px;
transform: translate(100%, 0%);
width: 50px;
height: 50px;
background-color: white;
color: $primary-color;
border-radius: 50%; /* Aangepast voor perfecte cirkel */
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 50px;
border: solid $primary-color 2px;
font-weight: bold;
}
.back-button {
position: absolute;
top: -90px;
right: -25px;
width: 50px;
height: 50px;
background-color: white;
color: $primary-color;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 35px;
border: solid $primary-color 2px;
font-weight: bold;
}
.back-button:hover {
background-color: $primary-color;
color: white
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,2 @@
[LocalizedFileNames]
achtergrond.webp=@achtergrond.webp,0

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

160
src/py/static/js/grafiek.js Normal file
View File

@@ -0,0 +1,160 @@
/**
* Class to handle the creation and manipulation of line charts on a canvas.
*/
class LineChart
{
/**
* Creates an instance of LineChart.
* @param {string} canvasId - The ID of the canvas element where the chart will be drawn.
*/
constructor(canvasId)
{
this.canvas = document.getElementById(canvasId);
if (this.canvas)
{
this.ctx = this.canvas.getContext("2d");
}
this.padding = 35; // Default padding around the graph
}
/**
* Clears the entire canvas, preparing it for a new drawing.
*/
clearCanvas()
{
if (this.canvas)
{
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
/**
* Draws a line chart on the canvas using the provided data.
* @param {Array<string>} xLabels - Labels for the x-axis.
* @param {Array<number>} data - Data points for the chart, one per x-label.
* @param {Array<number|string>} [yLabels=["", 10, 15, 20, 25, 30, 35, 40]] - Labels for the y-axis.
*/
drawChart(xLabels, data, yLabels = [0, 5, 10, 15, 20, 25, 30, 35, 40])
{
if (!this.canvas)
{
return; // Exit if canvas is not initialized
}
this.clearCanvas();
const graphWidth = this.canvas.width - this.padding * 2;
const graphHeight = this.canvas.height - this.padding * 2;
const maxValue = Math.max(...yLabels);
const graphScale = graphHeight / maxValue;
// Draw the axes
this.ctx.strokeStyle = "rgb(46, 86, 81)";
this.ctx.beginPath();
this.ctx.moveTo(this.padding, this.padding);
this.ctx.lineTo(this.padding, this.canvas.height - this.padding);
this.ctx.lineTo(this.canvas.width - this.padding, this.canvas.height - this.padding);
this.ctx.stroke();
// Calculate increments for plotting points
const xIncrement = graphWidth / (xLabels.length - 1);
// Plot the data points and draw the line
this.ctx.strokeStyle = "rgb(191, 215, 182)";
this.ctx.beginPath();
let xPos = this.padding;
let yPos = this.canvas.height - this.padding - (data[0] * graphScale);
this.ctx.moveTo(xPos, yPos);
for (let i = 1; i < data.length; i++)
{
xPos = this.padding + i * xIncrement;
yPos = this.canvas.height - this.padding - (data[i] * graphScale);
this.ctx.lineTo(xPos, yPos);
}
this.ctx.stroke();
// Draw points on the line and temperature labels
this.ctx.fillStyle = "rgb(46, 86, 81)";
for (let i = 0; i < data.length; i++)
{
xPos = this.padding + i * xIncrement;
yPos = this.canvas.height - this.padding - (data[i] * graphScale);
// Draw point for each data entry
this.ctx.beginPath();
this.ctx.arc(xPos, yPos, 3, 0, Math.PI * 2);
this.ctx.fill();
// Draw temperature values beside each point
this.ctx.fillStyle = "rgb(46, 86, 81)";
this.ctx.fillText(data[i] + "°C", xPos + 10, yPos - 10);
}
// Draw y-axis labels
this.ctx.textAlign = "right";
this.ctx.textBaseline = "middle"; // Center the text vertically
for (let i = 0; i < yLabels.length; i++)
{
const label = yLabels[i];
// Calculate the y position for the label
yPos = this.canvas.height - this.padding - (label * graphScale);
this.ctx.fillText(label, this.padding - 10, yPos);
}
// Draw x-axis labels
this.ctx.textAlign = "center";
this.ctx.textBaseline = "alphabetic"; // Default vertical alignment for text
for (let i = 0; i < xLabels.length; i++)
{
xPos = this.padding + i * xIncrement;
if (xLabels[i] !== "")
{
this.ctx.fillText(xLabels[i], xPos, this.canvas.height - this.padding + 20);
}
}
}
}
/**
* Fetches weather data from an API and draws a line chart with the fetched data.
* @param {LineChart} chart - The LineChart instance to draw on.
* @param {string} apiUrl - The URL to fetch the weather data from.
*/
function fetchWeatherDataAndDrawChart(chart, apiUrl) {
fetch(apiUrl)
.then(response => response.ok ? response.json() : Promise.reject('Network response was not ok.'))
.then(data => {
const dates = data.weather_forecast.map(item => {
// Split the date string
const [day, month, year] = item.dag.split('-').map(Number);
// Create a new Date object (Month is 0-based in JavaScript)
const date = new Date(year, month - 1, day);
// Map days to Dutch weekdays
const weekdays = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'];
const weekday = weekdays[date.getDay()];
// Format the date string
// return `${weekday} ${String(day).padStart(2, '0')}-${String(month).padStart(2, '0')}`;
return `${weekday}`;
});
const temperatures = data.weather_forecast.map(item => item.max_temp);
// Draw the chart with the transformed dates and temperatures
chart.drawChart(dates, temperatures);
})
.catch(error => console.error('There was a problem with the fetch operation:', error));
}
// Utility function to return the weekday in Dutch
function getWeekday(date) {
const options = { weekday: 'short' };
return date.toLocaleDateString('nl-NL', options);
}
const myChart = new LineChart("myCanvas");
fetchWeatherDataAndDrawChart(myChart, "http://127.0.0.1:5000/weather");

324
src/py/static/js/main.js Normal file
View File

@@ -0,0 +1,324 @@
// Importeer de ipcRenderer-module van Electron voor communicatie tussen processen.
// Dit maakt het mogelijk voor renderer-processen (webpagina's) om berichten te verzenden naar het hoofdproces.
const { ipcRenderer } = require("electron");
// Importeer Axios voor het maken van HTTP-verzoeken
const axios = require('axios');
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
function closeApplication()
{
if (confirm("Weet je zeker dat je de applicatie wilt sluiten?"))
{
window.close();
}
}
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
/**
* Functie om een modaal venster te openen.
* Deze functie stelt event listeners in voor het openen en sluiten van de modaal.
*/
function openModal()
{
// Verkrijg de elementen voor de modaal, de open-knop en de sluit-knop op basis van hun ID of klasse.
const modal = document.getElementById("myModal");
const button = document.getElementById("modalButton");
const close = document.getElementsByClassName("close")[0];
// Controleer of de elementen bestaan om fouten te voorkomen.
if (modal && button)
{
// Toon de modaal wanneer op de knop wordt geklikt.
button.onclick = function ()
{
modal.style.display = "block";
}
// Sluit de modaal wanneer op het 'sluiten' icoon wordt geklikt.
close.onclick = function ()
{
modal.style.display = "none";
}
// Sluit de modaal wanneer buiten de modaal wordt geklikt.
window.onclick = function (event)
{
if (event.target == modal)
{
modal.style.display = "none";
}
}
}
}
// Call openModal when DOM content is loaded
openModal();
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
/**
* *WAT IS DIT, WERKT HET? */
// Send a message to the main process to execute the Python script
ipcRenderer.send('run-python-script', ['some', 'arguments']);
ipcRenderer.on('python-script-response', (event, pythonData) =>
{
if (pythonData === 'error')
{
console.error('An error occurred while retrieving data from Python');
}
else
{
// Update HTML elements with data received from Python
document.getElementById('bodem-temperatuur').textContent = pythonData.bodemTemperatuur; // Adjust the property based on your actual Python response
}
});
// Listen for updates to HTML data from the main process
ipcRenderer.on('update-html-data', (event, data) =>
{
// Update the HTML with the received data
document.getElementById('batteryVoltage').innerText = data.batteryVoltage;
// Add similar lines for other data fields
});
// Trigger an event to request data update
ipcRenderer.send('request-update-data');
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
/**
* Functie om batterijdata op te halen wanneer de pagina laadt.
* Deze functie haalt data op met Axios en verwerkt deze vervolgens.
*/
function fetchBatteryData()
{
// Voer een GET-verzoek uit naar de server om batterijdata op te halen.
axios.get('http://127.0.0.1:5000')
.then(response =>
{
// Verwerk de ontvangen data.
const batteryData = response.data;
updateBatteryData(batteryData);
})
.catch(error =>
{
// Log eventuele fouten tijdens het ophalen.
console.error('Error fetching battery data:', error);
});
}
/**
* Functie om batterijdata op de pagina bij te werken.
* Deze functie update de HTML met de ontvangen batterijdata.
* @param {Array} batteryData - De ontvangen batterijdata.
*/
function updateBatteryData(batteryData)
{
// Check if on the specific page where the battery data should be updated
if (window.location.pathname.endsWith('index.html'))
{
// Update de data voor specifieke apparaten op basis van hun ID.
const sensor322Element = document.getElementById('deviceNumber-322');
const sensor256Element = document.getElementById('deviceNumber-256');
if (batteryData[1].device_id === 322 && sensor322Element)
{
sensor322Element.innerHTML = batteryData[1].device_id;
document.getElementById('voltage-322').innerHTML = batteryData[1].label;
document.getElementById('time-322').innerHTML = new Date(batteryData[1].last_seen * 1000).toLocaleTimeString();
document.getElementById('tevredenheid-322').innerHTML = batteryData[1].last_battery_voltage.toFixed(2);
}
if (batteryData[0].device_id === 256 && sensor256Element)
{
sensor256Element.innerHTML = batteryData[0].device_id;
document.getElementById('voltage-256').innerHTML = batteryData[0].label;
document.getElementById('time-256').innerHTML = new Date(batteryData[0].last_seen * 1000).toLocaleTimeString();
document.getElementById('tevredenheid-256').innerHTML = batteryData[0].last_battery_voltage.toFixed(2);
}
}
}
// Fetch battery data when the page loads
fetchBatteryData();
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠈⠀⠀⠀*/
function dynamischSensor()
{
if (window.location.pathname.endsWith('index.html') || window.location.pathname === '/')
{
const sensor1 = document.getElementById("sensor-1");
const sensor2 = document.getElementById("sensor-2");
if (sensor1 && sensor2)
{
sensor1.href = `sensor.html?id=${322}`;
sensor2.href = `sensor.html?id=${256}`;
}
else
{
console.log("Elementen 'sensor-1' of 'sensor-2' bestaan niet in de DOM.");
}
}
}
// Aanroepen van de functie
dynamischSensor();
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
PLANTEN ⠈⠀⠀⠀*/
function fetchPlantenData()
{
// Gebruik Axios om een GET-verzoek te versturen naar de planten endpoint.
axios.get('http://127.0.0.1:5000/planten')
.then(response =>
{
// Verwerk de ontvangen data.
const plantenData = response.data;
updatePlantenData(plantenData);
})
.catch(error =>
{
// Log eventuele fouten tijdens het ophalen.
console.error('Error fetching planten data:', error);
});
}
function getPlantIdFromUrl()
{
// Maak een URL-object van de huidige locatie.
const currentUrl = new URL(window.location.href);
// Gebruik de URLSearchParams API om de query parameters te verwerken.
const searchParams = currentUrl.searchParams;
// Haal de 'id' query parameter op.
return searchParams.get('id'); // Dit zal een string retourneren of null als het niet bestaat.
}
function updatePlantenData(plantenData)
{
// Verkrijg de plant ID uit de URL.
const plantId = parseInt(getPlantIdFromUrl(), 10);
// Vind de plant met die ID in de ontvangen JSON-data.
const gevondenPlant = plantenData.find(plant => plant.id === plantId);
// Update de titel van de pagina met de naam van de gevonden plant.
if (gevondenPlant)
{
document.title = gevondenPlant.plant_naam;
document.querySelector(".plant-titel").textContent = gevondenPlant.plant_naam;
// console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
}
else
{
console.log(`Geen plant gevonden met ID ${plantId}`);
}
}
// Roep fetchPlantenData aan ergens waar het logisch is binnen je applicatielogica, bijvoorbeeld na het laden van de pagina of na een gebruikersactie.
fetchPlantenData();
/*⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀*/
// Get the battery voltage
// const batteryVoltage = parseFloat(document.getElementById('battery-voltage'));
// // Select the img element
// const batteryImage = document.getElementById('battery-image');
// // Check the battery voltage and decide whether to show the image
// if (batteryVoltage < 3.0) {
// // Battery is lower than 3.0 volts, show the empty battery image
// batteryImage.src = '../static/img/warning-logo.png';
// } else if (batteryVoltage > 4.2) {
// // Battery is higher than 4.2 volts, do not show the image
// batteryImage.style.display = 'none';
// } else {
// // Battery voltage is within the desired range, hide the image
// batteryImage.style.display = 'none';
// }

View File

@@ -0,0 +1,115 @@
// Definitie van de Plant klasse.
class Plant {
// Constructor om een Plant object te initialiseren met data van een dataObject.
constructor(dataObject) {
// Initialiseren van de eigenschappen van de plant.
this.id = dataObject.id;
this.plantNaam = dataObject.plant_naam; // Naam van de plant.
this.plantensoort = dataObject.plantensoort; // Soort van de plant.
this.plantGeteelt = dataObject.plant_geteelt; // Geteelt status van de plant.
}
}
class PlantGrid {
// Constructor om een PlantGrid object te initialiseren.
constructor() {
this.grid = []; // De datastructuur die het raster van planten bevat.
this.cols = 2; // Aantal kolommen in het raster.
this.rows = 4; // Aantal rijen in het raster (inclusief de rij voor de "Toevoegen" knop).
// Initialiseren van het raster met null waarden.
for (let i = 0; i < this.rows; i++) {
this.grid[i] = new Array(this.cols).fill(null);
}
// Laadt JSON data van de server.
this.loadData();
}
// Methode om data te laden.
loadData() {
fetch('../script/json/plants.json')
.then(response => {
// Controleer of de netwerkrespons ok is.
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // Parse de JSON uit de respons.
})
.then(data => {
// Filter de data op planten die geteeld zijn.
const filteredData = data.filter(plantObject => plantObject.plant_geteelt === 1);
// Vul het raster met plantobjecten.
filteredData.slice(0, 8).forEach((plantObject, index) => {
const plant = new Plant(plantObject); // Maak een nieuw Plant object.
const col = index % this.cols; // Bereken de kolomindex.
const row = Math.floor(index / this.cols); // Bereken de rijindex.
this.grid[row][col] = plant; // Voeg de plant toe aan het raster.
});
// Toon het raster in de HTML tabel met id "planten".
this.displayGrid();
})
.catch(error => console.error('Error loading data:', error)); // Log eventuele fouten.
}
displayGrid() {
const plantenTable = document.getElementById("planten");
let itemCount = 0; // Counter for the number of items in the grid
this.grid.forEach((row, rowIndex) => {
const tr = document.createElement("tr");
row.forEach((plant, colIndex) => {
const td = document.createElement("td");
if (itemCount < 8) {
if (plant) {
// Handle regular plant items
const link = document.createElement("a");
link.href = `planteninfo.html?id=${plant.id}`; // Link naar de planteninfo pagina met plant id als query parameter
const article = document.createElement("article");
article.classList.add("plant-container");
link.appendChild(article); // Voeg het artikel toe aan de link
const img = article.appendChild(document.createElement("img"));
img.src = "../static/images/icon_awesome-apple-alt.png";
const h2 = article.appendChild(document.createElement("h2"));
h2.classList.add("plant-naam");
h2.textContent = plant.plantNaam;
td.appendChild(link); // Voeg de link toe aan de td
itemCount++;
} else if (rowIndex === this.rows - 1 && colIndex === this.cols - 1 && itemCount <= 7) {
// Handle the "Add" button
const article = document.createElement("article");
const img = article.appendChild(document.createElement("img"));
img.src = "../static/images/plus.png";
img.id = "toevoegen";
img.alt = "Add";
article.id = "modalButton";
article.onclick = openModal;
td.appendChild(article);
itemCount++;
}
}
tr.appendChild(td);
});
plantenTable.appendChild(tr);
});
}
}
document.addEventListener("DOMContentLoaded", () => {
const plantGrid = new PlantGrid();
});

View File

@@ -0,0 +1 @@
/*Hier komt alle code die te maken heeft met de "dynamische" planten pagina's*/

151
src/py/templates/index.html Normal file
View File

@@ -0,0 +1,151 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="De pagina voor de verschillende sensoren" />
<meta name="author" content="B. Diker, A. Oomen, J. Doekhi, R. van Putten, M. Çifçi" />
<meta name="keywords" content="Homepage / Dashboard / Algoritme / Voeding / Planten / Data / Kas" />
<title>Dashboard</title>
<link rel="stylesheet" href="../static/css/style.css">
<script src="../static/js/main.js" defer></script>
<script src="../static/js/planten.class.js"></script>
</head>
<body>
<section class="mainContainer">
<div class="pagina-container">
<div class="close-button" onclick="closeApplication()">&times;</div>
</div>
<article>
<a href="index.html">
<img src="../static/images/logo.png" class="goodgarden-logo">
</a>
</article>
<section class="mainBorder">
<section class="content">
<section class="kant-links">
<table id="planten">
</table>
<div class="formulier">
<div id="myModal" class="modal" style="display: none;">
<h1 id="plant-id">Plant Toevoegen</h1>
<span class="close">&times;</span>
<form action="http://localhost:3000/submit-form" method="post" onsubmit="return addplant()">
<label for="plantNaam">Naam van de plant</label>
<input type="text" name="plant_naam" id="plantNaam">
<label for="plantensoort">Soort van de plant</label>
<input type="text" name="plantensoort" id="plantensoort">
<label for="aanwezig">Aanwezig
<input type="radio" name="plant_geteelt" id="aanwezig" value="true">
</label>
<label for="afwezig">Afwezig
<input type="radio" name="plant_geteelt" id="afwezig" value="false">
</label>
<label for="ontvangenMeldingen">Meldingen ontvangen</label>
<select name="ontvangen_meldingen">
<option value="true">Ja</option>
<option value="false">Nee</option>
</select>
<section class="knop-container">
<button class="annulatie-knop" type="button" onclick="closeOverlay()">Annuleren</button>
<input type="submit" value="Submit">
</section>
</form>
</div>
</div>
<div id="overlay" onclick="closeOverlay"></div>
</section>
<section class="kant-rechts">
<!-- Sectie voor sensorinformatie -->
<section id="sectie-1">
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<a href="./pomp_informatie.html" id="kas-informatie">
<table class="table-informatie-kas">
<tr class="tr-informatie-pomp">
<td>Pomp Status:</td>
<td id="">Uitstekend!</td>
</tr>
<tr class="tr-informatie-pomp">
<td>Volgende Irrigatie:</td>
<td id="">24:00</td>
</tr>
<tr class="tr-informatie-pomp">
<td>Laatste Irrigatie:</td>
<td id="">18:00</td>
</tr>
<tr class="tr-informatie-pomp">
<td>Waterverbruik:</td>
<td id="">0.5 L/u</td>
</tr>
</table>
<a/>
</article>
</article>
<!-- Container voor algemene overzichtinformatie -->
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<!-- Link naar pagina met meer details over de sensoren -->
<a href="./sensor.html" id="sensor-1">
<!-- Tabel met sensorinformatie -->
<table class="table-informatie-kas">
<tr class="tr-informatie-kas">
<td>Sensor:</td>
<td id="deviceNumber-322"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Locatie:</td>
<td id="voltage-322"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Batterijvoltage:</td>
<td id="tevredenheid-322"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Dagen resterend:</td>
<td id="time-322"></td>
</tr>
</table>
<a/>
</article>
</article>
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<a href="sensoren.html" id="sensor-2">
<table class="table-informatie-kas">
<tr class="tr-informatie-kas">
<td>Sensor:</td>
<td id="deviceNumber-256"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Locatie:</td>
<td id="voltage-256"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Batterijvoltage:</td>
<td id="tevredenheid-256"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Dagen resterend:</td>
<td id="time-256"></td>
</tr>
</table>
<a/>
</article>
</article>
</section>
</section>
</section>
</section>
</section>
</section>
</body>
</html>

View File

@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="De pagina voor de verschillende sensoren" />
<meta name="author" content="B. Diker, A. Oomen, J. Doekhi, R. van Putten, M. Çifçi" />
<meta name="keywords" content="Kas informatie / Algoritme / Voeding / Planten / Data / Kas" />
<link rel="stylesheet" href="../static/css/style.css" />
<script src="../static/js/main.js" defer></script>
<script src="../static/js/grafiek.js" defer></script>
<title>Informatie Kas</title>
</head>
<body>
<section class="mainContainer">
<article>
<div class="pagina-container">
<div class="close-button" onclick="closeApplication()">&times;</div>
</div>
<div class="container">
<a href="index.html" class="back-button">&#8592;</a>
</div>
<a href="index.html">
<img src="../static/images/logo.png" class="goodgarden-logo">
</a>
</article>
<!-- De container die de inhoud van de informatiepagina bevat -->
<section class="mainBorder informatie-kas-main-container">
<article>
<h1 class="pagina-titel-kas">Informatie Kas</h1>
<!-- Sectie met overzicht van de kas gegevens -->
<section id="sectie-1">
<!-- Weergave van sensor data en kascondities -->
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<!-- Tabel voor het weergeven van sensorgegevens -->
<table class="table-informatie-kas">
<tr class="tr-informatie-kas">
<td>Device</td>
<td id="deviceNumber"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Batterij Voltage</td>
<td id="voltage"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Tijden</td>
<td id="time"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Zulu</td>
<td id="zulu"></td>
</tr>
</table>
</article>
</article>
<!-- Weergave van zonlichtdata in een grafiek -->
<article class="grafiek">
<article class="grafiek-innerbox">
<h2>Zonlicht</h2>
<canvas
id="myCanvas"
class="canvas-informatie-kas"
width="275"
height="275"
></canvas>
</article>
</article>
</section>
</article>
<!-- Sectie met aanvullende informatie over de kas in tabelvorm -->
<article class="grid-column-2">
<article class="grid-2-child">
<section class="parent-table">
<!-- Verschillende tabellen voor gedetailleerde informatie -->
<table class="kas-table-1">
<!-- Rijen met specifieke kas informatie, zoals aantal geplante en geoogste planten -->
<tr>
<td>Aantal geplant:</td>
<td id="totale_planten">Loading...</td>
</tr>
<tr>
<td>Succesvolle Oogst:</td>
<td>2</td>
</tr>
<tr>
<td>Gefaalde Oogst:</td>
<td>2</td>
</tr>
</table>
<table class="kas-table-2">
<!-- Rijen met klimaat- en bodemcondities -->
<tr>
<td>Warmste Maand:</td>
<td>n.v.t.</td>
</tr>
<tr>
<td>koudste Maand:</td>
<td>December</td>
</tr>
<tr>
<td>Gemiddelde Bodemtemp.:</td>
<td id="bodem-temperatuur">2˚C</td>
</tr>
<tr>
<td>Gemiddelde Uren Zonlicht:</td>
<td id="battery_voltage">2u</td>
</tr>
</table>
<table class="kas-table-3">
<!-- Rijen met irrigatie- en bemestingschema's -->
<tr>
<td>Laatste Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Aankomende Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Laatste Bemesting</td>
<td>2d</td>
</tr>
<tr>
<td>Aankomende Bemesting:</td>
<td>2w</td>
</tr>
</table>
</section>
</article>
</article>
</section>
</section>
</body>
</html>

View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="De pagina voor de verschillende sensoren" />
<meta name="author" content="B. Diker, A. Oomen, J. Doekhi, R. van Putten, M. Çifçi" />
<meta name="keywords" content="Planten informatie / Algoritme / Voeding / Planten / Data / Kas" />
<link rel="stylesheet" href="../static/css/style.css" />
<script src="../static/js/main.js" defer></script>
<script src="../static/js/grafiek.js" defer></script>
<title>Planten</title>
</head>
<body>
<section class="mainContainer">
<article>
<div class="pagina-container">
<div class="close-button" onclick="closeApplication()">&times;</div>
</div>
<div class="container">
<a href="index.html" class="back-button">&#8592;</a>
</div>
<a href="index.html">
<img src="../static/images/logo.png" class="goodgarden-logo">
</a>
</article>
<!-- Bevat de primaire inhoud van de pagina -->
<section class="mainBorder informatie-kas-main-container">
<article>
<h1 class="pagina-titel plant-titel">&nbsp;</h1>
<!-- Bevat een overzicht van algemene informatie en grafieken -->
<section id="sectie-1">
<!-- Weergave van belangrijke kasstatistieken -->
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<!-- Tabel met statische data over de kas -->
<table class="table-informatie-kas">
<!-- Rijen met specifieke informatie zoals dagen tot oogst en tevredenheid -->
<tr class="tr-informatie-kas">
<td>Aanwezig in kas</td>
<div class="switch-container">
<form action="{{ url_for('toggle') }}" method="POST">
<label class="switch">
<input type="checkbox" name="schakelaar" {% if schakelaar.status %}checked{% endif %}>
<span class="slider"></span>
</label>
</form>
</div>
</tr>
<tr class="tr-informatie-kas">
<td>Dagen tot Oogst</td>
<td>12</td>
</tr>
<tr class="tr-informatie-kas">
<td>Dagen in Kas</td>
<td>2</td>
</tr>
<tr class="tr-informatie-kas">
<td>Tevredenheid</td>
<td>80%</td>
</tr>
</table>
</article>
</article>
<!-- Sectie voor het weergeven van een grafiek over zonlicht -->
<article class="grafiek">
<article class="grafiek-innerbox">
<h2>Zonlicht</h2>
<canvas
id="myCanvas"
class="canvas-informatie-kas"
width="275"
height="275"
></canvas>
</article>
</article>
</section>
</article>
<!-- Presenteert aanvullende informatie in tabelvorm -->
<article class="grid-column-2">
<article class="grid-2-child">
<section class="parent-table">
<!-- Meerdere tabellen met informatie over geplante aantallen, oogstresultaten en klimaatcondities -->
<table class="kas-table-1">
<!-- Details over geplant aantal, succesvolle en gefaalde oogst -->
<tr>
<td>Aantal geplant:</td>
<td>2</td>
</tr>
<tr>
<td>Succesvolle Oogst:</td>
<td>2</td>
</tr>
<tr>
<td>Gefaalde Oogst:</td>
<td>2</td>
</tr>
</table>
<table class="kas-table-2">
<!-- Informatie over de klimaatcondities zoals de warmste en koudste maand, gemiddelde bodemtemperatuur en zonlichturen -->
<tr>
<td>Warmste Maand:</td>
<td>n.v.t.</td>
</tr>
<tr>
<td>koudste Maand:</td>
<td>December</td>
</tr>
<tr>
<td>Gemiddelde Bodemtemp.:</td>
<td id="bodem-temperatuur">2˚C</td>
</tr>
<tr>
<td>Gemiddelde Uren Zonlicht:</td>
<td id="battery_voltage">2u</td>
</tr>
</table>
<table class="kas-table-3">
<!-- Schema voor irrigatie en bemesting -->
<tr>
<td>Laatste Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Aankomende Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Laatste Bemesting</td>
<td>2d</td>
</tr>
<tr>
<td>Aankomende Bemesting:</td>
<td>2w</td>
</tr>
</table>
</section>
</article>
</article>
</section>
</section>
</body>
</html>

View File

@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Een pagina speciaal voor de pomp!" />
<meta name="author" content="B. Diker, A. Oomen, J. Doekhi, R. van Putten, M. Çifçi" />
<meta name="keywords" content="Pomp / Algoritme / Voeding / Planten / Data / Kas" />
<link rel="stylesheet" href="../static/css/style.css" />
<script src="../static/js/main.js" defer></script>
<script src="../static/js/grafiek.js" defer></script>
<title>Informatie Pomp</title>
</head>
<body>
<section class="mainContainer">
<article>
<div class="pagina-container">
<div class="close-button" onclick="closeApplication()">&times;</div>
</div>
<div class="container">
<a href="index.html" class="back-button">&#8592;</a>
</div>
<a href="index.html">
<img src="../static/images/logo.png" class="goodgarden-logo">
</a>
</article>
<!-- De container die de inhoud van de informatiepagina bevat -->
<section class="mainBorder informatie-kas-main-container">
<article>
<h1 class="pagina-titel">Informatie Pomp</h1>
<!-- Sectie met overzicht van de kas gegevens -->
<section id="sectie-1">
<!-- Weergave van sensor data en kascondities -->
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<!-- Tabel voor het weergeven van sensorgegevens -->
<table class="table-informatie-kas">
<tr class="tr-informatie-kas">
<td>Pomp Status:</td>
<td id="deviceNumber">Uitstekend!</td>
</tr>
<tr class="tr-informatie-kas">
<td>Waterverbruik:</td>
<td id="voltage">0.5 L/u</td>
</tr>
<tr class="tr-informatie-kas">
<td>Volgende Irrigatie</td>
<td id="time">24:00</td>
</tr>
<tr class="tr-informatie-kas">
<td>Laatste Irrigatie:</td>
<td id="zulu">18:00</td>
</tr>
</table>
</article>
</article>
<!-- Weergave van zonlichtdata in een grafiek -->
<article class="grafiek">
<article class="grafiek-innerbox">
<h2>Temperatuur</h2>
<canvas
id="myCanvas"
class="canvas-informatie-kas"
width="275"
height="275"
></canvas>
</article>
</article>
</section>
</article>
<!-- Sectie met aanvullende informatie over de kas in tabelvorm -->
<article class="grid-column-2">
<article class="grid-2-child">
<section class="parent-table">
<!-- Verschillende tabellen voor gedetailleerde informatie -->
<table class="kas-table-1">
<!-- Rijen met specifieke kas informatie, zoals aantal geplante en geoogste planten -->
<tr>
<td>Aantal geplant:</td>
<td id="totale_planten">Loading...</td>
</tr>
<tr>
<td>Succesvolle Oogst:</td>
<td>2</td>
</tr>
<tr>
<td>Gefaalde Oogst:</td>
<td>2</td>
</tr>
</table>
<table class="kas-table-2">
<!-- Rijen met klimaat- en bodemcondities -->
<tr>
<td>Warmste Maand:</td>
<td>n.v.t.</td>
</tr>
<tr>
<td>koudste Maand:</td>
<td>December</td>
</tr>
<tr>
<td>Gemiddelde Bodemtemp.:</td>
<td id="bodem-temperatuur">2˚C</td>
</tr>
<tr>
<td>Gemiddelde Uren Zonlicht:</td>
<td id="battery_voltage">2u</td>
</tr>
</table>
<table class="kas-table-3">
<!-- Rijen met irrigatie- en bemestingschema's -->
<tr>
<td>Laatste Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Aankomende Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Laatste Bemesting</td>
<td>2d</td>
</tr>
<tr>
<td>Aankomende Bemesting:</td>
<td>2w</td>
</tr>
</table>
</section>
</article>
</article>
</section>
</section>
</body>
</html>

View File

@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="De pagina voor de verschillende sensoren" />
<meta name="author" content="B. Diker, A. Oomen, J. Doekhi, R. van Putten, M. Çifçi" />
<meta name="keywords" content="Sensor / Algoritme / Voeding / Planten / Data / Kas" />
<link rel="stylesheet" href="../static/css/style.css" />
<script src="../static/js/main.js" defer></script>
<script src="../static/js/grafiek.js" defer></script>
<title>Informatie Sensor</title>
</head>
<body>
<section class="mainContainer">
<article>
<div class="pagina-container">
<div class="close-button" onclick="closeApplication()">&times;</div>
</div>
<div class="container">
<a href="index.html" class="back-button">&#8592;</a>
</div>
<a href="index.html">
<img src="../static/images/logo.png" class="goodgarden-logo">
</a>
</article>
<!-- De container die de inhoud van de informatiepagina bevat -->
<section class="mainBorder informatie-kas-main-container">
<article>
<h1 class="pagina-titel sensor-titel">Informatie Kas</h1>
<!-- Sectie met overzicht van de kas gegevens -->
<section id="sectie-1">
<!-- Weergave van sensor data en kascondities -->
<article class="parent-algemeen-overzicht">
<article class="algemeen-overzicht">
<!-- Tabel voor het weergeven van sensorgegevens -->
<table class="table-informatie-kas">
<tr class="tr-informatie-kas">
<td>Device</td>
<td id="deviceNumber"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Batterij Voltage</td>
<td id="voltage"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Tijden</td>
<td id="time"></td>
</tr>
<tr class="tr-informatie-kas">
<td>Zulu</td>
<td id="zulu"></td>
</tr>
</table>
</article>
</article>
<!-- Weergave van zonlichtdata in een grafiek -->
<article class="grafiek">
<article class="grafiek-innerbox">
<h2>Zonlicht</h2>
<canvas
id="sensorCanvas"
class="canvas-informatie-kas"
width="275"
height="275"
></canvas>
</article>
</article>
</section>
</article>
<!-- Sectie met aanvullende informatie over de kas in tabelvorm -->
<article class="grid-column-2">
<article class="grid-2-child">
<section class="parent-table">
<!-- Verschillende tabellen voor gedetailleerde informatie -->
<table class="kas-table-1">
<!-- Rijen met specifieke kas informatie, zoals aantal geplante en geoogste planten -->
<tr>
<td>Aantal geplant:</td>
<td id="totale_planten">Loading...</td>
</tr>
<tr>
<td>Succesvolle Oogst:</td>
<td>2</td>
</tr>
<tr>
<td>Gefaalde Oogst:</td>
<td>2</td>
</tr>
</table>
<table class="kas-table-2">
<!-- Rijen met klimaat- en bodemcondities -->
<tr>
<td>Warmste Maand:</td>
<td>n.v.t.</td>
</tr>
<tr>
<td>koudste Maand:</td>
<td>December</td>
</tr>
<tr>
<td>Gemiddelde Bodemtemp.:</td>
<td id="bodem-temperatuur">2˚C</td>
</tr>
<tr>
<td>Gemiddelde Uren Zonlicht:</td>
<td id="battery_voltage">2u</td>
</tr>
</table>
<table class="kas-table-3">
<!-- Rijen met irrigatie- en bemestingschema's -->
<tr>
<td>Laatste Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Aankomende Irrigatie:</td>
<td>2u</td>
</tr>
<tr>
<td>Laatste Bemesting</td>
<td>2d</td>
</tr>
<tr>
<td>Aankomende Bemesting:</td>
<td>2w</td>
</tr>
</table>
</section>
</article>
</article>
</section>
</section>
</body>
</html>