Christian Mueller 8d178eac6d feat: poll Teams presence independently of AIDA CSV
- Teams status is now always queried, even without AIDA CSV
- Users without personal_nr are also polled (Teams-only mode)
- If AIDA is unavailable, default to 'anwesend' and use Teams color
- Dashboard will now show Teams status even without SMB mount

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 22:14:43 +02:00

Busylight WLED Controller

Web-Anwendung zur Steuerung von WLED-Busylights basierend auf Microsoft Teams Presence-Status und AIDA-Zeiterfassung.

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) und des Teams-Status gesetzt.

Features

  • Dashboard mit Echtzeit-Uebersicht aller Busylights und deren aktuellem Status
  • Benutzer-Verwaltung mit automatischem Sync aus Azure AD
  • WLED-Geraete hinzufuegen, bearbeiten, entfernen und Live-Status pruefen (online/offline, LED-Anzahl, Firmware-Version)
  • Zuordnung von Benutzern zu WLED-Segmenten per Dropdown (statt UUID-Mapping in einer Config-Datei)
  • Farb-Regeln mit Color-Picker fuer jeden Teams-/Anwesenheitsstatus
  • Einstellungen fuer Azure AD, Polling-Intervall und AIDA-CSV-Pfad
  • Protokoll der letzten Statusaenderungen

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)

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 auf Linux bereitstellen

Die Zeiterfassungs-CSV liegt typischerweise auf einem Windows-Server. Fuer den Zugriff von Linux empfiehlt sich ein SMB/CIFS-Mount:

# Paket installieren
sudo apt install cifs-utils

# Mount-Punkt erstellen
sudo mkdir -p /mnt/zeiterfassung

# Credentials-Datei erstellen
sudo bash -c 'cat > /etc/samba/creds << EOF
username=DOMAIN\user
password=geheim
EOF'
sudo chmod 600 /etc/samba/creds

# Eintrag in /etc/fstab (eine Zeile)
# //SERVERNAME/Zeiterfassung /mnt/zeiterfassung cifs credentials=/etc/samba/creds,uid=busylight,gid=busylight,ro 0 0

# Mounten
sudo mount /mnt/zeiterfassung

In den Web-Einstellungen dann als CSV-Pfad eintragen:

/mnt/zeiterfassung/Aida/profiles/aida/Transfer/Export/Status.csv

Alternativ: Per Cronjob die CSV regelmaessig per scp oder rsync kopieren.

Starten

Manuell (zum Testen)

cd /opt/busylight
source venv/bin/activate
python app.py

Die Web-Oberflaeche ist dann erreichbar unter: http://SERVER-IP:8080

Als systemd-Service (Produktivbetrieb)

# Service-User anlegen
sudo useradd -r -s /usr/sbin/nologin busylight
sudo chown -R busylight:busylight /opt/busylight

# 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

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

3. WLED-Geraete anlegen

Unter WLED-Geraete die Hostnamen/IP-Adressen der WLED-Busylights eintragen. Die Seite zeigt automatisch den Online-Status, LED-Anzahl und Firmware-Version jedes Geraets an.

4. Zuordnungen erstellen

Unter Zuordnungen jeden aktiven Benutzer einem WLED-Geraet und Segment zuweisen:

  • Segment 0 = oben
  • Segment 1 = unten

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
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.

  • Farbige Segmente = Status wurde erfolgreich abgefragt
  • Graue Segmente mit "Warte auf Status..." = Zuordnung vorhanden, aber noch kein Polling-Ergebnis (z.B. AIDA-CSV nicht erreichbar)
  • "Nicht zugeordnet" = kein Benutzer diesem Segment zugewiesen

7. Protokoll

Unter Protokoll werden die letzten 200 Statusaenderungen mit Zeitstempel, WLED-URL und Ergebnis angezeigt.

Projektstruktur

busylight/
├── app.py              # FastAPI Web-Anwendung + CLI Auth
├── database.py         # SQLite Datenbank-Schema und Hilfsfunktionen
├── graph_client.py     # Microsoft Graph API Client
├── wled_client.py      # WLED HTTP API Client
├── scheduler.py        # Hintergrund-Polling (AIDA + Teams -> WLED)
├── 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
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

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)
  • Netzwerkverbindung vom Linux-Server zu den WLED-Geraeten pruefen: curl http://HOSTNAME/json/info
Description
Modulares Busylight WLED Controller System mit Plugin-System fuer Zeiterfassung
Readme 222 KiB
Languages
Python 48.9%
HTML 47.2%
Shell 2%
CSS 1.9%