From 9525758863ccf6b5e1cd5d45c856aa8ef3b37ffa Mon Sep 17 00:00:00 2001 From: cinemarene Date: Tue, 11 Feb 2020 22:35:57 +0100 Subject: [PATCH] Add Techem FHKV data II/III --- Makefile | 1 + README.md | 1 + simulations/simulation_techem_fhkv.txt | 1 + src/meter_fhkvdataiii.cc | 219 +++++++++++++++++++++++++ src/meters.h | 2 + src/wmbus.cc | 3 + 6 files changed, 227 insertions(+) create mode 100644 simulations/simulation_techem_fhkv.txt create mode 100644 src/meter_fhkvdataiii.cc diff --git a/Makefile b/Makefile index a112438..bfddbf5 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,7 @@ METER_OBJS:=\ $(BUILD)/meter_ehzp.o \ $(BUILD)/meter_esyswm.o \ $(BUILD)/meter_eurisii.o \ + $(BUILD)/meter_fhkvdataiii.o \ $(BUILD)/meter_hydrus.o \ $(BUILD)/meter_hydrodigit.o \ $(BUILD)/meter_iperl.o \ diff --git a/README.md b/README.md index 2057b20..afcddf6 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ Honeywell Q400 (q400) Supported heat cost allocators: Qundis Q caloric (qcaloric) Innotas EurisII (eurisii) +Techem FHKV data II/III (fhkvdataiii) Supported heat meter: Heat meter Techem Vario 4 (vario451) (non-standard protocol) diff --git a/simulations/simulation_techem_fhkv.txt b/simulations/simulation_techem_fhkv.txt new file mode 100644 index 0000000..5b0cbeb --- /dev/null +++ b/simulations/simulation_techem_fhkv.txt @@ -0,0 +1 @@ +telegram=||31446850249607016980A0119F27020480048300C408F709143C003D341A2B0B2A0707000000000000062D114457563D71A1850000| diff --git a/src/meter_fhkvdataiii.cc b/src/meter_fhkvdataiii.cc new file mode 100644 index 0000000..5734515 --- /dev/null +++ b/src/meter_fhkvdataiii.cc @@ -0,0 +1,219 @@ +/* + Copyright (C) 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 . +*/ + +#include"dvparser.h" +#include"meters.h" +#include"meters_common_implementation.h" +#include"wmbus.h" +#include"wmbus_utils.h" +#include"units.h" +#include"util.h" +#include + +struct MeterFHKVDataIII : public virtual HeatCostMeter, public virtual MeterCommonImplementation +{ + MeterFHKVDataIII(WMBus *bus, MeterInfo &mi); + + double currentPeriodEnergyConsumption(Unit u); + string currentPeriodDate(); + double previousPeriodEnergyConsumption(Unit u); + string previousPeriodDate(); + double currentRoomTemperature(Unit u); + double currentRadiatorTemperature(Unit u); + + private: + + void processContent(Telegram *t); + + string leadingZeroString(int num); + + double curr_energy_hca_ {}; + string curr_energy_hca_date {}; + double prev_energy_hca_ {}; + string prev_energy_hca_date {}; + double temp_room_ {}; + double temp_radiator_ {}; +}; + +unique_ptr createFHKVDataIII(WMBus *bus, MeterInfo &mi) +{ + return unique_ptr(new MeterFHKVDataIII(bus, mi)); +} + + +MeterFHKVDataIII::MeterFHKVDataIII(WMBus *bus, MeterInfo &mi) : + MeterCommonImplementation(bus, mi, MeterType::FHKVDATAIII, MANUFACTURER_TCH) +{ + addMedia(0x80); // T telegrams + + addLinkMode(LinkMode::T1); + + addPrint("current", Quantity::HCA, + [&](Unit u){ return currentPeriodEnergyConsumption(u); }, + "Energy consumption so far in this billing period.", + true, true); + + addPrint("current_date", Quantity::Text, + [&](){ return currentPeriodDate(); }, + "Date of current billing period.", + true, true); + + addPrint("previous", Quantity::HCA, + [&](Unit u){ return previousPeriodEnergyConsumption(u); }, + "Energy consumption in previous billing period.", + true, true); + + addPrint("previous_date", Quantity::Text, + [&](){ return previousPeriodDate(); }, + "Date of last billing period.", + true, true); + + addPrint("temp_room", Quantity::Temperature, + [&](Unit u){ return currentRoomTemperature(u); }, + "Current room temperature.", + true, true); + + addPrint("temp_radiator", Quantity::Temperature, + [&](Unit u){ return currentRadiatorTemperature(u); }, + "Current radiator temperature.", + true, true); +} + +double MeterFHKVDataIII::currentPeriodEnergyConsumption(Unit u) +{ + return curr_energy_hca_; +} + +string MeterFHKVDataIII::currentPeriodDate() +{ + return curr_energy_hca_date; +} + +double MeterFHKVDataIII::previousPeriodEnergyConsumption(Unit u) +{ + return prev_energy_hca_; +} + +string MeterFHKVDataIII::previousPeriodDate() +{ + return prev_energy_hca_date; +} + +double MeterFHKVDataIII::currentRoomTemperature(Unit u) +{ + assertQuantity(u, Quantity::Temperature); + return convert(temp_room_, Unit::C, u); +} + +double MeterFHKVDataIII::currentRadiatorTemperature(Unit u) +{ + assertQuantity(u, Quantity::Temperature); + return convert(temp_radiator_, Unit::C, u); +} + +void MeterFHKVDataIII::processContent(Telegram *t) +{ + // Unfortunately, the Techem FHKV data ii/iii is mostly a proprieatary protocol + // simple wrapped inside a wmbus telegram since the ci-field is 0xa0. + // Which means that the entire payload is manufacturer specific. + + vector content; + + t->extractPayload(&content); + + // Consumption + // Previous Consumption + uchar prev_lo = content[14]; + uchar prev_hi = content[15]; + double prev = (256.0*prev_hi+prev_lo); + prev_energy_hca_ = prev; + + string prevs; + strprintf(prevs, "%02x%02x", prev_lo, prev_hi); + int offset = t->parsed.size()+4; + t->addMoreExplanation(offset, " energy used in previous billing period (%f HCA)", prevs); + + // Previous Date + uchar date_prev_lo = content[12]; + uchar date_prev_hi = content[13]; + int date_prev = (256.0*date_prev_hi+date_prev_lo); + + int day_prev = (date_prev >> 0) & 0x1F; + int month_prev = (date_prev >> 5) & 0x0F; + int year_prev = (date_prev >> 9) & 0x3F; + prev_energy_hca_date = std::to_string((year_prev + 2000)) + "-" + leadingZeroString(month_prev) + "-" + leadingZeroString(day_prev) + "T02:00:00Z"; + + offset = t->parsed.size()+2; + t->addMoreExplanation(offset, " last date of previous billing period (%s)", prev_energy_hca_date); + + // Current Consumption + uchar curr_lo = content[18]; + uchar curr_hi = content[19]; + double curr = (256.0*curr_hi+curr_lo); + curr_energy_hca_ = curr; + + string currs; + strprintf(currs, "%02x%02x", curr_lo, curr_hi); + offset = t->parsed.size()+8; + t->addMoreExplanation(offset, " energy used in current billing period (%f HCA)", currs); + + // Current Date + uchar date_curr_lo = content[16]; + uchar date_curr_hi = content[17]; + int date_curr = (256.0*date_curr_hi+date_curr_lo); + + time_t now = time(0); + tm *ltm = localtime(&now); + int year_curr = 1900 + ltm->tm_year; + + int day_curr = (date_curr >> 4) & 0x1F; + if (day_curr <= 0) day_curr = 1; + int month_curr = (date_curr >> 9) & 0x0F; + if (month_curr <= 0) month_curr = 12; + curr_energy_hca_date = year_curr + "-" + leadingZeroString(month_curr) + "-" + leadingZeroString(day_curr) + "T02:00:00Z"; + + offset = t->parsed.size()+6; + t->addMoreExplanation(offset, " last date of current billing period (%s)", curr_energy_hca_date); + + // Temperature + // Room Temperature + uchar room_tlo = content[20]; + uchar room_thi = content[21]; + double room_t = (256.0*room_thi+room_tlo)/100; + temp_room_ = room_t; + + string room_ts; + strprintf(room_ts, "%02x%02x", room_tlo, room_thi); + offset = t->parsed.size()+10; + t->addMoreExplanation(offset, " current room temparature (%f °C)", room_ts); + + // Radiator Temperature + uchar radiator_tlo = content[22]; + uchar radiator_thi = content[23]; + double radiator_t = (256.0*radiator_thi+radiator_tlo)/100; + temp_radiator_ = radiator_t; + + string radiator_ts; + strprintf(radiator_ts, "%02x%02x", radiator_tlo, radiator_thi); + offset = t->parsed.size()+12; + t->addMoreExplanation(offset, " current radiator temparature (%f °C)", radiator_ts); +} + +string MeterFHKVDataIII::leadingZeroString(int num) { + string new_num = (num < 10 ? "0": "") + std::to_string(num); + return new_num; +} \ No newline at end of file diff --git a/src/meters.h b/src/meters.h index de202de..168b785 100644 --- a/src/meters.h +++ b/src/meters.h @@ -34,6 +34,7 @@ X(ehzp, T1_bit, Electricity, EHZP, EHZP) \ X(esyswm, T1_bit, Electricity, ESYSWM, ESYSWM) \ X(flowiq3100, C1_bit, Water, FLOWIQ3100, Multical21) \ + X(fhkvdataiii, T1_bit, HeatCostAllocation, FHKVDATAIII, FHKVDataIII) \ X(hydrus, T1_bit, Water, HYDRUS, Hydrus) \ X(hydrodigit, T1_bit, Water, HYDRODIGIT, Hydrodigit) \ X(iperl, T1_bit, Water, IPERL, Iperl) \ @@ -206,6 +207,7 @@ unique_ptr createIzar(WMBus *bus, MeterInfo &m); unique_ptr createQ400(WMBus *bus, MeterInfo &m); unique_ptr createQCaloric(WMBus *bus, MeterInfo &m); unique_ptr createEurisII(WMBus *bus, MeterInfo &m); +unique_ptr createFHKVDataIII(WMBus *bus, MeterInfo &m); unique_ptr createLansenTH(WMBus *bus, MeterInfo &m); unique_ptr createCMa12w(WMBus *bus, MeterInfo &m); unique_ptr createRfmAmb(WMBus *bus, MeterInfo &m); diff --git a/src/wmbus.cc b/src/wmbus.cc index 659fe74..2279917 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -411,6 +411,9 @@ string mediaType(int a_field_device_type) { case 0x62: return "Warm water"; // MKRadio3 case 0x72: return "Cold water"; // MKRadio3 + // Techem FHKV. + case 0x80: return "Heat Cost Allocator"; // FHKV data ii/iii + // Techem Vario 4 Typ 4.5.1 manufacturer specific. case 0xC3: return "Heat meter";