updated control functions

This commit is contained in:
2026-02-11 21:13:10 +01:00
parent 55f85f8684
commit af4355a83f
5 changed files with 232 additions and 53 deletions
BIN
View File
Binary file not shown.
+157 -36
View File
@@ -1,17 +1,26 @@
// app.js gemeinsames JavaScript
console.log("MK Control JS geladen");
// static/js/app.js MK Control Frontend
document.addEventListener('DOMContentLoaded', () => {
console.log('MK Control Frontend geladen');
const connectBtn = document.getElementById('connect-btn');
const connectSection = document.getElementById('connect-section');
const controlSection = document.getElementById('control-section');
// ── Elemente ───────────────────────────────────────────────────────────────
const connectBtn = document.getElementById('connect-btn');
const connectSection = document.getElementById('connect-section');
const controlSection = document.getElementById('control-section');
const channelsContainer = document.getElementById('channels-container');
const stopAllBtn = document.getElementById('stop-all-btn');
const stopAllBtn = document.getElementById('stop-all-btn');
const reconnectSection = document.getElementById('reconnect-section');
const reconnectBtn = document.getElementById('reconnect-btn');
if (!connectBtn) return; // Sicherstellen, dass wir auf der richtigen Seite sind
if (!connectBtn) {
console.warn('Nicht auf der Steuerseite Connect-Button nicht gefunden');
return;
}
// ── Config aus Template (von Flask eingebettet) ───────────────────────────
const config = window.config || {}; // Sicherstellen, dass config existiert
// ── Connect-Button ────────────────────────────────────────────────────────
connectBtn.addEventListener('click', async () => {
connectBtn.disabled = true;
connectBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span> Verbinde...';
@@ -27,39 +36,89 @@ document.addEventListener('DOMContentLoaded', () => {
if (result.success) {
connectSection.style.display = 'none';
controlSection.style.display = 'block';
reconnectSection.style.display = 'none';
renderChannels();
alert('Verbunden! (Simulation)');
console.log('Verbindung erfolgreich');
} else {
alert('Verbindung fehlgeschlagen: ' + (result.message || 'Unbekannter Fehler'));
alert('Verbindung fehlgeschlagen:\n' + (result.message || 'Unbekannter Fehler'));
}
} catch (err) {
console.error(err);
alert('Fehler bei der Verbindung: ' + err.message);
console.error('Connect-Fehler:', err);
alert('Netzwerk- oder Verbindungsfehler: ' + err.message);
} finally {
connectBtn.disabled = false;
connectBtn.innerHTML = '<i class="bi bi-bluetooth me-2"></i> Mit Hub verbinden';
}
});
stopAllBtn.addEventListener('click', async () => {
if (!confirm('Wirklich alle Kanäle stoppen?')) return;
// ── Alle stoppen ──────────────────────────────────────────────────────────
if (stopAllBtn) {
stopAllBtn.addEventListener('click', async () => {
if (!confirm('Wirklich ALLE Kanäle stoppen?')) return;
try {
const res = await fetch('/api/stop_all', { method: 'POST' });
const data = await res.json();
if (data.success) {
alert('Alle Kanäle gestoppt');
try {
const res = await fetch('/api/stop_all', { method: 'POST' });
const data = await res.json();
if (data.success) {
// Alle Motor-Slider zurücksetzen
document.querySelectorAll('.motor-slider').forEach(slider => {
slider.value = 0;
const display = slider.parentElement.querySelector('.value-display');
if (display) display.textContent = '0 %';
});
console.log('Alle Kanäle gestoppt');
alert('Alle Kanäle gestoppt');
} else {
alert('Fehler beim Stoppen:\n' + (data.message || 'Unbekannt'));
}
} catch (err) {
console.error('Stop-all Fehler:', err);
alert('Netzwerkfehler beim Stoppen aller Kanäle');
}
} catch (err) {
alert('Fehler beim Stoppen');
}
});
});
}
// ── Erneut verbinden ──────────────────────────────────────────────────────
if (reconnectBtn) {
reconnectBtn.addEventListener('click', async () => {
reconnectBtn.disabled = true;
reconnectBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> Verbinde...';
try {
const response = await fetch('/api/reconnect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.success) {
reconnectSection.style.display = 'none';
controlSection.style.display = 'block';
alert('Verbindung wiederhergestellt!');
// Optional: Kanäle neu rendern oder Status aktualisieren
} else {
alert('Erneute Verbindung fehlgeschlagen:\n' + (result.message || 'Unbekannt'));
}
} catch (err) {
console.error('Reconnect-Fehler:', err);
alert('Netzwerkfehler beim erneuten Verbinden');
} finally {
reconnectBtn.disabled = false;
reconnectBtn.innerHTML = '<i class="bi bi-arrow-repeat me-2"></i> Erneut verbinden';
}
});
}
// ── Kanäle dynamisch rendern ──────────────────────────────────────────────
function renderChannels() {
if (!channelsContainer) return;
channelsContainer.innerHTML = '';
if (!config.channels || config.channels.length === 0) {
channelsContainer.innerHTML = '<p class="text-center text-muted py-4">Keine Kanäle in der Konfiguration definiert.</p>';
channelsContainer.innerHTML = '<p class="text-center text-muted py-5">Keine Kanäle in der Konfiguration definiert.</p>';
return;
}
@@ -71,18 +130,26 @@ document.addEventListener('DOMContentLoaded', () => {
if (channel.type === 'motor') {
controlHTML = `
<label class="form-label">${channel.name} (${channel.port})</label>
<label class="form-label fw-bold">${channel.name} (${channel.port})</label>
<input type="range" class="form-range motor-slider"
min="-100" max="100" value="0" step="5"
data-port="${channel.port}">
<div class="text-center mt-1">
<span class="value-display">0 %</span>
<div class="d-flex justify-content-between mt-1 small">
<span>-100 %</span>
<span class="value-display fw-bold">0 %</span>
<span>+100 %</span>
</div>
<div class="text-center mt-2">
<button class="btn btn-sm btn-outline-danger stop-channel-btn"
data-port="${channel.port}">
<i class="bi bi-stop-fill me-1"></i> Stop
</button>
</div>
`;
} else {
// Licht, Sound, Fogger → Toggle-Button
// Licht, Sound, Fogger etc. → Toggle
controlHTML = `
<label class="form-label">${channel.name} (${channel.port})</label>
<label class="form-label fw-bold">${channel.name} (${channel.port})</label>
<button class="btn btn-outline-secondary w-100 toggle-btn"
data-port="${channel.port}" data-state="off">
AUS
@@ -91,7 +158,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
col.innerHTML = `
<div class="card h-100">
<div class="card h-100 shadow-sm">
<div class="card-body">
${controlHTML}
</div>
@@ -101,17 +168,49 @@ document.addEventListener('DOMContentLoaded', () => {
channelsContainer.appendChild(col);
});
// Event-Listener für Slider
// ── Event-Listener Slider (Motoren) ─────────────────────────────────────
document.querySelectorAll('.motor-slider').forEach(slider => {
const display = slider.parentElement.querySelector('.value-display');
slider.addEventListener('input', async () => {
const value = parseInt(slider.value) / 100;
display.textContent = `${slider.value} %`;
await sendControl(slider.dataset.port, value);
if (display) display.textContent = `${slider.value} %`;
try {
await sendControl(slider.dataset.port, value);
} catch (err) {
console.error('Slider-Steuerfehler:', err);
}
});
});
// Event-Listener für Toggle-Buttons
// ── Event-Listener Einzel-Stop pro Kanal ────────────────────────────────
document.querySelectorAll('.stop-channel-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const port = btn.dataset.port;
try {
await sendControl(port, 0);
// Slider zurücksetzen
const slider = document.querySelector(`input[data-port="${port}"]`);
if (slider) {
slider.value = 0;
const display = slider.parentElement.querySelector('.value-display');
if (display) display.textContent = '0 %';
}
// Feedback
btn.classList.add('btn-danger');
setTimeout(() => btn.classList.remove('btn-danger'), 600);
console.log(`Einzelstop: ${port}`);
} catch (err) {
console.error('Einzelstop-Fehler:', err);
}
});
});
// ── Event-Listener Toggle-Buttons (Licht/Sound/Fogger) ──────────────────
document.querySelectorAll('.toggle-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const current = btn.dataset.state;
@@ -121,11 +220,16 @@ document.addEventListener('DOMContentLoaded', () => {
btn.classList.toggle('btn-success', newState === 'on');
btn.classList.toggle('btn-outline-secondary', newState === 'off');
await sendControl(btn.dataset.port, newState === 'on' ? 1 : 0);
try {
await sendControl(btn.dataset.port, newState === 'on' ? 1 : 0);
} catch (err) {
console.error('Toggle-Fehler:', err);
}
});
});
}
// ── Hilfsfunktion: Steuerbefehl senden ────────────────────────────────────
async function sendControl(port, value) {
try {
const res = await fetch('/api/control', {
@@ -133,9 +237,26 @@ document.addEventListener('DOMContentLoaded', () => {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ port, value })
});
// Kann später Feedback geben
if (!res.ok) {
const errData = await res.json();
throw new Error(errData.message || 'Steuerbefehl fehlgeschlagen');
}
} catch (err) {
console.error('Steuerbefehl fehlgeschlagen:', err);
console.error('sendControl Fehler:', err);
// Optional: Hier könnte man showReconnect() aufrufen, wenn gewünscht
// showReconnect();
}
}
// ── Hilfsfunktion: Reconnect-Bereich anzeigen ─────────────────────────────
function showReconnect() {
if (reconnectSection && controlSection) {
controlSection.style.display = 'none';
reconnectSection.style.display = 'block';
}
}
// Optional: Bei Seitenlade-Fehler oder späterem Verbindungsverlust aufrufen
// showReconnect(); // ← zum Testen manuell einblenden
});