kopia lustrzana https://github.com/weetmuts/wmbusmeters
Refactor hydrodigit to new format.
rodzic
8498fdf222
commit
df9403696f
|
@ -138,12 +138,12 @@ telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0
|
|||
# Test BMeters HydroDigit water telegram
|
||||
telegram=|4E44B4098686868613077AF0004005_2F2F0C1366380000046D27287E2A0F150E00000000C10000D10000E60000FD00000C01002F0100410100540100680100890000A00000B30000002F2F2F2F2F2F|
|
||||
{"media":"water","meter":"hydrodigit","name":"HydrodigitWater","id":"86868686","total_m3":3.866,"meter_datetime":"2019-10-30 08:39","timestamp":"1111-11-11T11:11:11Z"}
|
||||
|HydrodigitWater;86868686;3.866000;2019-10-30 08:39;1111-11-11 11:11.11
|
||||
|HydrodigitWater;86868686;3.866;2019-10-30 08:39;1111-11-11 11:11.11
|
||||
|
||||
# Test another pair of BMeters HydroDigit water telegram
|
||||
telegram=|3044B4090123456713067A190020052F2F_0C1315000000046D0136A7270F050B000000002F2F2F2F2F2F2F2F2F2F2F69E5|
|
||||
{"media":"warm water","meter":"hydrodigit","name":"HydrodigitWater2","id":"67452301","total_m3":0.015,"meter_datetime":"2021-07-07 22:01","timestamp":"1111-11-11T11:11:11Z"}
|
||||
|HydrodigitWater2;67452301;0.015000;2021-07-07 22:01;1111-11-11 11:11.11
|
||||
|HydrodigitWater2;67452301;0.015;2021-07-07 22:01;1111-11-11 11:11.11
|
||||
|
||||
# Test Q400 water telegram
|
||||
telegram=|2E4409077272727210077AD71020052F2F_046D040D742C041377000000446D0000612C4413000000002F2F2F2F2F2F|
|
||||
|
@ -295,7 +295,7 @@ telegram=|6044B8059430040001037A1D005085E2B670BCF1A5C87E0C1A51DA18924EF984613DA2
|
|||
# Test Hydrocal M3 heat/cooling meter
|
||||
telegram=|8E44B409747372710B0D7A798080052F2F_0C0E59600100046D1D36B9290C13679947000C0E000000000C13590000000C13000000000C13000000000A5A18020A5E11020F823D06003D06003D06003D0600140600620500480400E402001601000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002F2F|
|
||||
{"media":"heat/cooling load","meter":"hydrocalm3","name":"HeatCool","id":"71727374","status": "TPL_MFCT_80","total_heating_kwh":4460.833333,"total_cooling_kwh":0,"device_datetime":"2021-09-25 22:29","total_heating_m3":479.967,"total_cooling_m3":0.059,"c1_volume_m3":0,"c2_volume_m3":0,"supply_temperature_c":21.8,"return_temperature_c":21.1,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|HeatCool;71727374;4460.833333;0;1111-11-11 11:11.11
|
||||
|HeatCool;71727374;4460.833333;2021-09-25 22:29;1111-11-11 11:11.11
|
||||
|
||||
# Test Weptech Munia temperature hygrometer
|
||||
telegram=|2E44B05C82340100021B7A460000002F2F0A6601020AFB1A570602FD971D00002F2F2F2F2F2F2F2F2F2F2F2F2F2F2F|
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace
|
|||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::EnergyMJ)
|
||||
.set(VIFRange::AnyEnergyVIF)
|
||||
.set(IndexNr(1))
|
||||
);
|
||||
|
||||
|
@ -154,4 +154,4 @@ namespace
|
|||
// Test:HeatCool hydrocalm3 71727374 NOKEY
|
||||
// telegram=|8E44B409747372710B0D7A798080052F2F_0C0E59600100046D1D36B9290C13679947000C0E000000000C13590000000C13000000000C13000000000A5A18020A5E11020F823D06003D06003D06003D0600140600620500480400E402001601000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002F2F|
|
||||
// {"media":"heat/cooling load","meter":"hydrocalm3","name":"HeatCool","id":"71727374","status": "TPL_MFCT_80","total_heating_kwh":4460.833333,"total_cooling_kwh":0,"device_datetime":"2021-09-25 22:29","total_heating_m3":479.967,"total_cooling_m3":0.059,"c1_volume_m3":0,"c2_volume_m3":0,"supply_temperature_c":21.8,"return_temperature_c":21.1,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |HeatCool;71727374;4460.833333;0;1111-11-11 11:11.11
|
||||
// |HeatCool;71727374;4460.833333;2021-09-25 22:29;1111-11-11 11:11.11
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Copyright (C) 2019-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("hydrodigit");
|
||||
di.setDefaultFields("name,id,total_m3,meter_datetime,timestamp");
|
||||
di.setMeterType(MeterType::WaterMeter);
|
||||
di.addLinkMode(LinkMode::T1);
|
||||
di.addDetection(MANUFACTURER_BMT, 0x06, 0x13);
|
||||
di.addDetection(MANUFACTURER_BMT, 0x07, 0x13);
|
||||
di.addDetection(MANUFACTURER_BMT, 0x07, 0x15);
|
||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||
});
|
||||
|
||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||
{
|
||||
addNumericFieldWithExtractor(
|
||||
"total",
|
||||
"The total water consumption recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::IMPORTANT,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"meter",
|
||||
"Meter timestamp for measurement.",
|
||||
PrintProperty::JSON,
|
||||
Quantity::PointInTime,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::DateTime),
|
||||
Unit::DateTimeLT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Test: HydrodigitWater hydrodigit 86868686 NOKEY
|
||||
// telegram=|4E44B4098686868613077AF0004005_2F2F0C1366380000046D27287E2A0F150E00000000C10000D10000E60000FD00000C01002F0100410100540100680100890000A00000B30000002F2F2F2F2F2F|
|
||||
// {"media":"water","meter":"hydrodigit","name":"HydrodigitWater","id":"86868686","total_m3":3.866,"meter_datetime":"2019-10-30 08:39","timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |HydrodigitWater;86868686;3.866;2019-10-30 08:39;1111-11-11 11:11.11
|
|
@ -69,6 +69,14 @@ LIST_OF_VIF_COMBINABLES
|
|||
return VIFCombinable::None;
|
||||
}
|
||||
|
||||
Unit toDefaultUnit(Vif v)
|
||||
{
|
||||
#define X(name,from,to,quantity,unit) { if (from <= v.intValue() && v.intValue() <= to) return unit; }
|
||||
LIST_OF_VIF_RANGES
|
||||
#undef X
|
||||
return Unit::Unknown;
|
||||
}
|
||||
|
||||
Unit toDefaultUnit(VIFRange v)
|
||||
{
|
||||
switch (v) {
|
||||
|
@ -1154,16 +1162,8 @@ bool extractDVdate(map<string,pair<int,DVEntry>> *dv_entries,
|
|||
|
||||
bool DVEntry::extractDate(struct tm *out)
|
||||
{
|
||||
// This will install the correct timezone
|
||||
// offset tm_gmtoff into the timestamp.
|
||||
time_t t = time(NULL);
|
||||
localtime_r(&t, out);
|
||||
out->tm_hour = 0;
|
||||
out->tm_min = 0;
|
||||
out->tm_sec = 0;
|
||||
out->tm_mday = 0;
|
||||
out->tm_mon = 0;
|
||||
out->tm_year = 0;
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->tm_isdst = -1; // Figure out the dst automatically!
|
||||
|
||||
vector<uchar> v;
|
||||
hex2bin(value, &v);
|
||||
|
|
|
@ -236,6 +236,8 @@ private:
|
|||
int nr_;
|
||||
};
|
||||
|
||||
Unit toDefaultUnit(Vif v);
|
||||
|
||||
enum class DVEntryCounterType
|
||||
{
|
||||
UNKNOWN,
|
||||
|
|
|
@ -81,6 +81,8 @@ shared_ptr<Printer> printer_;
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tzset(); // Load the current timezone.
|
||||
|
||||
auto config = parseCommandLine(argc, argv);
|
||||
|
||||
if (config->version)
|
||||
|
|
|
@ -28,9 +28,6 @@
|
|||
//
|
||||
#define METER_DETECTION \
|
||||
X(CCx01, MANUFACTURER_GSS, 0x02, 0x01) \
|
||||
X(HYDRODIGIT,MANUFACTURER_BMT, 0x06, 0x13) \
|
||||
X(HYDRODIGIT,MANUFACTURER_BMT, 0x07, 0x13) \
|
||||
X(HYDRODIGIT,MANUFACTURER_BMT, 0x07, 0x15) \
|
||||
X(LSE_08, MANUFACTURER_LSE, 0x08, 0x01) \
|
||||
X(MULTICAL302,MANUFACTURER_KAM, 0x04, 0x30) \
|
||||
X(MULTICAL302,MANUFACTURER_KAM, 0x0d, 0x30) \
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2019-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"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct MeterHydrodigit : public virtual MeterCommonImplementation {
|
||||
MeterHydrodigit(MeterInfo &mi);
|
||||
|
||||
// Total water counted through the meter
|
||||
double totalWaterConsumption(Unit u);
|
||||
bool hasTotalWaterConsumption();
|
||||
|
||||
private:
|
||||
void processContent(Telegram *t);
|
||||
|
||||
double total_water_consumption_m3_ {};
|
||||
string meter_datetime_;
|
||||
};
|
||||
|
||||
shared_ptr<Meter> createHydrodigit(MeterInfo &mi)
|
||||
{
|
||||
return shared_ptr<Meter>(new MeterHydrodigit(mi));
|
||||
}
|
||||
|
||||
MeterHydrodigit::MeterHydrodigit(MeterInfo &mi) :
|
||||
MeterCommonImplementation(mi, "hydrodigit")
|
||||
{
|
||||
setMeterType(MeterType::WaterMeter);
|
||||
|
||||
setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
|
||||
|
||||
addLinkMode(LinkMode::T1);
|
||||
|
||||
addPrint("total", Quantity::Volume,
|
||||
[&](Unit u){ return totalWaterConsumption(u); },
|
||||
"The total water consumption recorded by this meter.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
|
||||
addPrint("meter_datetime", Quantity::Text,
|
||||
[&](){ return meter_datetime_; },
|
||||
"Meter timestamp for measurement.",
|
||||
PrintProperty::FIELD | PrintProperty::JSON);
|
||||
}
|
||||
|
||||
void MeterHydrodigit::processContent(Telegram *t)
|
||||
{
|
||||
int offset;
|
||||
string key;
|
||||
|
||||
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::DateTime, 0, 0, &key, &t->dv_entries)) {
|
||||
struct tm datetime;
|
||||
extractDVdate(&t->dv_entries, key, &offset, &datetime);
|
||||
meter_datetime_ = strdatetime(&datetime);
|
||||
t->addMoreExplanation(offset, " meter_datetime (%s)", meter_datetime_.c_str());
|
||||
}
|
||||
|
||||
vector<uchar> data;
|
||||
t->extractMfctData(&data);
|
||||
}
|
||||
|
||||
double MeterHydrodigit::totalWaterConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Volume);
|
||||
return convert(total_water_consumption_m3_, Unit::M3, u);
|
||||
}
|
||||
|
||||
bool MeterHydrodigit::hasTotalWaterConsumption()
|
||||
{
|
||||
return true;
|
||||
}
|
|
@ -1193,6 +1193,7 @@ bool checkCommonField(string *buf, string field, Meter *m, Telegram *t, char c,
|
|||
bool checkPrintableField(string *buf, string field, Meter *m, Telegram *t, char c,
|
||||
vector<FieldInfo> &fields, bool human_readable)
|
||||
{
|
||||
|
||||
for (FieldInfo &fi : fields)
|
||||
{
|
||||
if (fi.xuantity() == Quantity::Text)
|
||||
|
@ -1206,40 +1207,34 @@ bool checkPrintableField(string *buf, string field, Meter *m, Telegram *t, char
|
|||
}
|
||||
else
|
||||
{
|
||||
// Doubles have to be converted into the proper unit.
|
||||
string default_unit = unitToStringLowerCase(fi.defaultUnit());
|
||||
string var = fi.vname()+"_"+default_unit;
|
||||
if (field == var)
|
||||
if (fi.defaultUnit() == Unit::DateLT)
|
||||
{
|
||||
// Default unit.
|
||||
*buf += valueToString(m->getNumericValue(&fi, fi.defaultUnit()), fi.defaultUnit());
|
||||
if (human_readable)
|
||||
{
|
||||
*buf += " ";
|
||||
*buf += unitToStringHR(fi.defaultUnit());
|
||||
}
|
||||
*buf += strdate(m->getNumericValue(&fi, Unit::DateLT));
|
||||
*buf += c;
|
||||
return true;
|
||||
}
|
||||
else if (fi.defaultUnit() == Unit::DateTimeLT)
|
||||
{
|
||||
*buf += strdatetime(m->getNumericValue(&fi, Unit::DateTimeLT));
|
||||
*buf += c;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Added conversion unit.
|
||||
Unit u = fi.defaultUnit();
|
||||
if (u != fi.defaultUnit())
|
||||
// Doubles have to be converted into the proper unit.
|
||||
string default_unit = unitToStringLowerCase(fi.defaultUnit());
|
||||
string var = fi.vname()+"_"+default_unit;
|
||||
if (field == var)
|
||||
{
|
||||
string unit = unitToStringLowerCase(u);
|
||||
string var = fi.vname()+"_"+unit;
|
||||
if (field == var)
|
||||
// Default unit.
|
||||
*buf += valueToString(m->getNumericValue(&fi, fi.defaultUnit()), fi.defaultUnit());
|
||||
if (human_readable)
|
||||
{
|
||||
*buf += valueToString(m->getNumericValue(&fi, u), u);
|
||||
if (human_readable)
|
||||
{
|
||||
*buf += " ";
|
||||
*buf += unitToStringHR(u);
|
||||
}
|
||||
*buf += c;
|
||||
return true;
|
||||
*buf += " ";
|
||||
*buf += unitToStringHR(fi.defaultUnit());
|
||||
}
|
||||
*buf += c;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2429,26 +2424,28 @@ bool FieldInfo::extractNumeric(Meter *m, Telegram *t, DVEntry *dve)
|
|||
{
|
||||
struct tm datetime;
|
||||
dve->extractDate(&datetime);
|
||||
// TODO Figure out why I have to use a temporary time_t offset here instead
|
||||
// of just subtracting datetime.tm_gmtoff, which gives the wrong result.
|
||||
time_t offset = datetime.tm_gmtoff;
|
||||
time_t tmp = mktime(&datetime)-offset;
|
||||
time_t tmp = mktime(&datetime);
|
||||
string bbb = strdatetime(tmp);
|
||||
extracted_double_value = tmp;
|
||||
}
|
||||
else if (matcher_.vif_range == VIFRange::Date)
|
||||
{
|
||||
struct tm date;
|
||||
dve->extractDate(&date);
|
||||
//time_t offset = date.tm_gmtoff;
|
||||
time_t tmp = mktime(&date);
|
||||
extracted_double_value = tmp;
|
||||
}
|
||||
else if (matcher_.vif_range != VIFRange::Any &&
|
||||
matcher_.vif_range != VIFRange::AnyVolumeVIF &&
|
||||
matcher_.vif_range != VIFRange::AnyEnergyVIF &&
|
||||
matcher_.vif_range != VIFRange::AnyPowerVIF &&
|
||||
matcher_.vif_range != VIFRange::None)
|
||||
else if (matcher_.vif_range == VIFRange::AnyEnergyVIF ||
|
||||
matcher_.vif_range == VIFRange::AnyVolumeVIF ||
|
||||
matcher_.vif_range == VIFRange::AnyPowerVIF)
|
||||
{
|
||||
// Find the actual unit used in the telegram.
|
||||
decoded_unit = toDefaultUnit(dve->vif);
|
||||
}
|
||||
else if (matcher_.vif_range != VIFRange::Any &&
|
||||
matcher_.vif_range != VIFRange::None)
|
||||
{
|
||||
// Pick the default unit for this range.
|
||||
decoded_unit = toDefaultUnit(matcher_.vif_range);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ LIST_OF_METER_TYPES
|
|||
X(auto, 0, AutoMeter, AUTO, Auto) \
|
||||
X(unknown, 0, UnknownMeter, UNKNOWN, Unknown) \
|
||||
X(gransystems,T1_bit, ElectricityMeter, CCx01, CCx01) \
|
||||
X(hydrodigit, T1_bit, WaterMeter, HYDRODIGIT, Hydrodigit) \
|
||||
X(multical302,C1_bit|T1_bit, HeatMeter, MULTICAL302, Multical302) \
|
||||
X(multical403,C1_bit, HeatMeter, MULTICAL403, Multical403) \
|
||||
X(multical602,C1_bit, HeatMeter, MULTICAL602, Multical602) \
|
||||
|
|
Ładowanie…
Reference in New Issue