kopia lustrzana https://github.com/weetmuts/wmbusmeters
Improve minomess driver. Add default fields setting to drivers.
rodzic
f7c1556f02
commit
eafa1d9fc1
4
CHANGES
4
CHANGES
|
@ -1,4 +1,6 @@
|
|||
|
||||
Improve minomess driver to handle telegrams from wired m-bus module.
|
||||
|
||||
Added Enercal F2 heat meter.
|
||||
|
||||
Paulo Rossi added support for the AMB3665-M wmbus dongle for N-mode 169 MHz telegrams. Thanks Paulo!
|
||||
|
@ -11,7 +13,7 @@ Eventually the old fields will go away in drivers.
|
|||
|
||||
ATTENTION! When a field is not optional in the driver description,
|
||||
but alas, no data has arrived in the telegram, then the json now
|
||||
contains a null for the value! Previousy 0 was used, which is misleading.
|
||||
contains a null for the value! Previousy 0 was used, which was misleading.
|
||||
|
||||
ATTENTION! The default location of the meter_readings directory has changed
|
||||
from /var/log/wmbusmeters/meter_readings to /var/lib/wmbusmeters/meter_readings
|
||||
|
|
|
@ -59,9 +59,12 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
|||
vector<string> telegram_shells;
|
||||
vector<string> alarm_shells;
|
||||
vector<string> extra_constant_fields;
|
||||
vector<string> selected_fields;
|
||||
|
||||
debug("(config) loading meter file %s\n", file.c_str());
|
||||
for (;;) {
|
||||
|
||||
for (;;)
|
||||
{
|
||||
pair<string,string> p = getNextKeyValue(buf, i);
|
||||
|
||||
if (p.first == "") break;
|
||||
|
@ -131,6 +134,16 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
|||
alarm_shells.push_back(p.second);
|
||||
}
|
||||
else
|
||||
if (p.first == "selectedfields")
|
||||
{
|
||||
if (selected_fields.size() > 0)
|
||||
{
|
||||
warning("(warning) selectfields already used! Ignoring selectfields %s", p.second.c_str());
|
||||
return;
|
||||
}
|
||||
selected_fields = splitString(p.second, ',');
|
||||
}
|
||||
else
|
||||
if (startsWith(p.first, "json_") ||
|
||||
startsWith(p.first, "field_"))
|
||||
{
|
||||
|
@ -183,7 +196,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
|||
mi.extra_constant_fields = extra_constant_fields;
|
||||
mi.shells = telegram_shells;
|
||||
mi.idsc = toIdsCommaSeparated(mi.ids);
|
||||
|
||||
mi.selected_fields = selected_fields;
|
||||
c->meters.push_back(mi);
|
||||
}
|
||||
|
||||
|
@ -629,15 +642,7 @@ void handleSelectedFields(Configuration *c, string s)
|
|||
warning("(warning) selectfields already used! Ignoring selectfields %s", s.c_str());
|
||||
return;
|
||||
}
|
||||
char buf[s.length()+1];
|
||||
strcpy(buf, s.c_str());
|
||||
char *saveptr {};
|
||||
const char *tok = strtok_r(buf, ",", &saveptr);
|
||||
while (tok != NULL)
|
||||
{
|
||||
c->selected_fields.push_back(tok);
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
}
|
||||
c->selected_fields = splitString(s, ',');
|
||||
}
|
||||
|
||||
void handleShell(Configuration *c, string cmdline)
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace
|
|||
static bool ok = registerDriver([](DriverInfo&di)
|
||||
{
|
||||
di.setName("minomess");
|
||||
di.setDefaultFields("name,id,total_m3,target_m3,status,timestamp");
|
||||
di.setMeterType(MeterType::WaterMeter);
|
||||
di.addLinkMode(LinkMode::C1);
|
||||
di.addDetection(MANUFACTURER_ZRI, 0x07, 0x00);
|
||||
|
@ -36,16 +37,9 @@ namespace
|
|||
|
||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||
{
|
||||
addNumericFieldWithExtractor(
|
||||
"total",
|
||||
"The total water consumption recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
);
|
||||
addOptionalCommonFields();
|
||||
addOptionalFlowRelatedFields();
|
||||
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"meter_date",
|
||||
|
@ -59,10 +53,12 @@ namespace
|
|||
/* If the meter is recently commissioned, the target water consumption value is bogus.
|
||||
The bits store 0xffffffff. Should we deal with this? Now a very large value is printed in the json. */
|
||||
|
||||
// The wmbus telegram contains only storage 8 for target_date and total.
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"target",
|
||||
"The total water consumption recorded at the beginning of this month.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT,
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::OPTIONAL,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
|
@ -74,13 +70,38 @@ namespace
|
|||
addStringFieldWithExtractor(
|
||||
"target_date",
|
||||
"Date when target water consumption was recorded.",
|
||||
PrintProperty::JSON,
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Date)
|
||||
.set(StorageNr(8))
|
||||
);
|
||||
|
||||
|
||||
// The wire mbus telegram contains 4 totals and dates. For the moment we only print nr 1 which is the latest.
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"target",
|
||||
"The total water consumption recorded at the beginning of this month.",
|
||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT | PrintProperty::OPTIONAL,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
.set(StorageNr(1))
|
||||
);
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"target_date",
|
||||
"Date when target water consumption was recorded.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Date)
|
||||
.set(StorageNr(1))
|
||||
);
|
||||
|
||||
/*
|
||||
According to data sheet, there are two status/info bytes, byte A and byte B.
|
||||
Unfortunately we do not now is byte A is the first or second byte. Oh well.
|
||||
|
@ -198,4 +219,9 @@ namespace
|
|||
// Test: Mino minomess 15503451 NOKEY
|
||||
// telegram=|6644496A1064035514377251345015496A0007EE0050052F2F#0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F|
|
||||
// {"media":"water","meter":"minomess","name":"Mino","id":"15503451","total_m3":0.059,"meter_date":"2021-11-30","target_m3":244444.442,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |Mino;15503451;0.059000;244444.442000;OK;1111-11-11 11:11.11
|
||||
// |Mino;15503451;0.059;244444.442;OK;1111-11-11 11:11.11
|
||||
|
||||
// Test: Minowired minomess 57575757 NOKEY
|
||||
// telegram=|6874746808007257575757496A000712000000_0C7857575757046D2414DE280413000000000C943C000000004413FFFFFFFF426CFFFF840113FFFFFFFF82016CFFFFC40113FFFFFFFFC2016CFFFF840213FFFFFFFF82026CFFFF043B000000000422E62F000004260000000034220000000002FD1700001F5716|
|
||||
// {"media":"water","meter":"minomess","name":"Minowired","id":"57575757","fabrication_no":"57575757","operating_time_h":0,"on_time_h":3.406111,"on_time_at_error_h":0,"meter_datetime":"2022-08-30 20:36","total_m3":0,"total_backward_m3":0,"volume_flow_m3h":0,"meter_date":null,"target_m3":4294967.295,"target_date":"2127-15-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |Minowired;57575757;0;4294967.295;OK;1111-11-11 11:11.11
|
||||
|
|
|
@ -137,5 +137,5 @@ namespace
|
|||
|
||||
// Test: qualco qualcosonic 03016408 NOKEY
|
||||
// telegram=|76440907086401030B0d7a78000000046d030fB726346d0000010134fd170000000004200f13cf0104240f13cf0104863B0000000004863cdc0000000413B5150600042B86f1ffff043B030200000259c002025d2c05026194fd0c780864010384086d3B17Bf258408863B000000008408863c0B000000|
|
||||
// {"media":"heat/cooling load","meter":"qualcosonic","name":"qualco","id":"03016408","fabrication_no":"03016408","operating_time_h":2.34167,"on_time_h":2.34167,"meter_datetime":"2021-06-23 15:03","meter_error_datetime":"2000-01-01 00:00","total_m3":398.773,"flow_temperature_c":7.04,"return_temperature_c":13.24,"flow_return_temperature_difference_c":-6.2,"volume_flow_m3h":0.515,"status":"OK","total_heat_energy_kwh":0,"total_cooling_energy_kwh":220,"power_kw":-3.706,"target_datetime":"2021-05-31 23:59","target_heat_energy_kwh":0,"target_cooling_energy_kwh":11,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
// {"media":"heat/cooling load","meter":"qualcosonic","name":"qualco","id":"03016408","fabrication_no":"03016408","operating_time_h":2.34167,"on_time_h":2.34167,"meter_datetime":"2021-06-23 15:03","meter_datetime_at_error":"2000-01-01 00:00","total_m3":398.773,"flow_temperature_c":7.04,"return_temperature_c":13.24,"flow_return_temperature_difference_c":-6.2,"volume_flow_m3h":0.515,"status":"OK","total_heat_energy_kwh":0,"total_cooling_energy_kwh":220,"power_kw":-3.706,"target_datetime":"2021-05-31 23:59","target_heat_energy_kwh":0,"target_cooling_energy_kwh":11,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
// |qualco;03016408;OK;0.000000;220.000000;-3.706000;2021-05-31 23:59;0.000000;11.000000;1111-11-11 11:11.11
|
||||
|
|
|
@ -1635,13 +1635,20 @@ bool checkConstantField(string *buf, string field, char c, vector<string> *extra
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
string concatFields(Meter *m, Telegram *t, char c, vector<FieldInfo> &prints, vector<Unit> &cs, bool hr,
|
||||
vector<string> *selected_fields, vector<string> *extra_constant_fields)
|
||||
{
|
||||
if (selected_fields == NULL || selected_fields->size() == 0)
|
||||
{
|
||||
return concatAllFields(m, t, c, prints, cs, hr, extra_constant_fields);
|
||||
// No global override, but is there a meter driver setting?
|
||||
if (m->selectedFields().size() > 0)
|
||||
{
|
||||
selected_fields = &m->selectedFields();
|
||||
}
|
||||
else
|
||||
{
|
||||
return concatAllFields(m, t, c, prints, cs, hr, extra_constant_fields);
|
||||
}
|
||||
}
|
||||
string buf = "";
|
||||
|
||||
|
@ -2225,6 +2232,14 @@ shared_ptr<Meter> createMeter(MeterInfo *mi)
|
|||
shared_ptr<Meter> newm = di->construct(*mi);
|
||||
newm->addConversions(mi->conversions);
|
||||
newm->setPollInterval(mi->poll_interval);
|
||||
if (mi->selected_fields.size() > 0)
|
||||
{
|
||||
newm->setSelectedFields(mi->selected_fields);
|
||||
}
|
||||
else
|
||||
{
|
||||
newm->setSelectedFields(di->defaultFields());
|
||||
}
|
||||
verbose("(meter) created %s %s %s %s\n",
|
||||
mi->name.c_str(),
|
||||
di->name().str().c_str(),
|
||||
|
@ -2758,6 +2773,17 @@ void MeterCommonImplementation::addOptionalCommonFields()
|
|||
.set(VIFRange::OnTime)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"on_time_at_error",
|
||||
"How long the meter has been in an error state while powered up.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
Quantity::Time,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::AtError)
|
||||
.set(VIFRange::OnTime)
|
||||
);
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"meter_datetime",
|
||||
"Date and time when the meter sent the telegram.",
|
||||
|
@ -2768,7 +2794,7 @@ void MeterCommonImplementation::addOptionalCommonFields()
|
|||
);
|
||||
|
||||
addStringFieldWithExtractor(
|
||||
"meter_error_datetime",
|
||||
"meter_datetime_at_error",
|
||||
"Date and time when the meter was in error.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
FieldMatcher::build()
|
||||
|
@ -2782,7 +2808,7 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
|
|||
{
|
||||
addNumericFieldWithExtractor(
|
||||
"total",
|
||||
"The total heating/cooling media volume consumption recorded by this meter.",
|
||||
"The total media volume consumption recorded by this meter.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
|
@ -2791,9 +2817,21 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
|
|||
.set(VIFRange::Volume)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"total_backward",
|
||||
"The total media volume flowing backward.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
Quantity::Volume,
|
||||
VifScaling::Auto,
|
||||
FieldMatcher::build()
|
||||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::Volume)
|
||||
.add(VIFCombinable::BackwardFlow)
|
||||
);
|
||||
|
||||
addNumericFieldWithExtractor(
|
||||
"flow_temperature",
|
||||
"Forward heat/cooling media temperature.",
|
||||
"Forward media temperature.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
Quantity::Temperature,
|
||||
VifScaling::Auto,
|
||||
|
@ -2804,7 +2842,7 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
|
|||
|
||||
addNumericFieldWithExtractor(
|
||||
"return_temperature",
|
||||
"Return heat/cooling media temperature.",
|
||||
"Return media temperature.",
|
||||
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||
Quantity::Temperature,
|
||||
VifScaling::Auto,
|
||||
|
@ -2834,4 +2872,6 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
|
|||
.set(MeasurementType::Instantaneous)
|
||||
.set(VIFRange::VolumeFlow)
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -183,6 +183,7 @@ struct MeterInfo
|
|||
vector<string> shells;
|
||||
vector<string> extra_constant_fields; // Additional static fields that are added to each message.
|
||||
vector<Unit> conversions; // Additional units desired in json.
|
||||
vector<string> selected_fields; // Usually set to the default fields, but can be override in meter config.
|
||||
|
||||
// If this is a meter that needs to be polled.
|
||||
int poll_interval; // Poll every x seconds.
|
||||
|
@ -251,13 +252,14 @@ private:
|
|||
MeterType type_; // Water, Electricity etc.
|
||||
function<shared_ptr<Meter>(MeterInfo&,DriverInfo&di)> constructor_; // Invoke this to create an instance of the driver.
|
||||
vector<DriverDetect> detect_;
|
||||
vector<string> default_fields_;
|
||||
|
||||
public:
|
||||
DriverInfo() {};
|
||||
DriverInfo(MeterDriver mt) : driver_(mt) {};
|
||||
void setName(std::string n) { name_ = n; }
|
||||
void setMeterType(MeterType t) { type_ = t; }
|
||||
|
||||
void setDefaultFields(string f) { default_fields_ = splitString(f, ','); }
|
||||
void addLinkMode(LinkMode lm) { linkmodes_.addLinkMode(lm); }
|
||||
void addMfctTPLStatusBits(Translate::Lookup lookup) { mfct_tpl_status_bits_ = lookup; }
|
||||
void setConstructor(function<shared_ptr<Meter>(MeterInfo&,DriverInfo&)> c) { constructor_ = c; }
|
||||
|
@ -267,6 +269,7 @@ public:
|
|||
MeterDriver driver() { return driver_; }
|
||||
DriverName name() { return name_; }
|
||||
MeterType type() { return type_; }
|
||||
vector<string>& defaultFields() { return default_fields_; }
|
||||
LinkModeSet linkModes() { return linkmodes_; }
|
||||
Translate::Lookup &mfctTPLStatusBits() { return mfct_tpl_status_bits_; }
|
||||
shared_ptr<Meter> construct(MeterInfo& mi) { return constructor_(mi, *this); }
|
||||
|
@ -429,6 +432,9 @@ struct Meter
|
|||
virtual string idsc() = 0;
|
||||
// This meter can report these fields, like total_m3, temp_c.
|
||||
virtual vector<FieldInfo> &fieldInfos() = 0;
|
||||
// Either the default fields specified in the driver, or override fields in the meter configuration file.
|
||||
virtual vector<string> &selectedFields() = 0;
|
||||
virtual void setSelectedFields(vector<string> &f) = 0;
|
||||
virtual string meterDriver() = 0;
|
||||
virtual string name() = 0;
|
||||
virtual MeterDriver driver() = 0;
|
||||
|
|
|
@ -95,6 +95,7 @@ protected:
|
|||
void setMeterType(MeterType mt);
|
||||
void addLinkMode(LinkMode lm);
|
||||
void addMfctTPLStatusBits(Translate::Lookup lookup);
|
||||
void setDefaultFields(string f);
|
||||
|
||||
// Print with the default unit for this quantity.
|
||||
void addPrint(string vname, Quantity vquantity,
|
||||
|
@ -235,6 +236,9 @@ protected:
|
|||
void addOptionalCommonFields();
|
||||
void addOptionalFlowRelatedFields();
|
||||
|
||||
vector<string> &selectedFields() { return selected_fields_; }
|
||||
void setSelectedFields(vector<string> &f) { selected_fields_ = f; }
|
||||
|
||||
private:
|
||||
|
||||
int index_ {};
|
||||
|
@ -261,6 +265,9 @@ protected:
|
|||
vector<Unit> conversions_;
|
||||
vector<FieldInfo> field_infos_;
|
||||
vector<string> field_names_;
|
||||
// Defaults to a setting specified in the driver. Can be overridden in the meter file.
|
||||
// There is also a global selected_fields that can be set on the command line or in the conf file.
|
||||
vector<string> selected_fields_;
|
||||
// Map difvif key to hex values from telegrams.
|
||||
std::map<std::string,std::pair<int,std::string>> hex_values_;
|
||||
// Map field name (total_volume) to numeric value.
|
||||
|
|
Ładowanie…
Reference in New Issue