From 19a94d4a84e3c98533afd12c3f293b8d5cd2cb61 Mon Sep 17 00:00:00 2001 From: Smittix Date: Sun, 8 Feb 2026 14:25:36 +0000 Subject: [PATCH] Move waterfall controls to function bar and fix SDR claim race on tune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move waterfall controls from the sidebar into a function-strip bar inside #listeningPostVisuals so they sit directly above the waterfall canvas. Also fix the "SDR device in use" error when clicking a waterfall frequency to listen — the WebSocket waterfall's device claim wasn't being released before the audio start request because the backend cleanup hadn't finished. Co-Authored-By: Claude Opus 4.6 --- routes/listening_post.py | 11 ++- static/js/modes/listening-post.js | 10 ++- templates/index.html | 114 ++++++++++++++++-------------- 3 files changed, 80 insertions(+), 55 deletions(-) diff --git a/routes/listening_post.py b/routes/listening_post.py index 5775641..8c48402 100644 --- a/routes/listening_post.py +++ b/routes/listening_post.py @@ -1305,11 +1305,20 @@ def start_audio() -> Response: scanner_config['device'] = device scanner_config['sdr_type'] = sdr_type - # Stop waterfall if it's using the same SDR + # Stop waterfall if it's using the same SDR (SSE path) if waterfall_running and waterfall_active_device == device: _stop_waterfall_internal() time.sleep(0.2) + # Release waterfall device claim if the WebSocket waterfall is still + # holding it. The JS client sends a stop command and closes the + # WebSocket before requesting audio, but the backend handler may not + # have finished its cleanup yet. + device_status = app_module.get_sdr_device_status() + if device_status.get(device) == 'waterfall': + app_module.release_sdr_device(device) + time.sleep(0.3) + # Claim device for listening audio if listening_active_device is None or listening_active_device != device: if listening_active_device is not None: diff --git a/static/js/modes/listening-post.js b/static/js/modes/listening-post.js index c7f6f92..2987e39 100644 --- a/static/js/modes/listening-post.js +++ b/static/js/modes/listening-post.js @@ -3172,8 +3172,12 @@ function setWaterfallControlButtons(running) { const startBtn = document.getElementById('startWaterfallBtn'); const stopBtn = document.getElementById('stopWaterfallBtn'); if (!startBtn || !stopBtn) return; - startBtn.style.display = running ? 'none' : 'block'; - stopBtn.style.display = running ? 'block' : 'none'; + startBtn.style.display = running ? 'none' : 'inline-block'; + stopBtn.style.display = running ? 'inline-block' : 'none'; + const dot = document.getElementById('waterfallStripDot'); + if (dot) { + dot.className = running ? 'status-dot sweeping' : 'status-dot inactive'; + } } function getWaterfallRangeFromInputs() { @@ -3925,6 +3929,8 @@ async function stopWaterfall() { if (typeof releaseDevice === 'function') { releaseDevice('waterfall'); } + // Allow backend WebSocket handler to finish cleanup and release SDR + await new Promise(resolve => setTimeout(resolve, 300)); return; } diff --git a/templates/index.html b/templates/index.html index 526df0c..a8814e2 100644 --- a/templates/index.html +++ b/templates/index.html @@ -505,42 +505,6 @@ - - - {% include 'partials/modes/pager.html' %} {% include 'partials/modes/sensor.html' %} @@ -607,16 +571,6 @@ - - -