diff --git a/meter_qcaloric.cc b/meter_qcaloric.cc new file mode 100644 index 0000000..57d8e42 --- /dev/null +++ b/meter_qcaloric.cc @@ -0,0 +1,158 @@ +/* + 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"util.h" + +#include +#include +#include +#include +#include +#include + +struct MeterQCaloric : public virtual HeatCostMeter, public virtual MeterCommonImplementation { + MeterQCaloric(WMBus *bus, const char *name, const char *id, const char *key); + + double totalEnergyConsumption(); + + void printMeter(string *human_readable, + string *fields, char separator, + string *json, + vector *envs); + +private: + void handleTelegram(Telegram *t); + void processContent(Telegram *t); + + double total_energy_ {}; +}; + +MeterQCaloric::MeterQCaloric(WMBus *bus, const char *name, const char *id, const char *key) : + MeterCommonImplementation(bus, name, id, key, QCALORIC_METER, MANUFACTURER_QDS, 0x08, LinkModeC1) +{ + MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*)); +} + +double MeterQCaloric::totalEnergyConsumption() +{ + return total_energy_; +} + +void MeterQCaloric::handleTelegram(Telegram *t) { + + if (!isTelegramForMe(t)) { + // This telegram is not intended for this meter. + return; + } + + verbose("(qcaloric) %s %02x%02x%02x%02x ", + name().c_str(), + t->a_field_address[0], t->a_field_address[1], t->a_field_address[2], + t->a_field_address[3]); + + if (t->a_field_device_type != 0x08) { + warning("(qcaloric) expected telegram for heat cost allocator, but got \"%s\"!\n", + mediaType(t->m_field, t->a_field_device_type).c_str()); + } + + if (t->m_field != manufacturer() || + t->a_field_version != 0x35) { + warning("(qcaloric) expected telegram from QDS meter with version 0x01, but got \"%s\" version 0x%02x !\n", + manufacturerFlag(t->m_field).c_str(), t->a_field_version); + } + + if (useAes()) { + vector aeskey = key(); + decryptMode5_AES_CBC(t, aeskey); + } else { + t->content = t->payload; + } + logTelegram("(qcaloric) log", t->parsed, t->content); + int content_start = t->parsed.size(); + processContent(t); + if (isDebugEnabled()) { + t->explainParse("(qcaloric)", content_start); + } + triggerUpdate(t); +} + +void MeterQCaloric::processContent(Telegram *t) +{ + map> values; + parseDV(t, t->content, t->content.begin(), t->content.size(), &values); +} + +unique_ptr createQCaloric(WMBus *bus, const char *name, const char *id, const char *key) +{ + return unique_ptr(new MeterQCaloric(bus,name,id,key)); +} + +void MeterQCaloric::printMeter(string *human_readable, + string *fields, char separator, + string *json, + vector *envs) +{ + + char buf[65536]; + buf[65535] = 0; + + snprintf(buf, sizeof(buf)-1, "%s\t%s\t% 3.3f kwh\t%s", + name().c_str(), + id().c_str(), + totalEnergyConsumption(), + datetimeOfUpdateHumanReadable().c_str()); + + *human_readable = buf; + + snprintf(buf, sizeof(buf)-1, "%s%c%s%c%f%c%s", + name().c_str(), separator, + id().c_str(), separator, + totalEnergyConsumption(), separator, + datetimeOfUpdateRobot().c_str()); + + *fields = buf; + +#define Q(x,y) "\""#x"\":"#y"," +#define QS(x,y) "\""#x"\":\""#y"\"," +#define QSE(x,y) "\""#x"\":\""#y"\"" + + snprintf(buf, sizeof(buf)-1, "{" + QS(media,electricity) + QS(meter,omnipower) + QS(name,%s) + QS(id,%s) + Q(total_kwh,%f) + QSE(timestamp,%s) + "}", + name().c_str(), + id().c_str(), + totalEnergyConsumption(), + datetimeOfUpdateRobot().c_str()); + + *json = buf; + + envs->push_back(string("METER_JSON=")+*json); + envs->push_back(string("METER_TYPE=qcaloric")); + envs->push_back(string("METER_ID=")+id()); + envs->push_back(string("METER_TOTAL_KWH=")+to_string(totalEnergyConsumption())); + envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot()); +}