From f771100a4cf6612559221a8a2dd6af6cfa1ff455 Mon Sep 17 00:00:00 2001 From: thatsatechnique <28403172+thatsatechnique@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:51:39 -0800 Subject: [PATCH] fix(ook): fix output panel layout, persist frames, wire global status bar - Fix double-scroll by switching ookOutputPanel to flex layout - Keep decoded frames visible after stopping (persist for review) - Wire global Clear/CSV/JSON status bar buttons to OOK functions - Hide default output pane in OOK mode (uses own panel) - Add command display showing the active rtl_433 command - Add JSON export and auto-scroll support - Fix 0x prefix stripping in OOK hex decoder - Fix PWM encoding hint text Co-Authored-By: Claude Opus 4.6 --- .gitignore | 3 ++ static/js/modes/ook.js | 63 +++++++++++++++++++++++++++++-- templates/index.html | 20 +++++----- templates/partials/modes/ook.html | 10 ++++- utils/ook.py | 6 ++- 5 files changed, 86 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 4c6d018..5c66355 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,6 @@ data/subghz/captures/ .env .env.* !.env.example + +# Local utility scripts +reset-sdr.* diff --git a/static/js/modes/ook.js b/static/js/modes/ook.js index 45a6da8..2c3c339 100644 --- a/static/js/modes/ook.js +++ b/static/js/modes/ook.js @@ -17,6 +17,7 @@ var OokMode = (function () { frameCount: 0, bitOrder: 'msb', // 'msb' | 'lsb' filterQuery: '', // active hex/ascii filter + command: '', // the rtl_433 command being run }; // ---- Initialization ---- @@ -95,6 +96,7 @@ var OokMode = (function () { updateUI(true); connectSSE(); clearOutput(); + showCommand(data.command || ''); } else { alert('Error: ' + (data.message || 'Unknown error')); } @@ -248,7 +250,9 @@ var OokMode = (function () { } panel.appendChild(div); - panel.scrollTop = panel.scrollHeight; + if (typeof autoScroll === 'undefined' || autoScroll) { + panel.scrollTop = panel.scrollHeight; + } } // ---- Bit order toggle ---- @@ -284,6 +288,12 @@ var OokMode = (function () { if (countEl) countEl.textContent = '0 frames'; var barEl = document.getElementById('ookStatusBarFrames'); if (barEl) barEl.textContent = '0 frames'; + + // Hide output panel if not currently running (no frames to show) + if (!state.running) { + var outputPanel = document.getElementById('ookOutputPanel'); + if (outputPanel) outputPanel.style.display = 'none'; + } } function exportLog() { @@ -308,6 +318,47 @@ var OokMode = (function () { URL.revokeObjectURL(url); } + function exportJSON() { + if (state.frames.length === 0) { alert('No frames to export'); return; } + var out = state.frames.map(function (msg) { + var interp = interpretBits(msg.bits, state.bitOrder); + return { + timestamp: msg.timestamp, + bit_count: msg.bit_count, + rssi: msg.rssi || null, + hex: interp.hex, + ascii: interp.ascii, + inverted: msg.inverted, + bits: msg.bits, + }; + }); + var blob = new Blob([JSON.stringify(out, null, 2)], { type: 'application/json' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = 'ook_frames.json'; + a.click(); + URL.revokeObjectURL(url); + } + + // ---- Command display ---- + + function showCommand(cmd) { + state.command = cmd; + var display = document.getElementById('ookCommandDisplay'); + var text = document.getElementById('ookCommandText'); + if (display && text && cmd) { + text.textContent = cmd; + display.style.display = 'block'; + } + } + + function copyCommand() { + if (state.command && navigator.clipboard) { + navigator.clipboard.writeText(state.command); + } + } + // ---- Modulation selector ---- function setEncoding(enc) { @@ -328,7 +379,7 @@ var OokMode = (function () { // Update timing hint var hints = { - pwm: 'Short pulse = 0, long pulse = 1. Most common for ISM OOK.', + pwm: 'Short pulse = 1, long pulse = 0. Most common for ISM OOK.', ppm: 'Short gap = 0, long gap = 1. Pulse position encoding.', manchester: 'Rising edge = 1, falling edge = 0. Self-clocking.', }; @@ -428,8 +479,12 @@ var OokMode = (function () { if (indicator) indicator.style.background = running ? '#00ff88' : 'var(--text-dim)'; if (statusText) statusText.textContent = running ? 'Listening' : 'Standby'; + // Keep output panel visible if there are frames to review (even after stopping) var outputPanel = document.getElementById('ookOutputPanel'); - if (outputPanel) outputPanel.style.display = running ? 'block' : 'none'; + if (outputPanel) { + var showPanel = running || state.frames.length > 0; + outputPanel.style.display = showPanel ? 'flex' : 'none'; + } } // ---- Public API ---- @@ -447,5 +502,7 @@ var OokMode = (function () { filterFrames: filterFrames, clearOutput: clearOutput, exportLog: exportLog, + exportJSON: exportJSON, + copyCommand: copyCommand, }; })(); diff --git a/templates/index.html b/templates/index.html index 467c4ed..cfbd143 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3292,11 +3292,11 @@ - @@ -140,6 +140,14 @@ Uses rtl_433 with a custom flex decoder. Requires rtl_433 installed. Works on any OOK/ASK signal in the SDR's frequency range.

+ diff --git a/utils/ook.py b/utils/ook.py index 8374562..f82cdd5 100644 --- a/utils/ook.py +++ b/utils/ook.py @@ -45,7 +45,11 @@ def decode_ook_frame(hex_data: str) -> dict[str, Any] | None: ``byte_count``, and ``bit_count``, or ``None`` on parse failure. """ try: - raw = bytes.fromhex(hex_data.replace(' ', '')) + cleaned = hex_data.replace(' ', '') + # rtl_433 flex decoder prefixes hex with '0x' — strip it + if cleaned.startswith(('0x', '0X')): + cleaned = cleaned[2:] + raw = bytes.fromhex(cleaned) except ValueError: return None