Add LoRa telemetry, fault counters, and time sync status

This commit is contained in:
2026-01-30 13:00:16 +01:00
parent 7e3b537e49
commit 8ba7675a1c
13 changed files with 437 additions and 21 deletions

View File

@@ -1,6 +1,8 @@
#include "mqtt_client.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "config.h"
#include "json_codec.h"
static WiFiClient wifi_client;
@@ -52,6 +54,97 @@ bool mqtt_publish_state(const MeterData &data) {
return mqtt_client.publish(topic.c_str(), payload.c_str());
}
bool mqtt_publish_faults(const char *device_id, const FaultCounters &counters, FaultType last_error, uint32_t last_error_age_sec) {
if (!device_id || device_id[0] == '\0') {
return false;
}
if (!mqtt_connect()) {
return false;
}
StaticJsonDocument<192> doc;
doc["err_m"] = counters.meter_read_fail;
doc["err_d"] = counters.decode_fail;
doc["err_tx"] = counters.lora_tx_fail;
if (last_error != FaultType::None) {
doc["err_last"] = static_cast<uint8_t>(last_error);
doc["err_last_age"] = last_error_age_sec;
}
String payload;
size_t len = serializeJson(doc, payload);
if (len == 0) {
return false;
}
String topic = String("smartmeter/") + device_id + "/faults";
return mqtt_client.publish(topic.c_str(), payload.c_str(), true);
}
static bool publish_discovery_sensor(const char *device_id, const char *key, const char *name, const char *unit, const char *device_class,
const char *state_topic, const char *value_template) {
StaticJsonDocument<256> doc;
String unique_id = String("dd3_") + device_id + "_" + key;
String sensor_name = String(device_id) + " " + name;
doc["name"] = sensor_name;
doc["state_topic"] = state_topic;
doc["unique_id"] = unique_id;
if (unit && unit[0] != '\0') {
doc["unit_of_measurement"] = unit;
}
if (device_class && device_class[0] != '\0') {
doc["device_class"] = device_class;
}
doc["value_template"] = value_template;
JsonObject device = doc.createNestedObject("device");
JsonArray identifiers = device.createNestedArray("identifiers");
identifiers.add(String("dd3-") + device_id);
device["name"] = String("DD3 ") + device_id;
device["model"] = "DD3-LoRa-Bridge";
device["manufacturer"] = "DD3";
String payload;
size_t len = serializeJson(doc, payload);
if (len == 0) {
return false;
}
String topic = String("homeassistant/sensor/") + device_id + "/" + key + "/config";
return mqtt_client.publish(topic.c_str(), payload.c_str(), true);
}
bool mqtt_publish_discovery(const char *device_id) {
if (!device_id || device_id[0] == '\0') {
return false;
}
if (!mqtt_connect()) {
return false;
}
String state_topic = String("smartmeter/") + device_id + "/state";
bool ok = true;
ok = ok && publish_discovery_sensor(device_id, "energy", "Energy", "kWh", "energy", state_topic.c_str(), "{{ value_json.e_kwh }}");
ok = ok && publish_discovery_sensor(device_id, "power", "Power", "W", "power", state_topic.c_str(), "{{ value_json.p_w }}");
ok = ok && publish_discovery_sensor(device_id, "v1", "Voltage L1", "V", "voltage", state_topic.c_str(), "{{ value_json.v1_v }}");
ok = ok && publish_discovery_sensor(device_id, "v2", "Voltage L2", "V", "voltage", state_topic.c_str(), "{{ value_json.v2_v }}");
ok = ok && publish_discovery_sensor(device_id, "v3", "Voltage L3", "V", "voltage", state_topic.c_str(), "{{ value_json.v3_v }}");
ok = ok && publish_discovery_sensor(device_id, "p1", "Power L1", "W", "power", state_topic.c_str(), "{{ value_json.p1_w }}");
ok = ok && publish_discovery_sensor(device_id, "p2", "Power L2", "W", "power", state_topic.c_str(), "{{ value_json.p2_w }}");
ok = ok && publish_discovery_sensor(device_id, "p3", "Power L3", "W", "power", state_topic.c_str(), "{{ value_json.p3_w }}");
ok = ok && publish_discovery_sensor(device_id, "bat_v", "Battery Voltage", "V", "voltage", state_topic.c_str(), "{{ value_json.bat_v }}");
ok = ok && publish_discovery_sensor(device_id, "bat_pct", "Battery", "%", "battery", state_topic.c_str(), "{{ value_json.bat_pct }}");
ok = ok && publish_discovery_sensor(device_id, "rssi", "LoRa RSSI", "dBm", "signal_strength", state_topic.c_str(), "{{ value_json.rssi }}");
ok = ok && publish_discovery_sensor(device_id, "snr", "LoRa SNR", "dB", "", state_topic.c_str(), "{{ value_json.snr }}");
String faults_topic = String("smartmeter/") + device_id + "/faults";
ok = ok && publish_discovery_sensor(device_id, "err_m", "Meter Read Errors", "count", "", faults_topic.c_str(), "{{ value_json.err_m }}");
ok = ok && publish_discovery_sensor(device_id, "err_d", "Decode Errors", "count", "", faults_topic.c_str(), "{{ value_json.err_d }}");
ok = ok && publish_discovery_sensor(device_id, "err_tx", "LoRa TX Errors", "count", "", faults_topic.c_str(), "{{ value_json.err_tx }}");
ok = ok && publish_discovery_sensor(device_id, "err_last_age", "Last Error Age", "s", "", faults_topic.c_str(), "{{ value_json.err_last_age }}");
return ok;
}
#ifdef ENABLE_TEST_MODE
bool mqtt_publish_test(const char *device_id, const String &payload) {
if (!mqtt_connect()) {