Drucker wird in der App gewaehlt, nicht auf dem Pi
Deploy auf den Timer-Server / deploy (push) Successful in 20s
Deploy auf den Timer-Server / deploy (push) Successful in 20s
Das Einrichtungsskript fragte bisher am Pi, welcher Drucker bedient werden soll. Damit stand die Einstellung an einem Ort, an den man nur mit einem Terminal kommt - und ein zweiter Drucker am Pi bedeutete: Skript nochmal. Jetzt fragt die App den Pi, was angeschlossen ist, und der Bediener waehlt in den Einstellungen. Die Bruecke bekam dafuer /devices; sie liest die Klartextnamen aus dem sysfs, damit dort "EPSON TM-T88IV" steht und nicht "lp0" - bei zwei Druckern der Unterschied zwischen Wissen und Raten. Der gewaehlte Pfad geht bei jedem Auftrag im Kopf X-Print-Device mit. Die Bruecke haelt dazu bewusst keinen Zustand und das Skript schreibt kein PRINT_DEVICE mehr: es soll genau eine Stelle geben, die weiss, welcher Drucker gemeint ist, naemlich die, an der es bedient wird. Leer bleibt "automatisch" - das erste gefundene Geraet, zugleich der Rueckfall, wenn aus lp0 nach dem Umstecken ein lp1 geworden ist. Der Pfad kommt damit von aussen und wird an beiden Enden geprueft, in PUT /api/settings und noch einmal in der Bruecke. Eine Schranke, die nur an einem Ende steht, ist keine: ohne die Pruefung auf dem Pi liesse sich der Dienst dazu bringen, in eine beliebige Datei zu schreiben. Nebenbei: ein nicht bereiter Drucker meldete sich in der Oberflaeche als "HTTP 503". Das verriet niemandem, dass nur ein Stecker lose ist. Die 503 der Bruecke ist kein Protokollfehler, und ihr Rumpf sagt, was fehlt - der wird jetzt ausgewertet. Geprueft am echten Pi (Debian 13, TM-T88IV): Einrichtung ohne Rueckfrage, Geraeteliste mit Namen, ausdrueckliche Wahl, Druck darauf, Rueckkehr auf automatisch, Druck darauf. Dazu die Abweise: /etc/passwd und ein Pfad mit angehaengtem Befehl werden zurueckgewiesen, ein erlaubtes aber fehlendes /dev/usb/lp7 ergibt eine Meldung im Klartext. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e68daee457
commit
0e8d826bce
10 files changed
+412
-104
No files matched your search
+93
-1
@@ -11,6 +11,7 @@ const state = {
|
||||
timers: [], // {id, customerName, isRunning, baseMs, syncedAt}
|
||||
rate: { rateAmount: 10, rateInterval: 10 },
|
||||
printerConfigured: false, // steuert, ob die Bon-Knoepfe erscheinen
|
||||
printerDevice: '', // gewaehltes Geraet am Pi, leer = automatisch
|
||||
printAutoOnStop: true, // Bon beim Beenden automatisch drucken
|
||||
tickHandle: null,
|
||||
syncHandle: null,
|
||||
@@ -564,6 +565,7 @@ async function loadSettings() {
|
||||
const settings = await api('/api/settings');
|
||||
state.rate = settings;
|
||||
state.printerConfigured = !!(settings.printer && settings.printer.configured);
|
||||
state.printerDevice = (settings.printer && settings.printer.device) || '';
|
||||
state.printAutoOnStop = settings.printAutoOnStop !== false;
|
||||
|
||||
document.getElementById('rateAmount').value = settings.rateAmount;
|
||||
@@ -578,6 +580,7 @@ async function loadSettings() {
|
||||
updateSettingsPreview();
|
||||
renderPrinterState(settings.printer);
|
||||
refreshPrinterStatus();
|
||||
loadDevices();
|
||||
} catch (e) {
|
||||
toast('Einstellungen konnten nicht geladen werden: ' + e.message, 'error');
|
||||
}
|
||||
@@ -670,6 +673,94 @@ async function refreshPrinterStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Druckerliste vom Pi holen.
|
||||
*
|
||||
* Sie kommt bewusst live und nicht aus dem Zwischenspeicher: wer hier auf
|
||||
* "Suchen" tippt, hat gerade ein Kabel umgesteckt und will genau das sehen.
|
||||
*
|
||||
* @param {boolean} lautstark Rueckmeldung geben - beim Knopfdruck ja, beim
|
||||
* stillen Laden der Einstellungen nein.
|
||||
*/
|
||||
async function loadDevices(lautstark = false) {
|
||||
const feld = document.getElementById('printerDevice');
|
||||
const gruppe = document.getElementById('printerDeviceGroup');
|
||||
const hinweis = document.getElementById('printerDeviceHint');
|
||||
if (!feld) return;
|
||||
|
||||
// Ohne eingerichteten Drucker gibt es nichts auszuwaehlen.
|
||||
gruppe.style.display = state.printerConfigured ? '' : 'none';
|
||||
if (!state.printerConfigured) return;
|
||||
|
||||
const gewaehlt = state.printerDevice || '';
|
||||
|
||||
try {
|
||||
const res = await api('/api/printer/devices');
|
||||
state.printerDevice = res.selected || '';
|
||||
|
||||
const automatisch = res.fallback
|
||||
? `automatisch (zurzeit ${res.fallback})`
|
||||
: 'automatisch';
|
||||
|
||||
feld.innerHTML = `<option value="">${escapeHtml(automatisch)}</option>` +
|
||||
res.devices.map(g => {
|
||||
const beschriftung = g.name ? `${g.path} — ${g.name}` : g.path;
|
||||
return `<option value="${escapeHtml(g.path)}">${escapeHtml(beschriftung)}</option>`;
|
||||
}).join('');
|
||||
|
||||
// Der eingestellte Drucker haengt gerade nicht dran. Er darf trotzdem
|
||||
// nicht aus der Liste fallen, sonst stellt ein Speichern ihn unbemerkt
|
||||
// auf "automatisch" um.
|
||||
const eingestellt = res.selected || '';
|
||||
if (eingestellt && !res.devices.some(g => g.path === eingestellt)) {
|
||||
feld.innerHTML += `<option value="${escapeHtml(eingestellt)}">` +
|
||||
`${escapeHtml(eingestellt)} — zurzeit nicht angeschlossen</option>`;
|
||||
}
|
||||
|
||||
feld.value = eingestellt;
|
||||
feld.disabled = false;
|
||||
hinweis.textContent = res.devices.length
|
||||
? 'Welches Gerät am Raspberry Pi bedruckt wird.'
|
||||
: 'Am Pi hängt gerade kein Drucker. Kabel prüfen, dann erneut suchen.';
|
||||
|
||||
if (lautstark) {
|
||||
toast(res.devices.length
|
||||
? `${res.devices.length} Drucker gefunden`
|
||||
: 'Am Pi hängt gerade kein Drucker', res.devices.length ? 'success' : 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
feld.innerHTML = `<option value="">${escapeHtml(gewaehlt || 'automatisch')}</option>`;
|
||||
feld.disabled = true;
|
||||
hinweis.textContent = 'Liste nicht abrufbar: ' + e.message;
|
||||
if (lautstark) toast('Drucker konnten nicht abgerufen werden: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/** Die Auswahl sofort speichern - ein eigener Speichern-Knopf waere hier
|
||||
* eine Falle, weil das Feld weit ueber dem Knopf steht. */
|
||||
async function saveDevice() {
|
||||
const feld = document.getElementById('printerDevice');
|
||||
const vorher = state.printerDevice || '';
|
||||
const neu = feld.value;
|
||||
|
||||
try {
|
||||
const antwort = await api('/api/settings', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
rateAmount: state.rate.rateAmount,
|
||||
rateInterval: state.rate.rateInterval,
|
||||
printerDevice: neu
|
||||
})
|
||||
});
|
||||
state.printerDevice = (antwort.printer && antwort.printer.device) || '';
|
||||
toast(neu ? 'Drucker gewählt: ' + neu : 'Drucker wird automatisch gewählt', 'success');
|
||||
await refreshPrinterStatus();
|
||||
} catch (e) {
|
||||
feld.value = vorher;
|
||||
toast('Auswahl nicht gespeichert: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function testPrint() {
|
||||
const btn = document.getElementById('printerTestBtn');
|
||||
if (btn) btn.disabled = true;
|
||||
@@ -801,8 +892,9 @@ function startSetupWatch() {
|
||||
{ name: 'Übernommen', ok: true,
|
||||
detail: 'Die App druckt ab jetzt auf diesen Pi. Ein Probedruck zeigt, ob Papier drin ist.' }
|
||||
], false);
|
||||
toast('Drucker eingerichtet: ' + z.host, 'success');
|
||||
toast('Drucker eingerichtet: ' + z.host + ' — jetzt oben den Drucker auswählen.', 'success');
|
||||
await refreshPrinterStatus();
|
||||
await loadDevices();
|
||||
await syncTimers().catch(() => {});
|
||||
} else if (z.state === 'expired') {
|
||||
stopSetupWatch();
|
||||
|
||||
Reference in new issue
Block a user