From 94e52230b5cce428057016def03153e7529d5d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Thu, 5 May 2022 20:38:42 +0200 Subject: [PATCH] Added hydroclima hca. --- CHANGES | 2 + README.md | 1 + src/driver_hydroclima.cc | 198 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 src/driver_hydroclima.cc diff --git a/CHANGES b/CHANGES index 85e0ec0..72fdd11 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,8 @@ from /var/log/wmbusmeters/meter_readings to /var/lib/wmbusmeters/meter_readings This only affects new installations. Existing conf files will use the old location as specified int the conf file. +Added hydroclima HCA. + Add detection of bad CUL firmware. Using :mbus as suffix to the meter driver now works to poll a meter of mbus instead of diff --git a/README.md b/README.md index 52f4c4c..952d821 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,7 @@ Qundis Q caloric (qcaloric) Sontex 868 (sontex868) Techem FHKV data II/III (fhkvdataiii) Siemens WHE542 (whe5x) +BMeters Hydroclima RFM (hydroclima) Supported heat meters: Heat meter Techem Compact V / Compact Ve (compact5) (non-standard protocol) diff --git a/src/driver_hydroclima.cc b/src/driver_hydroclima.cc new file mode 100644 index 0000000..c0ea93a --- /dev/null +++ b/src/driver_hydroclima.cc @@ -0,0 +1,198 @@ +/* + Copyright (C) 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 . +*/ + +#include"meters_common_implementation.h" + +namespace +{ + struct Driver : public virtual MeterCommonImplementation + { + Driver(MeterInfo &mi, DriverInfo &di); + void processContent(Telegram *t); + + double average_ambient_temperature_ {}; + double max_ambient_temperature_ {}; + double average_ambient_temperature_last_month_ {}; + double average_heater_temperature_last_month_ {}; + }; + + static bool ok = registerDriver([](DriverInfo&di) + { + di.setName("hydroclima"); + di.setMeterType(MeterType::HeatCostAllocationMeter); + di.addLinkMode(LinkMode::T1); + di.addDetection(MANUFACTURER_BMP, 0x08, 0x53); + di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr(new Driver(mi, di)); }); + }); + + Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di) + { + addNumericFieldWithExtractor( + "current_consumption", + "The current heat cost allocation.", + PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT, + Quantity::HCA, + VifScaling::Auto, + FieldMatcher::build() + .set(MeasurementType::Instantaneous) + .set(VIFRange::HeatCostAllocation) + ); + + addPrint("average_ambient_temperature", + Quantity::Temperature, + GET_FUNC(average_ambient_temperature_, Unit::C), + "Average ambient temperature since this beginning of this month.", + PrintProperty::JSON | PrintProperty::FIELD); + + addPrint("max_ambient_temperature", + Quantity::Temperature, + GET_FUNC(max_ambient_temperature_, Unit::C), + "Max ambient temperature since the beginning of this month.", + PrintProperty::JSON); + + addPrint("average_ambient_temperature_last_month", + Quantity::Temperature, + GET_FUNC(average_ambient_temperature_last_month_, Unit::C), + "Average ambient temperature last month.", + PrintProperty::JSON); + + addPrint("average_heater_temperature_last_month", + Quantity::Temperature, + GET_FUNC(average_heater_temperature_last_month_, Unit::C), + "Average heater temperature last month.", + PrintProperty::JSON); + + } + + double toTemperature(uchar hi, uchar lo) + { + return ((double)((hi<<8) | lo))/100.0; + } + + double toIndicationU(uchar hi, uchar lo) + { + return ((double)((hi<<8) | lo))/10.0; + } + + double toTotalIndicationU(uchar hihi, uchar hi, uchar lo) + { + int x = (hihi << 16) | (hi<<8) | lo; + return ((double)x)/10.0; + } + + void Driver::processContent(Telegram *t) + { + if (t->mfct_0f_index == -1) return; // Check that there is mfct data. + + int offset = t->header_size+t->mfct_0f_index; + + vector bytes; + t->extractMfctData(&bytes); // Extract raw frame data after the DIF 0x0F. + + debugPayload("(hydroclima mfct)", bytes); + + int i = 0; + int len = bytes.size(); + string info; + + // Typicall 25 bytes: 100043106A7D2C4A078F12202CB1242A06D306210021000000 + + if (i >= len) return; + uchar frame_identifier = bytes[i]; + t->addSpecialExplanation(i+offset, 1, KindOfData::PROTOCOL, Understanding::FULL, + "*** %02X frame identifier %s", frame_identifier, + frame_identifier == 0x10 ? "OK" :"UNKNOWN"); + i++; + + if (i+1 >= len) return; + uint16_t status = bytes[i+1]<<8 | bytes[i]; + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X status", bytes[i], bytes[i+1], status); + i+=2; + + if (i+1 >= len) return; + uint16_t time = bytes[i+1]<<8 | bytes[i]; + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X time", bytes[i], bytes[i+1], time); + i+=2; + + if (i+1 >= len) return; + uint16_t date = bytes[i+1]<<8 | bytes[i]; + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X date %x", bytes[i], bytes[i+1], date); + i+=2; + + if (i+1 >= len) return; + average_ambient_temperature_ = toTemperature(bytes[i+1], bytes[i]); + info = renderJsonOnlyDefaultUnit("average_ambient_temperature"); + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); + i+=2; + + if (i+1 >= len) return; + max_ambient_temperature_ = toTemperature(bytes[i+1], bytes[i]); + info = renderJsonOnlyDefaultUnit("max_ambient_temperature"); + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); + i+=2; + + if (i+1 >= len) return; + uint16_t max_date = bytes[i+1]<<8 | bytes[i]; + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X max date %x", bytes[i], bytes[i+1], max_date); + i+=2; + + if (i+1 >= len) return; + uint16_t num_measurements = bytes[i+1]<<8 | bytes[i]; + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X num measurements %d", bytes[i], bytes[i+1], num_measurements); + i+=2; + + if (i+1 >= len) return; + average_ambient_temperature_last_month_ = toTemperature(bytes[i+1], bytes[i]); + info = renderJsonOnlyDefaultUnit("average_ambient_temperature_last_month"); + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); + i+=2; + + if (i+1 >= len) return; + average_heater_temperature_last_month_ = toTemperature(bytes[i+1], bytes[i]); + info = renderJsonOnlyDefaultUnit("average_temperature_heater_last_month"); + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str()); + i+=2; + + if (i+1 >= len) return; + double indication_u = toIndicationU(bytes[i+1], bytes[i]); + t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X indication u %f", bytes[i], bytes[i+1], indication_u); + i+=2; + + if (i+2 >= len) return; + double total_indication_u = toTotalIndicationU(bytes[i+2], bytes[i+1], bytes[i]); + t->addSpecialExplanation(i+offset, 3, KindOfData::CONTENT, Understanding::FULL, + "*** %02X%02X%02X total indication u %f", bytes[i], bytes[i+1], bytes[i+2], total_indication_u); + i+=3; + } +} + + +// Test: HCA hydroclima 68036198 NOKEY +// Comment: +// telegram=|2e44b0099861036853087a000020002F2F036E0000000F100043106A7D2C4A078F12202CB1242A06D3062100210000| +// {"media":"heat cost allocation","meter":"hydroclima","name":"HCA","id":"68036198","current_consumption_hca":0,"average_ambient_temperature_c":18.66,"max_ambient_temperature_c":47.51,"average_ambient_temperature_last_month_c":15.78,"average_heater_temperature_last_month_c":17.47,"timestamp":"1111-11-11T11:11:11Z"} +// |HCA;68036198;0.000000;18.660000;1111-11-11 11:11.11