diff --git a/ReadMe.md b/ReadMe.md index 71b8b79..ba3dfd0 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -20,9 +20,6 @@ Zorg dat je in de hoofdmap "GoodGarden" zit. Kijk in je path: "../GoodGarden". A 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 diff --git a/app.js b/app.js index b4d2392..44fa17c 100644 --- a/app.js +++ b/app.js @@ -1,50 +1,68 @@ +// 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 })); -// Define a route for form POST requests -server.post("/submit-form", (req, res) => { +// 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], }; - // Execute Python script with plant name as an argument - 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"); - } + // 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"); + } + }); }); -// Start the server +// Definieert de poort waarop de server luistert. const PORT = 3000; -server.listen(PORT, () => { +server.listen(PORT, () => +{ console.log(`Server is listening on port ${PORT}`); }); -let mainWindow; +let mainWindow; // Referentie naar het hoofdvenster van de Electron app. -// Create the Electron application window -function createWindow() { +// Creëert het hoofdvenster van de Electron app. +function createWindow() +{ mainWindow = new BrowserWindow({ width: 1280, height: 800, @@ -58,42 +76,51 @@ function createWindow() { }, }); - mainWindow.loadFile( - path.join(__dirname, "src", "py", "templates", "index.html") - ); + mainWindow.loadFile(path.join(__dirname, "src", "py", "templates", "index.html")); - // IPC event listeners for running Python scripts and updating HTML data + // Stelt IPC Main Listeners in voor communicatie tussen hoofdproces en renderproces. setupIpcMainListeners(); } -// Start the Electron app +// Wanneer de app klaar is, wordt het hoofdvenster gecreëerd. app.whenReady().then(createWindow); -// Close the app when all windows are closed, except on macOS -app.on("window-all-closed", () => { - if (process.platform !== "darwin") { +// Sluit de app af wanneer alle vensters gesloten zijn (behalve op macOS). +app.on("window-all-closed", () => +{ + if (process.platform !== "darwin") + { app.quit(); } }); -// Re-create a window in the app when the dock icon is clicked and there are no other windows open. -app.on("activate", () => { - if (BrowserWindow.getAllWindows().length === 0) { +// 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(); } }); -function setupIpcMainListeners() { - ipcMain.on("run-python-script", (event, args) => { +// + +// 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, }; - - // The actual script path and event replies should be tailored to your application's needs + // Hier zou je PythonShell.run met deze opties aanroepen. }); - ipcMain.on("request-update-data", (event, args) => { + // 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", @@ -103,7 +130,9 @@ function setupIpcMainListeners() { event.reply("update-data-result", { databaseData }); }); - ipcMain.on("update-html-data", (event, data) => { + // 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); }); -} +} \ No newline at end of file diff --git a/app.py b/app.py index 0f1b667..f149a30 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,12 @@ 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", @@ -14,39 +14,40 @@ def database_connect(): database="goodgarden" ) return connection - + except Exception as e: - + print("Database connection failed:", e) return None - - + + # Function to get data from the MySQL database def get_database_data(): 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 = "05ddd06644" location = "Leiden" @@ -62,15 +63,16 @@ def get_weather(): return jsonify({"error": "Kon weerdata niet ophalen"}) live_weather = weather_response.get('liveweer', []) - today_forecast = weather_response.get('verwachting_vandaag', []) + 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 {}, - "today_forecast": today_forecast[0] if today_forecast else {} + "weather_forecast": weather_forecast, + "day_forecast": day_forecast # Voeg de dagverwachtingen toe aan de weerdata } return jsonify(weather_data) - + if __name__ == "__main__": - app.run(host="127.0.0.1", port=5000) - + app.run(host="127.0.0.1", port=5000) \ No newline at end of file diff --git a/mqtt/db_connect.py b/mqtt/db_connect.py index bc4922c..eda2e93 100644 --- a/mqtt/db_connect.py +++ b/mqtt/db_connect.py @@ -1,16 +1,36 @@ +# Importeer de benodigde modules voor MySQL connectiviteit. import mysql.connector from mysql.connector import Error def database_connect(): + """ + Maakt verbinding met de MySQL database. + + Probeert een verbinding met de MySQL-database op te zetten met behulp van + de mysql.connector.connect methode, gebruikmakend van de database + credentials. Bij succes retourneert het de verbinding; bij een mislukking + vangt het de fout op en print een bericht. + + Returns: + connection (mysql.connector.connect object): Een connectie object als + de verbinding succesvol is. Anders None. + """ try: + # Probeert een verbinding op te zetten met de MySQL database. connection = mysql.connector.connect( - host="localhost", - user="root", - password="", - database="goodgarden" + host="localhost", # Database host + user="root", # Database gebruikersnaam + password="", # Database wachtwoord + database="goodgarden" # Database naam ) + + # Controleert of de verbinding succesvol was. if connection.is_connected(): - return connection + return connection # Retourneert het verbinding object. + except Error as e: + # Vangt en print elke fout die optreedt tijdens het verbindingsproces. print(f"Connection NIET gelukt! ${e}") - return None \ No newline at end of file + + # Retourneert None als de verbinding mislukt. + return None diff --git a/mqtt/mqtt_client.py b/mqtt/mqtt_client.py index 35f8402..c7d00e7 100644 --- a/mqtt/mqtt_client.py +++ b/mqtt/mqtt_client.py @@ -1,15 +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 - client.connect(broker, port, 60) + # 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") diff --git a/mqtt/publisher.py b/mqtt/publisher.py index b6ff42a..5056e49 100644 --- a/mqtt/publisher.py +++ b/mqtt/publisher.py @@ -1,60 +1,82 @@ +# 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 -publish_interval = 30 # Secondes om een aanvraag te doen - MOET ~300 ZIJN!!!!!!!!! +# Stel het interval in seconden in voor het periodiek ophalen en publiceren van data. +publish_interval = 30 # MOET ~300 ZIJN voor productiegebruik. +# 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"} + # Voeg meer API-eindpunten en topics toe zoals vereist. ] 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("Connected with result 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)}") -client = create_client("publisher1", on_connect, on_message) # Gebruik een unieke client ID +# 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): - - json_data = json.dumps(data) # Serialiseer de data naar een JSON-string + """ + 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 published to 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" + access_token = "33bb3b42452306c58ecedc3c86cfae28ba22329c" # Voorbeeld token. try: headers = {"Authorization": f"Token {access_token}"} response = requests.get(url, headers=headers) - response.raise_for_status() # Zorgt ervoor dat HTTPError wordt opgeworpen voor slechte responses + response.raise_for_status() # Verifieert respons status. data = response.json() print(f"Data from {url}: {data}") publish_to_mqtt(mqtt_topic, data) - # load_data(data) # Zorg ervoor dat deze functie elders gedefinieerd is except requests.exceptions.RequestException as e: print(f"Error fetching data from {url}: {e}") if __name__ == "__main__": - client.loop_start() # Start de niet-blokkerende loop + client.loop_start() # Start de niet-blokkerende MQTT-client loop. while True: - fetch_and_publish_data() + 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. - - time.sleep(publish_interval) client.loop_stop() diff --git a/src/py/script/battery_voltage_events.py b/src/py/script/battery_voltage_events.py index 8f989dc..d37ac61 100644 --- a/src/py/script/battery_voltage_events.py +++ b/src/py/script/battery_voltage_events.py @@ -214,6 +214,7 @@ if __name__ == "__main__": # 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 diff --git a/src/py/script/db_connect.py b/src/py/script/db_connect.py index 8d61b95..648a1a7 100644 --- a/src/py/script/db_connect.py +++ b/src/py/script/db_connect.py @@ -1,19 +1,26 @@ +# Importeer de mysql.connector module en Error klasse uit deze module. 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", - user="root", - password="", - database="goodgarden" + host="localhost", # De hostnaam waar de database draait, in dit geval lokaal. + user="root", # De gebruikersnaam voor de database. + password="", # 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}") - return None - -# database_connect() \ No newline at end of file + # Als de verbinding niet succesvol was, retourneer None. + return None \ No newline at end of file diff --git a/src/py/script/devices.py b/src/py/script/devices.py index 7e5c161..1261446 100644 --- a/src/py/script/devices.py +++ b/src/py/script/devices.py @@ -49,9 +49,6 @@ def on_message(client, userdata, message): print(f"\033[92mMessage received on topic\033[0m {message.topic}: {data}") -# def berekenAlgo(data): - - if __name__ == "__main__": topic = "goodgarden/devices" subscribe.callback(on_message, topic) \ No newline at end of file diff --git a/src/py/script/par_events.py b/src/py/script/par_events.py index a5cda5e..7979e50 100644 --- a/src/py/script/par_events.py +++ b/src/py/script/par_events.py @@ -1,25 +1,38 @@ +# Importeer de json module om met JSON data te werken. 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) + # Initialiseer variabelen om de waarden van de apparaten op te slaan. device_322_value = None device_256_value = None + # Doorloop de "results" key in de data dictionary. for key in data["results"]: + # Controleer of de "device" key overeenkomt met device 322 of 256 en sla de waarde op. if key["device"] == 322: device_322_value = key["value"] elif key["device"] == 256: device_256_value = key["value"] + # Print de waarden van beide apparaten. print(f"Device 322 value: {device_322_value}") print(f"Device 256 value: {device_256_value}") + # Print het volledige bericht dat ontvangen is op het abonnementstopic. 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" - subscribe.callback(on_message, topic) \ No newline at end of file + # 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) diff --git a/src/py/script/planten.py b/src/py/script/planten.py index 6a51d24..ebbc540 100644 --- a/src/py/script/planten.py +++ b/src/py/script/planten.py @@ -1,51 +1,54 @@ +# Importeer de json en mysql.connector modules voor het werken met JSON data en MySQL databases. +# Importeer ook os voor bestandssysteem operaties. import json import mysql.connector import os -# Function to make a connection to the database +# Functie om een verbinding met de database te maken. def database_connect(): return mysql.connector.connect( - host="localhost", - user="root", - password="", - database="goodgarden" + host="localhost", # De server waar de database draait. + user="root", # De gebruikersnaam om in te loggen op de database. + password="", # Het wachtwoord voor de gebruiker. + database="goodgarden" # De naam van de database waarmee verbinding moet worden gemaakt. ) -# Function to get the absolute path of the current directory +# 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(): - # Establish a database connection + # Maak verbinding met de database. connection = database_connect() try: - cursor = connection.cursor(dictionary=True) # To fetch rows as dictionaries - # Execute the query to fetch data + # 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") - # Fetch all rows + # Haal alle rijen op. plants = cursor.fetchall() - # Get the absolute path of the current directory + # Verkrijg het absolute pad van de huidige directory. current_directory = get_current_directory() - # Construct the absolute path for the JSON file + # Construeer het absolute pad voor het JSON-bestand. json_file_path = os.path.join(current_directory, 'plants.json') - # Write fetched data to JSON file + # 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("Error fetching data from MySQL table:", error) finally: - # Close cursor and connection + # Sluit de cursor en de verbinding, indien ze bestaan. if 'cursor' in locals(): cursor.close() if connection.is_connected(): connection.close() -# Call the function to fetch data and write to JSON -fetch_plant_and_write_to_json() -# if __name__ == "__main__": -# fetch_plant_and_write_to_json() +# Roept de functie aan om gegevens op te halen en naar JSON te schrijven. +fetch_plant_and_write_to_json() \ No newline at end of file diff --git a/src/py/script/relative_humidity_events.py b/src/py/script/relative_humidity_events.py index 4b71813..363116a 100644 --- a/src/py/script/relative_humidity_events.py +++ b/src/py/script/relative_humidity_events.py @@ -1,13 +1,22 @@ +# Importeer de json module voor het werken met JSON data. 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" - subscribe.callback(on_message, topic) \ No newline at end of file + # Abonneer op het opgegeven topic en roep de on_message functie aan als callback voor ontvangen berichten. + subscribe.callback(on_message, topic) diff --git a/src/py/script/soil_electric_conductivity_events.py b/src/py/script/soil_electric_conductivity_events.py index 69f0960..8917a10 100644 --- a/src/py/script/soil_electric_conductivity_events.py +++ b/src/py/script/soil_electric_conductivity_events.py @@ -1,13 +1,24 @@ +# De json module wordt geïmporteerd om te werken met JSON-geformatteerde data. 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" - subscribe.callback(on_message, topic) \ No newline at end of file + # 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) diff --git a/src/py/script/soil_relative_permittivity_events.py b/src/py/script/soil_relative_permittivity_events.py index ce0cb25..0d01ff5 100644 --- a/src/py/script/soil_relative_permittivity_events.py +++ b/src/py/script/soil_relative_permittivity_events.py @@ -1,13 +1,22 @@ +# Importeer de json module om te werken met JSON data. 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" - subscribe.callback(on_message, topic) \ No newline at end of file + # 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) diff --git a/src/py/script/soil_temperature_events.py b/src/py/script/soil_temperature_events.py index 89b7116..7b06a01 100644 --- a/src/py/script/soil_temperature_events.py +++ b/src/py/script/soil_temperature_events.py @@ -1,13 +1,22 @@ +# Importeer de json module om te werken met JSON-geformatteerde data. 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" - subscribe.callback(on_message, topic) \ No newline at end of file + # Start het abonneren op het opgegeven topic met de on_message functie als de callback. + subscribe.callback(on_message, topic) diff --git a/src/py/script/samenvoegen_databases.py b/src/py/script/testen.py similarity index 100% rename from src/py/script/samenvoegen_databases.py rename to src/py/script/testen.py diff --git a/src/py/static/js/main.js b/src/py/static/js/main.js index 7519036..e5b49a1 100644 --- a/src/py/static/js/main.js +++ b/src/py/static/js/main.js @@ -1,98 +1,117 @@ +// 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 to open the modal -function openModal() { +/** + * 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]; - if (modal && button) { // Check if elements are found - // Toon de modal wanneer op de knop wordt geklikt - button.onclick = function () { + // 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 modal wanneer op het 'sluiten' icoon wordt geklikt - close.onclick = function () { + // Sluit de modaal wanneer op het 'sluiten' icoon wordt geklikt. + close.onclick = function () + { modal.style.display = "none"; } - // Sluit de modal wanneer buiten de modal wordt geklikt - window.onclick = function (event) { - if (event.target == modal) { + // Sluit de modaal wanneer buiten de modaal wordt geklikt. + window.onclick = function (event) + { + if (event.target == modal) + { modal.style.display = "none"; } } - } else { - console.error("Modal elements not found"); + } + else + { + console.error("Modaal elementen niet gevonden"); } } - -document.addEventListener('DOMContentLoaded', () => { - // Call openModal when DOM content is loaded + +document.addEventListener('DOMContentLoaded', () => +{ openModal(); - // Send a message to the main process to execute the Python script + // Roep andere functies hier aan zoals vereist, bijvoorbeeld: + // fetchWeatherDataAndDrawChart(); + + // Stuur een bericht naar het hoofdproces om het Python-script uit te voeren. 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 + // Luister naar updates van HTML-data vanuit het hoofdproces. + ipcRenderer.on('update-html-data', (event, data) => + { + // Werk de HTML bij met de ontvangen data. document.getElementById('batteryVoltage').innerText = data.batteryVoltage; - // Add similar lines for other data fields + // Voeg vergelijkbare regels toe voor andere data velden. }); - // Trigger an event to request data update + // Trigger een event om data update aan te vragen. ipcRenderer.send('request-update-data'); - // Fetch battery data when the page loads + // Haal batterij data op wanneer de pagina laadt. fetchBatteryData(); }); -function drawLineChart() +/** + * Functie om een lijngrafiek te tekenen. + * @param {Array} xLabels Labels voor de x-as. + * @param {Array} data De data punten voor de grafiek. + */ +function drawLineChart(xLabels, data) { - /*Dit is de data die getoond wordt als "punt" op de grafiek. 20 = y20 / x20, 50 = y50 / x50 enzovoort... De array "data" & "xLabels" moeten beide evenveel array items hebben!!*/ - const data = [20, 50, 60, 45, 50, 100, 70, 60, 65, 0, 85, 0]; - const xLabels = ["", "", "", "", "", 6, "", "", "", "", "", 12]; - - const yLabels = ["", 20, "", 40, "", 60, "", 80, "", 100]; /*NIET VERANDEREN!!!*/ + // Definieer Y labels (niet veranderen volgens commentaar). + const yLabels = ["", 10, 15, 20, 25, 30, 35, 40]; + // Verkrijg het canvas element en de context voor tekenen. const canvas = document.getElementById("myCanvas"); const ctx = canvas.getContext("2d"); + // Maak het canvas schoon voor nieuwe tekening. ctx.clearRect(0, 0, canvas.width, canvas.height); - const padding = 35; // Increased padding for Y labels + // Definieer padding en bereken grafiekafmetingen. + const padding = 35; // Vergrote padding voor Y labels. const graphWidth = canvas.width - padding * 2; const graphHeight = canvas.height - padding * 2; + // Teken de as van de grafiek. ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, canvas.height - padding); ctx.lineTo(canvas.width - padding, canvas.height - padding); ctx.stroke(); - // Set the color of the line + // Stel de kleur van de lijn in. ctx.strokeStyle = "rgb(143, 188, 143)"; + // Bereken incrementen voor het plotten. const xIncrement = graphWidth / (xLabels.length - 1); const yIncrement = graphHeight / (yLabels.length - 1); - // Plot the data + // Plot de data. ctx.beginPath(); ctx.moveTo(padding, canvas.height - padding - (data[0] / 100) * graphHeight); - for (let i = 1; i < data.length; i++) + for (let i = 1; i < data.length; i++) { const xPos = padding + i * xIncrement; const yPos = canvas.height - padding - (data[i] / 100) * graphHeight; @@ -100,84 +119,171 @@ function drawLineChart() } ctx.stroke(); - // Draw Y labels + // Teken Y labels. ctx.fillStyle = "black"; - ctx.textAlign = "right"; // Align text to the right - ctx.textBaseline = "middle"; // Center vertically + ctx.textAlign = "right"; // Tekst naar rechts uitlijnen. + ctx.textBaseline = "middle"; // Verticaal centreren. - for (let i = 0; i < yLabels.length; i++) + for (let i = 0; i < yLabels.length; i++) { - if (yLabels[i] !== "") + if (yLabels[i] !== "") { const yPos = canvas.height - padding - i * yIncrement; ctx.fillText(yLabels[i], padding - 10, yPos); } } - // Draw X labels - ctx.textAlign = "center"; // Center horizontally for X labels - for (let i = 0; i < xLabels.length; i++) + // Teken X labels. + ctx.textAlign = "center"; // Horizontaal centreren voor X labels. + for (let i = 0; i < xLabels.length; i++) { - if (xLabels[i] !== "") + if (xLabels[i] !== "") { const xPos = padding + i * xIncrement; ctx.fillText(xLabels[i], xPos, canvas.height - padding + 20); } } } - drawLineChart(); -// Function to fetch battery data from Flask API -function fetchBatteryData() { + +/** + * Functie om weergegevens op te halen en een grafiek te tekenen. + * Deze functie haalt weergegevens op van een lokale server en tekent een lijngrafiek + * op basis van de verkregen data. + */ +function fetchWeatherDataAndDrawChart() +{ + // URL van de API waar de weergegevens opgehaald kunnen worden. + const apiUrl = `http://127.0.0.1:5000/weather`; + + // Voer een GET-verzoek uit naar de API. + fetch(apiUrl) + .then(response => + { + // Controleer of het verzoek succesvol was. + if (response.ok) + { + return response.json(); + } + // Gooi een fout als het verzoek niet succesvol was. + throw new Error('Network response was not ok.'); + }) + .then(data => + { + // Verkrijg de weersvoorspelling voor de eerste 5 dagen. + const weatherForecast = data.weather_forecast.slice(0, 5); + // Converteer datums naar dagen van de week. + const dates = weatherForecast.map(day => convertDateToDayOfWeek(day.dag)); + // Verkrijg de maximale temperaturen. + const temperatures = weatherForecast.map(day => day.max_temp); + + // Teken de lijngrafiek met de verkregen data. + drawLineChart(dates, temperatures); + }) + .catch(error => + { + // Log eventuele fouten tijdens het ophalen. + console.error('There was a problem with the fetch operation:', error); + }); +} + +/** + * Functie om een datum (bijv. "07-02") om te zetten naar de dag van de week (bijv. "zo", "ma", etc.). + * @param {string} dateString - De datum als string in het formaat "dd-mm". + * @returns {string} De afkorting van de dag van de week. + */ +function convertDateToDayOfWeek(dateString) +{ + // Split de datum in dag en maand, en zet deze om naar nummers. + const [day, month] = dateString.split('-').map(Number); + // Maak een nieuwe datumobject (jaar is willekeurig omdat we alleen maand en dag nodig hebben). + const date = new Date(2024, month - 1, day); + // Verkrijg de dag van de week en zet deze om naar een afkorting. + const dayOfWeek = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'][date.getDay()]; + return dayOfWeek; +} + +/** + * 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 => { + .then(response => + { + // Verwerk de ontvangen data. const batteryData = response.data; updateBatteryData(batteryData); }) - .catch(error => { + .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) { - if(batteryData[1].device_id == 322) + // Update de data voor specifieke apparaten op basis van hun ID. + if (batteryData[1].device_id == 322) { document.getElementById('deviceNumber-322').innerHTML = batteryData[1].device_id; document.getElementById('voltage-322').innerHTML = batteryData[1].label; - document.getElementById('time-322').innerHTML = batteryData[1].last_seen; + document.getElementById('time-322').innerHTML = new Date(batteryData[0].last_seen).toLocaleTimeString(); document.getElementById('tevredenheid-322').innerHTML = batteryData[1].last_battery_voltage; } - if (batteryData[0].device_id == 256) + if (batteryData[0].device_id == 256) { document.getElementById('deviceNumber-256').innerHTML = batteryData[0].device_id; document.getElementById('voltage-256').innerHTML = batteryData[0].label; - document.getElementById('time-256').innerHTML = batteryData[0].last_seen; + document.getElementById('time-256').innerHTML = new Date(batteryData[0].last_seen).toLocaleTimeString(); document.getElementById('tevredenheid-256').innerHTML = batteryData[0].last_battery_voltage; } - console.log(batteryData); } - + +// Definieer de API-sleutel en de stad waarvoor we de weergegevens willen ophalen. const apiKey = "9516081f15727d063c9e2f08454a2fe9"; const city = "Leiden"; -// Maak de URL voor de API-aanroep -const apiUrl = `https://weerlive.nl/api/weerlive_api_v2.php?key=${apiKey}&locatie=${city}`; +// Construeer de URL voor de API-aanroep. In dit geval is het een lokale server. +const apiUrl = `http://127.0.0.1:5000/weather`; -// Doe een GET-verzoek naar de API +// Voer een GET-verzoek uit naar de API om de weergegevens op te halen. fetch(apiUrl) - .then(response => { - if (response.ok) { + .then(response => + { + // Controleer of de respons van de server in orde is (status code 200). + if (response.ok) + { + // Zo ja, parse de JSON uit de respons. return response.json(); } + // Zo niet, gooi een fout. throw new Error('Network response was not ok.'); }) - .then(data => { - // Verwerk de ontvangen weerdata + .then(data => + { + // Log de ontvangen data naar de console voor debugging. console.log(data); - const weatherIcon = data.live_weather.image; - const weatherImageUrl = `https://www.weerlive.nl/delen.php?size=klein&weer=${weatherIcon}`; - document.getElementById('weatherImage').src = weatherImageUrl; + + // Extraheren van de weersvoorspelling voor de eerste vijf dagen. + const weatherForecast = data.weather_forecast.slice(0, 5); + // Haal de dagen op waarvoor de voorspelling geldt. + const dates = weatherForecast.map(day => day.dag); + // Haal de maximale temperaturen op voor deze dagen. + const temperatures = weatherForecast.map(day => day.max_temp); + + // Update de grafiek met de nieuwe data. + drawLineChart(dates, temperatures); }) - .catch(error => { + .catch(error => + { + // Log eventuele fouten tijdens het ophalen van de data. console.error('There was a problem with the fetch operation:', error); }); \ No newline at end of file diff --git a/src/py/static/js/planten.class.js b/src/py/static/js/planten.class.js index 61a6f5d..6262cb5 100644 --- a/src/py/static/js/planten.class.js +++ b/src/py/static/js/planten.class.js @@ -1,51 +1,59 @@ +// 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; // Note the property name change - this.plantensoort = dataObject.plantensoort; - this.plantGeteelt = dataObject.plant_geteelt; + 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 = []; - this.cols = 2; // Number of columns - this.rows = 4; // Number of rows (including the row for the "Add" button) + 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). - // Initialize the grid with null values + // Initialiseren van het raster met null waarden. for (let i = 0; i < this.rows; i++) { this.grid[i] = new Array(this.cols).fill(null); } - // Load JSON data from the server + // Laadt JSON data van de server. this.loadData(); } - + + // Methode om data te laden. loadData() { - fetch('../script/plants.json') // Assuming your JSON data is stored in 'plants.json' + fetch('../script/plants.json') // Veronderstelt dat de JSON data is opgeslagen in 'plants.json'. .then(response => { + // Controleer of de netwerkrespons ok is. if (!response.ok) { throw new Error('Network response was not ok'); } - return response.json(); + 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); - // Populate the grid with plant objects + // Vul het raster met plantobjecten. filteredData.slice(0, 8).forEach((plantObject, index) => { - const plant = new Plant(plantObject); - const col = index % this.cols; - const row = Math.floor(index / this.cols); - this.grid[row][col] = plant; + 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. }); - // Display the grid in the HTML table with id "planten" + // Toon het raster in de HTML tabel met id "planten". this.displayGrid(); }) - .catch(error => console.error('Error loading data:', error)); + .catch(error => console.error('Error loading data:', error)); // Log eventuele fouten. } + displayGrid() { const plantenTable = document.getElementById("planten"); @@ -98,8 +106,63 @@ class PlantGrid { }); } + + displayGrid() { + const plantenTable = document.getElementById("planten"); // Verkrijg de tabel waarin het raster getoond moet worden. + + let itemCount = 0; // Teller voor het aantal items in het raster. + + this.grid.forEach((row, rowIndex) => { + const tr = document.createElement("tr"); // Maak een tabelrij element. + + row.forEach((plant, colIndex) => { + const td = document.createElement("td"); // Maak een tabeldata element. + + // Logica om plantitems of de "Toevoegen" knop te verwerken. + if (itemCount < 8) { + if (plant) { + // Verwerk normale plantitems. + // Creëer een link element naar de planteninformatiepagina met plant ID als query parameter. + // Voeg vervolgens een artikel, afbeelding en titel toe met de plantinformatie. + // Handle regular plant items + const link = document.createElement("a"); + link.href = `planteninfo.html?id=${plant.id}`; + + const article = document.createElement("article"); + article.classList.add("plant-container"); + link.appendChild(article); + + 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); + 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/Toevoegen.png"; + img.id = "toevoegen"; + img.alt = "Add"; + article.id = "modalButton"; + article.onclick = openModal; + + td.appendChild(article); + itemCount++; + } + } + + tr.appendChild(td); // Voeg de td toe aan de tr. + }); + + plantenTable.appendChild(tr); // Voeg de tr toe aan de tabel. + }); +} } document.addEventListener("DOMContentLoaded", () => { const plantGrid = new PlantGrid(); -}); +}); \ No newline at end of file diff --git a/src/py/templates/index.html b/src/py/templates/index.html index e6b9def..1505214 100644 --- a/src/py/templates/index.html +++ b/src/py/templates/index.html @@ -21,90 +21,62 @@
+
- - -
-
+ \ No newline at end of file diff --git a/src/py/templates/kas_informatie.html b/src/py/templates/kas_informatie.html index c41e3ba..f5ccc8e 100644 --- a/src/py/templates/kas_informatie.html +++ b/src/py/templates/kas_informatie.html @@ -17,32 +17,37 @@ +

Informatie Kas

+
+
+ - + - + - + - +
Device
Batterij Voltage
Tijden
Zulu
+

Zonlicht

@@ -56,10 +61,13 @@
+
+ + @@ -74,6 +82,7 @@
Aantal geplant: Loading...
+ @@ -92,6 +101,7 @@
Warmste Maand: n.v.t.
+ @@ -115,12 +125,6 @@ diff --git a/src/py/templates/planteninfo.html b/src/py/templates/planteninfo.html index 5b6ab93..e873a84 100644 --- a/src/py/templates/planteninfo.html +++ b/src/py/templates/planteninfo.html @@ -17,13 +17,18 @@ +

Informatie Kas

+
+
+
Laatste Irrigatie: 2u
+ @@ -43,6 +48,7 @@
Dagen tot Oogst 12
+

Zonlicht

@@ -56,10 +62,13 @@
+
+ + @@ -74,6 +83,7 @@
Aantal geplant: 2
+ @@ -92,6 +102,7 @@
Warmste Maand: n.v.t.
+
Laatste Irrigatie: 2u