feat(icom): offer the bands the connected radio actually has
The band row was hardwired to 160-6 m, so an IC-9700 - the one radio in the range with no HF at all - showed ten dead buttons and none of 2 m, 70 cm or 23 cm. bandsFor(model) follows the model, exactly as attOptions already did for the attenuator steps. bandOfHz's labels had to move with it: it returned '70' where the button says '70cm', so the current-band highlight could never have matched, and it knew nothing of 23 cm.
This commit is contained in:
@@ -47,12 +47,33 @@ const ZERO: IcomState = {
|
||||
|
||||
// Band buttons jump the VFO to a sensible default frequency (SSB/CW mix) using
|
||||
// the plain SetFrequency command — no band-stacking codes needed. Hz values.
|
||||
const BANDS: { l: string; hz: number }[] = [
|
||||
type Band = { l: string; hz: number };
|
||||
|
||||
const HF_BANDS: Band[] = [
|
||||
{ l: '160', hz: 1_840_000 }, { l: '80', hz: 3_750_000 }, { l: '40', hz: 7_100_000 },
|
||||
{ l: '30', hz: 10_130_000 }, { l: '20', hz: 14_150_000 }, { l: '17', hz: 18_130_000 },
|
||||
{ l: '15', hz: 21_250_000 }, { l: '12', hz: 24_950_000 }, { l: '10', hz: 28_400_000 },
|
||||
{ l: '6', hz: 50_150_000 },
|
||||
];
|
||||
const B6 = { l: '6', hz: 50_150_000 };
|
||||
const B2 = { l: '2', hz: 144_300_000 }; // SSB calling
|
||||
const B70 = { l: '70cm', hz: 432_200_000 }; // SSB calling
|
||||
const B23 = { l: '23cm', hz: 1_296_200_000 }; // SSB calling
|
||||
|
||||
// Band buttons jump the VFO to a sensible default frequency (SSB/CW mix) using
|
||||
// the plain SetFrequency command — no band-stacking codes needed.
|
||||
//
|
||||
// Which buttons to OFFER depends on the radio, exactly as the attenuator steps
|
||||
// do below. An IC-9700 has no HF at all, yet the console was showing it 160
|
||||
// through 6 and none of the bands it actually covers: ten dead buttons, and no
|
||||
// way to change band from here on the only rig where you would want to.
|
||||
function bandsFor(model?: string): Band[] {
|
||||
const m = (model ?? '').toUpperCase();
|
||||
if (m.includes('9700')) return [B2, B70, B23]; // VHF/UHF/SHF only
|
||||
if (m.includes('705')) return [...HF_BANDS, B6, B2, B70];
|
||||
if (m.includes('9100')) return [...HF_BANDS, B6, B2, B70, B23];
|
||||
// 7300 / 7610 / 7100 / unknown: HF + 6 m, the historical list.
|
||||
return [...HF_BANDS, B6];
|
||||
}
|
||||
|
||||
// Mode buttons for the console (like RS-BA1's row). SetCATMode picks USB/LSB for
|
||||
// SSB by frequency and the rig's data variant for digital modes.
|
||||
@@ -85,7 +106,10 @@ function bandOfHz(hz?: number): string {
|
||||
['160', 1.8, 2.0], ['80', 3.5, 4.0], ['60', 5.25, 5.45], ['40', 7.0, 7.3],
|
||||
['30', 10.1, 10.15], ['20', 14.0, 14.35], ['17', 18.068, 18.168],
|
||||
['15', 21.0, 21.45], ['12', 24.89, 24.99], ['10', 28.0, 29.7],
|
||||
['6', 50.0, 54.0], ['4', 70.0, 70.5], ['2', 144.0, 148.0], ['70', 430.0, 450.0],
|
||||
// Labels must match the band buttons' exactly — this is only used to light
|
||||
// the button for the band the rig is on, and '70' never matched '70cm'.
|
||||
['6', 50.0, 54.0], ['4', 70.0, 70.5], ['2', 144.0, 148.0],
|
||||
['70cm', 430.0, 450.0], ['23cm', 1240.0, 1300.0],
|
||||
];
|
||||
for (const [name, lo, hi] of bands) if (mhz >= lo && mhz <= hi) return name;
|
||||
return '';
|
||||
@@ -749,7 +773,7 @@ export function IcomPanel({ onReportRST, isNetwork = false }: { onReportRST?: (r
|
||||
{/* Band buttons + antenna selection. */}
|
||||
<Card icon={Antenna} title={t('icmp.bandsAntenna')} accent="#0891b2">
|
||||
<div className="grid grid-cols-5 gap-1.5">
|
||||
{BANDS.map((b) => {
|
||||
{bandsFor(st.model).map((b) => {
|
||||
const here = bandOfHz(mainHz) === b.l;
|
||||
return (
|
||||
<button key={b.l} type="button" onClick={() => SetCATFrequency(b.hz).catch(() => {})}
|
||||
|
||||
@@ -61,6 +61,7 @@ import { EventsOn } from '../../wailsjs/runtime/runtime';
|
||||
import {
|
||||
Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
|
||||
} from '@/components/ui/dialog';
|
||||
import { WebPublishPanel } from '@/components/WebPublishPanel';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
@@ -182,6 +183,7 @@ type SectionId =
|
||||
| 'external-services'
|
||||
| 'udp'
|
||||
| 'adifmon'
|
||||
| 'webpublish'
|
||||
| 'lookup'
|
||||
| 'lists-bands'
|
||||
| 'lists-modes'
|
||||
@@ -272,6 +274,7 @@ function buildTree(flexAvailable: boolean, t: (k: string) => string): TreeNode[]
|
||||
{ kind: 'item', label: t('sec.cluster'), id: 'cluster' },
|
||||
{ kind: 'item', label: t('sec.udp'), id: 'udp' },
|
||||
{ kind: 'item', label: t('sec.adifmon'), id: 'adifmon' },
|
||||
{ kind: 'item', label: t('sec.webpublish'), id: 'webpublish' },
|
||||
{ kind: 'item', label: t('sec.uscounties'), id: 'uscounties' },
|
||||
{ kind: 'item', label: t('sec.database'), id: 'database' },
|
||||
{ kind: 'item', label: t('sec.autostart'), id: 'autostart' },
|
||||
@@ -289,6 +292,7 @@ const SECTION_KEY: Partial<Record<SectionId, string>> = {
|
||||
'external-services': 'sec.external', lookup: 'sec.lookup', 'lists-bands': 'sec.bands', 'lists-modes': 'sec.modes',
|
||||
cluster: 'sec.cluster', backup: 'sec.backup', database: 'sec.database', autostart: 'sec.autostart', udp: 'sec.udp',
|
||||
adifmon: 'sec.adifmon',
|
||||
webpublish: 'sec.webpublish',
|
||||
uscounties: 'sec.uscounties',
|
||||
awards: 'sec.awards', cat: 'sec.cat', rotator: 'sec.rotator', winkeyer: 'sec.winkeyer', antenna: 'sec.antenna',
|
||||
antgenius: 'sec.antgenius', tunergenius: 'sec.tunergenius', pgxl: 'sec.pgxl', flex: 'sec.flex', audio: 'sec.audio', general: 'sec.general', email: 'sec.email',
|
||||
@@ -311,6 +315,7 @@ const SECTION_LABELS: Partial<Record<SectionId, string>> = {
|
||||
autostart: 'Autostart',
|
||||
udp: 'UDP integrations',
|
||||
adifmon: 'ADIF monitor',
|
||||
webpublish: 'Web publishing',
|
||||
awards: 'Awards',
|
||||
cat: 'CAT interface',
|
||||
rotator: 'Rotator',
|
||||
@@ -6025,6 +6030,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
// — useState/useEffect/useI18n — get a proper component context; PANELS[x]()
|
||||
// is a plain call and hook-holding panels must go through JSX like this.
|
||||
adifmon: () => <ADIFMonitorPanel />,
|
||||
webpublish: () => <WebPublishPanel />,
|
||||
relayauto: () => <RelayAutoPanel />,
|
||||
backup: BackupPanel,
|
||||
database: DatabasePanel,
|
||||
|
||||
@@ -117,7 +117,7 @@ const en: Dict = {
|
||||
'sec.general': 'General', 'sec.email': 'E-mail (SMTP)', 'sec.lookup': 'Callsign Lookup',
|
||||
'sec.bands': 'Bands', 'sec.modes': 'Modes & default RST', 'sec.cluster': 'DX Cluster',
|
||||
'sec.udp': 'UDP integrations', 'sec.database': 'Database', 'sec.autostart': 'Autostart', 'sec.backup': 'Database backup', 'sec.uscounties': 'US Counties',
|
||||
'sec.adifmon': 'ADIF monitor',
|
||||
'sec.webpublish': 'Web publishing', 'wpub.hint': 'Publishes your log as a file for a website: a standalone HTML page or a CSV, written locally and optionally uploaded by FTP. It is refreshed when you log a QSO and, if you set an interval, on a timer.', 'wpub.enable': 'Publish the log to a file', 'wpub.fileSection': 'The file', 'wpub.format': 'Format', 'wpub.formatHtml': 'HTML page', 'wpub.formatCsv': 'CSV', 'wpub.folder': 'Output folder', 'wpub.browse': 'Browse…', 'wpub.fileName': 'File name', 'wpub.title': 'Page title', 'wpub.titlePh': 'blank = your callsign', 'wpub.count': 'Last N QSOs', 'wpub.every': 'Refresh every', 'wpub.everyHint': 'minutes — 0 = only when a QSO is logged', 'wpub.columns': 'Columns', 'wpub.columnsHint': 'Click to add or remove. The order shown here is the order in the file.', 'wpub.ftpEnable': 'Upload by FTP', 'wpub.ftpHost': 'Server / port', 'wpub.ftpUser': 'User', 'wpub.ftpPassword': 'Password', 'wpub.ftpFolder': 'Remote folder', 'wpub.ftpFileName': 'Remote file name', 'wpub.ftpTls': 'Use TLS (FTPS)', 'wpub.publishNow': 'Publish now', 'wpub.testFtp': 'Test connection', 'wpub.lastRun': 'Last run:', 'sec.adifmon': 'ADIF monitor',
|
||||
'adifmon.hint': 'Watch external ADIF files and import new QSOs automatically — e.g. fldigi logging RTTY, or N1MM/VarAC. Imported QSOs are enriched, de-duplicated and uploaded to your external services just like a QSO logged here.',
|
||||
'adifmon.enable': 'Enable ADIF monitor',
|
||||
'adifmon.empty': 'No file watched yet. Add an ADIF file below.',
|
||||
@@ -540,7 +540,7 @@ const fr: Dict = {
|
||||
'sec.general': 'Général', 'sec.email': 'E-mail (SMTP)', 'sec.lookup': "Recherche d'indicatif",
|
||||
'sec.bands': 'Bandes', 'sec.modes': 'Modes & RST par défaut', 'sec.cluster': 'DX Cluster',
|
||||
'sec.udp': 'Intégrations UDP', 'sec.database': 'Base de données', 'sec.autostart': 'Démarrage auto', 'sec.backup': 'Sauvegarde base', 'sec.uscounties': 'Comtés US',
|
||||
'sec.adifmon': 'Moniteur ADIF',
|
||||
'sec.webpublish': 'Publication web', 'wpub.hint': "Publie ton journal dans un fichier destiné à un site web : une page HTML autonome ou un CSV, écrit en local et envoyé par FTP si tu le souhaites. Il est rafraîchi à chaque QSO enregistré et, si tu règles un intervalle, périodiquement.", 'wpub.enable': 'Publier le journal dans un fichier', 'wpub.fileSection': 'Le fichier', 'wpub.format': 'Format', 'wpub.formatHtml': 'Page HTML', 'wpub.formatCsv': 'CSV', 'wpub.folder': 'Dossier de sortie', 'wpub.browse': 'Parcourir…', 'wpub.fileName': 'Nom du fichier', 'wpub.title': 'Titre de la page', 'wpub.titlePh': 'vide = ton indicatif', 'wpub.count': 'N derniers QSO', 'wpub.every': 'Rafraîchir toutes les', 'wpub.everyHint': 'minutes — 0 = seulement à chaque QSO', 'wpub.columns': 'Colonnes', 'wpub.columnsHint': 'Clique pour ajouter ou retirer. L ordre affiché ici est celui du fichier.', 'wpub.ftpEnable': 'Envoyer par FTP', 'wpub.ftpHost': 'Serveur / port', 'wpub.ftpUser': 'Utilisateur', 'wpub.ftpPassword': 'Mot de passe', 'wpub.ftpFolder': 'Dossier distant', 'wpub.ftpFileName': 'Nom du fichier distant', 'wpub.ftpTls': 'Utiliser TLS (FTPS)', 'wpub.publishNow': 'Publier maintenant', 'wpub.testFtp': 'Tester la connexion', 'wpub.lastRun': 'Dernière exécution :', 'sec.adifmon': 'Moniteur ADIF',
|
||||
'adifmon.hint': "Surveille des fichiers ADIF externes et importe les nouveaux QSO automatiquement — ex. fldigi en RTTY, ou N1MM/VarAC. Les QSO importés sont enrichis, dédoublonnés et envoyés à tes services externes comme un QSO loggé ici.",
|
||||
'adifmon.enable': 'Activer le moniteur ADIF',
|
||||
'adifmon.empty': 'Aucun fichier surveillé. Ajoute un fichier ADIF ci-dessous.',
|
||||
|
||||
Vendored
+13
@@ -16,6 +16,7 @@ import {powergenius} from '../models';
|
||||
import {spe} from '../models';
|
||||
import {solar} from '../models';
|
||||
import {tunergenius} from '../models';
|
||||
import {webpub} from '../models';
|
||||
import {winkeyer} from '../models';
|
||||
import {alerts} from '../models';
|
||||
import {audio} from '../models';
|
||||
@@ -509,6 +510,10 @@ export function GetUltrabeamSettings():Promise<main.UltrabeamSettings>;
|
||||
|
||||
export function GetUltrabeamStatus():Promise<main.UltrabeamStatusInfo>;
|
||||
|
||||
export function GetWebPublishConfig():Promise<webpub.Config>;
|
||||
|
||||
export function GetWebPublishStatus():Promise<main.WebPublishStatus>;
|
||||
|
||||
export function GetWhatsNew():Promise<Array<main.ChangelogEntry>>;
|
||||
|
||||
export function GetWinkeyerSettings():Promise<main.WinkeyerSettings>;
|
||||
@@ -737,6 +742,8 @@ export function PickSaveDatabase():Promise<string>;
|
||||
|
||||
export function PopulateBuiltinReferences(arg1:string):Promise<number>;
|
||||
|
||||
export function PublishLogNow():Promise<string>;
|
||||
|
||||
export function QSLDefaultTemplateID():Promise<number>;
|
||||
|
||||
export function QSLDeleteTemplate(arg1:number):Promise<void>;
|
||||
@@ -913,6 +920,8 @@ export function SaveUDPIntegration(arg1:udp.Config):Promise<udp.Config>;
|
||||
|
||||
export function SaveUltrabeamSettings(arg1:main.UltrabeamSettings):Promise<void>;
|
||||
|
||||
export function SaveWebPublishConfig(arg1:webpub.Config):Promise<void>;
|
||||
|
||||
export function SaveWinkeyerSettings(arg1:main.WinkeyerSettings):Promise<void>;
|
||||
|
||||
export function ScpLookup(arg1:string):Promise<scp.Result>;
|
||||
@@ -1047,6 +1056,8 @@ export function TestStationDevice(arg1:main.StationDevice):Promise<main.StationT
|
||||
|
||||
export function TestUltrabeam(arg1:main.UltrabeamSettings):Promise<void>;
|
||||
|
||||
export function TestWebPublishFTP(arg1:webpub.Config):Promise<string>;
|
||||
|
||||
export function TuneYaesuATU():Promise<void>;
|
||||
|
||||
export function TunerGeniusActivate(arg1:number):Promise<void>;
|
||||
@@ -1079,6 +1090,8 @@ export function UploadCallsign(arg1:string):Promise<string>;
|
||||
|
||||
export function UploadQSOsManual(arg1:string,arg2:Array<number>):Promise<void>;
|
||||
|
||||
export function WebPublishColumns():Promise<Array<Record<string, string>>>;
|
||||
|
||||
export function WinkeyerBackspace():Promise<void>;
|
||||
|
||||
export function WinkeyerConnect():Promise<void>;
|
||||
|
||||
@@ -966,6 +966,14 @@ export function GetUltrabeamStatus() {
|
||||
return window['go']['main']['App']['GetUltrabeamStatus']();
|
||||
}
|
||||
|
||||
export function GetWebPublishConfig() {
|
||||
return window['go']['main']['App']['GetWebPublishConfig']();
|
||||
}
|
||||
|
||||
export function GetWebPublishStatus() {
|
||||
return window['go']['main']['App']['GetWebPublishStatus']();
|
||||
}
|
||||
|
||||
export function GetWhatsNew() {
|
||||
return window['go']['main']['App']['GetWhatsNew']();
|
||||
}
|
||||
@@ -1422,6 +1430,10 @@ export function PopulateBuiltinReferences(arg1) {
|
||||
return window['go']['main']['App']['PopulateBuiltinReferences'](arg1);
|
||||
}
|
||||
|
||||
export function PublishLogNow() {
|
||||
return window['go']['main']['App']['PublishLogNow']();
|
||||
}
|
||||
|
||||
export function QSLDefaultTemplateID() {
|
||||
return window['go']['main']['App']['QSLDefaultTemplateID']();
|
||||
}
|
||||
@@ -1774,6 +1786,10 @@ export function SaveUltrabeamSettings(arg1) {
|
||||
return window['go']['main']['App']['SaveUltrabeamSettings'](arg1);
|
||||
}
|
||||
|
||||
export function SaveWebPublishConfig(arg1) {
|
||||
return window['go']['main']['App']['SaveWebPublishConfig'](arg1);
|
||||
}
|
||||
|
||||
export function SaveWinkeyerSettings(arg1) {
|
||||
return window['go']['main']['App']['SaveWinkeyerSettings'](arg1);
|
||||
}
|
||||
@@ -2042,6 +2058,10 @@ export function TestUltrabeam(arg1) {
|
||||
return window['go']['main']['App']['TestUltrabeam'](arg1);
|
||||
}
|
||||
|
||||
export function TestWebPublishFTP(arg1) {
|
||||
return window['go']['main']['App']['TestWebPublishFTP'](arg1);
|
||||
}
|
||||
|
||||
export function TuneYaesuATU() {
|
||||
return window['go']['main']['App']['TuneYaesuATU']();
|
||||
}
|
||||
@@ -2106,6 +2126,10 @@ export function UploadQSOsManual(arg1, arg2) {
|
||||
return window['go']['main']['App']['UploadQSOsManual'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function WebPublishColumns() {
|
||||
return window['go']['main']['App']['WebPublishColumns']();
|
||||
}
|
||||
|
||||
export function WinkeyerBackspace() {
|
||||
return window['go']['main']['App']['WinkeyerBackspace']();
|
||||
}
|
||||
|
||||
@@ -3267,6 +3267,20 @@ export namespace main {
|
||||
this.text = source["text"];
|
||||
}
|
||||
}
|
||||
export class WebPublishStatus {
|
||||
last_run: string;
|
||||
last_err: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new WebPublishStatus(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.last_run = source["last_run"];
|
||||
this.last_err = source["last_err"];
|
||||
}
|
||||
}
|
||||
export class WinkeyerSettings {
|
||||
enabled: boolean;
|
||||
port: string;
|
||||
@@ -4813,6 +4827,53 @@ export namespace udp {
|
||||
|
||||
}
|
||||
|
||||
export namespace webpub {
|
||||
|
||||
export class Config {
|
||||
enabled: boolean;
|
||||
format: string;
|
||||
folder: string;
|
||||
file_name: string;
|
||||
title: string;
|
||||
count: number;
|
||||
interval_min: number;
|
||||
columns: string[];
|
||||
ftp_enabled: boolean;
|
||||
ftp_host: string;
|
||||
ftp_port: number;
|
||||
ftp_user: string;
|
||||
ftp_password: string;
|
||||
ftp_tls: boolean;
|
||||
ftp_folder: string;
|
||||
ftp_file_name: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Config(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.enabled = source["enabled"];
|
||||
this.format = source["format"];
|
||||
this.folder = source["folder"];
|
||||
this.file_name = source["file_name"];
|
||||
this.title = source["title"];
|
||||
this.count = source["count"];
|
||||
this.interval_min = source["interval_min"];
|
||||
this.columns = source["columns"];
|
||||
this.ftp_enabled = source["ftp_enabled"];
|
||||
this.ftp_host = source["ftp_host"];
|
||||
this.ftp_port = source["ftp_port"];
|
||||
this.ftp_user = source["ftp_user"];
|
||||
this.ftp_password = source["ftp_password"];
|
||||
this.ftp_tls = source["ftp_tls"];
|
||||
this.ftp_folder = source["ftp_folder"];
|
||||
this.ftp_file_name = source["ftp_file_name"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace winkeyer {
|
||||
|
||||
export class Status {
|
||||
|
||||
Reference in New Issue
Block a user