This guide covers operator-facing MQTT setup: broker configuration, Home Assistant auto-discovery, dashboards, automations, and troubleshooting.
Looking for the JSON schema or full topic list? See the canonical reference: DragonSync MQTT Payload Schema. It documents every topic and field and is updated alongside the code. This guide covers everything around the wire format — config, HA, broker setup, automations.
HA discovery is opt-in (mqtt_ha_enabled); the underlying JSON payloads are the same regardless of whether HA is involved.
In config.ini:
[SETTINGS]
# Enable MQTT output
mqtt_enabled = true
mqtt_host = 192.168.1.100
mqtt_port = 1883
# Authentication (optional)
mqtt_username = wardragon
mqtt_password = yourpassword
# Aggregate drone topic (all drones, single topic)
mqtt_topic = wardragon/drones
# Per-drone topics (wardragon/drone/<id>)
mqtt_per_drone_enabled = true
mqtt_per_drone_base = wardragon/drone
# ADS-B aircraft (when adsb_enabled = true)
mqtt_aircraft_enabled = false
mqtt_aircraft_topic = wardragon/aircraft
# FPV/RF signal alerts (when fpv_enabled = true)
mqtt_signals_enabled = false
mqtt_signals_topic = wardragon/signals
# Home Assistant auto-discovery (requires mqtt_per_drone_enabled = true)
mqtt_ha_enabled = true
mqtt_ha_prefix = homeassistant
mqtt_ha_device_base = wardragon_drone
mqtt_ha_signal_tracker = false
mqtt_ha_signal_id = signal_latest
# TLS (optional)
mqtt_tls = false
mqtt_ca_file =
mqtt_certfile =
mqtt_keyfile =
mqtt_tls_insecure = false
# Retain published state messages
mqtt_retain = trueNote: Per-drone topics are required for Home Assistant discovery. The aggregate topic alone is sufficient for plain MQTT consumers but cannot back HA entities (one entity needs a stable, dedicated topic).
Breaking change for MQTT subscribers (DragonSync v2.0+): kit-level system and service topics are now kit-scoped so multiple kits can share one broker without colliding. The unscoped legacy forms (
wardragon/system/attrs,wardragon/service/availability, etc.) are no longer published. If you subscribe to MQTT from a custom dashboard, Node-RED, scripts, or any tool, see the Subscription Migration section below. Drone, aircraft, and signal subscribers are unaffected.
Most useful topics for operators:
| Topic | Purpose |
|---|---|
wardragon/drones |
Aggregate drone stream — every drone update lands here |
wardragon/drone/<id> |
Per-drone state (required for HA discovery) |
wardragon/system/<kit_id>/attrs |
Kit telemetry (CPU, mem, GPS, SDR temps), scoped per kit |
wardragon/system/<kit_id>/availability |
online/offline per kit |
wardragon/aircraft |
ADS-B aircraft (when enabled) |
wardragon/signals |
FPV/RF signal alerts (when enabled) |
wardragon/service/<kit_id>/availability |
DragonSync process online/offline (LWT), scoped per kit |
homeassistant/... |
HA auto-discovery configs (when mqtt_ha_enabled) |
<kit_id> is the WarDragon kit's identifier (e.g. wardragon-G6PA14100J63). One kit per broker → one set of these topics. Multiple kits on the same broker → one set per kit, no collisions.
Three ways to subscribe to kit-level topics after the v2.0 schema change:
Wildcard (recommended for most consumers) — works for any number of kits, today or in the future:
wardragon/system/+/attrs (was: wardragon/system/attrs)
wardragon/system/+/state (was: wardragon/system/state)
wardragon/system/+/availability (was: wardragon/system/availability)
wardragon/service/+/availability (was: wardragon/service/availability)
The + matches any single topic segment. Single-kit operators see one stream of messages (same as before); multi-kit operators see one message per kit cleanly distinguished by the kit ID in the topic path.
Pin a specific kit — when you only care about one:
wardragon/system/wardragon-G6PA14100J63/attrs
Catch-all wardragon/# — receives every topic the kit publishes. If your consumer was already on wardragon/#, no change is needed. Best for logging, archival, debug tools, and any consumer that already filters by topic in its own code.
Drone, aircraft, and signal topics are unchanged from v1 — no subscription updates needed for those.
A drone payload looks like:
{
"id": "drone-F6Q8D244C00CL2KF",
"description": "DJI O4 (Decrypted)",
"lat": 27.8002846,
"lon": -82.6686196,
"alt": 65531.0,
"speed": 0.0,
"direction": 270.0,
"rssi": -117.0,
"id_type": "Serial Number (ANSI/CTA-2063-A)",
"freq_mhz": 5756.5,
"transport": "",
"seen_by": "wardragon-G6PA14100J63",
"track_type": "drone"
}Full schema reference: every topic, every field, types, always-present vs. conditional notes, plus signal/aircraft/system payloads — see DragonSync MQTT Payload Schema. That doc lives alongside the code that produces these payloads and is the source of truth.
When mqtt_ha_enabled = true and mqtt_per_drone_enabled = true, DragonSync publishes MQTT discovery configs that auto-create entities in HA. The state topics are the same JSON topics documented above; HA discovery adds the metadata HA needs to render entities.
| Topic pattern | Purpose |
|---|---|
homeassistant/device_tracker/wardragon_drone_<id>/config |
Main drone tracker |
homeassistant/device_tracker/wardragon_drone_<id>_pilot/config |
Pilot location tracker |
homeassistant/device_tracker/wardragon_drone_<id>_home/config |
Home location tracker |
homeassistant/sensor/wardragon_drone_<id>_<suffix>/config |
Per-drone telemetry sensors |
homeassistant/device_tracker/wardragon_drone_system/config |
WarDragon kit GPS tracker |
homeassistant/sensor/wardragon_drone_system_<suffix>/config |
WarDragon kit telemetry sensors |
homeassistant/device_tracker/wardragon_signal_signal_latest_<seen_by>/config |
Signal alert tracker (when mqtt_ha_signal_tracker = true) |
All discovery messages are retained, so HA picks them up immediately on (re)connect.
For each detected drone, HA creates a single device with these entities:
| Entity | Class | Source field | Notes |
|---|---|---|---|
device_tracker.<id> |
device_tracker | latitude, longitude |
Drone position on map |
device_tracker.pilot-<tail> |
device_tracker | pilot_attrs JSON | Pilot location |
device_tracker.home-<tail> |
device_tracker | home_attrs JSON | Home location |
sensor.<id>_lat |
sensor (°) | lat |
|
sensor.<id>_lon |
sensor (°) | lon |
|
sensor.<id>_alt |
sensor (m, distance) | alt |
Altitude HAE |
sensor.<id>_speed |
sensor (m/s, speed) | speed |
|
sensor.<id>_vspeed |
sensor (m/s) | vspeed |
Vertical speed |
sensor.<id>_height |
sensor (m) | height |
AGL |
sensor.<id>_dir |
sensor (°) | direction |
Course |
sensor.<id>_pilot_lat |
sensor (°) | pilot_lat |
|
sensor.<id>_pilot_lon |
sensor (°) | pilot_lon |
|
sensor.<id>_home_lat |
sensor (°) | home_lat |
|
sensor.<id>_home_lon |
sensor (°) | home_lon |
|
sensor.<id>_rssi |
sensor (dBm, signal_strength) | rssi |
|
sensor.<id>_freq |
sensor (MHz) | freq_mhz |
|
sensor.<id>_ua_type |
sensor | ua_type_name |
|
sensor.<id>_op_id |
sensor | operator_id |
|
sensor.<id>_transport |
sensor | transport |
Link layer (empty for OcuSync) |
sensor.<id>_description |
sensor | description |
Self-ID text |
sensor.<id>_main |
sensor | description |
Friendly device label |
Tail convention:
<tail>is the drone ID with any leadingdrone-stripped. So a drone withid = drone-ABC123produces a pilot entity namedpilot-ABC123.
A single device named "WarDragon System" with these entities:
| Entity | Class | Source field |
|---|---|---|
device_tracker.wardragon_pro |
device_tracker | system/attrs JSON |
sensor.cpu_usage |
sensor (%) | cpu_usage |
sensor.memory_available |
sensor (MB) | memory_available_mb |
sensor.memory_total |
sensor (MB) | memory_total_mb |
sensor.disk_used |
sensor (MB) | disk_used_mb |
sensor.disk_total |
sensor (MB) | disk_total_mb |
sensor.system_temp |
sensor (°C, temperature) | temperature_c |
sensor.uptime |
sensor (h) | uptime_s (converted from seconds) |
sensor.ground_speed |
sensor (m/s, speed) | speed_mps |
sensor.course |
sensor (°) | track_deg |
sensor.pluto_temp |
sensor (°C, temperature) | pluto_temp_c |
sensor.zynq_temp |
sensor (°C, temperature) | zynq_temp_c |
Only published when mqtt_signals_enabled = true and mqtt_ha_signal_tracker = true. One device_tracker per source sensor (<seen_by>):
| Entity | Source |
|---|---|
device_tracker.signal_alert_<seen_by> |
wardragon/signals/<seen_by> JSON |
ADS-B aircraft do not get HA entities — too high-volume. Subscribe to wardragon/aircraft directly if you need them.
If you prefer manual control, here's an example referencing the real field names:
mqtt:
device_tracker:
- name: "Drone F6Q8D244C00CL2KF"
state_topic: "wardragon/drone/drone-F6Q8D244C00CL2KF/state"
json_attributes_topic: "wardragon/drone/drone-F6Q8D244C00CL2KF"
payload_home: "home"
payload_not_home: "not_home"
availability_topic: "wardragon/drone/drone-F6Q8D244C00CL2KF/availability"
payload_available: "online"
payload_not_available: "offline"
sensor:
- name: "Drone Altitude"
state_topic: "wardragon/drone/drone-F6Q8D244C00CL2KF"
value_template: "{{ value_json.alt }}"
unit_of_measurement: "m"
- name: "Drone Speed"
state_topic: "wardragon/drone/drone-F6Q8D244C00CL2KF"
value_template: "{{ value_json.speed }}"
unit_of_measurement: "m/s"
- name: "Drone Description"
state_topic: "wardragon/drone/drone-F6Q8D244C00CL2KF"
value_template: "{{ value_json.description }}"type: map
entities:
- device_tracker.drone_f6q8d244c00cl2kf
- device_tracker.pilot_f6q8d244c00cl2kf
default_zoom: 15
dark_mode: truetype: entities
title: Detected Drone
entities:
- entity: sensor.drone_f6q8d244c00cl2kf_main
name: Description
- entity: sensor.drone_f6q8d244c00cl2kf_alt
name: Altitude
- entity: sensor.drone_f6q8d244c00cl2kf_speed
name: Speed
- entity: sensor.drone_f6q8d244c00cl2kf_rssi
name: Signal
- entity: sensor.drone_f6q8d244c00cl2kf_freq
name: FrequencyShow all detected drones dynamically:
type: custom:auto-entities
card:
type: entities
title: Active Drones
filter:
include:
- domain: device_tracker
entity_id: "*drone*"
sort:
method: last_changed
reverse: trueThe field names below match the real JSON schema documented above.
automation:
- alias: "Drone Detected Alert"
trigger:
- platform: mqtt
topic: "wardragon/drones"
action:
- service: notify.mobile_app
data:
title: "Drone Detected!"
message: >
{{ trigger.payload_json.description or 'Unknown drone' }}
({{ trigger.payload_json.id }})
at {{ trigger.payload_json.lat }}, {{ trigger.payload_json.lon }}automation:
- alias: "DJI Drone Alert"
trigger:
- platform: mqtt
topic: "wardragon/drones"
condition:
- condition: template
value_template: >
{{ 'DJI' in (trigger.payload_json.description or '') }}
action:
- service: notify.mobile_app
data:
title: "DJI Drone Detected"
message: "{{ trigger.payload_json.description }} ({{ trigger.payload_json.id }})"automation:
- alias: "Drone in Restricted Zone"
trigger:
- platform: mqtt
topic: "wardragon/drones"
condition:
- condition: template
value_template: >
{{ trigger.payload_json.lat | float > 40.710 and
trigger.payload_json.lat | float < 40.715 and
trigger.payload_json.lon | float > -74.010 and
trigger.payload_json.lon | float < -74.005 }}
action:
- service: notify.all
data:
title: "ALERT: Drone in Restricted Zone"
message: >
{{ trigger.payload_json.description or 'Unknown' }}
at {{ trigger.payload_json.lat }}, {{ trigger.payload_json.lon }}automation:
- alias: "RID Verified Drone"
trigger:
- platform: mqtt
topic: "wardragon/drones"
condition:
- condition: template
value_template: "{{ trigger.payload_json.rid_lookup_success == true }}"
action:
- service: notify.mobile_app
data:
title: "RID Verified"
message: >
{{ trigger.payload_json.rid_make }} {{ trigger.payload_json.rid_model }}
({{ trigger.payload_json.id }})# Install on Ubuntu/Debian
sudo apt install mosquitto mosquitto-clients
# Create password file
sudo mosquitto_passwd -c /etc/mosquitto/passwd wardragon
# Configure
sudo nano /etc/mosquitto/mosquitto.confAdd to mosquitto.conf:
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd
- Settings → Add-ons → Install "Mosquitto broker"
- Configure users in the add-on configuration
- Start the add-on
# Watch every WarDragon message
mosquitto_sub -h <broker> -u <user> -P <pass> -t "wardragon/#" -v
# Watch a specific drone
mosquitto_sub -h <broker> -t "wardragon/drone/drone-F6Q8D244C00CL2KF" -v
# Watch the aggregate drone feed
mosquitto_sub -h <broker> -t "wardragon/drones" -v
# Watch the kit
mosquitto_sub -h <broker> -t "wardragon/system/#" -vmosquitto_sub -h <broker> -t "homeassistant/#" -vlistener 8883
cafile /etc/mosquitto/ca.crt
certfile /etc/mosquitto/server.crt
keyfile /etc/mosquitto/server.key
require_certificate false
[SETTINGS]
mqtt_enabled = true
mqtt_host = 192.168.1.100
mqtt_port = 8883
mqtt_tls = true
mqtt_ca_file = /path/to/ca.crt
mqtt_certfile = /path/to/client.crt
mqtt_keyfile = /path/to/client.key
mqtt_tls_insecure = falseDragonSync uses async MQTT connect with automatic retry. If the broker is temporarily unavailable:
- CoT output continues normally
- Other outputs (Lattice, HTTP API) remain operational
- MQTT reconnects in the background (2–30s backoff)
- Messages are not queued during disconnection — anything published while offline is dropped
-
Verify broker connectivity:
mosquitto_pub -h <broker> -t "test" -m "hello" mosquitto_sub -h <broker> -t "test"
-
Check DragonSync MQTT config:
grep mqtt /home/dragon/DragonSync/config.ini journalctl -u dragonsync | grep -i mqtt -
Check the HA MQTT integration: Settings → Devices & Services → MQTT.
- Verify
mqtt_ha_enabled = trueandmqtt_per_drone_enabled = true(HA discovery requires per-drone topics). - Watch the discovery topic:
mosquitto_sub -h <broker> -t "homeassistant/#" -v
- Restart Home Assistant after the first discovery message.
- Verify username/password in DragonSync config match broker.
- Check broker logs for auth errors.
- Test credentials with
mosquitto_pub/mosquitto_sub.
- For OcuSync drones:
transportis intentionally empty (the receiver doesn't tag link layer for OcuSync).descriptionshould be populated with the detected DJI model and OcuSync generation (e.g.DJI Mini 2 (O2),DJI Mavic 3 (O3),DJI Mini 5). If empty, yourdji-receivermay be on an older version — pull the latest fromalphafox02/dragonsdr_dji_droneid. - For BLE / WiFi RID drones:
descriptioncarries the operator's self-ID text and may be empty if they didn't set one. rid_make/rid_model/rid_statusonly populate when FAA RID lookup succeeds (requiresrid_enabled = trueand a working RID database / API).
- DragonSync MQTT Payload Schema — canonical wire-format reference (every topic, every field)
- DragonSync Configuration
- WarDragonAnalytics
- TAK Integration