Refactor timestamp handling in MQTTMessageBuilder

- Introduced a new method `formatIsoTimestampForMqtt` to centralize ISO-like timestamp formatting for MQTT messages, applying timezone preferences.
- Updated `buildStatusMessage`, `buildPacketJSON`, `buildPacketJSONFromRaw`, and `buildRawJSON` methods to utilize the new timestamp formatting function, improving code clarity and reducing redundancy.
- Adjusted `publishStatusToSlot` and `publishStatus` in MQTTBridge to use the new timestamp formatting method, ensuring consistent timestamp handling across status messages.
This commit is contained in:
agessaman
2026-05-15 10:18:02 -07:00
parent 3cde9c1052
commit 5a004f3770
3 changed files with 40 additions and 57 deletions
+16 -38
View File
@@ -1,9 +1,21 @@
#include "MQTTMessageBuilder.h"
#include <ArduinoJson.h>
#include <cstring>
#include <time.h>
#include <Timezone.h>
#include "MeshCore.h"
void MQTTMessageBuilder::formatIsoTimestampForMqtt(time_t now, Timezone* timezone, char* buffer, size_t buffer_size) {
if (!buffer || buffer_size == 0) return;
time_t local_wall = timezone ? timezone->toLocal(now) : now;
struct tm* tm_info = localtime(&local_wall);
if (tm_info && strftime(buffer, buffer_size, "%Y-%m-%dT%H:%M:%S.000000", tm_info) > 0) {
return;
}
strncpy(buffer, "2024-01-01T12:00:00.000000", buffer_size - 1);
buffer[buffer_size - 1] = '\0';
}
int MQTTMessageBuilder::buildStatusMessage(
JsonDocument& doc,
const char* origin,
@@ -182,22 +194,11 @@ int MQTTMessageBuilder::buildPacketJSON(
) {
if (!packet) return 0;
// Get current device time (should be UTC since system timezone is set to UTC)
time_t now = time(nullptr);
// Convert to local time using timezone library (for timestamp field only)
time_t local_time = timezone ? timezone->toLocal(now) : now;
struct tm* local_timeinfo = localtime(&local_time);
// Format timestamp in ISO 8601 format (LOCAL TIME)
char timestamp[32];
if (local_timeinfo) {
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000000", local_timeinfo);
} else {
strcpy(timestamp, "2024-01-01T12:00:00.000000");
}
formatIsoTimestampForMqtt(now, timezone, timestamp, sizeof(timestamp));
// Get UTC time (since system timezone is UTC, time() returns UTC)
// Packet time/date: UTC (gmtime), same family as meshcoretomqtt serial fields
struct tm* utc_timeinfo = gmtime(&now);
// Format time and date (ALWAYS UTC)
@@ -266,22 +267,10 @@ int MQTTMessageBuilder::buildPacketJSONFromRaw(
) {
if (!packet || !raw_data || raw_len <= 0) return 0;
// Get current device time (should be UTC since system timezone is set to UTC)
time_t now = time(nullptr);
// Convert to local time using timezone library (for timestamp field only)
time_t local_time = timezone ? timezone->toLocal(now) : now;
struct tm* local_timeinfo = localtime(&local_time);
// Format timestamp in ISO 8601 format (LOCAL TIME)
char timestamp[32];
if (local_timeinfo) {
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000000", local_timeinfo);
} else {
strcpy(timestamp, "2024-01-01T12:00:00.000000");
}
formatIsoTimestampForMqtt(now, timezone, timestamp, sizeof(timestamp));
// Get UTC time (since system timezone is UTC, time() returns UTC)
struct tm* utc_timeinfo = gmtime(&now);
// Format time and date (ALWAYS UTC)
@@ -344,20 +333,9 @@ int MQTTMessageBuilder::buildRawJSON(
) {
if (!packet) return 0;
// Get current device time
time_t now = time(nullptr);
// Convert to local time using timezone library
time_t local_time = timezone ? timezone->toLocal(now) : now;
struct tm* timeinfo = localtime(&local_time);
// Format timestamp in ISO 8601 format
char timestamp[32];
if (timeinfo) {
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000000", timeinfo);
} else {
strcpy(timestamp, "2024-01-01T12:00:00.000000");
}
formatIsoTimestampForMqtt(now, timezone, timestamp, sizeof(timestamp));
// Convert packet to hex
// MAX_TRANS_UNIT is 255, so max hex size is 510 chars + null = 511 bytes
+17 -5
View File
@@ -11,12 +11,24 @@
* This class handles the formatting of mesh packets and device status
* into JSON messages for MQTT publishing according to the MeshCore
* packet capture specification.
*
* Timestamps in JSON use the configured Timezone (prefs) for status, packet, and
* raw topics. Packet payloads also include separate `time` and `date` strings in
* UTC (gmtime) so they stay aligned with meshcoretomqtt serial regex fields.
*/
class MQTTMessageBuilder {
private:
static const int JSON_BUFFER_SIZE = 1024;
public:
/**
* Format the MQTT JSON `timestamp` field (same rule for status, packet, raw).
* Applies Timezone prefs via `timezone->toLocal(now)`; if timezone is nullptr,
* uses `now` unchanged. Output is naive local wall time, ISO-like
* "%Y-%m-%dT%H:%M:%S.000000".
*/
static void formatIsoTimestampForMqtt(time_t now, Timezone* timezone, char* buffer, size_t buffer_size);
/**
* Build status message JSON
*
@@ -27,7 +39,7 @@ public:
* @param radio Radio information
* @param client_version Client version
* @param status Connection status ("online" or "offline")
* @param timestamp ISO 8601 timestamp
* @param timestamp ISO-like timestamp (see formatIsoTimestampForMqtt)
* @param buffer Output buffer for JSON string
* @param buffer_size Size of output buffer
* @param battery_mv Battery voltage in millivolts (optional, -1 to omit)
@@ -71,10 +83,10 @@ public:
*
* @param origin Device name
* @param origin_id Device public key (hex string)
* @param timestamp ISO 8601 timestamp
* @param timestamp ISO-like timestamp (see formatIsoTimestampForMqtt)
* @param direction Packet direction ("rx" or "tx")
* @param time Time in HH:MM:SS format
* @param date Date in DD/MM/YYYY format
* @param time Time in HH:MM:SS (UTC, gmtime; meshcoretomqtt serial parity)
* @param date Date in DD/MM/YYYY (UTC, gmtime)
* @param len Total packet length
* @param packet_type Packet type code
* @param route Routing type
@@ -114,7 +126,7 @@ public:
*
* @param origin Device name
* @param origin_id Device public key (hex string)
* @param timestamp ISO 8601 timestamp
* @param timestamp ISO-like timestamp (see formatIsoTimestampForMqtt)
* @param raw Raw packet data (hex string)
* @param buffer Output buffer for JSON string
* @param buffer_size Size of output buffer
+7 -14
View File
@@ -4,6 +4,7 @@
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <Timezone.h>
#include <time.h>
#ifdef WITH_SNMP
#include "../SNMPAgent.h"
@@ -1676,13 +1677,9 @@ void MQTTBridge::publishStatusToSlot(int index) {
char timestamp[32];
char radio_info[64];
// Get current timestamp in ISO 8601 format
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000000", &timeinfo);
} else {
strcpy(timestamp, "2024-01-01T12:00:00.000000");
}
// Status timestamp: same prefs-based wall clock as packet/raw JSON `timestamp`
// (not libc getLocalTime — SNTP uses UTC offset 0; prefs Timezone is separate).
MQTTMessageBuilder::formatIsoTimestampForMqtt(time(nullptr), _timezone, timestamp, sizeof(timestamp));
snprintf(radio_info, sizeof(radio_info), "%.6f,%.1f,%d,%d",
_prefs->freq, _prefs->bw, _prefs->sf, _prefs->cr);
@@ -2370,13 +2367,9 @@ bool MQTTBridge::publishStatus() {
char timestamp[32];
char radio_info[64];
// Get current timestamp in ISO 8601 format
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000000", &timeinfo);
} else {
strcpy(timestamp, "2024-01-01T12:00:00.000000");
}
// Status timestamp: same prefs-based wall clock as packet/raw JSON `timestamp`
// (not libc getLocalTime — SNTP uses UTC offset 0; prefs Timezone is separate).
MQTTMessageBuilder::formatIsoTimestampForMqtt(time(nullptr), _timezone, timestamp, sizeof(timestamp));
snprintf(radio_info, sizeof(radio_info), "%.6f,%.1f,%d,%d",
_prefs->freq, _prefs->bw, _prefs->sf, _prefs->cr);