busylight_modular/README.md
Christian Mueller 3329a5dc5a docs: complete README rewrite with webhook, segments, CSV format, troubleshooting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 23:45:17 +02:00

13 KiB

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

# Repository klonen
git clone https://git.cynfo.net/christian/busylight.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:

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):

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:

# 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):

# CSV-Pfad in den Web-Einstellungen eintragen:
/opt/busylight/Status.csv

Option B: SMB/CIFS-Mount

# 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)

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)

# 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