diff --git a/simulations/simulation_t1.txt b/simulations/simulation_t1.txt index 45887cd..353ba58 100644 --- a/simulations/simulation_t1.txt +++ b/simulations/simulation_t1.txt @@ -264,8 +264,8 @@ telegram=|534424232004256092687A370045752235854DEEEA5939FAD81C25FEEF5A23C38FB916 # Test Apator Elf Heat meter telegram=|51440186010905001837721956880101064004DA000020026CA9220E017799241103000C13641320000A2D00000A5A90060A5E800544050E77000001FD0C010A6564370AFD4731030A274907047F00000002| -{"media":"heat","meter":"elf","name":"Hetta","id":"01885619","meter_date":"2021-02-09","total_energy_consumption_kwh":3112.49977,"current_power_consumption_kw":0,"total_volume_m3":201.364,"total_energy_consumption_at_date_kwh":3047.8,"flow_temperature_c":69,"return_temperature_c":58,"external_temperature_c":37.64,"status":"200000","operating_time_h":44760,"version":"1","battery_v":0,"timestamp":"1111-11-11T11:11:11Z"} -|Hetta;01885619;3112.499770;0.000000;201.364000;69.000000;58.000000;37.640000;200000;1111-11-11 11:11.11 +{"media":"heat","meter":"elf","name":"Hetta","id":"01885619","status":"ERROR_FLAGS_2000000","meter_date":"2021-02-09","total_energy_consumption_kwh":3112.49977,"current_power_consumption_kw":0,"total_volume_m3":201.364,"total_energy_consumption_at_date_kwh":3047.8,"flow_temperature_c":69,"return_temperature_c":58,"external_temperature_c":37.64,"operating_time_h":4.993333,"version":"01","battery_v":3.31,"timestamp":"1111-11-11T11:11:11Z"} +|Hetta;01885619;3112.49977;0;201.364;69;58;37.64;ERROR_FLAGS_2000000;1111-11-11 11:11.11 # Test Diehl IZAR RC I G4 water meter telegram=|1E44A511909192937B077A9F0010052F2F_04130347030002FD1700002F2F2F| diff --git a/src/driver_elf.cc b/src/driver_elf.cc new file mode 100644 index 0000000..866e8f6 --- /dev/null +++ b/src/driver_elf.cc @@ -0,0 +1,196 @@ +/* + Copyright (C) 2021-2022 Fredrik Öhrström (gpl-3.0-or-later) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include"meters_common_implementation.h" + +namespace +{ + struct Driver : public virtual MeterCommonImplementation + { + Driver(MeterInfo &mi, DriverInfo &di); + }; + + static bool ok = registerDriver([](DriverInfo&di) + { + di.setName("elf"); + di.setDefaultFields("name,id,total_energy_consumption_kwh,current_power_consumption_kw,total_volume_m3,flow_temperature_c,return_temperature_c,external_temperature_c,status,timestamp"); + di.setMeterType(MeterType::HeatMeter); + di.addDetection(MANUFACTURER_APA, 0x04, 0x40); + di.addMfctTPLStatusBits( + { + { + { + "TPL_STS", + Translate::Type::BitToString, + 0xe0, // Always use 0xe0 for tpl mfct status bits. + "OK", + { + } + }, + }, + }); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + }); + + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) + { + addStringFieldWithExtractorAndLookup( + "status", + "Meter status from manufacturer status and tpl status field.", + PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | + PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS, + FieldMatcher::build() + .set(DifVifKey("047F")), + { + { + { + "ERROR_FLAGS", + Translate::Type::BitToString, + 0xffffffff, + "OK", + { + } + }, + }, + }); + + + addStringFieldWithExtractor( + "meter_date", + "The meter date.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Date) + ); + + addNumericFieldWithExtractor( + "total_energy_consumption", + "The total energy consumption recorded by this meter.", + PrintProperty::FIELD | PrintProperty::JSON | PrintProperty::IMPORTANT, + Quantity::Energy, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::EnergyWh) + ); + + addNumericFieldWithExtractor( + "current_power_consumption", + "Current power consumption.", + PrintProperty::FIELD | PrintProperty::JSON, + Quantity::Power, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::PowerW) + ); + + addNumericFieldWithExtractor( + "total_volume", + "Total volume of heat media.", + PrintProperty::FIELD | PrintProperty::JSON, + Quantity::Volume, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Volume) + ); + + addNumericFieldWithExtractor( + "total_energy_consumption_at_date", + "The total energy consumption recorded at the target date.", + PrintProperty::FIELD | PrintProperty::JSON, + Quantity::Energy, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::EnergyWh) + .set(StorageNr(1)) + ); + + addNumericFieldWithExtractor( + "flow_temperature", + "The flow temperature.", + PrintProperty::FIELD | PrintProperty::JSON, + Quantity::Temperature, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::FlowTemperature) + ); + + addNumericFieldWithExtractor( + "return_temperature", + "The return temperature.", + PrintProperty::FIELD | PrintProperty::JSON, + Quantity::Temperature, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ReturnTemperature) + ); + + addNumericFieldWithExtractor( + "external_temperature", + "The external temperature.", + PrintProperty::FIELD | PrintProperty::JSON, + Quantity::Temperature, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ExternalTemperature) + ); + + addNumericFieldWithExtractor( + "operating_time", + "How long the meter has been collecting data.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Time, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::OperatingTime) + ); + + addStringFieldWithExtractor( + "version", + "version.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::ModelVersion) + ); + + addNumericFieldWithExtractor( + "battery", + "Battery voltage.", + PrintProperty::JSON | PrintProperty::OPTIONAL, + Quantity::Voltage, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::Voltage) + ); + + } +} + +// Test: Hetta elf 01885619 NOKEY +// telegram=|51440186010905001837721956880101064004DA000020026CA9220E017799241103000C13641320000A2D00000A5A90060A5E800544050E77000001FD0C010A6564370AFD4731030A274907047F00000002| +// {"media":"heat","meter":"elf","name":"Hetta","id":"01885619","status":"ERROR_FLAGS_2000000","meter_date":"2021-02-09","total_energy_consumption_kwh":3112.49977,"current_power_consumption_kw":0,"total_volume_m3":201.364,"total_energy_consumption_at_date_kwh":3047.8,"flow_temperature_c":69,"return_temperature_c":58,"external_temperature_c":37.64,"operating_time_h":4.993333,"version":"01","battery_v":3.31,"timestamp":"1111-11-11T11:11:11Z"} +// |Hetta;01885619;3112.49977;0;201.364;69;58;37.64;ERROR_FLAGS_2000000;1111-11-11 11:11.11 diff --git a/src/driver_lansenth.cc b/src/driver_lansenth.cc index 0573393..11f6063 100644 --- a/src/driver_lansenth.cc +++ b/src/driver_lansenth.cc @@ -55,9 +55,6 @@ namespace PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS); - // {"media":"room sensor","meter":"lansenth","name":"Tempoo","id":"00010203","current_temperature_c":21.8,"current_relative_humidity_rh":43,"average_temperature_1h_c":21.79,"average_relative_humidity_1h_rh":43,"average_temperature_24h_c":21.97,"average_relative_humidity_24h_rh":42.5,"timestamp":"1111-11-11T11:11:11Z"} - // |Tempoo;00010203;21.800000;43.000000;1111-11-11 11:11.11 - addNumericFieldWithExtractor( "current_temperature", "The current temperature.", diff --git a/src/meter_detection.h b/src/meter_detection.h index 4c42f9e..a97a004 100644 --- a/src/meter_detection.h +++ b/src/meter_detection.h @@ -42,7 +42,6 @@ X(EURISII, MANUFACTURER_INE, 0x08, 0x55) \ X(EHZP, MANUFACTURER_EMH, 0x02, 0x02) \ X(ESYSWM, MANUFACTURER_ESY, 0x37, 0x30) \ - X(ELF, MANUFACTURER_APA, 0x04, 0x40) \ X(EM24, MANUFACTURER_KAM, 0x02, 0x33) \ X(EMERLIN868,MANUFACTURER_ELR, 0x37, 0x11) \ X(EV200, MANUFACTURER_ELR, 0x07, 0x0d) \ diff --git a/src/meter_elf.cc b/src/meter_elf.cc deleted file mode 100644 index 34d2199..0000000 --- a/src/meter_elf.cc +++ /dev/null @@ -1,270 +0,0 @@ -/* - Copyright (C) 2021 Fredrik Öhrström (gpl-3.0-or-later) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include"meters.h" -#include"meters_common_implementation.h" -#include"dvparser.h" -#include"wmbus.h" -#include"wmbus_utils.h" -#include"util.h" - -struct MeterElf : public virtual MeterCommonImplementation { - MeterElf(MeterInfo &mi); - - double totalEnergyConsumption(Unit u); - double targetEnergyConsumption(Unit u); - double currentPowerConsumption(Unit u); - string status(); - double totalVolume(Unit u); - double targetVolume(Unit u); - -private: - void processContent(Telegram *t); - - string meter_date_ {}; - uint32_t info_codes_ {}; - double total_energy_kwh_ {}; - double target_energy_kwh_ {}; - double current_power_kw_ {}; - double total_volume_m3_ {}; - double flow_temperature_c_ { 127 }; - double return_temperature_c_ { 127 }; - double external_temperature_c_ { 127 }; - uint16_t operating_time_days_ {}; - string version_; - double battery_v_ {}; -}; - -MeterElf::MeterElf(MeterInfo &mi) : - MeterCommonImplementation(mi, "elf") -{ - setMeterType(MeterType::HeatMeter); - - setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR); - - addLinkMode(LinkMode::C1); - - addPrint("meter_date", Quantity::Text, - [&](){ return meter_date_; }, - "Date when measurement was recorded.", - PrintProperty::JSON); - - addPrint("total_energy_consumption", Quantity::Energy, - [&](Unit u){ return totalEnergyConsumption(u); }, - "The total energy consumption recorded by this meter.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("current_power_consumption", Quantity::Power, - [&](Unit u){ return currentPowerConsumption(u); }, - "Current power consumption.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("total_volume", Quantity::Volume, - [&](Unit u){ return totalVolume(u); }, - "Total volume of heat media.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("total_energy_consumption_at_date", Quantity::Energy, - [&](Unit u){ return targetEnergyConsumption(u); }, - "The total energy consumption recorded at the target date.", - PrintProperty::JSON); - - addPrint("flow_temperature", Quantity::Temperature, - [&](Unit u){ assertQuantity(u, Quantity::Temperature); return convert(flow_temperature_c_, Unit::C, u); }, - "The water temperature.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("return_temperature", Quantity::Temperature, - [&](Unit u){ assertQuantity(u, Quantity::Temperature); return convert(return_temperature_c_, Unit::C, u); }, - "The return temperature.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("external_temperature", Quantity::Temperature, - [&](Unit u){ assertQuantity(u, Quantity::Temperature); return convert(external_temperature_c_, Unit::C, u); }, - "The external temperature.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("status", Quantity::Text, - [&](){ return status(); }, - "Status of meter.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("operating_time", Quantity::Time, - [&](Unit u){ assertQuantity(u, Quantity::Time); return convert(operating_time_days_, Unit::Day, u); }, - "Operating time.", - PrintProperty::JSON); - - addPrint("version", Quantity::Text, - [&](){ return version_; }, - "Version number.", - PrintProperty::JSON); - - addPrint("battery", Quantity::Voltage, - [&](Unit u){ assertQuantity(u, Quantity::Voltage); return convert(battery_v_, Unit::Volt, u); }, - "Battery voltage. Not yet implemented.", - PrintProperty::JSON); - -} - -shared_ptr createElf(MeterInfo &mi) { - return shared_ptr(new MeterElf(mi)); -} - -double MeterElf::totalEnergyConsumption(Unit u) -{ - assertQuantity(u, Quantity::Energy); - return convert(total_energy_kwh_, Unit::KWH, u); -} - -double MeterElf::targetEnergyConsumption(Unit u) -{ - assertQuantity(u, Quantity::Energy); - return convert(target_energy_kwh_, Unit::KWH, u); -} - -double MeterElf::totalVolume(Unit u) -{ - assertQuantity(u, Quantity::Volume); - return convert(total_volume_m3_, Unit::M3, u); -} - -double MeterElf::currentPowerConsumption(Unit u) -{ - assertQuantity(u, Quantity::Power); - return convert(current_power_kw_, Unit::KW, u); -} - -void MeterElf::processContent(Telegram *t) -{ - /* - (elf) 17: 02 dif (16 Bit Integer/Binary Instantaneous value) - (elf) 18: 6C vif (Date type G) - (elf) 19: * A922 meter date (2021-02-09) - (elf) 1b: 0E dif (12 digit BCD Instantaneous value) - (elf) 1c: 01 vif (Energy 10⁻² Wh) - (elf) 1d: * 779924110300 total energy consumption (3112.499770 kWh) - (elf) 23: 0C dif (8 digit BCD Instantaneous value) - (elf) 24: 13 vif (Volume l) - (elf) 25: * 64132000 total volume (201.364000 m3) - (elf) 29: 0A dif (4 digit BCD Instantaneous value) - (elf) 2a: 2D vif (Power 10² W) - (elf) 2b: * 0000 current power consumption (0.000000 kW) - (elf) 2d: 0A dif (4 digit BCD Instantaneous value) - (elf) 2e: 5A vif (Flow temperature 10⁻¹ °C) - (elf) 2f: * 9006 flow temperature (69.000000 °C) - (elf) 31: 0A dif (4 digit BCD Instantaneous value) - (elf) 32: 5E vif (Return temperature 10⁻¹ °C) - (elf) 33: * 8005 return temperature (58.000000 °C) - (elf) 35: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) - (elf) 36: 05 vif (Energy 10² Wh) - (elf) 37: * 0E770000 target energy consumption (3047.800000 kWh) - (elf) 3b: 01 dif (8 Bit Integer/Binary Instantaneous value) - (elf) 3c: FD vif (Second extension FD of VIF-codes) - (elf) 3d: 0C vife (Model/Version) - (elf) 3e: * 01 version (1) - (elf) 3f: 0A dif (4 digit BCD Instantaneous value) - (elf) 40: 65 vif (External temperature 10⁻² °C) - (elf) 41: * 6437 external temperature (37.640000 °C) - (elf) 43: 0A dif (4 digit BCD Instantaneous value) - (elf) 44: FD vif (Second extension FD of VIF-codes) - (elf) 45: 47 vife (10^-2 Volts) - (elf) 46: 3103 - (elf) 48: 0A dif (4 digit BCD Instantaneous value) - (elf) 49: 27 vif (Operating time days) - (elf) 4a: * 4907 operating time days (1865) - (elf) 4c: 04 dif (32 Bit Integer/Binary Instantaneous value) - (elf) 4d: 7F vif (Manufacturer specific) - (elf) 4e: * 00000002 info codes (200000) - */ - int offset; - string key; - - if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 0, 0, &key, &t->dv_entries)) { - struct tm date; - extractDVdate(&t->dv_entries, key, &offset, &date); - meter_date_ = strdate(&date); - t->addMoreExplanation(offset, " meter date (%s)", meter_date_.c_str()); - } - - if (extractDVuint32(&t->dv_entries, "047F", &offset, &info_codes_)) - { - t->addMoreExplanation(offset, " info codes (%s)", status().c_str()); - } - - if (extractDVuint16(&t->dv_entries, "0A27", &offset, &operating_time_days_)) - { - t->addMoreExplanation(offset, " operating time days (%d)", operating_time_days_); - } - - uchar ver; - if (extractDVuint8(&t->dv_entries, "01FD0C", &offset, &ver)) - { - version_ = tostrprintf("%u", ver); - t->addMoreExplanation(offset, " version (%s)", version_.c_str()); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyWh, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_energy_kwh_); - t->addMoreExplanation(offset, " total energy consumption (%f kWh)", total_energy_kwh_); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_volume_m3_); - t->addMoreExplanation(offset, " total volume (%f m3)", total_volume_m3_); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::EnergyWh, 1, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &target_energy_kwh_); - t->addMoreExplanation(offset, " target energy consumption (%f kWh)", target_energy_kwh_); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::PowerW, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, ¤t_power_kw_); - t->addMoreExplanation(offset, " current power consumption (%f kW)", current_power_kw_); - } - - 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::ReturnTemperature, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &return_temperature_c_); - t->addMoreExplanation(offset, " return temperature (%f °C)", return_temperature_c_); - } - - -} - -string MeterElf::status() -{ - string s; - - if (info_codes_ == 0) return "OK"; - - s = tostrprintf("%x", info_codes_); - if (s.length() > 0) { - s.pop_back(); // Remove final space - return s; - } - return s; -} diff --git a/src/meter_flowiq2200.cc b/src/meter_flowiq2200.cc deleted file mode 100644 index d66ecb5..0000000 --- a/src/meter_flowiq2200.cc +++ /dev/null @@ -1,501 +0,0 @@ -/* - Copyright (C) 2017-2020 Fredrik Öhrström (gpl-3.0-or-later) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include"dvparser.h" -#include"meters.h" -#include"meters_common_implementation.h" -#include"wmbus.h" -#include"wmbus_utils.h" -#include"util.h" - -#include - -using namespace std; - -// Are these bits really correct for this meter? - -#define INFO_CODE_DRY 0x01 -#define INFO_CODE_DRY_SHIFT (4+0) - -#define INFO_CODE_REVERSE 0x02 -#define INFO_CODE_REVERSE_SHIFT (4+3) - -#define INFO_CODE_LEAK 0x04 -#define INFO_CODE_LEAK_SHIFT (4+6) - -#define INFO_CODE_BURST 0x08 -#define INFO_CODE_BURST_SHIFT (4+9) - -struct MeterFlowIQ2200 : public virtual MeterCommonImplementation { - MeterFlowIQ2200(MeterInfo &mi, string mt); - - // Total water counted through the meter - double totalWaterConsumption(Unit u); - bool hasTotalWaterConsumption(); - - // Meter sends target water consumption or max flow, depending on meter configuration - // We can see which was sent inside the wmbus message! - - // Target water consumption: The total consumption at the start of the previous 30 day period. - double targetWaterConsumption(Unit u); - bool hasTargetWaterConsumption(); - - double currentFlow(Unit u); - - // Max flow during last month or last 24 hours depending on meter configuration. - double maxFlow(Unit u); - bool hasMaxFlow(); - double minFlow(Unit u); - - // Water temperature - double flowTemperature(Unit u); - bool hasFlowTemperature(); - double minFlowTemperature(Unit u); - double maxFlowTemperature(Unit u); - // Surrounding temperature - double externalTemperature(Unit u); - bool hasExternalTemperature(); - - // statusHumanReadable: DRY,REVERSED,LEAK,BURST if that status is detected right now, followed by - // (dry 15-21 days) which means that, even it DRY is not active right now, - // DRY has been active for 15-21 days during the last 30 days. - string statusHumanReadable(); - string status(); - string timeDry(); - string timeReversed(); - string timeLeaking(); - string timeBursting(); - -private: - - void processContent(Telegram *t); - string decodeTime(int time); - - uint16_t info_codes_ {}; - double total_water_consumption_m3_ {}; - bool has_total_water_consumption_ {}; - double target_water_consumption_m3_ {}; - bool has_target_water_consumption_ {}; - - double current_flow_m3h_ {}; - double max_flow_m3h_ {}; - double min_flow_m3h_ {}; - - double min_flow_temperature_c_ { 127 }; - double max_flow_temperature_c_ { 127 }; - - double external_temperature_c_ { 127 }; - bool has_external_temperature_ {}; - - string target_datetime_; -}; - -MeterFlowIQ2200::MeterFlowIQ2200(MeterInfo &mi, string mt) : - MeterCommonImplementation(mi, mt) -{ - setMeterType(MeterType::WaterMeter); - - setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR); - - addLinkMode(LinkMode::C1); - - addPrint("total", Quantity::Volume, - [&](Unit u){ return totalWaterConsumption(u); }, - "The total water consumption recorded by this meter.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("target", Quantity::Volume, - [&](Unit u){ return targetWaterConsumption(u); }, - "The total water consumption recorded at the beginning of this month.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("target_datetime", Quantity::Text, - [&](){ return target_datetime_; }, - "Timestamp for water consumption recorded at the beginning of this month.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("current_flow", Quantity::Flow, - [&](Unit u){ return currentFlow(u); }, - "The current flow of water.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("max_flow", Quantity::Flow, - [&](Unit u){ return maxFlow(u); }, - "The maxium flow recorded during previous period.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("min_flow", Quantity::Flow, - [&](Unit u){ return currentFlow(u); }, - "The minimum flow recorded during previous period.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("min_flow_temperature", Quantity::Temperature, - [&](Unit u){ return minFlowTemperature(u); }, - "The minimum water temperature during previous period.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("max_flow_temperature", Quantity::Temperature, - [&](Unit u){ return maxFlowTemperature(u); }, - "The maximum water temperature during previous period.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("external_temperature", Quantity::Temperature, - [&](Unit u){ return externalTemperature(u); }, - "The external temperature outside of the meter.", - PrintProperty::FIELD | PrintProperty::JSON); - - addPrint("", Quantity::Text, - [&](){ return statusHumanReadable(); }, - "Status of meter.", - PrintProperty::FIELD); - - addPrint("current_status", Quantity::Text, - [&](){ return status(); }, - "Status of meter.", - PrintProperty::JSON); - - addPrint("time_dry", Quantity::Text, - [&](){ return timeDry(); }, - "Amount of time the meter has been dry.", - PrintProperty::JSON); - - addPrint("time_reversed", Quantity::Text, - [&](){ return timeReversed(); }, - "Amount of time the meter has been reversed.", - PrintProperty::JSON); - - addPrint("time_leaking", Quantity::Text, - [&](){ return timeLeaking(); }, - "Amount of time the meter has been leaking.", - PrintProperty::JSON); - - addPrint("time_bursting", Quantity::Text, - [&](){ return timeBursting(); }, - "Amount of time the meter has been bursting.", - PrintProperty::JSON); -} - -double MeterFlowIQ2200::totalWaterConsumption(Unit u) -{ - assertQuantity(u, Quantity::Volume); - return convert(total_water_consumption_m3_, Unit::M3, u); -} - -bool MeterFlowIQ2200::hasTotalWaterConsumption() -{ - return has_total_water_consumption_; -} - -double MeterFlowIQ2200::targetWaterConsumption(Unit u) -{ - assertQuantity(u, Quantity::Volume); - return convert(target_water_consumption_m3_, Unit::M3, u); -} - -bool MeterFlowIQ2200::hasTargetWaterConsumption() -{ - return has_target_water_consumption_; -} - -double MeterFlowIQ2200::currentFlow(Unit u) -{ - assertQuantity(u, Quantity::Flow); - return convert(current_flow_m3h_, Unit::M3H, u); -} - -double MeterFlowIQ2200::maxFlow(Unit u) -{ - assertQuantity(u, Quantity::Flow); - return convert(max_flow_m3h_, Unit::M3H, u); -} - -double MeterFlowIQ2200::minFlow(Unit u) -{ - assertQuantity(u, Quantity::Flow); - return convert(min_flow_m3h_, Unit::M3H, u); -} - -bool MeterFlowIQ2200::hasMaxFlow() -{ - return true; -} - -double MeterFlowIQ2200::flowTemperature(Unit u) -{ - assertQuantity(u, Quantity::Temperature); - return convert(127, Unit::C, u); -} - -double MeterFlowIQ2200::minFlowTemperature(Unit u) -{ - assertQuantity(u, Quantity::Temperature); - return convert(min_flow_temperature_c_, Unit::C, u); -} - -double MeterFlowIQ2200::maxFlowTemperature(Unit u) -{ - assertQuantity(u, Quantity::Temperature); - return convert(max_flow_temperature_c_, Unit::C, u); -} - -bool MeterFlowIQ2200::hasFlowTemperature() -{ - // Actually no, it has only min and max. - return false; -} - -double MeterFlowIQ2200::externalTemperature(Unit u) -{ - assertQuantity(u, Quantity::Temperature); - return convert(external_temperature_c_, Unit::C, u); -} - -bool MeterFlowIQ2200::hasExternalTemperature() -{ - return has_external_temperature_; -} - -shared_ptr createFlowIQ2200(MeterInfo &mi) -{ - return shared_ptr(new MeterFlowIQ2200(mi, "flowiq2200")); -} - -void MeterFlowIQ2200::processContent(Telegram *t) -{ - /* -(flowiq2200) 14: 04 dif (32 Bit Integer/Binary Instantaneous value) -(flowiq2200) 15: FF vif (Vendor extension) -(flowiq2200) 16: 23 vife (per day) -(flowiq2200) 17: * 00000000 info codes (OK) -(flowiq2200) 1b: 04 dif (32 Bit Integer/Binary Instantaneous value) -(flowiq2200) 1c: 13 vif (Volume l) -(flowiq2200) 1d: * AEAC0000 total consumption (44.206000 m3) -(flowiq2200) 21: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1) -(flowiq2200) 22: 13 vif (Volume l) -(flowiq2200) 23: * 64A80000 target consumption (43.108000 m3) -(flowiq2200) 27: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1) -(flowiq2200) 28: 6C vif (Date type G) -(flowiq2200) 29: * 812A target_datetime (2020-10-01 00:00) -(flowiq2200) 2b: 02 dif (16 Bit Integer/Binary Instantaneous value) -(flowiq2200) 2c: 3B vif (Volume flow l/h) -(flowiq2200) 2d: * 0000 current flow (0.000000 m3/h) -(flowiq2200) 2f: 92 dif (16 Bit Integer/Binary Maximum value) -(flowiq2200) 30: 01 dife (subunit=0 tariff=0 storagenr=2) -(flowiq2200) 31: 3B vif (Volume flow l/h) -(flowiq2200) 32: * EF01 max flow (0.495000 m3/h) -(flowiq2200) 34: A2 dif (16 Bit Integer/Binary Minimum value) -(flowiq2200) 35: 01 dife (subunit=0 tariff=0 storagenr=2) -(flowiq2200) 36: 3B vif (Volume flow l/h) -(flowiq2200) 37: * 0000 min flow (0.000000 m3/h) -(flowiq2200) 39: 06 dif (48 Bit Integer/Binary Instantaneous value) -(flowiq2200) 3a: FF vif (Vendor extension) -(flowiq2200) 3b: 1B vife (?) -(flowiq2200) 3c: 067000097000 -(flowiq2200) 42: A1 dif (8 Bit Integer/Binary Minimum value) -(flowiq2200) 43: 01 dife (subunit=0 tariff=0 storagenr=2) -(flowiq2200) 44: 5B vif (Flow temperature °C) -(flowiq2200) 45: * 0C min flow temperature (12.000000 °C) -(flowiq2200) 46: 91 dif (8 Bit Integer/Binary Maximum value) -(flowiq2200) 47: 01 dife (subunit=0 tariff=0 storagenr=2) -(flowiq2200) 48: 5B vif (Flow temperature °C) -(flowiq2200) 49: * 14 max flow temperature (20.000000 °C) -(flowiq2200) 4a: A1 dif (8 Bit Integer/Binary Minimum value) -(flowiq2200) 4b: 01 dife (subunit=0 tariff=0 storagenr=2) -(flowiq2200) 4c: 67 vif (External temperature °C) -(flowiq2200) 4d: * 13 external temperature (19.000000 °C) - */ - string meter_name = toString(driver()).c_str(); - - int offset; - string key; - - extractDVuint16(&t->dv_entries, "04FF23", &offset, &info_codes_); - t->addMoreExplanation(offset, " info codes (%s)", statusHumanReadable().c_str()); - - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_m3_); - has_total_water_consumption_ = true; - t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &target_water_consumption_m3_); - has_target_water_consumption_ = true; - t->addMoreExplanation(offset, " target consumption (%f m3)", target_water_consumption_m3_); - } - - if (findKey(MeasurementType::Instantaneous, VIFRange::Date, 1, 0, &key, &t->dv_entries)) { - struct tm datetime; - extractDVdate(&t->dv_entries, key, &offset, &datetime); - target_datetime_ = strdatetime(&datetime); - t->addMoreExplanation(offset, " target_datetime (%s)", target_datetime_.c_str()); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, ¤t_flow_m3h_); - t->addMoreExplanation(offset, " current flow (%f m3/h)", current_flow_m3h_); - } - - if(findKey(MeasurementType::Maximum, VIFRange::VolumeFlow, 2, 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::Minimum, VIFRange::VolumeFlow, 2, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &min_flow_m3h_); - t->addMoreExplanation(offset, " min flow (%f m3/h)", min_flow_m3h_); - } - - if(findKey(MeasurementType::Minimum, VIFRange::FlowTemperature, 2, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &min_flow_temperature_c_); - t->addMoreExplanation(offset, " min flow temperature (%f °C)", min_flow_temperature_c_); - } - - if(findKey(MeasurementType::Maximum, VIFRange::FlowTemperature, 2, 0, &key, &t->dv_entries)) { - extractDVdouble(&t->dv_entries, key, &offset, &max_flow_temperature_c_); - t->addMoreExplanation(offset, " max flow temperature (%f °C)", max_flow_temperature_c_); - } - - if(findKey(MeasurementType::Instantaneous, VIFRange::ExternalTemperature, AnyStorageNr, 0, &key, &t->dv_entries)) { - has_external_temperature_ = extractDVdouble(&t->dv_entries, key, &offset, &external_temperature_c_); - t->addMoreExplanation(offset, " external temperature (%f °C)", external_temperature_c_); - } - -} - -string MeterFlowIQ2200::status() -{ - string s; - if (info_codes_ & INFO_CODE_DRY) s.append("DRY "); - if (info_codes_ & INFO_CODE_REVERSE) s.append("REVERSED "); - if (info_codes_ & INFO_CODE_LEAK) s.append("LEAK "); - if (info_codes_ & INFO_CODE_BURST) s.append("BURST "); - if (s.length() > 0) { - s.pop_back(); // Remove final space - return s; - } - return s; -} - -string MeterFlowIQ2200::timeDry() -{ - int time_dry = (info_codes_ >> INFO_CODE_DRY_SHIFT) & 7; - if (time_dry) { - return decodeTime(time_dry); - } - return ""; -} - -string MeterFlowIQ2200::timeReversed() -{ - int time_reversed = (info_codes_ >> INFO_CODE_REVERSE_SHIFT) & 7; - if (time_reversed) { - return decodeTime(time_reversed); - } - return ""; -} - -string MeterFlowIQ2200::timeLeaking() -{ - int time_leaking = (info_codes_ >> INFO_CODE_LEAK_SHIFT) & 7; - if (time_leaking) { - return decodeTime(time_leaking); - } - return ""; -} - -string MeterFlowIQ2200::timeBursting() -{ - int time_bursting = (info_codes_ >> INFO_CODE_BURST_SHIFT) & 7; - if (time_bursting) { - return decodeTime(time_bursting); - } - return ""; -} - -string MeterFlowIQ2200::statusHumanReadable() -{ - string s; - bool dry = info_codes_ & INFO_CODE_DRY; - int time_dry = (info_codes_ >> INFO_CODE_DRY_SHIFT) & 7; - if (dry || time_dry) { - if (dry) s.append("DRY"); - s.append("(dry "); - s.append(decodeTime(time_dry)); - s.append(") "); - } - - bool reversed = info_codes_ & INFO_CODE_REVERSE; - int time_reversed = (info_codes_ >> INFO_CODE_REVERSE_SHIFT) & 7; - if (reversed || time_reversed) { - if (dry) s.append("REVERSED"); - s.append("(rev "); - s.append(decodeTime(time_reversed)); - s.append(") "); - } - - bool leak = info_codes_ & INFO_CODE_LEAK; - int time_leak = (info_codes_ >> INFO_CODE_LEAK_SHIFT) & 7; - if (leak || time_leak) { - if (dry) s.append("LEAK"); - s.append("(leak "); - s.append(decodeTime(time_leak)); - s.append(") "); - } - - bool burst = info_codes_ & INFO_CODE_BURST; - int time_burst = (info_codes_ >> INFO_CODE_BURST_SHIFT) & 7; - if (burst || time_burst) { - if (dry) s.append("BURST"); - s.append("(burst "); - s.append(decodeTime(time_burst)); - s.append(") "); - } - if (s.length() > 0) { - s.pop_back(); - return s; - } - return "OK"; -} - -string MeterFlowIQ2200::decodeTime(int time) -{ - if (time>7) { - string meter_name = toString(driver()).c_str(); - warning("(%s) warning: Cannot decode time %d should be 0-7.\n", meter_name.c_str(), time); - } - switch (time) { - case 0: - return "0 hours"; - case 1: - return "1-8 hours"; - case 2: - return "9-24 hours"; - case 3: - return "2-3 days"; - case 4: - return "4-7 days"; - case 5: - return "8-14 days"; - case 6: - return "15-21 days"; - case 7: - return "22-31 days"; - default: - return "?"; - } -} diff --git a/src/meters.h b/src/meters.h index b0233a2..f9e89a5 100644 --- a/src/meters.h +++ b/src/meters.h @@ -69,7 +69,6 @@ LIST_OF_METER_TYPES X(eurisii, T1_bit, HeatCostAllocationMeter, EURISII, EurisII) \ X(ehzp, T1_bit, ElectricityMeter, EHZP, EHZP) \ X(esyswm, T1_bit, ElectricityMeter, ESYSWM, ESYSWM) \ - X(elf, T1_bit, HeatMeter, ELF, Elf) \ X(em24, C1_bit, ElectricityMeter, EM24, EM24) \ X(emerlin868, T1_bit, WaterMeter, EMERLIN868, EMerlin868) \ X(ev200, T1_bit, WaterMeter, EV200, EV200) \