Improve dashboard master config and separate slave deadzone updates.

Always show master deadzone input with read/set controls; apply bulk slave
changes via slaves_only without changing the master's BMA456.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-19 00:15:35 +02:00
co-authored by Cursor
parent 85aeab85c0
commit 80fb9cf55e
3 changed files with 121 additions and 39 deletions
+65 -35
View File
@@ -118,6 +118,12 @@
}
.btn-sm { font-size: 0.8rem; }
.dz-input { width: 5.5rem; }
.config-input { max-width: 8rem; }
.config-block {
border-top: 1px solid var(--pp-border);
margin-top: 1rem;
padding-top: 1rem;
}
</style>
</head>
<body x-data="dashboard()" x-init="connect()">
@@ -145,9 +151,10 @@
<div class="row g-4">
<section class="col-lg-4">
<div class="card h-100">
<div class="card-header">Master</div>
<div class="card-header">Master (BMA456 lokal)</div>
<div class="card-body">
<p class="text-muted small mb-2" x-text="'UART ' + (state.serial_port || '—')"></p>
<p class="text-muted small mb-3">Deadzone nur am Master — Slaves bleiben unverändert.</p>
<template x-if="!state.uart_connected">
<div class="alert alert-warning py-2 mb-2"
x-text="state.serial_error || 'UART disconnected — reconnecting…'"></div>
@@ -156,7 +163,7 @@
<div class="alert alert-danger py-2" x-text="state.serial_error || 'Serial error'"></div>
</template>
<template x-if="state.master?.ok">
<dl class="row mb-3">
<dl class="row mb-0">
<dt class="col-5 text-muted">Version</dt>
<dd class="col-7" x-text="state.master.version"></dd>
<dt class="col-5 text-muted">Git</dt>
@@ -164,21 +171,38 @@
<dt class="col-5 text-muted">Deadzone</dt>
<dd class="col-7" x-text="state.master.deadzone != null ? state.master.deadzone + ' LSB' : '—'"></dd>
</dl>
<div class="d-flex flex-wrap gap-2 align-items-center" x-show="state.uart_connected">
<input type="number" class="form-control form-control-sm dz-input"
min="0" max="4095" placeholder="LSB"
x-model.number="masterDz"
:disabled="busy">
<button type="button" class="btn btn-primary btn-sm"
@click="setDeadzone(0, masterDz)"
:disabled="busy || !state.uart_connected">
Master setzen
</button>
</div>
</template>
<template x-if="state.master && !state.master.ok">
<div class="alert alert-warning py-2" x-text="state.master.error"></div>
<div class="alert alert-warning py-2 mb-0" x-text="state.master.error"></div>
</template>
<div class="config-block">
<h6 class="text-secondary mb-2">Konfiguration Master</h6>
<label class="form-label text-muted small mb-1" for="master-dz-input">
Accel Deadzone (LSB)
</label>
<div class="d-flex flex-wrap gap-2 align-items-end">
<input id="master-dz-input" type="number"
class="form-control form-control-sm config-input"
min="0" max="4095" step="1"
placeholder="z. B. 100"
x-model.number="masterDz"
:disabled="busy || !state.uart_connected">
<button type="button" class="btn btn-outline-secondary btn-sm"
@click="readMasterDeadzone()"
:disabled="busy || !state.uart_connected">
Lesen
</button>
<button type="button" class="btn btn-primary btn-sm"
@click="setMasterDeadzone()"
:disabled="busy || !state.uart_connected">
Setzen
</button>
</div>
<p class="text-muted small mt-2 mb-0" x-show="!state.uart_connected">
UART nicht verbunden — Eingabe gesperrt.
</p>
</div>
</div>
</div>
</section>
@@ -200,7 +224,8 @@
<span class="badge bg-secondary" x-text="(state.clients || []).length + ' registered'"></span>
</div>
</div>
<div class="card-body p-0">
<p class="text-muted small px-3 pt-2 mb-0">Slaves per ESP-NOW — Master-Deadzone bleibt separat.</p>
<div class="card-body p-0 pt-2">
<div class="table-responsive">
<table class="table pp-table table-hover">
<thead>
@@ -307,7 +332,7 @@
this.configMsgOk = ok;
setTimeout(() => { this.configMsg = ''; }, 5000);
},
async setDeadzone(clientId, deadzone) {
async setDeadzone(clientId, deadzone, opts = {}) {
if (deadzone == null || deadzone < 0) {
this.flash('Ungültiger Deadzone-Wert', false);
return;
@@ -321,12 +346,19 @@
write: true,
deadzone: deadzone,
client_id: clientId,
all_clients: false
all_clients: !!opts.allClients,
slaves_only: !!opts.slavesOnly
})
});
const data = await r.json();
if (!r.ok || !data.success) {
this.flash(data.error || `Deadzone für Client ${clientId} fehlgeschlagen`, false);
const err = data.error ||
(opts.slavesOnly ? 'Deadzone für Slaves fehlgeschlagen' : `Deadzone für Client ${clientId} fehlgeschlagen`);
this.flash(err, false);
return;
}
if (opts.slavesOnly) {
this.flash(`Slaves: Deadzone ${data.deadzone} LSB (${data.slaves_updated} per ESP-NOW, Master unverändert)`, true);
return;
}
const who = clientId === 0 ? 'Master' : `Slave ${clientId}`;
@@ -337,35 +369,33 @@
this.busy = false;
}
},
async setDeadzoneAll(deadzone) {
if (deadzone == null || deadzone < 0) {
this.flash('Ungültiger Deadzone-Wert', false);
return;
}
async readMasterDeadzone() {
this.busy = true;
try {
const r = await fetch('/api/deadzone', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
write: true,
deadzone: deadzone,
client_id: 0,
all_clients: true
})
});
const r = await fetch('/api/deadzone?client_id=0');
const data = await r.json();
if (!r.ok || !data.success) {
this.flash(data.error || 'Deadzone für alle Slaves fehlgeschlagen', false);
this.flash(data.error || 'Master-Deadzone lesen fehlgeschlagen', false);
return;
}
this.flash(`Alle Slaves: Deadzone ${data.deadzone} LSB (${data.slaves_updated} per ESP-NOW)`, true);
this.masterDz = data.deadzone;
this.flash(`Master: Deadzone ${data.deadzone} LSB`, true);
} catch (e) {
this.flash(String(e), false);
} finally {
this.busy = false;
}
},
async setMasterDeadzone() {
await this.setDeadzone(0, this.masterDz);
},
async setDeadzoneAll(deadzone) {
if (deadzone == null || deadzone < 0) {
this.flash('Ungültiger Deadzone-Wert', false);
return;
}
await this.setDeadzone(0, deadzone, { allClients: true, slavesOnly: true });
},
async unicastTest(clientId) {
this.busy = true;
try {