kopia lustrzana https://github.com/weetmuts/wmbusmeters
Added support for multical803.
rodzic
c4239412a0
commit
14ad435d02
2
CHANGES
2
CHANGES
|
@ -1,4 +1,6 @@
|
|||
|
||||
Nikodem added support for Multical803! Thanks Nikodem!
|
||||
|
||||
Added a warning to be printed if an rtl_sdr dongle is found when using auto
|
||||
and either rtl_sdr or rtl_wmbus cannot be found in the path at startup.
|
||||
|
||||
|
|
1
Makefile
1
Makefile
|
@ -147,6 +147,7 @@ METER_OBJS:=\
|
|||
$(BUILD)/meter_multical302.o \
|
||||
$(BUILD)/meter_multical403.o \
|
||||
$(BUILD)/meter_multical603.o \
|
||||
$(BUILD)/meter_multical803.o \
|
||||
$(BUILD)/meter_omnipower.o \
|
||||
$(BUILD)/meter_q400.o \
|
||||
$(BUILD)/meter_qcaloric.o \
|
||||
|
|
|
@ -294,6 +294,7 @@ 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 and Cooling meter Kamstrup Multical 403 (multical403) (in C1 mode)
|
||||
Heat and Cooling meter Kamstrup Multical 603 (multical603) (in C1 mode)
|
||||
Heat and Cooling meter Kamstrup Multical 803 (multical803) (in C1 mode)
|
||||
|
||||
Supported room sensors:
|
||||
Bmeters RFM-AMB Thermometer/Hygrometer (rfmamb)
|
||||
|
|
|
@ -51,4 +51,8 @@ telegram=|5B442D2C02017878340A8D2096809C1320EF2B7934147ED7|2D0A0000FAFF000043180
|
|||
# Test Multical603 C1 telegram
|
||||
|
||||
telegram=|42442D2C3636363635048D20E18025B62087D078|0406A500000004FF072B01000004FF089C000000041421020000043B120000000259D014025D000904FF2200000000|
|
||||
{"media":"heat","meter":"multical603","name":"Heat","id":"36363636","total_energy_consumption_kwh":165,"total_volume_m3":5.45,"volume_flow_m3h":0.018,"t1_temperature_c":53.28,"t2_temperature_c":23.04,"at_date":"","current_status":"","energy_forward_kwh":"299","energy_returned_kwh":"156","timestamp":"1111-11-11T11:11:11Z"}
|
||||
{"media":"heat","meter":"multical603","name":"Heat","id":"36363636","total_energy_consumption_kwh":165,"total_volume_m3":5.45,"volume_flow_m3h":0.018,"t1_temperature_c":53.28,"t2_temperature_c":23.04,"at_date":"","current_status":"","energy_forward_kwh":299,"energy_returned_kwh":156,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
||||
# Test Multical803 C1 telegram
|
||||
telegram=|88442D2C8180808039048D20864051322084C178|040F0000000004FF070000000004FF0800000000041400000000844014000000008480401400000000043B0000000002590000025D0000142D0000000084100F0000000084200F0000000004FF2260000100026C892B440F00000000441400000000C4401400000000C480401400000000426C812B|
|
||||
{"media":"heat","meter":"multical803","name":"Heater","id":"80808081","total_energy_consumption_kwh":0,"total_volume_m3":0,"volume_flow_m3h":0,"t1_temperature_c":0,"t2_temperature_c":0,"at_date":"2020-11-09 00:00","current_status":"SENSOR_T1_BELOW_MEASURING_RANGE SENSOR_T2_BELOW_MEASURING_RANGE","energy_forward_kwh":0,"energy_returned_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||
|
|
|
@ -105,16 +105,15 @@ MeterMultical603::MeterMultical603(MeterInfo &mi) :
|
|||
"Status of meter.",
|
||||
true, true);
|
||||
|
||||
addPrint("energy_forward_kwh", Quantity::Text,
|
||||
[&](){ return to_string(energy_forward_kwh_); },
|
||||
addPrint("energy_forward", Quantity::Energy,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_forward_kwh_, Unit::KWH, u); },
|
||||
"Energy forward.",
|
||||
false, true);
|
||||
|
||||
addPrint("energy_returned_kwh", Quantity::Text,
|
||||
[&](){ return to_string(energy_returned_kwh_); },
|
||||
addPrint("energy_returned", Quantity::Energy,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_returned_kwh_, Unit::KWH, u); },
|
||||
"Energy returned.",
|
||||
false, true);
|
||||
|
||||
}
|
||||
|
||||
shared_ptr<HeatMeter> createMultical603(MeterInfo &mi) {
|
||||
|
@ -200,10 +199,10 @@ void MeterMultical603::processContent(Telegram *t)
|
|||
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
|
||||
|
||||
extractDVuint32(&t->values, "04FF07", &offset, &energy_forward_kwh_);
|
||||
t->addMoreExplanation(offset, " something A (%zu)", energy_forward_kwh_);
|
||||
t->addMoreExplanation(offset, " energy forward kwh (%zu)", energy_forward_kwh_);
|
||||
|
||||
extractDVuint32(&t->values, "04FF08", &offset, &energy_returned_kwh_);
|
||||
t->addMoreExplanation(offset, " something B (%zu)", energy_returned_kwh_);
|
||||
t->addMoreExplanation(offset, " energy returned kwh (%zu)", energy_returned_kwh_);
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, ValueInformation::EnergyWh, 0, 0, &key, &t->values)) {
|
||||
extractDVdouble(&t->values, key, &offset, &total_energy_kwh_);
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
Copyright (C) 2018-2020 Fredrik Öhrström
|
||||
2020 Eric Bus
|
||||
2020 Nikodem Jędrzejczak
|
||||
|
||||
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 MeterMultical803 : public virtual HeatMeter, public virtual MeterCommonImplementation {
|
||||
MeterMultical803(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_gj_ {};
|
||||
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_ {};
|
||||
|
||||
uint32_t energy_forward_gj_ {};
|
||||
uint32_t energy_returned_gj_ {};
|
||||
};
|
||||
|
||||
MeterMultical803::MeterMultical803(MeterInfo &mi) :
|
||||
MeterCommonImplementation(mi, MeterType::MULTICAL803)
|
||||
{
|
||||
setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR);
|
||||
|
||||
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);
|
||||
|
||||
addPrint("energy_forward", Quantity::Energy,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_forward_gj_, Unit::GJ, u); },
|
||||
"Energy forward.",
|
||||
false, true);
|
||||
|
||||
addPrint("energy_returned", Quantity::Energy,
|
||||
[&](Unit u){ assertQuantity(u, Quantity::Energy); return convert(energy_returned_gj_, Unit::GJ, u); },
|
||||
"Energy returned.",
|
||||
false, true);
|
||||
}
|
||||
|
||||
shared_ptr<HeatMeter> createMultical803(MeterInfo &mi) {
|
||||
return shared_ptr<HeatMeter>(new MeterMultical803(mi));
|
||||
}
|
||||
|
||||
double MeterMultical803::totalEnergyConsumption(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Energy);
|
||||
return convert(total_energy_gj_, Unit::KWH, u);
|
||||
}
|
||||
|
||||
double MeterMultical803::totalVolume(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Volume);
|
||||
return convert(total_volume_m3_, Unit::M3, u);
|
||||
}
|
||||
|
||||
double MeterMultical803::t1Temperature(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Temperature);
|
||||
return convert(t1_temperature_c_, Unit::C, u);
|
||||
}
|
||||
|
||||
bool MeterMultical803::hasT1Temperature()
|
||||
{
|
||||
return has_t1_temperature_;
|
||||
}
|
||||
|
||||
double MeterMultical803::t2Temperature(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Temperature);
|
||||
return convert(t2_temperature_c_, Unit::C, u);
|
||||
}
|
||||
|
||||
bool MeterMultical803::hasT2Temperature()
|
||||
{
|
||||
return has_t2_temperature_;
|
||||
}
|
||||
|
||||
double MeterMultical803::volumeFlow(Unit u)
|
||||
{
|
||||
assertQuantity(u, Quantity::Flow);
|
||||
return convert(volume_flow_m3h_, Unit::M3H, u);
|
||||
}
|
||||
|
||||
void MeterMultical803::processContent(Telegram *t)
|
||||
{
|
||||
/*
|
||||
(multical803) 13: 78 tpl-ci-field (EN 13757-3 Application Layer (no tplh))
|
||||
(multical803) 14: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 15: 06 vif (Energy kWh)
|
||||
(multical803) 16: * A5000000 total energy consumption (165.000000 kWh)
|
||||
(multical803) 1a: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 1b: FF vif (Vendor extension)
|
||||
(multical803) 1c: 07 vife (?)
|
||||
(multical803) 1d: 2B010000
|
||||
(multical803) 21: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 22: FF vif (Vendor extension)
|
||||
(multical803) 23: 08 vife (?)
|
||||
(multical803) 24: 9C000000
|
||||
(multical803) 28: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 29: 14 vif (Volume 10⁻² m³)
|
||||
(multical803) 2a: * 21020000 total volume (5.450000 m3)
|
||||
(multical803) 2e: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 2f: 3B vif (Volume flow l/h)
|
||||
(multical803) 30: * 12000000 volume flow (0.018000 m3/h)
|
||||
(multical803) 34: 02 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 35: 59 vif (Flow temperature 10⁻² °C)
|
||||
(multical803) 36: * D014 T1 flow temperature (53.280000 °C)
|
||||
(multical803) 38: 02 dif (16 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 39: 5D vif (Return temperature 10⁻² °C)
|
||||
(multical803) 3a: * 0009 T2 flow temperature (23.040000 °C)
|
||||
(multical803) 3c: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
||||
(multical803) 3d: FF vif (Vendor extension)
|
||||
(multical803) 3e: 22 vife (per hour)
|
||||
(multical803) 3f: * 00000000 info codes ()
|
||||
*/
|
||||
int offset;
|
||||
string key;
|
||||
|
||||
extractDVuint8(&t->values, "04FF22", &offset, &info_codes_);
|
||||
t->addMoreExplanation(offset, " info codes (%s)", status().c_str());
|
||||
|
||||
extractDVuint32(&t->values, "04FF07", &offset, &energy_forward_gj_);
|
||||
t->addMoreExplanation(offset, " energy forward gj (%zu)", energy_forward_gj_);
|
||||
|
||||
extractDVuint32(&t->values, "04FF08", &offset, &energy_returned_gj_);
|
||||
t->addMoreExplanation(offset, " energy returned gj (%zu)", energy_returned_gj_);
|
||||
|
||||
if(findKey(MeasurementType::Instantaneous, ValueInformation::EnergyWh, 0, 0, &key, &t->values)) {
|
||||
extractDVdouble(&t->values, key, &offset, &total_energy_gj_);
|
||||
t->addMoreExplanation(offset, " total energy consumption (%f kWh)", total_energy_gj_);
|
||||
}
|
||||
|
||||
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 MeterMultical803::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;
|
||||
}
|
|
@ -57,6 +57,7 @@
|
|||
X(multical302,C1_bit, HeatMeter, MULTICAL302, Multical302) \
|
||||
X(multical403,C1_bit, HeatMeter, MULTICAL403, Multical403) \
|
||||
X(multical603,C1_bit, HeatMeter, MULTICAL603, Multical603) \
|
||||
X(multical803,C1_bit, HeatMeter, MULTICAL803, Multical803) \
|
||||
X(omnipower, C1_bit, ElectricityMeter, OMNIPOWER, Omnipower) \
|
||||
X(rfmamb, T1_bit, TempHygroMeter, RFMAMB, RfmAmb) \
|
||||
X(rfmtx1, T1_bit, WaterMeter, RFMTX1, RfmTX1) \
|
||||
|
@ -137,6 +138,7 @@
|
|||
X(MULTICAL403,MANUFACTURER_KAM, 0x0c, 0x34) \
|
||||
X(MULTICAL403,MANUFACTURER_KAM, 0x0d, 0x34) \
|
||||
X(MULTICAL603,MANUFACTURER_KAM, 0x04, 0x35) \
|
||||
X(MULTICAL803,MANUFACTURER_KAM, 0x04, 0x39) \
|
||||
X(OMNIPOWER, MANUFACTURER_KAM, 0x02, 0x01) \
|
||||
X(RFMAMB, MANUFACTURER_BMT, 0x1b, 0x10) \
|
||||
X(RFMTX1, MANUFACTURER_BMT, 0x07, 0x05) \
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
name=Heater
|
||||
type=multical803
|
||||
id=80808081
|
||||
#key=testing comment
|
||||
key=
|
|
@ -19,6 +19,7 @@ $PROG --format=json simulations/simulation_c1.txt \
|
|||
Rum cma12w 66666666 "" \
|
||||
My403Cooling multical403 78780102 "" \
|
||||
Heat multical603 36363636 "" \
|
||||
Heater multical803 80808081 "" \
|
||||
> $TEST/test_output.txt 2> $TEST/test_stderr.txt
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
|
|
Ładowanie…
Reference in New Issue