A modern, modular amateur radio dashboard built with React and Vite. This is the fully extracted modular version - all components, hooks, and utilities are already separated into individual files.
# Install dependencies
npm install
# Start the server (auto-builds frontend and creates .env on first run)
npm start
# Edit .env with your callsign and grid locator
# Then restart:
npm start
# Open http://localhost:3000That's it! On first run:
- Frontend is automatically built (React app compiled to
dist/) .envfile is created from.env.example- Just edit
.envwith your callsign and locator
For development with hot reload:
# Terminal 1: Backend API server
node server.js
# Terminal 2: Frontend dev server with hot reload
npm run dev- Run
npm start- the server automatically creates.envfrom.env.example - Edit
.envwith your station info:CALLSIGN=K0CJH LOCATOR=EN10
- Restart the server - you're ready to go!
If you skip editing .env, the Settings panel will pop up in your browser asking for your callsign.
Settings are loaded in this order (first one wins):
- localStorage - Changes made in the browser Settings panel
- .env file - Your station configuration (won't be overwritten by updates)
- Defaults - Built-in fallback values
# Required - Your Station
CALLSIGN=N0CALL
LOCATOR=FN31
# Server Settings
PORT=3000
HOST=localhost # Use 0.0.0.0 for network access
# Display Preferences
UNITS=imperial # or 'metric'
TIME_FORMAT=12 # or '24'
THEME=dark # dark, light, legacy, retro
LAYOUT=modern # modern or classic
# Optional Features
SHOW_SATELLITES=true
SHOW_POTA=true
SHOW_DX_PATHS=true
# Optional Services
ITURHFPROP_URL= # For advanced propagation
DXSPIDER_PROXY_URL= # Custom DX cluster proxy
OPENWEATHER_API_KEY= # For local weatherTo access OpenHamClock from other devices on your network:
# In .env:
HOST=0.0.0.0
PORT=3000Then open http://<your-computer-ip>:3000 from any device.
| File | Git Tracked | Purpose |
|---|---|---|
.env.example |
β Yes | Template with all options documented |
.env |
β No | Your config (auto-created, never overwritten) |
config.example.json |
β Yes | Legacy JSON config template |
config.json |
β No | Legacy JSON config (optional) |
openhamclock-modular/
βββ src/
β βββ main.jsx # React entry point
β βββ App.jsx # Main application component
β βββ components/ # All UI components (fully extracted)
β β βββ index.js # Component exports
β β βββ Header.jsx # Top bar with clocks/controls
β β βββ WorldMap.jsx # Leaflet map with DX paths
β β βββ SpaceWeatherPanel.jsx
β β βββ BandConditionsPanel.jsx
β β βββ DXClusterPanel.jsx
β β βββ POTAPanel.jsx
β β βββ ContestPanel.jsx
β β βββ LocationPanel.jsx
β β βββ SettingsPanel.jsx
β β βββ DXFilterManager.jsx
β βββ hooks/ # All data fetching hooks (fully extracted)
β β βββ index.js # Hook exports
β β βββ useSpaceWeather.js
β β βββ useBandConditions.js
β β βββ useDXCluster.js
β β βββ useDXPaths.js
β β βββ usePOTASpots.js
β β βββ useContests.js
β β βββ useLocalWeather.js
β β βββ usePropagation.js
β β βββ useMySpots.js
β β βββ useDXpeditions.js
β β βββ useSatellites.js
β β βββ useSolarIndices.js
β βββ utils/ # Utility functions (fully extracted)
β β βββ index.js # Utility exports
β β βββ config.js # App config & localStorage
β β βββ geo.js # Grid squares, bearings, distances
β β βββ callsign.js # Band detection, filtering
β βββ styles/
β βββ main.css # All CSS with theme variables
βββ public/
β βββ index-monolithic.html # Original 5714-line reference
βββ server.js # Backend API server
βββ config.js # Server configuration
βββ package.json
βββ vite.config.js
βββ index.html # Vite entry HTML
Four themes available via Settings or .env:
- Dark (default) - Modern dark theme with amber accents
- Light - Light theme for daytime use
- Legacy - Classic HamClock green-on-black terminal style
- Retro - 90s Windows style with teal and silver
Themes use CSS custom properties defined in src/styles/main.css.
Two layouts available:
- Modern (default) - Responsive 3-column grid
- Classic - Original HamClock-style with black background, large colored numbers, rainbow frequency bar
All components are fully extracted and ready to modify:
| Component | Description | File |
|---|---|---|
| Header | Top bar with clocks, weather, controls | Header.jsx |
| WorldMap | Leaflet map with markers & paths | WorldMap.jsx |
| SpaceWeatherPanel | SFI, K-index, SSN display | SpaceWeatherPanel.jsx |
| BandConditionsPanel | HF band condition indicators | BandConditionsPanel.jsx |
| DXClusterPanel | Live DX spots list | DXClusterPanel.jsx |
| POTAPanel | Parks on the Air activations | POTAPanel.jsx |
| ContestPanel | Upcoming contests | ContestPanel.jsx |
| LocationPanel | DE/DX info with grid squares | LocationPanel.jsx |
| SettingsPanel | Configuration modal | SettingsPanel.jsx |
| DXFilterManager | DX cluster filtering modal | DXFilterManager.jsx |
All data fetching is handled by custom hooks:
| Hook | Purpose | Interval |
|---|---|---|
useSpaceWeather |
SFI, K-index, SSN from NOAA | 5 min |
useBandConditions |
Calculate band conditions | On SFI change |
useDXCluster |
DX spots with filtering | 5 sec |
useDXPaths |
DX paths for map | 10 sec |
usePOTASpots |
POTA activations | 1 min |
useContests |
Contest calendar | 30 min |
useLocalWeather |
Weather from Open-Meteo | 15 min |
usePropagation |
ITURHFProp predictions | 10 min |
useMySpots |
Your callsign spots | 30 sec |
useSatellites |
Satellite tracking | 5 sec |
useSolarIndices |
Extended solar data | 15 min |
| Module | Functions |
|---|---|
config.js |
loadConfig, saveConfig, applyTheme, MAP_STYLES |
geo.js |
calculateGridSquare, calculateBearing, calculateDistance, getSunPosition, getMoonPosition, getGreatCirclePoints |
callsign.js |
getBandFromFreq, getBandColor, detectMode, getCallsignInfo, filterDXPaths |
The backend server provides:
| Endpoint | Description |
|---|---|
/api/dxcluster/spots |
DX cluster spots |
/api/dxcluster/paths |
DX paths with coordinates |
/api/solar-indices |
Extended solar data |
/api/propagation |
HF propagation predictions |
/api/contests |
Contest calendar |
/api/myspots/:callsign |
Spots for your callsign |
/api/satellites/tle |
Satellite TLE data |
/api/dxpeditions |
Active DXpeditions |
One-line install for Raspberry Pi:
curl -fsSL https://raw.githubusercontent.com/accius/openhamclock/main/scripts/setup-pi.sh | bashOr with kiosk mode (auto-starts fullscreen on boot):
curl -fsSL https://raw.githubusercontent.com/accius/openhamclock/main/scripts/setup-pi.sh | bash -s -- --kioskAfter installation:
cd ~/openhamclock
nano .env # Edit your callsign and locator
./restart.sh# railway.toml and railway.json are included
railway updocker-compose up -dnpm run build
NODE_ENV=production node server.jsFor local/Pi installations, use the update script:
cd ~/openhamclock
./scripts/update.shThe update script will:
- β
Back up your
.envconfiguration - β Pull the latest code from GitHub
- β Install any new dependencies
- β Rebuild the frontend
- β Preserve your settings
Then restart the server:
sudo systemctl restart openhamclock
# or
./restart.shNote: If you installed from a zip file (not git clone), you'll need to:
- Back up your
.envfile - Download the new zip
- Extract and restore your
.env
- Fork the repository
- Pick a component/hook to improve
- Make changes in the appropriate file
- Test with all three themes
- Submit a PR
- Functional components with hooks
- CSS-in-JS for component-specific styles
- CSS variables for theme colors
- JSDoc comments for functions
- Descriptive variable names
# Run dev server
npm run dev
# Check all themes work
# Test on different screen sizes
# Verify data fetching worksMIT License - See LICENSE file
- K0CJH - Original OpenHamClock
- NOAA SWPC - Space weather data
- POTA - Parks on the Air API
- Open-Meteo - Weather data
- Leaflet - Mapping library