diff --git a/app/AGENTS_MQTT.md b/app/AGENTS_MQTT.md index b922635..c32e443 100644 --- a/app/AGENTS_MQTT.md +++ b/app/AGENTS_MQTT.md @@ -196,10 +196,9 @@ On connect and every 5 minutes thereafter, the community publisher sends a retai "timestamp": "2024-01-15T10:30:00.000000", "origin": "NodeName", "origin_id": "PUBKEY_HEX_UPPER", - "client": "RemoteTerm (github.com/...)", "model": "T-Deck", "firmware_version": "v2.2.2 (Build: 2025-01-15)", - "radio": "915.0MHz BW250.0 SF10 CR8", + "radio": "915.0,250.0,10,8", "client_version": "RemoteTerm/2.4.0", "stats": { "battery_mv": 4200, @@ -216,7 +215,7 @@ On connect and every 5 minutes thereafter, the community publisher sends a retai ``` - `model` and `firmware_version` are fetched once per connection via `send_device_query()` (requires firmware version >= 3) -- `radio` is formatted from `self_info` radio parameters (freq, BW, SF, CR) +- `radio` is comma-separated raw values from `self_info` (freq, BW, SF, CR) matching the reference format - `client_version` is read from Python package metadata (`remoteterm-meshcore`) - `stats` is fetched from `get_stats_core()` + `get_stats_radio()` every 5 minutes; omitted if firmware doesn't support stats commands - All radio queries use `blocking=False` — if the radio is busy, cached values are used. No user-facing operations are ever blocked. diff --git a/app/community_mqtt.py b/app/community_mqtt.py index 485ec3c..fead3f9 100644 --- a/app/community_mqtt.py +++ b/app/community_mqtt.py @@ -265,21 +265,24 @@ def _build_status_topic(settings: AppSettings, pubkey_hex: str) -> str: def _build_radio_info() -> str: - """Format the radio parameters string from self_info, or 'unknown'.""" + """Format the radio parameters string from self_info. + + Matches the reference format: ``"freq,bw,sf,cr"`` (comma-separated raw + values). Falls back to ``"0,0,0,0"`` when unavailable. + """ from app.radio import radio_manager try: if radio_manager.meshcore and radio_manager.meshcore.self_info: info = radio_manager.meshcore.self_info - freq = info.get("radio_freq") - bw = info.get("radio_bw") - sf = info.get("radio_sf") - cr = info.get("radio_cr") - if freq is not None and bw is not None and sf is not None and cr is not None: - return f"{freq}MHz BW{bw} SF{sf} CR{cr}" + freq = info.get("radio_freq", 0) + bw = info.get("radio_bw", 0) + sf = info.get("radio_sf", 0) + cr = info.get("radio_cr", 0) + return f"{freq},{bw},{sf},{cr}" except Exception: pass - return "unknown" + return "0,0,0,0" def _get_client_version() -> str: @@ -340,6 +343,7 @@ class CommunityMqttPublisher(BaseMqttPublisher): def _build_client_kwargs(self, settings: AppSettings) -> dict[str, Any]: from app.keystore import get_private_key, get_public_key + from app.radio import radio_manager private_key = get_private_key() public_key = get_public_key() @@ -357,12 +361,17 @@ class CommunityMqttPublisher(BaseMqttPublisher): tls_context = ssl.create_default_context() + device_name = "" + if radio_manager.meshcore and radio_manager.meshcore.self_info: + device_name = radio_manager.meshcore.self_info.get("name", "") + status_topic = _build_status_topic(settings, pubkey_hex) offline_payload = json.dumps( { "status": "offline", + "timestamp": datetime.now().isoformat(), + "origin": device_name or "MeshCore Device", "origin_id": pubkey_hex, - "client": _CLIENT_ID, } ) @@ -494,7 +503,6 @@ class CommunityMqttPublisher(BaseMqttPublisher): "timestamp": datetime.now().isoformat(), "origin": device_name or "MeshCore Device", "origin_id": pubkey_hex, - "client": _CLIENT_ID, "model": device_info.get("model", "unknown"), "firmware_version": device_info.get("firmware_version", "unknown"), "radio": _build_radio_info(), diff --git a/frontend/src/components/settings/SettingsMqttSection.tsx b/frontend/src/components/settings/SettingsMqttSection.tsx index 5f6fd99..5a5b8c5 100644 --- a/frontend/src/components/settings/SettingsMqttSection.tsx +++ b/frontend/src/components/settings/SettingsMqttSection.tsx @@ -341,7 +341,16 @@ export function SettingsMqttSection({

Share raw packet data with the MeshCore community for coverage mapping and network - analysis. Only raw RF packets are shared — never decrypted messages. + analysis. Only raw RF packets are shared — never decrypted messages. General parity + with{' '} + + meshcore-packet-capture + + .