update for soundboard with global soundfiles

This commit is contained in:
oberon 2026-02-15 21:35:45 +01:00
parent fc08da804f
commit 0eacdbf973
4 changed files with 191 additions and 59 deletions

21
app.py
View File

@ -35,6 +35,19 @@ def cleanup_old_log_dirs(max_age_days=90):
except Exception as e:
logger.error(f"Fehler beim Löschen von {subdir_path}: {e}")
# ── Hilfsfunktion - globale Sounds laden ────────────────────────────────────────────────────────────────
def load_global_sounds():
global_sounds_path = os.path.join(app.root_path, 'sounds.json') # oder configs/sounds.json
if os.path.exists(global_sounds_path):
try:
with open(global_sounds_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data.get('global_sounds', [])
except Exception as e:
logger.error(f"Fehler beim Laden globaler Sounds: {e}")
return []
return []
# ── Bluetooth ────────────────────────────────────────────────────────────────
from mkconnect.mouldking.MouldKing import MouldKing
from mkconnect.tracer.TracerConsole import TracerConsole
@ -216,10 +229,16 @@ def control_page():
logger.warning("current_config ist None → Redirect zu index")
return redirect(url_for('index'))
global_sounds = load_global_sounds()
logger.info(f"Übergebe config an Template: {current_config}")
print("DEBUG: config hat channels?", 'channels' in current_config, len(current_config.get('channels', [])))
return render_template('control.html', config=current_config)
return render_template(
'control.html',
config=current_config,
global_sounds=global_sounds
)
@app.route('/soundboard')

22
configs/sounds.json Normal file
View File

@ -0,0 +1,22 @@
{
"global_sounds": [
{
"id": "pfeife_lang",
"file": "pfeife_lang.wav",
"name": "Pfeife lang",
"description": "Langgezogene Dampflok-Pfeife"
},
{
"id": "dampf_ausstoß",
"file": "dampf_ausstoß.mp3",
"name": "Dampfausstoß",
"description": "Kurzes Zischen"
},
{
"id": "bahnhof_ankunft",
"file": "bahnhof_ankunft.mp3",
"name": "Bahnhofsankunft",
"description": "Stationsansage + Bremsquietschen"
}
]
}

View File

@ -307,6 +307,47 @@ document.addEventListener('DOMContentLoaded', () => {
}
}
// ── Soundboard Buttons ─────────────────────────────────────────────────────
document.querySelectorAll('.play-sound-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const soundId = btn.dataset.soundId;
btn.disabled = true;
const originalText = btn.innerHTML;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> Spielt...';
try {
const res = await fetch('/api/play_sound', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sound_id: soundId })
});
const data = await res.json();
if (data.success) {
btn.classList.remove('btn-outline-primary', 'btn-outline-secondary');
btn.classList.add('btn-success');
btn.innerHTML = '<i class="bi bi-check-lg me-2"></i> Gespielt';
setTimeout(() => {
btn.classList.remove('btn-success');
btn.classList.add('btn-outline-primary'); // oder secondary
btn.innerHTML = originalText;
btn.disabled = false;
}, 2000);
} else {
alert('Fehler: ' + (data.message || 'Unbekannt'));
btn.disabled = false;
btn.innerHTML = originalText;
}
} catch (err) {
console.error('Sound-Play-Fehler:', err);
alert('Netzwerkfehler beim Abspielen');
btn.disabled = false;
btn.innerHTML = originalText;
}
});
});
// Initialer Status
updateStatus(false);

View File

@ -6,6 +6,16 @@
<div class="container my-4">
<!-- Status-Anzeige (oben rechts fixiert) -->
<div class="position-fixed top-0 end-0 m-3" style="z-index: 1050;">
<div id="connection-status" class="badge bg-secondary px-3 py-2 fs-6">
<i class="bi bi-circle-fill me-2 text-muted"></i> Nicht verbunden
</div>
</div>
<div class="row">
<!-- Linke Spalte: Kanal-Steuerung (8/12 auf lg) -->
<div class="col-lg-8">
<div class="row mb-4 align-items-center">
<div class="col-md-8">
<h1 class="mb-1">{{ config.name }}</h1>
@ -30,15 +40,8 @@
</div>
</div>
<!-- Status-Anzeige -->
<div class="position-fixed top-0 end-0 m-3" style="z-index: 1050;">
<div id="connection-status" class="badge bg-secondary px-3 py-2 fs-6">
<i class="bi bi-circle-fill me-2 text-muted"></i> Nicht verbunden
</div>
</div>
<!-- Connect-Button -->
<div class="text-center mb-5" id="connect-section">
<!-- Connect-Bereich -->
<div id="connect-section" class="text-center mb-5">
<button id="connect-btn" class="btn btn-lg btn-success px-5 py-3">
<i class="bi bi-bluetooth me-2"></i> Mit Hub verbinden
</button>
@ -61,8 +64,9 @@
</div>
</div>
</div>
<!-- Reconnect-Bereich wird nur angezeigt, wenn Verbindung verloren -->
<div class="text-center mt-5" id="reconnect-section" style="display: none;">
<!-- Reconnect-Bereich wird nur bei Verbindungsverlust eingeblendet -->
<div id="reconnect-section" class="text-center mt-5" style="display: none;">
<div class="alert alert-warning">
<strong>Verbindung unterbrochen</strong><br>
Der Hub reagiert nicht mehr. Bitte erneut verbinden.
@ -71,18 +75,64 @@
<i class="bi bi-arrow-repeat me-2"></i> Erneut verbinden
</button>
</div>
</div>
<!-- Rechte Spalte: Soundboard (4/12 auf lg) -->
<div class="col-lg-4">
<div class="card shadow sticky-top" style="top: 20px;">
<div class="card-header bg-info text-white">
<h5 class="mb-0">Soundboard</h5>
</div>
<div class="card-body" style="max-height: 70vh; overflow-y: auto;">
<!-- Lok-spezifische Sounds -->
{% if config.sounds and config.sounds|length > 0 %}
<h6 class="mt-0 mb-3">Lok-spezifisch</h6>
<div class="d-grid gap-2 mb-4">
{% for sound in config.sounds %}
<button class="btn btn-outline-primary play-sound-btn"
data-sound-id="{{ sound.id }}">
{{ sound.name }}
{% if sound.description %}
<small class="d-block text-muted">{{ sound.description }}</small>
{% endif %}
</button>
{% endfor %}
</div>
{% endif %}
<!-- Globale / Standard-Sounds -->
{% if global_sounds and global_sounds|length > 0 %}
<h6 class="mt-4 mb-3">Standard-Sounds</h6>
<div class="d-grid gap-2">
{% for sound in global_sounds %}
<button class="btn btn-outline-secondary play-sound-btn"
data-sound-id="{{ sound.id }}">
{{ sound.name }}
{% if sound.description %}
<small class="d-block text-muted">{{ sound.description }}</small>
{% endif %}
</button>
{% endfor %}
</div>
{% endif %}
{% if (not config.sounds or config.sounds|length == 0) and (not global_sounds or global_sounds|length == 0) %}
<p class="text-muted text-center py-4">Keine Sounds konfiguriert</p>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
// config global verfügbar machen
window.mkConfig = {{ config | tojson | safe }};
console.log("window.mkConfig gesetzt:", window.mkConfig);
console.log("Channels:", window.mkConfig?.channels);
// Config + globale Sounds direkt ins JS übergeben
const config = {{ config | tojson | safe }};
const globalSounds = {{ global_sounds | tojson | safe }};
window.mkConfig = config; // falls du es global brauchst
</script>
{% endblock %}