Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 120 additions & 25 deletions rig-bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ It also connects FT8/FT4 decoding software (WSJT-X, JTDX, MSHV, JS8Call) to Open

1. [Supported Radios](#supported-radios)
2. [Getting Started](#getting-started)
3. [Connecting Your Radio](#connecting-your-radio)
4. [Connecting to OpenHamClock](#connecting-to-openhamclock)
5. [Digital Mode Software (FT8, JS8, etc.)](#digital-mode-software)
6. [APRS via Local TNC](#aprs-via-local-tnc)
7. [Antenna Rotator](#antenna-rotator)
8. [HTTPS Setup (needed for openhamclock.com)](#https-setup)
9. [Troubleshooting](#troubleshooting)
10. [Advanced Topics](#advanced-topics)
3. [Updating Rig Bridge](#updating-rig-bridge)
4. [Connecting Your Radio](#connecting-your-radio)
5. [Connecting to OpenHamClock](#connecting-to-openhamclock)
6. [Digital Mode Software (FT8, JS8, etc.)](#digital-mode-software)
7. [APRS via Local TNC](#aprs-via-local-tnc)
8. [Antenna Rotator](#antenna-rotator)
9. [HTTPS Setup (needed for openhamclock.com)](#https-setup)
10. [Troubleshooting](#troubleshooting)
11. [Glossary](#glossary)
12. [Advanced Topics](#advanced-topics)

---

Expand Down Expand Up @@ -67,15 +69,49 @@ Select **Simulated Radio** in the setup screen. A fake radio will drift through

### Step 1 — Download and run Rig Bridge

**Option A — Standalone executable (easiest, no installation needed)**
**Option A — Installer from the OpenHamClock Settings tab (recommended)**

1. Go to the Releases page and download the file for your operating system:
- `ohc-rig-bridge-win.exe` — Windows
- `ohc-rig-bridge-macos` — macOS (Intel)
- `ohc-rig-bridge-macos-arm` — macOS (Apple Silicon / M1, M2, M3, M4)
- `ohc-rig-bridge-linux` — Linux
2. Double-click the file to run it. On macOS you may need to right-click → Open the first time.
3. A terminal/console window will appear showing log messages — leave it running.
> Requires **Node.js** and **git** to be installed on your computer.

1. In OpenHamClock, open **Settings → Rig Bridge**.
2. Tick **Enable Rig Bridge**.
3. Click the download button for your operating system — **Windows**, **Mac**, or **Linux**.

**Windows**

4. Open your Downloads folder and double-click `install-rig-bridge.bat`.
A Command Prompt window will open, download Rig Bridge, and start it automatically.

**macOS**

4. Open **Terminal** (Applications → Utilities → Terminal) and run:
```bash
chmod +x ~/Downloads/install-rig-bridge.sh
~/Downloads/install-rig-bridge.sh
```
The script downloads Rig Bridge and starts it. Leave the Terminal window open.

**Linux**

4. Open a terminal and run:
```bash
chmod +x ~/Downloads/install-rig-bridge.sh
~/Downloads/install-rig-bridge.sh
```
The script downloads Rig Bridge and starts it. Leave the terminal open.

---

5. Once Rig Bridge is running, return to OpenHamClock **Settings → Rig Bridge** and click
**Open Setup UI** — this opens **http://localhost:5555** in a new tab.
6. Copy the **API Token** shown at the top of that page.
7. Back in OpenHamClock **Settings → Rig Bridge**, paste the token into the **API Token** field.
8. Confirm **Host** is `http://localhost` and **Port** is `5555`.
9. Tick **Click-to-tune** if you want spot clicks to tune your radio, then click **Save**.

Now configure your radio in the Rig Bridge Setup UI — see [Connecting Your Radio](#connecting-your-radio).

To update Rig Bridge in the future, see [Updating Rig Bridge](#updating-rig-bridge).

**Option B — Run from source with Node.js**

Expand Down Expand Up @@ -109,6 +145,48 @@ See [Connecting to OpenHamClock](#connecting-to-openhamclock) below.

---

## Updating Rig Bridge

To update to the latest version, re-run the installer script you downloaded during setup
with the `--update` flag. Your radio configuration is preserved automatically.

**Windows**

Open Command Prompt, navigate to your Downloads folder, and run:

```cmd
install-rig-bridge.bat --update
```

> You cannot pass arguments by double-clicking a `.bat` file — open Command Prompt first
> (`Win + R` → type `cmd` → Enter), then run the command above.

**macOS / Linux**

```bash
~/Downloads/install-rig-bridge.sh --update
```

> If you no longer have the original script, download it again from
> **Settings → Rig Bridge** in OpenHamClock — the `--update` flag works on a freshly
> downloaded copy too, as long as Rig Bridge is already installed.

### What the update does

1. Stops the running Rig Bridge instance (if any)
2. Downloads the latest files from the repository
3. Preserves your `rig-bridge-config.json` (radio settings, tokens, plugins)
4. Re-installs dependencies
5. Restarts Rig Bridge

### What gets reset

Nothing in your configuration is changed. Any manual edits you made directly to
`rig-bridge.js` or other source files **will be overwritten** — the update replaces
all source files.

---

## Connecting Your Radio

### Yaesu radios (FT-991A, FT-891, FT-710, FT-DX10, etc.)
Expand Down Expand Up @@ -280,7 +358,7 @@ For example: Rig Bridge runs on a Raspberry Pi or shack PC connected to the radi

---

### Scenario C — Using the cloud version at openhamclock.com
### Scenario C — Using the cloud version at openhamclock.com _(Cloud Relay: Alpha)_

This lets you control your radio at home from anywhere in the world through the openhamclock.com website.

Expand Down Expand Up @@ -322,12 +400,12 @@ Rig Bridge can receive decoded FT8, FT4, JT65, and other digital mode signals fr

### Supported software

| Software | Mode | Default Port |
| ----------- | ----------------------------- | ------------ |
| **WSJT-X** | FT8, FT4, JT65, JT9, and more | 2237 |
| **JTDX** | FT8, JT65 (enhanced decoding) | 2238 |
| **MSHV** | MSK144, Q65, and others | 2239 |
| **JS8Call** | JS8 keyboard messaging | 2242 |
| Software | Mode | Default Port | Maturity |
| ----------- | ----------------------------- | ------------ | -------- |
| **WSJT-X** | FT8, FT4, JT65, JT9, and more | 2237 | Beta |
| **JTDX** | FT8, JT65 (enhanced decoding) | 2238 | Alpha |
| **MSHV** | MSK144, Q65, and others | 2239 | Alpha |
| **JS8Call** | JS8 keyboard messaging | 2242 | Alpha |

All of these are **bidirectional** — OpenHamClock can also send replies, stop transmit, set free text, and highlight callsigns in the decode window.

Expand Down Expand Up @@ -362,7 +440,7 @@ By default, WSJT-X sends its decoded packets only to one listener. If you want b

---

## APRS via Local TNC
## APRS via Local TNC _(Beta)_

If you run a local APRS TNC (for example, [Direwolf](https://github.com/wb2osz/direwolf) connected to a VHF radio), Rig Bridge can receive APRS packets from it and show nearby stations on the OpenHamClock map — without needing an internet connection.

Expand All @@ -389,7 +467,7 @@ If you have a traditional hardware TNC connected via serial port:

---

## Antenna Rotator
## Antenna Rotator _(Alpha)_

Rig Bridge can control antenna rotators via [Hamlib's](https://hamlib.github.io/) `rotctld` daemon.

Expand Down Expand Up @@ -578,6 +656,23 @@ The certificate is valid for 10 years and is regenerated only if you click **Reg

---

## Glossary

### Maturity levels

Components and plugins in Rig Bridge are labelled with a maturity level to help you set
expectations before enabling them.

| Level | Meaning |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Alpha** | Test implementation with no rigid testing yet. Use with caution — behaviour may change and bugs are expected. Feedback and bug reports are especially welcome. |
| **Beta** | Already tested more intensively, but still experimental status. More stable than Alpha, but not yet considered production-ready. |

Unlabelled components (USB radio control, flrig, rigctld, TCI, SmartSDR) are considered
stable and have been tested across multiple hardware setups.

---

## Advanced Topics

The sections below are for technically minded users or developers who want to go deeper.
Expand Down
72 changes: 58 additions & 14 deletions rig-bridge/core/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,14 @@ function buildSetupHtml(version, firstRunToken = null) {
<h1>📻 OpenHamClock Rig Bridge</h1>
<div class="subtitle">Direct USB connection to your radio — no flrig or rigctld needed</div>
<div id="logoutArea" style="display:none;margin-top:8px;">
<div id="headerTokenArea" style="display:none;justify-content:center;margin-bottom:6px;">
<div style="display:inline-flex;align-items:center;gap:6px;background:#111620;border:1px solid #1e2530;border-radius:6px;padding:5px 10px;white-space:nowrap;">
<span style="font-size:11px;color:#4b5563;">API Token:</span>
<input type="password" id="headerToken" readonly style="background:none;border:none;color:#00ffcc;font-family:'Courier New',Courier,monospace;font-size:11px;outline:none;width:260px;letter-spacing:0.5px;">
<button class="copy-btn" onclick="copyHeaderToken()" style="font-size:10px;padding:2px 7px;">Copy</button>
<button class="copy-btn" id="headerTokenToggle" onclick="toggleHeaderToken()" style="font-size:10px;padding:2px 7px;">Show</button>
</div>
</div>
<span class="logout-link" onclick="doLogout()">🔓 Lock setup</span>
</div>
</div>
Expand All @@ -517,7 +525,7 @@ function buildSetupHtml(version, firstRunToken = null) {
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('radio', this)">📻 Radio</button>
<button class="tab-btn" onclick="switchTab('plugins', this)">🧩 Plugins</button>
<button class="tab-btn" onclick="switchTab('integrations', this)">🔌 WSJT-X</button>
<button class="tab-btn" onclick="switchTab('integrations', this)">🔌 WSJT-X <span style="font-size:9px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;color:#06b6d4;border:1px solid #06b6d4;border-radius:3px;padding:1px 4px;line-height:1.4;opacity:0.85;margin-left:4px;vertical-align:middle;">Beta</span></button>
<button class="tab-btn" onclick="switchTab('log', this)">🖥️ Log</button>
<button class="tab-btn" onclick="switchTab('security', this); loadTlsStatus();">🔒 Security</button>
</div>
Expand Down Expand Up @@ -734,11 +742,8 @@ function buildSetupHtml(version, firstRunToken = null) {

<!-- Instructions -->
<div class="ohc-instructions">
<strong>Setup in OpenHamClock:</strong><br>
1. Open <strong>Settings</strong> → <strong>Station Settings</strong> → <strong>Rig Control</strong><br>
2. Check <strong>Enable Rig Control</strong><br>
3. Set Host URL to: <code>http://localhost:5555</code><br>
4. Click any DX spot, POTA, or SOTA to tune your radio! 🎉
<strong>New to Rig Bridge?</strong><br>
Follow <a href="https://github.com/accius/openhamclock/blob/main/rig-bridge/README.md#option-a----installer-from-the-openhamclock-settings-tab-recommended" target="_blank" rel="noopener noreferrer" style="color:#00ffcc;">Quick Start Option A</a> for step-by-step setup instructions.
</div>
</div>

Expand Down Expand Up @@ -953,19 +958,19 @@ function buildSetupHtml(version, firstRunToken = null) {
// ── Plugin Manager ────────────────────────────────────────────────────

const PLUGIN_DEFS = [
{ key: 'mshv', name: 'MSHV', desc: 'Multi-stream digital mode software (MSK144, Q65, etc.)', fields: [
{ key: 'mshv', name: 'MSHV', maturity: 'alpha', desc: 'Multi-stream digital mode software (MSK144, Q65, etc.)', fields: [
{ id: 'udpPort', label: 'UDP Port', type: 'number', default: 2239 },
{ id: 'verbose', label: 'Verbose logging', type: 'checkbox', default: false },
]},
{ key: 'jtdx', name: 'JTDX', desc: 'Enhanced JT65/JT9/FT8 decoding (WSJT-X fork)', fields: [
{ key: 'jtdx', name: 'JTDX', maturity: 'alpha', desc: 'Enhanced JT65/JT9/FT8 decoding (WSJT-X fork)', fields: [
{ id: 'udpPort', label: 'UDP Port', type: 'number', default: 2238 },
{ id: 'verbose', label: 'Verbose logging', type: 'checkbox', default: false },
]},
{ key: 'js8call', name: 'JS8Call', desc: 'JS8 messaging protocol for keyboard-to-keyboard QSOs', fields: [
{ key: 'js8call', name: 'JS8Call', maturity: 'alpha', desc: 'JS8 messaging protocol for keyboard-to-keyboard QSOs', fields: [
{ id: 'udpPort', label: 'UDP Port', type: 'number', default: 2242 },
{ id: 'verbose', label: 'Verbose logging', type: 'checkbox', default: false },
]},
{ key: 'aprs', name: 'APRS TNC', desc: 'Local APRS via Direwolf or hardware TNC (KISS protocol)', fields: [
{ key: 'aprs', name: 'APRS TNC', maturity: 'beta', desc: 'Local APRS via Direwolf or hardware TNC (KISS protocol)', fields: [
{ id: 'protocol', label: 'Protocol', type: 'select', options: ['kiss-tcp', 'kiss-serial'], default: 'kiss-tcp' },
{ id: 'host', label: 'TNC Host', type: 'text', default: '127.0.0.1' },
{ id: 'port', label: 'TNC Port', type: 'number', default: 8001 },
Expand All @@ -974,18 +979,18 @@ function buildSetupHtml(version, firstRunToken = null) {
{ id: 'beaconInterval', label: 'Beacon Interval (sec)', type: 'number', default: 600 },
{ id: 'verbose', label: 'Verbose logging', type: 'checkbox', default: false },
]},
{ key: 'rotator', name: 'Rotator (rotctld)', desc: 'Antenna rotator control via Hamlib rotctld', fields: [
{ key: 'rotator', name: 'Rotator (rotctld)', maturity: 'alpha', desc: 'Antenna rotator control via Hamlib rotctld', fields: [
{ id: 'host', label: 'rotctld Host', type: 'text', default: '127.0.0.1' },
{ id: 'port', label: 'rotctld Port', type: 'number', default: 4533 },
{ id: 'pollInterval', label: 'Poll Interval (ms)', type: 'number', default: 1000 },
]},
{ key: 'winlink', name: 'Winlink', desc: 'Gateway discovery + Pat client for Winlink messaging', fields: [
{ key: 'winlink', name: 'Winlink', maturity: 'alpha', desc: 'Gateway discovery + Pat client for Winlink messaging', fields: [
{ id: 'apiKey', label: 'Winlink API Key', type: 'text', default: '' },
{ id: 'pat.enabled', label: 'Enable Pat Client', type: 'checkbox', default: false },
{ id: 'pat.host', label: 'Pat Host', type: 'text', default: '127.0.0.1' },
{ id: 'pat.port', label: 'Pat Port', type: 'number', default: 8080 },
]},
{ key: 'cloudRelay', name: 'Cloud Relay', desc: 'Proxy rig features to cloud-hosted OpenHamClock', fields: [
{ key: 'cloudRelay', name: 'Cloud Relay', maturity: 'alpha', desc: 'Proxy rig features to cloud-hosted OpenHamClock', fields: [
{ id: 'url', label: 'OHC Server URL', type: 'text', default: '' },
{ id: 'apiKey', label: 'Relay API Key', type: 'text', default: '' },
{ id: 'session', label: 'Session ID', type: 'text', default: '' },
Expand All @@ -994,6 +999,16 @@ function buildSetupHtml(version, firstRunToken = null) {
]},
];

function maturityBadge(level) {
if (!level) return '';
const color = level === 'alpha' ? '#f59e0b' : '#06b6d4';
const label = level === 'alpha' ? 'Alpha' : 'Beta';
return '<span style="font-size:9px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;' +
'color:' + color + ';border:1px solid ' + color + ';border-radius:3px;' +
'padding:1px 4px;line-height:1.4;opacity:0.85;margin-left:6px;vertical-align:middle;">' +
label + '</span>';
}

function renderPlugins(cfg) {
const container = document.getElementById('pluginList');
container.innerHTML = '';
Expand All @@ -1007,7 +1022,7 @@ function buildSetupHtml(version, firstRunToken = null) {
card.innerHTML =
'<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;">' +
'<div>' +
'<strong style="color:#c4c9d4; font-size:13px;">' + plugin.name + '</strong>' +
'<strong style="color:#c4c9d4; font-size:13px;">' + plugin.name + maturityBadge(plugin.maturity) + '</strong>' +
'<div style="font-size:11px; color:#6b7280; margin-top:2px;">' + plugin.desc + '</div>' +
'</div>' +
'<label style="position:relative; display:inline-block; width:44px; height:24px; margin:0;">' +
Expand Down Expand Up @@ -1248,8 +1263,17 @@ function buildSetupHtml(version, firstRunToken = null) {
}
const overlay = document.getElementById('loginOverlay');
const logoutArea = document.getElementById('logoutArea');
const headerTokenArea = document.getElementById('headerTokenArea');
const headerToken = document.getElementById('headerToken');
if (overlay) overlay.style.display = 'none';
if (logoutArea) logoutArea.style.display = 'block';
if (token && headerToken) {
headerToken.value = token;
headerToken.type = 'password';
const toggle = document.getElementById('headerTokenToggle');
if (toggle) toggle.textContent = 'Show';
}
if (headerTokenArea) headerTokenArea.style.display = token ? 'flex' : 'none';
loadApp();
}

Expand Down Expand Up @@ -1336,6 +1360,26 @@ function buildSetupHtml(version, firstRunToken = null) {
showToast('✅ Token copied!', 'success');
}

function copyHeaderToken() {
const inp = document.getElementById('headerToken');
if (!inp) return;
const prev = inp.type;
inp.type = 'text';
inp.select();
try { document.execCommand('copy'); } catch (e) {}
inp.type = prev;
showToast('✅ Token copied!', 'success');
}

function toggleHeaderToken() {
const inp = document.getElementById('headerToken');
const btn = document.getElementById('headerTokenToggle');
if (!inp) return;
const showing = inp.type === 'text';
inp.type = showing ? 'password' : 'text';
if (btn) btn.textContent = showing ? 'Show' : 'Hide';
}

async function dismissBanner() {
await fetch('/api/setup/token-seen', { method: 'POST', headers: authHeaders() }).catch(() => {});
const banner = document.getElementById('welcomeBanner');
Expand Down
21 changes: 18 additions & 3 deletions rig-bridge/start-rig-bridge.bat
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
@echo off
title OpenHamClock Rig Bridge

:: Read version from package.json
set "RB_VERSION=?"
for /f "usebackq delims=" %%V in (`node -e "try{process.stdout.write(require('./package.json').version)}catch(e){process.stdout.write('?')}" 2^>nul`) do set "RB_VERSION=%%V"

:: Read port and TLS setting from config
set "RB_PORT=5555"
set "RB_PROTO=http"
set "RB_CFG=%APPDATA%\openhamclock\rig-bridge-config.json"
if not exist "%RB_CFG%" set "RB_CFG=%USERPROFILE%\openhamclock-rig-bridge\rig-bridge-config.json"
if exist "%RB_CFG%" (
for /f "usebackq delims=" %%P in (`powershell -NoProfile -Command "try{(Get-Content '%RB_CFG%'|ConvertFrom-Json).port}catch{5555}"`) do set "RB_PORT=%%P"
for /f "usebackq delims=" %%T in (`powershell -NoProfile -Command "try{if((Get-Content '%RB_CFG%'|ConvertFrom-Json).tls.enabled){'https'}else{'http'}}catch{'http'}"`) do set "RB_PROTO=%%T"
)

echo.
echo Starting OpenHamClock Rig Bridge...
echo Setup UI will open at http://localhost:5555
echo OpenHamClock Rig Bridge v%RB_VERSION%
echo Setup UI: %RB_PROTO%://localhost:%RB_PORT%
echo.
echo Press Ctrl+C to stop.
echo.
Expand All @@ -16,7 +31,7 @@ if %errorlevel%==0 (
call npm install
echo.
)
start http://localhost:5555
start %RB_PROTO%://localhost:%RB_PORT%
node rig-bridge.js
) else (
echo ERROR: Node.js not found.
Expand Down
Loading
Loading