diff --git a/src/components/about/AboutTab.tsx b/src/components/about/AboutTab.tsx
index 6377ea8..08e734b 100644
--- a/src/components/about/AboutTab.tsx
+++ b/src/components/about/AboutTab.tsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { useDebugStore } from '../../store/debugStore';
+import { useOutOfBandStore } from '../../store/outOfBandStore';
import { Card } from '../ui/Card';
import { SectionTitle } from '../ui/SectionTitle';
import { Button } from '../ui/Button';
@@ -15,6 +16,7 @@ const OFFLINE_VERSION_URL = 'https://infamy.github.io/NeonPlug/';
export const AboutTab: React.FC = () => {
const { debugMode, setDebugMode } = useDebugStore();
+ const { allowOutOfBandFrequencies, setAllowOutOfBandFrequencies } = useOutOfBandStore();
const [offlineFallbackOpen, setOfflineFallbackOpen] = useState(false);
return (
@@ -299,6 +301,35 @@ npm run build:single
)}
+
+ {/* Out of Band Frequencies */}
+
+
Out of Band Frequencies
+
+
+ Enable this if your radio has modified firmware that supports frequencies outside the
+ standard VHF (87–174 MHz) and UHF (400–470 MHz) ranges. When enabled, out-of-band
+ channels will be read, displayed, and written without filtering. This setting is saved
+ with your codeplug.
+
+
+ {allowOutOfBandFrequencies && (
+
+ Out of band frequencies are enabled. No frequency filtering will be applied on read or write.
+
+ )}
+
+
= ({
const effectiveModel = useEffectiveRadioModel();
const { settings: radioSettings, updateSettings } = useRadioSettingsStore();
const caps = getCapabilitiesForModel(effectiveModel);
- const bandLimits = caps?.bandLimits ?? null;
+ const { allowOutOfBandFrequencies } = useOutOfBandStore();
+ const bandLimits = allowOutOfBandFrequencies ? null : (caps?.bandLimits ?? null);
const maxChannels = caps?.maxChannels ?? 4000;
const analogOnly = caps?.analogOnly === true;
const { scanLists } = useScanListsStore();
diff --git a/src/components/layout/Toolbar.tsx b/src/components/layout/Toolbar.tsx
index bb1396b..ee2f576 100644
--- a/src/components/layout/Toolbar.tsx
+++ b/src/components/layout/Toolbar.tsx
@@ -21,6 +21,7 @@ import { migrateCodeplug, type MigrationLoss } from '../../services/codeplugMigr
import { saveSnapshot, getSnapshots, getSnapshotData, clearSnapshots, type SnapshotEventType } from '../../services/codeplugSnapshots';
// Codeplug export/import are lazy loaded when needed
import { useRadioConnection } from '../../hooks/useRadioConnection';
+import { useOutOfBandStore } from '../../store/outOfBandStore';
import { ReadProgressModal } from '../ui/ReadProgressModal';
import { ConfirmModal } from '../ui/ConfirmModal';
import { isWebSerialSupported } from '../../utils/browserSupport';
@@ -108,6 +109,7 @@ export const Toolbar: React.FC = () => {
encryptionKeys,
exportDate: new Date().toISOString(),
version: '1.0.0',
+ allowOutOfBandFrequencies: useOutOfBandStore.getState().allowOutOfBandFrequencies || undefined,
});
const buildCodeplugDataFromStores = () => {
@@ -141,6 +143,7 @@ export const Toolbar: React.FC = () => {
encryptionKeys: eks.keys,
exportDate: new Date().toISOString(),
version: '1.0.0',
+ allowOutOfBandFrequencies: useOutOfBandStore.getState().allowOutOfBandFrequencies || undefined,
};
};
@@ -226,6 +229,9 @@ export const Toolbar: React.FC = () => {
setQuickContacts(codeplugData.quickContacts ?? []);
setRXGroups(codeplugData.rxGroups ?? []);
setEncryptionKeys(codeplugData.encryptionKeys ?? []);
+ if (codeplugData.allowOutOfBandFrequencies) {
+ useOutOfBandStore.getState().setAllowOutOfBandFrequencies(true);
+ }
const digCount = codeplugData.digitalEmergencies?.length ?? 0;
const analogCount = codeplugData.analogEmergencies?.length ?? 0;
diff --git a/src/hooks/useRadioConnection.ts b/src/hooks/useRadioConnection.ts
index 4963e71..8d916a5 100644
--- a/src/hooks/useRadioConnection.ts
+++ b/src/hooks/useRadioConnection.ts
@@ -21,6 +21,7 @@ import type { Channel } from '../models/Channel';
import type { Zone } from '../models/Zone';
import type { ScanList } from '../models/ScanList';
import { isValidChannelFrequency } from '../services/validation/frequencyValidator';
+import { useOutOfBandStore } from '../store/outOfBandStore';
import { parseBootImageHeader } from '../utils/bootImage';
/** Augment error message when tab was hidden during a serial operation (better reporting). */
@@ -167,6 +168,18 @@ export function useRadioConnection() {
onProgress?.(20, 'Parsing channels...', steps[4]);
const channels = await protocol.readChannels();
setChannels(channels);
+
+ // Auto-enable OOB flag if the radio has channels outside standard band limits
+ {
+ const { allowOutOfBandFrequencies, setAllowOutOfBandFrequencies } = useOutOfBandStore.getState();
+ if (!allowOutOfBandFrequencies) {
+ const readBandLimits = getCapabilitiesForModel(effectiveModel)?.bandLimits;
+ if (readBandLimits && channels.some(ch => !isValidChannelFrequency(ch, readBandLimits))) {
+ setAllowOutOfBandFrequencies(true);
+ }
+ }
+ }
+
// Enrich radioInfo with firmware from cached image (UV5R-Mini; getRadioInfo may have missed it)
if (typeof (protocol as any).getFirmwareFromCache === 'function') {
const fw = (protocol as any).getFirmwareFromCache();
@@ -779,10 +792,13 @@ export function useRadioConnection() {
try {
// Filter channels to only include those with valid frequencies (use effective model for capabilities)
const effectiveModel = radioInfo?.model ?? selectedRadioModel ?? null;
- const bandLimits = getCapabilitiesForModel(effectiveModel)?.bandLimits;
- const validChannels = channels.filter(ch => isValidChannelFrequency(ch, bandLimits));
+ const { allowOutOfBandFrequencies: writeAllowOob } = useOutOfBandStore.getState();
+ const bandLimits = writeAllowOob ? null : getCapabilitiesForModel(effectiveModel)?.bandLimits;
+ const validChannels = bandLimits
+ ? channels.filter(ch => isValidChannelFrequency(ch, bandLimits))
+ : channels;
const filteredCount = channels.length - validChannels.length;
-
+
if (filteredCount > 0) {
console.warn(`Filtered out ${filteredCount} channel(s) with frequencies outside supported ranges`);
}
diff --git a/src/services/codeplugExport.ts b/src/services/codeplugExport.ts
index c98378e..c5acb23 100644
--- a/src/services/codeplugExport.ts
+++ b/src/services/codeplugExport.ts
@@ -36,6 +36,7 @@ export interface CodeplugData {
encryptionKeys: EncryptionKey[];
exportDate: string;
version: string;
+ allowOutOfBandFrequencies?: boolean;
}
const CODEPLUG_VERSION = '1.0.0';
diff --git a/src/store/outOfBandStore.ts b/src/store/outOfBandStore.ts
new file mode 100644
index 0000000..7111c71
--- /dev/null
+++ b/src/store/outOfBandStore.ts
@@ -0,0 +1,28 @@
+import { create } from 'zustand';
+
+interface OutOfBandState {
+ allowOutOfBandFrequencies: boolean;
+ setAllowOutOfBandFrequencies: (v: boolean) => void;
+}
+
+const loadAllowOob = (): boolean => {
+ try {
+ return localStorage.getItem('neonplug-allow-oob') === 'true';
+ } catch {
+ return false;
+ }
+};
+
+const saveAllowOob = (v: boolean): void => {
+ try {
+ localStorage.setItem('neonplug-allow-oob', v ? 'true' : 'false');
+ } catch {}
+};
+
+export const useOutOfBandStore = create((set) => ({
+ allowOutOfBandFrequencies: loadAllowOob(),
+ setAllowOutOfBandFrequencies: (v) => {
+ saveAllowOob(v);
+ set({ allowOutOfBandFrequencies: v });
+ },
+}));