/* 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("lse_07_17"); di.setMeterType(MeterType::WaterMeter); di.addLinkMode(LinkMode::S1); di.addDetection(MANUFACTURER_LSE, 0x06, 0x18); di.addDetection(MANUFACTURER_LSE, 0x07, 0x18); di.addDetection(MANUFACTURER_LSE, 0x07, 0x16); di.addDetection(MANUFACTURER_LSE, 0x07, 0x17); di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); }); Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { addNumericFieldWithExtractor( "total", "The total water consumption recorded by this meter.", PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT, Quantity::Volume, VifScaling::Auto, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Volume) ); addNumericFieldWithExtractor( "due_date", "The water consumption at the due date.", PrintProperty::JSON | PrintProperty::FIELD, Quantity::Volume, VifScaling::Auto, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Volume) .set(StorageNr(1)) ); addStringFieldWithExtractor( "due_date", "The due date configured on the meter.", PrintProperty::JSON | PrintProperty::FIELD, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Date) .set(StorageNr(1)) ); addNumericFieldWithExtractor( "what_date", "The water consumption at the what date?", PrintProperty::JSON | PrintProperty::OPTIONAL, Quantity::Volume, VifScaling::Auto, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Volume) .set(StorageNr(8)) ); addStringFieldWithExtractor( "what_date", "The what date?", PrintProperty::JSON | PrintProperty::OPTIONAL, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::Date) .set(StorageNr(8)) ); addStringFieldWithExtractorAndLookup( "error_code", "Error code of the Meter, 0 means no error.", PrintProperty::JSON | PrintProperty::FIELD, FieldMatcher::build() .set(DifVifKey("02BB56") ), { { { "ERROR_FLAGS", Translate::Type::BitToString, 0xffff, "OK", { // { 0x01, "?" }, } }, }, }); addStringFieldWithExtractor( "error_date", "The date the error occured at. If no error, reads 2127-15-31 (FFFF).", PrintProperty::JSON | PrintProperty::FIELD, FieldMatcher::build() .set(MeasurementType::AtError) .set(VIFRange::Date) ); addStringFieldWithExtractor( "device_date_time", "Date when measurement was recorded.", PrintProperty::JSON | PrintProperty::FIELD, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::DateTime) ); addStringFieldWithExtractor( "meter_version", "Meter model/version.", PrintProperty::JSON | PrintProperty::OPTIONAL, FieldMatcher::build() .set(MeasurementType::Instantaneous) .set(VIFRange::ModelVersion) ); } } /* The following telegram corresponds to the Qundis QWater5.5 cold water meters I have here. From the device display it states that it is set to S-mode operation, sending a telegram every 4 h. Another option of this device is the C mode operation, sending telegrams every 7.5 s. Even though my meters are definitely Qundis QWater5.5, the meters do not identify with manufacturer code QDS but with LSE. (lse_07_17) 0f: 0C dif (8 digit BCD Instantaneous value) (lse_07_17) 10: 13 vif (Volume l) (lse_07_17) 11: * 04400100 total consumption (14.004000 m3) (lse_07_17) 15: 4C dif (8 digit BCD Instantaneous value storagenr=1) (lse_07_17) 16: 13 vif (Volume l) (lse_07_17) 17: * 40620000 due date consumption (6.240000 m3) (lse_07_17) 1b: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) (lse_07_17) 1c: 6C vif (Date type G) (lse_07_17) 1d: * 9F2C due date (2020-12-31) (lse_07_17) 1f: 02 dif (16 Bit Integer/Binary Instantaneous value) (lse_07_17) 20: BB vif (Volume flow l/h) (lse_07_17) 21: 56 vife (duration of limit exceed last lower is 2) (lse_07_17) 22: * 0000 error code (0) (lse_07_17) 24: 32 dif (16 Bit Integer/Binary Value during error state) (lse_07_17) 25: 6C vif (Date type G) (lse_07_17) 26: * FFFF error date (2127-15-31) (lse_07_17) 28: 04 dif (32 Bit Integer/Binary Instantaneous value) (lse_07_17) 29: 6D vif (Date and time type) (lse_07_17) 2a: * 180DA924 device datetime (2021-04-09 13:24) 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()); } // How do the following error codes on the display map to the code in the telegram? // According to the datasheet, these errors can appear on the display: // LEAC Leak in the system (no associated error code) // 0 Negative direction of flow. // 2 Operating hours expired. // 3 Hardware error. // 4 Permanently stored error. // b Communication via OPTO too often per month. // c Communication via M-Bus too often per month. // d Flow too high. // f Device was without voltage supply briefly. All parameter settings are lost. */ // Test: Water lse_07_17 13963399 NOKEY // telegram=|244465329933961318067AE1000000_8C04130070000082046CBE2B01FD0C11046D010CA22C| // {"media":"warm water","meter":"lse_07_17","name":"Water","id":"13963399","total_m3":null,"due_date_m3":null,"due_date":null,"what_date_m3":7,"what_date":"2021-11-30","error_code":null,"error_date":null,"device_date_time":"2021-12-02 12:01","meter_version":"11","timestamp":"1111-11-11T11:11:11Z"} // |Water;13963399;nan;nan;null;null;null;2021-12-02 12:01;1111-11-11 11:11.11 // telegram=|2A4465329933961318067AD8800000_8C04130070000082046CBE2B01FD0C11046D1800A12C02FDAC7E9B2E| // {"media":"warm water","meter":"lse_07_17","name":"Water","id":"13963399","total_m3":null,"due_date_m3":null,"due_date":null,"what_date_m3":7,"what_date":"2021-11-30","error_code":null,"error_date":null,"device_date_time":"2021-12-01 00:24","meter_version":"11","timestamp":"1111-11-11T11:11:11Z"} // |Water;13963399;nan;nan;null;null;null;2021-12-01 00:24;1111-11-11 11:11.11 // telegram=|2D4465329933961318067ADA000000_0C13567100004C1300000000426CFFFF02BB560000326CFFFF046D2307A12C| // {"media":"warm water","meter":"lse_07_17","name":"Water","id":"13963399","total_m3":7.156,"due_date_m3":0,"due_date":"2127-15-31","what_date_m3":7,"what_date":"2021-11-30","error_code":"OK","error_date":"2127-15-31","device_date_time":"2021-12-01 07:35","meter_version":"11","timestamp":"1111-11-11T11:11:11Z"} // |Water;13963399;7.156000;0.000000;2127-15-31;OK;2127-15-31;2021-12-01 07:35;1111-11-11 11:11.11