# Getraenkeautomat Monitor Getraenkeautomat Monitor ist eine kleine PHP/HTML/JS-Anwendung zur Ueberwachung von Fuellstaenden in Getraenkeautomaten. Ein ESP32 oder ein anderer Sensor-Client sendet Messwerte an eine einfache REST-nahe API. Die Anwendung berechnet daraus den geschaetzten Bestand pro Fach, visualisiert den aktuellen Status im Browser und loest bei kritischen Schwellwerten Alarme aus. Die Anwendung ist bewusst simpel gehalten: - Backend mit plain PHP - Frontend mit HTML, CSS und Vanilla JavaScript - Persistenz nur in JSON-Dateien - keine Datenbank - kein Framework ## Funktionsumfang - mehrere Automaten in einer Instanz - mehrere Faecher pro Automat - REST-nahe Eingangs-API fuer Einzelmessungen - Dashboard mit visueller Fuellstandsanzeige - Alarmierung per Webhook und per Email - Adminpanel zur Bearbeitung der JSON-Config - statischer Admin-Login mit bcrypt-Hash ## Architektur im Ueberblick Der Datenfluss ist einfach: 1. Ein Sensor-Client sendet einen Messwert in Millimetern an `POST /api/v1/readings.php`. 2. Die Anwendung sucht das passende Fach ueber `machine_id` und `sensor_id`. 3. Aus `full_distance_mm`, `empty_distance_mm` und `distance_per_unit` werden Fuellstand und Flaschenanzahl berechnet. 4. Der letzte bekannte Zustand wird in `data/state.json` gespeichert. 5. Wenn sich der Status von `ok` nach `critical` oder von `critical` nach `ok` aendert, wird ein Alarmereignis erzeugt. 6. Dashboard und Adminpanel lesen den aktuellen Zustand ueber `GET /api/v1/status.php`. ## Projektstruktur - `public/` - Webroot der Anwendung - enthaelt Dashboard, Adminpanel, CSS, JavaScript und API-Endpunkte - `src/` - Kernlogik der Anwendung - Konfigurationszugriff, Zustandsberechnung, Alarmierung, Authentifizierung - `data/` - JSON-Dateien fuer Konfiguration, aktuellen Zustand und Alarmhistorie Wichtige Dateien: - `public/index.php`: Dashboard - `public/admin/index.php`: Login und Adminpanel - `public/api/v1/readings.php`: API fuer eingehende Sensorwerte - `public/api/v1/status.php`: Status-API fuer Dashboard und Adminpanel - `src/MonitorService.php`: zentrale Orchestrierung fuer Lesen, Berechnen und Status - `src/InventoryService.php`: Berechnung von Fuellgrad und Bestand - `src/AlertService.php`: Webhook- und Email-Alarmierung - `data/config.json`: Hauptkonfiguration - `data/state.json`: letzter bekannter Zustand je Fach - `data/alert_log.json`: Alarm- und Entwarnungsereignisse ## Voraussetzungen Fuer den Betrieb wird auf dem Zielsystem benoetigt: - PHP 8.1 oder neuer empfohlen - Schreibrechte auf `data/` - funktionierende Mail-Konfiguration, falls `mail()` fuer Email-Alarme genutzt werden soll - Webserver oder PHP Built-in Server ## Schnellstart Sobald PHP auf dem Zielsystem installiert ist: ```bash php -S localhost:8000 -t public ``` Danach ist die Anwendung erreichbar unter: - Dashboard: `http://localhost:8000/` - Adminpanel: `http://localhost:8000/admin/` - Status-API: `http://localhost:8000/api/v1/status.php` - Swagger UI: `http://localhost:8000/docs/` - OpenAPI-Spec: `http://localhost:8000/openapi.yaml` ## Default-Zugangsdaten Die Beispiel-Konfiguration bringt absichtlich einfache Startwerte mit: - Admin-Benutzer: `admin` - Admin-Passwort: `admin123` - API-Bearer-Token: `demo-esp32-token` Diese Werte sollten direkt nach dem ersten Login angepasst werden. ## Dashboard Das Dashboard ist die oeffentliche Visualisierung der Anlage. Es zeigt: - Anzahl Automaten, Faecher und aktuell kritische Faecher - Filter nach Automat - pro Fach: - Label - Produktname - visuellen Fuellstand - geschaetzte Anzahl Flaschen - Alarmgrenze - letzten Messwert in Millimetern - letzten Messzeitpunkt - letzte Alarm- und Entwarnungsereignisse Die Daten werden regelmaessig per Polling ueber `status.php` aktualisiert. Das Intervall wird in `config.json` ueber `app.dashboard_refresh_seconds` gesteuert. ## Adminpanel Das Adminpanel ist ueber `/admin/` erreichbar und erlaubt: - Aendern des App-Namens und des Refresh-Intervalls - Aendern des API-Bearer-Tokens - Aendern von Admin-Benutzername und Passwort - Verwalten von Webhooks - Verwalten von Email-Empfaengern - Verwalten von Automaten und ihren Faechern Beim Speichern des Admin-Passworts wird immer ein bcrypt-Hash erzeugt. Das Passwort selbst wird nicht im Klartext gespeichert. ## Alarmverhalten Alarme werden nicht bei jeder eingehenden kritischen Messung ausgeloest, sondern nur bei Zustandswechseln: - `ok -> critical`: Alarm - `critical -> ok`: Entwarnung - `critical -> critical`: kein weiterer Alarm - `ok -> ok`: kein Alarm Dadurch werden doppelte Daueralarme vermieden. Ein Fach gilt als kritisch, wenn gilt: ```text units_estimated < alert_below_units ``` Ein Fach mit `alert_below_units = 2` loest also erst dann Alarm aus, wenn nur noch `1` oder `0` Einheiten geschaetzt werden. ## Wie die Bestandsberechnung funktioniert Jedes Fach hat drei wichtige Kalibrierwerte: - `full_distance_mm` - `empty_distance_mm` - `distance_per_unit` Die App behandelt `full_distance_mm` als 100 Prozent und `empty_distance_mm` als 0 Prozent. Dabei ist es egal, ob kleinere oder groessere Zahlen "voll" bedeuten, weil die Berechnung die Sensororientierung automatisch beruecksichtigt. Die grobe Logik ist: ```text fill_ratio = (distance_mm - empty_distance_mm) / (full_distance_mm - empty_distance_mm) fill_ratio wird auf 0..1 begrenzt max_units = abs(full_distance_mm - empty_distance_mm) / distance_per_unit units_estimated = round(fill_ratio * max_units) ``` Damit repraesentiert `distance_per_unit` die Aenderung des Messwerts pro Flasche oder Einheit. ## API-Referenz Die API ist jetzt auf drei Ebenen dokumentiert: - Interaktive Swagger UI unter `http://localhost:8000/docs/` - Maschinenlesbare OpenAPI-Spec unter `http://localhost:8000/openapi.yaml` - Erlaeuternde Referenz in [docs/API.md](docs/API.md) ### `POST /api/v1/readings.php` Nimmt genau einen Messwert entgegen. Header: - `Authorization: Bearer ` - `Content-Type: application/json` Body: ```json { "machine_id": "automat-lobby", "sensor_id": "fach-a1", "distance_mm": 184, "measured_at": "2026-04-15T19:20:00Z" } ``` `measured_at` ist optional. Wenn der Wert fehlt, setzt der Server die aktuelle Zeit. Beispiel: ```bash curl -X POST http://localhost:8000/api/v1/readings.php \ -H 'Authorization: Bearer demo-esp32-token' \ -H 'Content-Type: application/json' \ -d '{ "machine_id": "automat-lobby", "sensor_id": "fach-a1", "distance_mm": 184, "measured_at": "2026-04-15T19:20:00Z" }' ``` Erfolg: ```json { "ok": true, "machine_id": "automat-lobby", "sensor_id": "fach-a1", "slot_label": "A1", "units_estimated": 4, "fill_percent": 63, "state": "ok" } ``` ### `GET /api/v1/status.php` Liefert den aktuellen aggregierten Zustand aller Automaten und Faecher inklusive letzter Alarmereignisse. ## Konfiguration Die Konfiguration liegt in `data/config.json`. Eine detaillierte Beschreibung aller Felder steht in [docs/CONFIG.md](docs/CONFIG.md). Die obersten Bereiche sind: - `app`: Name, Zeitzone, UI-Refresh, Email-Absender - `api`: Bearer-Token fuer Sensor-Clients - `admin`: Benutzername und Passwort-Hash - `alerts`: wiederverwendbare Webhooks und Email-Empfaenger - `machines`: Automaten mit ihren Faechern ## Zustandsdateien ### `data/state.json` Diese Datei enthaelt den letzten bekannten Zustand jedes Fachs. Sie wird von der API bei jeder gueltigen Messung aktualisiert. Typische Inhalte: - letzter Messwert - letzter Messzeitpunkt - geschaetzter Bestand - maximaler Bestand - aktueller Status - verknuepfte Alarmkanal-IDs ### `data/alert_log.json` Diese Datei enthaelt die zuletzt ausgeloesten Alarmereignisse und Entwarnungen inklusive Lieferstatus fuer Webhooks und Emails. ## JSON-Persistenz und Dateisperren Die Anwendung nutzt fuer Schreibzugriffe Dateisperren ueber `flock()`. Das reduziert das Risiko beschaedigter JSON-Dateien bei parallelen Requests, zum Beispiel wenn mehrere Sensoren fast gleichzeitig Messwerte senden. ## Betriebshinweise - `data/` sollte nicht direkt oeffentlich ueber den Webserver auslieferbar sein. - Das Bearer-Token sollte nur an bekannte Sensor-Clients verteilt werden. - Die Beispiel-Zugangsdaten sollten in echten Umgebungen sofort ersetzt werden. - Webhooks sollten mit HTTPS betrieben werden. - Email funktioniert nur, wenn `mail()` auf dem Zielsystem korrekt eingerichtet ist. ## Typische Erweiterungen fuer spaetere Versionen - Batch-Endpoint fuer mehrere Sensorwerte in einem Request - Langzeithistorie mit Zeitreihen oder Charts - Benutzerverwaltung statt statischem Admin-Login - Retry-Mechanismus fuer fehlgeschlagene Webhooks - Healthcheck-Endpunkte - CSV- oder PDF-Export ## Weitere Dokumentation - [docs/API.md](docs/API.md) - [docs/CONFIG.md](docs/CONFIG.md)