kopia lustrzana https://github.com/weetmuts/wmbusmeters
Added meter code for Multical 403
rodzic
aa24eac099
commit
b40c2cdf5b
1
Makefile
1
Makefile
|
@ -131,6 +131,7 @@ METER_OBJS:=\
|
|||
$(BUILD)/meter_mkradio3.o \
|
||||
$(BUILD)/meter_multical21.o \
|
||||
$(BUILD)/meter_multical302.o \
|
||||
$(BUILD)/meter_multical403.o \
|
||||
$(BUILD)/meter_omnipower.o \
|
||||
$(BUILD)/meter_q400.o \
|
||||
$(BUILD)/meter_qcaloric.o \
|
||||
|
|
|
@ -226,6 +226,7 @@ Techem FHKV data II/III (fhkvdataiii)
|
|||
Supported heat meter:
|
||||
Heat meter Techem Vario 4 (vario451) (non-standard protocol)
|
||||
Heat meter Kamstrup Multical 302 (multical302) (in C1 mode, please open issue for T1 mode)
|
||||
Heat meter Kamstrup Multical 403 (multical403) (in C1 mode)
|
||||
|
||||
Supported room sensors:
|
||||
Lansen Thermometer/Hygrometer (lansenth)
|
||||
|
|
|
@ -328,9 +328,9 @@ bool findKey(MeasurementType mit, ValueInformation vif, int storagenr, int tarif
|
|||
int vi = v.second.second.value_information;
|
||||
int sn = v.second.second.storagenr;
|
||||
int tn = v.second.second.tariff;
|
||||
/*debug("(dvparser) match? %s type=%s vif=%02x (%s) and storagenr=%d\n",
|
||||
debug("(dvparser) match? %s type=%s vif=%02x (%s) and storagenr=%d\n",
|
||||
v.first.c_str(),
|
||||
measurementTypeName(ty).c_str(), vi, toString(toValueInformation(vi)), storagenr, sn);*/
|
||||
measurementTypeName(ty).c_str(), vi, toString(toValueInformation(vi)), storagenr, sn);
|
||||
|
||||
if (vi >= low && vi <= hi
|
||||
&& (mit == MeasurementType::Unknown || mit == ty)
|
||||
|
|
|
@ -31,10 +31,12 @@
|
|||
X(Volume,0x10,0x17) \
|
||||
X(VolumeFlow,0x38,0x3F) \
|
||||
X(FlowTemperature,0x58,0x5B) \
|
||||
X(ReturnTemperature,0x5D,0x5D) \
|
||||
X(ExternalTemperature,0x64,0x67) \
|
||||
X(HeatCostAllocation,0x6E,0x6E) \
|
||||
X(Date,0x6C,0x6C) \
|
||||
X(DateTime,0x6D,0x6D) \
|
||||
X(EnergyMJ,0x0e,0x0e) \
|
||||
X(EnergyWh,0x00,0x07) \
|
||||
X(PowerW,0x28,0x2f) \
|
||||
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
Copyright (C) 2018-2019 Fredrik Öhrström
|
||||
|
||||
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"
|
||||
|
||||
#define INFO_CODE_VOLTAGE_INTERRUPTED 1
|
||||
#define INFO_CODE_LOW_BATTERY_LEVEL 2
|
||||
#define INFO_CODE_EXTERNAL_ALARM 4
|
||||
#define INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE 8
|
||||
#define INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE 16
|
||||
#define INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE 32
|
||||
#define INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE 64
|
||||
#define INFO_CODE_TEMP_DIFF_WRONG_POLARITY 128
|
||||
|
||||
struct MeterMultical403 : public virtual HeatMeter, public virtual MeterCommonImplementation {
|
||||
MeterMultical403(WMBus *bus, MeterInfo &mi);
|
||||
|
||||
double totalEnergyConsumption(Unit u);
|
||||
string status();
|
||||
double totalVolume(Unit u);
|
||||
double volumeFlow(Unit u);
|
||||
|
||||
// Water temperatures
|
||||
double t1Temperature(Unit u);
|
||||
bool hasT1Temperature();
|
||||
double t2Temperature(Unit u);
|
||||
bool hasT2Temperature();
|
||||
|
||||
private:
|
||||
void processContent(Telegram *t);
|
||||
|
||||
uchar info_codes_ {};
|
||||
double total_energy_kwh_ {};
|
||||
double total_energy_mj_ {};
|
||||
double total_volume_m3_ {};
|
||||
double volume_flow_m3h_ {};
|
||||
double t1_temperature_c_ { 127 };
|
||||
bool has_t1_temperature_ {};
|
||||
double t2_temperature_c_ { 127 };
|
||||
bool has_t2_temperature_ {};
|
||||
string target_date_ {};
|
||||
};
|
||||
|
||||
MeterMultical403::MeterMultical403(WMBus *bus, MeterInfo &mi) :
|
||||
MeterCommonImplementation(bus, mi, MeterType::MULTICAL403, MANUFACTURER_KAM)
|
||||
{
|
||||
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
|
||||
|
||||
addMedia(0x0a); // Heat/Cooling load
|
||||
addMedia(0x0b); // Heat/Cooling load
|
||||
addMedia(0x0c); // Heat/Cooling load
|
||||
addMedia(0x0d); // Heat/Cooling load
|
||||
addExpectedVersion(0x34);
|
||||
addLinkMode(LinkMode::C1);
|
||||
|
||||
addPrint("total_energy_consumption", Quantity::Energy,
|
||||
[&](Unit u){ return totalEnergyConsumption(u); },
|
||||
"The total energy consumption recorded by this meter.",
|
||||
true, true);
|
||||
|
||||
addPrint("total_volume", Quantity::Volume,
|
||||
[&](Unit u){ return totalVolume(u); },
|
||||
"Total volume of media.",
|
||||
true, true);
|
||||
|
||||
addPrint("volume_flow", Quantity::Flow,
|
||||
[&](Unit u){ return volumeFlow(u); },
|
||||
"The current flow.",
|
||||
true, true);
|
||||
|
||||
addPrint("t1_temperature", Quantity::Temperature,
|
||||
[&](Unit u){ return t1Temperature(u); },
|
||||
"The T1 temperature.",
|
||||
true, true);
|
||||
|
||||
addPrint("t2_temperature", Quantity::Temperature,
|
||||
[&](Unit u){ return t2Temperature(u); },
|
||||
"The T2 temperature.",
|
||||
true, true);
|
||||
|
||||
addPrint("at_date", Quantity::Text,
|
||||
[&](){ return target_date_; },
|
||||
"Date when total energy consumption was recorded.",
|
||||
false, true);
|
||||
|
||||
addPrint("current_status", Quantity::Text,
|
||||
[&](){ return status(); },
|
||||
"Status of meter.",
|
||||
true, true);
|
||||
}
|
||||
|
||||
unique_ptr<HeatMeter> createMultical403(WMBus *bus, MeterInfo &mi) {
|
||||
return unique_ptr<HeatMeter>(new MeterMultical403(bus, mi));
|
||||
}
|
||||
|
||||
double MeterMultical403::totalEnergyConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Energy);
|
||||
return convert(total_energy_mj_, Unit::MJ, u);
|
||||
}
|
||||
|
||||
double MeterMultical403::totalVolume(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Volume);
|
||||
return convert(total_volume_m3_, Unit::M3, u);
|
||||
}
|
||||
|
||||
double MeterMultical403::t1Temperature(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Temperature);
|
||||
return convert(t1_temperature_c_, Unit::C, u);
|
||||
}
|
||||
|
||||
bool MeterMultical403::hasT1Temperature()
|
||||
{
|
||||
return has_t1_temperature_;
|
||||
}
|
||||
|
||||
double MeterMultical403::t2Temperature(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Temperature);
|
||||
return convert(t2_temperature_c_, Unit::C, u);
|
||||
}
|
||||
|
||||
bool MeterMultical403::hasT2Temperature()
|
||||
{
|
||||
return has_t2_temperature_;
|
||||
}
|
||||
|
||||
double MeterMultical403::volumeFlow(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Flow);
|
||||
return convert(volume_flow_m3h_, Unit::M3H, u);
|
||||
}
|
||||
|
||||
void MeterMultical403::processContent(Telegram *t)
|
||||
{
|
||||
int offset;
|
||||
string key;
|
||||
|
||||
extractDVuint8(&t->values, "04FF22", &offset, &info_codes_);
|
||||
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, ValueInformation::EnergyMJ, 0, 0, &key, &t->values)) {
|
||||
extractDVdouble(&t->values, key, &offset, &total_energy_mj_);
|
||||
t->addMoreExplanation(offset, " total energy consumption (%f MJ)", total_energy_mj_);
|
||||
}
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, ValueInformation::Volume, 0, 0, &key, &t->values)) {
|
||||
extractDVdouble(&t->values, key, &offset, &total_volume_m3_);
|
||||
t->addMoreExplanation(offset, " total volume (%f m3)", total_volume_m3_);
|
||||
}
|
||||
|
||||
if(findKey(MeasurementType::Unknown, ValueInformation::VolumeFlow, 0, 0, &key, &t->values)) {
|
||||
extractDVdouble(&t->values, key, &offset, &volume_flow_m3h_);
|
||||
t->addMoreExplanation(offset, " volume flow (%f m3/h)", volume_flow_m3h_);
|
||||
}
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, ValueInformation::FlowTemperature, 0, 0, &key, &t->values)) {
|
||||
has_t1_temperature_ = extractDVdouble(&t->values, key, &offset, &t1_temperature_c_);
|
||||
t->addMoreExplanation(offset, " T1 flow temperature (%f °C)", t1_temperature_c_);
|
||||
}
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, ValueInformation::ReturnTemperature, 0, 0, &key, &t->values)) {
|
||||
has_t2_temperature_ = extractDVdouble(&t->values, key, &offset, &t2_temperature_c_);
|
||||
t->addMoreExplanation(offset, " T2 flow temperature (%f °C)", t2_temperature_c_);
|
||||
}
|
||||
|
||||
if (findKey(MeasurementType::Unknown, ValueInformation::Date, 0, 0, &key, &t->values)) {
|
||||
struct tm datetime;
|
||||
extractDVdate(&t->values, key, &offset, &datetime);
|
||||
target_date_ = strdatetime(&datetime);
|
||||
t->addMoreExplanation(offset, " target date (%s)", target_date_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
string MeterMultical403::status()
|
||||
{
|
||||
string s;
|
||||
if (info_codes_ & INFO_CODE_VOLTAGE_INTERRUPTED) s.append("VOLTAGE_INTERRUPTED ");
|
||||
if (info_codes_ & INFO_CODE_LOW_BATTERY_LEVEL) s.append("LOW_BATTERY_LEVEL ");
|
||||
if (info_codes_ & INFO_CODE_EXTERNAL_ALARM) s.append("EXTERNAL_ALARM ");
|
||||
if (info_codes_ & INFO_CODE_SENSOR_T1_ABOVE_MEASURING_RANGE) s.append("SENSOR_T1_ABOVE_MEASURING_RANGE ");
|
||||
if (info_codes_ & INFO_CODE_SENSOR_T2_ABOVE_MEASURING_RANGE) s.append("SENSOR_T2_ABOVE_MEASURING_RANGE ");
|
||||
if (info_codes_ & INFO_CODE_SENSOR_T1_BELOW_MEASURING_RANGE) s.append("SENSOR_T1_BELOW_MEASURING_RANGE ");
|
||||
if (info_codes_ & INFO_CODE_SENSOR_T2_BELOW_MEASURING_RANGE) s.append("SENSOR_T2_BELOW_MEASURING_RANGE ");
|
||||
if (info_codes_ & INFO_CODE_TEMP_DIFF_WRONG_POLARITY) s.append("TEMP_DIFF_WRONG_POLARITY ");
|
||||
if (s.length() > 0) {
|
||||
s.pop_back(); // Remove final space
|
||||
return s;
|
||||
}
|
||||
return s;
|
||||
}
|
|
@ -47,6 +47,7 @@
|
|||
X(mkradio3, T1_bit, Water, MKRADIO3, MKRadio3) \
|
||||
X(multical21, C1_bit, Water, MULTICAL21, Multical21) \
|
||||
X(multical302,C1_bit, Heat, MULTICAL302, Multical302) \
|
||||
X(multical403,C1_bit, Heat, MULTICAL403, Multical403) \
|
||||
X(omnipower, C1_bit, Electricity, OMNIPOWER, Omnipower) \
|
||||
X(rfmamb, T1_bit, TempHygro, RFMAMB, RfmAmb) \
|
||||
X(rfmtx1, T1_bit, Water, RFMTX1, RfmTX1) \
|
||||
|
@ -224,6 +225,7 @@ LinkModeSet toMeterLinkModeSet(string& type);
|
|||
unique_ptr<WaterMeter> createMultical21(WMBus *bus, MeterInfo &m);
|
||||
unique_ptr<WaterMeter> createFlowIQ3100(WMBus *bus, MeterInfo &m);
|
||||
unique_ptr<HeatMeter> createMultical302(WMBus *bus, MeterInfo &m);
|
||||
unique_ptr<HeatMeter> createMultical403(WMBus *bus, MeterInfo &m);
|
||||
unique_ptr<HeatMeter> createVario451(WMBus *bus, MeterInfo &m);
|
||||
unique_ptr<WaterMeter> createWaterstarM(WMBus *bus, MeterInfo &m);
|
||||
unique_ptr<ElectricityMeter> createOmnipower(WMBus *bus, MeterInfo &m);
|
||||
|
|
|
@ -28,7 +28,11 @@ using namespace std;
|
|||
X(Hour, Year, {vto=vfrom/24.0/365;}) \
|
||||
X(Year, Hour, {vto=vfrom*24.0*365;}) \
|
||||
X(KWH, GJ, {vto=vfrom*0.0036;}) \
|
||||
X(KWH, MJ, {vto=vfrom*0.0036*1000.0;}) \
|
||||
X(GJ, KWH,{vto=vfrom/0.0036;}) \
|
||||
X(MJ, GJ, {vto=vfrom/1000.0;}) \
|
||||
X(MJ, KWH,{vto=vfrom/1000.0/0.0036;}) \
|
||||
X(GJ, MJ, {vto=vfrom*1000.0;}) \
|
||||
X(M3, L, {vto=vfrom*1000.0;}) \
|
||||
X(L, M3, {vto=vfrom/1000.0;}) \
|
||||
X(C, F, {vto=(vfrom*9.0/5.0)+32.0;}) \
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#define LIST_OF_UNITS \
|
||||
X(KWH,kwh,"kWh",Energy,"kilo Watt hour") \
|
||||
X(MJ,mj,"MJ",Energy,"Mega Joule") \
|
||||
X(GJ,gj,"GJ",Energy,"Giga Joule") \
|
||||
X(M3,m3,"m3",Volume,"cubic meter") \
|
||||
X(L,l,"l",Volume,"litre") \
|
||||
|
|
|
@ -83,7 +83,7 @@ mqtt_publish) sent to a REST API (eg curl) or store it in a database
|
|||
.TP
|
||||
\fBmeter_name\fR a mnemonic for your utility meter
|
||||
.TP
|
||||
\fBmeter_type\fR multical21/flowiq3100/supercom587/iperl/multical302/omnipower/qcaloric/apator162/amiplus
|
||||
\fBmeter_type\fR multical21/flowiq3100/supercom587/iperl/multical302/multical403/omnipower/qcaloric/apator162/amiplus
|
||||
(Can be suffix with :<mode>, eg apator162:t1 to tell wmbusmeters that you expect only t1 telegrams.
|
||||
This is necessary since an apator162 can be configured to send either c1 or t1 telegrams.)
|
||||
.TP
|
||||
|
|
Ładowanie…
Reference in New Issue