MQTT Remote Control

The MQTT Manager now supports remote control of EliteG19s via MQTT messages, implementing all methods from the IRemoteControl interface.

Implementation

The MQTT command handler uses a direct dispatch pattern with a dictionary mapping command names to delegates, providing type-safe, high-performance command execution without reflection.

Topic Structure

Commands are sent to topics following this pattern:

eliteg19s/journal/{commander_name}/command/{CommandName}

Where:

Message Examples

Simple command (no arguments):

Topic: eliteg19s/journal/cmdr_example/command/VolumeUp
Payload: (empty)

Command with a named property (preferred):

Topic: eliteg19s/journal/cmdr_example/command/ChangeVolume
Payload: {"delta": 10}

Play radio station (named property form):

Topic: eliteg19s/journal/cmdr_example/command/ListenToRadiostation
Payload: {"station": "BBC Radio 1"}

Note: Do not rely on positional args arrays; use named properties as shown above.

String command (plain text form — normalized to { "value": "..." }):

Topic: eliteg19s/journal/cmdr_example/command/ShowMessage
Payload: Hello from MQTT!

Response Topics

Command execution results are published to:

eliteg19s/journal/{commander_name}/command/{CommandName}/result

Behavior summary:

This lets callers perform request/response style interactions over MQTT: publish a command, then listen on the matching {CommandName}/result topic for the JSON result or an error object.

Example result payloads:

json { "status": "OK" }

json { "system": "Elite Dangerous", "commander": "CmdrExample", "location": "Sol" }

json { "error": "Unknown command: FooBar" }

Available Commands

Audio Control

Music Control

Music Sources

Screen Control

Button Simulation

Space Traffic Control

Navigation & GPS

Shopping List

Orrery Control

Commodity Search

Interactive Messages

Status Queries

System

Example Usage with Python

import paho.mqtt.client as mqtt
import json

# Connect to MQTT broker
client = mqtt.Client()
client.connect("localhost", 1883, 60)

# Simple command
client.publish("eliteg19s/journal/cmdr_example/command/VolumeUp", "")

# Command with named-property arguments (preferred)
payload = json.dumps({"delta": 15})
client.publish("eliteg19s/journal/cmdr_example/command/ChangeVolume", payload)

# String command (plain text form is normalized to { "value": "..." })
client.publish("eliteg19s/journal/cmdr_example/command/ShowMessage", "Hello Commander!")

# Play radio station (named property preferred)
payload = json.dumps({"station": "BBC Radio 1"})
client.publish("eliteg19s/journal/cmdr_example/command/ListenToRadiostation", payload)

# Listen for result (optional) - subscribe to the result topic before publishing
def on_message(client, userdata, msg):
  print("Result topic:", msg.topic, msg.payload.decode())

client.subscribe("eliteg19s/journal/cmdr_example/command/ChangeVolume/result")
client.on_message = on_message

client.disconnect()

Example Usage with mosquitto_pub

# Simple command
mosquitto_pub -t "eliteg19s/journal/cmdr_example/command/VolumeUp" -m ""

# Command with named-property arguments (preferred)
mosquitto_pub -t "eliteg19s/journal/cmdr_example/command/ChangeVolume" -m '{"delta": 10}'

# String message
mosquitto_pub -t "eliteg19s/journal/cmdr_example/command/ShowMessage" -m "Test message"

# Switch screen (named property)
mosquitto_pub -t "eliteg19s/journal/cmdr_example/command/SwitchScreen" -m '{"screen": "Menu"}'

# Optionally listen for the result in another terminal:
mosquitto_sub -t "eliteg19s/journal/cmdr_example/command/ChangeVolume/result"

Security Considerations

Troubleshooting

Commands Not Working

  1. Check that MQTT is enabled in EliteG19s options
  2. Verify the commander name in the topic matches the current commander
  3. Check the EliteG19s logs for MQTT connection status
  4. Ensure the command name exactly matches the IRemoteControl method name

Viewing Logs

Enable verbose logging in EliteG19s to see:

Home Assistant Timer Integration

Home Assistant does not currently create native timer entities from MQTT discovery payloads. Instead, publish the EliteG19s alarm attributes via MQTT (already handled by the application) and let Home Assistant manage its own helpers.

Automating a "Next Alarm" Timer Helper

  1. Create a timer helper in Home Assistant, for example timer.eliteg19s_next_alarm.
  2. (Optional but recommended) Create a few input_text helpers to surface alarm metadata:
  1. Add the automation below, adjusting the MQTT topic and entity IDs to match your setup.
alias: EliteG19s – Sync Next Alarm Timer
mode: restart
trigger:
  - platform: mqtt
    topic: eliteg19s/journal/magicmau/alarms/next/attributes
variables:
  payload: "{{ trigger.payload_json | default({}) }}"
  remaining: "{{ payload.remaining_seconds | int(0) }}"
  has_alarm: "{{ payload.alarm_id is defined and payload.alarm_id }}"
action:
  - choose:
      - conditions: "{{ has_alarm and remaining > 0 }}"
        sequence:
          - service: timer.start
            target:
              entity_id: timer.eliteg19s_next_alarm
            data:
              duration:
                seconds: "{{ remaining }}"
          - service: input_text.set_value
            target:
              entity_id: input_text.eliteg19s_alarm_type
            data:
              value: "{{ payload.type | default('Unknown') }}"
          - service: input_text.set_value
            target:
              entity_id: input_text.eliteg19s_alarm_name
            data:
              value: "{{ payload.name | default('') }}"
          - service: input_text.set_value
            target:
              entity_id: input_text.eliteg19s_alarm_destination
            data:
              value: "{{ payload.destination | default('') }}"
    default:
      - service: timer.cancel
        target:
          entity_id: timer.eliteg19s_next_alarm
      - service: timer.finish
        target:
          entity_id: timer.eliteg19s_next_alarm
      - service: input_text.set_value
        target:
          entity_id:
            - input_text.eliteg19s_alarm_type
            - input_text.eliteg19s_alarm_name
            - input_text.eliteg19s_alarm_destination
        data:
          value: ""

Node-RED Flow for Per-Alarm Timers

The flow below listens to each per-alarm MQTT attributes topic (eliteg19s/journal/<commander>/alarms/<alarm_id>/attributes) and starts or cancels a matching Home Assistant timer helper. Create timer helpers following the pattern timer.elite_alarm_<alarm_id> (the <alarm_id> segment is taken directly from the MQTT topic) before enabling the flow. Update the MQTT topic, broker, and Home Assistant server details after importing.

[
  {
    "id": "d6f6c1d7d3b40a9f",
    "type": "tab",
    "label": "EliteG19s Alarms → HA Timers",
    "disabled": false,
    "info": "Synchronise per-alarm MQTT attributes with Home Assistant timer helpers."
  },
  {
    "id": "1bc1c2f0d3d8c9b1",
    "type": "mqtt in",
    "z": "d6f6c1d7d3b40a9f",
    "name": "EliteG19s Alarm Attributes",
    "topic": "eliteg19s/journal/magicmau/alarms/+/attributes",
    "qos": "0",
    "datatype": "auto",
    "broker": "f1b0f0f8a2d5c3f4",
    "nl": false,
    "rap": true,
    "rh": 0,
    "x": 220,
    "y": 120,
    "wires": [["9b7fc4f4b1a3d6ce"]]
  },
  {
    "id": "9b7fc4f4b1a3d6ce",
    "type": "json",
    "z": "d6f6c1d7d3b40a9f",
    "name": "Parse JSON",
    "property": "payload",
    "action": "obj",
    "pretty": false,
    "x": 490,
    "y": 120,
    "wires": [["4b6f55bcb4a43e42"]]
  },
  {
    "id": "4b6f55bcb4a43e42",
    "type": "function",
    "z": "d6f6c1d7d3b40a9f",
    "name": "Prepare Timer Calls",
    "func": "const match = msg.topic.match(/alarms\\/([^/]+)\\/attributes$/);
if (!match) {
    return null;
}

const timerId = match[1];
const remaining = Number(msg.payload?.remaining_seconds ?? 0);

const entityId = `timer.elite_alarm_${timerId}`;

const startMsg = {
    entity_id: entityId,
    payload: {
        data: {
            entity_id: entityId,
            duration: {
                seconds: remaining
            }
        }
    }
};

const cancelMsg = {
    entity_id: entityId,
    payload: {
        data: {
            entity_id: entityId
        }
    }
};

if (remaining > 0) {
    return [startMsg, null];
}

return [null, cancelMsg];
",
    "outputs": 2,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 770,
    "y": 120,
    "wires": [["e9b9352fa3a9b87a"], ["d87b2f0aa5c1a4e3"]]
  },
  {
    "id": "e9b9352fa3a9b87a",
    "type": "api-call-service",
    "z": "d6f6c1d7d3b40a9f",
    "name": "Start Timer",
    "server": "f4d0c3a1b2f5d6e7",
    "version": 3,
    "debugenabled": false,
    "service_domain": "timer",
    "service": "start",
    "entityId": "",
    "data": "payload.data",
    "dataType": "jsonata",
    "mergecontext": "",
    "mustacheAltTags": false,
    "outputProperties": [],
    "queue": "none",
    "x": 1040,
    "y": 100,
    "wires": [[]]
  },
  {
    "id": "d87b2f0aa5c1a4e3",
    "type": "api-call-service",
    "z": "d6f6c1d7d3b40a9f",
    "name": "Cancel Timer",
    "server": "f4d0c3a1b2f5d6e7",
    "version": 3,
    "debugenabled": false,
    "service_domain": "timer",
    "service": "cancel",
    "entityId": "",
    "data": "payload.data",
    "dataType": "jsonata",
    "mergecontext": "",
    "mustacheAltTags": false,
    "outputProperties": [],
    "queue": "none",
    "x": 1040,
    "y": 160,
    "wires": [[]]
  },
  {
    "id": "f1b0f0f8a2d5c3f4",
    "type": "mqtt-broker",
    "name": "MQTT Broker",
    "broker": "mqtt.local",
    "port": "1883",
    "clientid": "",
    "usetls": false,
    "protocolVersion": "4",
    "keepalive": "60",
    "cleansession": true,
    "birthTopic": "",
    "birthQos": "0",
    "birthPayload": "",
    "birthMsg": {},
    "closeTopic": "",
    "closePayload": "",
    "willTopic": "",
    "willQos": "0",
    "willPayload": "",
    "willMsg": {},
    "sessionExpiry": ""
  },
  {
    "id": "f4d0c3a1b2f5d6e7",
    "type": "server",
    "name": "Home Assistant",
    "addon": true
  }
]

Tip: Adjust the MQTT topic, timer entity naming convention, and helper creation strategy to suit your environment. The timer.elite_alarm_<alarm_id> naming pattern keeps helpers aligned with the sanitized IDs that EliteG19s publishes.