Added ehzp electricity meter.

pull/70/head
weetmuts 2020-01-28 19:09:39 +01:00
rodzic d81a573c6c
commit e944f3c13f
8 zmienionych plików z 67 dodań i 37 usunięć

Wyświetl plik

@ -84,7 +84,7 @@ $(info Building $(VERSION))
CXXFLAGS := $(DEBUG_FLAGS) -fPIC -fmessage-length=0 -std=c++11 -Wall -Wno-unused-function -I$(BUILD)
$(BUILD)/%.o: src/%.cc $(wildcard src/%.h)
@#$(CXX) $(CXXFLAGS) $< -c -E > $@.src
$(CXX) $(CXXFLAGS) $< -c -E > $@.src
$(CXX) $(CXXFLAGS) $< -MMD -c -o $@
METER_OBJS:=\

Wyświetl plik

@ -211,6 +211,7 @@ Elvaco CMa12w Thermometer (cma12w)
Supported electricity meters:
Tauron Amiplus (amiplus) (includes vendor apator and echelon)
EMH Metering (ehzp)
Work in progress:
Electricity meter Kamstrup Omnipower (omnipower)

Wyświetl plik

@ -29,7 +29,7 @@
using namespace std;
const char *ValueInformationName(ValueInformation v)
const char *toString(ValueInformation v)
{
switch (v) {
#define X(name,from,to) case ValueInformation::name: return #name;
@ -39,6 +39,16 @@ LIST_OF_VALUETYPES
assert(0);
}
ValueInformation toValueInformation(int i)
{
switch (i) {
#define X(name,from,to) if (from >= i && i <= to) return ValueInformation::name;
LIST_OF_VALUETYPES
#undef X
}
return ValueInformation::None;
}
map<uint16_t,string> hash_to_format_;
bool loadFormatBytesFromSignature(uint16_t format_signature, vector<uchar> *format_bytes)
@ -279,10 +289,11 @@ bool parseDV(Telegram *t,
void valueInfoRange(ValueInformation v, int *low, int *hi)
{
switch (v) {
#define X(name,from,to) case ValueInformation::name: *low = from; *hi = to; break;
#define X(name,from,to) case ValueInformation::name: *low = from; *hi = to; return;
LIST_OF_VALUETYPES
#undef X
}
assert(0);
}
bool hasKey(std::map<std::string,std::pair<int,DVEntry>> *values, std::string key)
@ -290,29 +301,33 @@ bool hasKey(std::map<std::string,std::pair<int,DVEntry>> *values, std::string ke
return values->count(key) > 0;
}
bool findKey(MeasurementType mit, ValueInformation vif, int storagenr, std::string *key, std::map<std::string,std::pair<int,DVEntry>> *values)
bool findKey(MeasurementType mit, ValueInformation vif, int storagenr,
std::string *key, std::map<std::string,std::pair<int,DVEntry>> *values)
{
int low, hi;
valueInfoRange(vif, &low, &hi);
/* debug("(dvparser) looking for type=%s vif=%s storagenr=%d\n",
measurementTypeName(mit).c_str(), ValueInformationName(vif), storagenr);*/
debug("(dvparser) looking for type=%s vif=%s storagenr=%d value_ran_low=%02x value_ran_hi=%02x\n",
measurementTypeName(mit).c_str(), toString(vif), storagenr,
low, hi);
for (auto& v : *values)
{
MeasurementType ty = v.second.second.type;
int vi = v.second.second.value_information;
int sn = v.second.second.storagenr;
/* debug("(dvparser) match? type=%s vif=%s and storagenr=%d\n",
measurementTypeName(ty).c_str(), ValueInformationName(vif), storagenr, sn); */
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);
if (vi >= low && vi <= hi
&& (mit == MeasurementType::Unknown || mit == ty)
&& (storagenr == ANY_STORAGENR || storagenr == sn))
{
*key = v.first;
/*debug("(dvparser) found key %s for type=%s vif=%s storagenr=%d\n",
v.first.c_str(), measurementTypeName(ty).c_str(), ValueInformationName(vif), storagenr);*/
debug("(dvparser) found key %s for type=%s vif=%02x (%s) storagenr=%d\n",
v.first.c_str(), measurementTypeName(ty).c_str(),
vi, toString(toValueInformation(vi)), storagenr);
return true;
}
}
@ -437,6 +452,24 @@ bool extractDVdouble(map<string,pair<int,DVEntry>> *values,
+ ((unsigned int)v[2])*256*256
+ ((unsigned int)v[1])*256
+ ((unsigned int)v[0]);
} else if (t == 0x6) {
assert(v.size() == 6);
raw = ((uint64_t)v[5])*256*256*256*256*256
+ ((uint64_t)v[4])*256*256*256*256
+ ((uint64_t)v[3])*256*256*256
+ ((uint64_t)v[2])*256*256
+ ((uint64_t)v[1])*256
+ ((uint64_t)v[0]);
} else if (t == 0x7) {
assert(v.size() == 8);
raw = ((uint64_t)v[7])*256*256*256*256*256*256*256
+ ((uint64_t)v[6])*256*256*256*256*256*256
+ ((uint64_t)v[5])*256*256*256*256*256
+ ((uint64_t)v[4])*256*256*256*256
+ ((uint64_t)v[3])*256*256*256
+ ((uint64_t)v[2])*256*256
+ ((uint64_t)v[1])*256
+ ((uint64_t)v[0]);
}
double scale = 1.0;
if (auto_scale) scale = vifScale(vif);

Wyświetl plik

@ -28,6 +28,7 @@
#include<vector>
#define LIST_OF_VALUETYPES \
X(None,-1,-1) \
X(Volume,0x10,0x17) \
X(VolumeFlow,0x38,0x3F) \
X(FlowTemperature,0x58,0x5B) \
@ -35,7 +36,7 @@
X(HeatCostAllocation,0x6E,0x6E) \
X(Date,0x6C,0x6C) \
X(DateTime,0x6D,0x6D) \
X(EnergyWh,0x03,0x07) \
X(EnergyWh,0x00,0x07) \
X(PowerW,0x28,0x2f) \
enum class ValueInformation
@ -45,7 +46,8 @@ LIST_OF_VALUETYPES
#undef X
};
const char *ValueInformatioName(ValueInformation v);
const char *toString(ValueInformation v);
ValueInformation toValueInformation(int i);
bool loadFormatBytesFromSignature(uint16_t format_signature, vector<uchar> *format_bytes);

Wyświetl plik

@ -38,7 +38,7 @@ private:
double current_power_kw_ {};
double total_energy_returned_kwh_ {};
double current_power_returned_kw_ {};
string device_date_time_;
double on_time_h_ {};
};
MeterEHZP::MeterEHZP(WMBus *bus, MeterInfo &mi) :
@ -75,15 +75,11 @@ MeterEHZP::MeterEHZP(WMBus *bus, MeterInfo &mi) :
"The total energy production recorded by this meter.",
true, true);
addPrint("current_power_production", Quantity::Power,
[&](Unit u){ return currentPowerProduction(u); },
"Current power production.",
addPrint("on_time", Quantity::Time,
[&](Unit u){ assertQuantity(u, Quantity::Time);
return convert(on_time_h_, Unit::Hour, u); },
"Device on time.",
true, true);
addPrint("device_date_time", Quantity::Text,
[&](){ return device_date_time_; },
"Device date time.",
false, true);
}
unique_ptr<ElectricityMeter> createEHZP(WMBus *bus, MeterInfo &mi)
@ -120,26 +116,22 @@ void MeterEHZP::processContent(Telegram *t)
int offset;
string key;
if (findKey(MeasurementType::Unknown, ValueInformation::EnergyWh, 0, &key, &t->values)) {
if (findKey(MeasurementType::Unknown, ValueInformation::EnergyWh, 0, &key, &t->values))
{
extractDVdouble(&t->values, key, &offset, &total_energy_kwh_);
t->addMoreExplanation(offset, " total energy (%f kwh)", total_energy_kwh_);
}
if (findKey(MeasurementType::Unknown, ValueInformation::PowerW, 0, &key, &t->values)) {
if (findKey(MeasurementType::Unknown, ValueInformation::PowerW, 0, &key, &t->values))
{
extractDVdouble(&t->values, key, &offset, &current_power_kw_);
t->addMoreExplanation(offset, " current power (%f kw)", current_power_kw_);
}
extractDVdouble(&t->values, "0E833C", &offset, &total_energy_returned_kwh_);
extractDVdouble(&t->values, "07803C", &offset, &total_energy_returned_kwh_);
t->addMoreExplanation(offset, " total energy returned (%f kwh)", total_energy_returned_kwh_);
extractDVdouble(&t->values, "0BAB3C", &offset, &current_power_returned_kw_);
t->addMoreExplanation(offset, " current power returned (%f kw)", current_power_returned_kw_);
extractDVdouble(&t->values, "0420", &offset, &on_time_h_);
t->addMoreExplanation(offset, " on time (%f h)", on_time_h_);
if (findKey(MeasurementType::Unknown, ValueInformation::DateTime, 0, &key, &t->values)) {
struct tm datetime;
extractDVdate(&t->values, key, &offset, &datetime);
device_date_time_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str());
}
}

Wyświetl plik

@ -21,7 +21,9 @@
using namespace std;
#define LIST_OF_CONVERSIONS \
X(KWH, GJ, {vto=vfrom*0.0036;}) \
X(Second, Hour, {vto=vfrom/3600.0;}) \
X(Hour, Second, {vto=vfrom*3600.0;}) \
X(KWH, GJ, {vto=vfrom*0.0036;}) \
X(GJ, KWH,{vto=vfrom/0.0036;}) \
X(M3, L, {vto=vfrom*1000.0;}) \
X(L, M3, {vto=vfrom/1000.0;}) \

Wyświetl plik

@ -1,5 +1,5 @@
/*
Copyright (C) 2019 Fredrik Öhrström
Copyright (C) 2019-2020 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
@ -30,7 +30,7 @@
X(RelativeHumidity,RH) \
X(HCA,HCA) \
X(Text,TXT) \
X(Time,Seconds) \
X(Time,Hour) \
#define LIST_OF_UNITS \
X(KWH,kwh,kWh,Energy,"kilo Watt hour") \
@ -44,7 +44,8 @@
X(RH,rh,RH,RelativeHumidity,"relative humidity") \
X(HCA,hca,hca,HCA,"heat cost allocation") \
X(TXT,txt,txt,Text,"text") \
X(Seconds,s,s,Time,"seconds") \
X(Second,s,s,Time,"second") \
X(Hour,h,h,Time,"hour")
enum class Unit
{

Wyświetl plik

@ -1384,7 +1384,6 @@ bool Telegram::potentiallyDecrypt(vector<uchar>::iterator &pos)
}
else if (tpl_sec_mode == TPLSecurityMode::AES_CBC_NO_IV)
{
fprintf(stderr, "BLALSALDLASDÖLAKDLAKDSÖLADS\n");
bool ok = decrypt_TPL_AES_CBC_NO_IV(this, frame, pos, tpl_generated_key);
if (!ok) return false;
// Now the frame from pos and onwards has been decrypted.