/* 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); void processContent(Telegram *t); void processExtras(string miExtras); int registerSize(int c); }; static bool ok = registerDriver([](DriverInfo&di) { di.setName("apator162"); di.setDefaultFields("name,id,total_m3,timestamp"); di.setMeterType(MeterType::WaterMeter); di.addLinkMode(LinkMode::T1); di.addLinkMode(LinkMode::C1); di.addDetection(MANUFACTURER_APA, 0x06, 0x05); di.addDetection(MANUFACTURER_APA, 0x07, 0x05); di.addDetection(0x8614 /*APT?*/, 0x07, 0x05); // Older version of telegram that is not understood! di.usesProcessContent(); di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); }); Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { processExtras(mi.extras); addNumericField( "total", Quantity::Volume, DEFAULT_PRINT_PROPERTIES, "The total water consumption recorded by this meter."); } void Driver::processContent(Telegram *t) { // Unfortunately, the at-wmbus-16-2 is mostly a proprietary protocol // simply wrapped inside a wmbus telegram. if (t->tpl_ci == 0xb6) { // Oups really old style telegram that we cannot decode. t->discard = true; return; } // Anyway, it seems like telegram is broken up into registers. // Each register is identified with a single byte after which the content follows. // For example, the total volume is marked by 0x10 followed by 4 bytes. vector content; t->extractPayload(&content); map> vendor_values; // The first 8 bytes are error flags and a date time. // E.g. 0F005B5996000000 therefore we skip the first 8 bytes. // // 0F - Spcial function / packet // next 4B : Date - In default frame // next 3B : Faults - In default frame example: please see description of 0x01 register size_t i=8; while (i < content.size()) { int c = content[i]; int size = registerSize(c); if (c == 0xff) break; // An FF signals end of telegram padded to encryption boundary, // FFFFFFF623A where 4 last are perhaps crc or counter? i++; if (size == -1 || i+size >= content.size()) { vector frame; t->extractFrame(&frame); string hex = bin2hex(frame); if (t->beingAnalyzed() == false) { warning("(apator162) telegram contains a register (%02x) with unknown size.\n" "Please open an issue at https://github.com/wmbusmeters/wmbusmeters/\n" "and report this telegram: %s\n", c, hex.c_str()); } break; } if (c == 0x10 && size == 4 && i+size < content.size()) { // We found the register representing the total string total; strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]); int offset = i-1+t->header_size; vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) }; double total_water_consumption_m3 {}; extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3); total = "*** 10-"+total+" total consumption (%f m3)"; t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3); setNumericValue("total", Unit::M3, total_water_consumption_m3); } else { string msg = "*** "; msg += bin2hex(content, i-1, 1)+"-"+bin2hex(content, i, size); t->addSpecialExplanation(i-1+t->header_size, size, KindOfData::CONTENT, Understanding::NONE, msg.c_str()); } i += size; } } int Driver::registerSize(int c) { switch (c) { // case 0x0f: return 3; // Payload often starts with 0x0f, // which also means dif = manufacturer data follows. // After 0x0F there is always: // next 4B : Date - In default frame // next 3B : Faults - In default frame case 0x00: return 4; // Date case 0x01: return 3; // Faults - In default frame f.ex. 0F 09 4D A1 97 18 02 00 -> 18 02 00 -> 00 02 18 -> 0x0218 case 0xA1: case 0x10: return 4; // Total volume - In default frame case 0x11: return 2; // Flow case 0x40: return 6; // Detectors case 0x41: return 2; // Voltage case 0x42: return 4; // Energy case 0x43: return 2; // Life days - In default frame f.ex. 43 6E 0A -> 2670 days from first run case 0x71: return 9; case 0x73: return 1+4*4; // Historical data case 0x75: return 1+6*4; // Historical data case 0x7B: return 1+12*4; // Historical data case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x86: case 0x87: return 10; // Events case 0x85: case 0x88: case 0x8F: return 11; // Events case 0x8A: return 9; // Events case 0x8B: case 0x8C: return 6; // Events case 0x8E: return 7; // Events case 0xA0: return 4; case 0xA2: return 1; case 0xA3: return 7; case 0xA4: return 4; case 0xA5: case 0xA9: case 0xAF: return 1; case 0xA6: return 3; case 0xA7: case 0xA8: case 0xAA: case 0xAB: case 0xAC: case 0xAD: return 2; case 0xB0: return 5; case 0xB1: return 8; case 0xB2: return 16; case 0xB3: return 8; case 0xB4: return 2; case 0xB5: return 16; // Unknown case 0xB6: return 3; case 0xB7: return 3; case 0xB8: return 3; case 0xB9: return 3; case 0xBA: return 3; case 0xBB: return 3; case 0xBC: return 3; case 0xBD: return 3; case 0xBE: return 3; case 0xBF: return 3; case 0xC0: return 3; case 0xC1: return 3; case 0xC2: return 3; case 0xC3: return 3; case 0xC4: return 3; case 0xC5: return 3; case 0xC6: return 3; case 0xC7: return 3; case 0xD0: return 3; case 0xD3: return 3; case 0xF0: return 4; } return -1; } void Driver::processExtras(string miExtras) { map extras; bool ok = parseExtras(miExtras, &extras); if (!ok) { error("(apator162) invalid extra parameters (%s)\n", miExtras.c_str()); } } } // Test: Wasser apator162 20202020 NOKEY // telegram=|6E4401062020202005077A9A006085|2F2F0F0A734393CC0000435B0183001A54E06F630291342510|030F00007B013E0B00003E0B00003E0B00003E0B00003E0B00003E0B00003E0B0000650000003D0000003D0000003D00000000000000A0910CB003FFFFFFFFFFFFFFFFFFFFA62B| // {"media":"water","meter":"apator162","name":"Wasser","id":"20202020","total_m3":3.843,"timestamp":"1111-11-11T11:11:11Z"} // |Wasser;20202020;3.843;1111-11-11 11:11.11 // Test: MyTapWatera apator162 21202020 NOKEY // telegram=|4E4401062020202105077A13004085|2F2F0F6D4C389300020043840210|351F040075012C0B040048D603003E630300CD2C03001EF402000ACE0200A098A39603FFFFFFFFFFFFFFFFFFFFFFFFFF1977| // {"media":"water","meter":"apator162","name":"MyTapWatera","id":"21202020","total_m3":270.133,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWatera;21202020;270.133;1111-11-11 11:11.11 // Test: MyTapWaterb apator162 22202020 NOKEY // telegram=|4E4401062020202205077A4B004085|2F2F0FE566B99390000087C0B24B732679FF75350010|FCFB00004155594265086A0043B4017301DFF600006AE70000BFD5000051BC0000A0F56C2602FFFF1B1B| // {"media":"water","meter":"apator162","name":"MyTapWaterb","id":"22202020","total_m3":64.508,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWaterb;22202020;64.508;1111-11-11 11:11.11 // Test: MyTapWaterc apator162 23202020 NOKEY // telegram=|4E4401062020202305077A9D004085|2F2F0F81902C9300000010|B82F010041555942BD2882004319027301BC2601005C180100CB0A0100DFF60000A0F56C2602FFFFFFFFFFFFFFFFFFFFFFFFFF5B7C| // {"media":"water","meter":"apator162","name":"MyTapWaterc","id":"23202020","total_m3":77.752,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWaterc;23202020;77.752;1111-11-11 11:11.11 // Test: MyTapWaterd apator162 24202020 NOKEY // telegram=|4E4401062020202405077A6C0040852F2F|0F73B3E19410000084E15381E553810101000010|FA41010041555942BF4E8A00433B027301AD380100BC2601005C180100CB0A0100A0F56C2602FFFFD0D7| // {"media":"water","meter":"apator162","name":"MyTapWaterd","id":"24202020","total_m3":82.426,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWaterd;24202020;82.426;1111-11-11 11:11.11 // Test: MyTapWatere apator162 25202020 NOKEY // telegram=|4E4401062020202505077AEF0040852F2F|0F|071122|94|100200|43|6103|84|8B745953486C09100000|10|81920200|75|01F1800200E5640200534A02003431020080150200D9000200|A0|DC939703|FFFFA434| // {"media":"water","meter":"apator162","name":"MyTapWatere","id":"25202020","total_m3":168.577,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWatere;25202020;168.577;1111-11-11 11:11.11 // Test: MyTapWatere apator162 26202020 NOKEY // telegram=|6E4401062020202605077AAC0060852F2F|0F|0C4442|94|1A0000|43|B502|83|000A549B4159029C290F|10|AB440000|7B|012C440000BE3E00008838000072340000493000009B2C00001D2C0000822B00007428000010250000B7200000261C0000|A0|A4D9A103|FFFFFFFFFFFFFFFFFFFF7823| // {"media":"water","meter":"apator162","name":"MyTapWatere","id":"26202020","total_m3":17.579,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWatere;26202020;17.579;1111-11-11 11:11.11 // telegram=|6E4401062020202605077AAD0060852F2F|0F|0E4442|94|1A0000|43|B502|84|4265594C655901010000|10|AB440000|7B|012C440000BE3E00008838000072340000493000009B2C00001D2C0000822B00007428000010250000B7200000261C0000|A0|A4D9A103|FFFFFFFFFFFFFFFFFFFF6C1B| // {"media":"water","meter":"apator162","name":"MyTapWatere","id":"26202020","total_m3":17.579,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWatere;26202020;17.579;1111-11-11 11:11.11 // telegram=|6E4401062020202605077AAE0060852F2F|0F|0F4442|94|1A0000|43|B502|81|D87F57D87F5701010000|10|AB440000|7B|012C440000BE3E00008838000072340000493000009B2C00001D2C0000822B00007428000010250000B7200000261C0000|A0|A4D9A103|FFFFFFFFFFFFFFFFFFFF5F22| // {"media":"water","meter":"apator162","name":"MyTapWatere","id":"26202020","total_m3":17.579,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWatere;26202020;17.579;1111-11-11 11:11.11 // Test: MyTapWaterf apator162 03410514 NOKEY // telegram=|3E4401061405410305077A190030852F2F|0F|86B4B8|95|290200|40|C6C1|B4|F0F3F3|41|5559|42|FA701000|F0|01010000|10|BC780000|FFFFFFFFFFFFFFFFFFFFFF2483| // {"media":"water","meter":"apator162","name":"MyTapWaterf","id":"03410514","total_m3":30.908,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWaterf;03410514;30.908;1111-11-11 11:11.11 // Test: MyTapWaterg apator162 27202020 NOKEY // telegram=|6E4401062020202705077A3D0060852F2F|0F|151794|94|0A0200|43|0403|81|D87F57D87F5701010000|10|783E0000|7B01223C00009137000098320000392D000010290000F02600004C2400003422000004220000CB21000017200000C51C0000|A0|9AD9A103|FFFFFFFFFFFFFFFFFFFF367E| // {"media":"water","meter":"apator162","name":"MyTapWaterg","id":"27202020","total_m3":15.992,"timestamp":"1111-11-11T11:11:11Z"} // |MyTapWaterg;27202020;15.992;1111-11-11 11:11.11 // Test: NewAndOld apator162 00148686 NOKEY // Comment: New apator162 telegram which can be decoded. // telegram=4E4401068686140005077A350040852F2F_0F005B599600000010AA55000041545A42850BD800437D037301C5500000564B00009E4600006A410000A01778EC03FFFFFFFFFFFFFFFFFFFFFFFFFFE393 // {"media":"water","meter":"apator162","name":"NewAndOld","id":"00148686","total_m3":21.93,"timestamp":"1111-11-11T11:11:11Z"} // |NewAndOld;00148686;21.93;1111-11-11 11:11.11 // Comment: Old style apator162 telegram which cannot be decoded. // telegram=5A441486868614000507B6_0AFFFFF5450106F41BAD717A35004085C90AC6D97E3294827563E70F4CF00655FC796A76B87AD1D4A69D16F5EDD1084318F46559E43D2C60D2B1CE581D0CAC1BBC73A376B9D71F0D71C6C904B04DC30E // This telegram should not trigger any shell or other output! // telegram=4E4401068686140005077A350040852F2F_0F005B599600000010AA66000041545A42850BD800437D037301C5500000564B00009E4600006A410000A01778EC03FFFFFFFFFFFFFFFFFFFFFFFFFFE393 // {"media":"water","meter":"apator162","name":"NewAndOld","id":"00148686","total_m3":26.282,"timestamp":"1111-11-11T11:11:11Z"} // |NewAndOld;00148686;26.282;1111-11-11 11:11.11 // Test: has80 apator162 04040404 NOKEY // telegram=|704401060404040405077A0E0060852F2F_0F766DFB96010000430600808F67DB8F67DB01010000102F1F01007B01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A05F5C1804FFFFFFFFFFFFFFFFFFFF26BCD649| // {"media":"water","meter":"apator162","name":"has80","id":"04040404","total_m3":73.519,"timestamp":"1111-11-11T11:11:11Z"} // |has80;04040404;73.519;1111-11-11 11:11.11