test: add json stability and discovery payload coverage
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
#include "data_model.h"
|
||||
#include <esp_mac.h>
|
||||
|
||||
void init_device_ids(uint16_t &short_id, char *device_id, size_t device_id_len) {
|
||||
uint8_t mac[6] = {0};
|
||||
// Read base MAC without needing WiFi to be started.
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
short_id = (static_cast<uint16_t>(mac[4]) << 8) | mac[5];
|
||||
snprintf(device_id, device_id_len, "dd3-%04X", short_id);
|
||||
}
|
||||
|
||||
const char *rx_reject_reason_text(RxRejectReason reason) {
|
||||
switch (reason) {
|
||||
case RxRejectReason::CrcFail:
|
||||
return "crc_fail";
|
||||
case RxRejectReason::InvalidMsgKind:
|
||||
return "invalid_msg_kind";
|
||||
case RxRejectReason::LengthMismatch:
|
||||
return "length_mismatch";
|
||||
case RxRejectReason::DeviceIdMismatch:
|
||||
return "device_id_mismatch";
|
||||
case RxRejectReason::BatchIdMismatch:
|
||||
return "batch_id_mismatch";
|
||||
case RxRejectReason::UnknownSender:
|
||||
return "unknown_sender";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#include "json_codec.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
static constexpr size_t STATE_JSON_DOC_CAPACITY = 512;
|
||||
|
||||
static float round2(float value) {
|
||||
if (isnan(value)) {
|
||||
return value;
|
||||
}
|
||||
return roundf(value * 100.0f) / 100.0f;
|
||||
}
|
||||
|
||||
static int32_t round_to_i32(float value) {
|
||||
if (isnan(value)) {
|
||||
return 0;
|
||||
}
|
||||
long rounded = lroundf(value);
|
||||
if (rounded > INT32_MAX) {
|
||||
return INT32_MAX;
|
||||
}
|
||||
if (rounded < INT32_MIN) {
|
||||
return INT32_MIN;
|
||||
}
|
||||
return static_cast<int32_t>(rounded);
|
||||
}
|
||||
|
||||
static const char *short_id_from_device_id(const char *device_id) {
|
||||
if (!device_id) {
|
||||
return "";
|
||||
}
|
||||
size_t len = strlen(device_id);
|
||||
if (len >= 4) {
|
||||
return device_id + (len - 4);
|
||||
}
|
||||
return device_id;
|
||||
}
|
||||
|
||||
static void format_float_2(char *buf, size_t buf_len, float value) {
|
||||
if (!buf || buf_len == 0) {
|
||||
return;
|
||||
}
|
||||
if (isnan(value)) {
|
||||
snprintf(buf, buf_len, "null");
|
||||
return;
|
||||
}
|
||||
snprintf(buf, buf_len, "%.2f", round2(value));
|
||||
}
|
||||
|
||||
static void set_int_or_null(JsonDocument &doc, const char *key, float value) {
|
||||
if (!key || key[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
if (isnan(value)) {
|
||||
doc[key] = nullptr;
|
||||
return;
|
||||
}
|
||||
doc[key] = round_to_i32(value);
|
||||
}
|
||||
|
||||
bool meterDataToJson(const MeterData &data, String &out_json) {
|
||||
StaticJsonDocument<STATE_JSON_DOC_CAPACITY> doc;
|
||||
doc["id"] = short_id_from_device_id(data.device_id);
|
||||
doc["ts"] = data.ts_utc;
|
||||
char buf[16];
|
||||
format_float_2(buf, sizeof(buf), data.energy_total_kwh);
|
||||
doc["e_kwh"] = serialized(buf);
|
||||
set_int_or_null(doc, "p_w", data.total_power_w);
|
||||
set_int_or_null(doc, "p1_w", data.phase_power_w[0]);
|
||||
set_int_or_null(doc, "p2_w", data.phase_power_w[1]);
|
||||
set_int_or_null(doc, "p3_w", data.phase_power_w[2]);
|
||||
format_float_2(buf, sizeof(buf), data.battery_voltage_v);
|
||||
doc["bat_v"] = serialized(buf);
|
||||
doc["bat_pct"] = data.battery_percent;
|
||||
if (data.link_valid) {
|
||||
doc["rssi"] = data.link_rssi_dbm;
|
||||
doc["snr"] = data.link_snr_db;
|
||||
}
|
||||
if (data.err_meter_read > 0) {
|
||||
doc["err_m"] = data.err_meter_read;
|
||||
}
|
||||
if (data.err_decode > 0) {
|
||||
doc["err_d"] = data.err_decode;
|
||||
}
|
||||
if (data.err_lora_tx > 0) {
|
||||
doc["err_tx"] = data.err_lora_tx;
|
||||
}
|
||||
doc["err_last"] = static_cast<uint8_t>(data.last_error);
|
||||
doc["rx_reject"] = data.rx_reject_reason;
|
||||
doc["rx_reject_text"] = rx_reject_reason_text(static_cast<RxRejectReason>(data.rx_reject_reason));
|
||||
|
||||
out_json = "";
|
||||
size_t len = serializeJson(doc, out_json);
|
||||
return len > 0;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "ha_discovery_json.h"
|
||||
#include "config.h"
|
||||
#include "json_codec.h"
|
||||
|
||||
@@ -10,6 +11,13 @@ static PubSubClient mqtt_client(wifi_client);
|
||||
static WifiMqttConfig g_cfg;
|
||||
static String g_client_id;
|
||||
|
||||
static const char *ha_manufacturer_anchor() {
|
||||
StaticJsonDocument<32> doc;
|
||||
JsonObject device = doc.createNestedObject("device");
|
||||
device["manufacturer"] = HA_MANUFACTURER;
|
||||
return HA_MANUFACTURER;
|
||||
}
|
||||
|
||||
static const char *fault_text(FaultType fault) {
|
||||
switch (fault) {
|
||||
case FaultType::MeterRead:
|
||||
@@ -94,31 +102,9 @@ bool mqtt_publish_faults(const char *device_id, const FaultCounters &counters, F
|
||||
|
||||
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(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(device_id));
|
||||
device["name"] = String(device_id);
|
||||
device["model"] = "DD3-LoRa-Bridge";
|
||||
device["manufacturer"] = HA_MANUFACTURER;
|
||||
|
||||
String payload;
|
||||
size_t len = serializeJson(doc, payload);
|
||||
if (len == 0) {
|
||||
if (!ha_build_discovery_sensor_payload(device_id, key, name, unit, device_class, state_topic, value_template,
|
||||
ha_manufacturer_anchor(), payload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user