diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index d0c59d7..59af6cb 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -3119,6 +3119,10 @@ async function applyModeFromPicker() { modeEl.disabled = true; showHint("Setting mode…"); try { + if (typeof vchanInterceptMode === "function" && await vchanInterceptMode(mode)) { + showHint("Channel mode set", 1500); + return; + } await postPath(`/set_mode?mode=${encodeURIComponent(mode)}`); showHint("Mode set", 1500); if (mode.toUpperCase() === "WFM") { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js index 0d3fa8b..21eb76f 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js @@ -277,7 +277,11 @@ async function bmDelete(id) { async function bmApply(bm) { try { - await postPath("/set_mode?mode=" + encodeURIComponent(bm.mode)); + const onVirtual = typeof vchanInterceptMode === "function" + && await vchanInterceptMode(bm.mode); + if (!onVirtual) { + await postPath("/set_mode?mode=" + encodeURIComponent(bm.mode)); + } if (typeof modeEl !== "undefined" && modeEl) { modeEl.value = String(bm.mode || "").toUpperCase(); } @@ -290,12 +294,14 @@ async function bmApply(bm) { syncBandwidthInput(bm.bandwidth_hz); } } + // setRigFrequency is wrapped by vchan.js to redirect to the channel API + // when on a virtual channel, so this call works correctly in both cases. if (typeof setRigFrequency === "function") { await setRigFrequency(bm.freq_hz); } else { await postPath("/set_freq?hz=" + bm.freq_hz); } - // Toggle decoders when in DIG mode + // Toggle decoders when in DIG mode. if (bm.mode === "DIG" && Array.isArray(bm.decoders)) { const statusResp = await fetch("/status"); if (statusResp.ok) { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js index 3c1cd06..1ba6dc8 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js @@ -168,3 +168,70 @@ function vchanApplyCapabilities(caps) { if (!row) return; row.style.display = (caps && caps.filter_controls) ? "" : "none"; } + +// --------------------------------------------------------------------------- +// Freq / mode interception +// --------------------------------------------------------------------------- + +// Returns true when the active channel is a non-primary (virtual) channel. +function vchanIsOnVirtual() { + if (!vchanActiveId || vchanChannels.length === 0) return false; + return vchanActiveId !== vchanChannels[0].id; +} + +async function vchanSetChannelFreq(freqHz) { + if (!vchanRigId || !vchanActiveId) return; + try { + const resp = await fetch( + `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(vchanActiveId)}/freq`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ freq_hz: Math.round(freqHz) }), + } + ); + if (!resp.ok) console.warn("vchan: set freq failed", resp.status); + } catch (e) { + console.error("vchan: set freq error", e); + } +} + +async function vchanSetChannelMode(mode) { + if (!vchanRigId || !vchanActiveId) return; + try { + const resp = await fetch( + `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(vchanActiveId)}/mode`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ mode }), + } + ); + if (!resp.ok) console.warn("vchan: set mode failed", resp.status); + } catch (e) { + console.error("vchan: set mode error", e); + } +} + +// Called by app.js (applyModeFromPicker) and bookmarks.js (bmApply) before +// sending /set_mode to the server. Returns true if the change was handled +// by the virtual channel (caller should skip the server request). +window.vchanInterceptMode = async function(mode) { + if (!vchanIsOnVirtual()) return false; + await vchanSetChannelMode(mode); + return true; +}; + +// Wrap setRigFrequency (defined in app.js, loaded before this file) so that +// frequency changes are redirected to the active virtual channel instead of +// the server when on a non-primary channel. +(function() { + const _orig = window.setRigFrequency; + window.setRigFrequency = async function(freqHz) { + if (vchanIsOnVirtual()) { + await vchanSetChannelFreq(freqHz); + return; + } + if (typeof _orig === "function") return _orig(freqHz); + }; +})();