Refactor hydrodigit to new format.

pull/708/head
Fredrik Öhrström 2022-11-26 22:21:34 +01:00
rodzic 8498fdf222
commit df9403696f
10 zmienionych plików z 120 dodań i 150 usunięć

Wyświetl plik

@ -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|

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -236,6 +236,8 @@ private:
int nr_;
};
Unit toDefaultUnit(Vif v);
enum class DVEntryCounterType
{
UNKNOWN,

Wyświetl plik

@ -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)

Wyświetl plik

@ -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) \

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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) \