Special Callsign Operator Coordination Tool for Ham Radio
A web application for coordinating multiple operators activating the same special callsign. Avoid conflicts by blocking band/mode combinations in real-time.
| Feature | Description |
|---|---|
| π Real-time Heatmap | Interactive visualization of band/mode availability |
| π Band/Mode Blocking | Reserve combinations while you're active |
| π¬ Real-time Chat | MQTT-powered instant messaging between operators (no page refresh) |
| π‘ DX Cluster Spotting | Send spots to DX Cluster nodes via Telnet to announce activity |
| π’ Announcements | Admin announcements with notification badges |
| π Multi-Award Support | Manage multiple special callsigns/events |
| π Multi-Language | English, Spanish, Galician |
| π± Mobile-Friendly | Responsive design for smartphones |
| π₯ Multi-Operator | Secure authentication for teams |
| πΎ Backup/Restore | Database management for admins |
| π€ Telegram Bot | Block/unblock bands and receive notifications via Telegram |
# Clone and configure
git clone <repository-url>
cd award_planner
cp .env.example .env
# Edit .env with your admin credentials
# Run
docker-compose up -d
# Access at http://localhost:8501pip install -r requirements.txt
export ADMIN_CALLSIGN=EA1RFI
export ADMIN_PASSWORD=YourSecurePassword
streamlit run app.py| Variable | Description | Required |
|---|---|---|
ADMIN_CALLSIGN |
Super admin callsign | Yes |
ADMIN_PASSWORD |
Super admin password | Yes |
DATABASE_PATH |
SQLite database path | No (default: ham_coordinator.db) |
MQTT_WS_URL |
MQTT WebSocket URL for real-time chat (e.g. wss://yourdomain.com/mqtt) |
No (chat disabled when unset) |
MQTT_BROKER_HOST |
MQTT broker hostname (internal) | No (default: mosquitto) |
MQTT_BROKER_PORT |
MQTT broker port (internal) | No (default: 1883) |
DX_CLUSTER_HOST |
DX Cluster node hostname (e.g. dxfun.com) |
No (spotting disabled when unset) |
DX_CLUSTER_PORT |
DX Cluster Telnet port | No (default: 7300) |
DX_CLUSTER_CALLSIGN |
Callsign used to log in to the cluster | No |
DX_CLUSTER_PASSWORD |
Password for cluster authentication (if required) | No |
TELEGRAM_BOT_TOKEN |
Telegram bot API token from @BotFather | No (bot disabled when unset) |
QuendAward includes a real-time chat system powered by MQTT over WebSockets. Messages are delivered instantly between operators β no page refresh needed.
Browser A ββ(MQTT/WS)βββΊ Mosquitto Broker ββ(MQTT/WS)βββΊ Browser B
β
Python subscriber βββΊ SQLite (history)
- Mosquitto MQTT broker runs as a Docker sidecar, handling pub/sub routing
- mqtt.js in the browser connects via WebSocket for instant message delivery
- A Python MQTT subscriber thread persists messages to SQLite for chat history
- Chat rooms are per-award β each special callsign has its own channel
Set the MQTT_WS_URL environment variable to enable the chat tab:
# In .env
MQTT_WS_URL=wss://yourdomain.com/mqttWhen MQTT_WS_URL is not set, the chat tab is hidden and no MQTT connections are made. The nginx config included in the standalone deployment already proxies /mqtt to Mosquitto's WebSocket port.
Operators can send spots to a DX Cluster node directly from the activity dashboard, announcing that a special callsign is active on a specific frequency.
- An operator blocks a band/mode on the heatmap (spotting requires an active block)
- The DX Cluster Spot section appears below the heatmap with band/mode autofilled from the block
- The operator enters the spotted callsign, frequency, and an optional comment
- QuendAward connects to the configured DX Cluster node via Telnet and sends the spot command
DX de EA1RFI: 14025.0 EG90IARU QRV CW 1423Z
ββ cluster login ββ spotted ββ comment
Set the following environment variables to enable spotting:
# In .env
DX_CLUSTER_HOST=dxfun.com
DX_CLUSTER_PORT=8000
DX_CLUSTER_CALLSIGN=EA1RFI
DX_CLUSTER_PASSWORD= # Only if the cluster requires authenticationWhen DX_CLUSTER_HOST is not set, the send button shows a configuration error. The spot section is always visible but requires an active block to use. Clusters that require password authentication after the callsign login are supported via the optional DX_CLUSTER_PASSWORD variable.
QuendAward includes an optional Telegram bot that allows operators to manage band/mode blocks and receive real-time notifications directly from Telegram.
- Link operator accounts to Telegram via
/linkcommand - Block/unblock bands interactively with inline keyboards
- View current blocks for your selected award
- Real-time notifications when other operators block, unblock, or switch bands
- Chat mention alerts when someone @mentions you in the web chat
- Multi-language support (English, Spanish, Galician)
- Per-user settings for default award, notifications, and language
-
Create a Telegram bot:
- Open Telegram and message @BotFather
- Send
/newbotand follow the prompts - Copy the bot token (looks like
123456789:ABCdefGHIjklMNOpqrsTUVwxyz)
-
Configure the bot token:
# In .env TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz -
Run the bot (via Docker Compose or manually):
# Included automatically in docker-compose up # OR manually: python services/telegram_bot.py
-
Link your account: Open your bot on Telegram and send:
/link <your_callsign> <your_password> -
Set your default award:
/awards # List available awards /setaward <id> # Set your default award -
Manage blocks:
/block # Interactive block selection /myblocks # View your blocks /unblock # Release all your blocks /blocks # View all blocks for current award -
Configure preferences:
/notifications on # Enable notifications /lang en # Set language (en/es/gl) /status # View your settings
When linked operators have notifications enabled:
- Get notified when other operators block, unblock, or switch bands in your default award
- Receive alerts when someone @mentions your callsign in the web chat
- Notifications respect your language preference
Set your default award with /setaward <id> to receive relevant block notifications for that special callsign.
award_planner/
βββ app.py # Main application entry point
βββ config.py # Configuration constants
βββ database.py # Database compatibility layer
βββ Dockerfile
β
βββ core/ # Core modules
β βββ database.py # SQLite connection & schema
β βββ auth.py # Authentication & password hashing
β
βββ features/ # Feature modules
β βββ announcements.py # Admin announcements
β βββ awards.py # Special callsign management
β βββ blocks.py # Band/mode blocking logic
β βββ chat.py # Chat message persistence
β βββ dx_cluster.py # DX Cluster Telnet spotting
β βββ backup.py # Database backup/restore
β
βββ services/ # Background services
β βββ mqtt_subscriber.py # MQTT listener for chat persistence
β
βββ ui/ # User interface components
β βββ admin_panel.py # Admin panel tabs
β βββ components.py # Reusable UI components
β βββ charts.py # Plotly visualizations
β βββ chat_widget.py # Real-time chat (HTML/JS/CSS)
β βββ styles.py # Responsive CSS/JS
β
βββ i18n/ # Internationalization
β βββ translations.py # Translations (EN/ES/GL)
β
βββ mosquitto/ # MQTT broker config
β βββ config/
β βββ mosquitto.conf
β
βββ nginx/ # Reverse proxy config
β βββ nginx.conf
β
βββ docker-compose.yml
βββ docker-compose-standalone.yml
Bands: 160m, 80m, 60m, 40m, 30m, 20m, 17m, 15m, 12m, 10m, 8m, 6m, 2m, 70cm, SAT
Modes: SSB, CW, FT8, FT4, RTTY
| Role | Capabilities |
|---|---|
| Super Admin | Full access, configured via environment variables |
| Admin | Create operators, manage awards, announcements |
| Operator | Block/unblock bands, view dashboard, chat |
- Login with admin credentials
- Create Operators: Admin Panel β Create Operator
- Create Awards: Admin Panel β Special Callsigns
- Post Announcements: Admin Panel β Announcements
- Login with provided credentials
- Select the active award/special callsign
- Click on heatmap cells to block/unblock
- Check π for announcements
- Use the π¬ Chat tab to communicate with other operators in real time
- π¬π§ English
- πͺπΈ EspaΓ±ol
- π¬π± Galego (default)
Select language on the login page.
This project is licensed under the GNU General Public License v3.0 (GPLv3).
See LICENSE for details.
Daniel GarcΓa Coego (EA1RFI)
Contributions welcome! Please open an issue or pull request.