diff --git a/CHANGES b/CHANGES index f23cbb3..53cd659 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ + +ATTENTION! The hydrus driver has been rewritten from scratch! current_date is now meter_datetime +Several fields were printed with value 0 even if the telegram did not contain the actual zero, +such fields are no longer printed. + ATTENTION! The microclima driver has been refactored. The field device_date_time has been renamed to meter_datetime. Also two fields related to tariffs do not occur in the actual telegrams so these fields have been removed. The fields output has therefore been drastically changed. Use --selectfields= diff --git a/simulations/simulation_dll_tpl_diff.txt b/simulations/simulation_dll_tpl_diff.txt index 6f522d0..cb071ef 100644 --- a/simulations/simulation_dll_tpl_diff.txt +++ b/simulations/simulation_dll_tpl_diff.txt @@ -1,2 +1,2 @@ telegram=|6644242381818181640E7246564656A51170071F0050052F2F|15257A616F14139172137DAE3A0C000000008C2013917213000B3B0000000B26784601025AF5000266EF00046D1B08B7214C1338861200CC101300000000CC201338861200426C9F2C42EC7EBF2C| -{"media":"water","meter":"hydrus","name":"Vatten","id":"56465646","total_m3":114.35283,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"max_flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"current_date":"2021-01-23 08:27","total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31 00:00","actuality_duration_s":0,"operating_time_h":14678,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +{"media":"water","meter":"hydrus","name":"Vatten","id":"56465646","operating_time_h":14678,"meter_datetime":"2021-01-23 08:27","flow_temperature_c":24.5,"external_temperature_c":23.9,"status":"OK","total_tariff2_m3":137.291,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"flow_m3h":0,"total_at_date_m3":128.638,"at_date":"2020-12-31","timestamp":"1111-11-11T11:11:11Z"} diff --git a/simulations/simulation_izars.txt b/simulations/simulation_izars.txt index 155a38c..80376ca 100644 --- a/simulations/simulation_izars.txt +++ b/simulations/simulation_izars.txt @@ -6,12 +6,12 @@ telegram=|1944304C72242421D401A2|013D4013DD8B46A4999C1293E582CC| # Test new version of IZAR telegram=|2944A511780729662366A20118001378D3B3DB8CEDD77731F25832AAF3DA8CADF9774EA673172E8C61F2| -{"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","prefix":"","serial_number":"000000","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} +{"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} # Yet another version of IZAR telegram=|1944A511780779194820A1|21170013355F8EDB2D03C6912B1E37 -{"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","prefix":"","serial_number":"000000","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} +{"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} # And another izar, with a mfct specific tpl ci field a3. @@ -25,4 +25,4 @@ telegram=|1944304CDEFFE420CC01A2|63120013258F907B0AFF12529AC33B| # Yet another version of IZAR telegram=19442423860775035048A251520015BEB6B2E1ED623A18FC74A5 -{"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","prefix":"","serial_number":"000000","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} +{"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt index cd558a5..dc474fe 100644 --- a/simulations/simulation_t1.txt +++ b/simulations/simulation_t1.txt @@ -101,11 +101,11 @@ telegram=|2e44333003020100071b7a634820252f2f0265840842658308820165950802fb1aae01 # Test Lansen door window telegram telegram=|2e44333005020100071d7ab54800002f2f02fd1b110002fd971d01000efd3a2200000000008e40fd3a000000000000| -{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"CLOSED","a_counter":22,"b_counter":0,"timestamp":"1111-11-11T11:11:11Z"} +{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"CLOSED","a_counter":22,"b_counter":0,"error_flags": "ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_40","timestamp":"1111-11-11T11:11:11Z"} |Dooro;00010205;CLOSED;1111-11-11 11:11.11 telegram=|2e44333005020100071d7ab66800002f2f02fd1b550002fd971d01000efd3a2300000000008e40fd3a000000000000| -{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"OPEN","a_counter":23,"b_counter":0,"timestamp":"1111-11-11T11:11:11Z"} +{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"OPEN","a_counter":23,"b_counter":0,"error_flags": "ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_60","timestamp":"1111-11-11T11:11:11Z"} |Dooro;00010205;OPEN;1111-11-11 11:11.11 # Test Lansen pulse counter @@ -122,17 +122,17 @@ telegram=|5744b40988227711101b7ab20800000265a00842658f088201659f08226589081265a0 # Test Hydrus water meter telegram telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000| -{"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":10.4,"external_temperature_c":0,"current_date":"","total_at_date_m3":0,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2000-00-00 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":13.686516,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} -|HydrusWater;64646464;1.174;0;OK;1111-11-11 11:11.11 +{"at_datetime": "2019-10-31 23:59","flow_m3h": 0,"flow_temperature_c": 10.4,"id": "64646464","media": "water","meter": "hydrus","name": "HydrusWater","remaining_battery_life_y": 13.686797,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 0.2,"total_m3": 1.174} +|HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11 # Test Hydrus new version water meter telegram telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F| -{"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","total_m3":3.45,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":3.431,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2020-09-13 23:59","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":15.321013,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +{"at_datetime": "2020-09-13 23:59","customer": "A20B410178","flow_m3h": 0,"id": "65656565","media": "warm water","meter": "hydrus","name": "HydrusVater","remaining_battery_life_y": 15.321328,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 3.431,"total_m3": 3.45} |HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11 # Test Hydrus with default AES encryption telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C| -{"media":"water","meter":"hydrus","name":"HydrusAES","id":"64745666","total_m3":137.291,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"max_flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"current_date":"2021-01-23 08:27","total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31 00:00","actuality_duration_s":6673,"operating_time_h":14678,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +{"actuality_duration_s": 6673,"at_date": "2020-12-31","external_temperature_c": 23.9,"flow_m3h": 0,"flow_temperature_c": 24.5,"id": "64745666","media": "water","meter": "hydrus","meter_datetime": "2021-01-23 08:27","name": "HydrusAES","operating_time_h": 14678,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 128.638,"total_m3": 137.291,"total_tariff1_at_date_m3": 0,"total_tariff1_m3": 0,"total_tariff2_at_date_m3": 128.638,"total_tariff2_m3": 137.291} |HydrusAES;64745666;137.291;128.638;OK;1111-11-11 11:11.11 # Test BMeters HydroDigit water telegram @@ -270,18 +270,18 @@ telegram=|1E44A511909192937B077A9F0010052F2F_04130347030002FD1700002F2F2F| # Test IZAR RS 868 water meter telegram=|1E4424238B07797389607A8F00107D_041312170100426CBF23441344100100| -{"media":"water","meter":"hydrus","name":"HydrusIzarRS","id":"60897379","total_m3":71.442,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":69.7,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2021-03-31 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +{"at_date": "2021-03-31","id": "60897379","media": "water","meter": "hydrus","name": "HydrusIzarRS","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 69.7,"total_m3": 71.442} |HydrusIzarRS;60897379;71.442;69.7;OK;1111-11-11 11:11.11 # Test IZAR RS 868 water meter warm telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000| -{"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":48.002,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2021-03-31 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +{"at_date": "2021-03-31","id": "60904720","media": "warm water","meter": "hydrus","name": "HydrusIzarRSWarm","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 48.002,"total_m3": 49.373} |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11 # Test another Diehl branded meter with the izar protocol telegram=|19442423850798160018A2410100133EBBD44081053F243A82A3| -{"media":"water","meter":"izar","name":"IzarWater4","id":"18001698","prefix":"","serial_number":"000000","total_m3":835.689,"last_month_total_m3":820.329,"last_month_measure_date":"2021-09-01","remaining_battery_life_y":0.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} -|IzarWater4;18001698;;000000;835.689;820.329;2021-09-01;0.5;no_alarm;no_alarm;8;0;1111-11-11 11:11.11 +{"media":"water","meter":"izar","name":"IzarWater4","id":"18001698","total_m3":835.689,"last_month_total_m3":820.329,"last_month_measure_date":"2021-09-01","remaining_battery_life_y":0.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} +|IzarWater4;18001698;null;null;835.689;820.329;2021-09-01;0.5;no_alarm;no_alarm;8;null;1111-11-11 11:11.11 # Test Aventies Water Meter telegram=76442104710007612507727100076121042507B5006005E2E95A3C2A1279A5415E6732679B43369FD5FDDDD783EEEBB48236D34E7C94AF0A18A5FDA5F7D64111EB42D4D891622139F2952F9D12A20088DFA4CF8123871123EE1F6C1DCEA414879DDB4E05E508F1826D7EFBA6964DF804C9261EA23BBF03 diff --git a/src/driver_apator08.cc b/src/driver_apator08.cc index bfa508f..3a84834 100644 --- a/src/driver_apator08.cc +++ b/src/driver_apator08.cc @@ -17,67 +17,64 @@ #include"meters_common_implementation.h" -using namespace std; - -struct MeterApator08 : public virtual MeterCommonImplementation +namespace { - MeterApator08(MeterInfo &mi, DriverInfo &di); - -private: - - void processContent(Telegram *t); - - double total_water_consumption_m3_ {}; -}; - -static bool ok = registerDriver([](DriverInfo&di) -{ - di.setName("apator08"); - di.setDefaultFields("name,id,total_m3,timestamp"); - di.setMeterType(MeterType::WaterMeter); - di.addLinkMode(LinkMode::T1); - di.addDetection(0x8614/*APT?*/, 0x03, 0x03); - di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new MeterApator08(mi, di)); }); -}); - -MeterApator08::MeterApator08(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) -{ - addNumericField( - "total", - Quantity::Volume, - DEFAULT_PRINT_PROPERTIES, - "The total water consumption recorded by this meter.", - SET_FUNC(total_water_consumption_m3_, Unit::M3), - GET_FUNC(total_water_consumption_m3_, Unit::M3)); -} - -void MeterApator08::processContent(Telegram *t) -{ - // Unfortunately, the at-wmbus-08 is mostly a proprietary protocol - // simple wrapped inside a wmbus telegram. Naughty! - - // The telegram says gas (0x03) but it is a water meter.... so fix this. - t->dll_type = 0x07; - - vector content; - t->extractPayload(&content); - - map> vendor_values; - - string total; - strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]); - - vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) }; - int offset; - string key; - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &vendor_values)) + struct Driver : public virtual MeterCommonImplementation { - extractDVdouble(&vendor_values, key, &offset, &total_water_consumption_m3_); - // Now divide with 3! Is this the same for all apator08 meters? Time will tell. - total_water_consumption_m3_ /= 3.0; + Driver(MeterInfo &mi, DriverInfo &di); - total = "*** 10|"+total+" total consumption (%f m3)"; - t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3_); + private: + + void processContent(Telegram *t); + }; + + static bool ok = registerDriver([](DriverInfo&di) + { + di.setName("apator08"); + di.setDefaultFields("name,id,total_m3,timestamp"); + di.setMeterType(MeterType::WaterMeter); + di.addLinkMode(LinkMode::T1); + di.addDetection(0x8614/*APT?*/, 0x03, 0x03); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + }); + + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) + { + addNumericField( + "total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter."); + } + + void Driver::processContent(Telegram *t) + { + // The telegram says gas (0x03) but it is a water meter.... so fix this. + t->dll_type = 0x07; + + vector content; + t->extractPayload(&content); + + map> vendor_values; + + string total; + strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]); + + vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) }; + int offset; + string key; + if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &vendor_values)) + { + double total_water_consumption_m3 {}; + extractDVdouble(&vendor_values, key, &offset, &total_water_consumption_m3); + // Now divide with 3! Is this the same for all apator08 meters? Time will tell. + total_water_consumption_m3 /= 3.0; + + 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); + } } } diff --git a/src/driver_apator162.cc b/src/driver_apator162.cc index a509838..f16e861 100644 --- a/src/driver_apator162.cc +++ b/src/driver_apator162.cc @@ -26,8 +26,6 @@ namespace void processContent(Telegram *t); void processExtras(string miExtras); int registerSize(int c); - - double total_water_consumption_m3_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -47,10 +45,11 @@ namespace { processExtras(mi.extras); - addPrint("total", Quantity::Volume, - [&](Unit u){ return convert(total_water_consumption_m3_, Unit::M3, u); }, - "The total water consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField( + "total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter."); } void Driver::processContent(Telegram *t) @@ -105,9 +104,12 @@ namespace 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) }; - extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3_); + 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_); + t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3); + + setNumericValue("total", Unit::M3, total_water_consumption_m3); } else { diff --git a/src/driver_apator172.cc b/src/driver_apator172.cc index 597df4a..5485c7f 100644 --- a/src/driver_apator172.cc +++ b/src/driver_apator172.cc @@ -26,8 +26,6 @@ namespace Driver(MeterInfo &mi, DriverInfo &di); void processContent(Telegram *t); - - double total_water_consumption_m3_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -41,10 +39,11 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("total", Quantity::Volume, - [&](Unit u){ return convert(total_water_consumption_m3_, Unit::M3, u); }, - "The total water consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField( + "total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter."); } void Driver::processContent(Telegram *t) @@ -69,9 +68,11 @@ namespace extractDVdouble(&vendor_values, "0413", &offset, &tmp); // Single tick seems to be 1/3 of a m3. Divide by 3 and keep a single decimal. // This will report consumption as 100.0 100.3 100.7 101.0 etc. - total_water_consumption_m3_ = 0.1*std::round(10000.0*tmp/3.0); + double total_water_consumption_m3 = 0.1*std::round(10000.0*tmp/3.0); total = "*** "+total+" total consumption (%f m3)"; - t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3_); + t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3); + + setNumericValue("total", Unit::M3, total_water_consumption_m3); } } } diff --git a/src/driver_apatoreitn.cc b/src/driver_apatoreitn.cc index 5f866e0..a338cb0 100644 --- a/src/driver_apatoreitn.cc +++ b/src/driver_apatoreitn.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2022 Fredrik Öhrström (gpl-3.0-or-later) + Copyright (C) 2022-2023 Fredrik Öhrström (gpl-3.0-or-later) Copyright (C) 2022 Kajetan Krykwiński (gpl-3.0-or-later) This program is free software: you can redistribute it and/or modify @@ -26,14 +26,6 @@ namespace void processContent(Telegram *t); string dateToString(uchar date_lo, uchar date_hi); - - double prev_energy_hca_ {}; - double curr_energy_hca_ {}; - string curr_energy_hca_date_ {}; - string season_start_date_ {}; - string esb_date_ {}; - double temp_room_avg_ {}; - double temp_room_prev_avg_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -51,40 +43,37 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("current", Quantity::HCA, - [&](Unit u){ return convert(curr_energy_hca_, Unit::HCA, u);}, - "Energy consumption so far in this billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("current", + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption so far in this billing period."); - addPrint("previous", Quantity::HCA, - [&](Unit u){ return convert(prev_energy_hca_, Unit::HCA, u); }, - "Energy consumption in previous billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("previous", + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption in previous billing period."); - addPrint("current_date", Quantity::Text, - [&](){ return curr_energy_hca_date_; }, - "Current date, as reported by meter.", - DEFAULT_PRINT_PROPERTIES); + addStringField("current_date", + "Current date, as reported by meter.", + DEFAULT_PRINT_PROPERTIES); - addPrint("season_start_date", Quantity::Text, - [&](){ return season_start_date_; }, - "Season start date.", - DEFAULT_PRINT_PROPERTIES); + addStringField("season_start_date", + "Season start date.", + DEFAULT_PRINT_PROPERTIES); - addPrint("esb_date", Quantity::Text, - [&](){ return esb_date_; }, - "Electronic seal protection break date.", - DEFAULT_PRINT_PROPERTIES); + addStringField("esb_date", + "Electronic seal protection break date.", + DEFAULT_PRINT_PROPERTIES); - addPrint("temp_room_avg", Quantity::Temperature, - [&](Unit u){ return convert(temp_room_avg_, Unit::C, u); }, - "Average room temperature in current season.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("temp_room_avg", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Average room temperature in current season."); - addPrint("temp_room_prev_avg", Quantity::Temperature, - [&](Unit u){ return convert(temp_room_prev_avg_, Unit::C, u); }, - "Average room temperature in previous season.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("temp_room_prev_avg", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Average room temperature in previous season."); } void Driver::processContent(Telegram *t) @@ -123,38 +112,44 @@ namespace // Note: NOT byte swapped. Accidentally? works via dateToString conversion. uchar season_start_date_lo = content[1]; uchar season_start_date_hi = content[0]; - season_start_date_ = dateToString(season_start_date_lo, season_start_date_hi); + string season_start_date = dateToString(season_start_date_lo, season_start_date_hi); + setStringValue("season_start_date", season_start_date); // Previous season total allocation uchar prev_lo = content[4]; uchar prev_hi = content[5]; - prev_energy_hca_ = (256.0*prev_hi+prev_lo); + double previous_hca = (256.0*prev_hi+prev_lo); + setNumericValue("previous", Unit::HCA, previous_hca); // Electronic seal break date uchar esb_date_lo = content[6]; uchar esb_date_hi = content[7]; - esb_date_ = dateToString(esb_date_lo, esb_date_hi); + string esb_date = dateToString(esb_date_lo, esb_date_hi); + setStringValue("esb_date", esb_date); // Current season allocation uchar curr_lo = content[8]; uchar curr_hi = content[9]; - curr_energy_hca_ = (256.0*curr_hi+curr_lo); + double current_hca = (256.0*curr_hi+curr_lo); + setNumericValue("current", Unit::HCA, current_hca); // Current date reported by meter uchar date_curr_lo = content[10]; uchar date_curr_hi = content[11]; - curr_energy_hca_date_ = dateToString(date_curr_lo, date_curr_hi); + string current_date = dateToString(date_curr_lo, date_curr_hi); + setStringValue("current_date", current_date); // Previous season average temperature double temp_room_prev_avg_frac = content[12]; double temp_room_prev_avg_deg = content[13]; - temp_room_prev_avg_ = temp_room_prev_avg_deg + temp_room_prev_avg_frac/256.0; + double temp_room_prev_avg = temp_room_prev_avg_deg + temp_room_prev_avg_frac/256.0; + setNumericValue("temp_room_prev_avg", Unit::C, temp_room_prev_avg); // Current season average temperature double temp_room_avg_frac = content[14]; double temp_room_avg_deg = content[15]; - temp_room_avg_ = temp_room_avg_deg + temp_room_avg_frac/256.0; - + double temp_room_avg = temp_room_avg_deg + temp_room_avg_frac/256.0; + setNumericValue("temp_room_avg", Unit::C, temp_room_avg); } string Driver::dateToString(uchar date_lo, uchar date_hi) { diff --git a/src/driver_bfw240radio.cc b/src/driver_bfw240radio.cc index e0242d3..d413acb 100644 --- a/src/driver_bfw240radio.cc +++ b/src/driver_bfw240radio.cc @@ -24,11 +24,6 @@ namespace Driver(MeterInfo &mi, DriverInfo &di); void processContent(Telegram *t); - - double current_hca_; - double prev_hca_; - double historic_hca_[18]; - string device_date_; }; static bool ok = registerDriver([](DriverInfo&di) @@ -44,30 +39,30 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("current", Quantity::HCA, - [&](Unit u){ return convert(current_hca_, Unit::HCA, u);}, - "Energy consumption so far in this billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("current", + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption so far in this billing period."); - addPrint("prev", Quantity::HCA, - [&](Unit u){ return convert(prev_hca_, Unit::HCA, u); }, - "Energy consumption at end of previous billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("prev", + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption at end of previous billing period."); for (int i=0; i<18; ++i) { string info = tostrprintf("prev_%02d", i+1); string about = tostrprintf("Energy consumption %d months ago.", i+1); - addPrint(info, Quantity::HCA, - [this,i](Unit u){ return convert(historic_hca_[i], Unit::HCA, u);}, - about, DEFAULT_PRINT_PROPERTIES); + addNumericField(info, + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + about); } - addPrint("device_date", Quantity::Text, - [&](){ return device_date_; }, - "Device date when telegram was sent.", - DEFAULT_PRINT_PROPERTIES); + addStringField("device_date", + "Device date when telegram was sent.", + DEFAULT_PRINT_PROPERTIES); } @@ -116,28 +111,31 @@ namespace if (content.size() < 40) return; - current_hca_ = content[6]*256 + content[7]; + double current_hca = content[6]*256 + content[7]; + setNumericValue("current", Unit::HCA, current_hca); - string msg = tostrprintf("*** %02X%02X \"current_hca\":%g", content[6], content[7], current_hca_); + string msg = tostrprintf("*** %02X%02X \"current_hca\":%g", content[6], content[7], current_hca); t->addSpecialExplanation(6+t->header_size, 2, KindOfData::CONTENT, Understanding::FULL, msg.c_str()); - prev_hca_ = content[4]*256 + content[5]; + double prev_hca = content[4]*256 + content[5]; + setNumericValue("prev", Unit::HCA, prev_hca); - msg = tostrprintf("*** %02X%02X \"prev_hca\":%g", content[4], content[5], prev_hca_); + msg = tostrprintf("*** %02X%02X \"prev_hca\":%g", content[4], content[5], prev_hca); t->addSpecialExplanation(4+t->header_size, 2, KindOfData::CONTENT, Understanding::FULL, msg.c_str()); - device_date_ = tostrprintf("20%02x-%02x-%02x", content[39], content[39-1], content[39-2]); + string device_date = tostrprintf("20%02x-%02x-%02x", content[39], content[39-1], content[39-2]); + setStringValue("device_date", device_date); msg = tostrprintf("*** %02X%02X%02X \"device_date\":\"%s\"", content[39-2], content[39-1], content[39], - device_date_.c_str()); + device_date.c_str()); t->addSpecialExplanation(39-2+t->header_size, 3, KindOfData::CONTENT, Understanding::FULL, msg.c_str()); - - for (int i=0; i<18; ++i) { - historic_hca_[i] = getHistoric(i, content); + string info = tostrprintf("prev_%02d", i+1); + double historic_hca = getHistoric(i, content); + setNumericValue(info, Unit::HCA, historic_hca); } } } diff --git a/src/driver_compact5.cc b/src/driver_compact5.cc index 7ea5180..19f9318 100644 --- a/src/driver_compact5.cc +++ b/src/driver_compact5.cc @@ -24,10 +24,6 @@ namespace Driver(MeterInfo &mi, DriverInfo &di); void processContent(Telegram *t); - - double total_energy_kwh_ {}; - double curr_energy_kwh_ {}; - double prev_energy_kwh_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -48,20 +44,20 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("total", Quantity::Energy, - [&](Unit u){ return convert(total_energy_kwh_, Unit::KWH, u); }, - "The total energy consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("total", + Quantity::Energy, + DEFAULT_PRINT_PROPERTIES, + "The total energy consumption recorded by this meter."); - addPrint("current", Quantity::Energy, - [&](Unit u){ return convert(curr_energy_kwh_, Unit::KWH, u); }, - "Energy consumption so far in this billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("current", + Quantity::Energy, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption so far in this billing period."); - addPrint("previous", Quantity::Energy, - [&](Unit u){ return convert(prev_energy_kwh_, Unit::KWH, u); }, - "Energy consumption in previous billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("previous", + Quantity::Energy, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption in previous billing period."); } void Driver::processContent(Telegram *t) @@ -101,9 +97,14 @@ namespace t->explanations.push_back(ce); t->addMoreExplanation(offset, " energy used in current billing period (%f KWH)", curr); - total_energy_kwh_ = prev+curr; - curr_energy_kwh_ = curr; - prev_energy_kwh_ = prev; + double total_energy_kwh = prev+curr; + setNumericValue("total", Unit::KWH, total_energy_kwh); + + double curr_energy_kwh = curr; + setNumericValue("current", Unit::KWH, curr_energy_kwh); + + double prev_energy_kwh = prev; + setNumericValue("previous", Unit::KWH, prev_energy_kwh); } } diff --git a/src/driver_fhkvdataiii.cc b/src/driver_fhkvdataiii.cc index e6be0ef..461c885 100644 --- a/src/driver_fhkvdataiii.cc +++ b/src/driver_fhkvdataiii.cc @@ -23,23 +23,10 @@ namespace { Driver(MeterInfo &mi, DriverInfo &di); + private: + void processContent(Telegram *t); - - double currentPeriodEnergyConsumption(Unit u); - string currentPeriodDate(); - double previousPeriodEnergyConsumption(Unit u); - string previousPeriodDate(); - double currentRoomTemperature(Unit u); - double currentRadiatorTemperature(Unit u); - string leadingZeroString(int num); - - double curr_energy_hca_ {}; - string curr_energy_hca_date {}; - double prev_energy_hca_ {}; - string prev_energy_hca_date {}; - double temp_room_ {}; - double temp_radiator_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -56,72 +43,38 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("current", Quantity::HCA, - [&](Unit u){ return currentPeriodEnergyConsumption(u); }, - "Energy consumption so far in this billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("current", + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption so far in this billing period."); - addPrint("current_date", Quantity::Text, - [&](){ return currentPeriodDate(); }, - "Date of current billing period.", - DEFAULT_PRINT_PROPERTIES); + addStringField("current_date", + "Date of current billing period.", + DEFAULT_PRINT_PROPERTIES); - addPrint("previous", Quantity::HCA, - [&](Unit u){ return previousPeriodEnergyConsumption(u); }, - "Energy consumption in previous billing period.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("previous", + Quantity::HCA, + DEFAULT_PRINT_PROPERTIES, + "Energy consumption in previous billing period."); - addPrint("previous_date", Quantity::Text, - [&](){ return previousPeriodDate(); }, - "Date of last billing period.", - DEFAULT_PRINT_PROPERTIES); + addStringField("previous_date", + "Date of last billing period.", + DEFAULT_PRINT_PROPERTIES); - addPrint("temp_room", Quantity::Temperature, - [&](Unit u){ return currentRoomTemperature(u); }, - "Current room temperature.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("temp_room", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Current room temperature."); - addPrint("temp_radiator", Quantity::Temperature, - [&](Unit u){ return currentRadiatorTemperature(u); }, - "Current radiator temperature.", - DEFAULT_PRINT_PROPERTIES); - } - - double Driver::currentPeriodEnergyConsumption(Unit u) - { - return curr_energy_hca_; - } - - string Driver::currentPeriodDate() - { - return curr_energy_hca_date; - } - - double Driver::previousPeriodEnergyConsumption(Unit u) - { - return prev_energy_hca_; - } - - string Driver::previousPeriodDate() - { - return prev_energy_hca_date; - } - - double Driver::currentRoomTemperature(Unit u) - { - assertQuantity(u, Quantity::Temperature); - return convert(temp_room_, Unit::C, u); - } - - double Driver::currentRadiatorTemperature(Unit u) - { - assertQuantity(u, Quantity::Temperature); - return convert(temp_radiator_, Unit::C, u); + addNumericField("temp_radiator", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Current radiator temperature."); } void Driver::processContent(Telegram *t) { - // Unfortunately, the Techem FHKV data ii/iii is mostly a proprieatary protocol + // The Techem FHKV data ii/iii is mostly a proprietary protocol // simple wrapped inside a wmbus telegram since the ci-field is 0xa0. // Which means that the entire payload is manufacturer specific. @@ -135,18 +88,10 @@ namespace debugPayload("(fhkvdataiii) not enough data", content); return; } - // Consumption - // Previous Consumption - uchar prev_lo = content[3]; - uchar prev_hi = content[4]; - double prev = (256.0*prev_hi+prev_lo); - prev_energy_hca_ = prev; - string prevs; - strprintf(&prevs, "%02x%02x", prev_lo, prev_hi); - //t->addMoreExplanation(14, " energy used in previous billing period (%f HCA)", prevs); - // Previous Date + // Previous Date //////////////////////////////////////////////////////// + uchar date_prev_lo = content[1]; uchar date_prev_hi = content[2]; int date_prev = (256.0*date_prev_hi+date_prev_lo); @@ -154,21 +99,36 @@ namespace int day_prev = (date_prev >> 0) & 0x1F; int month_prev = (date_prev >> 5) & 0x0F; int year_prev = (date_prev >> 9) & 0x3F; - prev_energy_hca_date = std::to_string((year_prev + 2000)) + "-" + leadingZeroString(month_prev) + "-" + leadingZeroString(day_prev) + "T02:00:00Z"; - //t->addMoreExplanation(offset, " last date of previous billing period (%s)", prev_energy_hca_date); + string previous_date = + std::to_string((year_prev + 2000)) + "-" + + leadingZeroString(month_prev) + "-" + + leadingZeroString(day_prev) + "T02:00:00Z"; - // Current Consumption - uchar curr_lo = content[7]; - uchar curr_hi = content[8]; - double curr = (256.0*curr_hi+curr_lo); - curr_energy_hca_ = curr; + setStringValue("previous_date", previous_date); - string currs; - strprintf(&currs, "%02x%02x", curr_lo, curr_hi); - //t->addMoreExplanation(offset, " energy used in current billing period (%f HCA)", currs); + string bytes = tostrprintf("%02x%02x", content[1], content[2]); + string info = "*** "+bytes+" previous_date = %s"; + + t->addSpecialExplanation(t->header_size+1, 2, KindOfData::CONTENT, Understanding::FULL, + info.c_str(), previous_date.c_str()); + + // Previous Consumption ////////////////////////////////////////////////////////// + + uchar prev_lo = content[3]; + uchar prev_hi = content[4]; + double prev = (256.0*prev_hi+prev_lo); + double previous_hca = prev; + setNumericValue("previous", Unit::HCA, previous_hca); + + bytes = tostrprintf("%02x%02x", content[3], content[4]); + info = "*** "+bytes+" previous_hca = %.17g"; + + t->addSpecialExplanation(t->header_size+3, 2, KindOfData::CONTENT, Understanding::FULL, + info.c_str(), previous_hca); + + // Current Date ////////////////////////////////////////////////////////////////// - // Current Date uchar date_curr_lo = content[5]; uchar date_curr_hi = content[6]; int date_curr = (256.0*date_curr_hi+date_curr_lo); @@ -181,44 +141,73 @@ namespace if (day_curr <= 0) day_curr = 1; int month_curr = (date_curr >> 9) & 0x0F; if (month_curr <= 0) month_curr = 12; - curr_energy_hca_date = to_string(year_curr) + "-" + leadingZeroString(month_curr) + "-" + leadingZeroString(day_curr) + "T02:00:00Z"; - // t->addMoreExplanation(offset, " last date of current billing period (%s)", curr_energy_hca_date); + string current_date = + to_string(year_curr) + "-" + + leadingZeroString(month_curr) + "-" + + leadingZeroString(day_curr) + "T02:00:00Z"; + + setStringValue("current_date", current_date); + + bytes = tostrprintf("%02x%02x", content[5], content[6]); + info = "*** "+bytes+" current_date = %s"; + + t->addSpecialExplanation(t->header_size+5, 2, KindOfData::CONTENT, Understanding::FULL, + info.c_str(), current_date.c_str()); + + // Current Consumption /////////////////////////////////////////////////////////// + + uchar curr_lo = content[7]; + uchar curr_hi = content[8]; + double current_hca = (256.0*curr_hi+curr_lo); + + setNumericValue("current", Unit::HCA, current_hca); + + bytes = tostrprintf("%02x%02x", content[7], content[8]); + info = "*** "+bytes+" current_hca = %.17g"; + + t->addSpecialExplanation(t->header_size+7, 2, KindOfData::CONTENT, Understanding::FULL, + info.c_str(), current_hca); + + // Temperatures ////////////////////////////////////////////// - // Temperature uchar room_tlo; uchar room_thi; uchar radiator_tlo; uchar radiator_thi; + int offset = 9; + if(t->dll_version == 0x94) { - room_tlo = content[10]; - room_thi = content[11]; - radiator_tlo = content[12]; - radiator_thi = content[13]; - } else - { - room_tlo = content[9]; - room_thi = content[10]; - radiator_tlo = content[11]; - radiator_thi = content[12]; + offset= 10; } - // Room Temperature - double room_t = (256.0*room_thi+room_tlo)/100; - temp_room_ = room_t; + room_tlo = content[offset]; + room_thi = content[offset+1]; + radiator_tlo = content[offset+2]; + radiator_thi = content[offset+3]; - string room_ts; - strprintf(&room_ts, "%02x%02x", room_tlo, room_thi); - // t->addMoreExplanation(offset, " current room temparature (%f °C)", room_ts); + // Room Temperature //////////////////////////////////////////////////////////////// - // Radiator Temperature - double radiator_t = (256.0*radiator_thi+radiator_tlo)/100; - temp_radiator_ = radiator_t; + double temp_room_c = (256.0*room_thi+room_tlo)/100; + setNumericValue("temp_room", Unit::C, temp_room_c); - string radiator_ts; - strprintf(&radiator_ts, "%02x%02x", radiator_tlo, radiator_thi); - // t->addMoreExplanation(offset, " current radiator temparature (%f °C)", radiator_ts); + bytes = tostrprintf("%02x%02x", content[offset], content[offset+1]); + info = "*** "+bytes+" temp_room_c = %.17g"; + + t->addSpecialExplanation(t->header_size+offset, 2, KindOfData::CONTENT, Understanding::FULL, + info.c_str(), temp_room_c); + + // Radiator Temperature //////////////////////////////////////////////////////////// + + double temp_radiator_c = (256.0*radiator_thi+radiator_tlo)/100; + setNumericValue("temp_radiator", Unit::C, temp_radiator_c); + + bytes = tostrprintf("%02x%02x", content[offset+2], content[offset+3]); + info = "*** "+bytes+" temp_radiator_c = %.17g"; + + t->addSpecialExplanation(t->header_size+offset+2, 2, KindOfData::CONTENT, Understanding::FULL, + info.c_str(), temp_radiator_c); } string Driver::leadingZeroString(int num) { diff --git a/src/driver_hydroclima.cc b/src/driver_hydroclima.cc index a212455..75e7dff 100644 --- a/src/driver_hydroclima.cc +++ b/src/driver_hydroclima.cc @@ -25,11 +25,6 @@ namespace void processContent(Telegram *t); void decodeRF_RKN0(Telegram *t); void decodeRF_RKN9(Telegram *t); - - double average_ambient_temperature_ {}; - double max_ambient_temperature_ {}; - double average_ambient_temperature_last_month_ {}; - double average_heater_temperature_last_month_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -39,6 +34,7 @@ namespace di.setMeterType(MeterType::HeatCostAllocationMeter); di.addLinkMode(LinkMode::T1); di.addDetection(MANUFACTURER_BMP, 0x08, 0x53); + di.usesProcessContent(); di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); }); @@ -55,29 +51,25 @@ namespace .set(VIFRange::HeatCostAllocation) ); - addPrint("average_ambient_temperature", - Quantity::Temperature, - GET_FUNC(average_ambient_temperature_, Unit::C), - "Average ambient temperature since this beginning of this month.", - DEFAULT_PRINT_PROPERTIES ); + addNumericField("average_ambient_temperature", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Average ambient temperature since this beginning of this month."); - addPrint("max_ambient_temperature", - Quantity::Temperature, - GET_FUNC(max_ambient_temperature_, Unit::C), - "Max ambient temperature since the beginning of this month.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("max_ambient_temperature", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Max ambient temperature since the beginning of this month."); - addPrint("average_ambient_temperature_last_month", - Quantity::Temperature, - GET_FUNC(average_ambient_temperature_last_month_, Unit::C), - "Average ambient temperature last month.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("average_ambient_temperature_last_month", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Average ambient temperature last month."); - addPrint("average_heater_temperature_last_month", - Quantity::Temperature, - GET_FUNC(average_heater_temperature_last_month_, Unit::C), - "Average heater temperature last month.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("average_heater_temperature_last_month", + Quantity::Temperature, + DEFAULT_PRINT_PROPERTIES, + "Average heater temperature last month."); } double toTemperature(uchar hi, uchar lo) @@ -149,14 +141,16 @@ namespace i+=2; if (i+1 >= len) return; - average_ambient_temperature_ = toTemperature(bytes[i+1], bytes[i]); + double average_ambient_temperature_c = toTemperature(bytes[i+1], bytes[i]); + setNumericValue("average_ambient_temperature", Unit::C, average_ambient_temperature_c); info = renderJsonOnlyDefaultUnit("average_ambient_temperature", Quantity::Temperature); t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); i+=2; if (i+1 >= len) return; - max_ambient_temperature_ = toTemperature(bytes[i+1], bytes[i]); + double max_ambient_temperature_c = toTemperature(bytes[i+1], bytes[i]); + setNumericValue("max_ambient_temperature", Unit::C, max_ambient_temperature_c); info = renderJsonOnlyDefaultUnit("max_ambient_temperature", Quantity::Temperature); t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); @@ -175,14 +169,18 @@ namespace i+=2; if (i+1 >= len) return; - average_ambient_temperature_last_month_ = toTemperature(bytes[i+1], bytes[i]); + double average_ambient_temperature_last_month_c = toTemperature(bytes[i+1], bytes[i]); + setNumericValue("average_ambient_temperature_last_month", Unit::C, + average_ambient_temperature_last_month_c); info = renderJsonOnlyDefaultUnit("average_ambient_temperature_last_month", Quantity::Temperature); t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); i+=2; if (i+1 >= len) return; - average_heater_temperature_last_month_ = toTemperature(bytes[i+1], bytes[i]); + double average_heater_temperature_last_month_c = toTemperature(bytes[i+1], bytes[i]); + setNumericValue("average_heater_temperature_last_month", Unit::C, + average_heater_temperature_last_month_c); info = renderJsonOnlyDefaultUnit("average_heater_temperature_last_month", Quantity::Temperature); t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); @@ -252,5 +250,5 @@ namespace // Test: HCAA hydroclima 74393723 NOKEY // Comment: // telegram=|2D44B009233739743308780F9D1300023ED97AEC7BC5908A32C15D8A32C126915AC15AC126912691269187912689| -// {"media":"heat cost allocation","meter":"hydroclima","name":"HCAA","id":"74393723","average_ambient_temperature_c":0,"max_ambient_temperature_c":0,"average_ambient_temperature_last_month_c":0,"average_heater_temperature_last_month_c":0,"timestamp":"1111-11-11T11:11:11Z"} -// |HCAA;74393723;null;0;1111-11-11 11:11.11 +// {"media":"heat cost allocation","meter":"hydroclima","name":"HCAA","id":"74393723","timestamp":"1111-11-11T11:11:11Z"} +// |HCAA;74393723;null;null;1111-11-11 11:11.11 diff --git a/src/driver_hydrus.cc b/src/driver_hydrus.cc index 5b7d23c..9964281 100644 --- a/src/driver_hydrus.cc +++ b/src/driver_hydrus.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later) + Copyright (C) 2019-2023 Fredrik Öhrström (gpl-3.0-or-later) Copyright (C) 2021 Vincent Privat (gpl-3.0-or-later) This program is free software: you can redistribute it and/or modify @@ -17,47 +17,12 @@ */ #include"meters_common_implementation.h" -#include"manufacturer_specificities.h" namespace { struct Driver : public virtual MeterCommonImplementation { Driver(MeterInfo &mi, DriverInfo &di); - - void processContent(Telegram *t); - - private: - - // Total water counted through the meter - double totalWaterConsumption(Unit u); - double totalWaterConsumptionTariff1(Unit u); - double totalWaterConsumptionTariff2(Unit u); - double totalWaterConsumptionAtDate(Unit u); - double totalWaterConsumptionTariff1AtDate(Unit u); - double totalWaterConsumptionTariff2AtDate(Unit u); - double maxFlow(Unit u); - bool hasMaxFlow(); - double flowTemperature(Unit u); - double externalTemperature(Unit u); - - double total_water_consumption_m3_ {}; - double total_water_consumption_tariff1_m3_ {}; - double total_water_consumption_tariff2_m3_ {}; - string current_date_; - double total_water_consumption_at_date_m3_ {}; - double total_water_consumption_tariff1_at_date_m3_ {}; - double total_water_consumption_tariff2_at_date_m3_ {}; - string at_date_; - double max_flow_m3h_ {}; - double flow_temperature_c_ { 127 }; - double external_temperature_c_ {}; - uint32_t actuality_duration_s_ {}; - double operating_time_h_ {}; - double remaining_battery_life_year_ {}; - string status_; // TPL STS - - Translate::Lookup error_codes_; }; static bool ok = registerDriver([](DriverInfo&di) @@ -79,528 +44,148 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - error_codes_ = - Translate::Lookup( - { - { - { - "TPL_FLAGS", - Translate::Type::IndexToString, - AlwaysTrigger, MaskBits(0xe0), - "OK", - { - { 0x20, "AIR_IN_PIPE" }, - { 0x40, "WOOT_0x40" }, - { 0x60, "MEASUREMENT_ERROR" }, - { 0x80, "LEAKAGE_OR_NO_USAGE" }, - { 0xa0, "REVERSE_FLOW" }, - { 0xc0, "LOW_TEMPERATURE" }, - { 0xe0, "AIR_IN_PIPE" } } - } - }, - }); + addOptionalCommonFields("operating_time_h,actuality_duration_s,meter_datetime,customer"); + addOptionalFlowRelatedFields("flow_temperature_c,external_temperature_c"); - addPrint("total", Quantity::Volume, - [&](Unit u){ return totalWaterConsumption(u); }, - "The total water consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addStringField( + "status", + "Status of meter.", + PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS); - addPrint("total_tariff1", Quantity::Volume, - [&](Unit u){ return totalWaterConsumptionTariff1(u); }, - "The total water consumption recorded by this meter at tariff 1.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "total", + "The total water consumption recorded by this meter.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + ); - addPrint("total_tariff2", Quantity::Volume, - [&](Unit u){ return totalWaterConsumptionTariff2(u); }, - "The total water consumption recorded by this meter at tariff 2.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "total_tariff{tariff_counter}", + "The total water consumption recorded on tariff # by this meter.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(TariffNr(1),TariffNr(2)) + ); - addPrint("max_flow", Quantity::Flow, - [&](Unit u){ return maxFlow(u); }, - "The maximum flow recorded during previous period.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "total_tariff{tariff_counter}_at_date", + "The total water consumption recorded on tariff # by this meter at billing date.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(TariffNr(1),TariffNr(2)) + .set(StorageNr(1)) + ); - addPrint("flow_temperature", Quantity::Temperature, - [&](Unit u){ return flowTemperature(u); }, - "The water temperature.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "flow", + "The current water flow.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Flow, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::VolumeFlow)); - addPrint("external_temperature", Quantity::Temperature, - [&](Unit u){ return externalTemperature(u); }, - "The external temperature.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "total_at_date", + "The total water consumption recorded at date.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(1)) + ); - addPrint("current_date", Quantity::Text, - [&](){ return current_date_; }, - "Current date of measurement.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "at", + "The last billing period date.", + DEFAULT_PRINT_PROPERTIES, + Quantity::PointInTime, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Date) + .set(StorageNr(1)), + Unit::DateLT + ); - addPrint("total_at_date", Quantity::Volume, - [&](Unit u){ return totalWaterConsumptionAtDate(u); }, - "The total water consumption recorded at date.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "total_at_date", + "Fix this! The total water consumption recorded at last day. Perhaps?", + DEFAULT_PRINT_PROPERTIES, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + .set(StorageNr(3)) + ); - addPrint("total_tariff1_at_date", Quantity::Volume, - [&](Unit u){ return totalWaterConsumptionTariff1AtDate(u); }, - "The total water consumption recorded at tariff 1 at date.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "at", + "Fix this! The last billing period date last day. Perhaps?", + DEFAULT_PRINT_PROPERTIES, + Quantity::PointInTime, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::DateTime) + .set(StorageNr(3)) + ); - addPrint("total_tariff2_at_date", Quantity::Volume, - [&](Unit u){ return totalWaterConsumptionTariff2AtDate(u); }, - "The total water consumption recorded at tariff 2 at date.", - DEFAULT_PRINT_PROPERTIES); - - addPrint("at_date", Quantity::Text, - [&](){ return at_date_; }, - "Date when total water consumption was recorded.", - DEFAULT_PRINT_PROPERTIES); - - addPrint("actuality_duration", Quantity::Time, Unit::Second, - [&](Unit u){ return convert(actuality_duration_s_, Unit::Second, u); }, - "Elapsed time between measurement and transmission", - DEFAULT_PRINT_PROPERTIES); - - addPrint("operating_time", Quantity::Time, Unit::Hour, - [&](Unit u){ return convert(operating_time_h_, Unit::Hour, u); }, - "How long the meter is operating", - DEFAULT_PRINT_PROPERTIES); - - addPrint("remaining_battery_life", Quantity::Time, Unit::Year, - [&](Unit u){ return convert(remaining_battery_life_year_, Unit::Year, u); }, - "How many more years the battery is expected to last", - DEFAULT_PRINT_PROPERTIES); - - addPrint("status", Quantity::Text, - [&](){ return status_; }, - "The status is OK or some error condition.", - DEFAULT_PRINT_PROPERTIES); + addNumericFieldWithExtractor( + "remaining_battery_life", + "Remaining battery life in years.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Time, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::RemainingBattery), + Unit::Year); } - - void Driver::processContent(Telegram *t) - { - // There are two distinctintly different Hydrus telegrams. - // Unfortunately there seems to be no markings on the - // physical meter that tells us which version it is. - // - // Fortunately the mfct,media,version bits does distinguish - // the meters! - // - // This driver currently only decodes parts of both telegrams. - // Eventually we should either split this driver into two. - // Or make more generic capabilities in drivers to switch - // between different telegrams formats, that are similar - // but not quite. - - /* - New style telegram: - (hydrus) 11: 0C dif (8 digit BCD Instantaneous value) - (hydrus) 12: 13 vif (Volume l) - (hydrus) 13: * 50340000 total consumption (3.450000 m3) - (hydrus) 17: 0D dif (variable length Instantaneous value) - (hydrus) 18: FD vif (Second extension of VIF-codes) - (hydrus) 19: 11 vife (Customer) - (hydrus) 1a: 0A varlen=10 - (hydrus) 1b: 38373130313442303241 - (hydrus) 25: 0B dif (6 digit BCD Instantaneous value) - (hydrus) 26: 3B vif (Volume flow l/h) - (hydrus) 27: * 000000 max flow (0.000000 m3/h) - (hydrus) 2a: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 2b: FD vif (Second extension of VIF-codes) - (hydrus) 2c: 74 vife (Reserved) - (hydrus) 2d: * DC15 battery life days (5596) - (hydrus) 2f: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 30: 01 dife (subunit=0 tariff=0 storagenr=3) - (hydrus) 31: 6D vif (Date and time type) - (hydrus) 32: * 3B178D29 at date (2020-09-13 23:59) - (hydrus) 36: CC dif (8 digit BCD Instantaneous value storagenr=1) - (hydrus) 37: 01 dife (subunit=0 tariff=0 storagenr=3) - (hydrus) 38: 13 vif (Volume l) - (hydrus) 39: * 31340000 total consumption at date (3.431000 m3) - (hydrus) 3d: 2F skip - (hydrus) 3e: 2F skip - - {"media":"warm water","meter":"hydrus","name":"Vatten","id":"65656565","total_m3":3.45,"max_flow_m3h":0,"flow_temperature_c":127,"total_at_date_m3":3.431,"at_date":"2020-09-13 23:59","battery_life_days":"5596","status":"OK","timestamp":"2020-09-27T08:54:42Z"} - - Old style telegram - - (hydrus) 11: 01 dif (8 Bit Integer/Binary Instantaneous value) - (hydrus) 12: FD vif (Second extension of VIF-codes) - (hydrus) 13: 08 vife (Access Number (transmission count)) - (hydrus) 14: 30 - (hydrus) 15: 0C dif (8 digit BCD Instantaneous value) - (hydrus) 16: 13 vif (Volume l) - (hydrus) 17: * 74110000 total consumption (1.174000 m3) - (hydrus) 1b: 7C dif (8 digit BCD Value during error state storagenr=1) - (hydrus) 1c: 13 vif (Volume l) - (hydrus) 1d: 00000000 - (hydrus) 21: FC dif (8 digit BCD Value during error state storagenr=1) - (hydrus) 22: 10 dife (subunit=0 tariff=1 storagenr=1) - (hydrus) 23: 13 vif (Volume l) - (hydrus) 24: 00000000 - (hydrus) 28: FC dif (8 digit BCD Value during error state storagenr=1) - (hydrus) 29: 20 dife (subunit=0 tariff=2 storagenr=1) - (hydrus) 2a: 13 vif (Volume l) - (hydrus) 2b: 00000000 - (hydrus) 2f: 72 dif (16 Bit Integer/Binary Value during error state storagenr=1) - (hydrus) 30: 6C vif (Date type G) - (hydrus) 31: 0000 - (hydrus) 33: 0B dif (6 digit BCD Instantaneous value) - (hydrus) 34: 3B vif (Volume flow l/h) - (hydrus) 35: * 000000 max flow (0.000000 m3/h) - (hydrus) 38: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 39: FD vif (Second extension of VIF-codes) - (hydrus) 3a: 74 vife (Reserved) - (hydrus) 3b: * 8713 battery life days (4999) - (hydrus) 3d: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 3e: 5A vif (Flow temperature 10⁻¹ °C) - (hydrus) 3f: * 6800 flow temperature (10.400000 °C) - (hydrus) 41: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 42: 01 dife (subunit=0 tariff=0 storagenr=3) - (hydrus) 43: 6D vif (Date and time type) - (hydrus) 44: * 3B177F2A at date (2019-10-31 23:59) - (hydrus) 48: CC dif (8 digit BCD Instantaneous value storagenr=1) - (hydrus) 49: 01 dife (subunit=0 tariff=0 storagenr=3) - (hydrus) 4a: 13 vif (Volume l) - (hydrus) 4b: * 00020000 total consumption at date (0.200000 m3) - - {"media":"water","meter":"hydrus","name":"Votten","id":"64646464","total_m3":1.174,"max_flow_m3h":0,"flow_temperature_c":10.4,"total_at_date_m3":0.2,"at_date":"2019-10-31 23:59","battery_life_days":"4999","status":"OK","timestamp":"2020-09-27T08:54:42Z"} - - */ - /* - Yet another version telegram: - (hydrus) 0f: 2f2f decrypt check bytes - (hydrus) 11: 01 dif (8 Bit Integer/Binary Instantaneous value) - (hydrus) 12: FD vif (Second extension of VIF-codes) - (hydrus) 13: 08 vife (Access Number (transmission count)) - (hydrus) 14: 88 - (hydrus) 15: 0C dif (8 digit BCD Instantaneous value) - (hydrus) 16: 13 vif (Volume l) - (hydrus) 17: * 45911600 total consumption (169.145000 m3) - (hydrus) 1b: 8C dif (8 digit BCD Instantaneous value) - (hydrus) 1c: 10 dife (subunit=0 tariff=1 storagenr=0) - (hydrus) 1d: 05 vif (Energy 10² Wh) - (hydrus) 1e: 77900200 - (hydrus) 22: 0B dif (6 digit BCD Instantaneous value) - (hydrus) 23: 3B vif (Volume flow l/h) - (hydrus) 24: * 000000 max flow (0.000000 m3/h) - (hydrus) 27: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 28: 5A vif (Flow temperature 10⁻¹ °C) - (hydrus) 29: * DD00 flow temperature (22.100000 °C) - (hydrus) 2b: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 2c: FD vif (Second extension of VIF-codes) - (hydrus) 2d: 17 vife (Error flags (binary)) - (hydrus) 2e: 0000 - (hydrus) 30: 0A dif (4 digit BCD Instantaneous value) - (hydrus) 31: A6 vif (Operating time hours) - (hydrus) 32: 18 vife (?) - (hydrus) 33: 0000 - (hydrus) 35: 0B dif (6 digit BCD Instantaneous value) - (hydrus) 36: 26 vif (Operating time hours) - (hydrus) 37: 255802 - (hydrus) 3a: 2B dif (6 digit BCD Minimum value) - (hydrus) 3b: 3B vif (Volume flow l/h) - (hydrus) 3c: 000000 - (hydrus) 3f: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 40: 01 dife (subunit=0 tariff=0 storagenr=3) - (hydrus) 41: 6D vif (Date and time type) - (hydrus) 42: * 3B179F2A at date (2020-10-31 23:59) - (hydrus) 46: CC dif (8 digit BCD Instantaneous value storagenr=1) - (hydrus) 47: 01 dife (subunit=0 tariff=0 storagenr=3) - (hydrus) 48: 13 vif (Volume l) - (hydrus) 49: * 14811600 total consumption at date (168.114000 m3) - (hydrus) 4d: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 4e: 66 vif (External temperature 10⁻¹ °C) - (hydrus) 4f: D400 - (hydrus) 51: 2F skip - (hydrus) 52: 2F skip - (hydrus) 53: 2F skip - (hydrus) 54: 2F skip - (hydrus) 55: 2F skip - (hydrus) 56: 2F skip - (hydrus) 57: 2F skip - (hydrus) 58: 2F skip - (hydrus) 59: 2F skip - (hydrus) 5a: 2F skip - (hydrus) 5b: 2F skip - (hydrus) 5c: 2F skip - (hydrus) 5d: 2F skip - (hydrus) 5e: 2F skip - */ - - /* Another one for #216 / #223 - - (hydrus) 00: 66 length (102 bytes) - (hydrus) 01: 44 dll-c (from meter SND_NR) - (hydrus) 02: 2423 dll-mfct (HYD) - (hydrus) 04: 28001081 dll-id (81100028) - (hydrus) 08: 64 dll-version - (hydrus) 09: 0e dll-type (Bus/System component) - (hydrus) 0a: 72 tpl-ci-field (EN 13757-3 Application Layer (long tplh)) - (hydrus) 0b: 66567464 tpl-id (64745666) - (hydrus) 0f: a511 tpl-mfct (DME) - (hydrus) 11: 70 tpl-version - (hydrus) 12: 07 tpl-type (Water meter) - (hydrus) 13: 1f tpl-acc-field - (hydrus) 14: 00 tpl-sts-field - (hydrus) 15: 5005 tpl-cfg 0550 (AES_CBC_IV nb=5 cntn=0 ra=0 hc=0 ) - (hydrus) 17: 2f2f decrypt check bytes - (hydrus) 19: 03 dif (24 Bit Integer/Binary Instantaneous value) - (hydrus) 1a: 74 vif (Actuality duration seconds) - (hydrus) 1b: * 111A00 actuality duration (-0.000029 s) - (hydrus) 1e: 0C dif (8 digit BCD Instantaneous value) - (hydrus) 1f: 13 vif (Volume l) - (hydrus) 20: * 91721300 total consumption (137.291000 m3) - (hydrus) 24: 8C dif (8 digit BCD Instantaneous value) - (hydrus) 25: 10 dife (subunit=0 tariff=1 storagenr=0) - (hydrus) 26: 13 vif (Volume l) - (hydrus) 27: * 00000000 total consumption at tariff 1 (0.000000 m3) - (hydrus) 2b: 8C dif (8 digit BCD Instantaneous value) - (hydrus) 2c: 20 dife (subunit=0 tariff=2 storagenr=0) - (hydrus) 2d: 13 vif (Volume l) - (hydrus) 2e: * 91721300 total consumption at tariff 2 (137.291000 m3) - (hydrus) 32: 0B dif (6 digit BCD Instantaneous value) - (hydrus) 33: 3B vif (Volume flow l/h) - (hydrus) 34: * 000000 max flow (0.000000 m3/h) - (hydrus) 37: 0B dif (6 digit BCD Instantaneous value) - (hydrus) 38: 26 vif (Operating time hours) - (hydrus) 39: * 784601 operating time (14678.000000 h) - (hydrus) 3c: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 3d: 5A vif (Flow temperature 10⁻¹ °C) - (hydrus) 3e: * F500 flow temperature (24.500000 °C) - (hydrus) 40: 02 dif (16 Bit Integer/Binary Instantaneous value) - (hydrus) 41: 66 vif (External temperature 10⁻¹ °C) - (hydrus) 42: * EF00 external temperature (23.900000 °C) - (hydrus) 44: 04 dif (32 Bit Integer/Binary Instantaneous value) - (hydrus) 45: 6D vif (Date and time type) - (hydrus) 46: * 1B08B721 current date (2021-01-23 08:27) - (hydrus) 4a: 4C dif (8 digit BCD Instantaneous value storagenr=1) - (hydrus) 4b: 13 vif (Volume l) - (hydrus) 4c: * 38861200 total consumption at date (128.638000 m3) - (hydrus) 50: CC dif (8 digit BCD Instantaneous value storagenr=1) - (hydrus) 51: 10 dife (subunit=0 tariff=1 storagenr=1) - (hydrus) 52: 13 vif (Volume l) - (hydrus) 53: * 00000000 total consumption at tariff 1 at date (0.000000 m3) - (hydrus) 57: CC dif (8 digit BCD Instantaneous value storagenr=1) - (hydrus) 58: 20 dife (subunit=0 tariff=2 storagenr=1) - (hydrus) 59: 13 vif (Volume l) - (hydrus) 5a: * 38861200 total consumption at tariff 1 at date (128.638000 m3) - (hydrus) 5e: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 5f: 6C vif (Date type G) - (hydrus) 60: * 9F2C at date (2020-12-31 00:00) - (hydrus) 62: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 63: EC vif (Date type G) - (hydrus) 64: 7E vife (additive correction constant: unit of VIF * 10^-1) - (hydrus) 65: BF2C - */ - - /* And another two more that seem to be branded IZAR RS 868 */ - /* - (hydrus) 00: 1e length (30 bytes) - (hydrus) 01: 44 dll-c (from meter SND_NR) - (hydrus) 02: 2423 dll-mfct (HYD) - (hydrus) 04: 79738960 dll-id (60897379) - (hydrus) 08: 8b dll-version - (hydrus) 09: 07 dll-type (Water meter) - (hydrus) 0a: 7a tpl-ci-field (EN 13757-3 Application Layer (short tplh)) - (hydrus) 0b: 8f tpl-acc-field - (hydrus) 0c: 00 tpl-sts-field (OK) - (hydrus) 0d: 107d tpl-cfg 7d10 (accessibility synchronous SPECIFIC_16_31 ) - (hydrus) 0f: 04 dif (32 Bit Integer/Binary Instantaneous value) - (hydrus) 10: 13 vif (Volume l) - (hydrus) 11: * 12170100 total consumption (71.442000 m3) - (hydrus) 15: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 16: 6C vif (Date type G) - (hydrus) 17: * BF23 at date (2021-03-31 00:00) - (hydrus) 19: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 1a: 13 vif (Volume l) - (hydrus) 1b: * 44100100 total consumption at date (69.700000 m3) - - (hydrus) 01: 44 dll-c (from meter SND_NR) - (hydrus) 02: 2423 dll-mfct (HYD) - (hydrus) 04: 20479060 dll-id (60904720) - (hydrus) 08: 8b dll-version - (hydrus) 09: 06 dll-type (Warm Water (30°C-90°C) meter) - (hydrus) 0a: 7a tpl-ci-field (EN 13757-3 Application Layer (short tplh)) - (hydrus) 0b: 2a tpl-acc-field - (hydrus) 0c: 00 tpl-sts-field (OK) - (hydrus) 0d: 10d8 tpl-cfg d810 (bidirectional accessibility SPECIFIC_16_31 ) - (hydrus) 0f: 04 dif (32 Bit Integer/Binary Instantaneous value) - (hydrus) 10: 13 vif (Volume l) - (hydrus) 11: * DDC00000 total consumption (49.373000 m3) - (hydrus) 15: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 16: 6C vif (Date type G) - (hydrus) 17: * BF23 at date (2021-03-31 00:00) - (hydrus) 19: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (hydrus) 1a: 13 vif (Volume l) - (hydrus) 1b: * 82BB0000 total consumption at date (48.002000 m3) - */ - - int offset; - string key; - struct tm datetime; - - // Container 0 : current / total - - 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, 0, 1, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff1_m3_); - t->addMoreExplanation(offset, " total consumption at tariff 1 (%f m3)", total_water_consumption_tariff1_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 2, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff2_m3_); - t->addMoreExplanation(offset, " total consumption at tariff 2 (%f m3)", total_water_consumption_tariff2_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &max_flow_m3h_); - t->addMoreExplanation(offset, " max flow (%f m3/h)", max_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_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::ExternalTemperature, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &external_temperature_c_); - t->addMoreExplanation(offset, " external temperature (%f °C)", external_temperature_c_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) { - extractDVdate(&t->dv_entries, key, &offset, &datetime); - current_date_ = strdatetime(&datetime); - t->addMoreExplanation(offset, " current date (%s)", current_date_.c_str()); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::ActualityDuration, 0, 0, &key, &t->dv_entries)) { - extractDVuint24(&t->dv_entries, key, &offset, &actuality_duration_s_); - t->addMoreExplanation(offset, " actuality duration (%f s)", actuality_duration_s_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::OperatingTime, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &operating_time_h_); - t->addMoreExplanation(offset, " operating time (%f h)", operating_time_h_); - } - - // Container 1/3 : past/future records - - if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries) - || findKey(MeasurementType::Instantaneous, VIFRange::Volume, 3, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_at_date_m3_); - t->addMoreExplanation(offset, " total consumption at date (%f m3)", total_water_consumption_at_date_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 1, &key, &t->dv_entries) - || findKey(MeasurementType::Instantaneous, VIFRange::Volume, 3, 1, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff1_at_date_m3_); - t->addMoreExplanation(offset, " total consumption at tariff 1 at date (%f m3)", total_water_consumption_tariff1_at_date_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 2, &key, &t->dv_entries) - || findKey(MeasurementType::Instantaneous, VIFRange::Volume, 3, 2, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff2_at_date_m3_); - t->addMoreExplanation(offset, " total consumption at tariff 1 at date (%f m3)", total_water_consumption_tariff2_at_date_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Date , 1, 0, &key, &t->dv_entries) - || findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 3, 0, &key, &t->dv_entries)) { - extractDVdate(&t->dv_entries, key, &offset, &datetime); - at_date_ = strdatetime(&datetime); - t->addMoreExplanation(offset, " at date (%s)", at_date_.c_str()); - } - - // TODO: a date in the future is also transmitted with VIFE 7E in container 1 - - // custom - - uint16_t days {}; - if (hasKey(&t->dv_entries, "02FD74") && extractDVuint16(&t->dv_entries, "02FD74", &offset, &days)) - { - remaining_battery_life_year_ = ((double)days) / 365.25; - t->addMoreExplanation(offset, " battery life (%d days %f years)", days, remaining_battery_life_year_); - } - - status_ = ::decodeTPLStatusByteWithMfct(t->tpl_sts, error_codes_); - } - - double Driver::totalWaterConsumption(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_m3_, Unit::M3, u); - } - - double Driver::totalWaterConsumptionTariff1(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_tariff1_m3_, Unit::M3, u); - } - - double Driver::totalWaterConsumptionTariff2(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_tariff2_m3_, Unit::M3, u); - } - - double Driver::totalWaterConsumptionAtDate(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_at_date_m3_, Unit::M3, u); - } - - double Driver::totalWaterConsumptionTariff1AtDate(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_tariff1_at_date_m3_, Unit::M3, u); - } - - double Driver::totalWaterConsumptionTariff2AtDate(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_tariff2_at_date_m3_, Unit::M3, u); - } - - double Driver::maxFlow(Unit u) - { - assertQuantity(u, Quantity::Flow); - return convert(max_flow_m3h_, Unit::M3H, u); - } - - bool Driver::hasMaxFlow() - { - return true; - } - - double Driver::flowTemperature(Unit u) - { - assertQuantity(u, Quantity::Temperature); - return convert(flow_temperature_c_, Unit::C, u); - } - - double Driver::externalTemperature(Unit u) - { - assertQuantity(u, Quantity::Temperature); - return convert(external_temperature_c_, Unit::C, u); - } } - - // Test: HydrusWater hydrus 64646464 NOKEY +// Comment: // telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000| -// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":10.4,"external_temperature_c":0,"current_date":"","total_at_date_m3":0,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2000-00-00 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":13.686516,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} -// |HydrusWater;64646464;1.174;0;OK;1111-11-11 11:11.11 +// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"flow_m3h":0,"flow_temperature_c":10.4,"remaining_battery_life_y":13.686797,"status":"OK","at_datetime":"2019-10-31 23:59","total_at_date_m3": 0.2,"timestamp":"1111-11-11T11:11:11Z"} +// |HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11 // Test: HydrusVater hydrus 65656565 NOKEY +// Comment: // telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F| -// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","total_m3":3.45,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":3.431,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2020-09-13 23:59","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":15.321013,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","flow_m3h":0,"customer": "A20B410178","total_m3":3.45,"total_at_date_m3":3.431,"remaining_battery_life_y":15.321328,"at_datetime":"2020-09-13 23:59","total_at_date_m3": 3.431,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} // |HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11 // Test: HydrusAES hydrus 64745666 NOKEY +// Comment: // telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C| -// {"media":"water","meter":"hydrus","name":"HydrusAES","id":"64745666","total_m3":137.291,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"max_flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"current_date":"2021-01-23 08:27","total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31 00:00","actuality_duration_s":6673,"operating_time_h":14678,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +// {"media":"water","meter":"hydrus","name":"HydrusAES","id":"64745666","total_m3":137.291,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31","actuality_duration_s":6673,"operating_time_h":14678,"meter_datetime": "2021-01-23 08:27","status":"OK","timestamp":"1111-11-11T11:11:11Z"} // |HydrusAES;64745666;137.291;128.638;OK;1111-11-11 11:11.11 + +// Test: HydrusIzarRS hydrus 60897379 NOKEY +// Comment: Hydrus IZAR RS 868 water meter +// telegram=|1E4424238B07797389607A8F00107D_041312170100426CBF23441344100100| +// {"media":"water","meter":"hydrus","name":"HydrusIzarRS","id":"60897379","total_m3":71.442,"at_date": "2021-03-31","total_at_date_m3":69.7,"status":"OK","timestamp":"1111-11-11T11:11:11Z"} +// |HydrusIzarRS;60897379;71.442;69.7;OK;1111-11-11 11:11.11 + +// Test: HydrusIzarRSWarm hydrus 60904720 NOKEY +// Comment: Hydrus IZAR RS 868 water meter warm +// telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000| +// {"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_at_date_m3":48.002,"at_date":"2021-03-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"} +// |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11 diff --git a/src/driver_izar.cc b/src/driver_izar.cc index 459a567..9709270 100644 --- a/src/driver_izar.cc +++ b/src/driver_izar.cc @@ -1,6 +1,6 @@ /* Copyright (C) 2019 Jacek Tomasiak (gpl-3.0-or-later) - Copyright (C) 2020-2022 Fredrik Öhrström (gpl-3.0-or-later) + Copyright (C) 2020-2023 Fredrik Öhrström (gpl-3.0-or-later) Copyright (C) 2021 Vincent Privat (gpl-3.0-or-later) This program is free software: you can redistribute it and/or modify @@ -23,7 +23,8 @@ namespace { /** Contains all the booleans required to store the alarms of a PRIOS device. */ - typedef struct _izar_alarms { + struct IzarAlarms + { bool general_alarm; bool leakage_currently; bool leakage_previously; @@ -36,7 +37,7 @@ namespace bool sensor_fraud_previously; bool mechanical_fraud_currently; bool mechanical_fraud_previously; - } izar_alarms; + }; struct Driver : public virtual MeterCommonImplementation { @@ -46,27 +47,11 @@ namespace private: - double totalWaterConsumption(Unit u); - string serialNumber(); - double lastMonthTotalWaterConsumption(Unit u); - string setH0Date(); - string currentAlarmsText(); - string previousAlarmsText(); + string currentAlarmsText(IzarAlarms &alarms); + string previousAlarmsText(IzarAlarms &alarms); vector decodePrios(const vector &origin, const vector &payload, uint32_t key); - string prefix; - uint32_t serial_number {0}; - double remaining_battery_life; - uint16_t h0_year; - uint8_t h0_month; - uint8_t h0_day; - double total_water_consumption_l_ {}; - double last_month_total_water_consumption_l_ {}; - uint32_t transmit_period_s_ {}; - uint16_t manufacture_year {0}; - izar_alarms alarms; - vector keys; }; @@ -89,6 +74,7 @@ namespace di.addDetection(MANUFACTURER_DME, 0x07, 0x78); di.addDetection(MANUFACTURER_DME, 0x06, 0x78); di.addDetection(MANUFACTURER_HYD, 0x07, 0x86); + di.usesProcessContent(); di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); }); @@ -97,84 +83,52 @@ namespace { initializeDiehlDefaultKeySupport(meterKeys()->confidentiality_key, keys); - addPrint("prefix", Quantity::Text, - [&](){ return prefix; }, - "The alphanumeric prefix printed before serial number on device.", - DEFAULT_PRINT_PROPERTIES); + addStringField("prefix", + "The alphanumeric prefix printed before serial number on device.", + DEFAULT_PRINT_PROPERTIES); - addPrint("serial_number", Quantity::Text, - [&](){ return serialNumber(); }, - "The meter serial number.", - DEFAULT_PRINT_PROPERTIES); + addStringField("serial_number", + "The meter serial number.", + DEFAULT_PRINT_PROPERTIES); - addPrint("total", Quantity::Volume, - [&](Unit u){ return totalWaterConsumption(u); }, - "The total water consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter."); - addPrint("last_month_total", Quantity::Volume, - [&](Unit u){ return lastMonthTotalWaterConsumption(u); }, - "The total water consumption recorded by this meter around end of last month.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("last_month_total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter around end of last month."); - addPrint("last_month_measure_date", Quantity::Text, - [&](){ return setH0Date(); }, - "The date when the meter recorded the most recent billing value.", - DEFAULT_PRINT_PROPERTIES); + addStringField("last_month_measure_date", + "The date when the meter recorded the most recent billing value.", + DEFAULT_PRINT_PROPERTIES); - addPrint("remaining_battery_life", Quantity::Time, Unit::Year, - [&](Unit u){ return convert(remaining_battery_life, Unit::Year, u); }, - "How many more years the battery is expected to last", - DEFAULT_PRINT_PROPERTIES); + addNumericField("remaining_battery_life", + Quantity::Time, + DEFAULT_PRINT_PROPERTIES, + "How many more years the battery is expected to last", + Unit::Year); + addStringField("current_alarms", + "Alarms currently reported by the meter.", + DEFAULT_PRINT_PROPERTIES); - addPrint("current_alarms", Quantity::Text, - [&](){ return currentAlarmsText(); }, - "Alarms currently reported by the meter.", - DEFAULT_PRINT_PROPERTIES); + addStringField("previous_alarms", + "Alarms previously reported by the meter.", + DEFAULT_PRINT_PROPERTIES); - addPrint("previous_alarms", Quantity::Text, - [&](){ return previousAlarmsText(); }, - "Alarms previously reported by the meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("transmit_period", Quantity::Time, + DEFAULT_PRINT_PROPERTIES, + "The period at which the meter transmits its data.", + Unit::Second); - addPrint("transmit_period", Quantity::Time, Unit::Second, - [&](Unit u){ return convert(transmit_period_s_, Unit::Second, u); }, - "The period at which the meter transmits its data.", - DEFAULT_PRINT_PROPERTIES); - - addPrint("manufacture_year", Quantity::Text, - [&](){ return to_string(manufacture_year); }, - "The year during which the meter was manufactured.", - DEFAULT_PRINT_PROPERTIES); + addStringField("manufacture_year", + "The year during which the meter was manufactured.", + DEFAULT_PRINT_PROPERTIES); } - double Driver::totalWaterConsumption(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_l_, Unit::L, u); - } - - double Driver::lastMonthTotalWaterConsumption(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(last_month_total_water_consumption_l_, Unit::L, u); - } - - string Driver::setH0Date() - { - string date; - strprintf(&date, "%d-%02d-%02d", h0_year, h0_month%99, h0_day%99); - return date; - } - - string Driver::serialNumber() - { - string result; - strprintf(&result, "%06d", serial_number); - return result; - } - - string Driver::currentAlarmsText() + string Driver::currentAlarmsText(IzarAlarms &alarms) { string s; if (alarms.leakage_currently) { @@ -211,7 +165,7 @@ namespace return "no_alarm"; } - string Driver::previousAlarmsText() + string Driver::previousAlarmsText(IzarAlarms &alarms) { string s; if (alarms.leakage_previously) { @@ -259,35 +213,50 @@ namespace string digits = to_string((origin[7] & 0x03) << 24 | origin[6] << 16 | origin[5] << 8 | origin[4]); // get the manufacture year uint8_t yy = atoi(digits.substr(0, 2).c_str()); - manufacture_year = yy > 70 ? (1900 + yy) : (2000 + yy); // Maybe to adjust in 2070, if this code stills lives :D + int manufacture_year = yy > 70 ? (1900 + yy) : (2000 + yy); // Maybe to adjust in 2070, if this code stills lives :D + setStringValue("manufacture_year", tostrprintf("%d", manufacture_year)); + // get the serial number - serial_number = atoi(digits.substr(2, digits.size()).c_str()); + uint32_t serial_number = atoi(digits.substr(2, digits.size()).c_str()); + setStringValue("serial_number", tostrprintf("%06d", serial_number)); + // get letters uchar supplier_code = '@' + (((origin[9] & 0x0F) << 1) | (origin[8] >> 7)); uchar meter_type = '@' + ((origin[8] & 0x7C) >> 2); uchar diameter = '@' + (((origin[8] & 0x03) << 3) | (origin[7] >> 5)); // build the prefix - strprintf(&prefix, "%c%02d%c%c", supplier_code, yy, meter_type, diameter); + string prefix = tostrprintf("%c%02d%c%c", supplier_code, yy, meter_type, diameter); + setStringValue("prefix", prefix); } // get the remaining battery life (in year) and transmission period (in seconds) - remaining_battery_life = (frame[12] & 0x1F) / 2.0; - transmit_period_s_ = 1 << ((frame[11] & 0x0F) + 2); + double remaining_battery_life = (frame[12] & 0x1F) / 2.0; + setNumericValue("remaining_battery_life", Unit::Year, remaining_battery_life); - total_water_consumption_l_ = uint32FromBytes(decoded_content, 1, true); - last_month_total_water_consumption_l_ = uint32FromBytes(decoded_content, 5, true); + int transmit_period_s = 1 << ((frame[11] & 0x0F) + 2); + setNumericValue("transmit_period", Unit::Second, transmit_period_s); + + double total_water_consumption_l_ = uint32FromBytes(decoded_content, 1, true); + setNumericValue("total", Unit::L, total_water_consumption_l_); + + double last_month_total_water_consumption_l_ = uint32FromBytes(decoded_content, 5, true); + setNumericValue("last_month_total", Unit::L, last_month_total_water_consumption_l_); // get the date when the second measurement was taken - h0_year = ((decoded_content[10] & 0xF0) >> 1) + ((decoded_content[9] & 0xE0) >> 5); + uint16_t h0_year = ((decoded_content[10] & 0xF0) >> 1) + ((decoded_content[9] & 0xE0) >> 5); if (h0_year > 80) { h0_year += 1900; } else { h0_year += 2000; } - h0_month = decoded_content[10] & 0xF; - h0_day = decoded_content[9] & 0x1F; + uint8_t h0_month = decoded_content[10] & 0xF; + uint8_t h0_day = decoded_content[9] & 0x1F; + + setStringValue("last_month_measure_date", tostrprintf("%d-%02d-%02d", h0_year, h0_month%99, h0_day%99)); // read the alarms: + IzarAlarms alarms {}; + alarms.general_alarm = frame[11] >> 7; alarms.leakage_currently = frame[12] >> 7; alarms.leakage_previously = frame[12] >> 6 & 0x1; @@ -300,6 +269,9 @@ namespace alarms.sensor_fraud_previously = frame[13] >> 2 & 0x1; alarms.mechanical_fraud_currently = frame[13] >> 1 & 0x1; alarms.mechanical_fraud_previously = frame[13] & 0x1; + + setStringValue("current_alarms", currentAlarmsText(alarms)); + setStringValue("previous_alarms", previousAlarmsText(alarms)); } vector Driver::decodePrios(const vector &origin, const vector &frame, uint32_t key) @@ -309,19 +281,19 @@ namespace } // Test: IzarWater izar 21242472 NOKEY -// telegram=|1944304C72242421D401A2|013D4013DD8B46A4999C1293E582CC| +// telegram=|1944304C72242421D401A2_013D4013DD8B46A4999C1293E582CC| // {"media":"water","meter":"izar","name":"IzarWater","id":"21242472","prefix":"C19UA","serial_number":"145842","total_m3":3.488,"last_month_total_m3":3.486,"last_month_measure_date":"2019-09-30","remaining_battery_life_y":14.5,"current_alarms":"meter_blocked,underflow","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"2019","timestamp":"1111-11-11T11:11:11Z"} // |IzarWater;21242472;C19UA;145842;3.488;3.486;2019-09-30;14.5;meter_blocked,underflow;no_alarm;8;2019;1111-11-11 11:11.11 // Test: IzarWater2 izar 66236629 NOKEY // telegram=|2944A511780729662366A20118001378D3B3DB8CEDD77731F25832AAF3DA8CADF9774EA673172E8C61F2| -// {"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","prefix":"","serial_number":"000000","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} -// |IzarWater2;66236629;;000000;16.76;11.84;2019-11-30;12;no_alarm;no_alarm;8;0;1111-11-11 11:11.11 +// {"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} +// |IzarWater2;66236629;null;null;16.76;11.84;2019-11-30;12;no_alarm;no_alarm;8;null;1111-11-11 11:11.11 // Test: IzarWater3 izar 20481979 NOKEY -// telegram=|1944A511780779194820A1|21170013355F8EDB2D03C6912B1E37 -// {"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","prefix":"","serial_number":"000000","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} -// |IzarWater3;20481979;;000000;4.366;0;2020-12-31;11.5;no_alarm;no_alarm;8;0;1111-11-11 11:11.11 +// telegram=|1944A511780779194820A1_21170013355F8EDB2D03C6912B1E37 +// {"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} +// |IzarWater3;20481979;null;null;4.366;0;2020-12-31;11.5;no_alarm;no_alarm;8;null;1111-11-11 11:11.11 // Test: IzarWater4 izar 2124589c NOKEY // Comment: With mfct specific tpl ci field a3. @@ -331,11 +303,11 @@ namespace // Test: IzarWater5 izar 20e4ffde NOKEY // Comment: Ensure non-regression on manufacture year parsing -// telegram=|1944304CDEFFE420CC01A2|63120013258F907B0AFF12529AC33B| +// telegram=|1944304CDEFFE420CC01A2_63120013258F907B0AFF12529AC33B| // {"media":"water","meter":"izar","name":"IzarWater5","id":"20e4ffde","prefix":"C15SA","serial_number":"007710","total_m3":159.832,"last_month_total_m3":157.76,"last_month_measure_date":"2021-02-01","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":32,"manufacture_year":"2015","timestamp":"1111-11-11T11:11:11Z"} // |IzarWater5;20e4ffde;C15SA;007710;159.832;157.76;2021-02-01;9;no_alarm;no_alarm;32;2015;1111-11-11 11:11.11 // Test: IzarWater6 izar 48500375 NOKEY // telegram=|19442423860775035048A251520015BEB6B2E1ED623A18FC74A5| -// {"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","prefix":"","serial_number":"000000","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"} -// |IzarWater6;48500375;;000000;521.602;519.147;2021-11-15;9;no_alarm;leakage;8;0;1111-11-11 11:11.11 +// {"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"} +// |IzarWater6;48500375;null;null;521.602;519.147;2021-11-15;9;no_alarm;leakage;8;null;1111-11-11 11:11.11 diff --git a/src/driver_lansendw.cc b/src/driver_lansendw.cc index 24fecec..a448c39 100644 --- a/src/driver_lansendw.cc +++ b/src/driver_lansendw.cc @@ -17,144 +17,83 @@ #include"meters_common_implementation.h" -#define INFO_CODE_CLOSED 0x0011 -#define INFO_CODE_OPEN 0x0055 - -struct MeterLansenDW : public virtual MeterCommonImplementation +namespace { - MeterLansenDW(MeterInfo &mi, DriverInfo &di); - -private: - - void processContent(Telegram *t); - - string status(); - - uint16_t info_codes_ {}; - double pulse_counter_a_ {}; - double pulse_counter_b_ {}; -}; - -static bool ok = registerDriver([](DriverInfo&di) -{ - di.setName("lansendw"); - di.setDefaultFields("name,id,status,timestamp"); - di.setMeterType(MeterType::DoorWindowDetector); - di.addLinkMode(LinkMode::T1); - di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new MeterLansenDW(mi, di)); }); - di.addDetection(MANUFACTURER_LAS, 0x1d, 0x07); -}); - -MeterLansenDW::MeterLansenDW(MeterInfo &mi, DriverInfo &di) : - MeterCommonImplementation(mi, di) -{ - setMeterType(MeterType::DoorWindowDetector); - - setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); - - addLinkMode(LinkMode::T1); - - addPrint("status", Quantity::Text, - [&](){ return status(); }, - "The current status: OPEN or CLOSED.", - DEFAULT_PRINT_PROPERTIES); - - addPrint("a", Quantity::Counter, - [&](Unit u) { assertQuantity(u, Quantity::Counter); return pulse_counter_a_; }, - "How many times the door/window has been opened or closed.", - DEFAULT_PRINT_PROPERTIES); - - addPrint("b", Quantity::Counter, - [&](Unit u) { assertQuantity(u, Quantity::Counter); return pulse_counter_b_; }, - "The current number of counted pulses from counter b.", - DEFAULT_PRINT_PROPERTIES); -} - - -shared_ptr createLansenDW(MeterInfo &mi) -{ - DriverInfo di; - di.setName("lansendw"); - return shared_ptr(new MeterLansenDW(mi, di)); -} - - -void MeterLansenDW::processContent(Telegram *t) -{ - /* - (wmbus) 11: 02 dif (16 Bit Integer/Binary Instantaneous value) - (wmbus) 12: FD vif (Second extension of VIF-codes) - (wmbus) 13: 1B vife (Digital Input (binary)) - (wmbus) 14: 1100 - (wmbus) 16: 02 dif (16 Bit Integer/Binary Instantaneous value) - (wmbus) 17: FD vif (Second extension of VIF-codes) - (wmbus) 18: 97 vife (Error flags (binary)) - (wmbus) 19: 1D vife (Response delay time [bittimes]) - (wmbus) 1a: 0100 - (wmbus) 1c: 0E dif (12 digit BCD Instantaneous value) - (wmbus) 1d: FD vif (Second extension of VIF-codes) - (wmbus) 1e: 3A vife (Dimensionless / no VIF) - (wmbus) 1f: 220000000000 - (wmbus) 25: 8E dif (12 digit BCD Instantaneous value) - (wmbus) 26: 40 dife (subunit=1 tariff=0 storagenr=0) - (wmbus) 27: FD vif (Second extension of VIF-codes) - (wmbus) 28: 3A vife (Dimensionless / no VIF) - (wmbus) 29: 000000000000 - */ - - /* - (wmbus) 11: 02 dif (16 Bit Integer/Binary Instantaneous value) - (wmbus) 12: FD vif (Second extension of VIF-codes) - (wmbus) 13: 1B vife (Digital Input (binary)) - (wmbus) 14: 5500 - (wmbus) 16: 02 dif (16 Bit Integer/Binary Instantaneous value) - (wmbus) 17: FD vif (Second extension of VIF-codes) - (wmbus) 18: 97 vife (Error flags (binary)) - (wmbus) 19: 1D vife (Response delay time [bittimes]) - (wmbus) 1a: 0100 - (wmbus) 1c: 0E dif (12 digit BCD Instantaneous value) - (wmbus) 1d: FD vif (Second extension of VIF-codes) - (wmbus) 1e: 3A vife (Dimensionless / no VIF) - (wmbus) 1f: 230000000000 - (wmbus) 25: 8E dif (12 digit BCD Instantaneous value) - (wmbus) 26: 40 dife (subunit=1 tariff=0 storagenr=0) - (wmbus) 27: FD vif (Second extension of VIF-codes) - (wmbus) 28: 3A vife (Dimensionless / no VIF) - (wmbus) 29: 000000000000 - */ - int offset; - - if (extractDVuint16(&t->dv_entries, "02FD1B", &offset, &info_codes_)) + struct Driver : public virtual MeterCommonImplementation { - t->addMoreExplanation(offset, renderJsonOnlyDefaultUnit("status", Quantity::Text)); - } + Driver(MeterInfo &mi, DriverInfo &di); + }; - if (extractDVdouble(&t->dv_entries, "0EFD3A", &offset, &pulse_counter_a_, false)) + static bool ok = registerDriver([](DriverInfo&di) { - t->addMoreExplanation(offset, renderJsonOnlyDefaultUnit("counter_a", Quantity::Counter)); - } + di.setName("lansendw"); + di.setDefaultFields("name,id,status,timestamp"); + di.setMeterType(MeterType::DoorWindowDetector); + di.addLinkMode(LinkMode::T1); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + di.addDetection(MANUFACTURER_LAS, 0x1d, 0x07); + }); - if (extractDVdouble(&t->dv_entries, "8E40FD3A", &offset, &pulse_counter_b_, false)) + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - t->addMoreExplanation(offset, renderJsonOnlyDefaultUnit("counter_b", Quantity::Counter)); + addStringFieldWithExtractorAndLookup( + "status", + "The state (OPEN/CLOSED) for the door/window.", + DEFAULT_PRINT_PROPERTIES, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::DigitalInput), + Translate::Lookup() + .add(Translate::Rule("INPUT_BITS", Translate::Type::IndexToString) + .set(MaskBits(0xffff)) + .add(Translate::Map(0x11 ,"CLOSED", TestBit::Set)) + .add(Translate::Map(0x55 ,"OPEN", TestBit::Set)) + )); + + addStringFieldWithExtractorAndLookup( + "error_flags", + "Error flags.", + PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ErrorFlags), + Translate::Lookup() + .add(Translate::Rule("ERROR_FLAGS", Translate::Type::BitToString) + .set(MaskBits(0xffff)) + .set(DefaultMessage("OK")) + )); + + addNumericFieldWithExtractor( + "a", + "How many times the door/window has been opened or closed.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Counter, + VifScaling::None, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Dimensionless) + ); + + addNumericFieldWithExtractor( + "b", + "The current number of counted pulses from counter b.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Counter, + VifScaling::None, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Dimensionless) + .set(SubUnitNr(1)) + ); + } } -string MeterLansenDW::status() -{ - string s; - if (info_codes_ == INFO_CODE_OPEN) - { - s.append("OPEN"); - } - else if (info_codes_ == INFO_CODE_CLOSED) - { - s.append("CLOSED"); - } - else - { - s.append("ERR"); - } +// Test: Dooro lansendw 00010205 NOKEY +// telegram=|2e44333005020100071d7ab54800002f2f02fd1b110002fd971d01000efd3a2200000000008e40fd3a000000000000| +// {"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"CLOSED","a_counter":22,"b_counter":0,"error_flags":"ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_40","timestamp":"1111-11-11T11:11:11Z"} +// |Dooro;00010205;CLOSED;1111-11-11 11:11.11 - return s; -} +// telegram=|2e44333005020100071d7ab66800002f2f02fd1b550002fd971d01000efd3a2300000000008e40fd3a000000000000| +// {"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"OPEN","a_counter":23,"b_counter":0,"error_flags":"ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_60","timestamp":"1111-11-11T11:11:11Z"} +// |Dooro;00010205;OPEN;1111-11-11 11:11.11 diff --git a/src/driver_mkradio3.cc b/src/driver_mkradio3.cc index 6ff3f72..5387fcb 100644 --- a/src/driver_mkradio3.cc +++ b/src/driver_mkradio3.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later) + Copyright (C) 2019-2023 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 @@ -24,20 +24,6 @@ namespace Driver(MeterInfo &mi, DriverInfo &di); void processContent(Telegram *t); - - private: - - double totalWaterConsumption(Unit u); - bool hasTotalWaterConsumption(); - double targetWaterConsumption(Unit u); - bool hasTargetWaterConsumption(); - string currentDate(); - string previousDate(); - - double total_water_consumption_m3_ {}; - double target_water_consumption_m3_ {}; - string current_date_ {}; - string previous_date_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -54,25 +40,23 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("total", Quantity::Volume, - [&](Unit u){ return totalWaterConsumption(u); }, - "The total water consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter."); - addPrint("target", Quantity::Volume, - [&](Unit u){ return targetWaterConsumption(u); }, - "The total water consumption recorded at the beginning of this month.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("target", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded at the beginning of this month."); - addPrint("current_date", Quantity::Text, - [&](){ return currentDate(); }, - "Date of current billing period.", - DEFAULT_PRINT_PROPERTIES); + addStringField("current_date", + "Date of current billing period.", + DEFAULT_PRINT_PROPERTIES); - addPrint("prev_date", Quantity::Text, - [&](){ return previousDate(); }, - "Date of previous billing period.", - DEFAULT_PRINT_PROPERTIES); + addStringField("prev_date", + "Date of previous billing period.", + DEFAULT_PRINT_PROPERTIES); } void Driver::processContent(Telegram *t) @@ -91,83 +75,31 @@ namespace uint prev_date_day = (prev_date >> 0) & 0x1F; uint prev_date_month = (prev_date >> 5) & 0x0F; uint prev_date_year = (prev_date >> 9) & 0x3F; - strprintf(&previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day); - - string prev_date_str; - strprintf(&prev_date_str, "%04x", prev_date); - uint offset = t->parsed.size() + 1; - vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x6c, {}, 0, 0, 0, prev_date_str) }; - t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL)); - t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str()); + setStringValue("prev_date", + tostrprintf("%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day)); // Previous consumption uchar prev_lo = content[3]; uchar prev_hi = content[4]; double prev = (256.0*prev_hi+prev_lo)/10.0; - string prevs; - strprintf(&prevs, "%02x%02x", prev_lo, prev_hi); - offset = t->parsed.size()+3; - vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) }; - t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL)); - t->addMoreExplanation(offset, " prev consumption (%f m3)", prev); - // Current date uint16_t current_date = (content[6] << 8) | content[5]; uint current_date_day = (current_date >> 4) & 0x1F; uint current_date_month = (current_date >> 9) & 0x0F; - strprintf(¤t_date_, "%s-%02d-%02dT02:00:00Z", currentYear().c_str(), current_date_month, current_date_day); - - string current_date_str; - strprintf(¤t_date_str, "%04x", current_date); - offset = t->parsed.size() + 5; - vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x6c, {}, 0, 0, 0, current_date_str) }; - t->explanations.push_back(Explanation(offset, 1, current_date_str, KindOfData::CONTENT, Understanding::FULL)); - t->addMoreExplanation(offset, " current date (%s)", current_date_.c_str()); + setStringValue("current_date", + tostrprintf("%s-%02d-%02dT02:00:00Z", + currentYear().c_str(), current_date_month, current_date_day)); // Current consumption uchar curr_lo = content[7]; uchar curr_hi = content[8]; double curr = (256.0*curr_hi+curr_lo)/10.0; - string currs; - strprintf(&currs, "%02x%02x", curr_lo, curr_hi); - offset = t->parsed.size()+7; - vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) }; - t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL)); - t->addMoreExplanation(offset, " curr consumption (%f m3)", curr); - - total_water_consumption_m3_ = prev+curr; - target_water_consumption_m3_ = prev; - } - - double Driver::totalWaterConsumption(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_m3_, Unit::M3, u); - } - - bool Driver::hasTotalWaterConsumption() - { - return true; - } - - double Driver::targetWaterConsumption(Unit u) - { - return target_water_consumption_m3_; - } - - bool Driver::hasTargetWaterConsumption() - { - return true; - } - - string Driver::currentDate() { - return current_date_; - } - - string Driver::previousDate() { - return previous_date_; + double total_water_consumption_m3 = prev+curr; + setNumericValue("total", Unit::M3, total_water_consumption_m3); + double target_water_consumption_m3 = prev; + setNumericValue("target", Unit::M3, target_water_consumption_m3); } } diff --git a/src/driver_mkradio4.cc b/src/driver_mkradio4.cc index 7d66ca8..7d8f158 100644 --- a/src/driver_mkradio4.cc +++ b/src/driver_mkradio4.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later) + Copyright (C) 2019-2023 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 @@ -24,14 +24,6 @@ namespace Driver(MeterInfo &mi, DriverInfo &di); void processContent(Telegram *t); - - private: - - double totalWaterConsumption(Unit u); - double targetWaterConsumption(Unit u); - - double total_water_consumption_m3_ {}; - double target_water_consumption_m3_ {}; }; static bool ok = registerDriver([](DriverInfo&di) @@ -50,20 +42,20 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - addPrint("total", Quantity::Volume, - [&](Unit u){ return totalWaterConsumption(u); }, - "The total water consumption recorded by this meter.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("total", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded by this meter."); - addPrint("target", Quantity::Volume, - [&](Unit u){ return targetWaterConsumption(u); }, - "The total water consumption recorded at the beginning of this month.", - DEFAULT_PRINT_PROPERTIES); + addNumericField("target", + Quantity::Volume, + DEFAULT_PRINT_PROPERTIES, + "The total water consumption recorded at the beginning of this month."); } void Driver::processContent(Telegram *t) { - // Unfortunately, the MK Radio 4 is mostly a proprieatary protocol + // The MK Radio 4 is mostly a proprietary protocol // simple wrapped inside a wmbus telegram since the ci-field is 0xa2. // Which means that the entire payload is manufacturer specific. @@ -76,37 +68,32 @@ namespace uchar prev_hi = content[4]; double prev = (256.0*prev_hi+prev_lo)/10.0; + /* string prevs; strprintf(&prevs, "%02x%02x", prev_lo, prev_hi); int offset = t->parsed.size()+3; vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) }; t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL)); t->addMoreExplanation(offset, " prev consumption (%f m3)", prev); + */ uchar curr_lo = content[7]; uchar curr_hi = content[8]; double curr = (256.0*curr_hi+curr_lo)/10.0; + /* string currs; strprintf(&currs, "%02x%02x", curr_lo, curr_hi); offset = t->parsed.size()+7; vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) }; t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL)); t->addMoreExplanation(offset, " curr consumption (%f m3)", curr); + */ - total_water_consumption_m3_ = prev+curr; - target_water_consumption_m3_ = prev; - } - - double Driver::totalWaterConsumption(Unit u) - { - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_m3_, Unit::M3, u); - } - - double Driver::targetWaterConsumption(Unit u) - { - return target_water_consumption_m3_; + double total_water_consumption_m3 = prev+curr; + setNumericValue("total", Unit::M3, total_water_consumption_m3); + double target_water_consumption_m3 = prev; + setNumericValue("target", Unit::M3, target_water_consumption_m3); } } diff --git a/src/driver_tsd2.cc b/src/driver_tsd2.cc index 2533afb..9354e14 100644 --- a/src/driver_tsd2.cc +++ b/src/driver_tsd2.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2020-2022 Fredrik Öhrström (gpl-3.0-or-later) + Copyright (C) 2020-2023 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 @@ -24,23 +24,13 @@ namespace Driver(MeterInfo &mi, DriverInfo &di); void processContent(Telegram *t); - - private: - - string status(); - bool smokeDetected(); - string previousDate(); - - uint16_t info_codes_ {}; - bool error_ {}; - string previous_date_ {}; }; static bool ok = registerDriver([](DriverInfo&di) { di.setName("tsd2"); di.setDefaultFields("name,id,status,prev_date,timestamp"); - di.setMeterType(MeterType::WaterMeter); + di.setMeterType(MeterType::SmokeDetector); di.addLinkMode(LinkMode::T1); di.addDetection(MANUFACTURER_TCH, 0xf0, 0x76); @@ -49,88 +39,58 @@ namespace Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) { - setMeterType(MeterType::SmokeDetector); + addStringField("status", + "The current status: OK, SMOKE or ERROR.", + DEFAULT_PRINT_PROPERTIES); - setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); - - addLinkMode(LinkMode::T1); - - addPrint("status", Quantity::Text, - [&](){ return status(); }, - "The current status: OK, SMOKE or ERROR.", - DEFAULT_PRINT_PROPERTIES); - - addPrint("prev_date", Quantity::Text, - [&](){ return previousDate(); }, - "Date of previous billing period.", - DEFAULT_PRINT_PROPERTIES); + addStringField("prev_date", + "Date of previous billing period.", + DEFAULT_PRINT_PROPERTIES); } -#define INFO_CODE_SMOKE 0x0001 - - bool Driver::smokeDetected() - { - return info_codes_ & INFO_CODE_SMOKE; - } +#define INFO_CODE_SMOKE 0x01 void Driver::processContent(Telegram *t) { vector data; t->extractPayload(&data); - if(data.empty()) + if(data.size() < 3) { - error_ = true; + setStringValue("status", "ERROR"); return; } - info_codes_ = data[0]; - error_ = false; - // Check CRC etc here.... + uint8_t status = data[0]; + if (status & INFO_CODE_SMOKE) + { + setStringValue("status", "SMOKE"); + } + else + { + setStringValue("status", "OK"); + } - // Previous date uint16_t prev_date = (data[2] << 8) | data[1]; uint prev_date_day = (prev_date >> 0) & 0x1F; uint prev_date_month = (prev_date >> 5) & 0x0F; uint prev_date_year = (prev_date >> 9) & 0x3F; - strprintf(&previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day); - string prev_date_str; - strprintf(&prev_date_str, "%04x", prev_date); - uint offset = t->parsed.size() + 1; - t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL)); - t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str()); - } - - string Driver::status() - { - string s; - bool smoke = info_codes_ & INFO_CODE_SMOKE; - if (smoke) - { - s.append("SMOKE "); - } - - if (error_) - { - s.append("ERROR "); - } - - if (s.length() > 0) - { - s.pop_back(); - return s; - } - return "OK"; - } - - string Driver::previousDate() - { - return previous_date_; + setStringValue("prev_date", + tostrprintf("%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day)); } } // Test: Smokey tsd2 91633569 NOKEY + +// telegram=|294468506935639176F0A0_019F| +// {"media":"smoke detector","meter":"tsd2","name":"Smokey","id":"91633569","status":"ERROR","timestamp":"1111-11-11T11:11:11Z"} +// |Smokey;91633569;ERROR;null;1111-11-11 11:11.11 + // telegram=|294468506935639176F0A0_009F2782290060822900000401D6311AF93E1BF93E008DC3009ED4000FE500| // {"media":"smoke detector","meter":"tsd2","name":"Smokey","id":"91633569","status":"OK","prev_date":"2019-12-31T02:00:00Z","timestamp":"1111-11-11T11:11:11Z"} // |Smokey;91633569;OK;2019-12-31T02:00:00Z;1111-11-11 11:11.11 + +// telegram=|294468506935639176F0A0_019F2782290060822900000401D6311AF93E1BF93E008DC3009ED4000FE500| +// {"media":"smoke detector","meter":"tsd2","name":"Smokey","id":"91633569","status":"SMOKE","prev_date":"2019-12-31T02:00:00Z","timestamp":"1111-11-11T11:11:11Z"} +// |Smokey;91633569;SMOKE;2019-12-31T02:00:00Z;1111-11-11 11:11.11 diff --git a/src/meters.cc b/src/meters.cc index c4865d2..be6fc85 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -302,70 +302,6 @@ void MeterCommonImplementation::setMfctTPLStatusBits(Translate::Lookup &lookup) mfct_tpl_status_bits_ = lookup; } -void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, - function getValueFunc, string help, PrintProperties pprops) -{ - field_infos_.emplace_back( - FieldInfo(field_infos_.size(), - vname, - vquantity, - defaultUnitForQuantity(vquantity), - VifScaling::Auto, - FieldMatcher(), - help, - pprops, - getValueFunc, - NULL, - NULL, - NULL, - NoLookup, /* Lookup table */ - NULL /* Formula */ - )); -} - -void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, Unit unit, - function getValueFunc, string help, PrintProperties pprops) -{ - field_infos_.emplace_back( - FieldInfo(field_infos_.size(), - vname, - vquantity, - unit, - VifScaling::Auto, - FieldMatcher(), - help, - pprops, - getValueFunc, - NULL, - NULL, - NULL, - NoLookup, /* Lookup table */ - NULL /* Formula */ - )); -} - -void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, - function getValueFunc, - string help, PrintProperties pprops) -{ - field_infos_.emplace_back( - FieldInfo(field_infos_.size(), - vname, - vquantity, - defaultUnitForQuantity(vquantity), - VifScaling::Auto, - FieldMatcher(), - help, - pprops, - NULL, - getValueFunc, - NULL, - NULL, - NoLookup, /* Lookup table */ - NULL /* Formula */ - )); -} - void MeterCommonImplementation::addNumericFieldWithExtractor(string vname, string help, PrintProperties print_properties, @@ -469,31 +405,6 @@ void MeterCommonImplementation::addNumericFieldWithCalculatorAndMatcher(string v )); } -void MeterCommonImplementation::addNumericField( - string vname, - Quantity vquantity, - PrintProperties print_properties, - string help, - function setValueFunc, - function getValueFunc) -{ - field_infos_.emplace_back( - FieldInfo(field_infos_.size(), - vname, - vquantity, - defaultUnitForQuantity(vquantity), - VifScaling::None, - FieldMatcher(), - help, - print_properties, - getValueFunc, - NULL, - setValueFunc, - NULL, - NoLookup, /* Lookup table */ - NULL /* Formula */ - )); -} void MeterCommonImplementation::addNumericField( string vname, @@ -543,39 +454,6 @@ void MeterCommonImplementation::addStringFieldWithExtractor(string vname, )); } -void MeterCommonImplementation::addStringFieldWithExtractorAndLookup( - string vname, - Quantity vquantity, - DifVifKey dif_vif_key, - MeasurementType mt, - VIFRange vi, - StorageNr s, - TariffNr t, - IndexNr i, - PrintProperties print_properties, - string help, - function setValueFunc, - function getValueFunc, - Translate::Lookup lookup) -{ - field_infos_.emplace_back( - FieldInfo(field_infos_.size(), - vname, - vquantity, - defaultUnitForQuantity(vquantity), - VifScaling::None, - FieldMatcher::build().set(dif_vif_key).set(mt).set(vi).set(s).set(t).set(i), - help, - print_properties, - NULL, - getValueFunc, - NULL, - setValueFunc, - lookup, /* Lookup table */ - NULL /* Formula */ - )); -} - void MeterCommonImplementation::addStringFieldWithExtractorAndLookup(string vname, string help, PrintProperties print_properties, @@ -2566,6 +2444,35 @@ void MeterCommonImplementation::addOptionalCommonFields(string field_names) { set fields = splitStringIntoSet(field_names, ','); + if (checkIf(fields, "actuality_duration_s")) + { + addNumericFieldWithExtractor( + "actuality_duration", + "Lapsed time between measurement and transmission.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Time, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ActualityDuration), + Unit::Second + ); + } + + if (checkIf(fields, "actuality_duration_h")) + { + addNumericFieldWithExtractor( + "actuality_duration", + "Lapsed time between measurement and transmission.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Time, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ActualityDuration) + ); + } + if (checkIf(fields, "fabrication_no")) { addStringFieldWithExtractor( @@ -2805,6 +2712,20 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields(string field_names) ); } + if (checkIf(fields,"external_temperature_c")) + { + addNumericFieldWithExtractor( + "external_temperature", + "Temperature outside of meter.", + DEFAULT_PRINT_PROPERTIES, + Quantity::Temperature, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ExternalTemperature) + ); + } + if (checkIf(fields,"return_temperature_c")) { addNumericFieldWithExtractor( diff --git a/src/meters.h b/src/meters.h index 7e0410d..3c9ce63 100644 --- a/src/meters.h +++ b/src/meters.h @@ -182,6 +182,7 @@ private: vector detect_; vector default_fields_; int force_mfct_index_ = -1; // Used for meters not declaring mfct specific data using the dif 0f. + bool has_process_content_ = false; // Mark this driver as having mfct specific decoding. public: DriverInfo() {}; @@ -193,6 +194,8 @@ public: void forceMfctIndex(int i) { force_mfct_index_ = i; } void setConstructor(function(MeterInfo&,DriverInfo&)> c) { constructor_ = c; } void addDetection(uint16_t mfct, uchar type, uchar ver) { detect_.push_back({ mfct, type, ver }); } + void usesProcessContent() { has_process_content_ = true; } + vector &detect() { return detect_; } DriverName name() { return name_; } @@ -212,6 +215,7 @@ public: bool isValidMedia(uchar type); bool isCloseEnoughMedia(uchar type); int forceMfctIndex() { return force_mfct_index_; } + bool hasProcessContentCode() { return has_process_content_; } }; bool registerDriver(function setup); diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h index 629e30f..dcce1f1 100644 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -100,28 +100,6 @@ protected: void addLinkMode(LinkMode lm); void setMfctTPLStatusBits(Translate::Lookup &lookup); - // Print with the default unit for this quantity. - void addPrint(string vname, Quantity vquantity, - function getValueFunc, string help, PrintProperties pprops); - // Print with exactly this unit for this quantity. - void addPrint(string vname, Quantity vquantity, Unit unit, - function getValueFunc, string help, PrintProperties pprops); - // Print the dimensionless Text quantity, no unit is needed. - void addPrint(string vname, Quantity vquantity, - function getValueFunc, string help, PrintProperties pprops); - -#define SET_FUNC(varname,to_unit) {[=](Unit from_unit, double d){varname = convert(d, from_unit, to_unit);}} -#define GET_FUNC(varname,from_unit) {[=](Unit to_unit){return convert(varname, from_unit, to_unit);}} -#define LOOKUP_FIELD(DVKEY) NoDifVifKey,VifScaling::Auto,MeasurementType::Instantaneous,VIFRange::Any,AnyStorageNr,AnyTariffNr,IndexNr(1) -#define FIND_FIELD(TYPE,INFO) NoDifVifKey,VifScaling::Auto,TYPE,INFO,StorageNr(0),TariffNr(0),IndexNr(1) -#define FIND_FIELD_S(TYPE,INFO,STORAGE) NoDifVifKey,VifScaling::Auto,TYPE,INFO,STORAGE,TariffNr(0),IndexNr(1) -#define FIND_FIELD_ST(TYPE,INFO,STORAGE,TARIFF) NoDifVifKey,VifScaling::Auto,,TYPE,INFO,STORAGE,TARIFF,IndexNr(1) -#define FIND_FIELD_STI(TYPE,INFO,STORAGE,TARIFF,INDEX) NoDifVifKey,VifScaling::Auto,TYPE,INFO,STORAGE,TARIFF,INDEX - -#define FIND_SFIELD(TYPE,INFO) NoDifVifKey,TYPE,INFO,StorageNr(0),TariffNr(0),IndexNr(1) -#define FIND_SFIELD_S(TYPE,INFO,STORAGE) NoDifVifKey,TYPE,INFO,STORAGE,TariffNr(0),IndexNr(1) -#define FIND_SFIELD_ST(TYPE,INFO,STORAGE,TARIFF) NoDifVifKey,TYPE,INFO,STORAGE,TARIFF,IndexNr(1) -#define FIND_SFIELD_STI(TYPE,INFO,STORAGE,TARIFF,INDEX) NoDifVifKey,TYPE,INFO,STORAGE,TARIFF,INDEX void addNumericFieldWithExtractor( string vname, // Name of value without unit, eg "total" "total_month{storagenr}" @@ -149,14 +127,6 @@ protected: FieldMatcher matcher, // We can generate a calculated field per match. Unit display_unit = Unit::Unknown); // If specified use this unit for the json field instead instead of the default unit. - void addNumericField( - string vname, // Name of value without unit, eg total - Quantity vquantity, // Value belongs to this quantity. - PrintProperties print_properties, // Should this be printed by default in fields,json and hr. - string help, - function setValueFunc, // Use the SET macro above. - function getValueFunc); // Use the GET macro above. - void addNumericField( string vname, // Name of value without unit, eg total Quantity vquantity, // Value belongs to this quantity. @@ -173,21 +143,6 @@ protected: PrintProperties print_properties, FieldMatcher matcher); - void addStringFieldWithExtractorAndLookup( - string vname, // Name of value without unit, eg total - Quantity vquantity, // Value belongs to this quantity. - DifVifKey dif_vif_key, // You can hardocde a dif vif header here or use NoDifVifKey - MeasurementType mt, // If not using a hardcoded key, search for mt,vi,s,t and i instead. - VIFRange vi, - StorageNr s, - TariffNr t, - IndexNr i, - PrintProperties print_properties, // Should this be printed by default in fields,json and hr. - string help, - function setValueFunc, // Use the SET_STRING macro above. - function getValueFunc, // Use the GET_STRING macro above. - Translate::Lookup lookup); // Translate the bits/indexes. - void addStringFieldWithExtractorAndLookup( string vname, string help,