- HTML 68.5%
- Go 31.3%
Manifest-Antworten tragen jetzt X-Server-Now (Server-Wanduhr, Epoch ms). Der Player berechnet daraus einen geglaetteten Offset (RTT/2-korrigiert) und zeigt via nowMs()=Date.now()+Offset in allen Sekunden-Widgets (Uhr, Countdown, Analog, Weltuhr, Tage-seit) sowie in der Wanduhr-/ Video-Sync die identische Server-Zeit - unabhaengig vom NTP-Stand des einzelnen Geraets. Ergaenzt clockAlign (Sekundengrenze) um die gemeinsame Zeitbasis. - api.go: writeManifest() stempelt X-Server-Now auf alle 4 Manifest-Ausgaben - frontend/player.html: nowMs()/syncClockOffset(); alle Zeitlesestellen + clockAlign + syncClock-elapsed + Video-Sync auf nowMs(); poll liest Header |
||
|---|---|---|
| .forgejo/workflows | ||
| cmd/server | ||
| docs | ||
| frontend | ||
| internal | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
signagetools
Werkzeug zur Vorbereitung und Ausrollung von Digital-Signage-Programmen auf ein oder mehrere Displays – mit Ablaufplänen, zeitlicher Steuerung und zusammenhängenden Zeitplänen.
Repos
Das Projekt ist auf zwei Repos aufgeteilt:
signagetools(dieses Repo) – Server (Go-API), Scheduling-Logik und Admin-Frontend inkl. Builder (frontend/builder.html). Der Builder ist Authoring/Admin und gehört zur Server-Seite.signagetools-player– der On-Device-Player (Go-Agent + Player-UI), der auf Pi/Mini-PC läuft. Eigenes Deployable, eigener Release-Zyklus.
Die Design-Dokumente (docs/) leben zentral hier und gelten für beide.
Status
Design-Fundament steht (Features, Architektur, Scheduling, Datenmodell).
Server-MVP (Slice 1) läuft: Go-API mit PostgreSQL, Layouts speichern/laden,
Medien-Upload, Manifest-Endpunkt, docker-compose und CI. Der interaktive
Screen-Builder (frontend/builder.html) ist an die
API angebunden (Speichern/Öffnen + Bild-Upload). Der Player-Agent
(Repo signagetools-player) zieht das Manifest, cached Medien lokal und rendert
im Kiosk — end-to-end getestet inkl. Offline-Weiterbetrieb. Damit steht die
ganze Kette Builder → Server → Player.
Iteration 2 (Zeit-Scheduling) ist drin: Resolver mit Prioritäten, Overrides, Dayparting, Wochentagen und automatischem Rückfall auf den globalen Basisplan; Zeitplan-Editor im Builder; Player zieht ein zeitlich aufgelöstes Screen-Manifest.
Geräte-Enrollment + Player-Auth ist drin: neuer Player meldet sich an, zeigt einen Kopplungscode, wird im Builder unter „Geräte" freigegeben und erhält ein Token; Content-Pulls laufen ab dann Token-authentifiziert, Sperren wirkt sofort.
Admin-Authentifizierung ist drin: ist ADMIN_PASSWORD gesetzt, verlangen alle
Verwaltungs-Endpunkte (Layouts, Zeitplan, Medien, Geräte) einen Login; der Builder
zeigt dann ein Passwortfeld und schickt ein Session-Token mit. Ohne Passwort bleibt
alles offen (Entwicklung) — der Server warnt beim Start. Player-Endpunkte (Enroll,
Token, Manifest, Medien) bleiben bewusst außerhalb dieses Schutzes.
Bewusst noch offen: TLS (via Reverse-Proxy davor) und ein feineres Rollen-/Nutzermodell statt eines gemeinsamen Passworts.
Builder-Ausbau ist drin: Mediathek (Uploads durchsuchen und wiederverwenden, inkl. Video), Layout-Verwaltung (Neu, Duplizieren, Löschen aus der Serverliste), Zonen duplizieren/ausrichten, Playlist-Elemente duplizieren, Text-Styling (Farbe, Größe, horizontale/vertikale Ausrichtung), Bild-/Video-Fit (cover/contain) und Video als Inhaltstyp — alle Stil-Optionen werden persistiert und vom Player umgesetzt.
Weiter offen: Zonen-Verteilen (Mehrfachauswahl) und Widget-Inhalte (Uhr, Wetter, RSS).
Kaskadierende Zeitplan-Auflösung ist drin: Standorte und Gerätegruppen,
Geräte-Zuordnung (Standort + Gruppen) im Builder, und ein Resolver, der für ein
Gerät alle Ebenen berücksichtigt (Gerät > Screen > Gruppe > Standort > global;
Priorität schlägt Spezifität). Damit ist das Datenmodell aus datenmodell.md
funktional vollständig.
Monitoring (Basis) ist drin: die Geräteliste zeigt Online-Status (aus
last_seen, Fenster 90 s), „zuletzt gesehen", das gerade aufgelöste Layout
(„zeigt: …") und eine Online-Zusammenfassung; der Geräte-Dialog aktualisiert sich
alle 10 s selbst.
Proof-of-Play (Basis) ist drin: der Server schreibt beim Manifest-Abruf
intervallbasiert mit, welches Gerät wann welches Layout gezeigt hat (nur bei
Layout-Wechsel eine neue Zeile); der Geräte-Dialog zeigt je Gerät ein „Protokoll"
mit von–bis und Dauer. GET /api/playlog?device=&limit=.
Benutzer & Rollen sind drin: neben dem Bootstrap-Admin (ADMIN_PASSWORD)
gibt es benannte Konten mit bcrypt-Hash und den Rollen viewer (nur ansehen),
editor (Inhalte/Geräte) und admin (inkl. Benutzerverwaltung). Die Anmeldung
wird aktiv, sobald ein Passwort gesetzt ist oder ein Konto existiert. Betrieb,
TLS/Reverse-Proxy und Rollen sind in docs/deployment.md
beschrieben.
Uhr-Widget ist drin: ein Element-Typ clock zeigt die lokale Player-Zeit,
tickt sekündlich und lässt sich wie Text stylen (Farbe/Größe/Ausrichtung).
Formate: Uhrzeit, Uhrzeit mit Sekunden, Datum, Uhrzeit + Datum, Wochentag + Datum.
Zonen anordnen ist drin: mehrere Zonen per Häkchen oder Shift-Klick wählen und gemeinsam ausrichten (Kanten/Mitte), gleichmäßig verteilen (ab 3 Zonen) oder auf gleiche Breite/Höhe bringen.
Wetter-Widget ist drin: ein Element-Typ weather zeigt das aktuelle Wetter
(Open-Meteo, ohne API-Key) für einen Ort (Suche oder Koordinaten im Builder).
Der Player holt und cached die Daten selbst (offline-resilient, Aktualisierung
alle 10 min), gestylt wie Text.
RSS-Ticker ist drin: ein Element-Typ ticker zeigt ein Laufband mit
Schlagzeilen aus einem RSS/Atom-Feed. Der Player holt, parst und cached die
Schlagzeilen selbst (offline-resilient, Aktualisierung alle paar Minuten) und
scrollt sie flüssig.
Status-Einblendung ist drin: war der Server länger nicht erreichbar, zeigt der Player ein dezentes Eck-Badge („Server nicht erreichbar · zeige Cache · vor X"), sonst nichts. So sieht man im Feld sofort, ob ein Bildschirm auf altem Cache läuft. Schwelle: max. 90 s bzw. das Dreifache des Poll-Intervalls.
Aktueller Stand: Der Funktionsumfang ist seither stark gewachsen. Es gibt
über 30 Element-Typen (u. a. Wetter + Vorhersage mit Regen/Wind, Analoguhr,
Kalender/Raumstatus mit Feiertagen, Tabelle mit Live-Daten und Zellen-Ampel,
Fortschritt, Countdown, QR, Feiertage-Widget) — der vollständige Überblick in
docs/widgets.md. Dazu Betriebszeiten/Standby je Layout
und Zeit-Synchronisation mehrerer Player im Gleichtakt. Der Player-Agent hat
eine Browser-Ersteinrichtung (Doppelklick → koppeln, kein Kommandozeilen-
Flag nötig) und wird als fertige Binaries (inkl. Windows-.exe) über die
Releases des Player-Repos verteilt.
Dokumentation
docs/onboarding.md— neuen Screen hinzufügen (Player starten → koppeln → freigeben → Layout zuweisen).docs/monitoring.md— Player überwachen & steuern (Online-Status, Version, Screenshot, Fernsteuerung).docs/widgets.md— Widget-Katalog aller Element-Typen.docs/deployment.md— Betrieb, TLS/Reverse-Proxy, Rollen.docs/scheduling.md— Zeitplan-Auflösung.docs/architektur.md,docs/datenmodell.md— Architektur & Datenmodell.
Schnellstart (Server)
docker compose up --build # startet Server (:8080) + PostgreSQL
curl localhost:8080/healthz
Lokal ohne Container: PostgreSQL bereitstellen, .env.example nach .env
kopieren, dann make run.
API (Slice 1)
GET /healthz,GET /api/versionGET /api/layouts,POST /api/layouts,GET|DELETE /api/layouts/{id}GET /api/layouts/{id}/manifest— Manifest für ein festes LayoutGET /api/schedule,POST /api/schedule,DELETE /api/schedule/{id}— ZeitplanGET /api/screens/{target}/manifest— zeitlich aufgelöstes Manifest (was ein Player zieht)GET /api/resolve?target=&at=— Debug: welches Layout gälte wannGET /api/media,POST /api/media(multipartfile),GET /media/{id}/{name}
Admin-Auth (aktiv nur mit gesetztem ADMIN_PASSWORD)
GET /api/auth/status→{authRequired, authenticated}POST /api/auth/login({password}) →{token};POST /api/auth/logout- Verwaltungs-Endpunkte (Layouts/Zeitplan/Medien/Geräte) verlangen dann
Authorization: Bearer <token>.
Geräte / Enrollment
POST /api/devices/enroll→{deviceId, pairingCode}(Player-Bootstrap)GET /api/devices/token?deviceId=&code=— Player holt sein Token nach FreigabeGET /api/devices/manifest— Token-authentifiziert (Bearer), zeitlich aufgelöstGET /api/devices,POST /api/devices/{id}/approve({name,target,locationId,groupIds}),POST /api/devices/{id}/assign,POST /api/devices/{id}/revoke,DELETE /api/devices/{id}
Standorte & Gruppen
GET/POST /api/locations,DELETE /api/locations/{id}GET/POST /api/groups,DELETE /api/groups/{id}- Zeitplan-Einträge zielen per
scopeaufglobal | location | group | device | screen; das Gerät-Manifest löst kaskadierend auf: Gerät > Screen > Gruppe > Standort > global (bei Gleichstand höhere Priorität, dann neuer). Debug:GET /api/resolve?device=<id>.
Das POST /api/layouts-Format entspricht exakt dem JSON aus dem Builder.
Dokumente
docs/mvp.md– MVP-Schnitt, Roadmap und Tech-Stack (der Bauplan).docs/ci.md– Versionierung, Builds und CI.docs/feature-sammlung.md– zentrale Sammlung aller gewünschten Funktionen, gruppiert nach Bereichen und grob priorisiert.docs/architektur.md– getroffene Architektur- und Grundsatzentscheidungen.docs/scheduling.md– Design-Konzept der zeitlichen Steuerung (Kernthema: Prioritäten, Overrides, kaskadierende Zeitpläne).docs/datenmodell.md– Kern-Entitäten und ihre Beziehungen (das Gerüst zum Bauen).docs/offene-fragen.md– offene Entscheidungen, Ideen und Diskussionspunkte.
Priorisierung (Legende)
- [M] Muss – Kernfunktion, ohne die das Tool seinen Zweck nicht erfüllt.
- [S] Soll – wichtig, macht den Unterschied zwischen „solide" und „richtig gut".
- [K] Kann – nice-to-have, spätere Ausbaustufe.