feat: Tuner Genius XL — A/B channels, plus FlexRadio panel + Station Control cards

Push the tuner control further so it mirrors the native 4O3A app and the way the
PowerGenius XL is surfaced.

Backend (internal/tunergenius):
- Status now carries both RF channels A and B (source/mode, band, frequency,
  bound Flex nickname, per-channel bypass, antenna, PTT), the active channel,
  the C1/L/C2 relay-network positions, and the 3-way-vs-SO2R hardware variant
  (learned once from the `info` reply). Flat freq/antenna still mirror the active
  channel for the compact widget.
- New TunerGeniusActivate(ch) binding → `activate ch=N` (or `ant=N` on 3-way).

Frontend:
- New shared TunerCard, styled exactly like AmpCard (PWR/SWR meter bars,
  A/B channel selector with source+freq+antenna, Tune/Bypass/Operate). Used in
  BOTH the FlexRadio panel (its own card, like the PGXL) and Station Control.
- Docked TunerGeniusPanel widget now shows the two channels A/B with their
  source/frequency/antenna and lets you click to make one active — the missing
  A/B state the user flagged.
- i18n EN/FR for the new labels (channels, antenna, in-line/bypassed, title).

Still UNTESTED on hardware — verify the per-channel field names/units and the
activate behaviour on the real box.
This commit is contained in:
2026-07-25 10:47:11 +02:00
parent 933d601c03
commit 9b677c6b35
12 changed files with 439 additions and 28 deletions
+6 -1
View File
@@ -24,7 +24,7 @@ import {
GetDBConnectionInfo, GetLogbookRevision,
GetUltrabeamStatus, SetUltrabeamDirection,
GetAntGeniusStatus, GetAntGeniusSettings, AntGeniusActivate,
GetTunerGeniusStatus, GetTunerGeniusSettings, TunerGeniusAutotune, TunerGeniusSetBypass, TunerGeniusSetOperate,
GetTunerGeniusStatus, GetTunerGeniusSettings, TunerGeniusAutotune, TunerGeniusSetBypass, TunerGeniusSetOperate, TunerGeniusActivate,
OpenExternalURL,
ConnectAllClusters, DisconnectAllClusters, GetClusterStatus, SendClusterCommand,
ListClusterServers, ClusterSpotStatuses, SendClusterSpot,
@@ -1702,6 +1702,10 @@ export default function App() {
setTgStatus((s) => ({ ...s, operate: on }));
TunerGeniusSetOperate(on).catch((e) => setError(String(e?.message ?? e)));
};
const tgActivate = (ch: number) => {
setTgStatus((s) => ({ ...s, active: ch })); // optimistic
TunerGeniusActivate(ch).catch((e) => setError(String(e?.message ?? e)));
};
// RX band auto-follows the TX band (only differs for cross-band work).
useEffect(() => { setBandRx(band); }, [band]);
@@ -4705,6 +4709,7 @@ export default function App() {
onTune={tgTune}
onBypass={tgBypass}
onOperate={tgOperate}
onActivate={tgActivate}
onClose={() => { setShowTuner(false); writeUiPref('opslog.showTuner', '0'); }}
/>
</div>