Skip to content

feat: add MQTT downlink proxy support (subscribe + forward to radio)#155

Open
richardpowellus wants to merge 3 commits into
meshtastic:mainfrom
richardpowellus:feat/mqtt-downlink-proxy
Open

feat: add MQTT downlink proxy support (subscribe + forward to radio)#155
richardpowellus wants to merge 3 commits into
meshtastic:mainfrom
richardpowellus:feat/mqtt-downlink-proxy

Conversation

@richardpowellus
Copy link
Copy Markdown

Summary

The existing MQTT proxy implementation (added in #135) only supports uplink — publishing radio-received messages to an MQTT broker. After connecting to the broker, _maintain_mqtt_connection() simply awaits _is_stopped.wait(), never subscribing to any topics or forwarding incoming messages back to the radio.

This means devices relying on the MQTT proxy for internet connectivity (e.g. USB-connected devices without WiFi, like the Tracker T1000-E) can publish to the mesh via MQTT but cannot receive messages from other nodes through the MQTT mesh network.

Changes

_maintain_mqtt_connection()

  • Replaced the idle await self._is_stopped.wait() with a downlink subscription call followed by an async for message in self._mqtt_client.messages loop that forwards incoming messages to the radio.
  • On reconnection, subscriptions are re-established automatically.

New: _subscribe_to_downlink_topics()

  • Proactively subscribes to MQTT downlink topics based on the device's channel configuration.
  • Iterates channels, checks downlink_enabled, and subscribes to {root}/{region}/2/e/{channel_name}/#.
  • Uses config_pb2.Config.LoRaConfig.RegionCode.Name() for clean region string lookup (no hardcoded map).

New: _forward_mqtt_to_radio()

  • Wraps incoming MQTT messages in a MqttClientProxyMessageToRadio protobuf and sends to the radio via the existing connection.

_handle_mqtt_client_proxy_message()

  • Empty-payload messages from the firmware (subscribe requests) are now handled by subscribing to the requested topic, instead of being silently ignored.
  • Updated exception message from "Error publishing MQTT message" to "Error handling MQTT proxy message" for accuracy.

Testing

Tested on a Tracker T1000-E (firmware 2.6.11, no WiFi) connected via USB to Home Assistant (HAOS 2026.2.3) with Meshtastic HA integration v0.6.1. The device is in a remote area (North Bend, WA) unable to reach the Seattle/Bellevue mesh via LoRa alone.

After applying this patch:

  • MQTT connection established to mqtt.meshtastic.org
  • Downlink topics subscribed (msh/US/2/e/LongFast/#, etc.)
  • 5,010+ messages successfully forwarded from MQTT to radio
  • Device now participates in the broader mesh network via internet proxy

Related

Completes the MQTT proxy feature started in #135 by adding the missing downlink (subscribe + forward) path.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 2, 2026

CLA assistant check
All committers have signed the CLA.

- MQTT downlink proxy: subscribe to channel topics and forward messages to radio
- LoRa-to-MQTT relay: re-encrypt received LoRa packets and publish to MQTT
  (firmware in proxy mode only uplinks self-originated packets)
- Skip default channels (LongFast, etc.) to avoid serial link saturation
  from high-traffic public broker topics (~320 msgs/sec)
- Self-loop prevention: filter messages published by own gateway ID
- Fix double-region topic bug: mqtt.root already includes region
- AES-CTR encryption with proper Meshtastic nonce format
- JSON topic subscription support when json_enabled is set
@richardpowellus richardpowellus force-pushed the feat/mqtt-downlink-proxy branch from d94e046 to 4656674 Compare March 3, 2026 06:28
Add two new boolean toggles under 'MQTT Proxy' options section:
- Enable MQTT Downlink Forwarding (default: off)
- Enable LoRa-to-MQTT Uplink Relay (default: off)

Both features default to disabled, preserving existing behavior for
all current users. The toggles are available in both the initial setup
flow and the options reconfiguration UI.

The option values are threaded through:
  __init__.py -> api.py -> interface.py

This ensures the new downlink subscription and LoRa-to-MQTT relay
features are strictly opt-in.
- Update README with MQTT Proxy section documenting downlink forwarding,
  uplink relay, and configuration options
- Add 43 tests covering:
  - Config constants and option access patterns
  - PSK expansion (empty, single-byte indices, AES-128/256 passthrough)
  - AES-CTR encryption round-trip and nonce format
  - Default channel filtering (LongFast, LongSlow, etc.)
  - Topic construction (no double-region bug)
  - Self-loop prevention
  - Relay gating (same-node, PKI, disconnected, default channel, uplink disabled)
  - Downlink forwarding gating
  - Subscribe topic filtering
  - Constructor parameter gating for enable_mqtt_downlink/enable_mqtt_uplink_relay
- Add conftest.py with importlib-based loading to avoid homeassistant runtime dependency
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants