diff --git a/.gitignore b/.gitignore index 103d74d1b3cdc94a7c42ee74b6bcaa1718dcc7ab..c961177e5c111d056ef02ced0ab97c0de6b9ab4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +<<<<<<< HEAD +======= +<<<<<<< HEAD +<<<<<<< HEAD +>>>>>>> origin/mqtt ############# # python ignores: ############# @@ -203,3 +208,222 @@ pnpm-debug.log* *.njsproj *.sln *.sw? +<<<<<<< HEAD +======= +======= +# Ignoriere die virtuelle Umgebung +.venv/ +venv/ +======= +############# +# python ignores: +############# +>>>>>>> 5c73cc5 (Es tut endlich mit JSON, aber muss noch weiter angepasst werden) + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +<<<<<<< HEAD +*.bak +>>>>>>> d319744 (Initial Commit) +======= +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +############# +# vue ignores: +############# + +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +>>>>>>> 5c73cc5 (Es tut endlich mit JSON, aber muss noch weiter angepasst werden) +>>>>>>> origin/mqtt diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..46e5b6d8abc39126c9d09e5d8345fe1564c7f798 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,3 @@ +.venv +__pycache__ +*.pyc \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6d0556067a3d3f3aac08f1645ae679c46aade1d9 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,29 @@ +# Verwende das neueste Python-Image +FROM python:latest + +# Setze das Arbeitsverzeichnis im Container +WORKDIR /app + +# Kopiere die pyproject.toml und uv.lock-Dateien +COPY pyproject.toml . +COPY uv.lock . + +# Kopiere auch requirements.txt, falls vorhanden +COPY requirements.txt . + +# Installiere uv und die Abhängigkeiten +RUN pip install --upgrade pip && \ + pip install uv && \ + pip install --no-cache-dir -r requirements.txt + +# Kopiere den Rest des Projekts in den Container +COPY . . + +# Setze Umgebungsvariablen, falls nötig +ENV PYTHONUNBUFFERED 1 + +# Exponiere den Port 8000 für den Server +EXPOSE 8000 + +# Der Startbefehl: Starte den Server mit uv +CMD ["uv", "run", "python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/backend/app/views.py b/backend/app/views.py index 4aeec7f4fa2a084512d00f9d298b0a4c56ba1c3d..7bff2a9e70abf3f8e6590ac58906a1dcaa551079 100644 --- a/backend/app/views.py +++ b/backend/app/views.py @@ -32,7 +32,7 @@ def login_view(request): user = authenticate(request, username=email, password=password) if user: - login(request, user) + login(request, user) # also creates a session in the browser return JsonResponse({"success": True}) return JsonResponse( {"success": False, "message": "Invalid credentials"}, status=401 @@ -40,7 +40,7 @@ def login_view(request): def logout_view(request): - logout(request) + logout(request) return JsonResponse({"message": "Logged out"}) diff --git a/backend/core/urls.py b/backend/core/urls.py index 8e18b6fa40ad248ce188e6b17bf9fd7da01e8819..8d12f2b65075ac542493d4e5bd15b8d644b03734 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -20,5 +20,5 @@ from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), - path("", include("sim.urls")), + path("", include("app.urls")), ] diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000000000000000000000000000000000000..39150fa4babbb831f34589d7fdda62efb1e2fd42 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,31 @@ + +services: + + mqtt-backend: + image: mqtt-influx-backend + container_name: mqtt-backend + build: ./mqtt + command: uv run -m mqtt_influx_backend.main + env_file: + - ../mqtt_to_influxdb/.env + restart: unless-stopped + #depends_on: + # - influxdb + # - mosquitto + + backend: + build: ./backend + ports: + - "8000:8000" + volumes: + - ./backend:/app + + frontend: + build: ./frontend + ports: + - "5173:5173" + volumes: + - ./frontend:/app + - /app/node_modules + stdin_open: true + tty: true diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..5171c54083337f0b87926da2e8f52abefe19d70f --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f687c9bcb1a05b5d2655156a8e3b1dff6ba65fba --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,15 @@ +# frontend/Dockerfile + +FROM node:20 +WORKDIR /app + +# Nur lokale Abhängigkeiten, kein globales Vite! +COPY package*.json ./ + + +RUN npm install + +COPY . . + +EXPOSE 5173 +CMD ["npm", "run", "dev"] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1cead453a3912b048e3faa5f92818edcbc9bc08b..b956a2d3c72f5e4e0f5e405ecab5cf99218f1fc7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", - "vite": "^6.2.0" + "vite": "^6.3.2" } }, "node_modules/@babel/helper-string-parser": { @@ -958,6 +958,21 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1006,6 +1021,19 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -1083,16 +1111,36 @@ "node": ">=0.10.0" } }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", + "integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" }, "bin": { "vite": "bin/vite.js" diff --git a/frontend/package.json b/frontend/package.json index a093bcfe119e1f2251a7a6ebd1747861699da352..24a5e39e5550d96b6924a8173ff214b50fe5ffb9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,6 @@ }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", - "vite": "^6.2.0" + "vite": "^6.3.2" } } diff --git a/mqtt/Dockerfile b/mqtt/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b2c96ab27aced46b94a26cd235198f34e8e21caf --- /dev/null +++ b/mqtt/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12-slim + +# Arbeitsverzeichnis im Container +WORKDIR /app + +# Projektdateien kopieren +COPY . . + +# uv installieren +RUN pip install uv + +# Installiere Abhängigkeiten aus pyproject.toml +RUN uv pip install . --no-cache-dir --system + +# Startkommando +CMD ["uv", "run", "-m", "mqtt_influx_backend.main"] diff --git a/mqtt/console.txt b/mqtt/console.txt new file mode 100644 index 0000000000000000000000000000000000000000..4832f2c20cbf6572fa1db52c38351a6c9df7c05a --- /dev/null +++ b/mqtt/console.txt @@ -0,0 +1,10 @@ +mosquitto_pub -h 172.20.10.12 -t co2/esp32 -m "{ + "metadata": { + "timestamp": "2025-04-12T14:22:35Z", + "mac": "AA:BB:CC:DD:EE:FF", + "room": "A123" + }, + "co2": 615.3, + "temperature": 21.8, + "humidity": 45.2 +}" diff --git a/mqtt/pyproject.toml b/mqtt/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..70f1ca35436ac27b1d12bfbcc7f9bcbfb3721fb7 --- /dev/null +++ b/mqtt/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "mqtt-influx-backend" +version = "0.1.0" +description = "Backend to write MQTT sensor data to InfluxDB" +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "paho-mqtt", + "influxdb-client", + "python-dotenv", +] + +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/mqtt/src/__init__.py b/mqtt/src/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mqtt/src/mqtt_influx_backend/__init__.py b/mqtt/src/mqtt_influx_backend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mqtt/src/mqtt_influx_backend/influxDBWriter.py b/mqtt/src/mqtt_influx_backend/influxDBWriter.py new file mode 100644 index 0000000000000000000000000000000000000000..398a7f4c23e3d1de08f10ba86cbd6579f2ebbf0a --- /dev/null +++ b/mqtt/src/mqtt_influx_backend/influxDBWriter.py @@ -0,0 +1,20 @@ +from influxdb_client import InfluxDBClient, Point, WritePrecision +from influxdb_client.client.write_api import SYNCHRONOUS + +class InfluxDBWriter: + def __init__(self, url: str, token: str, org: str, bucket: str): + self.client = InfluxDBClient(url=url, token=token, org=org) + self.write_api = self.client.write_api(write_options=SYNCHRONOUS) + self.bucket = bucket + self.org = org + + def write_point(self, measurement: str, tags: dict, fields: dict, timestamp=None): + point = Point(measurement) + for k, v in tags.items(): + point.tag(k, v) + for k, v in fields.items(): + point.field(k, v) + if timestamp: + point.time(timestamp, WritePrecision.NS) + + self.write_api.write(bucket=self.bucket, org=self.org, record=point) diff --git a/mqtt/src/mqtt_influx_backend/jsonhandler.py b/mqtt/src/mqtt_influx_backend/jsonhandler.py new file mode 100644 index 0000000000000000000000000000000000000000..6d2744f5b8f8fe334d074625710f0d24f74aaeb4 --- /dev/null +++ b/mqtt/src/mqtt_influx_backend/jsonhandler.py @@ -0,0 +1,24 @@ +import json +import os + +def load_json(file_name: str) -> dict: + """ + ladet eine JSON Datei, wenn diese existiert, + und gibt diese als dictionary zurück + key : value + """ + if not os.path.exists(file_name): + return {} + with open(file_name) as f: + mac_room_mapping = json.load(f) + return mac_room_mapping + +def write_json(mac_room_mapping: dict, file_name: str): + """ + Nimmt ein dictionary und schreibt dessen + Inhalte in eine JSON Datei + """ + with open(file_name, "w") as f: + f.seek(0) + json.dump(mac_room_mapping, f, indent=4) + f.truncate() # TODO Check if truncate is necessary? diff --git a/mqtt/src/mqtt_influx_backend/loggingFactory.py b/mqtt/src/mqtt_influx_backend/loggingFactory.py new file mode 100644 index 0000000000000000000000000000000000000000..99618a839c22235ee3cc1f147430e366e103095d --- /dev/null +++ b/mqtt/src/mqtt_influx_backend/loggingFactory.py @@ -0,0 +1,35 @@ +import logging +import os +from logging.handlers import RotatingFileHandler + +LOG_DIR = "logs" +LOG_FILE = "app.log" +LOG_PATH = os.path.join(LOG_DIR, LOG_FILE) + +class LoggerFactory: + + #logger.info("Connected with result code %s", str(rc)) + #logger.warning("Neue MAC-Adresse gefunden: %s", mac) + #logger.error("Failed writing to InfluxDb: %s", e) + + @staticmethod + def get_logger(name: str, level=logging.DEBUG) -> logging.Logger: + if not os.path.exists(LOG_DIR): + os.makedirs(LOG_DIR) + + logger = logging.getLogger(name) + if logger.hasHandlers(): + return logger # vermeidet doppelte Handler + + logger.setLevel(level) + + formatter = logging.Formatter( + '[%(asctime)s] %(levelname)s in %(name)s: %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) + + file_handler = RotatingFileHandler(LOG_PATH, maxBytes=5_000_000, backupCount=5) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + return logger diff --git a/mqtt/src/mqtt_influx_backend/mQTTClientHandler.py b/mqtt/src/mqtt_influx_backend/mQTTClientHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..ed4ffb3895ce64184ca89047666bad3da5cb7fd8 --- /dev/null +++ b/mqtt/src/mqtt_influx_backend/mQTTClientHandler.py @@ -0,0 +1,83 @@ +import json + +from src.mqtt_influx_backend.loggingFactory import LoggerFactory +from datetime import datetime +import paho.mqtt.client as mqtt +from src.mqtt_influx_backend import jsonhandler +from src.mqtt_influx_backend import influxDBWriter + +class MQTTClientHandler: + + MAPPING_FILE_NAME = "src/mqtt_influx_backend/mac_to_room.json" + MEASUREMENT_NAME = "sensor_data" + TAG_ROOM = "room" + TAG_MAC = "mac" + FIELD_CO2 = "co2" + FIELD_TEMP = "temperature" + FIELD_HUMIDITY = "humidity" + + # Konstruktor + def __init__(self, broker_url: str, topic: str, influx_writer: influxDBWriter): + self.logger = LoggerFactory.get_logger(__name__) + # key: mac : value : room + self.mac_to_room = jsonhandler.load_json(self.MAPPING_FILE_NAME) + self.broker_url = broker_url + self.topic = topic + self.influx_writer = influx_writer + self.client = mqtt.Client() + # Methoden werden hier Events zugeteilt + self.client.on_connect = self.on_connect + self.client.on_message = self.on_message + + def on_connect(self, client, userdata, flags, rc): + self.logger.info("Connected with result code " + str(rc)) + client.subscribe(self.topic) + self.logger.info("Subscribed to " + self.topic ) + + # eventuell refactorn und die Aufgaben in Methoden aufteilen + def on_message(self, client, userdata, msg): + """ + Wenn das Topic eine Nachricht bekommt wird diese Methode ausgeführt + self: ist die MQTTClientHandler instanz, die wird gebraucht um die Einträge in + die InfluxDB zu schreiben + """ + msg = json.loads(msg.payload) + metadate = msg["metadata"] + + # hier prüfen, ob die Mac-Adresse einen Raum hat, + # wenn nicht trage es in mac_to_room leer ein + # "aa:bb:cc:dd:ee:ff" : "" + mac = metadate["mac-address"] + + if mac not in self.mac_to_room: + self.logger.warning(f"Neue MAC-Adresse gefunden: {mac}. Mapping wird ergänzt.") + self.mac_to_room[mac] = "" # leerer Platzhalter + jsonhandler.write_json(self.mac_to_room, self.MAPPING_FILE_NAME) + self.mac_to_room = jsonhandler.load_json(self.MAPPING_FILE_NAME) + return + + self.write_to_influxDB(msg,metadate) + + + def write_to_influxDB(self, msg : dict, metadate: dict): + try: + self.influx_writer.write_point( + measurement=self.MEASUREMENT_NAME, + tags={ + self.TAG_ROOM : self.mac_to_room[metadate["mac-address"]], + self.TAG_MAC: metadate["mac-address"] + }, + fields={ + self.FIELD_CO2: msg["co2"], + self.FIELD_TEMP: msg["temp"], + self.FIELD_HUMIDITY: msg["rh"], + }, + timestamp=metadate["time"], #fix + ) + print("Wrote to InfluxDB:", msg) # muss später rausgeschmiessen werden + except Exception as e: + self.logger.error(f"Failed writing to InfluxDb: {e}") + + def start(self): + self.client.connect(self.broker_url) + self.client.loop_forever() diff --git a/mqtt/src/mqtt_influx_backend/mac_to_room.json b/mqtt/src/mqtt_influx_backend/mac_to_room.json new file mode 100644 index 0000000000000000000000000000000000000000..94dcdcf82e1112dbfc011e6aeb91e37db2b7b8a5 --- /dev/null +++ b/mqtt/src/mqtt_influx_backend/mac_to_room.json @@ -0,0 +1,9 @@ +{ + "AA:BB:CC:DD:EE:FF": "Wohnzimmer", + "11:22:33:44:55:66": "K\u00fcche", + "77:88:99:AA:BB:CC": "Schlafzimmer", + "DE:AD:BE:EF:12:34": "", + "DK:AD:BE:EF:12:34": "", + "EK:AD:BE:EF:12:34": "", + "lK:AD:BE:EF:12:34": "" +} \ No newline at end of file diff --git a/mqtt/src/mqtt_influx_backend/main.py b/mqtt/src/mqtt_influx_backend/main.py new file mode 100644 index 0000000000000000000000000000000000000000..168546e063365924d7877ef27373e195344e4e1b --- /dev/null +++ b/mqtt/src/mqtt_influx_backend/main.py @@ -0,0 +1,28 @@ +from dotenv import load_dotenv +import os +import logging +from src.mqtt_influx_backend.mQTTClientHandler import MQTTClientHandler +from src.mqtt_influx_backend.influxDBWriter import InfluxDBWriter + +load_dotenv() + +def main(): + + influx_writer = InfluxDBWriter( + url=os.getenv("INFLUXDB_URL"), + token=os.getenv("INFLUXDB_TOKEN"), + org=os.getenv("INFLUXDB_ORG"), + bucket=os.getenv("INFLUXDB_BUCKET"), + ) + + mqtt_handler = MQTTClientHandler( + broker_url=os.getenv("MQTT_BROKER_URL"), + topic=os.getenv("MQTT_TOPIC"), + influx_writer=influx_writer, + ) + + mqtt_handler.start() + + +if __name__ == "__main__": + main() diff --git a/mqtt/uv.lock b/mqtt/uv.lock new file mode 100644 index 0000000000000000000000000000000000000000..91d64efb95c8e697807398c55e93edacc1939a89 --- /dev/null +++ b/mqtt/uv.lock @@ -0,0 +1,123 @@ +version = 1 +revision = 1 +requires-python = ">=3.10" + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "influxdb-client" +version = "1.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "python-dateutil" }, + { name = "reactivex" }, + { name = "setuptools" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/47/b756380917cb4b968bd871fc006128e2cc9897fb1ab4bcf7d108f9601e78/influxdb_client-1.48.0.tar.gz", hash = "sha256:414d5b5eff7d2b6b453f33e2826ea9872ea04a11996ba9c8604b0c1df57c8559", size = 386415 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/b3/1edc89584b8d1bc5226cf508b67ab64da3ba83041cab348861e6f4392326/influxdb_client-1.48.0-py3-none-any.whl", hash = "sha256:410db15db761df7ea98adb333c7a03f05bcc2ceef4830cefb7071b888be2b827", size = 746177 }, +] + +[[package]] +name = "mqtt-influx-backend" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "influxdb-client" }, + { name = "paho-mqtt" }, + { name = "python-dotenv" }, +] + +[package.metadata] +requires-dist = [ + { name = "influxdb-client" }, + { name = "paho-mqtt" }, + { name = "python-dotenv" }, +] + +[[package]] +name = "paho-mqtt" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/15/0a6214e76d4d32e7f663b109cf71fb22561c2be0f701d67f93950cd40542/paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834", size = 148848 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/cb/00451c3cf31790287768bb12c6bec834f5d292eaf3022afc88e14b8afc94/paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee", size = 67219 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, +] + +[[package]] +name = "reactivex" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/63/f776322df4d7b456446eff78c4e64f14c3c26d57d46b4e06c18807d5d99c/reactivex-4.0.4.tar.gz", hash = "sha256:e912e6591022ab9176df8348a653fe8c8fa7a301f26f9931c9d8c78a650e04e8", size = 119177 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/3f/2ed8c1b8fe3fc2ed816ba40554ef703aad8c51700e2606c139fcf9b7f791/reactivex-4.0.4-py3-none-any.whl", hash = "sha256:0004796c420bd9e68aad8e65627d85a8e13f293de76656165dffbcb3a0e3fb6a", size = 217791 }, +] + +[[package]] +name = "setuptools" +version = "78.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, +]