# Bondrucker-Bruecke Kleiner Dienst für den Raspberry Pi, an dem der Kassendrucker per USB hängt. ## Warum es ihn gibt Der Bondrucker ist ein USB-Gerät (`/dev/usb/lp0`) und damit nur für den Rechner sichtbar, an dem er steckt. Der Timer-Server läuft woanders. Diese Brücke nimmt fertige ESC/POS-Daten per HTTP entgegen und schiebt sie in das Gerät — mehr nicht. Der Bon selbst wird in der Timer-App gebaut (`backend/receipt.js`), damit Layout-Änderungen nicht auf dem Pi angefasst werden müssen. Nur Python-Standardbibliothek, kein pip, kein CUPS, kein node. ## Installation auf dem Pi ```bash # Dateien auf den Pi kopieren scp print-server.py bondrucker.service install.sh README.md pi@:/tmp/bondrucker/ # Dort einrichten ssh pi@ cd /tmp/bondrucker && sudo bash install.sh ``` `install.sh` legt an: | Was | Wo | |---|---| | Dienstbenutzer `bondrucker` (Gruppe `lp`) | — | | Programm | `/opt/bondrucker/print-server.py` | | Token | `/etc/bondrucker/env` (`0640`, root:bondrucker) | | udev-Regel | `/etc/udev/rules.d/99-bondrucker.rules` | | Dienst | `bondrucker.service`, aktiviert und gestartet | Am Ende gibt das Skript den Wert für `PRINTER_TOKEN` aus. Der gehört in die `.env` des Timer-Servers. ## Timer-Server konfigurieren In `backend/.env`: ``` PRINTER_HOST=192.168.8.157 PRINTER_PORT=9100 PRINTER_TOKEN= PRINTER_WIDTH=42 ``` Danach `systemctl restart timer-app`. Ist `PRINTER_HOST` leer, ist die Druckfunktion aus und die App verhält sich wie vorher — ohne Bon-Knöpfe. ## Prüfen ```bash # Zustand des Druckers curl -s http://:9100/status # Aus der App heraus: Einstellungen -> "Probedruck" ``` Antwortet `/status` mit `"ok": true`, ist das Gerät da und beschreibbar. ## Schnittstelle | Aufruf | Wirkung | |---|---| | `GET /status` | JSON mit Gerät, Beschreibbarkeit, letztem Fehler. HTTP 503, wenn kein Drucker da ist | | `POST /print` | Rumpf = rohe ESC/POS-Bytes, Kopfzeile `X-Print-Token`. Antwortet `{"ok":true,"bytes":N}` | Fehler kommen als HTTP 4xx/5xx mit `{"error": "..."}` zurück — die Timer-App zeigt diesen Text unverändert an. „Papier leer" und „Deckel offen" landen als `OSError` beim Schreiben und damit als HTTP 502 in der Anzeige. ## Betriebshinweise * **Feste IP vergeben.** Der Pi hängt per WLAN und DHCP im Netz. Wechselt die Adresse, druckt die App nicht mehr, bis `PRINTER_HOST` angepasst ist. Eine DHCP-Reservierung im Router ist der einfachste Weg. * **USB-Kabel neu gesteckt?** Aus `lp0` kann `lp1` werden. Der Dienst sucht deshalb selbst nach `/dev/usb/lp*` — es ist nichts zu tun. * **Ein Auftrag nach dem anderen.** Der Dienst serialisiert Druckaufträge, zwei gleichzeitige Bons können sich nicht ineinanderschieben. * **Netzgrenze.** Port 9100 sollte nur aus dem eigenen Netz erreichbar sein. Das Token schützt gegen versehentliche, nicht gegen entschlossene Zugriffe. ## Fehlersuche ```bash systemctl status bondrucker journalctl -u bondrucker -f # jeder Auftrag wird protokolliert # Am Pi direkt drucken, ohne App und ohne Dienst: printf 'Test\n\n\n\n\x1dVB\x00' | sudo tee /dev/usb/lp0 > /dev/null ``` | Symptom | Ursache | |---|---| | `/status` sagt `deviceExists: false` | Drucker aus oder USB-Kabel ab | | `/status` sagt `writable: false` | udev-Regel nicht gegriffen — `sudo udevadm trigger`, Dienst neu starten | | HTTP 403 | `PRINTER_TOKEN` in der `.env` weicht von `/etc/bondrucker/env` ab | | App meldet „nimmt keine Verbindung an" | Dienst läuft nicht, oder falsche IP/Port | | Umlaute als Kringel | Drucker beherrscht Codepage 16 (WPC1252) nicht — siehe `backend/escpos.js` |