#include #include #include #include "config.h" #include "data_model.h" #include "dd3_legacy_core.h" #include "ha_discovery_json.h" #include "json_codec.h" static void fill_state_sample(MeterData &data) { data = {}; data.ts_utc = 1769905000; data.short_id = 0xF19C; strncpy(data.device_id, "dd3-F19C", sizeof(data.device_id)); data.energy_total_kwh = 1234.5678f; data.total_power_w = 321.6f; data.phase_power_w[0] = 100.4f; data.phase_power_w[1] = 110.4f; data.phase_power_w[2] = 110.8f; data.battery_voltage_v = 3.876f; data.battery_percent = 77; data.link_valid = true; data.link_rssi_dbm = -71; data.link_snr_db = 7.25f; data.err_meter_read = 1; data.err_decode = 2; data.err_lora_tx = 3; data.last_error = FaultType::Decode; data.rx_reject_reason = static_cast(RxRejectReason::CrcFail); } static void test_state_json_required_keys_and_stability() { MeterData data = {}; fill_state_sample(data); String out_json; TEST_ASSERT_TRUE(meterDataToJson(data, out_json)); StaticJsonDocument<512> doc; DeserializationError err = deserializeJson(doc, out_json); TEST_ASSERT_TRUE(err == DeserializationError::Ok); const char *required_keys[] = { "id", "ts", "e_kwh", "p_w", "p1_w", "p2_w", "p3_w", "bat_v", "bat_pct", "rssi", "snr", "err_m", "err_d", "err_tx", "err_last", "rx_reject", "rx_reject_text"}; for (size_t i = 0; i < (sizeof(required_keys) / sizeof(required_keys[0])); ++i) { TEST_ASSERT_TRUE_MESSAGE(doc.containsKey(required_keys[i]), required_keys[i]); } TEST_ASSERT_EQUAL_STRING("F19C", doc["id"] | ""); TEST_ASSERT_EQUAL_UINT32(data.ts_utc, doc["ts"] | 0U); TEST_ASSERT_EQUAL_UINT8(static_cast(FaultType::Decode), doc["err_last"] | 0U); TEST_ASSERT_EQUAL_UINT8(static_cast(RxRejectReason::CrcFail), doc["rx_reject"] | 0U); TEST_ASSERT_EQUAL_STRING("crc_fail", doc["rx_reject_text"] | ""); TEST_ASSERT_FALSE(doc.containsKey("energy_total_kwh")); TEST_ASSERT_FALSE(doc.containsKey("power_w")); TEST_ASSERT_FALSE(doc.containsKey("battery_voltage")); } static void test_state_json_optional_keys_when_not_available() { MeterData data = {}; fill_state_sample(data); data.link_valid = false; data.err_meter_read = 0; data.err_decode = 0; data.err_lora_tx = 0; data.rx_reject_reason = static_cast(RxRejectReason::None); String out_json; TEST_ASSERT_TRUE(meterDataToJson(data, out_json)); StaticJsonDocument<512> doc; DeserializationError err = deserializeJson(doc, out_json); TEST_ASSERT_TRUE(err == DeserializationError::Ok); TEST_ASSERT_FALSE(doc.containsKey("rssi")); TEST_ASSERT_FALSE(doc.containsKey("snr")); TEST_ASSERT_FALSE(doc.containsKey("err_m")); TEST_ASSERT_FALSE(doc.containsKey("err_d")); TEST_ASSERT_FALSE(doc.containsKey("err_tx")); TEST_ASSERT_EQUAL_STRING("none", doc["rx_reject_text"] | ""); } static void test_ha_discovery_manufacturer_and_key_stability() { String payload; TEST_ASSERT_TRUE(ha_build_discovery_sensor_payload( "dd3-F19C", "energy", "Energy", "kWh", "energy", "smartmeter/dd3-F19C/state", "{{ value_json.e_kwh }}", HA_MANUFACTURER, payload)); StaticJsonDocument<384> doc; DeserializationError err = deserializeJson(doc, payload); TEST_ASSERT_TRUE(err == DeserializationError::Ok); TEST_ASSERT_TRUE(doc.containsKey("name")); TEST_ASSERT_TRUE(doc.containsKey("state_topic")); TEST_ASSERT_TRUE(doc.containsKey("unique_id")); TEST_ASSERT_TRUE(doc.containsKey("value_template")); TEST_ASSERT_TRUE(doc.containsKey("device")); TEST_ASSERT_EQUAL_STRING("dd3-F19C_energy", doc["unique_id"] | ""); TEST_ASSERT_EQUAL_STRING("smartmeter/dd3-F19C/state", doc["state_topic"] | ""); TEST_ASSERT_EQUAL_STRING("{{ value_json.e_kwh }}", doc["value_template"] | ""); JsonObject device = doc["device"].as(); TEST_ASSERT_TRUE(device.containsKey("identifiers")); TEST_ASSERT_TRUE(device.containsKey("name")); TEST_ASSERT_TRUE(device.containsKey("model")); TEST_ASSERT_TRUE(device.containsKey("manufacturer")); TEST_ASSERT_EQUAL_STRING("DD3-LoRa-Bridge", device["model"] | ""); TEST_ASSERT_EQUAL_STRING("AcidBurns", device["manufacturer"] | ""); TEST_ASSERT_EQUAL_STRING("dd3-F19C", device["name"] | ""); TEST_ASSERT_EQUAL_STRING("dd3-F19C", device["identifiers"][0] | ""); } void setup() { dd3_legacy_core_force_link(); UNITY_BEGIN(); RUN_TEST(test_state_json_required_keys_and_stability); RUN_TEST(test_state_json_optional_keys_when_not_available); RUN_TEST(test_ha_discovery_manufacturer_and_key_stability); UNITY_END(); } void loop() {}