You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
4.9 KiB

/*
* Copyright (C) 2025 by Andy Taylor MW0MWZ
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef MQTTPublisher_H
#define MQTTPublisher_H
#if defined(MQTT)
/*
* Inline helper functions that format and publish D-Star events as JSON to the
* MQTT broker via the global g_mqtt (CMQTTConnection*) instance.
*
* All helpers are no-ops when g_mqtt is nullptr (MQTT not configured) or when
* the broker is not connected. They publish to the "json" sub-topic, which is
* automatically prefixed with the configured repeater name by CMQTTConnection.
*
* Display-Driver-compatible MQTT JSON publishing for DStarRepeater:
* Publishes event-driven JSON messages to the "json" topic in the format
* expected by Display-Driver, enabling LCD/OLED display output.
*
* These are separate from the periodic status messages on the "status"
* topic — the display needs discrete start/end events, not continuous
* state snapshots.
*/
#include "MQTTConnection.h"
#include "Logger.h"
#include <string>
#include <cstdio>
// Escape a string for safe embedding in a JSON value.
// Handles the characters that would break JSON string syntax or silently
// corrupt the document: backslash, double-quote, and the common C0 controls.
// Non-printable control characters other than \n/\r/\t are dropped entirely
// so that malformed radio headers cannot produce invalid JSON.
static inline std::string jsonEscape(const std::string& s)
{
std::string out;
out.reserve(s.size());
for (char c : s) {
if (c == '"') out += "\\\"";
else if (c == '\\') out += "\\\\";
else if (c == '\n') out += "\\n";
else if (c == '\r') out += "\\r";
else if (c == '\t') out += "\\t";
else if (c >= 0x20) out += c;
// drop other non-printable control characters
}
return out;
}
// Publish a D-Star call start event (RF or network)
static inline void mqttPublishDStarStart(const std::string& myCall1, const std::string& myCall2,
const std::string& yourCall, const std::string& rptCall2, const char* source)
{
if (g_mqtt == nullptr)
return;
// Escape all callsign strings: over-the-air headers are untrusted input
// and may contain characters (e.g. '"' or '\') that break JSON syntax.
// 'source' is a string literal supplied by our own code, not radio data.
const std::string cs1 = jsonEscape(myCall1);
const std::string cs2 = jsonEscape(myCall2);
const std::string yc = jsonEscape(yourCall);
const std::string rc2 = jsonEscape(rptCall2);
char buf[512];
::snprintf(buf, sizeof(buf),
"{\"D-Star\":{\"action\":\"start\","
"\"source_cs\":\"%s\",\"source_ext\":\"%s\","
"\"destination_cs\":\"%s\",\"reflector\":\"%s\","
"\"source\":\"%s\"}}",
cs1.c_str(),
cs2.c_str(),
yc.c_str(),
rc2.c_str(),
source);
g_mqtt->publish("json", buf);
}
// Publish a D-Star call end event (normal termination)
static inline void mqttPublishDStarEnd()
{
if (g_mqtt == nullptr)
return;
g_mqtt->publish("json", "{\"D-Star\":{\"action\":\"end\"}}");
}
// Publish a D-Star call lost event (watchdog timeout / abnormal)
static inline void mqttPublishDStarLost()
{
if (g_mqtt == nullptr)
return;
g_mqtt->publish("json", "{\"D-Star\":{\"action\":\"lost\"}}");
}
// Publish idle mode (no traffic)
static inline void mqttPublishIdle()
{
if (g_mqtt == nullptr)
return;
g_mqtt->publish("json", "{\"MMDVM\":{\"mode\":\"idle\"}}");
}
// Publish D-Star RSSI value (DVAP signal strength in dBm)
static inline void mqttPublishRSSI(int rssi)
{
if (g_mqtt == nullptr)
return;
char buf[128];
::snprintf(buf, sizeof(buf),
"{\"RSSI\":{\"mode\":\"D-Star\",\"value\":%d}}",
rssi);
g_mqtt->publish("json", buf);
}
// Publish D-Star BER value
static inline void mqttPublishBER(float ber)
{
if (g_mqtt == nullptr)
return;
char buf[128];
::snprintf(buf, sizeof(buf),
"{\"BER\":{\"mode\":\"D-Star\",\"value\":%.1f}}",
ber);
g_mqtt->publish("json", buf);
}
// Publish D-Star slow data text
static inline void mqttPublishText(const std::string& text)
{
if (g_mqtt == nullptr)
return;
// Slow-data text arrives over the air and is untrusted; escape it so
// that a crafted transmission cannot produce malformed JSON.
const std::string escaped = jsonEscape(text);
char buf[256];
::snprintf(buf, sizeof(buf),
"{\"Text\":{\"mode\":\"D-Star\",\"value\":\"%s\"}}",
escaped.c_str());
g_mqtt->publish("json", buf);
}
#endif
#endif

Powered by TurnKey Linux.