kopia lustrzana https://github.com/weetmuts/wmbusmeters
Refactor driver elf.
rodzic
811cd92748
commit
e02c985821
|
@ -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|
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<Meter>(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
|
|
@ -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.",
|
||||
|
|
|
@ -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) \
|
||||
|
|
270
src/meter_elf.cc
270
src/meter_elf.cc
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<Meter> createElf(MeterInfo &mi) {
|
||||
return shared_ptr<Meter>(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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include"dvparser.h"
|
||||
#include"meters.h"
|
||||
#include"meters_common_implementation.h"
|
||||
#include"wmbus.h"
|
||||
#include"wmbus_utils.h"
|
||||
#include"util.h"
|
||||
|
||||
#include<assert.h>
|
||||
|
||||
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<Meter> createFlowIQ2200(MeterInfo &mi)
|
||||
{
|
||||
return shared_ptr<Meter>(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 "?";
|
||||
}
|
||||
}
|
|
@ -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) \
|
||||
|
|
Ładowanie…
Reference in New Issue