diff --git a/frontend/src/components/FlexPanel.tsx b/frontend/src/components/FlexPanel.tsx index 7ea3830..fc90194 100644 --- a/frontend/src/components/FlexPanel.tsx +++ b/frontend/src/components/FlexPanel.tsx @@ -4,7 +4,7 @@ import { GetFlexState, FlexSetPower, FlexSetTunePower, FlexTune, FlexSetVox, FlexSetVoxLevel, FlexSetVoxDelay, FlexSetProcessor, FlexSetProcessorLevel, FlexSetMon, FlexSetMonLevel, FlexSetMic, FlexMox, FlexAmpOperate, - GetPGXLStatus, PGXLSetFanMode, + GetPGXLStatus, PGXLSetFanMode, GetPGXLSettings, GetSPEStatus, SPESetOperate, FlexSetAGCMode, FlexSetAGCThreshold, FlexSetAudioLevel, FlexSetMute, FlexSetRXAntenna, FlexSetTXAntenna, FlexSetSplit, FlexSetActiveSlice, FlexSetTXSlice, FlexSetRIT, FlexSetRITFreq, FlexSetXIT, FlexSetXITFreq, FlexSetNB, FlexSetNBLevel, FlexSetNR, FlexSetNRLevel, FlexSetANF, FlexSetANFLevel, @@ -310,6 +310,29 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number return () => { alive = false; window.clearInterval(id); }; }, []); + // Configured amplifier type (pgxl / spe*), so we show the SPE control when the + // operator runs an SPE Expert instead of the PowerGenius. + const [ampType, setAmpType] = useState('pgxl'); + const [ampEnabled, setAmpEnabled] = useState(false); + useEffect(() => { + let alive = true; + const load = () => GetPGXLSettings().then((s: any) => { if (alive) { setAmpType(s?.type || 'pgxl'); setAmpEnabled(!!s?.enabled); } }).catch(() => {}); + load(); + const id = window.setInterval(load, 5000); + return () => { alive = false; window.clearInterval(id); }; + }, []); + const isSPE = ampEnabled && ampType !== 'pgxl'; + // SPE Expert live status (only polled when an SPE amp is configured). + const [spe, setSpe] = useState({ connected: false }); + useEffect(() => { + if (!isSPE) return; + let alive = true; + const tick = () => GetSPEStatus().then((s: any) => alive && setSpe(s || { connected: false })).catch(() => {}); + tick(); + const id = window.setInterval(tick, 1500); + return () => { alive = false; window.clearInterval(id); }; + }, [isSPE]); + const change = (key: keyof FlexState, val: number | boolean | string, send: () => Promise) => { hold.current[key] = Date.now() + 900; setSt((p) => ({ ...p, [key]: val })); @@ -760,6 +783,35 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number + {/* SPE Expert amplifier (serial/TCP) — shown when it's the configured amp. + The Flex doesn't report SPE amps, so this card is driven by OpsLog's own + SPE link rather than the Flex amplifier object. */} + {isSPE && ( + +
+ + + + {spe.connected ? (spe.tx ? 'TX' : 'RX') : t('flxp.pgOffline')} + + {spe.connected && ( + + {spe.output_w}W · SWR {Number(spe.swr_ant ?? 0).toFixed(1)} · {spe.temp_c}°C · {spe.power_level} + + )} +
+ {(spe.warnings || spe.alarms) && ( + ⚠ {spe.warnings} {spe.alarms} + )} +
+ + )} + {/* External amplifier (PowerGenius XL) — only when detected. */} {st.amp_available && ( @@ -776,7 +828,7 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number {/* Fan mode — shown when the PowerGenius is configured (Settings → PowerGenius). The dot shows the direct-connection state; the selector is disabled until connected (hover it for the error). */} - {(pg.host || pg.connected) && ( + {ampType === 'pgxl' && (pg.host || pg.connected) && (