No description
Find a file
Julian Appel 6aa5a7b025 Standardport wieder 3000, Port 80 per Umgebungsvariable
Port 80 stammt aus dem Kiosk-Setup auf dem Pi, wo Chromium die Seite ohne
Portangabe unter http://localhost oeffnet. Als Default erzwingt er lokal
erhoehte Rechte. Auf dem Pi stattdessen PORT=80 setzen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 20:39:27 +02:00
scoreboard Standardport wieder 3000, Port 80 per Umgebungsvariable 2026-08-19 20:39:27 +02:00
.gitignore Added socketio, and moment 2024-04-21 12:48:59 +02:00
README.md Added comments and readme 2026-04-10 22:16:51 +02:00

Scoreboard-JS

Echtzeit-Anzeigetafel für Sportveranstaltungen — zeigt Timer und Spielstand auf einem separaten Monitor an und wird über ein Admin-Panel gesteuert.


Übersicht

Das System besteht aus zwei Teilen:

Teil URL Beschreibung
Monitor http://<host>:3000/ Vollbildanzeige für den Beamer/Monitor (Timer + Score)
Admin-Panel http://<host>:3000/admin Steuerung von Timer und Scoreboard

Alle Änderungen vom Admin-Panel werden über WebSockets (Socket.IO) in Echtzeit auf dem Monitor angezeigt — ohne Seiten-Reload.


Voraussetzungen

  • Node.js (v18 oder neuer empfohlen)
  • npm

Installation & Start

cd scoreboard
npm install
npm start

Der Server startet mit nodemon und ist anschließend unter Port 3000 erreichbar.
Der Socket.IO-Server läuft separat auf Port 3001.


Projektstruktur

scoreboard/
├── app.js                          # Express-App, Middleware, Routen
├── bin/www                         # HTTP-Server, startet die App
├── controllers/
│   ├── socketio.js                 # Socket.IO-Server (Port 3001)
│   ├── timer.js                    # Timer-Logik (Start, Pause, Reset, IncDec)
│   ├── score.js                    # Score-Logik (Punkte, Teams, Seitenwechsel)
│   └── cli.js                      # CLI-Controller (aktuell Testcode)
├── routes/
│   ├── admin.js                    # REST-Endpunkte für das Admin-Panel
│   └── index.js                    # Monitor-Ansicht + WebSocket-Verbindungshandler
├── views/
│   ├── admin.hbs                   # Admin-Panel (Handlebars-Template)
│   ├── index.hbs                   # Monitor ohne Scoreboard (nur Timer)
│   └── indexScore.hbs              # Monitor mit Scoreboard (Timer + Score + Teamnamen)
└── public/
    ├── javascripts/
    │   ├── admin.js                # Frontend-Logik Admin-Panel
    │   ├── index.js                # Frontend-Logik Monitor (ohne Score)
    │   └── indexScore.js           # Frontend-Logik Monitor (mit Score)
    └── stylesheets/
        ├── admin.css
        ├── index.css
        ├── indexScore.css
        └── seven-segment.css       # Seven-Segment-Display-Schrift für den Timer

Funktionen

Timer

  • Countdown-Timer mit konfigurierbarer Startzeit (Standard: 7:00 Minuten)
  • Start / Pause
  • Zurücksetzen auf eine neue Zeit (0:00 bis 10:00, in 15-Sekunden-Schritten)
  • Feineinstellung im laufenden Betrieb: ±1s, ±5s, ±10s
  • Nach Ablauf wird timerEnded an alle Clients gesendet und "ENDE" auf dem Monitor angezeigt

Scoreboard

  • Punktestand für Team A und Team B
  • Punkte einzeln erhöhen (+1) oder verringern (-1)
  • Score direkt auf einen bestimmten Wert setzen
  • Score zurücksetzen (beide Teams auf 0)
  • Scoreboard ein-/ausblenden: Der Monitor wechselt zwischen Timer-only-Ansicht (index.hbs) und Timer+Score-Ansicht (indexScore.hbs)

Teams konfigurieren

Jedes Team hat folgende Felder:

Feld Beschreibung
name Hauptname des Teams
name2 Zweiter Name (nur bei Spielgemeinschaft)
isSpielgemeinschaft Wenn aktiv, werden beide Namen untereinander angezeigt

Seitenwechsel (Halbzeit)

Über "Seitenwechsel" werden Team A und Team B auf dem Monitor die Seiten tauschen — der Spielstand bleibt unverändert, nur die Anzeigepositionen (links/rechts) wechseln.

Monitor neu laden

Der Button "Monitor neu laden" im Admin-Panel sendet ein refresh-Event an alle verbundenen Clients und löst einen Seiten-Reload des Monitors aus. Nützlich z.B. nach dem Ein-/Ausblenden des Scoreboards.


Architektur / Datenfluss

Admin-Panel (Browser)
        │
        │  HTTP REST (fetch)
        ▼
  Express-Server :3000
  routes/admin.js
        │
        ├── controllers/timer.js  ──┐
        └── controllers/score.js  ──┤
                                    │  io.sockets.emit(event, data)
                                    ▼
                          Socket.IO-Server :3001
                                    │
                          WebSocket-Verbindung
                                    │
                    ┌───────────────┴───────────────┐
                    ▼                               ▼
          Monitor (index.hbs)           Monitor (indexScore.hbs)
          index.js                      indexScore.js

WebSocket-Events

Event Richtung Beschreibung
timerDurationLeft Server → Client Aktuelle Restzeit als String MM:SS
timerEnded Server → Client Timer abgelaufen
score Server → Client Aktueller Spielstand als String X:Y
scoreSideswitch Server → Client Seitenwechsel ausgelöst — Frontend neu laden
refresh Server → Client Kompletten Seiten-Reload auslösen

REST-API (Admin-Endpunkte)

Alle Endpunkte sind unter /admin erreichbar.

Allgemein

Methode Endpunkt Beschreibung
GET /admin/refreshMonitor Sendet refresh an alle Monitors

Timer

Methode Endpunkt Body Beschreibung
GET /admin/timerStart Timer starten
GET /admin/timerPause Timer pausieren
POST /admin/timerReset { duration: <Sekunden> } Timer zurücksetzen
POST /admin/timerIncDec { value: <Sekunden> } Timer erhöhen (positiv) / verringern (negativ)
GET /admin/timerGetValues Aktuelle Timer-Werte abrufen

Antwortformat timerGetValues:

{
  "isPaused": true,
  "duration": "<ISO 8601>",
  "durationLeft": "<ISO 8601>",
  "print": "07:00"
}

Scoreboard

Methode Endpunkt Body Beschreibung
GET /admin/scoreToggle Scoreboard ein-/ausblenden
GET /admin/scoreGetValues Aktuelle Score-Werte abrufen
GET /admin/scoreClearScore Spielstand beider Teams auf 0
GET /admin/scoreToggleSideswitch Seiten der Teams tauschen
POST /admin/scoreSetScore { team, score } Score direkt setzen
POST /admin/scoreAlterScore { team, dir } Score um 1 ändern (dir: "inc" / "dec")
POST /admin/scoreConfigTeams { teamA: { name, name2, isSpielgemeinschaft }, teamB: { ... } } Teams konfigurieren

team ist jeweils "teamA" oder "teamB".

Antwortformat scoreGetValues:

{
  "enabled": false,
  "teamA": { "name": "Team A", "name2": "", "score": 0, "isSpielgemeinschaft": 0 },
  "teamB": { "name": "Team B", "name2": "", "score": 0, "isSpielgemeinschaft": 0 },
  "sideswitch": false,
  "print": "0:0"
}

Abhängigkeiten

Paket Verwendung
express HTTP-Server und Routing
socket.io WebSocket-Kommunikation
moment Timer-Berechnung und Zeitformatierung
hbs Handlebars als View-Engine
morgan HTTP-Request-Logging
nodemon Automatischer Server-Neustart bei Dateiänderungen