# Busylight WLED Controller Web-Anwendung zur Steuerung von WLED-Busylights basierend auf Microsoft Teams Presence-Status, AIDA-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 (AIDA), des Teams-Status und des Telefonstatus gesetzt. ## Features - **Dashboard** mit Echtzeit-Uebersicht aller Busylights (AIDA-, Teams- und Telefon-Status als Badges) - **Benutzer-Verwaltung** mit automatischem Sync aus 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) - **Einstellungen** fuer Azure AD, Polling-Intervall und AIDA-CSV-Pfad - **Protokoll** der letzten Statusaenderungen ## Funktionsweise | Quelle | Beschreibung | Auswirkung auf WLED | |--------|-------------|---------------------| | AIDA 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 AIDA-Status | **Prioritaet:** Telefon > Teams > AIDA. Die WLED-Farbe wird nur gesetzt wenn der Mitarbeiter laut AIDA anwesend oder im Homeoffice ist. ## Voraussetzungen - Linux-Server (Debian 12+/Ubuntu 22.04+) - Python 3.10+ - Netzwerkzugriff auf die WLED-Geraete (HTTP Port 80) - Azure AD App-Registrierung (siehe unten) - Zugriff auf die AIDA-Zeiterfassungs-CSV (lokal oder per SMB-Mount) - Optional: Telefonanlage mit Webhook-Unterstuetzung ## 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 ``` ## Azure AD einrichten ### 1. App-Registrierung erstellen 1. Azure Portal oeffnen: https://portal.azure.com 2. **Azure Active Directory** -> **App-Registrierungen** -> **Neue Registrierung** 3. Einstellungen: - **Name**: `Busylight Web` (oder beliebig) - **Unterstuetzte Kontotypen**: Nur Konten in diesem Organisationsverzeichnis - **Umleitungs-URI**: leer lassen 4. **Registrieren** klicken ### 2. Oeffentliche Clientflows aktivieren 1. In der erstellten App-Registrierung: **Authentifizierung** (linkes Menue) 2. Ganz unten: **Erweiterte Einstellungen** 3. **Oeffentliche Clientflows zulassen** -> **Ja** 4. **Speichern** ### 3. API-Berechtigungen setzen 1. **API-Berechtigungen** -> **Berechtigung hinzufuegen** -> **Microsoft Graph** 2. **Delegierte Berechtigungen** waehlen 3. Folgende Berechtigungen hinzufuegen: - `User.Read` - `User.Read.All` - `Presence.Read` - `Presence.Read.All` 4. **Administratorzustimmung erteilen** klicken ### 4. IDs notieren Von der Uebersichtsseite der App-Registrierung: - **Anwendungs-ID (Client)** -> das ist die `Client ID` - **Verzeichnis-ID (Mandant)** -> das ist die `Tenant ID` ### 5. Credentials in Busylight eintragen Entweder ueber das Web-Interface unter **Einstellungen**, oder per CLI: ```bash cd /opt/busylight && source venv/bin/activate python -c " from database import init_db, set_setting init_db() set_setting('azure_client_id', 'DEINE-CLIENT-ID') set_setting('azure_tenant_id', 'DEINE-TENANT-ID') set_setting('azure_auth_tenant', 'DEINE-TENANT-ID') " ``` ### 6. Azure Authentifizierung durchfuehren Die Authentifizierung erfolgt einmalig ueber den Device-Code-Flow im Terminal. Um den Device-Code sauber angezeigt zu bekommen (ohne Azure-Debug-Logs): ```bash cd /opt/busylight && source venv/bin/activate python -W ignore -c " import logging logging.getLogger('azure').setLevel(logging.WARNING) from app import cli_auth cli_auth() " ``` Es wird ein Code angezeigt, z.B.: ``` To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code LW5ZR5BFC to authenticate. ``` Diesen Code im Browser eingeben und mit einem Azure-AD-Konto anmelden. Nach erfolgreicher Anmeldung wird das Token in der Datenbank gespeichert und automatisch erneuert. ## Migration von der alten config.cfg Falls das System vorher mit dem alten Python-Script und `config.cfg` betrieben wurde, koennen alle Daten (User, Geraete, Segmente, Farben) automatisch importiert werden: ```bash # config.cfg ins Projektverzeichnis kopieren cp /pfad/zur/alten/config.cfg . # Migration ausfuehren python migrate.py ``` Die alte `config.cfg` wird danach nicht mehr benoetigt. ## AIDA-CSV bereitstellen Die Zeiterfassungs-CSV liegt typischerweise auf einem Windows-Server (z.B. `\\192.168.30.224\export`). ### Option A: Lokale Kopie Die CSV-Datei direkt auf den Server kopieren (z.B. per Cronjob/rsync): ```bash # CSV-Pfad in den Web-Einstellungen eintragen: /opt/busylight/Status.csv ``` ### Option B: SMB/CIFS-Mount ```bash # Paket installieren sudo apt install cifs-utils # Mount-Punkt und Credentials erstellen sudo mkdir -p /mnt/aida sudo bash -c 'cat > /etc/samba/creds << EOF username=BENUTZER password=PASSWORT domain=DOMAIN EOF' sudo chmod 600 /etc/samba/creds # Eintrag in /etc/fstab (eine Zeile) # //192.168.30.224/export /mnt/aida cifs credentials=/etc/samba/creds,ro 0 0 # Mounten sudo mount /mnt/aida ``` **Hinweis:** In LXC-Containern (Proxmox) muss CIFS-Mount erst in den Container-Features aktiviert werden. In den Web-Einstellungen dann als CSV-Pfad eintragen: ``` /mnt/aida/Status.csv ``` ### CSV-Format Die AIDA-CSV hat folgendes Format (eine Zeile pro Mitarbeiter): ``` ~~00341~~,~~1~~ ~~00412~~,~~0~~ ~~00433~~,~~2~~ ``` Dabei bedeuten die Status-Codes (konfigurierbar in den Einstellungen): - `~~0~~` = Abwesend - `~~1~~` = Anwesend - `~~2~~` = Homeoffice ## Telefonanlage (Webhook) Die Anwendung startet automatisch einen Webhook-Server auf **Port 8081** fuer die Anbindung einer Telefonanlage. ### 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) ``` Die Info-Seite unter `http://SERVER:8081/` zeigt eine Uebersicht der Endpoints. ### 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 AIDA anwesend oder im Homeoffice ist - `callend`: Setzt WLED zurueck auf den AIDA-Status (anwesend=gruen, homeoffice=gelb). 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 ## 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 fuer Telefonanlage ### Als systemd-Service (Produktivbetrieb) ```bash # Service installieren 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 # Neustart nach Updates cd /opt/busylight && git pull sudo systemctl restart busylight ``` ## Benutzung ### 1. Einstellungen konfigurieren Unter **Einstellungen** die Azure AD Credentials (Client ID, Tenant ID) und den AIDA-CSV-Pfad eintragen. ### 2. Benutzer synchronisieren Unter **Benutzer** auf "Aus Azure synchronisieren" klicken. Alle Azure AD Benutzer werden importiert. Anschliessend: - Benutzer aktivieren/deaktivieren (nur aktive Benutzer werden abgefragt) - AIDA-Personalnummer zuordnen (Format: `~~00341~~`) - Telefon-Durchwahl zuordnen (z.B. `123`) ### 3. WLED-Geraete anlegen Unter **WLED-Geraete** 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 laut AIDA 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 | ### 6. 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: - **AIDA**: Anwesend (gruen) / Abwesend (grau) / Homeoffice (gelb) - **Teams**: Verfuegbar / Beschaeftigt / Nicht stoeren / Offline / etc. - **Telefon**: Telefoniert (rot) / Frei (grau) ### 7. Protokoll Unter **Protokoll** werden die letzten 200 Statusaenderungen mit Zeitstempel, WLED-URL und Ergebnis angezeigt. ## Hintergrund-Jobs Die Anwendung fuehrt automatisch folgende Jobs im Hintergrund aus: | Job | Intervall | Beschreibung | |-----|-----------|-------------| | Status-Polling | 15 Sekunden (konfigurierbar) | Teams-Presence und AIDA-Status abfragen, WLED aktualisieren | | Device-Check | 240 Sekunden | Alle WLED-Geraete auf Erreichbarkeit pruefen | ## Projektstruktur ``` busylight/ ├── app.py # FastAPI Web-Anwendung + CLI Auth + Startup ├── webhook.py # Webhook-Server fuer Telefonanlage (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) ├── migrate.py # Import aus alter config.cfg ├── requirements.txt # Python-Abhaengigkeiten ├── busylight.service # systemd Service-Datei ├── static/ │ └── style.css # CSS Styles └── templates/ ├── base.html # Basis-Layout mit Sidebar ├── dashboard.html # Status-Uebersicht ├── users.html # Benutzer-Verwaltung ├── devices.html # WLED-Geraete-Verwaltung ├── assignments.html # Zuordnungen User -> Segment ├── colors.html # Farb-Regeln ├── settings.html # Einstellungen └── log.html # Aktivitaets-Protokoll ``` ## Ports und Firewall | Port | Richtung | Zweck | |------|----------|-------| | 8080/tcp | Eingehend | Web-Oberflaeche | | 8081/tcp | Eingehend | Webhook-API fuer Telefonanlage | | 80/tcp | Ausgehend | WLED HTTP API | | 443/tcp | Ausgehend | Microsoft Graph API | ## Troubleshooting ### Dashboard zeigt nur graue Segmente - AIDA-CSV-Pfad in den Einstellungen pruefen (muss auf dem Linux-Server erreichbar sein) - Benutzer muessen aktiviert sein UND eine Personalnummer haben - Zuordnung Benutzer -> WLED-Geraet + Segment muss vorhanden sein ### Teams-Status wird nicht angezeigt - Teams-Status wird immer abgefragt, auch ohne AIDA - 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 AIDA anwesend oder im Homeoffice sein ### Azure Auth schlaegt fehl - "Oeffentliche Clientflows zulassen" muss in der Azure App aktiviert sein - Auth-Tenant muss die Tenant-ID sein (nicht "common"), wenn die App auf "Nur meine Organisation" steht - Bei erneutem Auth: `python app.py --auth` loescht automatisch den alten Token ### WLED-Geraete offline - Unter WLED-Geraete den Status pruefen (Wifi-Button oder "Alle jetzt pruefen") - Netzwerkverbindung vom Linux-Server zu den WLED-Geraeten pruefen: `curl http://HOSTNAME/json/info` ### Nach git pull aendern sich nichts - Service neustarten: `sudo systemctl restart busylight`