diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt
index b25b9a1..3a140f9 100644
--- a/simulations/simulation_t1.txt
+++ b/simulations/simulation_t1.txt
@@ -237,11 +237,6 @@ telegram=|37446850336633663943a2_10672c866100181c01000480794435d5000000000000000
{"media":"heat","meter":"compact5","name":"Heating2","id":"66336633","total_kwh":25250,"current_kwh":284,"previous_kwh":24966,"timestamp":"1111-11-11T11:11:11Z"}
|Heating2;66336633;25250;284;24966;1111-11-11 11:11.11
-# Test Maddalena EVO 868 wmbus module on water meter
-telegram=|aa4424347677787950077ac10000202f2f041306070000046d1e31b12104fd17000000000e787880048120004413c9040000426c9f2c840113c904000082016c9f2cd3013b9a0200c4016d0534a7218104fd280182046c9f2c840413c9040000c404131b00000084051300000000c405130000000084061300000000c406130000000084071300000000c407130000000084081300000000c408130000000084091300000000c4091300000000ffff|
-{"media":"water","meter":"evo868","name":"Votchka","id":"79787776","total_m3":1.798,"device_date_time":"2021-01-17 17:30","current_status":"OK","fabrication_no":"002081048078","consumption_at_set_date_m3":1.225,"set_date":"2020-12-31","consumption_at_set_date_2_m3":1.225,"set_date_2":"2020-12-31","max_flow_since_datetime_m3h":0.666,"max_flow_datetime":"2021-01-07 20:05","consumption_at_history_1_m3":1.225,"history_1_date":"2020-12-31","consumption_at_history_2_m3":0.027,"history_2_date":"2020-11-30","consumption_at_history_3_m3":0,"history_3_date":"2020-10-31","consumption_at_history_4_m3":0,"history_4_date":"2020-09-30","consumption_at_history_5_m3":0,"history_5_date":"2020-08-31","consumption_at_history_6_m3":0,"history_6_date":"2020-07-31","consumption_at_history_7_m3":0,"history_7_date":"2020-06-30","consumption_at_history_8_m3":0,"history_8_date":"2020-05-31","consumption_at_history_9_m3":0,"history_9_date":"2020-04-30","consumption_at_history_10_m3":0,"history_10_date":"2020-03-31","consumption_at_history_11_m3":0,"history_11_date":"2020-02-29","consumption_at_history_12_m3":0,"history_12_date":"2020-01-31","timestamp":"1111-11-11T11:11:11Z"}
-|Votchka;79787776;1.798000;OK;1.225000;2020-12-31;1111-11-11 11:11.11
-
# Test Gran-System-S electricity meter 101
telegram=||7844731e78610418010278046d0f13bc21040394030000841003690300008420032b00000084300300000000848010030000000084016d0000bc2184010394030000841103690300008421032b00000084310300000000848110030000000004fd482e09000004fd5b0000000002fb2d861304fd1700000201|
{"media":"electricity","meter":"gransystems","name":"Gran101","id":"18046178","total_energy_consumption_kwh":0.916,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":0,"voltage_at_phase_3_v":0,"currrent_at_phase_1_a":0,"currrent_at_phase_2_a":0,"currrent_at_phase_3_a":0,"frequency_hz":49.98,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
diff --git a/src/driver_evo868.cc b/src/driver_evo868.cc
new file mode 100644
index 0000000..f6f74c4
--- /dev/null
+++ b/src/driver_evo868.cc
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
+
+ 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 3 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, see .
+*/
+
+#include"meters_common_implementation.h"
+
+namespace
+{
+ struct Driver : public virtual MeterCommonImplementation
+ {
+ Driver(MeterInfo &mi, DriverInfo &di);
+ };
+
+ static bool ok = registerDriver([](DriverInfo&di)
+ {
+ di.setName("evo868");
+ di.setDefaultFields("name,id,total_m3,set_date,consumption_at_set_date_m3,timestamp");
+ di.setMeterType(MeterType::WaterMeter);
+ di.addLinkMode(LinkMode::T1);
+ di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); });
+ di.addDetection(MANUFACTURER_MAD, 0x07, 0x50);
+
+ });
+
+ Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
+ {
+ addStringFieldWithExtractorAndLookup(
+ "current_status",
+ "Status of meter.",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT
+ | PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::ErrorFlags),
+ {
+ {
+ {
+ "ERROR_FLAGS",
+ Translate::Type::BitToString,
+ 0xffff,
+ "OK",
+ {
+ // { 0x01 , "WOOT" },
+ }
+ },
+ },
+ });
+
+ addOptionalCommonFields("fabrication_no,meter_datetime");
+ addOptionalFlowRelatedFields("total_m3");
+
+ addNumericFieldWithExtractor(
+ "consumption_at_set_date",
+ "The total water consumption at the most recent billing period date.",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .set(StorageNr(1))
+ );
+
+ addStringFieldWithExtractor(
+ "set_date",
+ "The most recent billing period date.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Date)
+ .set(StorageNr(1))
+ );
+
+ addNumericFieldWithExtractor(
+ "consumption_at_set_date_2",
+ "The total water consumption at the second most recent billing period date.",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .set(StorageNr(2))
+ );
+
+ addStringFieldWithExtractor(
+ "set_date_2",
+ "The second most recent billing period date.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Date)
+ .set(StorageNr(2))
+ );
+
+ addNumericFieldWithExtractor(
+ "max_flow_since_datetime",
+ "Maximum water flow since date time.",
+ PrintProperty::JSON | PrintProperty::FIELD,
+ Quantity::Flow,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Maximum)
+ .set(VIFRange::VolumeFlow)
+ .set(StorageNr(3))
+ );
+
+ addStringFieldWithExtractor(
+ "max_flow_datetime",
+ "The datetime to which maximum flow is measured.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::DateTime)
+ .set(StorageNr(3))
+ );
+
+ addNumericFieldWithExtractor(
+ "consumption_at_history_{storage_counter-7counter}",
+ "The total water consumption at the historic date.",
+ PrintProperty::JSON,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .set(StorageNr(8),StorageNr(19))
+ );
+
+ }
+
+}
+
+// est: Votchka evo868 79787776 NOKEY
+// omment: Test Maddalena EVO 868 wmbus module on water meter
+// elegram=|aa4424347677787950077ac10000202f2f041306070000046d1e31b12104fd17000000000e787880048120004413c9040000426c9f2c840113c904000082016c9f2cd3013b9a0200c4016d0534a7218104fd280182046c9f2c840413c9040000c404131b00000084051300000000c405130000000084061300000000c406130000000084071300000000c407130000000084081300000000c408130000000084091300000000c4091300000000ffff|
+// "media":"water","meter":"evo868","name":"Votchka","id":"79787776","total_m3":1.798,"device_date_time":"2021-01-17 17:30","current_status":"OK","fabrication_no":"002081048078","consumption_at_set_date_m3":1.225,"set_date":"2020-12-31","consumption_at_set_date_2_m3":1.225,"set_date_2":"2020-12-31","max_flow_since_datetime_m3h":0.666,"max_flow_datetime":"2021-01-07 20:05","consumption_at_history_1_m3":1.225,"history_1_date":"2020-12-31","consumption_at_history_2_m3":0.027,"history_2_date":"2020-11-30","consumption_at_history_3_m3":0,"history_3_date":"2020-10-31","consumption_at_history_4_m3":0,"history_4_date":"2020-09-30","consumption_at_history_5_m3":0,"history_5_date":"2020-08-31","consumption_at_history_6_m3":0,"history_6_date":"2020-07-31","consumption_at_history_7_m3":0,"history_7_date":"2020-06-30","consumption_at_history_8_m3":0,"history_8_date":"2020-05-31","consumption_at_history_9_m3":0,"history_9_date":"2020-04-30","consumption_at_history_10_m3":0,"history_10_date":"2020-03-31","consumption_at_history_11_m3":0,"history_11_date":"2020-02-29","consumption_at_history_12_m3":0,"history_12_date":"2020-01-31","timestamp":"1111-11-11T11:11:11Z"}
+// Votchka;79787776;1.798;OK;1.225;2020-12-31;1111-11-11 11:11.11
diff --git a/src/dvparser.h b/src/dvparser.h
index 571e041..e616e89 100644
--- a/src/dvparser.h
+++ b/src/dvparser.h
@@ -282,6 +282,7 @@ struct SubUnitNr
SubUnitNr(int n) : nr_(n) {}
int intValue() { return nr_; }
bool operator==(SubUnitNr s) { return nr_ == s.nr_; }
+ bool operator!=(SubUnitNr s) { return nr_ != s.nr_; }
bool operator>=(SubUnitNr s) { return nr_ >= s.nr_; }
bool operator<=(SubUnitNr s) { return nr_ <= s.nr_; }
@@ -450,6 +451,16 @@ struct FieldMatcher
FieldMatcher &set(IndexNr i) { index_nr = i; return *this; }
bool matches(DVEntry &dv_entry);
+
+ // Returns true of there is any range for storage, tariff, subunit nrs.
+ // I.e. this matcher is expected to match against multiple dv entries!
+ bool expectedToMatchAgainstMultipleEntries()
+ {
+ return (match_storage_nr && storage_nr_from != storage_nr_to)
+ || (match_tariff_nr && tariff_nr_from != tariff_nr_to)
+ || (match_subunit_nr && subunit_nr_from != subunit_nr_to);
+ }
+
std::string str();
};
diff --git a/src/formula.cc b/src/formula.cc
index 7f85f55..91d84b3 100644
--- a/src/formula.cc
+++ b/src/formula.cc
@@ -1083,7 +1083,14 @@ string StringInterpolatorImplementation::apply(DVEntry *dve)
if (f < formulas_.size())
{
- result += tostrprintf("%g", formulas_[f]->calculate(Unit::COUNTER, dve));
+ if (dve != NULL)
+ {
+ result += tostrprintf("%g", formulas_[f]->calculate(Unit::COUNTER, dve));
+ }
+ else
+ {
+ result += "{NULL}";
+ }
f++;
}
}
diff --git a/src/meter_detection.h b/src/meter_detection.h
index 26260f5..4ed7561 100644
--- a/src/meter_detection.h
+++ b/src/meter_detection.h
@@ -29,7 +29,6 @@
#define METER_DETECTION \
X(CCx01, MANUFACTURER_GSS, 0x02, 0x01) \
X(EURISII, MANUFACTURER_INE, 0x08, 0x55) \
- X(EVO868, MANUFACTURER_MAD, 0x07, 0x50) \
X(HYDROCALM3,MANUFACTURER_BMT, 0x0d, 0x0b) \
X(HYDRODIGIT,MANUFACTURER_BMT, 0x06, 0x13) \
X(HYDRODIGIT,MANUFACTURER_BMT, 0x07, 0x13) \
diff --git a/src/meter_evo868.cc b/src/meter_evo868.cc
deleted file mode 100644
index c3b775d..0000000
--- a/src/meter_evo868.cc
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- Copyright (C) 2017-2020 Fredrik Öhrström (gpl-3.0-or-later)
-
- 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 3 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, see .
-*/
-
-#include"dvparser.h"
-#include"meters.h"
-#include"meters_common_implementation.h"
-#include"wmbus.h"
-#include"wmbus_utils.h"
-#include"util.h"
-
-#include
-
-using namespace std;
-
-struct MeterEvo868 : public virtual MeterCommonImplementation {
- MeterEvo868(MeterInfo &mi);
-
- // Total water counted through the meter
- double totalWaterConsumption(Unit u);
- bool hasTotalWaterConsumption();
- uint32_t error_flags_ {};
- string fabrication_no_;
- double consumption_at_set_date_m3_ {};
- string set_date_;
- double consumption_at_set_date_2_m3_ {};
- string set_date_2_;
- double max_flow_since_datetime_m3h_ {};
- string max_flow_datetime_;
-
- double consumption_at_history_date_m3_[12];
- string history_date_[12];
-
- string status();
-
-private:
- void processContent(Telegram *t);
-
- double total_water_consumption_m3_ {};
- string device_date_time_;
-};
-
-shared_ptr createEVO868(MeterInfo &mi)
-{
- return shared_ptr(new MeterEvo868(mi));
-}
-
-MeterEvo868::MeterEvo868(MeterInfo &mi) :
- MeterCommonImplementation(mi, "evo868")
-{
- setMeterType(MeterType::WaterMeter);
-
- setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
-
- addLinkMode(LinkMode::T1);
-
- addPrint("total", Quantity::Volume,
- [&](Unit u){ return totalWaterConsumption(u); },
- "The total water consumption recorded by this meter.",
- PrintProperty::FIELD | PrintProperty::JSON);
-
- addPrint("device_date_time", Quantity::Text,
- [&](){ return device_date_time_; },
- "Device date time.",
- PrintProperty::JSON);
-
- addPrint("current_status", Quantity::Text,
- [&](){ return status(); },
- "Status of meter.",
- PrintProperty::FIELD | PrintProperty::JSON);
-
- addPrint("fabrication_no", Quantity::Text,
- [&](){ return fabrication_no_; },
- "Fabrication number.",
- PrintProperty::JSON);
-
- addPrint("consumption_at_set_date", Quantity::Volume,
- [&](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_set_date_m3_, Unit::M3, u); },
- "The total water consumption at the most recent billing period date.",
- PrintProperty::FIELD | PrintProperty::JSON);
-
- addPrint("set_date", Quantity::Text,
- [&](){ return set_date_; },
- "The most recent billing period date.",
- PrintProperty::FIELD | PrintProperty::JSON);
-
- addPrint("consumption_at_set_date_2", Quantity::Volume,
- [&](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_set_date_2_m3_, Unit::M3, u); },
- "The total water consumption at the second recent billing period date.",
- PrintProperty::JSON);
-
- addPrint("set_date_2", Quantity::Text,
- [&](){ return set_date_2_; },
- "The second recent billing period date.",
- PrintProperty::JSON);
-
- addPrint("max_flow_since_datetime", Quantity::Flow,
- [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(max_flow_since_datetime_m3h_, Unit::M3H, u); },
- "Maximum water flow since date time.",
- PrintProperty::JSON);
-
- addPrint("max_flow_datetime", Quantity::Text,
- [&](){ return max_flow_datetime_; },
- "The datetime to which maximum flow is measured.",
- PrintProperty::JSON);
-
- for (int i=1; i<=12; ++i)
- {
- string key = tostrprintf("consumption_at_history_%d", i);
- string epl = tostrprintf("The total water consumption at the history date %d.", i);
-
- addPrint(key, Quantity::Volume,
- [this,i](Unit u){ assertQuantity(u, Quantity::Volume); return convert(consumption_at_history_date_m3_[i-1], Unit::M3, u); },
- epl,
- PrintProperty::JSON);
- key = tostrprintf("history_%d_date", i);
- epl = tostrprintf("The history date %d.", i);
-
- addPrint(key, Quantity::Text,
- [this,i](){ return history_date_[i-1]; },
- epl,
- PrintProperty::JSON);
- }
-
-}
-
-void MeterEvo868::processContent(Telegram *t)
-{
- /*
-(evo868) 11: 04 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 12: 13 vif (Volume l)
-(evo868) 13: * 06070000 total consumption (1.798000 m3)
-
-(evo868) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 18: 6D vif (Date and time type)
-(evo868) 19: 1E31B121
-
-(evo868) 1d: 04 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 1e: FD vif (Second extension of VIF-codes)
-(evo868) 1f: 17 vife (Error flags (binary))
-(evo868) 20: 00000000
-
-(evo868) 24: 0E dif (12 digit BCD Instantaneous value)
-(evo868) 25: 78 vif (Fabrication no)
-(evo868) 26: 788004812000
-
-(evo868) 2c: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 2d: 13 vif (Volume l)
-(evo868) 2e: C9040000
-
-(evo868) 32: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 33: 6C vif (Date type G)
-(evo868) 34: 9F2C
-
-(evo868) 36: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 37: 01 dife (subunit=0 tariff=0 storagenr=2)
-(evo868) 38: 13 vif (Volume l)
-(evo868) 39: C9040000
-
-(evo868) 3d: 82 dif (16 Bit Integer/Binary Instantaneous value)
-(evo868) 3e: 01 dife (subunit=0 tariff=0 storagenr=2)
-(evo868) 3f: 6C vif (Date type G)
-(evo868) 40: 9F2C
-
-(evo868) 42: D3 dif (24 Bit Integer/Binary Maximum value storagenr=1)
-(evo868) 43: 01 dife (subunit=0 tariff=0 storagenr=3)
-(evo868) 44: 3B vif (Volume flow l/h)
-(evo868) 45: 9A0200
-
-(evo868) 48: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 49: 01 dife (subunit=0 tariff=0 storagenr=3)
-(evo868) 4a: 6D vif (Date and time type)
-(evo868) 4b: 0534A721
-
-(evo868) 4f: 81 dif (8 Bit Integer/Binary Instantaneous value)
-(evo868) 50: 04 dife (subunit=0 tariff=0 storagenr=8)
-(evo868) 51: FD vif (Second extension of VIF-codes)
-(evo868) 52: 28 vife (Storage interval month(s))
-(evo868) 53: 01
-
-(evo868) 54: 82 dif (16 Bit Integer/Binary Instantaneous value)
-(evo868) 55: 04 dife (subunit=0 tariff=0 storagenr=8)
-(evo868) 56: 6C vif (Date type G)
-(evo868) 57: 9F2C
-
-(evo868) 59: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 5a: 04 dife (subunit=0 tariff=0 storagenr=8)
-(evo868) 5b: 13 vif (Volume l)
-(evo868) 5c: C9040000
-
-(evo868) 60: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 61: 04 dife (subunit=0 tariff=0 storagenr=9)
-(evo868) 62: 13 vif (Volume l)
-(evo868) 63: 1B000000
-
-(evo868) 67: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 68: 05 dife (subunit=0 tariff=0 storagenr=10)
-(evo868) 69: 13 vif (Volume l)
-(evo868) 6a: 00000000
-
-(evo868) 6e: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 6f: 05 dife (subunit=0 tariff=0 storagenr=11)
-(evo868) 70: 13 vif (Volume l)
-(evo868) 71: 00000000
-
-(evo868) 75: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 76: 06 dife (subunit=0 tariff=0 storagenr=12)
-(evo868) 77: 13 vif (Volume l)
-(evo868) 78: 00000000
-
-(evo868) 7c: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 7d: 06 dife (subunit=0 tariff=0 storagenr=13)
-(evo868) 7e: 13 vif (Volume l)
-(evo868) 7f: 00000000
-
-(evo868) 83: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 84: 07 dife (subunit=0 tariff=0 storagenr=14)
-(evo868) 85: 13 vif (Volume l)
-(evo868) 86: 00000000
-
-(evo868) 8a: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 8b: 07 dife (subunit=0 tariff=0 storagenr=15)
-(evo868) 8c: 13 vif (Volume l)
-(evo868) 8d: 00000000
-
-(evo868) 91: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) 92: 08 dife (subunit=0 tariff=0 storagenr=16)
-(evo868) 93: 13 vif (Volume l)
-(evo868) 94: 00000000
-
-(evo868) 98: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) 99: 08 dife (subunit=0 tariff=0 storagenr=17)
-(evo868) 9a: 13 vif (Volume l)
-(evo868) 9b: 00000000
-
-(evo868) 9f: 84 dif (32 Bit Integer/Binary Instantaneous value)
-(evo868) a0: 09 dife (subunit=0 tariff=0 storagenr=18)
-(evo868) a1: 13 vif (Volume l)
-(evo868) a2: 00000000
-
-(evo868) a6: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(evo868) a7: 09 dife (subunit=0 tariff=0 storagenr=19)
-(evo868) a8: 13 vif (Volume l)
-(evo868) a9: 00000000
- */
- int offset;
- string key;
-
- if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
- extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_m3_);
- t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) {
- struct tm datetime;
- extractDVdate(&t->dv_entries, key, &offset, &datetime);
- device_date_time_ = strdatetime(&datetime);
- t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str());
- }
-
- extractDVuint32(&t->dv_entries, "04FD17", &offset, &error_flags_);
- t->addMoreExplanation(offset, " error flags (%s)", status().c_str());
-
- extractDVReadableString(&t->dv_entries, "0E78", &offset, &fabrication_no_);
- t->addMoreExplanation(offset, " fabrication no (%s)", fabrication_no_.c_str());
-
- if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries)) {
- extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_m3_);
- t->addMoreExplanation(offset, " consumption at set date (%f m3)", consumption_at_set_date_m3_);
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 1, 0, &key, &t->dv_entries)) {
- struct tm date;
- extractDVdate(&t->dv_entries, key, &offset, &date);
- set_date_ = strdate(&date);
- t->addMoreExplanation(offset, " set date (%s)", set_date_.c_str());
- }
-
- if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 2, 0, &key, &t->dv_entries)) {
- extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_set_date_2_m3_);
- t->addMoreExplanation(offset, " consumption at set date 2 (%f m3)", consumption_at_set_date_2_m3_);
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 2, 0, &key, &t->dv_entries)) {
- struct tm date;
- extractDVdate(&t->dv_entries, key, &offset, &date);
- set_date_2_ = strdate(&date);
- t->addMoreExplanation(offset, " set date 2 (%s)", set_date_2_.c_str());
- }
-
- if(findKey(MeasurementType::Maximum, VIFRange::VolumeFlow, 3, 0, &key, &t->dv_entries)) {
- extractDVdouble(&t->dv_entries, key, &offset, &max_flow_since_datetime_m3h_);
- t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_since_datetime_m3h_);
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 3, 0, &key, &t->dv_entries)) {
- struct tm datetime;
- extractDVdate(&t->dv_entries, key, &offset, &datetime);
- max_flow_datetime_ = strdatetime(&datetime);
- t->addMoreExplanation(offset, " max flow since datetime (%s)", max_flow_datetime_.c_str());
- }
-
- uint8_t month_increment = 0;
- extractDVuint8(&t->dv_entries, "8104FD28", &offset, &month_increment);
- t->addMoreExplanation(offset, " month increment (%d)", month_increment);
-
- struct tm date;
- if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 8, 0, &key, &t->dv_entries)) {
- extractDVdate(&t->dv_entries, key, &offset, &date);
- string start = strdate(&date);
- t->addMoreExplanation(offset, " history starts with date (%s)", start.c_str());
- }
-
- // 12 months of historical data, starting in storage 8.
- for (int i=1; i<=12; ++i)
- {
- if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, i+7, 0, &key, &t->dv_entries)) {
- extractDVdouble(&t->dv_entries, key, &offset, &consumption_at_history_date_m3_[i-1]);
- t->addMoreExplanation(offset, " consumption at history %d (%f m3)", i, consumption_at_history_date_m3_[i-1]);
- struct tm d = date;
- if (i>1) addMonths(&d, 1-i);
- history_date_[i-1] = strdate(&d);
- }
- }
-}
-
-double MeterEvo868::totalWaterConsumption(Unit u)
-{
- assertQuantity(u, Quantity::Volume);
- return convert(total_water_consumption_m3_, Unit::M3, u);
-}
-
-bool MeterEvo868::hasTotalWaterConsumption()
-{
- return true;
-}
-
-string MeterEvo868::status()
-{
- if (error_flags_ == 0)
- {
- return "OK";
- }
-
- /* Possible errors according to datasheet:
- overflow (threshold configurable, must be activated)
- backflow (threshold set, configurable)
- leak
- meter blocked
- non-used (days threshold set, configurable)
- magnetic and mechanical tampering (removal)
- */
-
- // How do we decode these?
- string info;
- strprintf(&info, "ERROR bits %08x", error_flags_);
- return info;
-}
diff --git a/src/meters.cc b/src/meters.cc
index d332a3f..f806b1c 100644
--- a/src/meters.cc
+++ b/src/meters.cc
@@ -1871,7 +1871,8 @@ bool MeterCommonImplementation::handleTelegram(AboutTelegram &about, vector found;
+ // Multiple dventries can be matched against a single wildcard FieldInfo.
+ map> founds;
// Sort the dv_entries based on their offset in the telegram.
// I.e. restore the ordering that was implicit in the telegram.
@@ -1902,14 +1903,14 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
{
current_match_nr++;
- if (fi.matcher().index_nr != IndexNr(current_match_nr))
+ if (fi.matcher().index_nr != IndexNr(current_match_nr) &&
+ !fi.matcher().expectedToMatchAgainstMultipleEntries())
{
// This field info did match, but requires another index nr!
// Increment the current index nr and look for the next match.
}
- else if (found.count(&fi) == 0)
+ else if (founds[&fi].count(dve) == 0 || fi.matcher().expectedToMatchAgainstMultipleEntries())
{
- // This field_info has not been matched to a dv_entry before!
debug("(meters) using field info %s(%s)[%d] to extract %s at offset %d\n",
fi.vname().c_str(),
toString(fi.xuantity()),
@@ -1919,19 +1920,27 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
dve->addFieldInfo(&fi);
fi.performExtraction(this, t, dve);
- found[&fi] = dve;
+ founds[&fi].insert(dve);
}
else
{
- DVEntry *old = found[&fi];
+ if (isVerboseEnabled())
+ {
+ set old = founds[&fi];
+ string olds;
+ for (DVEntry *dve : old)
+ {
+ olds += to_string(dve->offset)+",";
+ }
+ olds.pop_back();
- verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since "
- "field %s was already matched against dventry %s at offset %d !\n",
- dve->dif_vif_key.str().c_str(),
- dve->offset,
- fi.vname().c_str(),
- old->dif_vif_key.str().c_str(),
- old->offset);
+ verbose("(meter) while processing field extractors ignoring dventry %s at offset %d matching since "
+ "field %s was already matched against offsets %s !\n",
+ dve->dif_vif_key.str().c_str(),
+ dve->offset,
+ fi.vname().c_str(),
+ olds.c_str());
+ }
}
}
}
@@ -1945,7 +1954,7 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
{
fi.performExtraction(this, t, NULL);
}
- else if (found.count(&fi) == 0 && fi.printProperties().hasJOINTPLSTATUS())
+ else if (founds.count(&fi) == 0 && fi.printProperties().hasJOINTPLSTATUS())
{
// This is a status field and it joins the tpl status but it also
// has a potential dve match, which did not trigger. Now
@@ -2144,15 +2153,22 @@ FieldInfo::FieldInfo(int index,
string FieldInfo::renderJsonOnlyDefaultUnit(Meter *m)
{
- return renderJson(m, NULL);
+ return renderJson(m, NULL, NULL);
}
string FieldInfo::renderJsonText(Meter *m)
{
- return renderJson(m, NULL);
+ return renderJson(m, NULL, NULL);
}
-string FieldInfo::generateFieldName(DVEntry *dve)
+string FieldInfo::generateFieldNameNoUnit(DVEntry *dve)
+{
+ if (!valid_field_name_) return "bad_field_name";
+
+ return field_name_->apply(dve);
+}
+
+string FieldInfo::generateFieldNameWithUnit(DVEntry *dve)
{
if (!valid_field_name_) return "bad_field_name";
@@ -2167,12 +2183,14 @@ string FieldInfo::generateFieldName(DVEntry *dve)
return var+"_"+default_unit;
}
-string FieldInfo::renderJson(Meter *m, vector *conversions)
+
+string FieldInfo::renderJson(Meter *m, DVEntry *dve, vector *conversions)
{
string s;
string default_unit = unitToStringLowerCase(defaultUnit());
- string var = vname();
+ string field_name = generateFieldNameNoUnit(dve);
+
if (xuantity() == Quantity::Text)
{
string v = m->getStringValue(this);
@@ -2182,18 +2200,18 @@ string FieldInfo::renderJson(Meter *m, vector *conversions)
// be translated into "something":null in the json, indicating that there is no value.
// This should not be a problem for now. Lets deal with it when a meter decides to send "null"
// as its version string for example.
- s += "\""+var+"\":null";
+ s += "\""+field_name+"\":null";
}
else
{
// Normally the string values are quoted in json. TODO quote the value properly.
// A well crafted meter could send a version string with " and break the json format.
- s += "\""+var+"\":\""+v+"\"";
+ s += "\""+field_name+"\":\""+v+"\"";
}
}
else
{
- s += "\""+var+"_"+default_unit+"\":"+valueToString(m->getNumericValue(this, defaultUnit()), defaultUnit());
+ s += "\""+field_name+"_"+default_unit+"\":"+valueToString(m->getNumericValue(this, defaultUnit()), defaultUnit());
if (conversions != NULL)
{
@@ -2202,7 +2220,7 @@ string FieldInfo::renderJson(Meter *m, vector *conversions)
{
string unit = unitToStringLowerCase(u);
// Appending extra conversion unit.
- s += ",\""+var+"_"+unit+"\":"+valueToString(m->getNumericValue(this, u), u);
+ s += ",\""+field_name+"_"+unit+"\":"+valueToString(m->getNumericValue(this, u), u);
}
}
}
@@ -2259,7 +2277,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
}
// Iterate over the meter field infos...
- map found; // Found from the telegram
+ map> founds; // Multiple dventries can match to a single field info.
set found_vnames;
for (FieldInfo& fi : field_infos_)
@@ -2274,10 +2292,11 @@ void MeterCommonImplementation::printMeter(Telegram *t,
// Has the entry been matches to this field, then print it as json.
if (dve->hasFieldInfo(&fi))
{
- assert(found.count(&fi) == 0);
+ assert(founds[&fi].count(dve) == 0);
- found[&fi] = dve;
- found_vnames.insert(fi.vname());
+ founds[&fi].insert(dve);
+ string field_name = fi.generateFieldNameNoUnit(dve);
+ found_vnames.insert(field_name);
}
}
}
@@ -2287,17 +2306,20 @@ void MeterCommonImplementation::printMeter(Telegram *t,
{
if (fi.printProperties().hasJSON())
{
- if (found.count(&fi) != 0)
+ if (founds.count(&fi) != 0)
{
- DVEntry *dve = found[&fi];
- debug("(meters) render field %s(%s %s)[%d] with dventry @%d key %s data %s\n",
- fi.vname().c_str(), toString(fi.xuantity()), unitToStringLowerCase(fi.defaultUnit()).c_str(), fi.index(),
- dve->offset,
- dve->dif_vif_key.str().c_str(),
- dve->value.c_str());
- string out = fi.renderJson(this, &conversions());
- debug("(meters) %s\n", out.c_str());
- s += indent+out+","+newline;
+ // This field info has matched against some dventries.
+ for (DVEntry *dve : founds[&fi])
+ {
+ debug("(meters) render field %s(%s %s)[%d] with dventry @%d key %s data %s\n",
+ fi.vname().c_str(), toString(fi.xuantity()), unitToStringLowerCase(fi.defaultUnit()).c_str(), fi.index(),
+ dve->offset,
+ dve->dif_vif_key.str().c_str(),
+ dve->value.c_str());
+ string out = fi.renderJson(this, dve, &conversions());
+ debug("(meters) %s\n", out.c_str());
+ s += indent+out+","+newline;
+ }
}
else
{
@@ -2314,7 +2336,7 @@ void MeterCommonImplementation::printMeter(Telegram *t,
// Or if no value has been received, null.
debug("(meters) render field %s(%s)[%d] without dventry\n",
fi.vname().c_str(), toString(fi.xuantity()), fi.index());
- string out = fi.renderJson(this, &conversions());
+ string out = fi.renderJson(this, NULL, &conversions());
debug("(meters) %s\n", out.c_str());
s += indent+out+","+newline;
}
@@ -2813,8 +2835,11 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve)
assert(dve != NULL);
assert(key == "" || dve->dif_vif_key.str() == key);
- // Generate the json field name:
- string field_name = generateFieldName(dve);
+ string field_name;
+ if (isDebugEnabled())
+ {
+ field_name = generateFieldNameWithUnit(dve);
+ }
double extracted_double_value = NAN;
if (dve->extractDouble(&extracted_double_value,
@@ -2848,7 +2873,7 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve)
extracted_double_value);
}
m->setNumericValue(this, default_unit_, convert(extracted_double_value, decoded_unit, default_unit_));
- t->addMoreExplanation(dve->offset, renderJson(m, &m->conversions()));
+ t->addMoreExplanation(dve->offset, renderJson(m, dve, &m->conversions()));
found = true;
}
return found;
@@ -2939,7 +2964,7 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve)
assert(key == "" || dve->dif_vif_key.str() == key);
// Generate the json field name:
- string field_name = generateFieldName(dve);
+ string field_name = generateFieldNameNoUnit(dve);
uint64_t extracted_bits {};
if (lookup_.hasLookups() || (print_properties_.hasJOINTPLSTATUS()))
diff --git a/src/meters.h b/src/meters.h
index 914f8b2..3dd7214 100644
--- a/src/meters.h
+++ b/src/meters.h
@@ -62,7 +62,6 @@ LIST_OF_METER_TYPES
X(auto, 0, AutoMeter, AUTO, Auto) \
X(unknown, 0, UnknownMeter, UNKNOWN, Unknown) \
X(eurisii, T1_bit, HeatCostAllocationMeter, EURISII, EurisII) \
- X(evo868, T1_bit, WaterMeter, EVO868, EVO868) \
X(gransystems,T1_bit, ElectricityMeter, CCx01, CCx01) \
X(hydrocalm3, T1_bit, HeatMeter, HYDROCALM3, HydrocalM3) \
X(hydrodigit, T1_bit, WaterMeter, HYDRODIGIT, Hydrodigit) \
@@ -350,13 +349,14 @@ struct FieldInfo
void performCalculation(Meter *m);
string renderJsonOnlyDefaultUnit(Meter *m);
- string renderJson(Meter *m, vector *additional_conversions);
+ string renderJson(Meter *m, DVEntry *dve, vector *additional_conversions);
string renderJsonText(Meter *m);
// Render the field name based on the actual field from the telegram.
// A FieldInfo can be declared to handle any number of storage fields of a certain range.
// The vname is then a pattern total_at_month_{storage_counter} that gets translated into
// total_at_month_2 (for the dventry with storage nr 2.)
- string generateFieldName(DVEntry *dve);
+ string generateFieldNameWithUnit(DVEntry *dve);
+ string generateFieldNameNoUnit(DVEntry *dve);
// Check if the meter object stores a value for this field.
bool hasValue(Meter *m);
diff --git a/src/testinternals.cc b/src/testinternals.cc
index 0ed6a79..8bb51e5 100644
--- a/src/testinternals.cc
+++ b/src/testinternals.cc
@@ -1790,6 +1790,9 @@ LIST_OF_QUANTITIES
test_si_convert(1.0, 60.0, Unit::Minute, "min", Unit::Second, "s", Quantity::Time, &from_set, &to_set);
// 1 day is 1/365.2425 year
test_si_convert(1.0, 1.0/365.2425, Unit::Day, "d", Unit::Year, "y", Quantity::Time, &from_set, &to_set);
+ // 1 month is 30.437 days
+ test_si_convert(2.0, 30.437*2, Unit::Month, "month", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
+ test_si_convert(30.437*2, 2.0, Unit::Day, "d", Unit::Month, "month", Quantity::Time, &from_set, &to_set);
// 100 hours is 100/24 days.
test_si_convert(100.0, 100.0/24.0, Unit::Hour, "h", Unit::Day, "d", Quantity::Time, &from_set, &to_set);
// 1 year is 365.2425 days.
diff --git a/src/units.cc b/src/units.cc
index 750d6ad..7c81377 100644
--- a/src/units.cc
+++ b/src/units.cc
@@ -93,6 +93,7 @@ using namespace std;
X(Minute, 60.0, SIExp().s(1)) \
X(Hour, 3600.0, SIExp().s(1)) \
X(Day, 3600.0*24, SIExp().s(1)) \
+ X(Month, 3600.0*24*30.437, SIExp().s(1)) \
X(Year, 3600.0*24*365.2425, SIExp().s(1)) \
X(DateTimeUT, 1.0, SIExp().s(1)) \
X(DateTimeUTC, 1.0, SIExp().s(1)) \
diff --git a/src/units.h b/src/units.h
index 2768faf..1c8a61b 100644
--- a/src/units.h
+++ b/src/units.h
@@ -102,6 +102,7 @@ LIST_OF_QUANTITIES
X(Minute,min,"min",Time,"minute") \
X(Hour,h,"h",Time,"hour") \
X(Day,d,"d",Time,"day") \
+ X(Month,month,"month",Time,"month") \
X(Year,y,"y",Time,"year") \
X(DateTimeUT,ut,"ut",PointInTime,"unix timestamp") \
X(DateTimeUTC,utc,"utc",PointInTime,"coordinated universal time") \
diff --git a/test.sh b/test.sh
index eb83096..61c3463 100755
--- a/test.sh
+++ b/test.sh
@@ -9,6 +9,12 @@ then
exit 1
fi
+if ! command -v jq > /dev/null
+then
+ echo "You have to install jq !"
+ exit 1
+fi
+
$TESTINTERNAL
if [ "$?" = "0" ]; then
echo OK: test internals
diff --git a/tests/test_anyid.sh b/tests/test_anyid.sh
index 670d5be..88c2891 100755
--- a/tests/test_anyid.sh
+++ b/tests/test_anyid.sh
@@ -8,15 +8,15 @@ TEST=testoutput
TESTNAME="Test ANYID"
TESTRESULT="ERROR"
-cat > $TEST/test_expected.txt < $TEST/test_expected.txt
{"media":"cold water","meter":"multical21","name":"Vatten","id":"76348799","status":"DRY","total_m3":6.408,"target_m3":6.408,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
EOF
$PROG --format=json 2A442D2C998734761B168D2091D37CAC21576C7802FF207100041308190000441308190000615B7F616713 \
- Vatten auto ANYID NOKEY > $TEST/test_output.txt 2>&1
+ Vatten auto ANYID NOKEY 2>&1 | jq --sort-keys . > $TEST/test_output.txt
if [ "$?" = "0" ]
then
- cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
+ cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
diff $TEST/test_expected.txt $TEST/test_responses.txt
if [ "$?" = "0" ]
then
diff --git a/tests/test_c1_meters.sh b/tests/test_c1_meters.sh
index ace6536..39624eb 100755
--- a/tests/test_c1_meters.sh
+++ b/tests/test_c1_meters.sh
@@ -9,7 +9,7 @@ TEST=testoutput
TESTNAME="Test C1 meters"
TESTRESULT="ERROR"
-cat simulations/simulation_c1.txt | grep '^{' > $TEST/test_expected.txt
+cat simulations/simulation_c1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
$PROG --format=json simulations/simulation_c1.txt \
MyHeater multical302 67676767 NOKEY \
MyHeaterMj multical302 46464646 NOKEY \
@@ -27,11 +27,11 @@ $PROG --format=json simulations/simulation_c1.txt \
Smokey ei6500 00012811 NOKEY \
Vatten weh_07 86868686 NOKEY \
Mino minomess 55036410 NOKEY \
- > $TEST/test_output.txt 2> $TEST/test_stderr.txt
+ | jq --sort-keys . > $TEST/test_output.txt 2> $TEST/test_stderr.txt
if [ "$?" = "0" ]
then
- cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
+ cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
diff $TEST/test_expected.txt $TEST/test_responses.txt
if [ "$?" = "0" ]
then
diff --git a/tests/test_drivers.sh b/tests/test_drivers.sh
index 068b36a..7d21617 100755
--- a/tests/test_drivers.sh
+++ b/tests/test_drivers.sh
@@ -41,8 +41,6 @@ do
echo OK json: $TESTNAME
TESTRESULT="OK"
else
- jq --sort-keys . $TEST/test_expected_json.txt
- jq --sort-keys . $TEST/test_response_json.txt
TESTRESULT="ERROR"
fi
else
diff --git a/tests/test_list_envs.sh b/tests/test_list_envs.sh
index 559eca8..a8d4bba 100755
--- a/tests/test_list_envs.sh
+++ b/tests/test_list_envs.sh
@@ -8,9 +8,9 @@ TEST=testoutput
TESTNAME="Test list-envs and list-fields"
TESTRESULT="ERROR"
-$PROG --listenvs=amiplus > $TEST/test_output.txt 2>&1
+$PROG --listenvs=amiplus | sort > $TEST/test_output.txt 2>&1
-cat < $TEST/test_expected.txt
+cat < $TEST/test_expected.txt
METER_JSON
METER_ID
METER_NAME
@@ -54,9 +54,9 @@ then
exit 1
fi
-$PROG --listfields=amiplus > $TEST/test_output.txt 2>&1
+$PROG --listfields=amiplus | sort > $TEST/test_output.txt 2>&1
-cat < $TEST/test_expected.txt
+cat < $TEST/test_expected.txt
id The meter id number.
name Your name for the meter.
media What does the meter measure?
diff --git a/tests/test_listen_to_all.sh b/tests/test_listen_to_all.sh
index 3ad532c..12cc13d 100755
--- a/tests/test_listen_to_all.sh
+++ b/tests/test_listen_to_all.sh
@@ -255,11 +255,6 @@ Received telegram from: 66336633
type: Heat meter (0x43)
ver: 0x39
driver: compact5
-Received telegram from: 79787776
- manufacturer: (MAD) Maddalena, Italy (0x3424)
- type: Water meter (0x07)
- ver: 0x50
- driver: evo868
Received telegram from: 18046178
manufacturer: (GSS) R D Gran System S, Belarus (0x1e73)
type: Electricity meter (0x02)
diff --git a/tests/test_mbus.sh b/tests/test_mbus.sh
index 7a954f2..d2bd7a4 100755
--- a/tests/test_mbus.sh
+++ b/tests/test_mbus.sh
@@ -9,15 +9,15 @@ TEST=testoutput
TESTNAME="Test mbus"
TESTRESULT="ERROR"
-cat simulations/simulation_mbus.txt | grep '^{' > $TEST/test_expected.txt
+cat simulations/simulation_mbus.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
$PROG --format=json simulations/simulation_mbus.txt \
MyUltra ultraheat 70444600 NOKEY \
MySenso sensostar 10484075 NOKEY \
- > $TEST/test_output.txt 2> $TEST/test_stderr.txt
+ 2> $TEST/test_stderr.txt | jq --sort-keys . > $TEST/test_output.txt
if [ "$?" = "0" ]
then
- cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
+ cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
diff $TEST/test_expected.txt $TEST/test_responses.txt
if [ "$?" = "0" ]
then
diff --git a/tests/test_s1_meters.sh b/tests/test_s1_meters.sh
index 5aa719f..d375917 100755
--- a/tests/test_s1_meters.sh
+++ b/tests/test_s1_meters.sh
@@ -14,11 +14,11 @@ METERS="HCA auto 91835132 NOKEY \
QWW auto 11121314 NOKEY \
QWWW auto 12417708 NOKEY"
-cat simulations/simulation_s1.txt | grep '^{' > $TEST/test_expected.txt
-$PROG --format=json simulations/simulation_s1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt
+cat simulations/simulation_s1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
+$PROG --format=json simulations/simulation_s1.txt $METERS 2> $TEST/test_stderr.txt | jq --sort-keys . > $TEST/test_output.txt
if [ "$?" = "0" ]
then
- cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
+ cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
diff $TEST/test_expected.txt $TEST/test_responses.txt
if [ "$?" = "0" ]
then
diff --git a/tests/test_t1_meters.sh b/tests/test_t1_meters.sh
index 55483b0..a771f27 100755
--- a/tests/test_t1_meters.sh
+++ b/tests/test_t1_meters.sh
@@ -24,7 +24,6 @@ METERS="MyWarmWater supercom587 12345678 NOKEY
Gran101 gransystems 18046178 NOKEY
Gran301 gransystems 20100117 NOKEY
HeatMeter eurisii 88018801 NOKEY
- Votchka evo868 79787776 NOKEY
Smokeo lansensm 00010204 NOKEY
Tempoo lansenth 00010203 NOKEY
Dooro lansendw 00010205 NOKEY
@@ -70,11 +69,11 @@ METERS="MyWarmWater supercom587 12345678 NOKEY
QSmokep qsmoke 48128850 NOKEY"
-cat simulations/simulation_t1.txt | grep '^{' > $TEST/test_expected.txt
-$PROG --format=json simulations/simulation_t1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt
+cat simulations/simulation_t1.txt | grep '^{' | jq --sort-keys . > $TEST/test_expected.txt
+$PROG --format=json simulations/simulation_t1.txt $METERS 2> $TEST/test_stderr.txt | jq --sort-keys . > $TEST/test_output.txt
if [ "$?" = "0" ]
then
- cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
+ cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
diff $TEST/test_expected.txt $TEST/test_responses.txt
if [ "$?" = "0" ]
then