diff --git a/.gitignore b/.gitignore index be40c27..a380544 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/webserver/plots/ +/webserver/static/plots/ diff --git a/README.md b/README.md index edba6a3..cb05239 100644 --- a/README.md +++ b/README.md @@ -81,17 +81,33 @@ Data should be sent and received via MQTT to an ESP32. The Pi acts as a sensor g - `main.py` The main file for the project +- `The Graphs` + https://ron.sh/creating-real-time-charts-with-fastapi/ + ### Git Help -![A Git Cheatsheet](gitHelp.png "Git Cheatsheet") +![A Git Cheatsheet](images/gitHelp.png "Git Cheatsheet") ### MarkDown Help -![A MarkDown Cheatsheet](mdHelp.png "MarkDown Cheatsheet") +![A MarkDown Cheatsheet](images/mdHelp.png "MarkDown Cheatsheet") ### Html Help -![A Html Cheatsheet](htmlHelp.png "Html Cheatsheet") +![A Html Cheatsheet](images/htmlHelp.png "Html Cheatsheet") + +### Css Help + +![A Css Cheatsheet](images/cssHelp.png "Css Cheatsheet") + +### JavaScript Help + +![A JavaScript Cheatsheet](images/javascriptHelp.png "JavaScript Cheatsheet") + +### Jinja Help + +![A Jinja Cheatsheet](images/jinjaHelp.png "Jinja Cheatsheet") + ## Arbeitsaufträge / Aufgabeneinteilung diff --git a/images/cssHelp.png b/images/cssHelp.png new file mode 100644 index 0000000..9e5ccc0 Binary files /dev/null and b/images/cssHelp.png differ diff --git a/gitHelp.png b/images/gitHelp.png similarity index 100% rename from gitHelp.png rename to images/gitHelp.png diff --git a/htmlHelp.png b/images/htmlHelp.png similarity index 100% rename from htmlHelp.png rename to images/htmlHelp.png diff --git a/images/javascriptHelp.png b/images/javascriptHelp.png new file mode 100644 index 0000000..3404f20 Binary files /dev/null and b/images/javascriptHelp.png differ diff --git a/images/jinjaHelp.png b/images/jinjaHelp.png new file mode 100644 index 0000000..3252119 Binary files /dev/null and b/images/jinjaHelp.png differ diff --git a/mdHelp.png b/images/mdHelp.png similarity index 100% rename from mdHelp.png rename to images/mdHelp.png diff --git a/main.py b/main.py index e69de29..e3e8151 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,12 @@ +# Copyright 2025 Kieler, Chiara +# +# Licensed under the AGPLv3.0 (the "License"); +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.gnu.org/licenses/ + +# What this file should do? It should: +# 1. Start all the Sensors +# 2. Start the Webserver +# 3. Somehow transfer data from the Sensors to the Webserver diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e69de29..0000000 diff --git a/shell.nix.old b/shell.nix.old deleted file mode 100644 index 714bdea..0000000 --- a/shell.nix.old +++ /dev/null @@ -1,12 +0,0 @@ -{ pkgs ? import {} }: -(pkgs.buildFHSUserEnv { - name = "venv"; - targetPkgs = pkgs: (with pkgs; [ - python312 - python312Packages.pip - python312Packages.virtualenv - python312Packages.matplotlib - ]); - runScript = "bash --init-file /etc/profile"; - # runScript = "zsh"; -}).env diff --git a/webserver/data_big.json b/webserver/data_big.json new file mode 100644 index 0000000..52e39ec --- /dev/null +++ b/webserver/data_big.json @@ -0,0 +1,100 @@ +{ + "location": "Building B - Research Wing", + "sensors": [ + { + "id": "temp-A12", + "type": "temperature", + "unit": "°C", + "readings": [ + { "ts": 1747814400, "value": 21.0 }, + { "ts": 1747818000, "value": 21.4 }, + { "ts": 1747821600, "value": 22.1 }, + { "ts": 1747825200, "value": 22.6 }, + { "ts": 1747828800, "value": 22.8 }, + { "ts": 1747832400, "value": 23.0 } + ] + }, + { + "id": "hum_B2", + "type": "humidity", + "unit": "%", + "readings": [ + { "ts": 1747814400, "value": 50.1 }, + { "ts": 1747818000, "value": 51.7 }, + { "ts": 1747821600, "value": 49.3 }, + { "ts": 1747825200, "value": 48.8 }, + { "ts": 1747828800, "value": 47.6 }, + { "ts": 1747832400, "value": 46.9 }, + { "ts": 1747836000, "value": 45.2 }, + { "ts": 1747839600, "value": 44.7 }, + { "ts": 1747843200, "value": 43.5 } + ] + }, + { + "id": "PRES-009", + "type": "pressure", + "unit": "hPa", + "readings": [ + { "ts": 1747814400, "value": 1011.0 }, + { "ts": 1747818000, "value": 1010.8 } + ] + }, + { + "id": "sensorX04", + "type": "CO2", + "unit": "ppm", + "readings": [ + { "ts": 1747814400, "value": 415 }, + { "ts": 1747818000, "value": 420 }, + { "ts": 1747821600, "value": 432 }, + { "ts": 1747825200, "value": 440 }, + { "ts": 1747828800, "value": 437 } + ] + }, + { + "id": "luxSensor-1", + "type": "light", + "unit": "lux", + "readings": [ + { "ts": 1747814400, "value": 300 }, + { "ts": 1747818000, "value": 450 }, + { "ts": 1747821600, "value": 600 }, + { "ts": 1747825200, "value": 550 }, + { "ts": 1747828800, "value": 500 }, + { "ts": 1747832400, "value": 480 }, + { "ts": 1747836000, "value": 470 }, + { "ts": 1747839600, "value": 460 }, + { "ts": 1747843200, "value": 455 }, + { "ts": 1747846800, "value": 440 }, + { "ts": 1747850400, "value": 430 }, + { "ts": 1747854000, "value": 420 } + ] + }, + { + "id": "vib_07", + "type": "vibration", + "unit": "mm/s", + "readings": [ + { "ts": 1747814400, "value": 0.05 }, + { "ts": 1747818000, "value": 0.06 }, + { "ts": 1747821600, "value": 0.08 }, + { "ts": 1747825200, "value": 0.07 }, + { "ts": 1747828800, "value": 0.09 }, + { "ts": 1747832400, "value": 0.1 }, + { "ts": 1747836000, "value": 0.12 } + ] + }, + { + "id": "motion-Z3", + "type": "motion", + "unit": "count", + "readings": [ + { "ts": 1747814400, "value": 3 }, + { "ts": 1747818000, "value": 1 }, + { "ts": 1747821600, "value": 0 }, + { "ts": 1747825200, "value": 2 } + ] + } + ] +} + diff --git a/webserver/data.json b/webserver/data_smol.json similarity index 99% rename from webserver/data.json rename to webserver/data_smol.json index af037db..6a570b0 100644 --- a/webserver/data.json +++ b/webserver/data_smol.json @@ -1,3 +1,4 @@ + { "location": "Building A - Lab 3", "sensors": [ diff --git a/webserver/server.py b/webserver/server.py index d6235fe..14d39c9 100644 --- a/webserver/server.py +++ b/webserver/server.py @@ -1,28 +1,40 @@ +# Copyright 2025 Kieler, Chiara +# +# Licensed under the AGPLv3.0 (the "License"); +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.gnu.org/licenses/ + + from datetime import datetime -import re -from flask import Flask, send_from_directory, render_template +from flask import Flask, render_template import json +import matplotlib +import hashlib +matplotlib.use('Agg') import matplotlib.pyplot as plt # Global Variables: -jsonpath = "data.json" -htmldata = "" +jsonpath = "data_big.json" +sensors: dict = {} -# class Entries: -# def __init__(self, id: str, ts: list, value: list): -# self.id = id -# templist: list = [] -# for i in range(len(ts)): -# templist.append([ts[i], value[i]]) -# self.entries: list = templist -# -# def printEntries(self): -# print(self.entries) -# -# -# tese = Entries("sensor0", [1, 2, 3, 4], [10, 20, 30, 40]) -# tese.printEntries() +def readJson(): + f = open(jsonpath) + jsondata = json.load(f) # returns JSON object as a dictionary + for i in jsondata["sensors"]: # Iterating through the json list + timestamps: list = [] + values: list = [] + # print(len(i["readings"])) + for j in range(len(i["readings"])): + timestamps.append(i["readings"][j]["ts"]) + values.append(i["readings"][j]["value"]) + # print(i) + # print(i["readings"][1]) + # print("\n") + sensors[i["id"]] = Sensor(i["id"], i["unit"], i["type"], timestamps, values) + f.close() class Sensor: @@ -34,6 +46,7 @@ class Sensor: self.values: list = value self.timeonly: list = [] self.timedate: list = [] + self.hash = hashlib.sha256(repr(ts).encode()).hexdigest() for i in ts: # print(i) self.timeonly.append(datetime.fromtimestamp(int(i)).strftime("%H:%M.%S")) @@ -73,6 +86,29 @@ class Sensor: def getReadingsTime(self): return self.getReadings(timetype="time") + def getChartJSData(self, limit=5, reversed=False, timetype="ts"): + datalist: list = [[],[]] + match timetype: + case "ts": + for i in range(limit if limit < len(self.ts) else len(self.ts)): + datalist[0].append(self.ts[i]) + datalist[1].append(self.values[i]) + case "time": + for i in range(limit if limit < len(self.ts) else len(self.ts)): + datalist[0].append(self.timeonly[i]) + datalist[1].append(self.values[i]) + case "timedate": + for i in range(limit if limit < len(self.ts) else len(self.ts)): + datalist[0].append(self.timedate[i]) + datalist[1].append(self.values[i]) + case _: + return "ERROR: timetype must be one of 'ts', 'time', 'timedate'" + if reversed: + datalist[0].reverse() + datalist[1].reverse() + return datalist + + def getValueByTimestamp(self, ts: int): c = 0 for i in self.ts: @@ -97,112 +133,15 @@ class Sensor: plt.close() return path - # def formatLine(self, ts: int): - # for i in self.ts: - # if i == ts: - # return f"{self.timedate}> {self.values[]}" + def getHash(self): + return self.hash -# Json -f = open(jsonpath) -jsondata = json.load(f) # returns JSON object as a dictionary -# sensordict = dict() -sensors: dict = {} -for i in jsondata["sensors"]: # Iterating through the json list - timestamps: list = [] - values: list = [] - # print(len(i["readings"])) - for j in range(len(i["readings"])): - timestamps.append(i["readings"][j]["ts"]) - values.append(i["readings"][j]["value"]) - # print(i) - # print(i["readings"][1]) - # print("\n") - # sensordict[i["id"]] = [i["type"], i["unit"], i["readings"]] - # print(sensordict[i["id"]]) - sensors[i["id"]] = Sensor(i["id"], i["unit"], i["type"], timestamps, values) +readJson() +# print("\n") +# print(sensors) -f.close() -print("\n") -# print(sensordict) -print(sensors) -# print(sensors["sensor_001"].getReadings(limit=2, reversed=True, timetype="time")) - -# for i in sensordict: -# type = sensordict[i][0] -# unit = sensordict[i][1] -# readings = "" -# datapoints: list = [] -# sensorrange = len(sensordict[i][2]) if len(sensordict[i][2]) < 5 else 5 -# for j in range(sensorrange): -# datapoints.append([]) -# ts = sensordict[i][2][j]["ts"] -# value = sensordict[i][2][j]["value"] -# datapoints[j].append(ts) -# datapoints[j].append(value) -# datapoints[j].append(unit) -# datapoints.sort(reverse=True) -# -# graphdata: list = [[], []] -# for j in datapoints: -# ts = j[0] -# time = datetime.fromtimestamp(ts).strftime("%d.%m.%Y %H:%M.%S") -# timeNoDate = datetime.fromtimestamp(ts).strftime("%H:%M.%S") -# value = j[1] -# unit = j[2] -# # print(f"{time} {j}") -# readings += f"{time}> {value}{unit}
" -# graphdata[0].append(timeNoDate) -# graphdata[1].append(value) -# -# # create_plot(i, unit, graphdata[0], graphdata[1]) -# -# htmldata += f""" -#

Id: {i}

-#

Type: {type}

-#

Readings:

-#
-# {readings} -# Graph -#
-#
""" -# -# html = f""" -# -# -# -# -# Sensor Data -# -# -# -# {htmldata} -# -# -# """ - - -# class MyHandler(BaseHTTPRequestHandler): -# def do_GET(self): -# self.send_response(200) -# # self.send_header("Content-type", "text/html") -# self.end_headers() -# self.wfile.write(html.encode("utf-8")) -# -# -# if __name__ == "__main__": -# server_address = ("", 8000) -# httpd = HTTPServer(server_address, MyHandler) -# print("Serving on http://localhost:8000") -# httpd.serve_forever() app = Flask(__name__) @@ -211,13 +150,10 @@ app = Flask(__name__) def index(): return render_template("index.html", data=sensors) - -# @app.before_request -# def before_request(): -# for sensor in sensors.values(): -# print(sensor) -# sensor.renderPlot() - +@app.before_request +def reload_data(): + readJson() + print("hello") if __name__ == "__main__": app.run(debug=True) diff --git a/webserver/static/CaskaydiaCove-Regular.ttf b/webserver/static/CaskaydiaCove-Regular.ttf new file mode 100644 index 0000000..04df61f --- /dev/null +++ b/webserver/static/CaskaydiaCove-Regular.ttf @@ -0,0 +1,2034 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + caskaydia-cove/fonts/ttf/CaskaydiaCove-Regular.ttf at master · eliheuer/caskaydia-cove · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ Skip to content + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + +
+ + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+ +
+
+ +
+ +
+

Footer

+ + + + +
+
+ + + + + © 2025 GitHub, Inc. + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + diff --git a/webserver/static/plots/sensor_001.png b/webserver/static/plots/sensor_001.png deleted file mode 100644 index 78d8e46..0000000 Binary files a/webserver/static/plots/sensor_001.png and /dev/null differ diff --git a/webserver/static/plots/sensor_002.png b/webserver/static/plots/sensor_002.png deleted file mode 100644 index 359be6c..0000000 Binary files a/webserver/static/plots/sensor_002.png and /dev/null differ diff --git a/webserver/static/plots/sensor_003.png b/webserver/static/plots/sensor_003.png deleted file mode 100644 index 60d677c..0000000 Binary files a/webserver/static/plots/sensor_003.png and /dev/null differ diff --git a/webserver/static/plots/test.png b/webserver/static/plots/test.png deleted file mode 100644 index 1af3b86..0000000 Binary files a/webserver/static/plots/test.png and /dev/null differ diff --git a/webserver/templates/index.html b/webserver/templates/index.html index 373749b..dbe1128 100644 --- a/webserver/templates/index.html +++ b/webserver/templates/index.html @@ -1,3 +1,11 @@ + + + + + + + + @@ -7,28 +15,96 @@ href="{{ url_for('static', filename='favicon.ico') }}" /> Sensor Data + -

Sensor Readings

+
+
+

Sensor Readings

+
+

{% for sensor in data.items() %}

+

{{ sensor[1].getId() }}[{{ sensor[1].getType() }}]

- +
+
+
    + {% for key,value in sensor[1].getReadingsTimeDate().items() %} +
  • {{ key }}» {{ value }}{{ sensor[1].getUnit() }}
  • + {% endfor %} +
+
+ +
+ {% endfor %}