Rèplica digital d'alta fidelitat de les pantalles d'informació en temps real del Metro de Barcelona (TMB).
Pensada per a amants del transport que vulguin muntar una pantalla temàtica a casa: propers trens, destí, sentit i alertes de servei amb l'estètica oficial de la xarxa, configurable via URL.
Idioma: Català · Castellano
Resolució recomanada.
- Màxima fidelitat: pantalla tipus barra / ultrapanoràmica 2,8:1, equivalent al llenç natiu 1512 × 540. La franja omple la pantalla de punta a punta, com els panells reals d'andana.
- Ús general / futur: Full HD 1920 × 1080 (16:9). Aquí la franja actual es mostra centrada; és la resolució que requerirà la propera versió amb estacions amb transbordament (16:9).
- Característiques
- Stack tecnològic
- Arquitectura
- Estructura del projecte
- Posada en marxa
- Variables d'entorn
- Scripts
- Configuració per URL
- Mode kiosk
- API de TMB
- Gestió de textos
- Desplegament (Vercel)
- Autohospedatge amb Docker
- Raspberry Pi (kiosk per HDMI)
- Paleta oficial
- Crèdits i llicència
- Temps real des de l'API oficial de TMB (
api.tmb.cat/v1). - Configurador integrat: línia i estació de tota la xarxa, resoltes dinàmicament des de l'API.
- Càlcul de minuts en client comparant
temps_arribada(Unix ts) amb el rellotge del sistema; mostraEntraals darrers 15 s. - Alertes duals: oficials (endpoint
alerts) i personalitzades (Rodalies, FGC, Tram… amb títol, text, colors i icona). - URLs autoconfigurables: tota la configuració del panell viatja en query params.
- Mode kiosk: amaga el botó de configuració; es reobre amb la tecla
c.
| Capa | Tecnologia |
|---|---|
| UI | React 19 |
| Llenguatge | TypeScript 5.9 |
| Build | Vite 7 |
| Estils | Tailwind CSS 4 (@tailwindcss/postcss) |
| Routing | React Router 7 (/, /credits, /docs) |
| Icones | lucide-react |
| Utils d'estil | class-variance-authority, clsx, tailwind-merge |
El configurador serialitza l'estat del panell a la URL; la pantalla el deserialitza en carregar, consulta l'API de TMB i renderitza amb el càlcul de minuts fet en client.
flowchart LR
A[Configurator.tsx] -->|generateShareableUrl| B[URL amb query params]
B -->|parseConfigFromUrl| C[Home.tsx / estat]
C --> D[services/tmbApi.ts]
D <-->|transit · itransit · alerts| E[(TMB API v1)]
D -->|calculateTimeRemaining| F[MetroScreen.tsx]
C --> F
F --> G[Panell HDMI]
| Mòdul | Rol |
|---|---|
pages/Home.tsx |
Orquestrador: carrega config (URL o defaults), estat i escalat del llenç |
components/Configurator.tsx |
Configuració, alertes personalitzades, tecla c, pantalla completa |
components/MetroScreen.tsx |
Render del panell |
services/tmbApi.ts |
Accés a l'API + calculateTimeRemaining |
utils/urlGenerator.ts |
Serialització/parse de la config a la URL |
config/api.config.ts |
Credencials, BASE_URL, DEFAULT_PANEL_CONFIG |
types/tmb.ts |
Contractes de l'API |
src/
├─ components/ Configurator.tsx · MetroScreen.tsx · ui/select.tsx
├─ config/ api.config.ts
├─ constants/ texts.ts
├─ pages/ Home.tsx · Credits.tsx · Documentation.tsx
├─ services/ tmbApi.ts
├─ types/ tmb.ts
├─ utils/ urlGenerator.ts
└─ App.tsx · main.tsx
public/ lineas/ · transport-icons/ · alertas/
Requereix Node 20.19+ (Vite 7) i credencials de TMB Developer.
git clone https://github.com/00b1b1/tmb-pantalles.git
cd tmb-pantalles
npm install
cp .env.example .env # edita les claus
npm run devVITE_TMB_APP_ID=el_teu_app_id
VITE_TMB_APP_KEY=la_teva_app_keyAl ser una app de client, Vite injecta les VITE_* al bundle: són visibles al navegador. Per a producció, restringeix l'ús de l'App Key al portal de TMB o intermedia amb un proxy.
| Script | Acció |
|---|---|
npm run dev |
Dev server (HMR) |
npm run build |
tsc -b + build de Vite |
npm run preview |
Serveix el build |
npm run lint |
ESLint |
Cada panell es defineix per query params (generateShareableUrl() els genera, parseConfigFromUrl() els llegeix).
| Paràmetre | Tipus | Req. | Default | Descripció |
|---|---|---|---|---|
line |
string |
Sí | — | Nom de línia (L4) |
station |
number |
Sí | — | Codi d'estació |
direction |
number |
No | 1 |
Sentit/andana |
alerts |
boolean |
No | false |
Alertes oficials |
emergency |
boolean |
No | true |
Avís d'emergència |
hideConfig |
boolean |
No | false |
Amaga el botó (kiosk) |
alertIds |
number[] |
No | [] |
IDs d'alertes actives (coma) |
customAlerts |
string |
No | — | Alertes pròpies (JSON + Base64) |
/?line=L4&station=428&direction=1&hideConfig=true
Amb hideConfig=true (o el toggle) s'oculta el botó de configuració; es reobre amb la tecla c. El configurador també alterna pantalla completa via requestFullscreen().
Base: https://api.tmb.cat/v1 (totes les crides adjunten app_id i app_key).
| Funció | Endpoint |
|---|---|
fetchAllLines() |
transit/linies/metro/ |
fetchLineStations(lineCode) |
transit/linies/metro/{lineCode}/estacions |
fetchStationInfo(stationId, lineId) |
itransit/metro/estacions/{stationId}?codi_linia={lineId} |
fetchLineAlerts(lineName) |
alerts/metro/channels/WEB/routes/{lineName} |
calculateTimeRemaining() opera sobre timestamps Unix (ms): retorna mm:ss, col·lapsa a N min quan escau, i mostra Entra als darrers 15 s.
Textos centralitzats a src/constants/texts.ts (objecte APP_TEXTS). Modifica'ls per editar o traduir tota la interfície.
Preset Vite (build npm run build, output dist) i defineix VITE_TMB_APP_ID / VITE_TMB_APP_KEY a Environment Variables.
Build de producció servida per nginx (amb fallback SPA per a les rutes de React Router). Les claus VITE_* s'incrusten en temps de build, per això es passen com a build args.
Dockerfile, nginx.conf i docker-compose.yml inclosos al repo. Aixecar:
cp .env.example .env # VITE_TMB_APP_ID / VITE_TMB_APP_KEY
docker compose up -d --buildDisponible a http://localhost:8080. Reconstruir després de canvis: docker compose up -d --build.
Reproductor dedicat que serveix la teva pròpia build (no la demo) i arrenca en kiosk. Ligero: Pi OS Lite + Docker + cage (compositor Wayland d'una sola app) + Chromium. Recomanat Pi 4/5 per compilar; en plaques petites, aixeca el contenidor en un altre host de la LAN i apunta la URL a http://IP:8080.
1. OS + Docker. Flasheja Raspberry Pi OS Lite (64-bit) (hostname, SSH i Wi‑Fi des de l'Imager). Després:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER" # torna a iniciar sessió2. Servir la build. Segueix Autohospedatge amb Docker → panell a http://localhost:8080.
3. Resolució HDMI. A /boot/firmware/cmdline.txt (mateixa línia, video= al final):
- 16:9:
video=HDMI-A-1:1920x1080@60 - Barra 2,8:1 (~1512×540):
video=HDMI-A-1:1512x540M@60— el sufixMgenera el modeline CVT (el monitor l'ha d'acceptar). - Vertical: afegeix
,rotate=90.
4. Kiosk.
sudo apt install -y --no-install-recommends cage chromium-browser
cat > ~/kiosk.sh <<'EOF'
#!/usr/bin/env bash
URL="http://localhost:8080/?line=L4&station=428&direction=1&hideConfig=true"
exec chromium-browser --kiosk "$URL" --ozone-platform=wayland \
--noerrdialogs --disable-infobars --disable-session-crashed-bubble \
--check-for-update-interval=31536000
EOF
chmod +x ~/kiosk.sh5. Autoarranque. sudo raspi-config → Console Autologin. A ~/.bash_profile:
[ "$(tty)" = "/dev/tty1" ] && exec cage -- ~/kiosk.shsudo reboot i el panell omple la pantalla.
Opcional: reinici diari 0 4 * * * /sbin/reboot (sudo crontab -e); apagat nocturn amb wlr-randr --output HDMI-A-1 --off / --on.
| Mostra | Ús | Hex |
|---|---|---|
| Vermell TMB | #C8102E |
|
| Groc alerta | #FFE501 |
|
| Fons | #FFFFFF |
|
| Text / detalls | #000000 |
Icones de transport: TMB-Barcelona/TMB-Icons.
Desenvolupat per a la comunitat de transport de Barcelona per Ahmed Bibi. Llicència MIT.
Projecte no oficial de Transports Metropolitans de Barcelona (TMB). Marques, colors i iconografia pertanyen als seus titulars.
Resolución recomendada.
- Máxima fidelidad: pantalla tipo barra / ultrapanorámica 2,8:1, equivalente al lienzo nativo 1512 × 540. La franja llena la pantalla de punta a punta, como los paneles reales de andén.
- Uso general / futuro: Full HD 1920 × 1080 (16:9). Aquí la franja actual se muestra centrada; es la resolución que requerirá la próxima versión con estaciones con transbordo (16:9).
- Características
- Stack tecnológico
- Arquitectura
- Estructura del proyecto
- Puesta en marcha
- Variables de entorno
- Scripts
- Configuración por URL
- Modo kiosk
- API de TMB
- Gestión de textos
- Despliegue (Vercel)
- Autohospedaje con Docker
- Raspberry Pi (kiosk por HDMI)
- Paleta oficial
- Créditos y licencia
- Tiempo real desde la API oficial de TMB (
api.tmb.cat/v1). - Configurador integrado: línea y estación de toda la red, resueltas dinámicamente desde la API.
- Cálculo de minutos en cliente comparando
temps_arribada(Unix ts) con el reloj del sistema; muestraEntraen los últimos 15 s. - Alertas duales: oficiales (endpoint
alerts) y personalizadas (Rodalies, FGC, Tram… con título, texto, colores e icono). - URLs autoconfigurables: toda la configuración del panel viaja en query params.
- Modo kiosk: oculta el botón de configuración; se reabre con la tecla
c.
| Capa | Tecnología |
|---|---|
| UI | React 19 |
| Lenguaje | TypeScript 5.9 |
| Build | Vite 7 |
| Estilos | Tailwind CSS 4 (@tailwindcss/postcss) |
| Routing | React Router 7 (/, /credits, /docs) |
| Iconos | lucide-react |
| Utils de estilo | class-variance-authority, clsx, tailwind-merge |
El configurador serializa el estado del panel en la URL; la pantalla lo deserializa al cargar, consulta la API de TMB y renderiza con el cálculo de minutos hecho en cliente.
flowchart LR
A[Configurator.tsx] -->|generateShareableUrl| B[URL con query params]
B -->|parseConfigFromUrl| C[Home.tsx / estado]
C --> D[services/tmbApi.ts]
D <-->|transit · itransit · alerts| E[(TMB API v1)]
D -->|calculateTimeRemaining| F[MetroScreen.tsx]
C --> F
F --> G[Panel HDMI]
| Módulo | Rol |
|---|---|
pages/Home.tsx |
Orquestador: carga config (URL o defaults), estado y escalado del lienzo |
components/Configurator.tsx |
Configuración, alertas personalizadas, tecla c, pantalla completa |
components/MetroScreen.tsx |
Render del panel |
services/tmbApi.ts |
Acceso a la API + calculateTimeRemaining |
utils/urlGenerator.ts |
Serialización/parse de la config en la URL |
config/api.config.ts |
Credenciales, BASE_URL, DEFAULT_PANEL_CONFIG |
types/tmb.ts |
Contratos de la API |
src/
├─ components/ Configurator.tsx · MetroScreen.tsx · ui/select.tsx
├─ config/ api.config.ts
├─ constants/ texts.ts
├─ pages/ Home.tsx · Credits.tsx · Documentation.tsx
├─ services/ tmbApi.ts
├─ types/ tmb.ts
├─ utils/ urlGenerator.ts
└─ App.tsx · main.tsx
public/ lineas/ · transport-icons/ · alertas/
Requiere Node 20.19+ (Vite 7) y credenciales de TMB Developer.
git clone https://github.com/00b1b1/tmb-pantalles.git
cd tmb-pantalles
npm install
cp .env.example .env # edita las claves
npm run devVITE_TMB_APP_ID=tu_app_id
VITE_TMB_APP_KEY=tu_app_keyAl ser una app de cliente, Vite inyecta las VITE_* en el bundle: son visibles en el navegador. Para producción, restringe el uso de la App Key en el portal de TMB o intermedia con un proxy.
| Script | Acción |
|---|---|
npm run dev |
Dev server (HMR) |
npm run build |
tsc -b + build de Vite |
npm run preview |
Sirve el build |
npm run lint |
ESLint |
Cada panel se define por query params (generateShareableUrl() los genera, parseConfigFromUrl() los lee).
| Parámetro | Tipo | Req. | Default | Descripción |
|---|---|---|---|---|
line |
string |
Sí | — | Nombre de línea (L4) |
station |
number |
Sí | — | Código de estación |
direction |
number |
No | 1 |
Sentido/andén |
alerts |
boolean |
No | false |
Alertas oficiales |
emergency |
boolean |
No | true |
Aviso de emergencia |
hideConfig |
boolean |
No | false |
Oculta el botón (kiosk) |
alertIds |
number[] |
No | [] |
IDs de alertas activas (coma) |
customAlerts |
string |
No | — | Alertas propias (JSON + Base64) |
/?line=L4&station=428&direction=1&hideConfig=true
Con hideConfig=true (o el toggle) se oculta el botón de configuración; se reabre con la tecla c. El configurador también alterna pantalla completa vía requestFullscreen().
Base: https://api.tmb.cat/v1 (todas las llamadas adjuntan app_id y app_key).
| Función | Endpoint |
|---|---|
fetchAllLines() |
transit/linies/metro/ |
fetchLineStations(lineCode) |
transit/linies/metro/{lineCode}/estacions |
fetchStationInfo(stationId, lineId) |
itransit/metro/estacions/{stationId}?codi_linia={lineId} |
fetchLineAlerts(lineName) |
alerts/metro/channels/WEB/routes/{lineName} |
calculateTimeRemaining() opera sobre timestamps Unix (ms): devuelve mm:ss, colapsa a N min cuando procede, y muestra Entra en los últimos 15 s.
Textos centralizados en src/constants/texts.ts (objeto APP_TEXTS). Modifícalos para editar o traducir toda la interfaz.
Preset Vite (build npm run build, output dist) y define VITE_TMB_APP_ID / VITE_TMB_APP_KEY en Environment Variables.
Build de producción servida por nginx (con fallback SPA para las rutas de React Router). Las claves VITE_* se hornean en tiempo de build, por eso se pasan como build args.
Dockerfile, nginx.conf y docker-compose.yml incluidos en el repo. Levantar:
cp .env.example .env # VITE_TMB_APP_ID / VITE_TMB_APP_KEY
docker compose up -d --buildDisponible en http://localhost:8080. Reconstruir tras cambios: docker compose up -d --build.
Reproductor dedicado que sirve tu propia build (no la demo) y arranca en kiosk. Ligero: Pi OS Lite + Docker + cage (compositor Wayland de una sola app) + Chromium. Recomendado Pi 4/5 para compilar; en placas pequeñas, levanta el contenedor en otro host de la LAN y apunta la URL a http://IP:8080.
1. OS + Docker. Flashea Raspberry Pi OS Lite (64-bit) (hostname, SSH y Wi‑Fi desde el Imager). Después:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER" # vuelve a iniciar sesión2. Servir la build. Sigue Autohospedaje con Docker → panel en http://localhost:8080.
3. Resolución HDMI. En /boot/firmware/cmdline.txt (misma línea, video= al final):
- 16:9:
video=HDMI-A-1:1920x1080@60 - Barra 2,8:1 (~1512×540):
video=HDMI-A-1:1512x540M@60— el sufijoMgenera el modeline CVT (el monitor debe aceptarlo). - Vertical: añade
,rotate=90.
4. Kiosk.
sudo apt install -y --no-install-recommends cage chromium-browser
cat > ~/kiosk.sh <<'EOF'
#!/usr/bin/env bash
URL="http://localhost:8080/?line=L4&station=428&direction=1&hideConfig=true"
exec chromium-browser --kiosk "$URL" --ozone-platform=wayland \
--noerrdialogs --disable-infobars --disable-session-crashed-bubble \
--check-for-update-interval=31536000
EOF
chmod +x ~/kiosk.sh5. Autoarranque. sudo raspi-config → Console Autologin. En ~/.bash_profile:
[ "$(tty)" = "/dev/tty1" ] && exec cage -- ~/kiosk.shsudo reboot y el panel llena la pantalla.
Opcional: reinicio diario 0 4 * * * /sbin/reboot (sudo crontab -e); apagado nocturno con wlr-randr --output HDMI-A-1 --off / --on.
| Muestra | Uso | Hex |
|---|---|---|
| Rojo TMB | #C8102E |
|
| Amarillo alerta | #FFE501 |
|
| Fondo | #FFFFFF |
|
| Texto / detalles | #000000 |
Iconos de transporte: TMB-Barcelona/TMB-Icons.
Desarrollado para la comunidad de transporte de Barcelona por Ahmed Bibi. Licencia MIT.
Proyecto no oficial de Transports Metropolitans de Barcelona (TMB). Marcas, colores e iconografía pertenecen a sus titulares.