Bisher war das Logo fest im Repository verdrahtet. Damit hat ein Update das Logo eines Kunden ueberschrieben - beim Sprung auf den modularen Stand zum Beispiel von logo.svg auf die mitgelieferte logo.png. Unter Einstellungen > Logo laesst sich jetzt ein eigenes Logo hochladen. Es erscheint in der Seitenleiste, auf der Login-Seite und auf der Zentrale-Seite, mit Vorschau auf hellem und dunklem Grund. - Datei liegt unter static/custom/ und ist per .gitignore von der Versionierung ausgenommen, ein git pull ueberschreibt sie also nicht - PNG, JPG, SVG, WEBP, GIF bis 2 MB; Format und Groesse werden geprueft - es bleibt immer genau eine Datei liegen, alte Endungen werden entfernt - fehlt die Datei, greift automatisch das mitgelieferte Logo - Upload und Zuruecksetzen liegen hinter dem Admin-Login - logo_url() ist als Jinja-Global registriert, damit auch die Seiten ohne _ctx() (Login, Zentrale) dasselbe Logo zeigen .gitignore ergaenzt: static/custom/ sowie die bisher fehlenden busylight.db-shm, busylight.db-wal und Status.csv - letztere lagen auf dem Produktivsystem als untracked herum. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
368 lines
14 KiB
Markdown
368 lines
14 KiB
Markdown
# Busylight WLED Controller
|
|
|
|
Web-Anwendung zur Steuerung von WLED-Busylights basierend auf Microsoft Teams Presence-Status, Zeiterfassung und Telefonanlage (Webhook).
|
|
|
|
Jedes WLED-Geraet verfuegt ueber zwei Segmente (oben/unten), die jeweils einem Benutzer zugeordnet werden. Die Farbe des Segments wird automatisch anhand des Anwesenheitsstatus, des Teams-Status und des Telefonstatus gesetzt.
|
|
|
|
## Features
|
|
|
|
- **Dashboard** mit Echtzeit-Uebersicht aller Busylights (Zeiterfassungs-, Teams- und Telefon-Status als Badges)
|
|
- **Benutzer-Verwaltung** mit automatischem Sync aus Microsoft Entra ID (Azure AD)
|
|
- **WLED-Geraete** hinzufuegen, bearbeiten, entfernen mit automatischem Status-Check (online/offline, LED-Anzahl, Firmware)
|
|
- **Zuordnung** von Benutzern zu WLED-Segmenten per Dropdown
|
|
- **Farb-Regeln** mit Color-Picker fuer jeden Status
|
|
- **Telefon-Webhook** fuer Telefonanlage (callstart/callend auf Port 8081)
|
|
- **Modulare Zeiterfassung** per CSV oder REST API - kompatibel mit jeder Zeiterfassung
|
|
- **Webbasierte Azure-Authentifizierung** direkt im Browser (kein Terminal noetig)
|
|
- **Zentrale (Self-Service)** - wechselnde Besetzung des Empfangs-Busylights per Durchwahl, ohne Admin-Passwort
|
|
- **Eigenes Logo** pro Kunde hochladbar, ueberlebt Updates
|
|
- **Einstellungen** fuer Azure, Polling-Intervall und Zeiterfassung
|
|
- **Protokoll** der letzten Statusaenderungen
|
|
|
|
## Funktionsweise
|
|
|
|
| Quelle | Beschreibung | Auswirkung auf WLED |
|
|
|--------|-------------|---------------------|
|
|
| Zeiterfassung | Anwesend / Abwesend / Homeoffice | Grundfarbe (gruen / aus / gelb) |
|
|
| Microsoft Teams | Verfuegbar / Beschaeftigt / Nicht stoeren / etc. | Farbe wenn anwesend |
|
|
| Telefonanlage (Webhook) | Telefoniert / Frei | Rot wenn aktiv, danach zurueck auf Zeiterfassungs-Status |
|
|
|
|
**Prioritaet:** Telefon > Teams > Zeiterfassung. Die WLED-Farbe wird nur gesetzt wenn der Mitarbeiter laut Zeiterfassung anwesend oder im Homeoffice ist.
|
|
|
|
## Voraussetzungen
|
|
|
|
- Linux-Server (Debian 13+ empfohlen)
|
|
- Python 3.10+
|
|
- Netzwerkzugriff auf die WLED-Geraete (HTTP Port 80)
|
|
- Microsoft Entra ID App-Registrierung (siehe [Azure-Einrichtung](docs/azure-einrichtung.md))
|
|
- Optional: Zeiterfassung per CSV oder REST API
|
|
- Optional: Telefonanlage mit Webhook-Unterstuetzung (z.B. Starface)
|
|
|
|
## Installation
|
|
|
|
### Schnellinstallation (Debian 13)
|
|
|
|
```bash
|
|
curl -s https://git.cynfo.net/christian/busylight_modular/raw/branch/master/install.sh | bash
|
|
```
|
|
|
|
### Manuelle Installation
|
|
|
|
```bash
|
|
# Repository klonen
|
|
git clone https://git.cynfo.net/christian/busylight_modular.git /opt/busylight
|
|
cd /opt/busylight
|
|
|
|
# Python Virtual Environment erstellen
|
|
python3 -m venv venv
|
|
source venv/bin/activate
|
|
|
|
# Abhaengigkeiten installieren
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
## Einrichtung
|
|
|
|
### 1. Azure verbinden
|
|
|
|
Detaillierte Anleitung: [docs/azure-einrichtung.md](docs/azure-einrichtung.md)
|
|
|
|
Kurzfassung:
|
|
1. App-Registrierung in Azure erstellen
|
|
2. Oeffentliche Clientflows aktivieren
|
|
3. API-Berechtigungen setzen (User.Read.All, Presence.Read.All)
|
|
4. Im Web-Interface unter **Einstellungen** die IDs eintragen
|
|
5. Auf **"Mit Azure verbinden"** klicken und im Browser anmelden
|
|
|
|
### 2. Benutzer synchronisieren
|
|
|
|
Unter **Benutzer** auf "Aus Azure synchronisieren" klicken. Anschliessend:
|
|
- Benutzer aktivieren/deaktivieren (nur aktive Benutzer werden abgefragt)
|
|
- Personalnummer zuordnen (fuer Zeiterfassung)
|
|
- Telefon-Durchwahl zuordnen (fuer Starface-Webhook)
|
|
|
|
### 3. WLED-Geraete anlegen
|
|
|
|
Unter **Tuerschilder** die Hostnamen/IP-Adressen der WLED-Busylights eintragen.
|
|
Der Status (online/offline, LEDs, Firmware) wird automatisch alle 4 Minuten geprueft.
|
|
|
|
### 4. Zuordnungen erstellen
|
|
|
|
Unter **Zuordnungen** jeden aktiven Benutzer einem WLED-Geraet und Segment zuweisen:
|
|
- Segment 0 = unten
|
|
- Segment 1 = oben
|
|
|
|
### 5. Farben anpassen
|
|
|
|
Unter **Farb-Regeln** die Farben fuer jeden Status per Color-Picker anpassen:
|
|
|
|
| Status | Beschreibung |
|
|
|--------|-------------|
|
|
| `abwesend` | Benutzer nicht anwesend |
|
|
| `homeoffice` | Benutzer im Homeoffice |
|
|
| `phone_active` | Benutzer telefoniert (Webhook) |
|
|
| `teams_available` | Teams: Verfuegbar |
|
|
| `teams_busy` | Teams: Beschaeftigt |
|
|
| `teams_donotdisturb` | Teams: Nicht stoeren |
|
|
| `teams_inacall` | Teams: In einem Anruf |
|
|
| `teams_inameeting` | Teams: In einer Besprechung |
|
|
| `teams_presenting` | Teams: Praesentiert |
|
|
| `teams_away` | Teams: Abwesend |
|
|
| `teams_offline` | Teams: Offline |
|
|
| `teams_outofoffice` | Teams: Abwesend (ganztaegig) |
|
|
| `teams_inactive` | Teams: Inaktiv |
|
|
| `teams_offwork` | Teams: Nicht bei der Arbeit |
|
|
|
|
## Zeiterfassung
|
|
|
|
Die Zeiterfassung ist modular - es gibt zwei Wege den Anwesenheitsstatus zu liefern:
|
|
|
|
### Option A: CSV-Datei
|
|
|
|
Eine CSV-Datei mit dem Anwesenheitsstatus wird periodisch gelesen. Das Format:
|
|
|
|
```
|
|
~~00100~~,~~1~~
|
|
~~00101~~,~~0~~
|
|
~~00102~~,~~2~~
|
|
```
|
|
|
|
Status-Codes (konfigurierbar in den Einstellungen):
|
|
- `~~0~~` = Abwesend
|
|
- `~~1~~` = Anwesend
|
|
- `~~2~~` = Homeoffice
|
|
|
|
Die CSV kann von jedem Zeiterfassungssystem per Script erzeugt werden.
|
|
In den **Einstellungen** den Pfad zur CSV eintragen.
|
|
|
|
### Option B: REST API
|
|
|
|
Externe Systeme koennen den Status direkt per API setzen (Port 8081):
|
|
|
|
```bash
|
|
# Status setzen
|
|
curl -X POST -H "Content-Type: application/json" \
|
|
-d '{"status":"anwesend"}' \
|
|
http://SERVER:8081/api/attendance/00100
|
|
|
|
# Alle Status abfragen
|
|
curl http://SERVER:8081/api/attendance
|
|
```
|
|
|
|
Beide Optionen koennen parallel genutzt werden.
|
|
|
|
## Telefonanlage (Webhook)
|
|
|
|
Die Anwendung startet automatisch einen Webhook-Server auf **Port 8081** fuer die Anbindung einer Telefonanlage (z.B. Starface).
|
|
|
|
### Webhook-Endpoints
|
|
|
|
```
|
|
GET/POST http://SERVER:8081/{durchwahl}/callstart - Anruf gestartet (WLED -> Rot)
|
|
GET/POST http://SERVER:8081/{durchwahl}/callend - Anruf beendet (WLED -> zurueck auf Zeiterfassungs-Status)
|
|
```
|
|
|
|
### Einrichtung
|
|
|
|
1. Unter **Benutzer** im Web-Interface die **Durchwahl** pro User eintragen (z.B. `123`)
|
|
2. In der Telefonanlage den Webhook konfigurieren:
|
|
- Anrufbeginn: `http://SERVER-IP:8081/123/callstart`
|
|
- Anrufende: `http://SERVER-IP:8081/123/callend`
|
|
|
|
### Verhalten
|
|
|
|
- `callstart`: Setzt WLED auf Telefon-Farbe (Standard: Rot), aber **nur** wenn der Mitarbeiter laut Zeiterfassung anwesend oder im Homeoffice ist
|
|
- `callend`: Setzt WLED zurueck auf den Zeiterfassungs-Status. Der Teams-Status wird beim naechsten Polling-Zyklus (15s) automatisch aktualisiert
|
|
- Waehrend eines aktiven Telefonats ueberschreibt das Teams-Polling die Farbe nicht
|
|
- Die Telefon-Farbe (`phone_active`) ist unter **Farb-Regeln** konfigurierbar
|
|
|
|
## Logo
|
|
|
|
Unter **Einstellungen > Logo** kann ein kundenspezifisches Logo hochgeladen werden. Es
|
|
erscheint in der Seitenleiste, auf der Login-Seite und auf der Zentrale-Seite.
|
|
|
|
- Formate: PNG, JPG, SVG, WEBP, GIF, maximal 2 MB
|
|
- Dargestellt wird eine Hoehe von etwa 40 Pixeln. Die Seitenleiste ist dunkel, ein Logo
|
|
mit heller Schrift oder transparentem Hintergrund wirkt dort am besten - die
|
|
Einstellungsseite zeigt eine Vorschau auf hellem und dunklem Grund.
|
|
- Die Datei liegt unter `static/custom/` und ist per `.gitignore` von der Versionierung
|
|
ausgenommen. **Ein `git pull` ueberschreibt das Kundenlogo nicht.**
|
|
- Mit **Zurueck zum Standardlogo** wird die Datei entfernt und wieder
|
|
`static/logo.png` aus dem Repository verwendet.
|
|
|
|
## Zentrale (Self-Service)
|
|
|
|
Ein Busylight im Empfangsbereich wird wechselnd besetzt. Damit die Mitarbeiter das
|
|
selbst umstellen koennen, gibt es eine eigene Seite, die **ohne Admin-Passwort**
|
|
erreichbar ist und die Durchwahl als Kennung nutzt.
|
|
|
|
### Einrichtung
|
|
|
|
1. Unter **Benutzer** die **Durchwahl** pro Mitarbeiter eintragen (dieselbe wie fuer den Telefon-Webhook)
|
|
2. Unter **Einstellungen > Zentrale (Self-Service)** das Busylight des Empfangs und das Segment auswaehlen
|
|
3. Die Seite `http://SERVER:8080/zentrale` an die Mitarbeiter geben
|
|
|
|
### Bedienung
|
|
|
|
```
|
|
http://SERVER:8080/zentrale - Durchwahl eingeben und Zentrale uebernehmen
|
|
http://SERVER:8080/zentrale/{durchwahl} - Direktlink pro Person, z.B. als QR-Code
|
|
```
|
|
|
|
Der Direktlink zeigt nur den Stand an und aendert nichts - das Umstellen passiert
|
|
erst per Knopfdruck.
|
|
|
|
### Verhalten
|
|
|
|
- **Zentrale uebernehmen**: Der Mitarbeiter wird auf das Segment der Zentrale
|
|
gesetzt. Das eigene Tuerschild gibt er dabei ab, weil ein Mitarbeiter nur einer
|
|
Zuordnung gleichzeitig zugewiesen sein kann. Der alte Platz wird gemerkt und das
|
|
Segment ausgeschaltet.
|
|
- **Zurueck auf mein Tuerschild**: Der gemerkte Platz wird wiederhergestellt und die
|
|
Zentrale freigegeben (Farbe `abwesend`, Standard: aus).
|
|
- **Vorgaenger mit eigenem Tuerschild**: Wer die Zentrale abgibt, wird dabei
|
|
automatisch auf sein eigenes Tuerschild zurueckgesetzt. Er muss also nicht erst
|
|
selbst "zurueck" klicken, damit der Nachfolger uebernehmen kann. Ist sein Tuerschild
|
|
inzwischen jemand anderem zugeordnet, bleibt er ohne Zuordnung - die Meldung nennt
|
|
dann, wer dort sitzt.
|
|
- **Stamm-Besetzer der Zentrale**: Wer normalerweise auf der Zentrale sitzt und kein
|
|
eigenes Tuerschild hat, wird beim ersten Verdraengen als Stamm-Besetzer vermerkt.
|
|
Er kommt automatisch zurueck, sobald die Zentrale wieder freigegeben wird - egal
|
|
wie viele Vertretungen sich zwischenzeitlich abgewechselt haben. Sein Vermerk bleibt
|
|
dauerhaft stehen, denn die Zentrale ist sein normaler Platz und keine Vertretung.
|
|
- **Der Stamm-Besetzer selbst** braucht "Zurueck auf mein Tuerschild" nicht: sein
|
|
Platz ist die Zentrale. Klickt er es trotzdem, aendert sich nichts und die Seite
|
|
sagt es ihm.
|
|
- **Belegter Heimatplatz**: Ist das eigene Tuerschild inzwischen jemand anderem
|
|
zugeordnet, wird die Rueckkehr abgelehnt und nichts geaendert. Dann muss der
|
|
Administrator die Zuordnung aufloesen.
|
|
- **Sofortiges Umschalten**: Die Farbe wird direkt ans Geraet geschickt. Das Polling
|
|
allein wuerde nicht reichen, weil es nur bei einer Statusaenderung sendet und der
|
|
Status des Mitarbeiters beim Platzwechsel unveraendert ist.
|
|
- **Protokoll**: Jeder Wechsel landet unter **Protokoll** (`zentrale` beim Uebernehmen,
|
|
`zentrale_frei` beim Freigeben).
|
|
- **Vorrang des Administrators**: Wird ein Mitarbeiter unter **Zuordnungen** neu
|
|
zugeordnet, verfaellt sein gemerkter Heimatplatz - die ausdrueckliche Zuordnung
|
|
gilt. Wird seine Zuordnung dort nur geloescht, bleibt der gemerkte Platz erhalten,
|
|
damit er von der Zentrale zurueckkehren kann.
|
|
|
|
### Sicherheit
|
|
|
|
Die Durchwahl ist eine Kennung, kein Geheimnis. Wer Port 8080 erreicht, kann jeden
|
|
Kollegen auf die Zentrale setzen. Fuer den internen Gebrauch ist das gewollt - die
|
|
Aenderung ist sofort sichtbar, im Protokoll nachvollziehbar und mit einem Klick
|
|
zurueckzunehmen. Die Seite gehoert deshalb nicht ins Internet.
|
|
|
|
## Starten
|
|
|
|
### Manuell (zum Testen)
|
|
|
|
```bash
|
|
cd /opt/busylight
|
|
source venv/bin/activate
|
|
python app.py
|
|
```
|
|
|
|
Dabei werden zwei Server gestartet:
|
|
- **Port 8080**: Web-Oberflaeche
|
|
- **Port 8081**: Webhook-Server (Telefonanlage + Zeiterfassungs-API)
|
|
|
|
### Als systemd-Service (Produktivbetrieb)
|
|
|
|
```bash
|
|
# Service installieren (bei manueller Installation)
|
|
sudo cp busylight.service /etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now busylight
|
|
|
|
# Status pruefen
|
|
sudo systemctl status busylight
|
|
|
|
# Logs anzeigen
|
|
sudo journalctl -u busylight -f
|
|
|
|
# Update
|
|
cd /opt/busylight && git pull
|
|
sudo systemctl restart busylight
|
|
```
|
|
|
|
## Dashboard
|
|
|
|
Das **Dashboard** zeigt alle Busylights mit dem aktuellen Status in Echtzeit an und aktualisiert sich automatisch alle 10 Sekunden. Pro Benutzer werden drei Status-Badges angezeigt:
|
|
|
|
- **Zeiterfassung**: Anwesend (gruen) / Abwesend (grau) / Homeoffice (gelb)
|
|
- **Teams**: Verfuegbar / Beschaeftigt / Nicht stoeren / Offline / etc.
|
|
- **Telefon**: Telefoniert (rot) / Frei (grau)
|
|
|
|
## Hintergrund-Jobs
|
|
|
|
| Job | Intervall | Beschreibung |
|
|
|-----|-----------|-------------|
|
|
| Status-Polling | 15 Sekunden (konfigurierbar) | Teams-Presence und Zeiterfassungs-Status abfragen, WLED aktualisieren |
|
|
| Device-Check | 240 Sekunden | Alle WLED-Geraete auf Erreichbarkeit pruefen |
|
|
|
|
## Projektstruktur
|
|
|
|
```
|
|
busylight/
|
|
├── app.py # FastAPI Web-Anwendung + Azure Auth + Startup
|
|
├── webhook.py # Webhook-Server + Zeiterfassungs-API (Port 8081)
|
|
├── database.py # SQLite Datenbank-Schema und Hilfsfunktionen
|
|
├── graph_client.py # Microsoft Graph API Client
|
|
├── wled_client.py # WLED HTTP API Client
|
|
├── scheduler.py # Hintergrund-Jobs (Polling + Device-Check)
|
|
├── install.sh # Installer fuer Debian 13
|
|
├── requirements.txt # Python-Abhaengigkeiten
|
|
├── busylight.service # systemd Service-Datei
|
|
├── docs/ # Dokumentation
|
|
├── static/
|
|
│ └── style.css # CSS Styles
|
|
└── templates/ # Jinja2 HTML-Templates (u.a. zentrale.html)
|
|
```
|
|
|
|
## Ports und Firewall
|
|
|
|
| Port | Richtung | Zweck |
|
|
|------|----------|-------|
|
|
| 8080/tcp | Eingehend | Web-Oberflaeche |
|
|
| 8081/tcp | Eingehend | Webhook-API (Telefon + Zeiterfassung) |
|
|
| 80/tcp | Ausgehend | WLED HTTP API |
|
|
| 443/tcp | Ausgehend | Microsoft Graph API |
|
|
|
|
## Troubleshooting
|
|
|
|
### Dashboard zeigt nur graue Segmente
|
|
- Zeiterfassung pruefen (CSV-Pfad oder API-Status)
|
|
- Benutzer muessen aktiviert sein UND eine Personalnummer haben
|
|
- Zuordnung Benutzer -> WLED-Geraet + Segment muss vorhanden sein
|
|
|
|
### Teams-Status wird nicht angezeigt
|
|
- Azure-Authentifizierung pruefen (Sidebar zeigt "Azure verbunden")
|
|
- API-Berechtigungen in Azure pruefen (Presence.Read.All)
|
|
|
|
### Telefon-Webhook reagiert nicht
|
|
- Port 8081 erreichbar? `curl http://SERVER:8081/`
|
|
- Durchwahl beim Benutzer eingetragen?
|
|
- Benutzer muss laut Zeiterfassung anwesend oder im Homeoffice sein
|
|
|
|
### Azure Auth schlaegt fehl
|
|
- "Oeffentliche Clientflows zulassen" muss in der Azure App aktiviert sein
|
|
- Siehe [docs/azure-einrichtung.md](docs/azure-einrichtung.md) fuer Details
|
|
|
|
### WLED-Geraete offline
|
|
- Unter Tuerschilder den Status pruefen ("Alle jetzt pruefen")
|
|
- Netzwerkverbindung pruefen: `curl http://HOSTNAME/json/info`
|
|
|
|
### Zentrale-Seite sagt "noch nicht eingerichtet"
|
|
|
|
Unter **Einstellungen > Zentrale (Self-Service)** ist kein Busylight ausgewaehlt.
|
|
Ohne diese Einstellung weiss die Anwendung nicht, welches Geraet der Empfang ist.
|
|
|
|
### Durchwahl wird auf der Zentrale-Seite nicht erkannt
|
|
|
|
Die Durchwahl muss unter **Benutzer** exakt so eingetragen sein wie eingegeben, und
|
|
der Benutzer muss **aktiv** sein. Inaktive Benutzer werden bewusst abgewiesen.
|
|
|
|
### Nach git pull aendert sich nichts
|
|
- Service neustarten: `sudo systemctl restart busylight`
|