Files
Ai/FT991A-Remote/FT991A-Remote/FT991A_RemoteApp.swift
T
Claude 1e153f2f85 Add FT-991A Remote Control App for macOS
Complete Phase 1 implementation of the Yaesu FT-991A remote control
application with CAT protocol support over USB serial (CP210x).

Features implemented:
- SerialPortManager with auto-detection of CP210x ports
- Full CAT protocol parser and command builder
- RadioState model with all transceiver parameters
- Modern SwiftUI interface with frequency/mode/level controls
- Skeuomorphic front panel view (switchable)
- Debug panel with CAT command console
- QSO log panel with CSV export/import
- Audio routing panel with BlackHole integration
- Settings with connection, UI, keyboard configuration
- Menu bar extra for background operation
- German/English localization
- Logging system for debugging

Supports: Frequency control, VFO A/B, all modes (LSB/USB/CW/FM/AM/
DATA/RTTY/C4FM), level controls, NB/NR/DNF/ATU/Split functions,
S-meter/Power/SWR metering, PTT control via Shift key.

Target: macOS 15.0+ (Sequoia/Tahoe)
2025-12-18 10:59:15 +00:00

110 lines
3.3 KiB
Swift

//
// FT991A_RemoteApp.swift
// FT991A-Remote
//
// Yaesu FT-991A Remote Control Application for macOS
// CAT Protocol via USB Serial (Silicon Labs CP210x)
//
import SwiftUI
@main
struct FT991A_RemoteApp: App {
@StateObject private var radioViewModel = RadioViewModel()
@StateObject private var settingsController = SettingsController()
@StateObject private var logViewModel = LogViewModel()
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(radioViewModel)
.environmentObject(settingsController)
.environmentObject(logViewModel)
.frame(minWidth: 800, minHeight: 600)
}
.windowStyle(.hiddenTitleBar)
.commands {
CommandGroup(replacing: .newItem) { }
CommandMenu("Radio") {
Button(radioViewModel.isConnected ? "Trennen" : "Verbinden") {
radioViewModel.toggleConnection()
}
.keyboardShortcut("k", modifiers: .command)
Divider()
Button("VFO A/B tauschen") {
radioViewModel.swapVFO()
}
.keyboardShortcut("s", modifiers: [.command, .shift])
.disabled(!radioViewModel.isConnected)
Button("A=B") {
radioViewModel.equalizeVFO()
}
.keyboardShortcut("e", modifiers: [.command, .shift])
.disabled(!radioViewModel.isConnected)
Divider()
Button("ATU Tune") {
radioViewModel.startATUTune()
}
.keyboardShortcut("t", modifiers: [.command, .shift])
.disabled(!radioViewModel.isConnected)
}
CommandMenu("Ansicht") {
Picker("UI-Stil", selection: $settingsController.uiStyle) {
Text("Modern").tag(UIStyle.modern)
Text("Frontpanel").tag(UIStyle.skeuomorph)
}
Divider()
Toggle("Debug-Panel anzeigen", isOn: $settingsController.showDebugPanel)
.keyboardShortcut("d", modifiers: [.command, .option])
Toggle("Log-Panel anzeigen", isOn: $settingsController.showLogPanel)
.keyboardShortcut("l", modifiers: [.command, .option])
}
}
Settings {
SettingsView()
.environmentObject(radioViewModel)
.environmentObject(settingsController)
}
MenuBarExtra("FT-991A", systemImage: radioViewModel.isConnected ? "antenna.radiowaves.left.and.right" : "antenna.radiowaves.left.and.right.slash") {
MenuBarView()
.environmentObject(radioViewModel)
.environmentObject(settingsController)
}
}
}
// MARK: - UI Style Enum
enum UIStyle: String, Codable, CaseIterable {
case modern = "Modern"
case skeuomorph = "Frontpanel"
}
// MARK: - Language Enum
enum AppLanguage: String, Codable, CaseIterable {
case german = "de"
case english = "en"
var displayName: String {
switch self {
case .german: return "Deutsch"
case .english: return "English"
}
}
}