Ei kuvausta

Medowar 9efeed3a66 reworking webhook functionality 1 kuukausi sitten
admin 9efeed3a66 reworking webhook functionality 1 kuukausi sitten
api e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten
api-docs e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten
data 9efeed3a66 reworking webhook functionality 1 kuukausi sitten
docs 9efeed3a66 reworking webhook functionality 1 kuukausi sitten
src 9efeed3a66 reworking webhook functionality 1 kuukausi sitten
.gitignore e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten
.htaccess e523335153 fixing htaccess not passing authentication 1 kuukausi sitten
README.md e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten
app.js e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten
index.php e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten
openapi.yaml 9efeed3a66 reworking webhook functionality 1 kuukausi sitten
setup_testenv.sh 424def56b7 adding setup for testenv 1 kuukausi sitten
styles.css e03c0452a7 Enhance application configuration and error logging 1 kuukausi sitten

README.md

Getränkeautomat Monitor

Getränkeautomat Monitor ist eine kleine PHP/HTML/JS-Anwendung zur Überwachung von Füllständen in Getränkeautomaten. Ein ESP32 oder ein anderer Sensor-Client sendet Messwerte an eine einfache REST-nahe API. Die Anwendung berechnet daraus den geschätzten Bestand pro Fach, visualisiert den aktuellen Status im Browser und löst 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

Dokumentation

Funktionsumfang

  • mehrere Automaten in einer Instanz
  • mehrere Fächer pro Automat
  • REST-nahe Eingangs-API für Einzelmessungen
  • Dashboard mit visueller Füllstandsanzeige
  • Alarmierung per Webhook und per Email
  • Adminpanel zur Bearbeitung der JSON-Config
  • statischer Admin-Login mit bcrypt-Hash

Architektur im Überblick

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 über machine_id und sensor_id.
  3. Aus full_distance_mm, empty_distance_mm und distance_per_unit werden Füllstand 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 ändert, wird ein Alarmereignis erzeugt.
  6. Dashboard und Adminpanel lesen den aktuellen Zustand über GET /api/v1/status.php.

Projektstruktur

Die Anwendung ist jetzt so aufgebaut, dass der gesamte Projektordner direkt als Apache-Unterordner deployt werden kann.

  • index.php, admin/, api/, api-docs/, app.js, styles.css, openapi.yaml
    • öffentliche Runtime-Dateien
  • src/
    • interne PHP-Kernlogik
    • wird auf Apache-Hosting per .htaccess gesperrt
  • data/
    • JSON-Dateien für Konfiguration, aktuellen Zustand und Alarmhistorie
    • enthält zusätzlich php_errors.log für PHP-Fehler
    • liegt im selben Ordner wie die App, ist aber per .htaccess gesperrt
  • docs/
    • Markdown-Dokumentation für Entwickler
    • wird auf Apache-Hosting ebenfalls per .htaccess gesperrt

Wichtige Dateien:

  • index.php: Dashboard
  • admin/index.php: Login und Adminpanel
  • api/v1/readings.php: API für eingehende Sensorwerte
  • api/v1/status.php: Status-API für Dashboard und Adminpanel
  • api-docs/index.html: Swagger UI
  • openapi.yaml: OpenAPI-Spec
  • src/MonitorService.php: zentrale Orchestrierung für Lesen, Berechnen und Status
  • src/InventoryService.php: Berechnung von Füllgrad 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
  • data/php_errors.log: PHP-Fehlerlog der Anwendung

Voraussetzungen

Für den Betrieb wird auf dem Zielsystem benötigt:

  • PHP 8.1 oder neuer empfohlen
  • Schreibrechte auf data/
  • Apache mit aktivem .htaccess-Support für die Produktionsbereitstellung
  • funktionierende Mail-Konfiguration, falls mail() für Email-Alarme genutzt werden soll
  • Webserver oder PHP Built-in Server

PHP schreibt Laufzeitfehler in data/php_errors.log.

Schnellstart

Sobald PHP auf dem Zielsystem installiert ist:

php -S localhost:8000

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/api-docs/
  • OpenAPI-Spec: http://localhost:8000/openapi.yaml

Hinweis: Der PHP Built-in Server wertet .htaccess nicht aus. Die Verzeichnisse src/, data/ und docs/ werden auf Shared Hosting erst durch Apache geschützt.

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 öffentliche Visualisierung der Anlage. Es zeigt:

  • Anzahl Automaten, Fächer und aktuell kritische Fächer
  • Filter nach Automat
  • pro Fach:
    • Label
    • Produktname
    • visuellen Füllstand
    • geschätzte Anzahl Flaschen
    • Alarmgrenze
    • letzten Messwert in Millimetern
    • letzten Messzeitpunkt
  • letzte Alarm- und Entwarnungsereignisse

Die Daten werden regelmäßig per Polling über status.php aktualisiert. Das Intervall wird in config.json über app.dashboard_refresh_seconds gesteuert.

Adminpanel

Das Adminpanel ist über /admin/ erreichbar und erlaubt:

  • Ändern des App-Namens und des Refresh-Intervalls
  • Ändern des API-Bearer-Tokens
  • Ändern von Admin-Benutzername und Passwort
  • Verwalten von Webhooks
  • Verwalten von Email-Empfängern
  • Verwalten von Automaten und ihren Fächern

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 ausgelöst, 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:

units_estimated < alert_below_units

Ein Fach mit alert_below_units = 2 löst also erst dann Alarm aus, wenn nur noch 1 oder 0 Einheiten geschätzt 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 größere Zahlen "voll" bedeuten, weil die Berechnung die Sensororientierung automatisch berücksichtigt.

Die grobe Logik ist:

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 repräsentiert distance_per_unit die Änderung des Messwerts pro Flasche oder Einheit.

API-Referenz

Die API ist jetzt auf drei Ebenen dokumentiert:

  • Interaktive Swagger UI unter http://localhost:8000/api-docs/
  • Maschinenlesbare OpenAPI-Spec unter http://localhost:8000/openapi.yaml
  • Erläuternde Referenz in docs/API.md

POST /api/v1/readings.php

Nimmt genau einen Messwert entgegen.

Header:

  • Authorization: Bearer <token>
  • Content-Type: application/json

Body:

{
  "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:

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:

{
  "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 Fächer inklusive letzter Alarmereignisse.

Konfiguration

Die Konfiguration liegt in data/config.json. Eine detaillierte Beschreibung aller Felder steht in docs/CONFIG.md.

Die obersten Bereiche sind:

  • app: Name, Zeitzone, UI-Refresh, Email-Absender
  • api: Bearer-Token für Sensor-Clients
  • admin: Benutzername und Passwort-Hash
  • alerts: wiederverwendbare Webhooks und Email-Empfänger
  • machines: Automaten mit ihren Fächern

Zustandsdateien

data/state.json

Diese Datei enthält den letzten bekannten Zustand jedes Fachs. Sie wird von der API bei jeder gültigen Messung aktualisiert.

Typische Inhalte:

  • letzter Messwert
  • letzter Messzeitpunkt
  • geschätzter Bestand
  • maximaler Bestand
  • aktueller Status
  • verknüpfte Alarmkanal-IDs

data/alert_log.json

Diese Datei enthält die zuletzt ausgelösten Alarmereignisse und Entwarnungen inklusive Lieferstatus für Webhooks und Emails.

JSON-Persistenz und Dateisperren

Die Anwendung nutzt für Schreibzugriffe Dateisperren über flock(). Das reduziert das Risiko beschädigter JSON-Dateien bei parallelen Requests, zum Beispiel wenn mehrere Sensoren fast gleichzeitig Messwerte senden.

Betriebshinweise

  • data/ sollte nicht direkt öffentlich über 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 für spätere Versionen

  • Batch-Endpoint für mehrere Sensorwerte in einem Request
  • Langzeithistorie mit Zeitreihen oder Charts
  • Benutzerverwaltung statt statischem Admin-Login
  • Retry-Mechanismus für fehlgeschlagene Webhooks
  • Healthcheck-Endpunkte
  • CSV- oder PDF-Export

Weitere Dokumentation