first commit
This commit is contained in:
@@ -0,0 +1,168 @@
|
|||||||
|
# Kiosk Chromium — Debian 13 (trixie) — Dell OptiPlex 3040
|
||||||
|
|
||||||
|
Kiosk wyświetlający stronę `http://instrukcje.local` w Chromium na pełnym ekranie. Używa `sway` jako kompozytora Wayland oraz `swayidle` + `wlopm` do zarządzania energią monitora.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wymagania
|
||||||
|
|
||||||
|
- Dell OptiPlex 3040, monitor DP lub HDMI
|
||||||
|
- Debian 13 (trixie) netinst — instalacja minimalna bez środowiska graficznego
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalacja krok po kroku
|
||||||
|
|
||||||
|
### 1. Utwórz użytkownika kiosk
|
||||||
|
|
||||||
|
```bash
|
||||||
|
useradd -m -r -s /usr/sbin/nologin kiosk
|
||||||
|
usermod -aG tty,input,video,dialout kiosk
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Zainstaluj pakiety
|
||||||
|
|
||||||
|
```bash
|
||||||
|
apt update && apt install -y \
|
||||||
|
sway swayidle wlopm wlr-randr \
|
||||||
|
chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
> `cage` **nie** obsługuje protokołu `wlr-output-power-management-v1`, przez co `wlopm` z nim nie działa — ekran można wygasić, ale nie można go wybudzić. Dlatego używamy `sway`.
|
||||||
|
|
||||||
|
### 3. Skopiuj pliki konfiguracyjne
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Skrypt zarządzający kioskiem
|
||||||
|
cp kiosk-manager.sh /home/kiosk/kiosk-manager.sh
|
||||||
|
chmod +x /home/kiosk/kiosk-manager.sh
|
||||||
|
|
||||||
|
# Strona oczekiwania na sieć
|
||||||
|
cp waiting.html /home/kiosk/waiting.html
|
||||||
|
chown kiosk:kiosk /home/kiosk/waiting.html
|
||||||
|
|
||||||
|
# Konfiguracja sway
|
||||||
|
mkdir -p /home/kiosk/.config/sway
|
||||||
|
cp sway-config /home/kiosk/.config/sway/config
|
||||||
|
chown -R kiosk:kiosk /home/kiosk/.config
|
||||||
|
|
||||||
|
# Jednostka systemd
|
||||||
|
cp kiosk.service /etc/systemd/system/kiosk.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Włącz i uruchom serwis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable kiosk.service
|
||||||
|
systemctl start kiosk.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Opis plików
|
||||||
|
|
||||||
|
### `kiosk.service`
|
||||||
|
|
||||||
|
Jednostka systemd uruchamiająca `sway` jako użytkownik `kiosk`. Ważne parametry:
|
||||||
|
|
||||||
|
| Opcja | Wartość | Opis |
|
||||||
|
|---|---|---|
|
||||||
|
| `PAMName=login` | `login` | Dostęp do urządzeń przez logind (GPU, input) |
|
||||||
|
| `RuntimeDirectory=kiosk` | `kiosk` | Automatycznie tworzy `/run/kiosk` z prawami 0700 |
|
||||||
|
| `XDG_RUNTIME_DIR` | `/run/kiosk` | Wymagane przez sway / Wayland |
|
||||||
|
| `WLR_LIBSEAT_BACKEND` | `logind` | Dostęp do GPU bez uprawnień roota |
|
||||||
|
| `IDLE_TIME` | `120` | Czas bezczynności (sekundy) przed wygaszeniem ekranu |
|
||||||
|
| `ExecStart` | `/usr/bin/sway` | Kompozytor Wayland |
|
||||||
|
| `Restart=always` | — | Automatyczny restart po awarii / wyjściu Chromium |
|
||||||
|
|
||||||
|
**Zmiana czasu wygaszania:**
|
||||||
|
```bash
|
||||||
|
# Edytuj /etc/systemd/system/kiosk.service
|
||||||
|
Environment=IDLE_TIME=300 # 5 minut
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart kiosk.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `sway-config` → `/home/kiosk/.config/sway/config`
|
||||||
|
|
||||||
|
Minimalna konfiguracja sway:
|
||||||
|
|
||||||
|
```
|
||||||
|
output * bg #000000 solid_color # czarne tło
|
||||||
|
output * transform 90 # obrót monitora o 90° (usuń jeśli zbędne)
|
||||||
|
exec swayidle before-sleep "wlopm --off '*'" # wygaszenie przy sleep
|
||||||
|
exec /home/kiosk/kiosk-manager.sh # start kiosku
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `kiosk-manager.sh` → `/home/kiosk/kiosk-manager.sh`
|
||||||
|
|
||||||
|
Uruchamiany przez sway przy starcie sesji. Kolejność działania:
|
||||||
|
|
||||||
|
1. Uruchamia `swayidle` **bez flagi `-w`** — ignoruje idle inhibitory (Chromium ustawia inhibitor Wayland; `swayidle -w` respektuje go i nigdy nie wygasiłby ekranu)
|
||||||
|
2. `wlopm --off '*'` po IDLE_TIME sekundach — wysyła DPMS standby przez protokół `wlr-output-power-management-v1`; link DP/HDMI pozostaje aktywny, więc wybudzenie jest niezawodne
|
||||||
|
3. Chromium startuje **natychmiast** z lokalnym plikiem `waiting.html` (ekran oczekiwania)
|
||||||
|
4. Ping-loop czeka, aż `KIOSK_HOST` (192.168.40.126) odpowie
|
||||||
|
5. Po uzyskaniu sieci: Chromium jest zamykany i restartowany z docelowym `http://instrukcje.local`
|
||||||
|
|
||||||
|
**Zmiana adresu IP serwera:**
|
||||||
|
```bash
|
||||||
|
# W kiosk-manager.sh zmień zmienną:
|
||||||
|
KIOSK_HOST="192.168.40.126"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `waiting.html` → `/home/kiosk/waiting.html`
|
||||||
|
|
||||||
|
Lokalna strona HTML wyświetlana przez Chromium w trakcie oczekiwania na sieć. Ciemne tło, animowany spinner i pulsujący napis „Oczekiwanie na sieć...".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zarządzanie i diagnostyka
|
||||||
|
|
||||||
|
### Logi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ostatnie 100 linii logów serwisu
|
||||||
|
journalctl -u kiosk.service -b --no-pager -n 100 -o cat
|
||||||
|
|
||||||
|
# Śledzenie na żywo
|
||||||
|
journalctl -u kiosk.service -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart / zatrzymanie
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl restart kiosk.service
|
||||||
|
systemctl stop kiosk.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sprawdzenie wyjść wideo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk wlr-randr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ręczne wygaszenie / wybudzenie
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk wlopm --off '*'
|
||||||
|
WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk wlopm --on '*'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rozwiązywanie problemów
|
||||||
|
|
||||||
|
| Objaw | Przyczyna | Rozwiązanie |
|
||||||
|
|---|---|---|
|
||||||
|
| Serwis natychmiast się kończy | Brak `/run/kiosk` | Sprawdź `RuntimeDirectory=kiosk` w pliku unit |
|
||||||
|
| `XDG_RUNTIME_DIR is not set` | Brak zmiennej środowiskowej | Upewnij się że `Environment=XDG_RUNTIME_DIR=/run/kiosk` jest w unit |
|
||||||
|
| Ekran się nie wygasza | Chromium blokuje idle | `swayidle` musi być uruchomiony **bez** flagi `-w` |
|
||||||
|
| Ekran gaśnie ale nie wraca | `wlr-randr --off` rozłącza link DRM | Używaj `wlopm` z `sway` (nie `wlr-randr --off`) |
|
||||||
|
| `wlopm: Wayland server does not support...` | Kompozytor bez protokołu power-management | Zamień `cage` na `sway` |
|
||||||
|
| Czarny ekran po restarcie | Chromium trzyma zablokowany profil | `rm -rf /home/kiosk/.cache/chromium && systemctl restart kiosk.service` |
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
IDLE_TIME=${IDLE_TIME:-300}
|
||||||
|
KIOSK_HOST="192.168.40.126"
|
||||||
|
KIOSK_URL="http://instrukcje.local"
|
||||||
|
WAITING_PAGE="file:///home/kiosk/waiting.html"
|
||||||
|
|
||||||
|
CHROMIUM_OPTS=(
|
||||||
|
--ozone-platform=wayland
|
||||||
|
--no-first-run
|
||||||
|
--noerrdialogs
|
||||||
|
--disable-infobars
|
||||||
|
--disable-gpu-suspend
|
||||||
|
--force-device-scale-factor=1
|
||||||
|
--user-data-dir=/home/kiosk/.cache/chromium
|
||||||
|
)
|
||||||
|
|
||||||
|
# wlopm działa z sway (obsługuje wlr-output-power-management-v1)
|
||||||
|
# swayidle bez -w: ignoruje idle inhibitory (np. z Chromium)
|
||||||
|
swayidle \
|
||||||
|
timeout "$IDLE_TIME" "wlopm --off '*'" \
|
||||||
|
resume "wlopm --on '*'" &
|
||||||
|
|
||||||
|
# Uruchom Chromium od razu z lokalną stroną oczekiwania
|
||||||
|
chromium --kiosk --app="$WAITING_PAGE" "${CHROMIUM_OPTS[@]}" &
|
||||||
|
CHROMIUM_PID=$!
|
||||||
|
|
||||||
|
# Czekaj na dostępność serwera kiosku
|
||||||
|
while ! ping -c 1 -W 1 "$KIOSK_HOST" > /dev/null 2>&1; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sieć dostępna — zamknij Chromium i uruchom właściwy kiosk
|
||||||
|
kill "$CHROMIUM_PID"
|
||||||
|
wait "$CHROMIUM_PID" 2>/dev/null
|
||||||
|
|
||||||
|
exec chromium --kiosk --app="$KIOSK_URL" "${CHROMIUM_OPTS[@]}"
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# kiosk-wrapper: uruchamiany jako root
|
||||||
|
set -e
|
||||||
|
|
||||||
|
XDG_RUNTIME_DIR=/run/kiosk
|
||||||
|
WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-wayland-0}
|
||||||
|
DPMS_PIPE=/run/kiosk-dpms
|
||||||
|
DPMS_FILE=$(find /sys/class/drm -maxdepth 2 -name "dpms" 2>/dev/null \
|
||||||
|
| xargs grep -l "^On" 2>/dev/null | head -n1)
|
||||||
|
|
||||||
|
mkdir -p "$XDG_RUNTIME_DIR"
|
||||||
|
chown kiosk:kiosk "$XDG_RUNTIME_DIR" || true
|
||||||
|
chmod 0700 "$XDG_RUNTIME_DIR" || true
|
||||||
|
|
||||||
|
# Usuń stary pipe jeśli istnieje
|
||||||
|
rm -f "$DPMS_PIPE"
|
||||||
|
mkfifo -m 0666 "$DPMS_PIPE"
|
||||||
|
|
||||||
|
# Demon DPMS (root) — czyta polecenia on/off z pipe i zapisuje do sysfs
|
||||||
|
# Działa w tle przez cały czas życia serwisu
|
||||||
|
( while true; do
|
||||||
|
if read -r cmd < "$DPMS_PIPE"; then
|
||||||
|
case "$cmd" in
|
||||||
|
off) echo -n "Off" > "$DPMS_FILE" 2>/dev/null ;;
|
||||||
|
on) echo -n "On" > "$DPMS_FILE" 2>/dev/null ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done ) &
|
||||||
|
DPMS_DAEMON_PID=$!
|
||||||
|
|
||||||
|
export XDG_RUNTIME_DIR
|
||||||
|
export WAYLAND_DISPLAY
|
||||||
|
export DPMS_PIPE
|
||||||
|
|
||||||
|
# Uruchom cage; po jego zakończeniu zabij demona
|
||||||
|
trap "kill $DPMS_DAEMON_PID 2>/dev/null; rm -f $DPMS_PIPE" EXIT
|
||||||
|
exec /usr/bin/cage -s -- /home/kiosk/kiosk-manager.sh
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Instrukcje Kiosk
|
||||||
|
After=network-online.target systemd-user-sessions.service
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=kiosk
|
||||||
|
Group=kiosk
|
||||||
|
PAMName=login
|
||||||
|
Type=simple
|
||||||
|
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
RuntimeDirectory=kiosk
|
||||||
|
RuntimeDirectoryMode=0700
|
||||||
|
Environment=XDG_RUNTIME_DIR=/run/kiosk
|
||||||
|
Environment=WLR_LIBSEAT_BACKEND=logind
|
||||||
|
Environment=WLR_RENDERER=gles2
|
||||||
|
|
||||||
|
Environment=IDLE_TIME=120
|
||||||
|
|
||||||
|
Environment=XDG_SESSION_TYPE=wayland
|
||||||
|
Environment=XDG_SESSION_CLASS=user
|
||||||
|
|
||||||
|
CapabilityBoundingSet=CAP_SYS_TTY_CONFIG
|
||||||
|
AmbientCapabilities=CAP_SYS_TTY_CONFIG
|
||||||
|
|
||||||
|
ExecStart=/usr/bin/sway
|
||||||
|
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
# Minimalny config sway dla kiosku
|
||||||
|
# Plik docelowy: /home/kiosk/.config/sway/config
|
||||||
|
|
||||||
|
# Czarne tło zamiast domyślnego
|
||||||
|
output * bg #000000 solid_color
|
||||||
|
|
||||||
|
# Obrót monitora o 270 stopni (DP/HDMI)
|
||||||
|
output * transform 270
|
||||||
|
|
||||||
|
# NumLock zawsze włączony (startup i wybudzanie)
|
||||||
|
input * xkb_numlock enabled
|
||||||
|
|
||||||
|
# Ukryj kursor po 3 sekundach bezczynności
|
||||||
|
#seat * hide_cursor 3000
|
||||||
|
|
||||||
|
# Wyłącz wygaszacz ekranu sway (zarządzamy sami przez swayidle)
|
||||||
|
exec swayidle before-sleep "wlopm --off '*'"
|
||||||
|
|
||||||
|
# Uruchom skrypt kiosku (swayidle + Chromium)
|
||||||
|
exec /home/kiosk/kiosk-manager.sh
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Oczekiwanie na sieć</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #0a0a0a;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: 'Segoe UI', Arial, sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border: 5px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-top-color: rgba(255, 255, 255, 0.55);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
animation: pulse 2.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 0.6; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots {
|
||||||
|
display: inline-block;
|
||||||
|
width: 2em;
|
||||||
|
text-align: left;
|
||||||
|
animation: dots 1.5s steps(4, end) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dots {
|
||||||
|
0% { content: ''; clip-path: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots::after {
|
||||||
|
content: '';
|
||||||
|
animation: dotcycle 1.5s steps(4, end) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dotcycle {
|
||||||
|
0% { content: ''; }
|
||||||
|
25% { content: '.'; }
|
||||||
|
50% { content: '..'; }
|
||||||
|
75% { content: '...'; }
|
||||||
|
100% { content: ''; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div class="text">Oczekiwanie na sieć<span class="dots"></span></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user