diff --git a/CHANGES b/CHANGES
index 033e961..ee1fc49 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,15 @@
+ATTENTION! When values are missing in the fields output, they were previously
+reported as "nan" but they are now reported as "null".
+
+ATTENTION! The unismart driver has been refactored to the new driver format.
+Several fields were unknown before and are still unknown and their content
+have slightly changed: status and version.
+
+The field "other_counter" is replaced with just "other" and its content changed.
+The field "suppler_info" is replaced with "supplier_info".
+The field "device_date_time" is replaced with "device_timestamp" and now contains seconds
+and a timezone.
+
ATTENTION! A bug was fixed where the on_time_h and operating_time_h
(and related error times) were wrong.
diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt
index d6dc789..012250c 100644
--- a/simulations/simulation_t1.txt
+++ b/simulations/simulation_t1.txt
@@ -29,14 +29,14 @@ telegram=|1844AE4C4455223368077A55000000_041389E20100023B0000|
# Test amiplus/apator electricity meter
telegram=|4E4401061010101002027A00004005_2F2F0E035040691500000B2B300300066D00790C7423400C78371204860BABC8FC100000000E833C8074000000000BAB3C0000000AFDC9FC0136022F2F2F2F2F|
-{"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
-|MyElectricity1;10101010;15694.050000;0.330000;7.480000;0.000000;236.000000;nan;nan;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
+{"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
+|MyElectricity1;10101010;15694.05;0.33;7.48;0;236;null;null;null;null;null;null;null;null;1111-11-11 11:11.11
# Test amiplus/apator electricity meter with three phase voltages
telegram=|5E44B6105843250000027A2A005005_2F2F0C7835221400066D404708AC2A400E032022650900000E833C0000000000001B2B9647000B2B5510000BAB3C0000000AFDC9FC0135020AFDC9FC0245020AFDC9FC0339020BABC8FC100000002F2F|
-{"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
-|MyElectricity2;00254358;9652.220000;1.055000;0.000000;0.000000;235.000000;245.000000;239.000000;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
+{"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
+|MyElectricity2;00254358;9652.22;1.055;0;0;235;245;239;null;null;null;null;null;null;1111-11-11 11:11.11
# Test MKRadio3 T1 telegrams
@@ -294,8 +294,8 @@ telegram=76442104710007612507727100076121042507B5006005E2E95A3C2A1279A5415E67326
# Test Unismart Gas Meter
telegram=|6044B8059430040001037A1D005085E2B670BCF1A5C87E0C1A51DA18924EF984613DA2A9CD39D8F4C7208326C76D42DBEADF80D574192B71BD7C4F56A7F1513151768A9DB804883B28CB085CA2D0F7438C361CB9E2734712ED9BFBB2A14EF55208|
-{"media":"gas","meter":"unismart","name":"GasMeter","id":"00043094","fabrication_no":"3162296","total_date_time":"2021-09-15 13:18","total_m3":917,"target_date_time":"2021-09-01 06:00","target_m3":911.32,"version":"UGG4","device_date_time":"2021-09-15 13:18","suppler_info":"00","status":"F00C","parameter_set":"02","other_counter":20,"timestamp":"1111-11-11T11:11:11Z"}
-|GasMeter;00043094;917.000000;911.320000;1111-11-11 11:11.11
+{"media":"gas","meter":"unismart","name":"GasMeter","id":"00043094","fabrication_no":"03162296","status":"STATUS_FLAGS_CF0","other":"OTHER_FLAGS_14","total_date_time":"2021-09-15 13:18","total_m3":917,"target_date_time":"2021-09-01 06:00","target_m3":911.32,"version":" 4GGU","supplier_info":"00","parameter_set":"02","meter_timestamp":"2021-09-15 13:18:30","timestamp":"1111-11-11T11:11:11Z"}
+|GasMeter;00043094;917;911.32;1111-11-11 11:11.11
# Test Hydrocal M3 heat/cooling meter
telegram=|8E44B409747372710B0D7A798080052F2F_0C0E59600100046D1D36B9290C13679947000C0E000000000C13590000000C13000000000C13000000000A5A18020A5E11020F823D06003D06003D06003D0600140600620500480400E402001601000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002F2F|
diff --git a/src/driver_amiplus.cc b/src/driver_amiplus.cc
index 6be3325..77ba740 100644
--- a/src/driver_amiplus.cc
+++ b/src/driver_amiplus.cc
@@ -27,6 +27,7 @@ namespace
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("amiplus");
+ di.setDefaultFields("name,id,total_energy_consumption_kwh,current_power_consumption_kw,total_energy_production_kwh,current_power_production_kw,voltage_at_phase_1_v,voltage_at_phase_2_v,voltage_at_phase_3_v,total_energy_consumption_tariff_1_kwh,total_energy_consumption_tariff_2_kwh,total_energy_consumption_tariff_3_kwh,total_energy_production_tariff_1_kwh,total_energy_production_tariff_2_kwh,total_energy_production_tariff_3_kwh,timestamp");
di.setMeterType(MeterType::ElectricityMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_APA, 0x02, 0x02);
@@ -188,19 +189,19 @@ namespace
// Test: MyElectricity1 amiplus 10101010 NOKEY
// telegram=|4E4401061010101002027A00004005_2F2F0E035040691500000B2B300300066D00790C7423400C78371204860BABC8FC100000000E833C8074000000000BAB3C0000000AFDC9FC0136022F2F2F2F2F|
-// {"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
-// |MyElectricity1;10101010;15694.050000;0.330000;7.480000;0.000000;236.000000;nan;nan;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
+// {"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"voltage_at_phase_1_v":236,"voltage_at_phase_2_v":null,"voltage_at_phase_3_v":null,"device_date_time":"2019-03-20 12:57:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
+// |MyElectricity1;10101010;15694.05;0.33;7.48;0;236;null;null;null;null;null;null;null;null;1111-11-11 11:11.11
// Test: MyElectricity2 amiplus 00254358 NOKEY
// amiplus/apator electricity meter with three phase voltages
// telegram=|5E44B6105843250000027A2A005005_2F2F0C7835221400066D404708AC2A400E032022650900000E833C0000000000001B2B9647000B2B5510000BAB3C0000000AFDC9FC0135020AFDC9FC0245020AFDC9FC0339020BABC8FC100000002F2F|
-// {"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
-// |MyElectricity2;00254358;9652.220000;1.055000;0.000000;0.000000;235.000000;245.000000;239.000000;nan;nan;nan;nan;nan;nan;1111-11-11 11:11.11
+// {"media":"electricity","meter":"amiplus","name":"MyElectricity2","id":"00254358","total_energy_consumption_kwh":9652.22,"current_power_consumption_kw":1.055,"total_energy_production_kwh":0,"current_power_production_kw":0,"voltage_at_phase_1_v":235,"voltage_at_phase_2_v":245,"voltage_at_phase_3_v":239,"device_date_time":"2021-10-12 08:07:00","total_energy_consumption_tariff_1_kwh":null,"total_energy_consumption_tariff_2_kwh":null,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":null,"total_energy_production_tariff_2_kwh":null,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
+// |MyElectricity2;00254358;9652.22;1.055;0;0;235;245;239;null;null;null;null;null;null;1111-11-11 11:11.11
// Test: MyElectricity3 amiplus 86064864 NOKEY
// amiplus/apator electricity meter with three phase voltages and 2 tariffs
// telegram=|804401066448068602027A000070052F2F_066D1E5C11DA21400C78644806868E10036110012500008E20038106531800008E10833C9949000000008E20833C8606000000001B2B5228020B2B3217000BAB3C0000000AFDC9FC0131020AFDC9FC0225020AFDC9FC0331020BABC8FC100000002F2F2F2F2F2F2F2F2F2F2F2F2FDE47|
-// {"media":"electricity","meter":"amiplus","name":"MyElectricity3","id":"86064864","total_energy_consumption_kwh":null,"current_power_consumption_kw":1.732,"total_energy_production_kwh":null,"current_power_production_kw":0,"voltage_at_phase_1_v":231,"voltage_at_phase_2_v":225,"voltage_at_phase_3_v":231,"device_date_time":"2022-01-26 17:28","total_energy_consumption_tariff_1_kwh":25011.061,"total_energy_consumption_tariff_2_kwh":18530.681,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":4.999,"total_energy_production_tariff_2_kwh":0.686,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
-// |MyElectricity3;86064864;nan;1.732000;nan;0.000000;231.000000;225.000000;231.000000;25011.061000;18530.681000;nan;4.999000;0.686000;nan;1111-11-11 11:11.11
+// {"media":"electricity","meter":"amiplus","name":"MyElectricity3","id":"86064864","total_energy_consumption_kwh":null,"current_power_consumption_kw":1.732,"total_energy_production_kwh":null,"current_power_production_kw":0,"voltage_at_phase_1_v":231,"voltage_at_phase_2_v":225,"voltage_at_phase_3_v":231,"device_date_time":"2022-01-26 17:28:30","total_energy_consumption_tariff_1_kwh":25011.061,"total_energy_consumption_tariff_2_kwh":18530.681,"total_energy_consumption_tariff_3_kwh":null,"total_energy_production_tariff_1_kwh":4.999,"total_energy_production_tariff_2_kwh":0.686,"total_energy_production_tariff_3_kwh":null,"timestamp":"1111-11-11T11:11:11Z"}
+// |MyElectricity3;86064864;null;1.732;null;0;231;225;231;25011.061;18530.681;null;4.999;0.686;null;1111-11-11 11:11.11
diff --git a/src/driver_cma12w.cc b/src/driver_cma12w.cc
index 42a2243..8f7a489 100644
--- a/src/driver_cma12w.cc
+++ b/src/driver_cma12w.cc
@@ -39,7 +39,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
+ addOptionalCommonFields("software_version");
addStringField(
"status",
diff --git a/src/driver_ehzp.cc b/src/driver_ehzp.cc
index 0e75fec..d417d21 100644
--- a/src/driver_ehzp.cc
+++ b/src/driver_ehzp.cc
@@ -41,7 +41,7 @@ namespace
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT |
PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS);
- addOptionalCommonFields();
+ addOptionalCommonFields("on_time_h");
addNumericFieldWithExtractor(
"total_energy_consumption",
diff --git a/src/driver_enercal.cc b/src/driver_enercal.cc
index 22d4e32..7f603be 100644
--- a/src/driver_enercal.cc
+++ b/src/driver_enercal.cc
@@ -237,7 +237,7 @@ namespace
.set(StorageNr(1))
);
- addOptionalCommonFields();
+ addOptionalCommonFields("operating_time_h,on_time_h,meter_datetime");
}
}
diff --git a/src/driver_esyswm.cc b/src/driver_esyswm.cc
index c13c119..9ccc166 100644
--- a/src/driver_esyswm.cc
+++ b/src/driver_esyswm.cc
@@ -37,7 +37,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
+ addOptionalCommonFields("fabrication_no,enhanced_id,location");
addStringFieldWithExtractor(
"location_hex",
diff --git a/src/driver_itron.cc b/src/driver_itron.cc
index f3f3501..07f0723 100644
--- a/src/driver_itron.cc
+++ b/src/driver_itron.cc
@@ -43,8 +43,8 @@ namespace
addLinkMode(LinkMode::T1);
- addOptionalCommonFields();
- addOptionalFlowRelatedFields();
+ addOptionalCommonFields("enhanced_id,meter_datetime");
+ addOptionalFlowRelatedFields("total_m3,total_backward_m3,volume_flow_m3h");
addStringFieldWithExtractorAndLookup(
"status",
@@ -136,10 +136,10 @@ namespace
// Test: SomeWater itron 12345698 NOKEY
// Comment: Test ITRON T1 telegram not encrypted, which has no 2f2f markers.
// telegram=|384497269856341203077AD90000A0#0413FD110000066D2C1AA1D521004413300F0000426CBF2C047F0000060C027F862A0E79678372082100|
-// {"media":"water","meter":"itron","name":"SomeWater","id":"12345698","enhanced_id":"002108728367","meter_datetime":"2022-01-21 01:26","total_m3":4.605,"status":"OK","target_m3":3.888,"target_date":"2021-12-31","unknown_a":"WOOTA_C060000","unknown_b":"WOOTB_2A86","timestamp":"1111-11-11T11:11:11Z"}
+// {"media":"water","meter":"itron","name":"SomeWater","id":"12345698","enhanced_id":"002108728367","meter_datetime":"2022-01-21 01:26:44","total_m3":4.605,"status":"OK","target_m3":3.888,"target_date":"2021-12-31","unknown_a":"WOOTA_C060000","unknown_b":"WOOTB_2A86","timestamp":"1111-11-11T11:11:11Z"}
// |SomeWater;12345698;4.605;3.888;1111-11-11 11:11.11
// Test: MoreWater itron 18000056 NOKEY
// telegram=|46449726560000183307725600001897263307AF0030052F2F_066D0E1015C82A000C13771252000C933C000000000B3B0400004C1361045200426CC12A03FD971C0000002F2F2F|
-// {"media":"water","meter":"itron","name":"MoreWater","id":"18000056","meter_datetime":"2022-10-08 21:16","total_m3":521.277,"total_backward_m3":0,"volume_flow_m3h":0.004,"status":"OK","target_m3":520.461,"target_date":"2022-10-01","timestamp":"1111-11-11T11:11:11Z"}
+// {"media":"water","meter":"itron","name":"MoreWater","id":"18000056","meter_datetime":"2022-10-08 21:16:14","total_m3":521.277,"total_backward_m3":0,"volume_flow_m3h":0.004,"status":"OK","target_m3":520.461,"target_date":"2022-10-01","timestamp":"1111-11-11T11:11:11Z"}
// |MoreWater;18000056;521.277;520.461;1111-11-11 11:11.11
diff --git a/src/driver_minomess.cc b/src/driver_minomess.cc
index 1a8db51..337264f 100644
--- a/src/driver_minomess.cc
+++ b/src/driver_minomess.cc
@@ -37,8 +37,8 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
- addOptionalFlowRelatedFields();
+ addOptionalCommonFields("meter_date,fabrication_no,operating_time_h,on_time_h,on_time_at_error_h,meter_datetime");
+ addOptionalFlowRelatedFields("total_m3,total_backward_m3,volume_flow_m3h");
/* If the meter is recently commissioned, the target water consumption value is bogus.
The bits store 0xffffffff. Should we deal with this? Now a very large value is printed in the json.
diff --git a/src/driver_multical603.cc b/src/driver_multical603.cc
index bb31e61..87fa60d 100644
--- a/src/driver_multical603.cc
+++ b/src/driver_multical603.cc
@@ -39,7 +39,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
+ addOptionalCommonFields("on_time_h");
// Technical Description Multical 603 page 116 section 7.7.2 Information code types on serial communication.
addStringFieldWithExtractorAndLookup(
diff --git a/src/driver_munia.cc b/src/driver_munia.cc
index 97dd7b1..571b04f 100644
--- a/src/driver_munia.cc
+++ b/src/driver_munia.cc
@@ -36,8 +36,6 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
-
addStringFieldWithExtractorAndLookup(
"status",
"Meter status. Reports OK if neither tpl sts nor error flags have bits set.",
@@ -59,11 +57,6 @@ namespace
},
}));
- /*
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::ErrorFlags)
- .add(VIFCombinable::StandardConformantDataContent)
-*/
addNumericFieldWithExtractor(
"current_temperature",
"The current temperature.",
diff --git a/src/driver_piigth.cc b/src/driver_piigth.cc
index 04b742c..a31e31b 100644
--- a/src/driver_piigth.cc
+++ b/src/driver_piigth.cc
@@ -36,7 +36,7 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
+ addOptionalCommonFields("fabrication_no,software_version");
addStringField(
"status",
diff --git a/src/driver_q400.cc b/src/driver_q400.cc
index 14de1c9..c17250b 100644
--- a/src/driver_q400.cc
+++ b/src/driver_q400.cc
@@ -36,8 +36,8 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
- addOptionalFlowRelatedFields();
+ addOptionalCommonFields("meter_datetime");
+ addOptionalFlowRelatedFields("total_m3,total_forward_m3,total_backward_m3,flow_temperature_c,volume_flow_m3h");
addStringField(
"status",
diff --git a/src/driver_qualcosonic.cc b/src/driver_qualcosonic.cc
index 558e20f..22a06b5 100644
--- a/src/driver_qualcosonic.cc
+++ b/src/driver_qualcosonic.cc
@@ -35,8 +35,8 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
- addOptionalCommonFields();
- addOptionalFlowRelatedFields();
+ addOptionalCommonFields("fabrication_no,operating_time_h,on_time_h,meter_datetime,meter_datetime_at_error");
+ addOptionalFlowRelatedFields("total_m3,flow_temperature_c,return_temperature_c,flow_return_temperature_difference_c,volume_flow_m3h");
addStringFieldWithExtractorAndLookup(
"status",
diff --git a/src/driver_unismart.cc b/src/driver_unismart.cc
new file mode 100644
index 0000000..3142e11
--- /dev/null
+++ b/src/driver_unismart.cc
@@ -0,0 +1,168 @@
+/*
+ Copyright (C) 2021-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("unismart");
+ di.setDefaultFields("name,id,total_m3,target_m3,timestamp");
+ di.setMeterType(MeterType::GasMeter);
+ di.addLinkMode(LinkMode::T1);
+ di.addDetection(MANUFACTURER_AMX, 0x03, 0x01);
+
+ di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); });
+ });
+
+ Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
+ {
+ addOptionalCommonFields("fabrication_no");
+
+ addStringFieldWithExtractorAndLookup(
+ "status",
+ "Status of meter?",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::STATUS,
+ FieldMatcher::build()
+ .set(DifVifKey("02FD74")),
+ {
+ {
+ {
+ "STATUS_FLAGS",
+ Translate::Type::BitToString,
+ 0xffff,
+ "OK",
+ {
+ }
+ },
+ },
+ });
+
+ addStringFieldWithExtractorAndLookup(
+ "other",
+ "Other status of meter?",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::STATUS,
+ FieldMatcher::build()
+ .set(DifVifKey("017F")),
+ {
+ {
+ {
+ "OTHER_FLAGS",
+ Translate::Type::BitToString,
+ 0xff,
+ "",
+ {
+ }
+ },
+ },
+ });
+
+ addStringFieldWithExtractor(
+ "total_date_time",
+ "Timestamp for this total measurement.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::DateTime)
+ .set(IndexNr(1))
+ );
+
+ addNumericFieldWithExtractor(
+ "total",
+ "The total gas consumption recorded by this meter.",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .add(VIFCombinable::UncorrectedMeterUnit)
+ );
+
+ addStringFieldWithExtractor(
+ "target_date_time",
+ "Timestamp for gas consumption recorded at the beginning of this month.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::DateTime)
+ .set(StorageNr(1))
+ );
+
+ addNumericFieldWithExtractor(
+ "target",
+ "The total gas consumption recorded by this meter at the beginning of this month.",
+ PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .set(StorageNr(1))
+ .add(VIFCombinable::UncorrectedMeterUnit)
+ );
+
+ addStringFieldWithExtractor(
+ "version",
+ "Model version.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::ModelVersion)
+ );
+
+ addStringFieldWithExtractor(
+ "supplier_info",
+ "Supplier info?",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::SpecialSupplierInformation)
+ );
+
+ addStringFieldWithExtractor(
+ "parameter_set",
+ "Meter configued with this parameter set?",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::ParameterSet)
+ );
+
+ addStringFieldWithExtractor(
+ "meter_timestamp",
+ "Timestamp when this measurement was sent.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::DateTime)
+ .set(IndexNr(2))
+ );
+
+ }
+}
+
+// Test: GasMeter unismart 00043094 00000000000000000000000000000000
+// telegram=|6044B8059430040001037A1D005085E2B670BCF1A5C87E0C1A51DA18924EF984613DA2A9CD39D8F4C7208326C76D42DBEADF80D574192B71BD7C4F56A7F1513151768A9DB804883B28CB085CA2D0F7438C361CB9E2734712ED9BFBB2A14EF55208|
+// {"media":"gas","meter":"unismart","name":"GasMeter","id":"00043094","fabrication_no":"03162296","status":"STATUS_FLAGS_CF0","other":"OTHER_FLAGS_14","total_date_time":"2021-09-15 13:18","total_m3":917,"target_date_time":"2021-09-01 06:00","target_m3":911.32,"version":" 4GGU","supplier_info":"00","parameter_set":"02","meter_timestamp":"2021-09-15 13:18:30","timestamp":"1111-11-11T11:11:11Z"}
+// |GasMeter;00043094;917;911.32;1111-11-11 11:11.11
diff --git a/src/dvparser.cc b/src/dvparser.cc
index 07db1d2..6c24fa4 100644
--- a/src/dvparser.cc
+++ b/src/dvparser.cc
@@ -1168,7 +1168,10 @@ bool DVEntry::extractDate(struct tm *out)
// ..ss ssss
int sec = (0x3f) & v[0];
out->tm_sec = sec;
- // some daylight saving time decoding needed here....
+ // There are also bits for day of week, week of year.
+ // A bit for if daylight saving is in use or not and its offset.
+ // A bit if it is a leap year.
+ // I am unsure how to deal with this here..... TODO
}
return ok;
diff --git a/src/dvparser.h b/src/dvparser.h
index 6a25078..57cb384 100644
--- a/src/dvparser.h
+++ b/src/dvparser.h
@@ -62,6 +62,7 @@
X(Current,0x7D50,0x7D5F, Quantity::Current, Unit::Ampere) \
X(ResetCounter,0x7D60,0x7D60, Quantity::Counter, Unit::COUNTER) \
X(CumulationCounter,0x7D61,0x7D61, Quantity::Counter, Unit::COUNTER) \
+ X(SpecialSupplierInformation,0x7D67,0x7D67, Quantity::Text, Unit::TXT) \
X(RemainingBattery,0x7D74,0x7D74, Quantity::Time, Unit::Day) \
X(DurationSinceReadout,0x7DAC,0x7DAC, Quantity::Time, Unit::Hour) \
X(AnyVolumeVIF,0x00,0x00, Quantity::Volume, Unit::Unknown) \
@@ -280,6 +281,7 @@ struct IndexNr
IndexNr(int n) : nr_(n) {}
int intValue() { return nr_; }
bool operator==(IndexNr s) { return nr_ == s.nr_; }
+ bool operator!=(IndexNr s) { return nr_ != s.nr_; }
private:
int nr_;
@@ -381,8 +383,9 @@ struct FieldMatcher
SubUnitNr subunit_nr_from { 0 };
SubUnitNr subunit_nr_to { 0 };
- // If the telegram has multiple identical difvif entries, use entry with this index nr.
- // First entry has nr 1, which is the default value.
+ // If the telegram has multiple identical difvif entries matching this field
+ // and you want to catch the second matching entry, then set the index nr to 2.
+ // The default is 1.
IndexNr index_nr { 1 };
FieldMatcher() : active(false) { }
diff --git a/src/meter_detection.h b/src/meter_detection.h
index 214cda2..7b5f4e0 100644
--- a/src/meter_detection.h
+++ b/src/meter_detection.h
@@ -75,7 +75,6 @@
X(SONTEX868, MANUFACTURER_SON, 0x08, 0x16) \
X(TOPASESKR, MANUFACTURER_AMT, 0x06, 0xf1) \
X(TOPASESKR, MANUFACTURER_AMT, 0x07, 0xf1) \
- X(UNISMART, MANUFACTURER_AMX, 0x03, 0x01) \
diff --git a/src/meter_unismart.cc b/src/meter_unismart.cc
deleted file mode 100644
index ef0e2c4..0000000
--- a/src/meter_unismart.cc
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- Copyright (C) 2021 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 MeterUnismart : public virtual MeterCommonImplementation {
- MeterUnismart(MeterInfo &mi);
-
- // Total gas counted through the meter
- double totalGasConsumption(Unit u);
- bool hasTotalGasConsumption();
- // Consumption at the beginning of this month.
- double targetGasConsumption(Unit u);
-
-private:
- void processContent(Telegram *t);
-
- string fabrication_no_;
- string total_date_time_;
- double total_gas_consumption_m3_ {};
- string target_date_time_;
- double target_gas_consumption_m3_ {};
- string version_;
- string device_date_time_;
-
- string supplier_info_;
- string status_;
- string parameter_set_;
- uint8_t other_;
-};
-
-shared_ptr createUnismart(MeterInfo &mi)
-{
- return shared_ptr(new MeterUnismart(mi));
-}
-
-MeterUnismart::MeterUnismart(MeterInfo &mi) :
- MeterCommonImplementation(mi, "unismart")
-{
- setMeterType(MeterType::GasMeter);
-
- setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
-
- addLinkMode(LinkMode::T1);
-
- addPrint("fabrication_no", Quantity::Text,
- [&](){ return fabrication_no_; },
- "Static fabrication no information.",
- PrintProperty::JSON);
-
- addPrint("total_date_time", Quantity::Text,
- [&](){ return total_date_time_; },
- "Timestamp for this total measurement.",
- PrintProperty::JSON);
-
- addPrint("total", Quantity::Volume,
- [&](Unit u){ return totalGasConsumption(u); },
- "The total gas consumption recorded by this meter.",
- PrintProperty::FIELD | PrintProperty::JSON);
-
- addPrint("target_date_time", Quantity::Text,
- [&](){ return target_date_time_; },
- "Timestamp for gas consumption recorded at the beginning of this month.",
- PrintProperty::JSON);
-
- addPrint("target", Quantity::Volume,
- [&](Unit u){ return targetGasConsumption(u); },
- "The total gas consumption recorded by this meter at the beginning of this month.",
- PrintProperty::FIELD | PrintProperty::JSON);
-
- addPrint("version", Quantity::Text,
- [&](){ return version_; },
- "Model/version a reported by meter.",
- PrintProperty::JSON);
-
- addPrint("device_date_time", Quantity::Text,
- [&](){ return device_date_time_; },
- "Device date time? Seems to be the same as total date time.",
- PrintProperty::JSON);
-
- addPrint("suppler_info", Quantity::Text,
- [&](){ return supplier_info_; },
- "?",
- PrintProperty::JSON);
-
- addPrint("status", Quantity::Text,
- [&](){ return status_; },
- "?",
- PrintProperty::JSON);
-
- addPrint("parameter_set", Quantity::Text,
- [&](){ return parameter_set_; },
- "?",
- PrintProperty::JSON);
-
- addPrint("other", Quantity::Counter,
- [&](Unit u){ return other_; },
- "?",
- PrintProperty::JSON);
-
-}
-
-void MeterUnismart::processContent(Telegram *t)
-{
- /*
- (unismart) 11: 0C dif (8 digit BCD Instantaneous value)
-(unismart) 12: 78 vif (Fabrication no)
-(unismart) 13: 96221603
-(unismart) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
-(unismart) 18: 6D vif (Date and time type)
-(unismart) 19: 122DAF29
-(unismart) 1d: 0C dif (8 digit BCD Instantaneous value)
-(unismart) 1e: 94 vif (Volume 10⁻² m³)
-(unismart) 1f: 3A vife (uncorrected meter unit)
-(unismart) 20: * 00170900 total consumption (917.000000 m3)
-(unismart) 24: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
-(unismart) 25: 6D vif (Date and time type)
-(unismart) 26: 0026A129
-(unismart) 2a: 4C dif (8 digit BCD Instantaneous value storagenr=1)
-(unismart) 2b: 94 vif (Volume 10⁻² m³)
-(unismart) 2c: 3A vife (uncorrected meter unit)
-(unismart) 2d: 32110900
-(unismart) 31: 01 dif (8 Bit Integer/Binary Instantaneous value)
-(unismart) 32: FD vif (Second extension FD of VIF-codes)
-(unismart) 33: 67 vife (Special supplier information)
-(unismart) 34: 00
-(unismart) 35: 02 dif (16 Bit Integer/Binary Instantaneous value)
-(unismart) 36: FD vif (Second extension FD of VIF-codes)
-(unismart) 37: 74 vife (Reserved)
-(unismart) 38: F00C
-(unismart) 3a: 0D dif (variable length Instantaneous value)
-(unismart) 3b: FD vif (Second extension FD of VIF-codes)
-(unismart) 3c: 0C vife (Model/Version)
-(unismart) 3d: 06 varlen=6
-(unismart) 3e: 554747342020
-(unismart) 44: 01 dif (8 Bit Integer/Binary Instantaneous value)
-(unismart) 45: FD vif (Second extension FD of VIF-codes)
-(unismart) 46: 0B vife (Parameter set identification)
-(unismart) 47: 02
-(unismart) 48: 01 dif (8 Bit Integer/Binary Instantaneous value)
-(unismart) 49: 7F vif (Manufacturer specific)
-(unismart) 4a: 14
-(unismart) 4b: 06 dif (48 Bit Integer/Binary Instantaneous value)
-(unismart) 4c: 6D vif (Date and time type)
-(unismart) 4d: 1E120DAF296D
-(unismart) 53: 2F skip
-(unismart) 54: 2F skip
-(unismart) 55: 2F skip
-(unismart) 56: 2F skip
-(unismart) 57: 2F skip
-(unismart) 58: 2F skip
-(unismart) 59: 2F skip
-(unismart) 5a: 2F skip
-(unismart) 5b: 2F skip
-(unismart) 5c: 2F skip
-(unismart) 5d: 2F skip
-(unismart) 5e: 2F skip
-
-*/
- int offset;
- string key;
-
- uint64_t v {};
- if (extractDVlong(&t->dv_entries, "0C78", &offset, &v))
- {
- fabrication_no_ = to_string(v);
- t->addMoreExplanation(offset, " fabrication no (%zu)", v);
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) {
- struct tm datetime;
- extractDVdate(&t->dv_entries, key, &offset, &datetime);
- total_date_time_ = strdatetime(&datetime);
- t->addMoreExplanation(offset, " total datetime (%s)", total_date_time_.c_str());
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries))
- {
- extractDVdouble(&t->dv_entries, key, &offset, &total_gas_consumption_m3_);
- t->addMoreExplanation(offset, " total consumption (%f m3)", total_gas_consumption_m3_);
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 1, 0, &key, &t->dv_entries)) {
- struct tm datetime;
- extractDVdate(&t->dv_entries, key, &offset, &datetime);
- target_date_time_ = strdatetime(&datetime);
- t->addMoreExplanation(offset, " target datetime (%s)", target_date_time_.c_str());
- }
-
- if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries))
- {
- extractDVdouble(&t->dv_entries, key, &offset, &target_gas_consumption_m3_);
- t->addMoreExplanation(offset, " target consumption (%f m3)", target_gas_consumption_m3_);
- }
-
- string tmp;
- if (extractDVHexString(&t->dv_entries, "0DFD0C", &offset, &tmp))
- {
- vector bin;
- hex2bin(tmp, &bin);
- version_ = safeString(bin);
- trimWhitespace(&version_);
- t->addMoreExplanation(offset, " version (%s)", version_.c_str());
- }
-
- struct tm datetime;
- if (extractDVdate(&t->dv_entries, "066D", &offset, &datetime))
- {
- device_date_time_ = strdatetime(&datetime);
- t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str());
- }
-
- if (extractDVHexString(&t->dv_entries, "01FD67", &offset, &supplier_info_))
- {
- t->addMoreExplanation(offset, " suppler info (%s)", supplier_info_.c_str());
- }
-
- if (extractDVHexString(&t->dv_entries, "02FD74", &offset, &status_))
- {
- t->addMoreExplanation(offset, " status (%s)", status_.c_str());
- }
-
- if (extractDVHexString(&t->dv_entries, "01FD0B", &offset, ¶meter_set_))
- {
- t->addMoreExplanation(offset, " parameter set (%s)", parameter_set_.c_str());
- }
-
- if (extractDVuint8(&t->dv_entries, "017F", &offset, &other_))
- {
- t->addMoreExplanation(offset, " status2 (%d)", other_);
- }
-}
-
-double MeterUnismart::totalGasConsumption(Unit u)
-{
- assertQuantity(u, Quantity::Volume);
- return convert(total_gas_consumption_m3_, Unit::M3, u);
-}
-
-bool MeterUnismart::hasTotalGasConsumption()
-{
- return true;
-}
-
-double MeterUnismart::targetGasConsumption(Unit u)
-{
- assertQuantity(u, Quantity::Volume);
- return convert(target_gas_consumption_m3_, Unit::M3, u);
-}
diff --git a/src/meters.cc b/src/meters.cc
index e67e293..20e206f 100644
--- a/src/meters.cc
+++ b/src/meters.cc
@@ -1873,6 +1873,8 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
{
map found;
+ // Sort the dv_entries based on their offset in the telegram.
+ // I.e. restore the ordering that was implicit in the telegram.
vector sorted_entries;
for (auto &p : t->dv_entries)
@@ -1882,30 +1884,32 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
sort(sorted_entries.begin(), sorted_entries.end(),
[](const DVEntry* a, const DVEntry *b) -> bool { return a->offset < b->offset; });
- // Iterate through the data content (dv_entries) in the telegram.
- for (DVEntry *dve : sorted_entries)
+ // Now go through each field_info defined by the driver.
+ for (FieldInfo &fi : field_infos_)
{
- // We have telegram content, a dif-vif-value entry.
- // Now check for a field info that wants to handle this telegram content entry.
- for (FieldInfo &fi : field_infos_)
+ int current_match_nr = 0;
+
+ // This field_info has not been matched to a dv_entry before!
+ debug("(meters) trying field info %s(%s)[%d]...\n",
+ fi.vname().c_str(),
+ toString(fi.xuantity()),
+ fi.index());
+
+ // Iterate through dv_entries in the telegram in the same order the telegram presented them.
+ for (DVEntry *dve : sorted_entries)
{
if (fi.hasMatcher() && fi.matches(dve))
{
- if (found.count(&fi) != 0)
- {
- DVEntry *old = found[&fi];
+ current_match_nr++;
- 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);
- }
- else
+ if (fi.matcher().index_nr != IndexNr(current_match_nr))
{
- // We have field that wants to handle this entry!
+ // 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)
+ {
+ // 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()),
@@ -1917,6 +1921,18 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
fi.performExtraction(this, t, dve);
found[&fi] = dve;
}
+ else
+ {
+ DVEntry *old = found[&fi];
+
+ 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);
+ }
}
}
}
@@ -2912,7 +2928,17 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve)
{
struct tm datetime;
dve->extractDate(&datetime);
- string extracted_device_date_time = strdatetime(&datetime);
+ string extracted_device_date_time;
+
+ if (dve->value.size() == 12)
+ {
+ // A long date time sec + timezone field. TODO add timezone data.
+ extracted_device_date_time = strdatetimesec(&datetime);
+ }
+ else
+ {
+ extracted_device_date_time = strdatetime(&datetime);
+ }
m->setStringValue(this, extracted_device_date_time);
t->addMoreExplanation(dve->offset, renderJsonText(m));
found = true;
@@ -2926,13 +2952,14 @@ bool FieldInfo::extractString(Meter *m, Telegram *t, DVEntry *dve)
t->addMoreExplanation(dve->offset, renderJsonText(m));
found = true;
}
- else if (matcher_.vif_range == VIFRange::EnhancedIdentification ||
+ else if (matcher_.vif_range == VIFRange::Any ||
+ matcher_.vif_range == VIFRange::EnhancedIdentification ||
matcher_.vif_range == VIFRange::FabricationNo ||
matcher_.vif_range == VIFRange::ModelVersion ||
matcher_.vif_range == VIFRange::SoftwareVersion ||
matcher_.vif_range == VIFRange::Customer ||
matcher_.vif_range == VIFRange::Location ||
- matcher_.vif_range == VIFRange::Any ||
+ matcher_.vif_range == VIFRange::SpecialSupplierInformation ||
matcher_.vif_range == VIFRange::ParameterSet)
{
string extracted_id;
@@ -3030,204 +3057,302 @@ bool Address::parse(string &s)
return true;
}
-void MeterCommonImplementation::addOptionalCommonFields()
+bool checkIf(set &fields, const char *s)
{
- addStringFieldWithExtractor(
- "fabrication_no",
- "Fabrication number.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::FabricationNo)
- );
-
- addStringFieldWithExtractor(
- "enhanced_id",
- "Enhanced identification number.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::EnhancedIdentification)
- );
-
- addStringFieldWithExtractor(
- "software_version",
- "Software version.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::SoftwareVersion)
- );
-
- addStringFieldWithExtractor(
- "customer",
- "Customer name.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::Customer)
- );
-
- addStringFieldWithExtractor(
- "location",
- "Meter installed at this customer location.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::Location)
- );
-
- addNumericFieldWithExtractor(
- "operating_time",
- "How long the meter has been collecting data.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Time,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::OperatingTime)
- );
-
- addNumericFieldWithExtractor(
- "on_time",
- "How long the meter has been powered up.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Time,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::OnTime)
- );
-
- addNumericFieldWithExtractor(
- "on_time_at_error",
- "How long the meter has been in an error state while powered up.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Time,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::AtError)
- .set(VIFRange::OnTime)
- );
-
- addStringFieldWithExtractor(
- "meter_date",
- "Date when the meter sent the telegram.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::Date)
- );
-
- addStringFieldWithExtractor(
- "meter_date_at_error",
- "Date when the meter was in error.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::AtError)
- .set(VIFRange::Date)
- );
-
- addStringFieldWithExtractor(
- "meter_datetime",
- "Date and time when the meter sent the telegram.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::DateTime)
- );
-
- addStringFieldWithExtractor(
- "meter_datetime_at_error",
- "Date and time when the meter was in error.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- FieldMatcher::build()
- .set(MeasurementType::AtError)
- .set(VIFRange::DateTime)
- );
+ if (fields.count(s) > 0)
+ {
+ fields.erase(s);
+ return true;
+ }
+ return false;
}
-void MeterCommonImplementation::addOptionalFlowRelatedFields()
+void checkFieldsEmpty(set &fields, string name)
{
- addNumericFieldWithExtractor(
- "total",
- "The total media volume consumption recorded by this meter.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Volume,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::Volume)
- );
+ if (fields.size() > 0)
+ {
+ string info;
+ for (auto &s : fields) { info += s+" "; }
- addNumericFieldWithExtractor(
- "total_forward",
- "The total media volume flowing forward.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Volume,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::Volume)
- .add(VIFCombinable::ForwardFlow)
- );
+ warning("(meter) when adding common fields to driver %s, these fields were not found: %s\n",
+ name.c_str(),
+ info.c_str());
+ }
+}
- addNumericFieldWithExtractor(
- "total_backward",
- "The total media volume flowing backward.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Volume,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::Volume)
- .add(VIFCombinable::BackwardFlow)
- );
+void MeterCommonImplementation::addOptionalCommonFields(string field_names)
+{
+ set fields = splitStringIntoSet(field_names, ',');
- addNumericFieldWithExtractor(
- "flow_temperature",
- "Forward media temperature.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Temperature,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::FlowTemperature)
- );
+ if (checkIf(fields, "fabrication_no"))
+ {
+ addStringFieldWithExtractor(
+ "fabrication_no",
+ "Fabrication number.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::FabricationNo)
+ );
+ }
- addNumericFieldWithExtractor(
- "return_temperature",
- "Return media temperature.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Temperature,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::ReturnTemperature)
- );
+ if (checkIf(fields,"enhanced_id"))
+ {
+ addStringFieldWithExtractor(
+ "enhanced_id",
+ "Enhanced identification number.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::EnhancedIdentification)
+ );
+ }
- addNumericFieldWithExtractor(
- "flow_return_temperature_difference",
- "The difference between flow and return media temperatures.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Temperature,
- VifScaling::AutoSigned,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::TemperatureDifference)
- );
+ if (checkIf(fields,"software_version"))
+ {
+ addStringFieldWithExtractor(
+ "software_version",
+ "Software version.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::SoftwareVersion)
+ );
+ }
- addNumericFieldWithExtractor(
- "volume_flow",
- "Media volume flow.",
- PrintProperty::JSON | PrintProperty::OPTIONAL,
- Quantity::Flow,
- VifScaling::Auto,
- FieldMatcher::build()
- .set(MeasurementType::Instantaneous)
- .set(VIFRange::VolumeFlow)
- );
+ if (checkIf(fields,"model_version"))
+ {
+ addStringFieldWithExtractor(
+ "model_version",
+ "Meter model version.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::ModelVersion)
+ );
+ }
+
+ if (checkIf(fields,"customer"))
+ {
+ addStringFieldWithExtractor(
+ "customer",
+ "Customer name.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Customer)
+ );
+ }
+
+ if (checkIf(fields,"location"))
+ {
+ addStringFieldWithExtractor(
+ "location",
+ "Meter installed at this customer location.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Location)
+ );
+ }
+
+ if (checkIf(fields,"operating_time_h"))
+ {
+ addNumericFieldWithExtractor(
+ "operating_time",
+ "How long the meter has been collecting data.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Time,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::OperatingTime)
+ );
+ }
+
+ if (checkIf(fields,"on_time_h"))
+ {
+ addNumericFieldWithExtractor(
+ "on_time",
+ "How long the meter has been powered up.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Time,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::OnTime)
+ );
+ }
+
+ if (checkIf(fields,"on_time_at_error_h"))
+ {
+ addNumericFieldWithExtractor(
+ "on_time_at_error",
+ "How long the meter has been in an error state while powered up.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Time,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::AtError)
+ .set(VIFRange::OnTime)
+ );
+ }
+
+ if (checkIf(fields,"meter_date"))
+ {
+ addStringFieldWithExtractor(
+ "meter_date",
+ "Date when the meter sent the telegram.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Date)
+ );
+ }
+
+ if (checkIf(fields,"meter_date_at_error"))
+ {
+ addStringFieldWithExtractor(
+ "meter_date_at_error",
+ "Date when the meter was in error.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::AtError)
+ .set(VIFRange::Date)
+ );
+ }
+
+ if (checkIf(fields,"meter_datetime"))
+ {
+ addStringFieldWithExtractor(
+ "meter_datetime",
+ "Date and time when the meter sent the telegram.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::DateTime)
+ );
+ }
+
+ if (checkIf(fields,"meter_datetime_at_error"))
+ {
+ addStringFieldWithExtractor(
+ "meter_datetime_at_error",
+ "Date and time when the meter was in error.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ FieldMatcher::build()
+ .set(MeasurementType::AtError)
+ .set(VIFRange::DateTime)
+ );
+ }
+
+ checkFieldsEmpty(fields, name());
+}
+
+void MeterCommonImplementation::addOptionalFlowRelatedFields(string field_names)
+{
+ set fields = splitStringIntoSet(field_names, ',');
+
+ if (checkIf(fields,"total_m3"))
+ {
+ addNumericFieldWithExtractor(
+ "total",
+ "The total media volume consumption recorded by this meter.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ );
+ }
+
+ if (checkIf(fields,"total_forward_m3"))
+ {
+ addNumericFieldWithExtractor(
+ "total_forward",
+ "The total media volume flowing forward.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .add(VIFCombinable::ForwardFlow)
+ );
+ }
+
+ if (checkIf(fields,"total_backward_m3"))
+ {
+ addNumericFieldWithExtractor(
+ "total_backward",
+ "The total media volume flowing backward.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Volume,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::Volume)
+ .add(VIFCombinable::BackwardFlow)
+ );
+ }
+
+ if (checkIf(fields,"flow_temperature_c"))
+ {
+ addNumericFieldWithExtractor(
+ "flow_temperature",
+ "Forward media temperature.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Temperature,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::FlowTemperature)
+ );
+ }
+
+ if (checkIf(fields,"return_temperature_c"))
+ {
+ addNumericFieldWithExtractor(
+ "return_temperature",
+ "Return media temperature.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Temperature,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::ReturnTemperature)
+ );
+ }
+
+ if (checkIf(fields,"flow_return_temperature_difference_c"))
+ {
+ addNumericFieldWithExtractor(
+ "flow_return_temperature_difference",
+ "The difference between flow and return media temperatures.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Temperature,
+ VifScaling::AutoSigned,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::TemperatureDifference)
+ );
+ }
+
+ if (checkIf(fields,"volume_flow_m3h"))
+ {
+ addNumericFieldWithExtractor(
+ "volume_flow",
+ "Media volume flow.",
+ PrintProperty::JSON | PrintProperty::OPTIONAL,
+ Quantity::Flow,
+ VifScaling::Auto,
+ FieldMatcher::build()
+ .set(MeasurementType::Instantaneous)
+ .set(VIFRange::VolumeFlow)
+ );
+ }
}
diff --git a/src/meters.h b/src/meters.h
index ca57221..83dadfa 100644
--- a/src/meters.h
+++ b/src/meters.h
@@ -83,7 +83,6 @@ LIST_OF_METER_TYPES
X(sontex868, T1_bit, HeatCostAllocationMeter, SONTEX868, Sontex868) \
X(topaseskr, T1_bit, WaterMeter, TOPASESKR, TopasEsKr) \
X(lse_08, S1_bit|C1_bit, HeatCostAllocationMeter, LSE_08, LSE_08) \
- X(unismart, T1_bit, GasMeter, UNISMART, Unismart) \
enum class MeterDriver {
@@ -292,7 +291,8 @@ enum PrintProperty
DEPRECATED = 32, // This field is about to be removed or changed in a newer driver, which will have a new name.
STATUS = 64, // This is >the< status field and it should read OK of not error flags are set.
JOIN_TPL_STATUS = 128, // This text field also includes the tpl status decoding. multiple OK:s collapse to a single OK.
- JOIN_INTO_STATUS = 256 // This text field is injected into the already defined status field. multiple OK:s collapse.
+ JOIN_INTO_STATUS = 256, // This text field is injected into the already defined status field. multiple OK:s collapse.
+ OFFICIAL = 512 // This field is listed as an official field for the driver.
};
struct PrintProperties
diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h
index c0e0308..2cbd94f 100644
--- a/src/meters_common_implementation.h
+++ b/src/meters_common_implementation.h
@@ -103,7 +103,6 @@ protected:
void setMeterType(MeterType mt);
void addLinkMode(LinkMode lm);
void addMfctTPLStatusBits(Translate::Lookup lookup);
- void setDefaultFields(string f);
// Print with the default unit for this quantity.
void addPrint(string vname, Quantity vquantity,
@@ -257,8 +256,8 @@ protected:
std::string decodeTPLStatusByte(uchar sts);
- void addOptionalCommonFields();
- void addOptionalFlowRelatedFields();
+ void addOptionalCommonFields(string fields);
+ void addOptionalFlowRelatedFields(string fields);
vector &selectedFields() { return selected_fields_; }
void setSelectedFields(vector &f) { selected_fields_ = f; }
diff --git a/src/util.cc b/src/util.cc
index faffa0f..3b8bf5c 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -1884,6 +1884,13 @@ vector splitString(const string &s, char c)
return v;
}
+set splitStringIntoSet(const string &s, char c)
+{
+ vector v = splitString(s, c);
+ set words(v.begin(), v.end());
+ return words;
+}
+
vector splitDeviceString(const string& ds)
{
string s = ds;
diff --git a/src/util.h b/src/util.h
index e7595dc..cf70f10 100644
--- a/src/util.h
+++ b/src/util.h
@@ -23,6 +23,7 @@
#include
#include
#include