DX Monitor
Real-time DX Cluster monitor with intelligent Telegram alerts and a web dashboard. Runs as a Docker container on any NAS or Linux server, or as a standalone Windows executable.
DX Monitor connects to any DX Cluster (CC11/VE7CC protocol), cross-references incoming spots against your own logbook, and fires Telegram alerts only for contacts that are relevant to you — new DXCC, new band, new mode, or unconfirmed QSLs. The web dashboard lets you configure everything without touching any file.
DX Monitor
Monitor de DX Cluster en tiempo real con alertas Telegram inteligentes y dashboard web. Se ejecuta como contenedor Docker en cualquier NAS o servidor Linux, o como ejecutable standalone para Windows.
DX Monitor se conecta a cualquier DX Cluster (protocolo CC11/VE7CC), cruza los spots entrantes con tu propio libro de QSO y lanza alertas Telegram solo para los contactos relevantes: nuevo DXCC, nueva banda, nuevo modo o QSL pendiente. El dashboard web permite configurarlo todo sin tocar ningún fichero.
Features
Funcionalidades
Architecture
A single Python process runs Flask (web) and background threads (cluster, log reload, CTY update) together in one container. No inter-process communication needed.
Docker (NAS/Linux)
- Image: ea3tb/dx-monitor:latest
- Single container, port 8765
- Volume: dx_docker_data
- Hostfs mounted read-only at /hostfs
- No env vars — config via dashboard
- Restart: unless-stopped
Windows (standalone)
- DXMonitor.exe — no install
- System tray icon, auto-opens browser
- Same Flask + thread architecture
- Swisslog MDB: Access Engine 2016
- Built with PyInstaller
- Windows 10/11 64-bit
Arquitectura
Un único proceso Python ejecuta Flask (web) y hilos de fondo (cluster, recarga de log, actualización CTY) en el mismo contenedor. Sin comunicación entre procesos.
Docker (NAS/Linux)
- Imagen: ea3tb/dx-monitor:latest
- Contenedor único, puerto 8765
- Volumen: dx_docker_data
- Hostfs montado solo lectura en /hostfs
- Sin variables de entorno
- Restart: unless-stopped
Windows (standalone)
- DXMonitor.exe — sin instalación
- Icono en bandeja, abre navegador automáticamente
- Misma arquitectura Flask + hilos
- Swisslog MDB: Access Engine 2016
- Compilado con PyInstaller
- Windows 10/11 64-bit
DX Cluster
DX Monitor speaks the CC11/VE7CC protocol over plain TCP. One persistent connection per session.
Connection flow
Auto-connect
If cluster_host and cluster_login are set in config.json, the cluster thread connects automatically on startup. Otherwise it waits for the Connect button on the dashboard.
Keepalive & reconnect
Sends sh/dx 1 every 180 seconds to prevent the server from closing the connection. On unexpected disconnect, waits 30 seconds and reconnects automatically. Manual disconnect (via dashboard) stops the reconnect loop.
Pause/resume
Connect and Disconnect buttons act on a threading.Event (_cluster_paused). The cluster thread respects this flag without restarting the process.
Anti-duplicate
Spots for the same (callsign, band, mode) are suppressed within a 10-minute sliding window. This prevents alert floods when multiple stations spot the same DX simultaneously.
hfpredictor.cronux.net:7304 — any CC11-compatible cluster works.
DX Cluster
DX Monitor habla el protocolo CC11/VE7CC sobre TCP plano. Una conexión persistente por sesión.
Flujo de conexión
Auto-conexión
Si cluster_host y cluster_login están en config.json, el hilo del cluster conecta automáticamente al arrancar. Si no, espera al botón Conectar del dashboard.
Keepalive y reconexión
Envía sh/dx 1 cada 180 segundos para evitar que el servidor cierre la conexión. Ante una desconexión inesperada, espera 30 s y reconecta automáticamente. La desconexión manual (desde el dashboard) detiene el bucle de reconexión.
Pausa/reanudación
Los botones Conectar y Desconectar actúan sobre un threading.Event (_cluster_paused). El hilo del cluster respeta este flag sin reiniciar el proceso.
Anti-duplicados
Los spots para el mismo (indicativo, banda, modo) se suprimen dentro de una ventana deslizante de 10 minutos. Evita inundaciones de alertas cuando varias estaciones spotean el mismo DX simultáneamente.
hfpredictor.cronux.net:7304 — funciona cualquier cluster compatible CC11.
Telegram Alerts
DX Monitor sends formatted Telegram messages via the Bot API using your personal bot token and chat ID. All alert parameters are configured from the dashboard — no manual file editing.
Alert types
Alert content
Each alert includes: DX callsign and entity, frequency, band, mode, spotter, azimuth Short Path / Long Path (degrees), distance (km), and timestamp in local or UTC time.
Mode inference logic
Mode is determined in this order:
- Spot comment explicitly mentions CW / SSB / RTTY / FT8 / FT4 → use that mode
- No mode in comment + frequency matches FT8 or FT4 nominal (±1 kHz) → infer FT8 / FT4
- No mode, not FT8/FT4 → infer CW or SSB by band plan. Never RTTY.
- Frequency in exclusive CW segment → force CW regardless of comment
Language & time
Alert messages are sent in Spanish or English (alert_lang). Timestamps use the configured timezone (40+ grouped zones + manual IANA entry) in local or UTC mode (time_mode).
https://api.telegram.org/bot<TOKEN>/getUpdates
Alertas Telegram
DX Monitor envía mensajes Telegram formateados via Bot API usando tu token de bot personal y tu chat ID. Todos los parámetros se configuran desde el dashboard, sin editar ficheros.
Tipos de alerta
Contenido de la alerta
Cada alerta incluye: indicativo y entidad DX, frecuencia, banda, modo, spotter, azimut vía corta / vía larga (grados), distancia (km) y timestamp en hora local o UTC.
Lógica de inferencia de modo
El modo se determina en este orden:
- El comentario del spot menciona explícitamente CW / SSB / RTTY / FT8 / FT4 → se usa ese modo
- Sin modo en el comentario + frecuencia coincide con nominal FT8 o FT4 (±1 kHz) → se infiere FT8 / FT4
- Sin modo, no FT8/FT4 → se infiere CW o SSB por band plan. Nunca RTTY.
- Frecuencia en segmento exclusivo CW → se fuerza CW independientemente del comentario
Idioma y hora
Los mensajes se envían en español o inglés (alert_lang). Los timestamps usan la zona horaria configurada (40+ zonas agrupadas + campo IANA manual) en modo local o UTC (time_mode).
https://api.telegram.org/bot<TOKEN>/getUpdates
Log Sources
DX Monitor reads your logbook to know which DXCCs, bands and modes you have already worked. Four formats are supported and selected from the dashboard.
| Format | Notes |
|---|---|
| HRD XML | Ham Radio Deluxe — automatic background export. DX Monitor watches a directory and reloads XML files at the configured interval. Path navigable from the dashboard browser. |
| Swisslog MDB | Microsoft Access database (.mdb). Requires Access Database Engine 2016 on Windows. The app offers automatic installation if missing. |
| Log4OM SQLite | Log4OM v2 — reads the SQLite database directly. No export needed. |
| ADIF | Universal format exported by any logging software. Reload on demand or at interval. |
Reload interval
Configurable in minutes from the dashboard. The log thread wakes at each interval, reads the source, and updates the in-memory DXCC/band/mode database used by the alert engine.
Docker path handling
Inside the container, the host filesystem is mounted read-only at /hostfs. The function host_path() transparently converts host paths entered in the dashboard to their /hostfs-prefixed equivalents. The directory browser (📂) navigates /hostfs and displays the real host paths.
Fuentes de log
DX Monitor lee tu libro de QSO para saber qué DXCCs, bandas y modos ya tienes trabajados. Se soportan cuatro formatos, seleccionables desde el dashboard.
| Formato | Notas |
|---|---|
| HRD XML | Ham Radio Deluxe — exportación automática en segundo plano. DX Monitor vigila un directorio y recarga los ficheros XML al intervalo configurado. Ruta navegable desde el explorador del dashboard. |
| Swisslog MDB | Base de datos Microsoft Access (.mdb). Requiere Access Database Engine 2016 en Windows. La app ofrece instalación automática si falta. |
| Log4OM SQLite | Log4OM v2 — lee la base de datos SQLite directamente. Sin necesidad de exportar. |
| ADIF | Formato universal exportado por cualquier programa de log. Recarga bajo demanda o al intervalo configurado. |
Intervalo de recarga
Configurable en minutos desde el dashboard. El hilo de log se activa en cada intervalo, lee la fuente y actualiza la base de datos en memoria de DXCC/banda/modo que usa el motor de alertas.
Paths en Docker
Dentro del contenedor, el filesystem del host se monta en solo lectura en /hostfs. La función host_path() convierte de forma transparente los paths del host introducidos en el dashboard a sus equivalentes prefijados con /hostfs. El explorador de directorios (📂) navega /hostfs y muestra los paths reales del host.
Real-time Log & Alerts
The bottom of the dashboard shows two live panels updated by SSE — no page refresh, no polling.
How SSE works
No watcher thread. _escribir_status() writes status.json atomically with os.replace and calls sse_push() directly — zero latency between event and browser update. If the connection drops, the dashboard reconnects automatically after 5 seconds.
Live DX alerts panel
Each incoming alert that passes all filters appears instantly: callsign, entity, frequency, band, mode, azimuth SP/LP, distance, spotter, and timestamp. Anti-duplicate window (10 min) prevents repeated entries for the same spot.
System log panel
Last 40 lines of dx_monitor.log, color-coded by level:
Log lines are translated to the dashboard language (ES/EN), including internal key names like pais_nuevo, modo_sin_qsl, etc. Log timestamps respect the timezone configured in config.json.
Log y alertas en tiempo real
La parte inferior del dashboard muestra dos paneles en vivo actualizados por SSE — sin recarga de página, sin polling.
Cómo funciona SSE
Sin hilo vigilante. _escribir_status() escribe status.json atómicamente con os.replace y llama a sse_push() directamente — latencia cero entre evento y actualización en el navegador. Si se pierde la conexión, el dashboard reconecta automáticamente tras 5 segundos.
Panel de alertas DX en vivo
Cada alerta entrante que pasa todos los filtros aparece al instante: indicativo, entidad, frecuencia, banda, modo, azimut SP/LP, distancia, spotter y timestamp. La ventana antiduplicados (10 min) evita entradas repetidas para el mismo spot.
Panel de log del sistema
Últimas 40 líneas de dx_monitor.log, coloreadas por nivel:
Las líneas del log se traducen al idioma del dashboard (ES/EN), incluidas las claves internas como pais_nuevo, modo_sin_qsl, etc. Los timestamps respetan la zona horaria configurada en config.json.
Docker Installation
Quick start
Create a docker-compose.yml:
services: dxmonitor: image: ea3tb/dx-monitor:latest container_name: dx_monitor_docker restart: unless-stopped ports: - "8765:8765" volumes: - /:/hostfs:ro - dx_docker_data:/opt/dx_monitor_docker networks: - dx_net volumes: dx_docker_data: driver: local networks: dx_net: driver: bridge
# Deploy docker compose up -d # Open dashboard http://<nas-ip>:8765
Useful commands
# Real-time logs docker logs -f dx_monitor_docker # Status docker compose ps # Stop / restart docker compose down docker compose up -d # Shell access docker exec -it dx_monitor_docker bash # View active config docker exec dx_monitor_docker cat /opt/dx_monitor_docker/config.json
Update to latest image
# Pull new image and redeploy
docker compose pull
docker compose up -d
config.json is created with empty defaults. Fill in callsign, log path, cluster and Telegram from the dashboard — no file editing needed.
Instalación Docker
Inicio rápido
Crea un docker-compose.yml:
services: dxmonitor: image: ea3tb/dx-monitor:latest container_name: dx_monitor_docker restart: unless-stopped ports: - "8765:8765" volumes: - /:/hostfs:ro - dx_docker_data:/opt/dx_monitor_docker networks: - dx_net volumes: dx_docker_data: driver: local networks: dx_net: driver: bridge
# Desplegar docker compose up -d # Abrir dashboard http://<ip-del-nas>:8765
Comandos útiles
# Logs en tiempo real docker logs -f dx_monitor_docker # Estado docker compose ps # Parar / reiniciar docker compose down docker compose up -d # Acceso al shell docker exec -it dx_monitor_docker bash # Ver config activa docker exec dx_monitor_docker cat /opt/dx_monitor_docker/config.json
Actualizar a la última imagen
# Descargar nueva imagen y redesplegar
docker compose pull
docker compose up -d
config.json se crea con defaults vacíos. Rellena indicativo, ruta del log, cluster y Telegram desde el dashboard — sin editar ficheros.
Configuration
All settings are stored in /opt/dx_monitor_docker/config.json and edited from the dashboard. Fields save automatically on blur.
| Field | Description |
|---|---|
| callsign | Your amateur radio callsign |
| locator | Maidenhead grid locator — auto-calculates lat/lon for azimuth and distance |
| cluster_host | DX Cluster hostname or IP |
| cluster_port | TCP port (usually 7300 or 7304) |
| cluster_login | Login callsign sent to the cluster |
| cluster_password | Cluster password, hideable with 👁 button |
| telegram_token | Bot API token, hideable with 👁 button |
| telegram_chat_id | Destination chat ID |
| alert_lang | Alert message language: es or en |
| timezone | 40+ grouped zones + manual IANA entry (e.g. Europe/Madrid) |
| time_mode | local (per timezone) or utc |
| hrd_xml_dir | Directory containing HRD XML export files |
| hrd_xml_glob | Glob pattern to match XML files (e.g. *.xml) |
Configuración
Todos los ajustes se guardan en /opt/dx_monitor_docker/config.json y se editan desde el dashboard. Los campos se guardan automáticamente al perder el foco.
| Campo | Descripción |
|---|---|
| callsign | Tu indicativo de radioaficionado |
| locator | Locator Maidenhead — calcula automáticamente lat/lon para azimut y distancia |
| cluster_host | Hostname o IP del DX Cluster |
| cluster_port | Puerto TCP (habitualmente 7300 o 7304) |
| cluster_login | Indicativo de login enviado al cluster |
| cluster_password | Contraseña del cluster, ocultable con el botón 👁 |
| telegram_token | Token de la Bot API, ocultable con el botón 👁 |
| telegram_chat_id | Chat ID de destino |
| alert_lang | Idioma de las alertas: es o en |
| timezone | 40+ zonas agrupadas + campo IANA manual (ej. Europe/Madrid) |
| time_mode | local (según timezone) o utc |
| hrd_xml_dir | Directorio con los ficheros de exportación XML de HRD |
| hrd_xml_glob | Patrón glob para los ficheros XML (ej. *.xml) |
Alert Filters
Stored in flags.json. All toggles are available from the dashboard and take effect immediately without restarting.
| Filter | Values | Description |
|---|---|---|
| iaru_region | R1 / R2 / R3 | Band plan used for mode inference and frequency validation |
| bandas_activas | 160m – 23cm | Only spots on selected bands trigger alerts |
| modos_activos | CW, SSB, RTTY, FT8, FT4 | Only spots in selected modes trigger alerts |
| pais_nuevo | on/off | Alert for new DXCC entity |
| pais_trabajado | on/off | Alert for worked DXCC without QSL |
| banda_nueva | on/off | Alert for new band on known DXCC |
| banda_sin_qsl | on/off | Alert for band worked, QSL pending |
| modo_nuevo | on/off | Alert for new mode |
| modo_sin_qsl | on/off | Alert for mode worked, QSL pending |
Filtros de alerta
Guardados en flags.json. Todos los toggles están disponibles desde el dashboard y surten efecto inmediatamente sin reiniciar.
| Filtro | Valores | Descripción |
|---|---|---|
| iaru_region | R1 / R2 / R3 | Band plan usado para inferencia de modo y validación de frecuencia |
| bandas_activas | 160m – 23cm | Solo los spots en las bandas seleccionadas generan alertas |
| modos_activos | CW, SSB, RTTY, FT8, FT4 | Solo los spots en los modos seleccionados generan alertas |
| pais_nuevo | on/off | Alerta para nueva entidad DXCC |
| pais_trabajado | on/off | Alerta para DXCC trabajado sin QSL |
| banda_nueva | on/off | Alerta para nueva banda en DXCC conocido |
| banda_sin_qsl | on/off | Alerta para banda trabajada con QSL pendiente |
| modo_nuevo | on/off | Alerta para nuevo modo |
| modo_sin_qsl | on/off | Alerta para modo trabajado con QSL pendiente |