Skip to content

Add LOTW (Logbook of the World) integration with TQSL support#166

Closed
Claude wants to merge 7 commits into
mainfrom
claude/add-lotw-support-to-log4ym
Closed

Add LOTW (Logbook of the World) integration with TQSL support#166
Claude wants to merge 7 commits into
mainfrom
claude/add-lotw-support-to-log4ym

Conversation

@Claude
Copy link
Copy Markdown
Contributor

@Claude Claude AI commented Feb 19, 2026

Implements LOTW upload functionality following QLog's approach: TQSL command-line integration for signing and uploading QSOs to ARRL's Logbook of the World.

Backend Changes

Data Models

  • Added LotwSettings with TQSL path, station location, and installation status caching
  • Added LotwSyncStatus, LotwSyncedAt fields to Qso model
  • QSOs automatically marked as Modified when edited after sync (follows QLog's Y/N/M pattern)

Service Layer

  • LotwService implements TQSL integration:
    • Auto-detects TQSL on Windows (C:\Program Files\TrustedQSL\tqsl.exe), macOS (/Applications/TrustedQSL.app), Linux (/usr/bin/tqsl)
    • Retrieves configured station locations via tqsl -l
    • Signs and uploads QSOs via tqsl -d -l "location" -u <adif_file>
    • Generates LOTW-compatible ADIF exports

API & Real-time Updates

  • LotwController with endpoints: /api/lotw/installation, /api/lotw/locations, /api/lotw/settings, /api/lotw/upload, /api/lotw/sync
  • SignalR OnLotwSyncProgress event for real-time upload progress
  • Repository methods: GetUnsyncedToLotwAsync(), UpdateLotwSyncStatusAsync(), GetPendingLotwSyncCountAsync() (both MongoDB and LiteDB)

Frontend Changes

API Client

  • TypeScript interfaces: LotwInstallationResponse, LotwStationLocationsResponse, LotwSettingsRequest, LotwUploadResponse
  • Client methods: getLotwInstallation(), getLotwStationLocations(), updateLotwSettings(), uploadToLotw(), syncToLotw(), cancelLotwSync()

Example Usage

// Check TQSL installation
const status = await client.getLotwInstallation();
// { isInstalled: true, tqslPath: "/usr/bin/tqsl", version: "2.7.1" }

// Get station locations
const locations = await client.getLotwStationLocations();
// { locations: ["Home Station", "Portable"] }

// Sync unsynced QSOs
const result = await client.syncToLotw();
// { totalCount: 42, uploadedCount: 42, failedCount: 0, success: true }

Remaining Work

UI components still needed:

  • LOTW settings panel (station location selector, TQSL status display)
  • Logbook sync button and status column
  • SignalR event handlers for progress display
Original prompt

This section details on the original issue you should resolve

<issue_title>Add LOTW Support</issue_title>
<issue_description>Research QLog and see how LOTW (log book of the world is implemented)

let's add the same functionality to Log4YM

</issue_description>

Comments on the Issue (you are @claude[agent] in this section)

Claude AI and others added 3 commits February 19, 2026 22:06
Co-authored-by: brianbruff <753297+brianbruff@users.noreply.github.com>
Co-authored-by: brianbruff <753297+brianbruff@users.noreply.github.com>
Co-authored-by: brianbruff <753297+brianbruff@users.noreply.github.com>
@Claude Claude AI changed the title [WIP] Add LOTW support to Log4YM functionality Add LOTW (Logbook of the World) integration with TQSL support Feb 19, 2026
@Claude Claude AI requested a review from brianbruff February 19, 2026 22:12
@brianbruff
Copy link
Copy Markdown
Owner

@claude Implement the LoTW settings UX on the frontend. The backend API surface is already in place (see LotwController.cs and the API client methods in client.ts). Take inspiration from QLog's LoTW integration UI for the design and workflow. Wire up the existing API endpoints (/lotw/installation, /lotw/locations, /lotw/settings, /lotw/upload, /lotw/sync) into a proper settings panel and any relevant QSO action buttons.

@claude
Copy link
Copy Markdown

claude Bot commented Feb 20, 2026

Claude finished @brianbruff's task in 11m 33s —— View job


LoTW Settings UX — Implemented

All tasks complete. Here's what was implemented across 6 files (commit 6985ca4):

Tasks:

  • Read existing code and understand the frontend structure
  • Update settingsStore.ts — add LotwSettings interface and state
  • Update signalr.ts — add LotwSyncProgressEvent and OnLotwSyncProgress handler
  • Update appStore.ts — add lotwSyncProgress / setLotwSyncProgress state
  • Update useSignalR.ts — wire up onLotwSyncProgress event handler
  • Implement LotwSettingsSection in SettingsPanel.tsx
  • Add LOTW sync button + progress to LogHistoryPlugin.tsx

What was built

Settings Panel → LoTW section (SettingsPanel.tsx)

  • Enable/disable toggle for LoTW uploads
  • Auto-detects TQSL on mount via GET /api/lotw/installation — shows version and path in a status badge
  • Optional TQSL path override input (for non-standard install locations)
  • Station location dropdown, populated from GET /api/lotw/locations, with a Refresh button
  • "Verify TQSL Setup" button → calls PUT /api/lotw/settings and shows success/error inline
  • Help text with instructions and a reference to lotw.arrl.org

Log History toolbar (LogHistoryPlugin.tsx)

  • "Push to LoTW" button appears conditionally when LoTW is enabled in settings
  • Full progress display: starting state → animated progress bar → complete/cancelled banner
  • Cancel button wired to cancelLotwSync() during active sync
  • Progress driven by OnLotwSyncProgress SignalR events in real time

Infrastructure (settingsStore.ts, appStore.ts, signalr.ts, useSignalR.ts)

  • LotwSettings (enabled, tqslPath, stationLocation) saved in main settings (MongoDB)
  • LotwSyncProgress state mirrors the existing QRZ sync pattern
  • LotwSyncProgressEvent type and OnLotwSyncProgress SignalR listener added
    |

- Add LotwSettings to settingsStore (enabled, tqslPath, stationLocation)
- Add LotwSyncProgressEvent to signalr.ts with OnLotwSyncProgress handler
- Add lotwSyncProgress state to appStore (mirrors QRZ sync pattern)
- Wire up onLotwSyncProgress handler in useSignalR.ts
- Add LotwSettingsSection to SettingsPanel with TQSL status check,
  station location dropdown, and verify button
- Add conditional 'Push to LoTW' button in LogHistoryPlugin toolbar
- Add LOTW sync progress/complete display in LogHistoryPlugin

Co-authored-by: Brian Keating <brianbruff@users.noreply.github.com>
@brianbruff
Copy link
Copy Markdown
Owner

image

initial tests on macos are not finding tsql
i'm running this test with dotnet run for backend and pnpm run for web front end.

@claude understand why this is not working on macos and add the suitable fix

@claude
Copy link
Copy Markdown

claude Bot commented Feb 21, 2026

Claude finished @brianbruff's task in 2m 4s —— View job


macOS TQSL Detection — Fixed

  • Gather context
  • Read LotwService.cs — found the bugs
  • Implement the fix (ca733f4)
  • Committed and pushed

Root Cause — Two Bugs Fixed

Bug 1 — TQSL writes --version output to stderr, not stdout (LotwService.cs)

TQSL is a wxWidgets GUI app and on macOS/Linux it writes version info to stderr, not stdout. The original check only looked at output (stdout), which was always empty, so detection always fell through to "Not Installed".

- if (exitCode == 0 && !string.IsNullOrEmpty(output))
- {
-     var version = ParseTqslVersion(output);
+ // TQSL (wxWidgets app) writes version info to stderr on macOS/Linux;
+ // accept output from either stream.
+ var versionText = !string.IsNullOrWhiteSpace(output) ? output : error;
+
+ if (exitCode == 0 || !string.IsNullOrWhiteSpace(versionText))
+ {
+     var version = ParseTqslVersion(versionText ?? string.Empty);

Bug 2 — Missing Apple Silicon Homebrew path

On Apple Silicon Macs, Homebrew installs to /opt/homebrew/bin/ not /usr/local/bin/.

  pathsToCheck.Add("/Applications/TrustedQSL.app/Contents/MacOS/tqsl");
+ pathsToCheck.Add("/opt/homebrew/bin/tqsl");   // Apple Silicon Macs
  pathsToCheck.Add("/usr/local/bin/tqsl");       // Intel Macs / Homebrew x86

Both fixes are in commit ca733f4 and pushed to the branch.
· Branch

Two bugs prevented TQSL from being detected on macOS:

1. TQSL (wxWidgets) writes --version output to stderr, not stdout.
   The detection only checked stdout (output), so it always fell
   through to "Not Installed". Now checks stderr as a fallback and
   also accepts a zero exit code even without version text.

2. Apple Silicon Macs install Homebrew to /opt/homebrew/bin/ not
   /usr/local/bin/. Added /opt/homebrew/bin/tqsl to the macOS
   search paths.

Co-authored-by: Brian Keating <brianbruff@users.noreply.github.com>
@brianbruff
Copy link
Copy Markdown
Owner

was implemented by @EI6JGB

@brianbruff brianbruff closed this Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add LOTW Support

2 participants