From 9152e2888206b58beeb012f80213866594667dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Wed, 12 Oct 2022 17:55:47 +0200 Subject: [PATCH] Refactor q400 driver. --- CHANGES | 17 ++ simulations/simulation_t1.txt | 12 +- src/driver_q400.cc | 107 +++++++++++++ src/meter_detection.h | 1 - src/meter_q400.cc | 281 ---------------------------------- src/meters.cc | 12 ++ src/meters.h | 1 - tests/test_t1_meters.sh | 2 +- 8 files changed, 143 insertions(+), 290 deletions(-) create mode 100644 src/driver_q400.cc delete mode 100644 src/meter_q400.cc diff --git a/CHANGES b/CHANGES index a7dfcbd..b28f376 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,21 @@ +ATTENTION! The q400 driver has been refactored to the new driver +format and some errors in the fields were discovered! Since the new driver +format cannot generate these wrong json fields, the new q400 driver +is not backwards compatible, except for the total_m3 field which is the same. + +Same fields and same content: total_m3, consumption_at_set_date_m3" +Same field different content: meter_datetime now reports a full date time "2019-12-20 13:04" +before it reported only the date "2019-12-20". +New field new content: set_date is now set_datetime and reports "2019-12-01 00:00" instead of "2019-12-00" + +For the Axioma version of the q400: + +Broken fields: forward_flow_m3h, backward_flow_m3h, set_forward_flow_m3h, set_backward_flow_m3h +replaced with: total_forward_m3, total_backward_m3, forward_at_set_date_m3, backward_at_set_date_m3 + +Same fields and same content: flow_temperature_c, flow_m3h + Added the bfw 240 radio heat cost allocator. Kajetan Krykwiński added support for the Apator E-ITN heat cost allocator. Thanks Kajetan! diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt index 7df3a6d..d3e11d7 100644 --- a/simulations/simulation_t1.txt +++ b/simulations/simulation_t1.txt @@ -145,15 +145,15 @@ telegram=|3044B4090123456713067A190020052F2F_0C1315000000046D0136A7270F050B00000 {"media":"warm water","meter":"hydrodigit","name":"HydrodigitWater2","id":"67452301","total_m3":0.015,"meter_datetime":"2021-07-07 22:01","timestamp":"1111-11-11T11:11:11Z"} |HydrodigitWater2;67452301;0.015000;2021-07-07 22:01;1111-11-11 11:11.11 -# Test Q400 water telegram (encrypted) -telegram=|2E4409077272727210077AD7102005CC2FF08D057E306D8C3078AE44AD6E3D37F8515B92FB068347783DFBB25C3C28| -{"media":"water","meter":"q400","name":"Q400Water","id":"72727272","total_m3":0,"set_date":"2019-12-01","consumption_at_set_date_m3":0,"meter_datetime":"","flow_m3h":0,"forward_flow_m3h":0,"backward_flow_m3h":0,"flow_temperature_c":0,"set_forward_flow_m3h":0,"set_backward_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"} -|Q400Water;72727272;0.000000;1111-11-11 11:11.11 +# Test Q400 water telegram +telegram=|2E4409077272727210077AD71020052F2F_046D040D742C041377000000446D0000612C4413000000002F2F2F2F2F2F| +{"media":"water","meter":"q400","name":"Q400Water","id":"72727272","meter_datetime":"2019-12-20 13:04","total_m3":0.119,"status":"TEMPORARY_ERROR","set_datetime":"2019-12-01 00:00","consumption_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"} +|Q400Water;72727272;0.119;1111-11-11 11:11.11 # Test Axioma W1 telegram with additional fields compared to the older q400 meter. telegram=|5E4409077372727210077A710050052F2F_046D0110A92704130000000004933B0000000004933C00000000023B000002592A0A446D0000A12744130000000044933B0000000044933C0000000001FD74622F2F2F2F2F2F2F2F2F2F2F2F2F2F| -{"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","total_m3":0,"set_date":"2021-07-01","consumption_at_set_date_m3":0,"meter_datetime":"2021-07-09","flow_m3h":0,"forward_flow_m3h":0,"backward_flow_m3h":0,"flow_temperature_c":26.02,"set_forward_flow_m3h":0,"set_backward_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"} -|AxiomaWater;72727273;0.000000;1111-11-11 11:11.11 +{"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","meter_datetime":"2021-07-09 16:01","total_m3":0,"total_forward_m3":0,"total_backward_m3":0,"flow_temperature_c":26.02,"volume_flow_m3h":0,"status":"OK","set_datetime":"2021-07-01 00:00","consumption_at_set_date_m3":0,"forward_at_set_date_m3":0,"backward_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"} +|AxiomaWater;72727273;0;1111-11-11 11:11.11 # Test electricity meter with eBZ wMB E01. telegram=|5B445A149922992202378C20F6900F002C25BC9E0000BF48954821BC508D72992299225A140102F6003007102F2F040330F92A0004A9FF01FF24000004A9FF026A29000004A9FF03460600000DFD11063132333435362F2F2F2F2F2F| diff --git a/src/driver_q400.cc b/src/driver_q400.cc new file mode 100644 index 0000000..14de1c9 --- /dev/null +++ b/src/driver_q400.cc @@ -0,0 +1,107 @@ +/* + Copyright (C) 2020-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("q400"); + di.setDefaultFields("name,id,total_m3,timestamp"); + di.setMeterType(MeterType::WaterMeter); + di.addLinkMode(LinkMode::T1); + di.addDetection(MANUFACTURER_AXI, 0x07, 0x10); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + }); + + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) + { + addOptionalCommonFields(); + addOptionalFlowRelatedFields(); + + addStringField( + "status", + "Status and error flags.", + PrintProperty::JSON | PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS); + + addStringFieldWithExtractor( + "set_datetime", + "Date and time when the previous billing period ended.", + PrintProperty::JSON, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::DateTime) + .set(StorageNr(1)) + ); + + addNumericFieldWithExtractor( + "consumption_at_set_date", + "The total water consumption at the end of the previous billing period.", + PrintProperty::JSON, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(1)) + ); + + addNumericFieldWithExtractor( + "forward_at_set_date", + "The total media volume flowing forward at the end of previous billing period.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(1)) + .add(VIFCombinable::ForwardFlow) + ); + + addNumericFieldWithExtractor( + "backward_at_set_date", + "The total media volume flowing backward at the end of the previous billing period.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(1)) + .add(VIFCombinable::BackwardFlow) + ); + + } +} + +// Test: Q400Water q400 72727272 NOKEY +// telegram=|2E4409077272727210077AD71020052F2F_046D040D742C041377000000446D0000612C4413000000002F2F2F2F2F2F| +// {"media":"water","meter":"q400","name":"Q400Water","id":"72727272","meter_datetime":"2019-12-20 13:04","total_m3":0.119,"status":"TEMPORARY_ERROR","set_datetime":"2019-12-01 00:00","consumption_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"} +// |Q400Water;72727272;0.119;1111-11-11 11:11.11 + +// Test: AxiomaWater q400 72727273 NOKEY +// Comment: Test Axioma W1 telegram with additional fields compared to the older q400 meter. +// telegram=|5E4409077372727210077A710050052F2F_046D0110A92704130022000004933B0000000004933C00000000023B000002592A0A446D0000A12744130000000044933B0000000044933C0000000001FD74622F2F2F2F2F2F2F2F2F2F2F2F2F2F| +// {"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","meter_datetime":"2021-07-09 16:01","total_m3":8.704,"total_forward_m3":0,"total_backward_m3":0,"flow_temperature_c":26.02,"volume_flow_m3h":0,"status":"OK","set_datetime":"2021-07-01 00:00","consumption_at_set_date_m3":0,"forward_at_set_date_m3":0,"backward_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"} +// |AxiomaWater;72727273;8.704;1111-11-11 11:11.11 diff --git a/src/meter_detection.h b/src/meter_detection.h index 1f0a706..8946422 100644 --- a/src/meter_detection.h +++ b/src/meter_detection.h @@ -75,7 +75,6 @@ X(RFMAMB, MANUFACTURER_BMT, 0x1b, 0x10) \ X(RFMTX1, MANUFACTURER_BMT, 0x07, 0x05) \ X(TSD2, MANUFACTURER_TCH, 0xf0, 0x76) \ - X(Q400, MANUFACTURER_AXI, 0x07, 0x10) \ X(SONTEX868, MANUFACTURER_SON, 0x08, 0x16) \ X(TOPASESKR, MANUFACTURER_AMT, 0x06, 0xf1) \ X(TOPASESKR, MANUFACTURER_AMT, 0x07, 0xf1) \ diff --git a/src/meter_q400.cc b/src/meter_q400.cc deleted file mode 100644 index 57f16ea..0000000 --- a/src/meter_q400.cc +++ /dev/null @@ -1,281 +0,0 @@ -/* - Copyright (C) 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" - -using namespace std; - -struct MeterQ400 : public virtual MeterCommonImplementation { - MeterQ400(MeterInfo &mi); - - // Total water counted through the meter - double totalWaterConsumption(Unit u); - bool hasTotalWaterConsumption(); - - string setDate(); - double consumptionAtSetDate(Unit u); - -private: - void processContent(Telegram *t); - - string meter_datetime_; - double total_water_consumption_m3_ {}; - string set_date_; - double consumption_at_set_date_m3_ {}; - - double flow_m3h_ {}; - // What is flow really? The sum of forward and backward flow? Or the same as forward flow? - double forward_flow_m3h_ {}; - double backward_flow_m3h_ {}; - double flow_temperature_c_ {}; - - // Historical flow, perhaps over the last month? - double set_forward_flow_m3h_ {}; - double set_backward_flow_m3h_ {}; -}; - -shared_ptr createQ400(MeterInfo &mi) -{ - return shared_ptr(new MeterQ400(mi)); -} - -MeterQ400::MeterQ400(MeterInfo &mi) : - MeterCommonImplementation(mi, "q400") -{ - 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("set_date", Quantity::Text, - [&](){ return setDate(); }, - "The most recent billing period date.", - PrintProperty::JSON); - - addPrint("consumption_at_set_date", Quantity::Volume, - [&](Unit u){ return consumptionAtSetDate(u); }, - "The total water consumption at the most recent billing period date.", - PrintProperty::JSON); - - addPrint("meter_datetime", Quantity::Text, - [&](){ return meter_datetime_; }, - "Meter timestamp for measurement.", - PrintProperty::JSON); - - addPrint("flow", Quantity::Flow, - [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(flow_m3h_, Unit::M3H, u); }, - "Water flow?", - PrintProperty::JSON); - - addPrint("forward_flow", Quantity::Flow, - [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(forward_flow_m3h_, Unit::M3H, u); }, - "Forward flow.", - PrintProperty::JSON); - - addPrint("backward_flow", Quantity::Flow, - [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(backward_flow_m3h_, Unit::M3H, u); }, - "Backward flow.", - PrintProperty::JSON); - - addPrint("flow_temperature", Quantity::Temperature, - [&](Unit u){ assertQuantity(u, Quantity::Temperature); return convert(flow_temperature_c_, Unit::C, u); }, - "The water temperature.", - PrintProperty::JSON); - - addPrint("set_forward_flow", Quantity::Flow, - [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(set_forward_flow_m3h_, Unit::M3H, u); }, - "Historical forward flow.", - PrintProperty::JSON); - - addPrint("set_backward_flow", Quantity::Flow, - [&](Unit u){ assertQuantity(u, Quantity::Flow); return convert(set_backward_flow_m3h_, Unit::M3H, u); }, - "Historical backward flow.", - PrintProperty::JSON); -} - -void MeterQ400::processContent(Telegram *t) -{ - /* - This is the first q400 meter telegram content: - - (q400) 0f: 2f2f decrypt check bytes - (q400) 11: 04 dif (32 Bit Integer/Binary Instantaneous value) - (q400) 12: 6D vif (Date and time type) - (q400) 13: 040D742C - (q400) 17: 04 dif (32 Bit Integer/Binary Instantaneous value) - (q400) 18: 13 vif (Volume l) - (q400) 19: * 00000000 total consumption (0.000000 m3) - (q400) 1d: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (q400) 1e: 6D vif (Date and time type) - (q400) 1f: * 0000612C set date (2019-12-01) - (q400) 23: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (q400) 24: 13 vif (Volume l) - (q400) 25: * 00000000 consumption at set date (0.000000 m3) - (q400) 29: 2F skip - (q400) 2a: 2F skip - (q400) 2b: 2F skip - (q400) 2c: 2F skip - (q400) 2d: 2F skip - (q400) 2e: 2F skip - */ - - /* And here is the Axioma W1 meter which reports identical - version and type and manufacturer as the old q400 meter. - But it contains a lot more data.....silly, - they should have a different meter type. - - (q400) 0f: 2f2f decrypt check bytes - (q400) 11: 04 dif (32 Bit Integer/Binary Instantaneous value) - (q400) 12: 6D vif (Date and time type) - (q400) 13: 0110A927 - (q400) 17: 04 dif (32 Bit Integer/Binary Instantaneous value) - (q400) 18: 13 vif (Volume l) - (q400) 19: * 00000000 total consumption (0.000000 m3) - (q400) 1d: 04 dif (32 Bit Integer/Binary Instantaneous value) - (q400) 1e: 93 vif (Volume l) - (q400) 1f: 3B vife (forward flow) - (q400) 20: 00000000 - (q400) 24: 04 dif (32 Bit Integer/Binary Instantaneous value) - (q400) 25: 93 vif (Volume l) - (q400) 26: 3C vife (backward flow) - (q400) 27: 00000000 - (q400) 2b: 02 dif (16 Bit Integer/Binary Instantaneous value) - (q400) 2c: 3B vif (Volume flow l/h) - (q400) 2d: 0000 - (q400) 2f: 02 dif (16 Bit Integer/Binary Instantaneous value) - (q400) 30: 59 vif (Flow temperature 10⁻² °C) - (q400) 31: 2A0A - (q400) 33: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (q400) 34: 6D vif (Date and time type) - (q400) 35: * 0000A127 set date (2021-07-01) - (q400) 39: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (q400) 3a: 13 vif (Volume l) - (q400) 3b: * 00000000 consumption at set date (0.000000 m3) - (q400) 3f: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (q400) 40: 93 vif (Volume l) - (q400) 41: 3B vife (forward flow) - (q400) 42: 00000000 - (q400) 46: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (q400) 47: 93 vif (Volume l) - (q400) 48: 3C vife (backward flow) - (q400) 49: 00000000 - (q400) 4d: 01 dif (8 Bit Integer/Binary Instantaneous value) - (q400) 4e: FD vif (Second extension FD of VIF-codes) - (q400) 4f: 74 vife (Reserved) - (q400) 50: 62 - (q400) 51: 2F skip - (q400) 52: 2F skip - (q400) 53: 2F skip - (q400) 54: 2F skip - (q400) 55: 2F skip - (q400) 56: 2F skip - (q400) 57: 2F skip - (q400) 58: 2F skip - (q400) 59: 2F skip - (q400) 5a: 2F skip - (q400) 5b: 2F skip - (q400) 5c: 2F skip - (q400) 5d: 2F skip - (q400) 5e: 2F skip - */ - int offset; - string key; - - // Find keys common to both q400 and axioma. - - 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::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::DateTime, 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()); - } - - // Now the axioma values: - if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) - { - struct tm date; - extractDVdate(&t->dv_entries, key, &offset, &date); - meter_datetime_ = strdate(&date); - t->addMoreExplanation(offset, " meter datetime (%s)", meter_datetime_.c_str()); - } - - extractDVdouble(&t->dv_entries, "04933B", &offset, &forward_flow_m3h_); - t->addMoreExplanation(offset, " forward flow (%f m3/h)", forward_flow_m3h_); - - extractDVdouble(&t->dv_entries, "04933C", &offset, &backward_flow_m3h_); - t->addMoreExplanation(offset, " backward flow (%f m3/h)", backward_flow_m3h_); - - // Why does the meter send both forward flow and flow? Aren't they the same? - if(findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &flow_m3h_); - t->addMoreExplanation(offset, " flow (%f m3/h)", flow_m3h_); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::FlowTemperature, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &flow_temperature_c_); - t->addMoreExplanation(offset, " flow temperature (%f °C)", flow_temperature_c_); - } - - extractDVdouble(&t->dv_entries, "44933B", &offset, &set_forward_flow_m3h_); - t->addMoreExplanation(offset, " set forward flow (%f m3/h)", set_forward_flow_m3h_); - - extractDVdouble(&t->dv_entries, "44933C", &offset, &set_backward_flow_m3h_); - t->addMoreExplanation(offset, " set backward flow (%f m3/h)", set_backward_flow_m3h_); -} - -double MeterQ400::totalWaterConsumption(Unit u) -{ - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_m3_, Unit::M3, u); -} - -bool MeterQ400::hasTotalWaterConsumption() -{ - return true; -} - -string MeterQ400::setDate() -{ - return set_date_; -} - -double MeterQ400::consumptionAtSetDate(Unit u) -{ - assertQuantity(u, Quantity::Volume); - return convert(consumption_at_set_date_m3_, Unit::M3, u); -} diff --git a/src/meters.cc b/src/meters.cc index 09f4820..ffc9af5 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -3075,6 +3075,18 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields() .set(VIFRange::Volume) ); + 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) + ); + addNumericFieldWithExtractor( "total_backward", "The total media volume flowing backward.", diff --git a/src/meters.h b/src/meters.h index 9a3476d..4856de7 100644 --- a/src/meters.h +++ b/src/meters.h @@ -83,7 +83,6 @@ LIST_OF_METER_TYPES X(rfmamb, T1_bit, TempHygroMeter, RFMAMB, RfmAmb) \ X(rfmtx1, T1_bit, WaterMeter, RFMTX1, RfmTX1) \ X(tsd2, T1_bit, SmokeDetector, TSD2, TSD2) \ - X(q400, T1_bit, WaterMeter, Q400, Q400) \ X(sontex868, T1_bit, HeatCostAllocationMeter, SONTEX868, Sontex868) \ X(topaseskr, T1_bit, WaterMeter, TOPASESKR, TopasEsKr) \ X(vario451, T1_bit, HeatMeter, VARIO451, Vario451) \ diff --git a/tests/test_t1_meters.sh b/tests/test_t1_meters.sh index 13b86ef..55483b0 100755 --- a/tests/test_t1_meters.sh +++ b/tests/test_t1_meters.sh @@ -41,7 +41,7 @@ METERS="MyWarmWater supercom587 12345678 NOKEY HydrusIzarRS auto 60897379 NOKEY HydrodigitWater hydrodigit 86868686 NOKEY HydrodigitWater2 hydrodigit 67452301 NOKEY - Q400Water q400 72727272 AAA896100FED12DD614DD5D46369ACDD + Q400Water q400 72727272 NOKEY AxiomaWater q400 72727273 NOKEY Elen1 ebzwmbe 22992299 NOKEY Elen2 esyswm 77997799 NOKEY