From a4aa5bd2323ce03c27b87d8201d580ce72d9c533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20X=2E=20F=C3=BCrst?= Date: Sat, 25 Jul 2026 14:16:32 +0000 Subject: [PATCH] Readme und noise.tgz dazu --- Laerm_API.md | 255 ++++++++++++++++++++++ README.md | 122 ++++++++++- noise.tgz | Bin 0 -> 1399 bytes noisesensors/.env.example | 13 ++ noisesensors/data/mongo/entries/create.js | 8 + noisesensors/docker-compose.yml | 84 +++++++ 6 files changed, 475 insertions(+), 7 deletions(-) create mode 100644 Laerm_API.md create mode 100644 noise.tgz create mode 100644 noisesensors/.env.example create mode 100644 noisesensors/data/mongo/entries/create.js create mode 100644 noisesensors/docker-compose.yml diff --git a/Laerm_API.md b/Laerm_API.md new file mode 100644 index 0000000..18115ec --- /dev/null +++ b/Laerm_API.md @@ -0,0 +1,255 @@ +# Lärm-API +rxf 2023-11-29 + +Über diese API können Lärm-Daten für alle Sensoren direkt aus der Datenbank ausgelesen werden. Es existieren verschiedene Aufrufe. +Im folgenden wird der HOST, auf dem das Programm läuft (der Docker-Container *noise*) als **\** dargestellt. + +### Live - Messdaten +#### Aufruf +/api/getsensordata?sensorid=xxx&csv=true&span=xxx&datetime=xxxx&pretty> +oder ohne Parameter +/api/getsensordata> + +#### Parameter +Alle Parameter sind optional und werden mit sinnvollen Defaults belegt, wenn sie fehlen. + + * ***sensorid:*** Nummer des Sensors, für den die Daten abgerufen werden sollen. Ist der Sensor nicht in der Datenbank, wird ein leeres Werte-Array zurückgegeben. Default = 1 (Sensor 1 existiert nicht, also wird bei fehlender Sensornummer ein leeres Array zurück gegeben) + * ***span:*** Zeitspanne in Tagen, für die die Daten ausgegeben werden. Default = 1, max = 30. + * ***datetime*** Zeitpunkt, ab dem die Ausgabe beginnen soll. Die Zeitangabe erfolgt im ISO8601-Format (also YYY-MM-HHThh:mmZ). + * Ist dieser Wert angegeben, so gilt für die zu berechnende Zeitspanne: + Startzeitpunkt = ***datetime***, Endzeitpunkt = ***datetime + span (Tage)*** + * Ist dieser Wert **nicht** angegeben, so berechnet sich die Zeitspanne zu: + Startzeitpunkt = ***aktuelle Zeit - span (Tage)***, Endzeitpunkt = ***aktuelle Zeit*** + * ***csv:*** Ist ***csv=true*** angegeben, so werden die Daten als CSV-Datei ausgegeben. Ohne das ***csv*** werden die Daten als JSON ausgegeben. Wird bei JSON-Ausgabe noch der Parameter **pretty** angegeben, so werden die JSON-Daten in einem gut lesbaren Format ausgegeben. + + +#### Ausgabe +Das Ausgabe-Format ist entweder ein JSON-Dokument oder eine CSV-Datei. +Das JSON-Dokument hat z.B. folgenden Aufbau: + +``` +{ + "err": null, + "options": { + "sid": 37833, + "indoor": 0, + "span": 1, + "start": "2023-11-28T14:21:21Z", + "data": "live", + "count": 73 + }, + "values": [ + { + "datetime": "2023-11-29T10:55:56Z", + "LA_min": 35.94, + "LA_max": 47.12, + "LAeq": 39.74, + "E10tel_eq": 9418.895965228417 + }, + { + "datetime": "2023-11-29T11:07:47Z", + "LA_min": 36.45, + "LA_max": 52, + "LAeq": 41.53, + "E10tel_eq": 14223.287871228213 + }, + ... + ] +} +``` +und als CSV-Datei: + +``` +datetime,LAeq,LAmax,LAmin,"10^(LAeq/10)" +2020-08-10T09:37:29,52.5,68.75,39.58,177827.94100389228 +2020-08-10T09:39:59,44.84,67.48,39.21,30478.94989627983 +2020-08-10T09:42:31,51.68,64.79,39.58,147231.25024327193 +2020-08-10T09:45:02,44.43,56.95,39.46,27733.20104651838 +2020-08-10T09:47:32,50.15,64.87,38.95,103514.2166679343 +2020-08-10T09:50:03,47.17,58.74,39.78,52119.47111050811 +... +2020-08-11T09:31:00,55.05,71.6,40.13,319889.51096913975 +2020-08-11T09:33:31,53.91,66.21,40.62,246036.76041476274 +``` + +### Auswertungen der Messdaten +------------------ +#### Aufruf +/api/getsensordata?sensorid=xxx&data=xxx&peak=70&csv=true&span=xxx&datetime=xxxx&pretty> + +#### Parameter +Die Parameter sind die gleichen wie oben und optional. +Als zusätzlicher Parameter kommt hier ***data*** hinzu. Darüber wird ausgewählt, welche Auswertung verwendet werden soll. + +Die Bedeutung der einzelnen Werte für ***data***: + +* ***data=live***: Ergibt die identische Ausgabe wie oben, d.h. data=live kann auch weggelassen werden. +* ***data=havg***: Ausgabe der Stunden-Mittelwerte. In der Augabe wird die Anzahl der Überschreitungen (als ***peakcount***) des Lärm-Pegels über einen bestimmten db-Wert (default = 70) angegeben. Mit dem Parameter ***peak=xxx*** kan dieser db-Wert eingestellt werden. +* ***data=davg***: Ausgabe der Tages-Mittelwerte. Für die Spitzenwert-Überschreitungen (***peakcount***) gilt das Gleiche wie bei Stundenmittel. +* ***data=daynight***: Ausgabe der Tages- und der Nacht-Mittelwerte. Die Tagesmittel werden von 6:00 bis 22:00 Uhr und die Nachtmittel von 22:00 bis 6:00 Uhr berechnet. +* ***data=lden***: Ausgabe des LDEN-Mittelwertes. Hier wird der Tag weiter aufgeteilt: Tag (D) ist von 6:00 bit 18:00 Uhr, Abend (E) ist von 18:00 bis 22:00 Uhr und Nacht (N) geht von 22:00 bis 6:00 Uhr. Die einzelnen Mittelwerte werden noch unterschiedlich bewertet und daraus dann der gesamte Mittelwert (der LDEN) berechnet. + +#### Ausgabe +Hie sind die Augaben ebenfalls als JSON-Dokument oder als CSV-Datei (mit der Option ***csv=true***) abrufbar. + +Stundenmittel (JSON): + +``` +{ + "err": null, + "options": { + "sid": 37833, + "indoor": 0, + "span": 7, + "start": "2023-11-22T14:30:08Z", + "data": "havg", + "peak": 70, + "count": 168 + }, + "values": [ + { + "peakcount": 0, + "datetime": "2023-11-24T00:00:00Z", + "n_AVG": 38.42796079826682 + }, + { + "peakcount": 0, + "datetime": "2023-11-24T01:00:00Z", + "n_AVG": 37.946929500486355 + }, + .... + ] +} + +``` +Stundenmittel (CSV): + +``` +datetime,n_AVG,peakcount +2020-07-11T22:00:00Z,42.760062147998624,0 +2020-07-11T23:00:00Z,40.712802683893074,0 +2020-07-12T00:00:00Z,40.71753006698157,1 +2020-07-12T01:00:00Z,39.25606411190053,0 +``` + +Tagesmittel (JSON): + +``` +{ + "err": null, + "options": { + "sid": 37833, + "indoor": 0, + "span": 30, + "start": "2023-10-30T00:00:00Z", + "data": "davg", + "peak": 70, + "count": 30 + }, + "values": [ + { + "datetime": "2023-11-24T00:00:00Z", + "n_AVG": 43.21691983032072, + "peakcount": 39 + }, + { + "datetime": "2023-11-25T00:00:00Z", + "n_AVG": null, + "peakcount": 7 + }, + ... + ] +} +``` +Tagesmittel (CSV): + +``` +datetime,n_AVG,peakcount +2020-07-11T00:00:00Z,41.698517715727675,0 +2020-07-12T00:00:00Z,47.33575272178947,32 +2020-07-13T00:00:00Z,54.63937702461399,94 +``` + +Tag-Nacht-Mittel (JSON) + +``` +{ + "err": null, + "options": { + "sid": 37833, + "indoor": 0, + "span": 30, + "start": "2023-10-30T00:00:00Z", + "data": "daynight", + "count": 30 + }, + "values": [ + { + "datetime": "2023-11-24T00:00:00Z", + "n_dayAVG": 43.900464786300105, + "n_nightAVG": 40.851023645531406 + }, + { + "datetime": "2023-11-25T00:00:00Z", + "n_dayAVG": null, + "n_nightAVG": null + }, + ... + ] +} + +``` +Tag-Nacht-Mittel (CSV) + +``` +datetime,n_dayAVG,n_nightAVG +2020-07-11T22:00:00Z,0,40.93231330612532 +2020-07-12T06:00:00Z,49.14770564689205,46.99391595352652 +2020-07-13T06:00:00Z,56.305828057753864,64.57989823641728 +2020-07-14T06:00:00Z,63.62474952627255,43.17808436755339 +... +``` + +LDEN (JSON) + +``` +{ + "err": null, + "options": { + "sid": 37833, + "indoor": 0, + "span": 30, + "start": "2023-10-30T00:00:00Z", + "data": "lden", + "count": 30 + }, + "values": [ + { + "datetime": "2023-11-24T00:00:00Z", + "lden": null + }, + { + "datetime": "2023-11-25T00:00:00Z", + "lden": null + }, + ... + ] +} +``` +LDEN (CSV) + +``` +datetime,lden +2020-07-11T22:00:00Z,46.161208717927906 +2020-07-12T06:00:00Z,53.79509744837196 +2020-07-13T06:00:00Z,69.9515379149476 +2020-07-14T06:00:00Z,62.02707812443635 +2020-07-15T07:00:00Z,56.98105132364213 +``` +#### Daten abspeichern +Um die ausgelesenen Messdaten in eine Datei abzuspeichern, kann am Einfachsten das Programm **curl** verwendet werden. Sollen z.B. die aktuellen Daten des Sensors *37833* als JSON in die Datei **sensor_37833.json** gespeichert werden, so sieht der Aufruf folgendermaßen aus: + +~~~~ +curl -o sensor_37833.json "https:///api/getsensordata?sensorid=37833" +~~~~ +Entprechendes gilt für die anderen Auswertungen. \ No newline at end of file diff --git a/README.md b/README.md index f1c5bfb..72af019 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,118 @@ -# laermsensor-stack +# Projekt Lärm-Sensoren + +## Installation der Container +### Dateien +#### Directory-Struktur +Die Datei **noise.tgz** enthält die folgenden Dateien und Verzeichnisse: + +~~~ + +| ++- noisesensors + | + +- docker-compose.yml + +- log + +- data + | +- mongo + | | +- data + | | +- entries + | | | +- create.js + | + influx + | | + data + +: file, : directory +~~~ + +* **docker-compose.yml** +enthält die Anweisungen für die Docker-Container. Dies kann so an das aktuelle Portainer-Stack angehängt werden. +* **log** +Ablage für die Logdateien der einzelnene Container (**noise.log**, **sensorapi.log** und **readin.log**) . Diese werden täglich komprimiert. Es werden Versionen für 7 Tage aufgehoben. +* **data** +hier darunter befinden sich alle Daten, sowohl die Datenbanken als auch Konfigurationen. Außerdem werden jeweils die laufend eingelesenen Daten als **actdata.json** abgelegt. Diese werden bei jedem neuen Einlesen wieder überschrieben. +* **data/mongo** +Verzeichnis für die Mongo-Datenbank +* **data/mngo/data** +hier befinden sich die eingelesenen Daten +* **data/mongo/entries** +hier liegt die Datei **create.js**, mit deren Hilfe bei ersten Start der Datenbank die Collections und die Indexe erzeugt werden. +* **data/influx** +Verzeichnis für die Influx-Datenbank +* **data/influx/data** +Hier dann die eingelesenen Daten + + +### Aufrufe +Entpacken des Tar-Files **noise.tgz** in einem beliebigen Verzeichnis (das Direktoty *noisesensors* wird angelegt). Der komplette Pfad zu dem Verzeichnis (also ab root) muss in der Environmentvariablen **LOCALDIR** hinterlegt werden. + +Nun das *docker-compose.yml* in das Portainer-Stack einfügen. Zusätzlich folgende Environment-Variablen festlegen: + +~~~ +DOCKER_INFLUXDB_INIT_USERNAME= +DOCKER_INFLUXDB_INIT_PASSWORD= +DOCKER_INFLUXTOKEN= +MONGO_ROOT_USERNAME= +MONGO_ROOT_PASSWORD= +LOCALDIR= +STORE=influx +~~~ +\, \, \, \ und \ sind anzupassen. + +Danach einloggen in das Docker-Registry auf *citysensor.de*: + +~~~ +docker login docker.citysensor.de +~~~ +Einloggen mit Username und Passwort (siehe mail), anschließend kann das Portainer-Stack gestartet werden (die Container werden beim ersten mal automatisch von *docker.citysensor.de* herunter geholt). + +Damit sollte dann Alles laufen. Das Programm holt alle 5min die neuesten Daten von *sensor.community* ab und speichert die Messwerte in der InfluxDB und die Metadaten des Sensors in der Mongo-Datenbank. +Das Logfile kann via **tail -f log/noise.log** im Verzeichnise **noisesensors** mitgeschrieben werden. + + +### Container +* **readin** +Über diesen Container werden alle 5 min. die aktuellen Daten von *sensor.community* abgeholt und in den beiden Datenbanken gespeichert + +* **logrotate** +Mit Hilfe dieses Containers werde die Log-Files im Unterverzeichnis *log* täglich rotiert. Es werden 7 Versionen behalten, ältere gelöscht. + +* **sensorapi** +Über diesen Container werde die Daten aus der Mongo-Datenbank ausgelesen, aufbereitet und als JSON- oder CSV-Datei ausgegeben. + +* **noise** +Mit diesem Container wird die Grafik aufbereitet. Auch kann darüber die API (im Container *sensorapi*) angesprochen und die Daten abgerufen werden. (Für die Parameter der Anfrage siehe die extra Beschreibung **Laerm_API**). Dieser Container sollte von außen über einen Domainnamen erreichbar sein. Ansonsten hört er auf **Port 3003** auf HTTP-Anfragen. + +* **mongodb** +Die Mongo-Datenbank. Es gibt eine Datenbank namens **sensor_data**. Diese hat folgende Collections: + * properties + Hier liegen für alle Sensoren die Metadaten (wie Ortskoordinaten, Typ, Zeitpunkt der letzten Übertragung etc.) + * statistics + Dient nur Kontrolle, schreibt verschiedenen Statistikdaten wie Dauer des Einlesens, Dauer des Speicherns in den DBs etc. + **Zugriff:** + Am Besten über das Programm **MongoDB Compass**. Zugangsdaten entprechend der im Portainer hinterlegten. + +* **influxdb** +Die Influx-Datenbank. Hier werden in dem Bucket *sensor_data* die reinen Messwert der einzelnen Sensoren gespeichert. +**Zugriff:** +http://\:8086 +Logindaten: Entsprechen der im Portainer hinterlegten. + + +## Sourcen für die Container + +Das gesamte Projekt ist im GitHub unter **laermsensor-stack** abgelegt. Für jeden der 3 Container existiert darunter ein Verzeichnis: +**readin**, **sensorapi** und **noise**. In diesen Verzeichnisse sind alle benötigten Sourcen enthalten. + +Sollte was geändert werden, so muss mit **deploy.sh** der Container neu erzeugt und auf die Registry (siehe oben) gepushed werden. + +Das vierte hier liegende Verzeichnis **noisesensors** ist nur dummy, es spiegelt die Dateistruktur für das aktuelle Stack ab, wie oben beschrieben (wird mit Hilfe von **noise.tgz** erzeugt). + +## Versionen + + Version | Datum | Bemerkung +---- | ---- | ---- + 1.0.0 | 2023-11-30 | + 2.0.0 | 2026-07-25 | Anpassung an Stack-Änderungen + -Monorepo fuer den noisesensors-Stack (esprimo, Dockge: /opt/stacks/esprimo/noisesensors). -- **readin/** – Sensordaten von sensor.community -> InfluxDB/MongoDB (Cron). Image `docker.citysensor.de/readin` -- **sensorapi/** – Backend-API (liest Influx/Mongo). Image `docker.citysensor.de/sensorapi` -- **noise/** – Web-Frontend (laerm_web, nutzt sensorapi). Image `docker.citysensor.de/noise` -Deploy je Service: `cd && ./deploy.sh` (nativer amd64-Build -> Registry), -dann `cd /opt/stacks/esprimo/noisesensors && docker compose up -d `. diff --git a/noise.tgz b/noise.tgz new file mode 100644 index 0000000000000000000000000000000000000000..78c12b88bf36c76ca10e29bd5352d349c36121d2 GIT binary patch literal 1399 zcmV--1&I0|iwFP!000001MOLDZ{j!*?q`05m0O8cY9RzdR#91r7IqhH+f+bxZ+k+4 zxU7$0M~>;Xs`zL3?f%$}Z9+*4q}`?LosRjC7teTRJkJb_9m+_IV@zYl{7KOy7CuL4kKbOemCNG)M~N=tAF|64GH9tiqsX2iHy zaLTY8NH14i4K+GoRQQBq-lP6FS<`GXrZiGy(^?hehIjg@ z*@pGMKfC$`A|M0NJNFt*7sXgiBD78}iKYv#treKA(~?AXrIvK)S7`CtA6zsb;!X!; zo#@FG40h!LA1BgM@p|LDbTvVe|gwjbd)0WN+w6q2ifIvk~YVZR>Q(2?S zweYaALV$}}ZWX2~Yd?COZo0$}(mrcWVtB^t{(9*moO03VuAI6N)>3VGiuzomF z8mEhv>Q~*5E)sl#7e}u;?MpdrS@F4RDl75w8S#F8fj~nws|hxIUOBl%7Kx%CMd}SL z2T#L*GJg@&bTG}C@C8o68xlIY^LbF5v%h6&aEVuC!Hxrc$!gGx;%* z|5nNLtkQgiN?L^yFjYLV|4lUa z#Ql#a=;{CY=54%S|5vfAmfZh2rnB4sZG%>|*_*WSBK#c(@UNKV z$`1c+P{y`xBYE%={HuEZZ^O_I|1D4!{sZVW1upy)Wa85$N1R2N3s_=aNj$u#^%@}?_;N~t1jbxnwlq8x28?mo+W|XV~n`#2S;s4*M?9TtULMzYnHfZBz{lCKBvPwJrw?S*qb2pR(FUQ|5m3QBN zw?ZlW3z*&%@U1_NLcBp6S^b{_o4xe+e_j8({_pkaCv?hj0Npv(E0MqSM$D9jw*$C5jx!ejL&t;tE +DOCKER_INFLUXDB_INIT_PASSWORD= +DOCKER_INFLUXTOKEN= + +# Credentials für die Mongo-DB (in der werden die Metadaten eines Sensors gepeichert) +MONGO_ROOT_USERNAME= +MONGO_ROOT_PASSWORD= + +LOCALDIR= + +# Datenbank, in welche die laufenden Messwerte gespeichert werden (kannn 'influx' oder 'mongo' sein) +STORE=influx diff --git a/noisesensors/data/mongo/entries/create.js b/noisesensors/data/mongo/entries/create.js new file mode 100644 index 0000000..ef90ae9 --- /dev/null +++ b/noisesensors/data/mongo/entries/create.js @@ -0,0 +1,8 @@ +db = db.getSiblingDB("sensor_data") +db.createCollection('noise_sensors',{timeseries: {timeField: 'datetime', metaField: 'sensorid'}}) +db.noise_sensors.createIndex({datetime: 1}) +db.noise_sensors.createIndex({sensorid: 1}) +db.noise_sensors.createIndex({datetime: 1, sensorid: 1}) +db.createCollection('properties') +db.properties.createIndex({"location.id": 1}) +db.properties.createIndex({"location.loc.coordinates": "2dsphere"}) diff --git a/noisesensors/docker-compose.yml b/noisesensors/docker-compose.yml new file mode 100644 index 0000000..b84eedc --- /dev/null +++ b/noisesensors/docker-compose.yml @@ -0,0 +1,84 @@ +services: + mongodb: + image: mongo:7 + volumes: + - ${LOCALDIR}/noisesensors/data/mongo/entries:/docker-entrypoint-initdb.d + - ${LOCALDIR}/noisesensors/data/mongo/data:/data/db + ports: + - "27017:27017" + container_name: mongodb + environment: + - MONGO_INITDB_DATABASE=sensor_data + - MONGO_INITDB_ROOT_USERNAME=${MONGO_ROOT_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_ROOT_PASSWORD} + restart: unless-stopped + + influxdb: + image: influxdb:2.0 + ports: + - '8086:8086' + volumes: + - ${LOCALDIR}/noisesensors/data/influx/data:/var/lib/influxdb2 + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: ${DOCKER_INFLUXDB_INIT_USERNAME} + DOCKER_INFLUXDB_INIT_PASSWORD: ${DOCKER_INFLUXDB_INIT_PASSWORD} + DOCKER_INFLUXDB_INIT_ORG: citysensor + DOCKER_INFLUXDB_INIT_BUCKET: sensor_data + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${DOCKER_INFLUXTOKEN} + restart: + unless-stopped + container_name: influxdb + + readin: + image: docker.citysensor.de/readin + environment: + MONGOHOST: mongodb + INFLUXHOST: influxdb + TYP: "[\"noise\"]" + STORE: ${STORE:-mongo} + MONGOAUTH: true + MONGOUSRP: ${MONGO_ROOT_USERNAME}:${MONGO_ROOT_PASSWORD} + INFLUXTOKEN: ${DOCKER_INFLUXTOKEN} + volumes: + - ${LOCALDIR}/noisesensors/log:/var/log + - ${LOCALDIR}/noisesensors/data:/opt/app/data + container_name: readin + restart: unless-stopped + + logrotate: + image: linkyard/logrotate + volumes: + - ${LOCALDIR}/noisesensors/log:/logs + environment: + LOGROTATE_SIZE: "1k" + LOGROTATE_ROTATE: "7" + LOGROTATE_CRON: "daily" + restart: unless-stopped + container_name: logrotate + + sensorapi: + image: docker.citysensor.de/sensorapi + container_name: sensorapi + volumes: + - ${LOCALDIR}/noisesensors/log:/var/log + environment: + INFLUXHOST: influxdb + INFLUXTOKEN: ${DOCKER_INFLUXTOKEN} + DBASE: influx + MONGOHOST: mongodb + MONGOAUTH: true + MONGOUSRP: ${MONGO_ROOT_USERNAME}:${MONGO_ROOT_PASSWORD} + restart: unless-stopped + + noise: + image: docker.citysensor.de/noise + volumes: + - ${LOCALDIR}/noisesensors/log:/var/log + container_name: noise + environment: + APIHOST: http://sensorapi:3005 + ports: + - "3003:3000" + restart: unless-stopped +