Rewrite remaining drivers using ancient code for printing values.

pull/783/head
Fredrik Öhrström 2023-01-02 22:39:39 +01:00
rodzic d7d1cda149
commit 952489c7e0
21 zmienionych plików z 710 dodań i 1469 usunięć

Wyświetl plik

@ -1,3 +1,8 @@
ATTENTION! The hydrus driver has been rewritten from scratch! current_date is now meter_datetime
Several fields were printed with value 0 even if the telegram did not contain the actual zero,
such fields are no longer printed.
ATTENTION! The microclima driver has been refactored. The field device_date_time has been renamed
to meter_datetime. Also two fields related to tariffs do not occur in the actual telegrams so these
fields have been removed. The fields output has therefore been drastically changed. Use --selectfields=

Wyświetl plik

@ -1,2 +1,2 @@
telegram=|6644242381818181640E7246564656A51170071F0050052F2F|15257A616F14139172137DAE3A0C000000008C2013917213000B3B0000000B26784601025AF5000266EF00046D1B08B7214C1338861200CC101300000000CC201338861200426C9F2C42EC7EBF2C|
{"media":"water","meter":"hydrus","name":"Vatten","id":"56465646","total_m3":114.35283,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"max_flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"current_date":"2021-01-23 08:27","total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31 00:00","actuality_duration_s":0,"operating_time_h":14678,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
{"media":"water","meter":"hydrus","name":"Vatten","id":"56465646","operating_time_h":14678,"meter_datetime":"2021-01-23 08:27","flow_temperature_c":24.5,"external_temperature_c":23.9,"status":"OK","total_tariff2_m3":137.291,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"flow_m3h":0,"total_at_date_m3":128.638,"at_date":"2020-12-31","timestamp":"1111-11-11T11:11:11Z"}

Wyświetl plik

@ -6,12 +6,12 @@ telegram=|1944304C72242421D401A2|013D4013DD8B46A4999C1293E582CC|
# Test new version of IZAR
telegram=|2944A511780729662366A20118001378D3B3DB8CEDD77731F25832AAF3DA8CADF9774EA673172E8C61F2|
{"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","prefix":"","serial_number":"000000","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
{"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}
# Yet another version of IZAR
telegram=|1944A511780779194820A1|21170013355F8EDB2D03C6912B1E37
{"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","prefix":"","serial_number":"000000","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
{"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}
# And another izar, with a mfct specific tpl ci field a3.
@ -25,4 +25,4 @@ telegram=|1944304CDEFFE420CC01A2|63120013258F907B0AFF12529AC33B|
# Yet another version of IZAR
telegram=19442423860775035048A251520015BEB6B2E1ED623A18FC74A5
{"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","prefix":"","serial_number":"000000","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
{"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}

Wyświetl plik

@ -101,11 +101,11 @@ telegram=|2e44333003020100071b7a634820252f2f0265840842658308820165950802fb1aae01
# Test Lansen door window telegram
telegram=|2e44333005020100071d7ab54800002f2f02fd1b110002fd971d01000efd3a2200000000008e40fd3a000000000000|
{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"CLOSED","a_counter":22,"b_counter":0,"timestamp":"1111-11-11T11:11:11Z"}
{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"CLOSED","a_counter":22,"b_counter":0,"error_flags": "ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_40","timestamp":"1111-11-11T11:11:11Z"}
|Dooro;00010205;CLOSED;1111-11-11 11:11.11
telegram=|2e44333005020100071d7ab66800002f2f02fd1b550002fd971d01000efd3a2300000000008e40fd3a000000000000|
{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"OPEN","a_counter":23,"b_counter":0,"timestamp":"1111-11-11T11:11:11Z"}
{"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"OPEN","a_counter":23,"b_counter":0,"error_flags": "ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_60","timestamp":"1111-11-11T11:11:11Z"}
|Dooro;00010205;OPEN;1111-11-11 11:11.11
# Test Lansen pulse counter
@ -122,17 +122,17 @@ telegram=|5744b40988227711101b7ab20800000265a00842658f088201659f08226589081265a0
# Test Hydrus water meter telegram
telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
{"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":10.4,"external_temperature_c":0,"current_date":"","total_at_date_m3":0,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2000-00-00 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":13.686516,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|HydrusWater;64646464;1.174;0;OK;1111-11-11 11:11.11
{"at_datetime": "2019-10-31 23:59","flow_m3h": 0,"flow_temperature_c": 10.4,"id": "64646464","media": "water","meter": "hydrus","name": "HydrusWater","remaining_battery_life_y": 13.686797,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 0.2,"total_m3": 1.174}
|HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11
# Test Hydrus new version water meter telegram
telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
{"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","total_m3":3.45,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":3.431,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2020-09-13 23:59","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":15.321013,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
{"at_datetime": "2020-09-13 23:59","customer": "A20B410178","flow_m3h": 0,"id": "65656565","media": "warm water","meter": "hydrus","name": "HydrusVater","remaining_battery_life_y": 15.321328,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 3.431,"total_m3": 3.45}
|HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11
# Test Hydrus with default AES encryption
telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C|
{"media":"water","meter":"hydrus","name":"HydrusAES","id":"64745666","total_m3":137.291,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"max_flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"current_date":"2021-01-23 08:27","total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31 00:00","actuality_duration_s":6673,"operating_time_h":14678,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
{"actuality_duration_s": 6673,"at_date": "2020-12-31","external_temperature_c": 23.9,"flow_m3h": 0,"flow_temperature_c": 24.5,"id": "64745666","media": "water","meter": "hydrus","meter_datetime": "2021-01-23 08:27","name": "HydrusAES","operating_time_h": 14678,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 128.638,"total_m3": 137.291,"total_tariff1_at_date_m3": 0,"total_tariff1_m3": 0,"total_tariff2_at_date_m3": 128.638,"total_tariff2_m3": 137.291}
|HydrusAES;64745666;137.291;128.638;OK;1111-11-11 11:11.11
# Test BMeters HydroDigit water telegram
@ -270,18 +270,18 @@ telegram=|1E44A511909192937B077A9F0010052F2F_04130347030002FD1700002F2F2F|
# Test IZAR RS 868 water meter
telegram=|1E4424238B07797389607A8F00107D_041312170100426CBF23441344100100|
{"media":"water","meter":"hydrus","name":"HydrusIzarRS","id":"60897379","total_m3":71.442,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":69.7,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2021-03-31 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
{"at_date": "2021-03-31","id": "60897379","media": "water","meter": "hydrus","name": "HydrusIzarRS","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 69.7,"total_m3": 71.442}
|HydrusIzarRS;60897379;71.442;69.7;OK;1111-11-11 11:11.11
# Test IZAR RS 868 water meter warm
telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000|
{"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":48.002,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2021-03-31 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
{"at_date": "2021-03-31","id": "60904720","media": "warm water","meter": "hydrus","name": "HydrusIzarRSWarm","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 48.002,"total_m3": 49.373}
|HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11
# Test another Diehl branded meter with the izar protocol
telegram=|19442423850798160018A2410100133EBBD44081053F243A82A3|
{"media":"water","meter":"izar","name":"IzarWater4","id":"18001698","prefix":"","serial_number":"000000","total_m3":835.689,"last_month_total_m3":820.329,"last_month_measure_date":"2021-09-01","remaining_battery_life_y":0.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
|IzarWater4;18001698;;000000;835.689;820.329;2021-09-01;0.5;no_alarm;no_alarm;8;0;1111-11-11 11:11.11
{"media":"water","meter":"izar","name":"IzarWater4","id":"18001698","total_m3":835.689,"last_month_total_m3":820.329,"last_month_measure_date":"2021-09-01","remaining_battery_life_y":0.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}
|IzarWater4;18001698;null;null;835.689;820.329;2021-09-01;0.5;no_alarm;no_alarm;8;null;1111-11-11 11:11.11
# Test Aventies Water Meter
telegram=76442104710007612507727100076121042507B5006005E2E95A3C2A1279A5415E6732679B43369FD5FDDDD783EEEBB48236D34E7C94AF0A18A5FDA5F7D64111EB42D4D891622139F2952F9D12A20088DFA4CF8123871123EE1F6C1DCEA414879DDB4E05E508F1826D7EFBA6964DF804C9261EA23BBF03

Wyświetl plik

@ -17,67 +17,64 @@
#include"meters_common_implementation.h"
using namespace std;
struct MeterApator08 : public virtual MeterCommonImplementation
namespace
{
MeterApator08(MeterInfo &mi, DriverInfo &di);
private:
void processContent(Telegram *t);
double total_water_consumption_m3_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("apator08");
di.setDefaultFields("name,id,total_m3,timestamp");
di.setMeterType(MeterType::WaterMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(0x8614/*APT?*/, 0x03, 0x03);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new MeterApator08(mi, di)); });
});
MeterApator08::MeterApator08(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addNumericField(
"total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.",
SET_FUNC(total_water_consumption_m3_, Unit::M3),
GET_FUNC(total_water_consumption_m3_, Unit::M3));
}
void MeterApator08::processContent(Telegram *t)
{
// Unfortunately, the at-wmbus-08 is mostly a proprietary protocol
// simple wrapped inside a wmbus telegram. Naughty!
// The telegram says gas (0x03) but it is a water meter.... so fix this.
t->dll_type = 0x07;
vector<uchar> content;
t->extractPayload(&content);
map<string,pair<int,DVEntry>> vendor_values;
string total;
strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]);
vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
int offset;
string key;
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &vendor_values))
struct Driver : public virtual MeterCommonImplementation
{
extractDVdouble(&vendor_values, key, &offset, &total_water_consumption_m3_);
// Now divide with 3! Is this the same for all apator08 meters? Time will tell.
total_water_consumption_m3_ /= 3.0;
Driver(MeterInfo &mi, DriverInfo &di);
total = "*** 10|"+total+" total consumption (%f m3)";
t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3_);
private:
void processContent(Telegram *t);
};
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("apator08");
di.setDefaultFields("name,id,total_m3,timestamp");
di.setMeterType(MeterType::WaterMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(0x8614/*APT?*/, 0x03, 0x03);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
});
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addNumericField(
"total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.");
}
void Driver::processContent(Telegram *t)
{
// The telegram says gas (0x03) but it is a water meter.... so fix this.
t->dll_type = 0x07;
vector<uchar> content;
t->extractPayload(&content);
map<string,pair<int,DVEntry>> vendor_values;
string total;
strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]);
vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
int offset;
string key;
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &vendor_values))
{
double total_water_consumption_m3 {};
extractDVdouble(&vendor_values, key, &offset, &total_water_consumption_m3);
// Now divide with 3! Is this the same for all apator08 meters? Time will tell.
total_water_consumption_m3 /= 3.0;
total = "*** 10|"+total+" total consumption (%f m3)";
t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3);
setNumericValue("total", Unit::M3, total_water_consumption_m3);
}
}
}

Wyświetl plik

@ -26,8 +26,6 @@ namespace
void processContent(Telegram *t);
void processExtras(string miExtras);
int registerSize(int c);
double total_water_consumption_m3_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -47,10 +45,11 @@ namespace
{
processExtras(mi.extras);
addPrint("total", Quantity::Volume,
[&](Unit u){ return convert(total_water_consumption_m3_, Unit::M3, u); },
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField(
"total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.");
}
void Driver::processContent(Telegram *t)
@ -105,9 +104,12 @@ namespace
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
int offset = i-1+t->header_size;
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3_);
double total_water_consumption_m3 {};
extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3);
total = "*** 10-"+total+" total consumption (%f m3)";
t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3_);
t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3);
setNumericValue("total", Unit::M3, total_water_consumption_m3);
}
else
{

Wyświetl plik

@ -26,8 +26,6 @@ namespace
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
double total_water_consumption_m3_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -41,10 +39,11 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("total", Quantity::Volume,
[&](Unit u){ return convert(total_water_consumption_m3_, Unit::M3, u); },
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField(
"total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.");
}
void Driver::processContent(Telegram *t)
@ -69,9 +68,11 @@ namespace
extractDVdouble(&vendor_values, "0413", &offset, &tmp);
// Single tick seems to be 1/3 of a m3. Divide by 3 and keep a single decimal.
// This will report consumption as 100.0 100.3 100.7 101.0 etc.
total_water_consumption_m3_ = 0.1*std::round(10000.0*tmp/3.0);
double total_water_consumption_m3 = 0.1*std::round(10000.0*tmp/3.0);
total = "*** "+total+" total consumption (%f m3)";
t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3_);
t->addSpecialExplanation(offset, 4, KindOfData::CONTENT, Understanding::FULL, total.c_str(), total_water_consumption_m3);
setNumericValue("total", Unit::M3, total_water_consumption_m3);
}
}
}

Wyświetl plik

@ -1,5 +1,5 @@
/*
Copyright (C) 2022 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2022-2023 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2022 Kajetan Krykwiński (gpl-3.0-or-later)
This program is free software: you can redistribute it and/or modify
@ -26,14 +26,6 @@ namespace
void processContent(Telegram *t);
string dateToString(uchar date_lo, uchar date_hi);
double prev_energy_hca_ {};
double curr_energy_hca_ {};
string curr_energy_hca_date_ {};
string season_start_date_ {};
string esb_date_ {};
double temp_room_avg_ {};
double temp_room_prev_avg_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -51,40 +43,37 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("current", Quantity::HCA,
[&](Unit u){ return convert(curr_energy_hca_, Unit::HCA, u);},
"Energy consumption so far in this billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("current",
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption so far in this billing period.");
addPrint("previous", Quantity::HCA,
[&](Unit u){ return convert(prev_energy_hca_, Unit::HCA, u); },
"Energy consumption in previous billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("previous",
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption in previous billing period.");
addPrint("current_date", Quantity::Text,
[&](){ return curr_energy_hca_date_; },
"Current date, as reported by meter.",
DEFAULT_PRINT_PROPERTIES);
addStringField("current_date",
"Current date, as reported by meter.",
DEFAULT_PRINT_PROPERTIES);
addPrint("season_start_date", Quantity::Text,
[&](){ return season_start_date_; },
"Season start date.",
DEFAULT_PRINT_PROPERTIES);
addStringField("season_start_date",
"Season start date.",
DEFAULT_PRINT_PROPERTIES);
addPrint("esb_date", Quantity::Text,
[&](){ return esb_date_; },
"Electronic seal protection break date.",
DEFAULT_PRINT_PROPERTIES);
addStringField("esb_date",
"Electronic seal protection break date.",
DEFAULT_PRINT_PROPERTIES);
addPrint("temp_room_avg", Quantity::Temperature,
[&](Unit u){ return convert(temp_room_avg_, Unit::C, u); },
"Average room temperature in current season.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("temp_room_avg",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Average room temperature in current season.");
addPrint("temp_room_prev_avg", Quantity::Temperature,
[&](Unit u){ return convert(temp_room_prev_avg_, Unit::C, u); },
"Average room temperature in previous season.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("temp_room_prev_avg",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Average room temperature in previous season.");
}
void Driver::processContent(Telegram *t)
@ -123,38 +112,44 @@ namespace
// Note: NOT byte swapped. Accidentally? works via dateToString conversion.
uchar season_start_date_lo = content[1];
uchar season_start_date_hi = content[0];
season_start_date_ = dateToString(season_start_date_lo, season_start_date_hi);
string season_start_date = dateToString(season_start_date_lo, season_start_date_hi);
setStringValue("season_start_date", season_start_date);
// Previous season total allocation
uchar prev_lo = content[4];
uchar prev_hi = content[5];
prev_energy_hca_ = (256.0*prev_hi+prev_lo);
double previous_hca = (256.0*prev_hi+prev_lo);
setNumericValue("previous", Unit::HCA, previous_hca);
// Electronic seal break date
uchar esb_date_lo = content[6];
uchar esb_date_hi = content[7];
esb_date_ = dateToString(esb_date_lo, esb_date_hi);
string esb_date = dateToString(esb_date_lo, esb_date_hi);
setStringValue("esb_date", esb_date);
// Current season allocation
uchar curr_lo = content[8];
uchar curr_hi = content[9];
curr_energy_hca_ = (256.0*curr_hi+curr_lo);
double current_hca = (256.0*curr_hi+curr_lo);
setNumericValue("current", Unit::HCA, current_hca);
// Current date reported by meter
uchar date_curr_lo = content[10];
uchar date_curr_hi = content[11];
curr_energy_hca_date_ = dateToString(date_curr_lo, date_curr_hi);
string current_date = dateToString(date_curr_lo, date_curr_hi);
setStringValue("current_date", current_date);
// Previous season average temperature
double temp_room_prev_avg_frac = content[12];
double temp_room_prev_avg_deg = content[13];
temp_room_prev_avg_ = temp_room_prev_avg_deg + temp_room_prev_avg_frac/256.0;
double temp_room_prev_avg = temp_room_prev_avg_deg + temp_room_prev_avg_frac/256.0;
setNumericValue("temp_room_prev_avg", Unit::C, temp_room_prev_avg);
// Current season average temperature
double temp_room_avg_frac = content[14];
double temp_room_avg_deg = content[15];
temp_room_avg_ = temp_room_avg_deg + temp_room_avg_frac/256.0;
double temp_room_avg = temp_room_avg_deg + temp_room_avg_frac/256.0;
setNumericValue("temp_room_avg", Unit::C, temp_room_avg);
}
string Driver::dateToString(uchar date_lo, uchar date_hi) {

Wyświetl plik

@ -24,11 +24,6 @@ namespace
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
double current_hca_;
double prev_hca_;
double historic_hca_[18];
string device_date_;
};
static bool ok = registerDriver([](DriverInfo&di)
@ -44,30 +39,30 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("current", Quantity::HCA,
[&](Unit u){ return convert(current_hca_, Unit::HCA, u);},
"Energy consumption so far in this billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("current",
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption so far in this billing period.");
addPrint("prev", Quantity::HCA,
[&](Unit u){ return convert(prev_hca_, Unit::HCA, u); },
"Energy consumption at end of previous billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("prev",
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption at end of previous billing period.");
for (int i=0; i<18; ++i)
{
string info = tostrprintf("prev_%02d", i+1);
string about = tostrprintf("Energy consumption %d months ago.", i+1);
addPrint(info, Quantity::HCA,
[this,i](Unit u){ return convert(historic_hca_[i], Unit::HCA, u);},
about, DEFAULT_PRINT_PROPERTIES);
addNumericField(info,
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
about);
}
addPrint("device_date", Quantity::Text,
[&](){ return device_date_; },
"Device date when telegram was sent.",
DEFAULT_PRINT_PROPERTIES);
addStringField("device_date",
"Device date when telegram was sent.",
DEFAULT_PRINT_PROPERTIES);
}
@ -116,28 +111,31 @@ namespace
if (content.size() < 40) return;
current_hca_ = content[6]*256 + content[7];
double current_hca = content[6]*256 + content[7];
setNumericValue("current", Unit::HCA, current_hca);
string msg = tostrprintf("*** %02X%02X \"current_hca\":%g", content[6], content[7], current_hca_);
string msg = tostrprintf("*** %02X%02X \"current_hca\":%g", content[6], content[7], current_hca);
t->addSpecialExplanation(6+t->header_size, 2, KindOfData::CONTENT, Understanding::FULL, msg.c_str());
prev_hca_ = content[4]*256 + content[5];
double prev_hca = content[4]*256 + content[5];
setNumericValue("prev", Unit::HCA, prev_hca);
msg = tostrprintf("*** %02X%02X \"prev_hca\":%g", content[4], content[5], prev_hca_);
msg = tostrprintf("*** %02X%02X \"prev_hca\":%g", content[4], content[5], prev_hca);
t->addSpecialExplanation(4+t->header_size, 2, KindOfData::CONTENT, Understanding::FULL, msg.c_str());
device_date_ = tostrprintf("20%02x-%02x-%02x", content[39], content[39-1], content[39-2]);
string device_date = tostrprintf("20%02x-%02x-%02x", content[39], content[39-1], content[39-2]);
setStringValue("device_date", device_date);
msg = tostrprintf("*** %02X%02X%02X \"device_date\":\"%s\"", content[39-2], content[39-1], content[39],
device_date_.c_str());
device_date.c_str());
t->addSpecialExplanation(39-2+t->header_size, 3, KindOfData::CONTENT, Understanding::FULL, msg.c_str());
for (int i=0; i<18; ++i)
{
historic_hca_[i] = getHistoric(i, content);
string info = tostrprintf("prev_%02d", i+1);
double historic_hca = getHistoric(i, content);
setNumericValue(info, Unit::HCA, historic_hca);
}
}
}

Wyświetl plik

@ -24,10 +24,6 @@ namespace
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
double total_energy_kwh_ {};
double curr_energy_kwh_ {};
double prev_energy_kwh_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -48,20 +44,20 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("total", Quantity::Energy,
[&](Unit u){ return convert(total_energy_kwh_, Unit::KWH, u); },
"The total energy consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("total",
Quantity::Energy,
DEFAULT_PRINT_PROPERTIES,
"The total energy consumption recorded by this meter.");
addPrint("current", Quantity::Energy,
[&](Unit u){ return convert(curr_energy_kwh_, Unit::KWH, u); },
"Energy consumption so far in this billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("current",
Quantity::Energy,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption so far in this billing period.");
addPrint("previous", Quantity::Energy,
[&](Unit u){ return convert(prev_energy_kwh_, Unit::KWH, u); },
"Energy consumption in previous billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("previous",
Quantity::Energy,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption in previous billing period.");
}
void Driver::processContent(Telegram *t)
@ -101,9 +97,14 @@ namespace
t->explanations.push_back(ce);
t->addMoreExplanation(offset, " energy used in current billing period (%f KWH)", curr);
total_energy_kwh_ = prev+curr;
curr_energy_kwh_ = curr;
prev_energy_kwh_ = prev;
double total_energy_kwh = prev+curr;
setNumericValue("total", Unit::KWH, total_energy_kwh);
double curr_energy_kwh = curr;
setNumericValue("current", Unit::KWH, curr_energy_kwh);
double prev_energy_kwh = prev;
setNumericValue("previous", Unit::KWH, prev_energy_kwh);
}
}

Wyświetl plik

@ -23,23 +23,10 @@ namespace
{
Driver(MeterInfo &mi, DriverInfo &di);
private:
void processContent(Telegram *t);
double currentPeriodEnergyConsumption(Unit u);
string currentPeriodDate();
double previousPeriodEnergyConsumption(Unit u);
string previousPeriodDate();
double currentRoomTemperature(Unit u);
double currentRadiatorTemperature(Unit u);
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_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -56,72 +43,38 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("current", Quantity::HCA,
[&](Unit u){ return currentPeriodEnergyConsumption(u); },
"Energy consumption so far in this billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("current",
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption so far in this billing period.");
addPrint("current_date", Quantity::Text,
[&](){ return currentPeriodDate(); },
"Date of current billing period.",
DEFAULT_PRINT_PROPERTIES);
addStringField("current_date",
"Date of current billing period.",
DEFAULT_PRINT_PROPERTIES);
addPrint("previous", Quantity::HCA,
[&](Unit u){ return previousPeriodEnergyConsumption(u); },
"Energy consumption in previous billing period.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("previous",
Quantity::HCA,
DEFAULT_PRINT_PROPERTIES,
"Energy consumption in previous billing period.");
addPrint("previous_date", Quantity::Text,
[&](){ return previousPeriodDate(); },
"Date of last billing period.",
DEFAULT_PRINT_PROPERTIES);
addStringField("previous_date",
"Date of last billing period.",
DEFAULT_PRINT_PROPERTIES);
addPrint("temp_room", Quantity::Temperature,
[&](Unit u){ return currentRoomTemperature(u); },
"Current room temperature.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("temp_room",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Current room temperature.");
addPrint("temp_radiator", Quantity::Temperature,
[&](Unit u){ return currentRadiatorTemperature(u); },
"Current radiator temperature.",
DEFAULT_PRINT_PROPERTIES);
}
double Driver::currentPeriodEnergyConsumption(Unit u)
{
return curr_energy_hca_;
}
string Driver::currentPeriodDate()
{
return curr_energy_hca_date;
}
double Driver::previousPeriodEnergyConsumption(Unit u)
{
return prev_energy_hca_;
}
string Driver::previousPeriodDate()
{
return prev_energy_hca_date;
}
double Driver::currentRoomTemperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(temp_room_, Unit::C, u);
}
double Driver::currentRadiatorTemperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(temp_radiator_, Unit::C, u);
addNumericField("temp_radiator",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Current radiator temperature.");
}
void Driver::processContent(Telegram *t)
{
// Unfortunately, the Techem FHKV data ii/iii is mostly a proprieatary protocol
// The Techem FHKV data ii/iii is mostly a proprietary protocol
// simple wrapped inside a wmbus telegram since the ci-field is 0xa0.
// Which means that the entire payload is manufacturer specific.
@ -135,18 +88,10 @@ namespace
debugPayload("(fhkvdataiii) not enough data", content);
return;
}
// Consumption
// Previous Consumption
uchar prev_lo = content[3];
uchar prev_hi = content[4];
double prev = (256.0*prev_hi+prev_lo);
prev_energy_hca_ = prev;
string prevs;
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
//t->addMoreExplanation(14, " energy used in previous billing period (%f HCA)", prevs);
// Previous Date
// Previous Date ////////////////////////////////////////////////////////
uchar date_prev_lo = content[1];
uchar date_prev_hi = content[2];
int date_prev = (256.0*date_prev_hi+date_prev_lo);
@ -154,21 +99,36 @@ namespace
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";
//t->addMoreExplanation(offset, " last date of previous billing period (%s)", prev_energy_hca_date);
string previous_date =
std::to_string((year_prev + 2000)) + "-" +
leadingZeroString(month_prev) + "-" +
leadingZeroString(day_prev) + "T02:00:00Z";
// Current Consumption
uchar curr_lo = content[7];
uchar curr_hi = content[8];
double curr = (256.0*curr_hi+curr_lo);
curr_energy_hca_ = curr;
setStringValue("previous_date", previous_date);
string currs;
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
//t->addMoreExplanation(offset, " energy used in current billing period (%f HCA)", currs);
string bytes = tostrprintf("%02x%02x", content[1], content[2]);
string info = "*** "+bytes+" previous_date = %s";
t->addSpecialExplanation(t->header_size+1, 2, KindOfData::CONTENT, Understanding::FULL,
info.c_str(), previous_date.c_str());
// Previous Consumption //////////////////////////////////////////////////////////
uchar prev_lo = content[3];
uchar prev_hi = content[4];
double prev = (256.0*prev_hi+prev_lo);
double previous_hca = prev;
setNumericValue("previous", Unit::HCA, previous_hca);
bytes = tostrprintf("%02x%02x", content[3], content[4]);
info = "*** "+bytes+" previous_hca = %.17g";
t->addSpecialExplanation(t->header_size+3, 2, KindOfData::CONTENT, Understanding::FULL,
info.c_str(), previous_hca);
// Current Date //////////////////////////////////////////////////////////////////
// Current Date
uchar date_curr_lo = content[5];
uchar date_curr_hi = content[6];
int date_curr = (256.0*date_curr_hi+date_curr_lo);
@ -181,44 +141,73 @@ namespace
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 = to_string(year_curr) + "-" + leadingZeroString(month_curr) + "-" + leadingZeroString(day_curr) + "T02:00:00Z";
// t->addMoreExplanation(offset, " last date of current billing period (%s)", curr_energy_hca_date);
string current_date =
to_string(year_curr) + "-" +
leadingZeroString(month_curr) + "-" +
leadingZeroString(day_curr) + "T02:00:00Z";
setStringValue("current_date", current_date);
bytes = tostrprintf("%02x%02x", content[5], content[6]);
info = "*** "+bytes+" current_date = %s";
t->addSpecialExplanation(t->header_size+5, 2, KindOfData::CONTENT, Understanding::FULL,
info.c_str(), current_date.c_str());
// Current Consumption ///////////////////////////////////////////////////////////
uchar curr_lo = content[7];
uchar curr_hi = content[8];
double current_hca = (256.0*curr_hi+curr_lo);
setNumericValue("current", Unit::HCA, current_hca);
bytes = tostrprintf("%02x%02x", content[7], content[8]);
info = "*** "+bytes+" current_hca = %.17g";
t->addSpecialExplanation(t->header_size+7, 2, KindOfData::CONTENT, Understanding::FULL,
info.c_str(), current_hca);
// Temperatures //////////////////////////////////////////////
// Temperature
uchar room_tlo;
uchar room_thi;
uchar radiator_tlo;
uchar radiator_thi;
int offset = 9;
if(t->dll_version == 0x94)
{
room_tlo = content[10];
room_thi = content[11];
radiator_tlo = content[12];
radiator_thi = content[13];
} else
{
room_tlo = content[9];
room_thi = content[10];
radiator_tlo = content[11];
radiator_thi = content[12];
offset= 10;
}
// Room Temperature
double room_t = (256.0*room_thi+room_tlo)/100;
temp_room_ = room_t;
room_tlo = content[offset];
room_thi = content[offset+1];
radiator_tlo = content[offset+2];
radiator_thi = content[offset+3];
string room_ts;
strprintf(&room_ts, "%02x%02x", room_tlo, room_thi);
// t->addMoreExplanation(offset, " current room temparature (%f °C)", room_ts);
// Room Temperature ////////////////////////////////////////////////////////////////
// Radiator Temperature
double radiator_t = (256.0*radiator_thi+radiator_tlo)/100;
temp_radiator_ = radiator_t;
double temp_room_c = (256.0*room_thi+room_tlo)/100;
setNumericValue("temp_room", Unit::C, temp_room_c);
string radiator_ts;
strprintf(&radiator_ts, "%02x%02x", radiator_tlo, radiator_thi);
// t->addMoreExplanation(offset, " current radiator temparature (%f °C)", radiator_ts);
bytes = tostrprintf("%02x%02x", content[offset], content[offset+1]);
info = "*** "+bytes+" temp_room_c = %.17g";
t->addSpecialExplanation(t->header_size+offset, 2, KindOfData::CONTENT, Understanding::FULL,
info.c_str(), temp_room_c);
// Radiator Temperature ////////////////////////////////////////////////////////////
double temp_radiator_c = (256.0*radiator_thi+radiator_tlo)/100;
setNumericValue("temp_radiator", Unit::C, temp_radiator_c);
bytes = tostrprintf("%02x%02x", content[offset+2], content[offset+3]);
info = "*** "+bytes+" temp_radiator_c = %.17g";
t->addSpecialExplanation(t->header_size+offset+2, 2, KindOfData::CONTENT, Understanding::FULL,
info.c_str(), temp_radiator_c);
}
string Driver::leadingZeroString(int num) {

Wyświetl plik

@ -25,11 +25,6 @@ namespace
void processContent(Telegram *t);
void decodeRF_RKN0(Telegram *t);
void decodeRF_RKN9(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)
@ -39,6 +34,7 @@ namespace
di.setMeterType(MeterType::HeatCostAllocationMeter);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_BMP, 0x08, 0x53);
di.usesProcessContent();
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
});
@ -55,29 +51,25 @@ namespace
.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.",
DEFAULT_PRINT_PROPERTIES );
addNumericField("average_ambient_temperature",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Average ambient temperature since this beginning of this month.");
addPrint("max_ambient_temperature",
Quantity::Temperature,
GET_FUNC(max_ambient_temperature_, Unit::C),
"Max ambient temperature since the beginning of this month.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("max_ambient_temperature",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Max ambient temperature since the beginning of this month.");
addPrint("average_ambient_temperature_last_month",
Quantity::Temperature,
GET_FUNC(average_ambient_temperature_last_month_, Unit::C),
"Average ambient temperature last month.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("average_ambient_temperature_last_month",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Average ambient temperature last month.");
addPrint("average_heater_temperature_last_month",
Quantity::Temperature,
GET_FUNC(average_heater_temperature_last_month_, Unit::C),
"Average heater temperature last month.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("average_heater_temperature_last_month",
Quantity::Temperature,
DEFAULT_PRINT_PROPERTIES,
"Average heater temperature last month.");
}
double toTemperature(uchar hi, uchar lo)
@ -149,14 +141,16 @@ namespace
i+=2;
if (i+1 >= len) return;
average_ambient_temperature_ = toTemperature(bytes[i+1], bytes[i]);
double average_ambient_temperature_c = toTemperature(bytes[i+1], bytes[i]);
setNumericValue("average_ambient_temperature", Unit::C, average_ambient_temperature_c);
info = renderJsonOnlyDefaultUnit("average_ambient_temperature", Quantity::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]);
double max_ambient_temperature_c = toTemperature(bytes[i+1], bytes[i]);
setNumericValue("max_ambient_temperature", Unit::C, max_ambient_temperature_c);
info = renderJsonOnlyDefaultUnit("max_ambient_temperature", Quantity::Temperature);
t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL,
"*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str());
@ -175,14 +169,18 @@ namespace
i+=2;
if (i+1 >= len) return;
average_ambient_temperature_last_month_ = toTemperature(bytes[i+1], bytes[i]);
double average_ambient_temperature_last_month_c = toTemperature(bytes[i+1], bytes[i]);
setNumericValue("average_ambient_temperature_last_month", Unit::C,
average_ambient_temperature_last_month_c);
info = renderJsonOnlyDefaultUnit("average_ambient_temperature_last_month", Quantity::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;
average_heater_temperature_last_month_ = toTemperature(bytes[i+1], bytes[i]);
double average_heater_temperature_last_month_c = toTemperature(bytes[i+1], bytes[i]);
setNumericValue("average_heater_temperature_last_month", Unit::C,
average_heater_temperature_last_month_c);
info = renderJsonOnlyDefaultUnit("average_heater_temperature_last_month", Quantity::Temperature);
t->addSpecialExplanation(i+offset, 2, KindOfData::CONTENT, Understanding::FULL,
"*** %02X%02X (%s)", bytes[i], bytes[i+1], info.c_str());
@ -252,5 +250,5 @@ namespace
// Test: HCAA hydroclima 74393723 NOKEY
// Comment:
// telegram=|2D44B009233739743308780F9D1300023ED97AEC7BC5908A32C15D8A32C126915AC15AC126912691269187912689|
// {"media":"heat cost allocation","meter":"hydroclima","name":"HCAA","id":"74393723","average_ambient_temperature_c":0,"max_ambient_temperature_c":0,"average_ambient_temperature_last_month_c":0,"average_heater_temperature_last_month_c":0,"timestamp":"1111-11-11T11:11:11Z"}
// |HCAA;74393723;null;0;1111-11-11 11:11.11
// {"media":"heat cost allocation","meter":"hydroclima","name":"HCAA","id":"74393723","timestamp":"1111-11-11T11:11:11Z"}
// |HCAA;74393723;null;null;1111-11-11 11:11.11

Wyświetl plik

@ -1,5 +1,5 @@
/*
Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2019-2023 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2021 Vincent Privat (gpl-3.0-or-later)
This program is free software: you can redistribute it and/or modify
@ -17,47 +17,12 @@
*/
#include"meters_common_implementation.h"
#include"manufacturer_specificities.h"
namespace
{
struct Driver : public virtual MeterCommonImplementation
{
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
private:
// Total water counted through the meter
double totalWaterConsumption(Unit u);
double totalWaterConsumptionTariff1(Unit u);
double totalWaterConsumptionTariff2(Unit u);
double totalWaterConsumptionAtDate(Unit u);
double totalWaterConsumptionTariff1AtDate(Unit u);
double totalWaterConsumptionTariff2AtDate(Unit u);
double maxFlow(Unit u);
bool hasMaxFlow();
double flowTemperature(Unit u);
double externalTemperature(Unit u);
double total_water_consumption_m3_ {};
double total_water_consumption_tariff1_m3_ {};
double total_water_consumption_tariff2_m3_ {};
string current_date_;
double total_water_consumption_at_date_m3_ {};
double total_water_consumption_tariff1_at_date_m3_ {};
double total_water_consumption_tariff2_at_date_m3_ {};
string at_date_;
double max_flow_m3h_ {};
double flow_temperature_c_ { 127 };
double external_temperature_c_ {};
uint32_t actuality_duration_s_ {};
double operating_time_h_ {};
double remaining_battery_life_year_ {};
string status_; // TPL STS
Translate::Lookup error_codes_;
};
static bool ok = registerDriver([](DriverInfo&di)
@ -79,528 +44,148 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
error_codes_ =
Translate::Lookup(
{
{
{
"TPL_FLAGS",
Translate::Type::IndexToString,
AlwaysTrigger, MaskBits(0xe0),
"OK",
{
{ 0x20, "AIR_IN_PIPE" },
{ 0x40, "WOOT_0x40" },
{ 0x60, "MEASUREMENT_ERROR" },
{ 0x80, "LEAKAGE_OR_NO_USAGE" },
{ 0xa0, "REVERSE_FLOW" },
{ 0xc0, "LOW_TEMPERATURE" },
{ 0xe0, "AIR_IN_PIPE" } }
}
},
});
addOptionalCommonFields("operating_time_h,actuality_duration_s,meter_datetime,customer");
addOptionalFlowRelatedFields("flow_temperature_c,external_temperature_c");
addPrint("total", Quantity::Volume,
[&](Unit u){ return totalWaterConsumption(u); },
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addStringField(
"status",
"Status of meter.",
PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS);
addPrint("total_tariff1", Quantity::Volume,
[&](Unit u){ return totalWaterConsumptionTariff1(u); },
"The total water consumption recorded by this meter at tariff 1.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"total",
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
);
addPrint("total_tariff2", Quantity::Volume,
[&](Unit u){ return totalWaterConsumptionTariff2(u); },
"The total water consumption recorded by this meter at tariff 2.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"total_tariff{tariff_counter}",
"The total water consumption recorded on tariff # by this meter.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(TariffNr(1),TariffNr(2))
);
addPrint("max_flow", Quantity::Flow,
[&](Unit u){ return maxFlow(u); },
"The maximum flow recorded during previous period.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"total_tariff{tariff_counter}_at_date",
"The total water consumption recorded on tariff # by this meter at billing date.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(TariffNr(1),TariffNr(2))
.set(StorageNr(1))
);
addPrint("flow_temperature", Quantity::Temperature,
[&](Unit u){ return flowTemperature(u); },
"The water temperature.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"flow",
"The current water flow.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Flow,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::VolumeFlow));
addPrint("external_temperature", Quantity::Temperature,
[&](Unit u){ return externalTemperature(u); },
"The external temperature.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"total_at_date",
"The total water consumption recorded at date.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(1))
);
addPrint("current_date", Quantity::Text,
[&](){ return current_date_; },
"Current date of measurement.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"at",
"The last billing period date.",
DEFAULT_PRINT_PROPERTIES,
Quantity::PointInTime,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Date)
.set(StorageNr(1)),
Unit::DateLT
);
addPrint("total_at_date", Quantity::Volume,
[&](Unit u){ return totalWaterConsumptionAtDate(u); },
"The total water consumption recorded at date.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"total_at_date",
"Fix this! The total water consumption recorded at last day. Perhaps?",
DEFAULT_PRINT_PROPERTIES,
Quantity::Volume,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Volume)
.set(StorageNr(3))
);
addPrint("total_tariff1_at_date", Quantity::Volume,
[&](Unit u){ return totalWaterConsumptionTariff1AtDate(u); },
"The total water consumption recorded at tariff 1 at date.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"at",
"Fix this! The last billing period date last day. Perhaps?",
DEFAULT_PRINT_PROPERTIES,
Quantity::PointInTime,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DateTime)
.set(StorageNr(3))
);
addPrint("total_tariff2_at_date", Quantity::Volume,
[&](Unit u){ return totalWaterConsumptionTariff2AtDate(u); },
"The total water consumption recorded at tariff 2 at date.",
DEFAULT_PRINT_PROPERTIES);
addPrint("at_date", Quantity::Text,
[&](){ return at_date_; },
"Date when total water consumption was recorded.",
DEFAULT_PRINT_PROPERTIES);
addPrint("actuality_duration", Quantity::Time, Unit::Second,
[&](Unit u){ return convert(actuality_duration_s_, Unit::Second, u); },
"Elapsed time between measurement and transmission",
DEFAULT_PRINT_PROPERTIES);
addPrint("operating_time", Quantity::Time, Unit::Hour,
[&](Unit u){ return convert(operating_time_h_, Unit::Hour, u); },
"How long the meter is operating",
DEFAULT_PRINT_PROPERTIES);
addPrint("remaining_battery_life", Quantity::Time, Unit::Year,
[&](Unit u){ return convert(remaining_battery_life_year_, Unit::Year, u); },
"How many more years the battery is expected to last",
DEFAULT_PRINT_PROPERTIES);
addPrint("status", Quantity::Text,
[&](){ return status_; },
"The status is OK or some error condition.",
DEFAULT_PRINT_PROPERTIES);
addNumericFieldWithExtractor(
"remaining_battery_life",
"Remaining battery life in years.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::RemainingBattery),
Unit::Year);
}
void Driver::processContent(Telegram *t)
{
// There are two distinctintly different Hydrus telegrams.
// Unfortunately there seems to be no markings on the
// physical meter that tells us which version it is.
//
// Fortunately the mfct,media,version bits does distinguish
// the meters!
//
// This driver currently only decodes parts of both telegrams.
// Eventually we should either split this driver into two.
// Or make more generic capabilities in drivers to switch
// between different telegrams formats, that are similar
// but not quite.
/*
New style telegram:
(hydrus) 11: 0C dif (8 digit BCD Instantaneous value)
(hydrus) 12: 13 vif (Volume l)
(hydrus) 13: * 50340000 total consumption (3.450000 m3)
(hydrus) 17: 0D dif (variable length Instantaneous value)
(hydrus) 18: FD vif (Second extension of VIF-codes)
(hydrus) 19: 11 vife (Customer)
(hydrus) 1a: 0A varlen=10
(hydrus) 1b: 38373130313442303241
(hydrus) 25: 0B dif (6 digit BCD Instantaneous value)
(hydrus) 26: 3B vif (Volume flow l/h)
(hydrus) 27: * 000000 max flow (0.000000 m3/h)
(hydrus) 2a: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 2b: FD vif (Second extension of VIF-codes)
(hydrus) 2c: 74 vife (Reserved)
(hydrus) 2d: * DC15 battery life days (5596)
(hydrus) 2f: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 30: 01 dife (subunit=0 tariff=0 storagenr=3)
(hydrus) 31: 6D vif (Date and time type)
(hydrus) 32: * 3B178D29 at date (2020-09-13 23:59)
(hydrus) 36: CC dif (8 digit BCD Instantaneous value storagenr=1)
(hydrus) 37: 01 dife (subunit=0 tariff=0 storagenr=3)
(hydrus) 38: 13 vif (Volume l)
(hydrus) 39: * 31340000 total consumption at date (3.431000 m3)
(hydrus) 3d: 2F skip
(hydrus) 3e: 2F skip
{"media":"warm water","meter":"hydrus","name":"Vatten","id":"65656565","total_m3":3.45,"max_flow_m3h":0,"flow_temperature_c":127,"total_at_date_m3":3.431,"at_date":"2020-09-13 23:59","battery_life_days":"5596","status":"OK","timestamp":"2020-09-27T08:54:42Z"}
Old style telegram
(hydrus) 11: 01 dif (8 Bit Integer/Binary Instantaneous value)
(hydrus) 12: FD vif (Second extension of VIF-codes)
(hydrus) 13: 08 vife (Access Number (transmission count))
(hydrus) 14: 30
(hydrus) 15: 0C dif (8 digit BCD Instantaneous value)
(hydrus) 16: 13 vif (Volume l)
(hydrus) 17: * 74110000 total consumption (1.174000 m3)
(hydrus) 1b: 7C dif (8 digit BCD Value during error state storagenr=1)
(hydrus) 1c: 13 vif (Volume l)
(hydrus) 1d: 00000000
(hydrus) 21: FC dif (8 digit BCD Value during error state storagenr=1)
(hydrus) 22: 10 dife (subunit=0 tariff=1 storagenr=1)
(hydrus) 23: 13 vif (Volume l)
(hydrus) 24: 00000000
(hydrus) 28: FC dif (8 digit BCD Value during error state storagenr=1)
(hydrus) 29: 20 dife (subunit=0 tariff=2 storagenr=1)
(hydrus) 2a: 13 vif (Volume l)
(hydrus) 2b: 00000000
(hydrus) 2f: 72 dif (16 Bit Integer/Binary Value during error state storagenr=1)
(hydrus) 30: 6C vif (Date type G)
(hydrus) 31: 0000
(hydrus) 33: 0B dif (6 digit BCD Instantaneous value)
(hydrus) 34: 3B vif (Volume flow l/h)
(hydrus) 35: * 000000 max flow (0.000000 m3/h)
(hydrus) 38: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 39: FD vif (Second extension of VIF-codes)
(hydrus) 3a: 74 vife (Reserved)
(hydrus) 3b: * 8713 battery life days (4999)
(hydrus) 3d: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 3e: 5A vif (Flow temperature 10¹ °C)
(hydrus) 3f: * 6800 flow temperature (10.400000 °C)
(hydrus) 41: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 42: 01 dife (subunit=0 tariff=0 storagenr=3)
(hydrus) 43: 6D vif (Date and time type)
(hydrus) 44: * 3B177F2A at date (2019-10-31 23:59)
(hydrus) 48: CC dif (8 digit BCD Instantaneous value storagenr=1)
(hydrus) 49: 01 dife (subunit=0 tariff=0 storagenr=3)
(hydrus) 4a: 13 vif (Volume l)
(hydrus) 4b: * 00020000 total consumption at date (0.200000 m3)
{"media":"water","meter":"hydrus","name":"Votten","id":"64646464","total_m3":1.174,"max_flow_m3h":0,"flow_temperature_c":10.4,"total_at_date_m3":0.2,"at_date":"2019-10-31 23:59","battery_life_days":"4999","status":"OK","timestamp":"2020-09-27T08:54:42Z"}
*/
/*
Yet another version telegram:
(hydrus) 0f: 2f2f decrypt check bytes
(hydrus) 11: 01 dif (8 Bit Integer/Binary Instantaneous value)
(hydrus) 12: FD vif (Second extension of VIF-codes)
(hydrus) 13: 08 vife (Access Number (transmission count))
(hydrus) 14: 88
(hydrus) 15: 0C dif (8 digit BCD Instantaneous value)
(hydrus) 16: 13 vif (Volume l)
(hydrus) 17: * 45911600 total consumption (169.145000 m3)
(hydrus) 1b: 8C dif (8 digit BCD Instantaneous value)
(hydrus) 1c: 10 dife (subunit=0 tariff=1 storagenr=0)
(hydrus) 1d: 05 vif (Energy 10² Wh)
(hydrus) 1e: 77900200
(hydrus) 22: 0B dif (6 digit BCD Instantaneous value)
(hydrus) 23: 3B vif (Volume flow l/h)
(hydrus) 24: * 000000 max flow (0.000000 m3/h)
(hydrus) 27: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 28: 5A vif (Flow temperature 10¹ °C)
(hydrus) 29: * DD00 flow temperature (22.100000 °C)
(hydrus) 2b: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 2c: FD vif (Second extension of VIF-codes)
(hydrus) 2d: 17 vife (Error flags (binary))
(hydrus) 2e: 0000
(hydrus) 30: 0A dif (4 digit BCD Instantaneous value)
(hydrus) 31: A6 vif (Operating time hours)
(hydrus) 32: 18 vife (?)
(hydrus) 33: 0000
(hydrus) 35: 0B dif (6 digit BCD Instantaneous value)
(hydrus) 36: 26 vif (Operating time hours)
(hydrus) 37: 255802
(hydrus) 3a: 2B dif (6 digit BCD Minimum value)
(hydrus) 3b: 3B vif (Volume flow l/h)
(hydrus) 3c: 000000
(hydrus) 3f: C4 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 40: 01 dife (subunit=0 tariff=0 storagenr=3)
(hydrus) 41: 6D vif (Date and time type)
(hydrus) 42: * 3B179F2A at date (2020-10-31 23:59)
(hydrus) 46: CC dif (8 digit BCD Instantaneous value storagenr=1)
(hydrus) 47: 01 dife (subunit=0 tariff=0 storagenr=3)
(hydrus) 48: 13 vif (Volume l)
(hydrus) 49: * 14811600 total consumption at date (168.114000 m3)
(hydrus) 4d: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 4e: 66 vif (External temperature 10¹ °C)
(hydrus) 4f: D400
(hydrus) 51: 2F skip
(hydrus) 52: 2F skip
(hydrus) 53: 2F skip
(hydrus) 54: 2F skip
(hydrus) 55: 2F skip
(hydrus) 56: 2F skip
(hydrus) 57: 2F skip
(hydrus) 58: 2F skip
(hydrus) 59: 2F skip
(hydrus) 5a: 2F skip
(hydrus) 5b: 2F skip
(hydrus) 5c: 2F skip
(hydrus) 5d: 2F skip
(hydrus) 5e: 2F skip
*/
/* Another one for #216 / #223
(hydrus) 00: 66 length (102 bytes)
(hydrus) 01: 44 dll-c (from meter SND_NR)
(hydrus) 02: 2423 dll-mfct (HYD)
(hydrus) 04: 28001081 dll-id (81100028)
(hydrus) 08: 64 dll-version
(hydrus) 09: 0e dll-type (Bus/System component)
(hydrus) 0a: 72 tpl-ci-field (EN 13757-3 Application Layer (long tplh))
(hydrus) 0b: 66567464 tpl-id (64745666)
(hydrus) 0f: a511 tpl-mfct (DME)
(hydrus) 11: 70 tpl-version
(hydrus) 12: 07 tpl-type (Water meter)
(hydrus) 13: 1f tpl-acc-field
(hydrus) 14: 00 tpl-sts-field
(hydrus) 15: 5005 tpl-cfg 0550 (AES_CBC_IV nb=5 cntn=0 ra=0 hc=0 )
(hydrus) 17: 2f2f decrypt check bytes
(hydrus) 19: 03 dif (24 Bit Integer/Binary Instantaneous value)
(hydrus) 1a: 74 vif (Actuality duration seconds)
(hydrus) 1b: * 111A00 actuality duration (-0.000029 s)
(hydrus) 1e: 0C dif (8 digit BCD Instantaneous value)
(hydrus) 1f: 13 vif (Volume l)
(hydrus) 20: * 91721300 total consumption (137.291000 m3)
(hydrus) 24: 8C dif (8 digit BCD Instantaneous value)
(hydrus) 25: 10 dife (subunit=0 tariff=1 storagenr=0)
(hydrus) 26: 13 vif (Volume l)
(hydrus) 27: * 00000000 total consumption at tariff 1 (0.000000 m3)
(hydrus) 2b: 8C dif (8 digit BCD Instantaneous value)
(hydrus) 2c: 20 dife (subunit=0 tariff=2 storagenr=0)
(hydrus) 2d: 13 vif (Volume l)
(hydrus) 2e: * 91721300 total consumption at tariff 2 (137.291000 m3)
(hydrus) 32: 0B dif (6 digit BCD Instantaneous value)
(hydrus) 33: 3B vif (Volume flow l/h)
(hydrus) 34: * 000000 max flow (0.000000 m3/h)
(hydrus) 37: 0B dif (6 digit BCD Instantaneous value)
(hydrus) 38: 26 vif (Operating time hours)
(hydrus) 39: * 784601 operating time (14678.000000 h)
(hydrus) 3c: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 3d: 5A vif (Flow temperature 10¹ °C)
(hydrus) 3e: * F500 flow temperature (24.500000 °C)
(hydrus) 40: 02 dif (16 Bit Integer/Binary Instantaneous value)
(hydrus) 41: 66 vif (External temperature 10¹ °C)
(hydrus) 42: * EF00 external temperature (23.900000 °C)
(hydrus) 44: 04 dif (32 Bit Integer/Binary Instantaneous value)
(hydrus) 45: 6D vif (Date and time type)
(hydrus) 46: * 1B08B721 current date (2021-01-23 08:27)
(hydrus) 4a: 4C dif (8 digit BCD Instantaneous value storagenr=1)
(hydrus) 4b: 13 vif (Volume l)
(hydrus) 4c: * 38861200 total consumption at date (128.638000 m3)
(hydrus) 50: CC dif (8 digit BCD Instantaneous value storagenr=1)
(hydrus) 51: 10 dife (subunit=0 tariff=1 storagenr=1)
(hydrus) 52: 13 vif (Volume l)
(hydrus) 53: * 00000000 total consumption at tariff 1 at date (0.000000 m3)
(hydrus) 57: CC dif (8 digit BCD Instantaneous value storagenr=1)
(hydrus) 58: 20 dife (subunit=0 tariff=2 storagenr=1)
(hydrus) 59: 13 vif (Volume l)
(hydrus) 5a: * 38861200 total consumption at tariff 1 at date (128.638000 m3)
(hydrus) 5e: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 5f: 6C vif (Date type G)
(hydrus) 60: * 9F2C at date (2020-12-31 00:00)
(hydrus) 62: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 63: EC vif (Date type G)
(hydrus) 64: 7E vife (additive correction constant: unit of VIF * 10^-1)
(hydrus) 65: BF2C
*/
/* And another two more that seem to be branded IZAR RS 868 */
/*
(hydrus) 00: 1e length (30 bytes)
(hydrus) 01: 44 dll-c (from meter SND_NR)
(hydrus) 02: 2423 dll-mfct (HYD)
(hydrus) 04: 79738960 dll-id (60897379)
(hydrus) 08: 8b dll-version
(hydrus) 09: 07 dll-type (Water meter)
(hydrus) 0a: 7a tpl-ci-field (EN 13757-3 Application Layer (short tplh))
(hydrus) 0b: 8f tpl-acc-field
(hydrus) 0c: 00 tpl-sts-field (OK)
(hydrus) 0d: 107d tpl-cfg 7d10 (accessibility synchronous SPECIFIC_16_31 )
(hydrus) 0f: 04 dif (32 Bit Integer/Binary Instantaneous value)
(hydrus) 10: 13 vif (Volume l)
(hydrus) 11: * 12170100 total consumption (71.442000 m3)
(hydrus) 15: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 16: 6C vif (Date type G)
(hydrus) 17: * BF23 at date (2021-03-31 00:00)
(hydrus) 19: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 1a: 13 vif (Volume l)
(hydrus) 1b: * 44100100 total consumption at date (69.700000 m3)
(hydrus) 01: 44 dll-c (from meter SND_NR)
(hydrus) 02: 2423 dll-mfct (HYD)
(hydrus) 04: 20479060 dll-id (60904720)
(hydrus) 08: 8b dll-version
(hydrus) 09: 06 dll-type (Warm Water (30°C-90°C) meter)
(hydrus) 0a: 7a tpl-ci-field (EN 13757-3 Application Layer (short tplh))
(hydrus) 0b: 2a tpl-acc-field
(hydrus) 0c: 00 tpl-sts-field (OK)
(hydrus) 0d: 10d8 tpl-cfg d810 (bidirectional accessibility SPECIFIC_16_31 )
(hydrus) 0f: 04 dif (32 Bit Integer/Binary Instantaneous value)
(hydrus) 10: 13 vif (Volume l)
(hydrus) 11: * DDC00000 total consumption (49.373000 m3)
(hydrus) 15: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 16: 6C vif (Date type G)
(hydrus) 17: * BF23 at date (2021-03-31 00:00)
(hydrus) 19: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
(hydrus) 1a: 13 vif (Volume l)
(hydrus) 1b: * 82BB0000 total consumption at date (48.002000 m3)
*/
int offset;
string key;
struct tm datetime;
// Container 0 : current / total
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_m3_);
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 1, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff1_m3_);
t->addMoreExplanation(offset, " total consumption at tariff 1 (%f m3)", total_water_consumption_tariff1_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 2, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff2_m3_);
t->addMoreExplanation(offset, " total consumption at tariff 2 (%f m3)", total_water_consumption_tariff2_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::VolumeFlow, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &max_flow_m3h_);
t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_m3h_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::FlowTemperature, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &flow_temperature_c_);
t->addMoreExplanation(offset, " flow temperature (%f °C)", flow_temperature_c_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::ExternalTemperature, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &external_temperature_c_);
t->addMoreExplanation(offset, " external temperature (%f °C)", external_temperature_c_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->dv_entries)) {
extractDVdate(&t->dv_entries, key, &offset, &datetime);
current_date_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " current date (%s)", current_date_.c_str());
}
if (findKey(MeasurementType::Instantaneous, VIFRange::ActualityDuration, 0, 0, &key, &t->dv_entries)) {
extractDVuint24(&t->dv_entries, key, &offset, &actuality_duration_s_);
t->addMoreExplanation(offset, " actuality duration (%f s)", actuality_duration_s_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::OperatingTime, 0, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &operating_time_h_);
t->addMoreExplanation(offset, " operating time (%f h)", operating_time_h_);
}
// Container 1/3 : past/future records
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->dv_entries)
|| findKey(MeasurementType::Instantaneous, VIFRange::Volume, 3, 0, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_at_date_m3_);
t->addMoreExplanation(offset, " total consumption at date (%f m3)", total_water_consumption_at_date_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 1, &key, &t->dv_entries)
|| findKey(MeasurementType::Instantaneous, VIFRange::Volume, 3, 1, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff1_at_date_m3_);
t->addMoreExplanation(offset, " total consumption at tariff 1 at date (%f m3)", total_water_consumption_tariff1_at_date_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 2, &key, &t->dv_entries)
|| findKey(MeasurementType::Instantaneous, VIFRange::Volume, 3, 2, &key, &t->dv_entries)) {
extractDVdouble(&t->dv_entries, key, &offset, &total_water_consumption_tariff2_at_date_m3_);
t->addMoreExplanation(offset, " total consumption at tariff 1 at date (%f m3)", total_water_consumption_tariff2_at_date_m3_);
}
if (findKey(MeasurementType::Instantaneous, VIFRange::Date , 1, 0, &key, &t->dv_entries)
|| findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 3, 0, &key, &t->dv_entries)) {
extractDVdate(&t->dv_entries, key, &offset, &datetime);
at_date_ = strdatetime(&datetime);
t->addMoreExplanation(offset, " at date (%s)", at_date_.c_str());
}
// TODO: a date in the future is also transmitted with VIFE 7E in container 1
// custom
uint16_t days {};
if (hasKey(&t->dv_entries, "02FD74") && extractDVuint16(&t->dv_entries, "02FD74", &offset, &days))
{
remaining_battery_life_year_ = ((double)days) / 365.25;
t->addMoreExplanation(offset, " battery life (%d days %f years)", days, remaining_battery_life_year_);
}
status_ = ::decodeTPLStatusByteWithMfct(t->tpl_sts, error_codes_);
}
double Driver::totalWaterConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_m3_, Unit::M3, u);
}
double Driver::totalWaterConsumptionTariff1(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_tariff1_m3_, Unit::M3, u);
}
double Driver::totalWaterConsumptionTariff2(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_tariff2_m3_, Unit::M3, u);
}
double Driver::totalWaterConsumptionAtDate(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_at_date_m3_, Unit::M3, u);
}
double Driver::totalWaterConsumptionTariff1AtDate(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_tariff1_at_date_m3_, Unit::M3, u);
}
double Driver::totalWaterConsumptionTariff2AtDate(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_tariff2_at_date_m3_, Unit::M3, u);
}
double Driver::maxFlow(Unit u)
{
assertQuantity(u, Quantity::Flow);
return convert(max_flow_m3h_, Unit::M3H, u);
}
bool Driver::hasMaxFlow()
{
return true;
}
double Driver::flowTemperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(flow_temperature_c_, Unit::C, u);
}
double Driver::externalTemperature(Unit u)
{
assertQuantity(u, Quantity::Temperature);
return convert(external_temperature_c_, Unit::C, u);
}
}
// Test: HydrusWater hydrus 64646464 NOKEY
// Comment:
// telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":10.4,"external_temperature_c":0,"current_date":"","total_at_date_m3":0,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2000-00-00 00:00","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":13.686516,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusWater;64646464;1.174;0;OK;1111-11-11 11:11.11
// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"flow_m3h":0,"flow_temperature_c":10.4,"remaining_battery_life_y":13.686797,"status":"OK","at_datetime":"2019-10-31 23:59","total_at_date_m3": 0.2,"timestamp":"1111-11-11T11:11:11Z"}
// |HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11
// Test: HydrusVater hydrus 65656565 NOKEY
// Comment:
// telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","total_m3":3.45,"total_tariff1_m3":0,"total_tariff2_m3":0,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":0,"current_date":"","total_at_date_m3":3.431,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":0,"at_date":"2020-09-13 23:59","actuality_duration_s":0,"operating_time_h":0,"remaining_battery_life_y":15.321013,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","flow_m3h":0,"customer": "A20B410178","total_m3":3.45,"total_at_date_m3":3.431,"remaining_battery_life_y":15.321328,"at_datetime":"2020-09-13 23:59","total_at_date_m3": 3.431,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11
// Test: HydrusAES hydrus 64745666 NOKEY
// Comment:
// telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C|
// {"media":"water","meter":"hydrus","name":"HydrusAES","id":"64745666","total_m3":137.291,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"max_flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"current_date":"2021-01-23 08:27","total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31 00:00","actuality_duration_s":6673,"operating_time_h":14678,"remaining_battery_life_y":0,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// {"media":"water","meter":"hydrus","name":"HydrusAES","id":"64745666","total_m3":137.291,"total_tariff1_m3":0,"total_tariff2_m3":137.291,"flow_m3h":0,"flow_temperature_c":24.5,"external_temperature_c":23.9,"total_at_date_m3":128.638,"total_tariff1_at_date_m3":0,"total_tariff2_at_date_m3":128.638,"at_date":"2020-12-31","actuality_duration_s":6673,"operating_time_h":14678,"meter_datetime": "2021-01-23 08:27","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusAES;64745666;137.291;128.638;OK;1111-11-11 11:11.11
// Test: HydrusIzarRS hydrus 60897379 NOKEY
// Comment: Hydrus IZAR RS 868 water meter
// telegram=|1E4424238B07797389607A8F00107D_041312170100426CBF23441344100100|
// {"media":"water","meter":"hydrus","name":"HydrusIzarRS","id":"60897379","total_m3":71.442,"at_date": "2021-03-31","total_at_date_m3":69.7,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusIzarRS;60897379;71.442;69.7;OK;1111-11-11 11:11.11
// Test: HydrusIzarRSWarm hydrus 60904720 NOKEY
// Comment: Hydrus IZAR RS 868 water meter warm
// telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000|
// {"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_at_date_m3":48.002,"at_date":"2021-03-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
// |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11

Wyświetl plik

@ -1,6 +1,6 @@
/*
Copyright (C) 2019 Jacek Tomasiak (gpl-3.0-or-later)
Copyright (C) 2020-2022 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2020-2023 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2021 Vincent Privat (gpl-3.0-or-later)
This program is free software: you can redistribute it and/or modify
@ -23,7 +23,8 @@
namespace
{
/** Contains all the booleans required to store the alarms of a PRIOS device. */
typedef struct _izar_alarms {
struct IzarAlarms
{
bool general_alarm;
bool leakage_currently;
bool leakage_previously;
@ -36,7 +37,7 @@ namespace
bool sensor_fraud_previously;
bool mechanical_fraud_currently;
bool mechanical_fraud_previously;
} izar_alarms;
};
struct Driver : public virtual MeterCommonImplementation
{
@ -46,27 +47,11 @@ namespace
private:
double totalWaterConsumption(Unit u);
string serialNumber();
double lastMonthTotalWaterConsumption(Unit u);
string setH0Date();
string currentAlarmsText();
string previousAlarmsText();
string currentAlarmsText(IzarAlarms &alarms);
string previousAlarmsText(IzarAlarms &alarms);
vector<uchar> decodePrios(const vector<uchar> &origin, const vector<uchar> &payload, uint32_t key);
string prefix;
uint32_t serial_number {0};
double remaining_battery_life;
uint16_t h0_year;
uint8_t h0_month;
uint8_t h0_day;
double total_water_consumption_l_ {};
double last_month_total_water_consumption_l_ {};
uint32_t transmit_period_s_ {};
uint16_t manufacture_year {0};
izar_alarms alarms;
vector<uint32_t> keys;
};
@ -89,6 +74,7 @@ namespace
di.addDetection(MANUFACTURER_DME, 0x07, 0x78);
di.addDetection(MANUFACTURER_DME, 0x06, 0x78);
di.addDetection(MANUFACTURER_HYD, 0x07, 0x86);
di.usesProcessContent();
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
});
@ -97,84 +83,52 @@ namespace
{
initializeDiehlDefaultKeySupport(meterKeys()->confidentiality_key, keys);
addPrint("prefix", Quantity::Text,
[&](){ return prefix; },
"The alphanumeric prefix printed before serial number on device.",
DEFAULT_PRINT_PROPERTIES);
addStringField("prefix",
"The alphanumeric prefix printed before serial number on device.",
DEFAULT_PRINT_PROPERTIES);
addPrint("serial_number", Quantity::Text,
[&](){ return serialNumber(); },
"The meter serial number.",
DEFAULT_PRINT_PROPERTIES);
addStringField("serial_number",
"The meter serial number.",
DEFAULT_PRINT_PROPERTIES);
addPrint("total", Quantity::Volume,
[&](Unit u){ return totalWaterConsumption(u); },
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.");
addPrint("last_month_total", Quantity::Volume,
[&](Unit u){ return lastMonthTotalWaterConsumption(u); },
"The total water consumption recorded by this meter around end of last month.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("last_month_total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter around end of last month.");
addPrint("last_month_measure_date", Quantity::Text,
[&](){ return setH0Date(); },
"The date when the meter recorded the most recent billing value.",
DEFAULT_PRINT_PROPERTIES);
addStringField("last_month_measure_date",
"The date when the meter recorded the most recent billing value.",
DEFAULT_PRINT_PROPERTIES);
addPrint("remaining_battery_life", Quantity::Time, Unit::Year,
[&](Unit u){ return convert(remaining_battery_life, Unit::Year, u); },
"How many more years the battery is expected to last",
DEFAULT_PRINT_PROPERTIES);
addNumericField("remaining_battery_life",
Quantity::Time,
DEFAULT_PRINT_PROPERTIES,
"How many more years the battery is expected to last",
Unit::Year);
addStringField("current_alarms",
"Alarms currently reported by the meter.",
DEFAULT_PRINT_PROPERTIES);
addPrint("current_alarms", Quantity::Text,
[&](){ return currentAlarmsText(); },
"Alarms currently reported by the meter.",
DEFAULT_PRINT_PROPERTIES);
addStringField("previous_alarms",
"Alarms previously reported by the meter.",
DEFAULT_PRINT_PROPERTIES);
addPrint("previous_alarms", Quantity::Text,
[&](){ return previousAlarmsText(); },
"Alarms previously reported by the meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("transmit_period", Quantity::Time,
DEFAULT_PRINT_PROPERTIES,
"The period at which the meter transmits its data.",
Unit::Second);
addPrint("transmit_period", Quantity::Time, Unit::Second,
[&](Unit u){ return convert(transmit_period_s_, Unit::Second, u); },
"The period at which the meter transmits its data.",
DEFAULT_PRINT_PROPERTIES);
addPrint("manufacture_year", Quantity::Text,
[&](){ return to_string(manufacture_year); },
"The year during which the meter was manufactured.",
DEFAULT_PRINT_PROPERTIES);
addStringField("manufacture_year",
"The year during which the meter was manufactured.",
DEFAULT_PRINT_PROPERTIES);
}
double Driver::totalWaterConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_l_, Unit::L, u);
}
double Driver::lastMonthTotalWaterConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(last_month_total_water_consumption_l_, Unit::L, u);
}
string Driver::setH0Date()
{
string date;
strprintf(&date, "%d-%02d-%02d", h0_year, h0_month%99, h0_day%99);
return date;
}
string Driver::serialNumber()
{
string result;
strprintf(&result, "%06d", serial_number);
return result;
}
string Driver::currentAlarmsText()
string Driver::currentAlarmsText(IzarAlarms &alarms)
{
string s;
if (alarms.leakage_currently) {
@ -211,7 +165,7 @@ namespace
return "no_alarm";
}
string Driver::previousAlarmsText()
string Driver::previousAlarmsText(IzarAlarms &alarms)
{
string s;
if (alarms.leakage_previously) {
@ -259,35 +213,50 @@ namespace
string digits = to_string((origin[7] & 0x03) << 24 | origin[6] << 16 | origin[5] << 8 | origin[4]);
// get the manufacture year
uint8_t yy = atoi(digits.substr(0, 2).c_str());
manufacture_year = yy > 70 ? (1900 + yy) : (2000 + yy); // Maybe to adjust in 2070, if this code stills lives :D
int manufacture_year = yy > 70 ? (1900 + yy) : (2000 + yy); // Maybe to adjust in 2070, if this code stills lives :D
setStringValue("manufacture_year", tostrprintf("%d", manufacture_year));
// get the serial number
serial_number = atoi(digits.substr(2, digits.size()).c_str());
uint32_t serial_number = atoi(digits.substr(2, digits.size()).c_str());
setStringValue("serial_number", tostrprintf("%06d", serial_number));
// get letters
uchar supplier_code = '@' + (((origin[9] & 0x0F) << 1) | (origin[8] >> 7));
uchar meter_type = '@' + ((origin[8] & 0x7C) >> 2);
uchar diameter = '@' + (((origin[8] & 0x03) << 3) | (origin[7] >> 5));
// build the prefix
strprintf(&prefix, "%c%02d%c%c", supplier_code, yy, meter_type, diameter);
string prefix = tostrprintf("%c%02d%c%c", supplier_code, yy, meter_type, diameter);
setStringValue("prefix", prefix);
}
// get the remaining battery life (in year) and transmission period (in seconds)
remaining_battery_life = (frame[12] & 0x1F) / 2.0;
transmit_period_s_ = 1 << ((frame[11] & 0x0F) + 2);
double remaining_battery_life = (frame[12] & 0x1F) / 2.0;
setNumericValue("remaining_battery_life", Unit::Year, remaining_battery_life);
total_water_consumption_l_ = uint32FromBytes(decoded_content, 1, true);
last_month_total_water_consumption_l_ = uint32FromBytes(decoded_content, 5, true);
int transmit_period_s = 1 << ((frame[11] & 0x0F) + 2);
setNumericValue("transmit_period", Unit::Second, transmit_period_s);
double total_water_consumption_l_ = uint32FromBytes(decoded_content, 1, true);
setNumericValue("total", Unit::L, total_water_consumption_l_);
double last_month_total_water_consumption_l_ = uint32FromBytes(decoded_content, 5, true);
setNumericValue("last_month_total", Unit::L, last_month_total_water_consumption_l_);
// get the date when the second measurement was taken
h0_year = ((decoded_content[10] & 0xF0) >> 1) + ((decoded_content[9] & 0xE0) >> 5);
uint16_t h0_year = ((decoded_content[10] & 0xF0) >> 1) + ((decoded_content[9] & 0xE0) >> 5);
if (h0_year > 80) {
h0_year += 1900;
} else {
h0_year += 2000;
}
h0_month = decoded_content[10] & 0xF;
h0_day = decoded_content[9] & 0x1F;
uint8_t h0_month = decoded_content[10] & 0xF;
uint8_t h0_day = decoded_content[9] & 0x1F;
setStringValue("last_month_measure_date", tostrprintf("%d-%02d-%02d", h0_year, h0_month%99, h0_day%99));
// read the alarms:
IzarAlarms alarms {};
alarms.general_alarm = frame[11] >> 7;
alarms.leakage_currently = frame[12] >> 7;
alarms.leakage_previously = frame[12] >> 6 & 0x1;
@ -300,6 +269,9 @@ namespace
alarms.sensor_fraud_previously = frame[13] >> 2 & 0x1;
alarms.mechanical_fraud_currently = frame[13] >> 1 & 0x1;
alarms.mechanical_fraud_previously = frame[13] & 0x1;
setStringValue("current_alarms", currentAlarmsText(alarms));
setStringValue("previous_alarms", previousAlarmsText(alarms));
}
vector<uchar> Driver::decodePrios(const vector<uchar> &origin, const vector<uchar> &frame, uint32_t key)
@ -309,19 +281,19 @@ namespace
}
// Test: IzarWater izar 21242472 NOKEY
// telegram=|1944304C72242421D401A2|013D4013DD8B46A4999C1293E582CC|
// telegram=|1944304C72242421D401A2_013D4013DD8B46A4999C1293E582CC|
// {"media":"water","meter":"izar","name":"IzarWater","id":"21242472","prefix":"C19UA","serial_number":"145842","total_m3":3.488,"last_month_total_m3":3.486,"last_month_measure_date":"2019-09-30","remaining_battery_life_y":14.5,"current_alarms":"meter_blocked,underflow","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"2019","timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater;21242472;C19UA;145842;3.488;3.486;2019-09-30;14.5;meter_blocked,underflow;no_alarm;8;2019;1111-11-11 11:11.11
// Test: IzarWater2 izar 66236629 NOKEY
// telegram=|2944A511780729662366A20118001378D3B3DB8CEDD77731F25832AAF3DA8CADF9774EA673172E8C61F2|
// {"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","prefix":"","serial_number":"000000","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater2;66236629;;000000;16.76;11.84;2019-11-30;12;no_alarm;no_alarm;8;0;1111-11-11 11:11.11
// {"media":"water","meter":"izar","name":"IzarWater2","id":"66236629","total_m3":16.76,"last_month_total_m3":11.84,"last_month_measure_date":"2019-11-30","remaining_battery_life_y":12,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater2;66236629;null;null;16.76;11.84;2019-11-30;12;no_alarm;no_alarm;8;null;1111-11-11 11:11.11
// Test: IzarWater3 izar 20481979 NOKEY
// telegram=|1944A511780779194820A1|21170013355F8EDB2D03C6912B1E37
// {"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","prefix":"","serial_number":"000000","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater3;20481979;;000000;4.366;0;2020-12-31;11.5;no_alarm;no_alarm;8;0;1111-11-11 11:11.11
// telegram=|1944A511780779194820A1_21170013355F8EDB2D03C6912B1E37
// {"media":"water","meter":"izar","name":"IzarWater3","id":"20481979","total_m3":4.366,"last_month_total_m3":0,"last_month_measure_date":"2020-12-31","remaining_battery_life_y":11.5,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater3;20481979;null;null;4.366;0;2020-12-31;11.5;no_alarm;no_alarm;8;null;1111-11-11 11:11.11
// Test: IzarWater4 izar 2124589c NOKEY
// Comment: With mfct specific tpl ci field a3.
@ -331,11 +303,11 @@ namespace
// Test: IzarWater5 izar 20e4ffde NOKEY
// Comment: Ensure non-regression on manufacture year parsing
// telegram=|1944304CDEFFE420CC01A2|63120013258F907B0AFF12529AC33B|
// telegram=|1944304CDEFFE420CC01A2_63120013258F907B0AFF12529AC33B|
// {"media":"water","meter":"izar","name":"IzarWater5","id":"20e4ffde","prefix":"C15SA","serial_number":"007710","total_m3":159.832,"last_month_total_m3":157.76,"last_month_measure_date":"2021-02-01","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"no_alarm","transmit_period_s":32,"manufacture_year":"2015","timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater5;20e4ffde;C15SA;007710;159.832;157.76;2021-02-01;9;no_alarm;no_alarm;32;2015;1111-11-11 11:11.11
// Test: IzarWater6 izar 48500375 NOKEY
// telegram=|19442423860775035048A251520015BEB6B2E1ED623A18FC74A5|
// {"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","prefix":"","serial_number":"000000","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"manufacture_year":"0","timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater6;48500375;;000000;521.602;519.147;2021-11-15;9;no_alarm;leakage;8;0;1111-11-11 11:11.11
// {"media":"water","meter":"izar","name":"IzarWater6","id":"48500375","total_m3":521.602,"last_month_total_m3":519.147,"last_month_measure_date":"2021-11-15","remaining_battery_life_y":9,"current_alarms":"no_alarm","previous_alarms":"leakage","transmit_period_s":8,"timestamp":"1111-11-11T11:11:11Z"}
// |IzarWater6;48500375;null;null;521.602;519.147;2021-11-15;9;no_alarm;leakage;8;null;1111-11-11 11:11.11

Wyświetl plik

@ -17,144 +17,83 @@
#include"meters_common_implementation.h"
#define INFO_CODE_CLOSED 0x0011
#define INFO_CODE_OPEN 0x0055
struct MeterLansenDW : public virtual MeterCommonImplementation
namespace
{
MeterLansenDW(MeterInfo &mi, DriverInfo &di);
private:
void processContent(Telegram *t);
string status();
uint16_t info_codes_ {};
double pulse_counter_a_ {};
double pulse_counter_b_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("lansendw");
di.setDefaultFields("name,id,status,timestamp");
di.setMeterType(MeterType::DoorWindowDetector);
di.addLinkMode(LinkMode::T1);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new MeterLansenDW(mi, di)); });
di.addDetection(MANUFACTURER_LAS, 0x1d, 0x07);
});
MeterLansenDW::MeterLansenDW(MeterInfo &mi, DriverInfo &di) :
MeterCommonImplementation(mi, di)
{
setMeterType(MeterType::DoorWindowDetector);
setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
addLinkMode(LinkMode::T1);
addPrint("status", Quantity::Text,
[&](){ return status(); },
"The current status: OPEN or CLOSED.",
DEFAULT_PRINT_PROPERTIES);
addPrint("a", Quantity::Counter,
[&](Unit u) { assertQuantity(u, Quantity::Counter); return pulse_counter_a_; },
"How many times the door/window has been opened or closed.",
DEFAULT_PRINT_PROPERTIES);
addPrint("b", Quantity::Counter,
[&](Unit u) { assertQuantity(u, Quantity::Counter); return pulse_counter_b_; },
"The current number of counted pulses from counter b.",
DEFAULT_PRINT_PROPERTIES);
}
shared_ptr<Meter> createLansenDW(MeterInfo &mi)
{
DriverInfo di;
di.setName("lansendw");
return shared_ptr<Meter>(new MeterLansenDW(mi, di));
}
void MeterLansenDW::processContent(Telegram *t)
{
/*
(wmbus) 11: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 12: FD vif (Second extension of VIF-codes)
(wmbus) 13: 1B vife (Digital Input (binary))
(wmbus) 14: 1100
(wmbus) 16: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 17: FD vif (Second extension of VIF-codes)
(wmbus) 18: 97 vife (Error flags (binary))
(wmbus) 19: 1D vife (Response delay time [bittimes])
(wmbus) 1a: 0100
(wmbus) 1c: 0E dif (12 digit BCD Instantaneous value)
(wmbus) 1d: FD vif (Second extension of VIF-codes)
(wmbus) 1e: 3A vife (Dimensionless / no VIF)
(wmbus) 1f: 220000000000
(wmbus) 25: 8E dif (12 digit BCD Instantaneous value)
(wmbus) 26: 40 dife (subunit=1 tariff=0 storagenr=0)
(wmbus) 27: FD vif (Second extension of VIF-codes)
(wmbus) 28: 3A vife (Dimensionless / no VIF)
(wmbus) 29: 000000000000
*/
/*
(wmbus) 11: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 12: FD vif (Second extension of VIF-codes)
(wmbus) 13: 1B vife (Digital Input (binary))
(wmbus) 14: 5500
(wmbus) 16: 02 dif (16 Bit Integer/Binary Instantaneous value)
(wmbus) 17: FD vif (Second extension of VIF-codes)
(wmbus) 18: 97 vife (Error flags (binary))
(wmbus) 19: 1D vife (Response delay time [bittimes])
(wmbus) 1a: 0100
(wmbus) 1c: 0E dif (12 digit BCD Instantaneous value)
(wmbus) 1d: FD vif (Second extension of VIF-codes)
(wmbus) 1e: 3A vife (Dimensionless / no VIF)
(wmbus) 1f: 230000000000
(wmbus) 25: 8E dif (12 digit BCD Instantaneous value)
(wmbus) 26: 40 dife (subunit=1 tariff=0 storagenr=0)
(wmbus) 27: FD vif (Second extension of VIF-codes)
(wmbus) 28: 3A vife (Dimensionless / no VIF)
(wmbus) 29: 000000000000
*/
int offset;
if (extractDVuint16(&t->dv_entries, "02FD1B", &offset, &info_codes_))
struct Driver : public virtual MeterCommonImplementation
{
t->addMoreExplanation(offset, renderJsonOnlyDefaultUnit("status", Quantity::Text));
}
Driver(MeterInfo &mi, DriverInfo &di);
};
if (extractDVdouble(&t->dv_entries, "0EFD3A", &offset, &pulse_counter_a_, false))
static bool ok = registerDriver([](DriverInfo&di)
{
t->addMoreExplanation(offset, renderJsonOnlyDefaultUnit("counter_a", Quantity::Counter));
}
di.setName("lansendw");
di.setDefaultFields("name,id,status,timestamp");
di.setMeterType(MeterType::DoorWindowDetector);
di.addLinkMode(LinkMode::T1);
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
di.addDetection(MANUFACTURER_LAS, 0x1d, 0x07);
});
if (extractDVdouble(&t->dv_entries, "8E40FD3A", &offset, &pulse_counter_b_, false))
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
t->addMoreExplanation(offset, renderJsonOnlyDefaultUnit("counter_b", Quantity::Counter));
addStringFieldWithExtractorAndLookup(
"status",
"The state (OPEN/CLOSED) for the door/window.",
DEFAULT_PRINT_PROPERTIES,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::DigitalInput),
Translate::Lookup()
.add(Translate::Rule("INPUT_BITS", Translate::Type::IndexToString)
.set(MaskBits(0xffff))
.add(Translate::Map(0x11 ,"CLOSED", TestBit::Set))
.add(Translate::Map(0x55 ,"OPEN", TestBit::Set))
));
addStringFieldWithExtractorAndLookup(
"error_flags",
"Error flags.",
PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ErrorFlags),
Translate::Lookup()
.add(Translate::Rule("ERROR_FLAGS", Translate::Type::BitToString)
.set(MaskBits(0xffff))
.set(DefaultMessage("OK"))
));
addNumericFieldWithExtractor(
"a",
"How many times the door/window has been opened or closed.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Counter,
VifScaling::None,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Dimensionless)
);
addNumericFieldWithExtractor(
"b",
"The current number of counted pulses from counter b.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Counter,
VifScaling::None,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::Dimensionless)
.set(SubUnitNr(1))
);
}
}
string MeterLansenDW::status()
{
string s;
if (info_codes_ == INFO_CODE_OPEN)
{
s.append("OPEN");
}
else if (info_codes_ == INFO_CODE_CLOSED)
{
s.append("CLOSED");
}
else
{
s.append("ERR");
}
// Test: Dooro lansendw 00010205 NOKEY
// telegram=|2e44333005020100071d7ab54800002f2f02fd1b110002fd971d01000efd3a2200000000008e40fd3a000000000000|
// {"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"CLOSED","a_counter":22,"b_counter":0,"error_flags":"ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_40","timestamp":"1111-11-11T11:11:11Z"}
// |Dooro;00010205;CLOSED;1111-11-11 11:11.11
return s;
}
// telegram=|2e44333005020100071d7ab66800002f2f02fd1b550002fd971d01000efd3a2300000000008e40fd3a000000000000|
// {"media":"reserved","meter":"lansendw","name":"Dooro","id":"00010205","status":"OPEN","a_counter":23,"b_counter":0,"error_flags":"ERROR_FLAGS_1 PERMANENT_ERROR UNKNOWN_60","timestamp":"1111-11-11T11:11:11Z"}
// |Dooro;00010205;OPEN;1111-11-11 11:11.11

Wyświetl plik

@ -1,5 +1,5 @@
/*
Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2019-2023 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
@ -24,20 +24,6 @@ namespace
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
private:
double totalWaterConsumption(Unit u);
bool hasTotalWaterConsumption();
double targetWaterConsumption(Unit u);
bool hasTargetWaterConsumption();
string currentDate();
string previousDate();
double total_water_consumption_m3_ {};
double target_water_consumption_m3_ {};
string current_date_ {};
string previous_date_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -54,25 +40,23 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("total", Quantity::Volume,
[&](Unit u){ return totalWaterConsumption(u); },
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.");
addPrint("target", Quantity::Volume,
[&](Unit u){ return targetWaterConsumption(u); },
"The total water consumption recorded at the beginning of this month.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("target",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded at the beginning of this month.");
addPrint("current_date", Quantity::Text,
[&](){ return currentDate(); },
"Date of current billing period.",
DEFAULT_PRINT_PROPERTIES);
addStringField("current_date",
"Date of current billing period.",
DEFAULT_PRINT_PROPERTIES);
addPrint("prev_date", Quantity::Text,
[&](){ return previousDate(); },
"Date of previous billing period.",
DEFAULT_PRINT_PROPERTIES);
addStringField("prev_date",
"Date of previous billing period.",
DEFAULT_PRINT_PROPERTIES);
}
void Driver::processContent(Telegram *t)
@ -91,83 +75,31 @@ namespace
uint prev_date_day = (prev_date >> 0) & 0x1F;
uint prev_date_month = (prev_date >> 5) & 0x0F;
uint prev_date_year = (prev_date >> 9) & 0x3F;
strprintf(&previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day);
string prev_date_str;
strprintf(&prev_date_str, "%04x", prev_date);
uint offset = t->parsed.size() + 1;
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x6c, {}, 0, 0, 0, prev_date_str) };
t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str());
setStringValue("prev_date",
tostrprintf("%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day));
// Previous consumption
uchar prev_lo = content[3];
uchar prev_hi = content[4];
double prev = (256.0*prev_hi+prev_lo)/10.0;
string prevs;
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
offset = t->parsed.size()+3;
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " prev consumption (%f m3)", prev);
// Current date
uint16_t current_date = (content[6] << 8) | content[5];
uint current_date_day = (current_date >> 4) & 0x1F;
uint current_date_month = (current_date >> 9) & 0x0F;
strprintf(&current_date_, "%s-%02d-%02dT02:00:00Z", currentYear().c_str(), current_date_month, current_date_day);
string current_date_str;
strprintf(&current_date_str, "%04x", current_date);
offset = t->parsed.size() + 5;
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x6c, {}, 0, 0, 0, current_date_str) };
t->explanations.push_back(Explanation(offset, 1, current_date_str, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " current date (%s)", current_date_.c_str());
setStringValue("current_date",
tostrprintf("%s-%02d-%02dT02:00:00Z",
currentYear().c_str(), current_date_month, current_date_day));
// Current consumption
uchar curr_lo = content[7];
uchar curr_hi = content[8];
double curr = (256.0*curr_hi+curr_lo)/10.0;
string currs;
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
offset = t->parsed.size()+7;
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " curr consumption (%f m3)", curr);
total_water_consumption_m3_ = prev+curr;
target_water_consumption_m3_ = prev;
}
double Driver::totalWaterConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_m3_, Unit::M3, u);
}
bool Driver::hasTotalWaterConsumption()
{
return true;
}
double Driver::targetWaterConsumption(Unit u)
{
return target_water_consumption_m3_;
}
bool Driver::hasTargetWaterConsumption()
{
return true;
}
string Driver::currentDate() {
return current_date_;
}
string Driver::previousDate() {
return previous_date_;
double total_water_consumption_m3 = prev+curr;
setNumericValue("total", Unit::M3, total_water_consumption_m3);
double target_water_consumption_m3 = prev;
setNumericValue("target", Unit::M3, target_water_consumption_m3);
}
}

Wyświetl plik

@ -1,5 +1,5 @@
/*
Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2019-2023 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
@ -24,14 +24,6 @@ namespace
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
private:
double totalWaterConsumption(Unit u);
double targetWaterConsumption(Unit u);
double total_water_consumption_m3_ {};
double target_water_consumption_m3_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
@ -50,20 +42,20 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
addPrint("total", Quantity::Volume,
[&](Unit u){ return totalWaterConsumption(u); },
"The total water consumption recorded by this meter.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("total",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded by this meter.");
addPrint("target", Quantity::Volume,
[&](Unit u){ return targetWaterConsumption(u); },
"The total water consumption recorded at the beginning of this month.",
DEFAULT_PRINT_PROPERTIES);
addNumericField("target",
Quantity::Volume,
DEFAULT_PRINT_PROPERTIES,
"The total water consumption recorded at the beginning of this month.");
}
void Driver::processContent(Telegram *t)
{
// Unfortunately, the MK Radio 4 is mostly a proprieatary protocol
// The MK Radio 4 is mostly a proprietary protocol
// simple wrapped inside a wmbus telegram since the ci-field is 0xa2.
// Which means that the entire payload is manufacturer specific.
@ -76,37 +68,32 @@ namespace
uchar prev_hi = content[4];
double prev = (256.0*prev_hi+prev_lo)/10.0;
/*
string prevs;
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
int offset = t->parsed.size()+3;
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
t->explanations.push_back(Explanation(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " prev consumption (%f m3)", prev);
*/
uchar curr_lo = content[7];
uchar curr_hi = content[8];
double curr = (256.0*curr_hi+curr_lo)/10.0;
/*
string currs;
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
offset = t->parsed.size()+7;
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
t->explanations.push_back(Explanation(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " curr consumption (%f m3)", curr);
*/
total_water_consumption_m3_ = prev+curr;
target_water_consumption_m3_ = prev;
}
double Driver::totalWaterConsumption(Unit u)
{
assertQuantity(u, Quantity::Volume);
return convert(total_water_consumption_m3_, Unit::M3, u);
}
double Driver::targetWaterConsumption(Unit u)
{
return target_water_consumption_m3_;
double total_water_consumption_m3 = prev+curr;
setNumericValue("total", Unit::M3, total_water_consumption_m3);
double target_water_consumption_m3 = prev;
setNumericValue("target", Unit::M3, target_water_consumption_m3);
}
}

Wyświetl plik

@ -1,5 +1,5 @@
/*
Copyright (C) 2020-2022 Fredrik Öhrström (gpl-3.0-or-later)
Copyright (C) 2020-2023 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
@ -24,23 +24,13 @@ namespace
Driver(MeterInfo &mi, DriverInfo &di);
void processContent(Telegram *t);
private:
string status();
bool smokeDetected();
string previousDate();
uint16_t info_codes_ {};
bool error_ {};
string previous_date_ {};
};
static bool ok = registerDriver([](DriverInfo&di)
{
di.setName("tsd2");
di.setDefaultFields("name,id,status,prev_date,timestamp");
di.setMeterType(MeterType::WaterMeter);
di.setMeterType(MeterType::SmokeDetector);
di.addLinkMode(LinkMode::T1);
di.addDetection(MANUFACTURER_TCH, 0xf0, 0x76);
@ -49,88 +39,58 @@ namespace
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
{
setMeterType(MeterType::SmokeDetector);
addStringField("status",
"The current status: OK, SMOKE or ERROR.",
DEFAULT_PRINT_PROPERTIES);
setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
addLinkMode(LinkMode::T1);
addPrint("status", Quantity::Text,
[&](){ return status(); },
"The current status: OK, SMOKE or ERROR.",
DEFAULT_PRINT_PROPERTIES);
addPrint("prev_date", Quantity::Text,
[&](){ return previousDate(); },
"Date of previous billing period.",
DEFAULT_PRINT_PROPERTIES);
addStringField("prev_date",
"Date of previous billing period.",
DEFAULT_PRINT_PROPERTIES);
}
#define INFO_CODE_SMOKE 0x0001
bool Driver::smokeDetected()
{
return info_codes_ & INFO_CODE_SMOKE;
}
#define INFO_CODE_SMOKE 0x01
void Driver::processContent(Telegram *t)
{
vector<uchar> data;
t->extractPayload(&data);
if(data.empty())
if(data.size() < 3)
{
error_ = true;
setStringValue("status", "ERROR");
return;
}
info_codes_ = data[0];
error_ = false;
// Check CRC etc here....
uint8_t status = data[0];
if (status & INFO_CODE_SMOKE)
{
setStringValue("status", "SMOKE");
}
else
{
setStringValue("status", "OK");
}
// Previous date
uint16_t prev_date = (data[2] << 8) | data[1];
uint prev_date_day = (prev_date >> 0) & 0x1F;
uint prev_date_month = (prev_date >> 5) & 0x0F;
uint prev_date_year = (prev_date >> 9) & 0x3F;
strprintf(&previous_date_, "%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day);
string prev_date_str;
strprintf(&prev_date_str, "%04x", prev_date);
uint offset = t->parsed.size() + 1;
t->explanations.push_back(Explanation(offset, 1, prev_date_str, KindOfData::CONTENT, Understanding::FULL));
t->addMoreExplanation(offset, " previous date (%s)", previous_date_.c_str());
}
string Driver::status()
{
string s;
bool smoke = info_codes_ & INFO_CODE_SMOKE;
if (smoke)
{
s.append("SMOKE ");
}
if (error_)
{
s.append("ERROR ");
}
if (s.length() > 0)
{
s.pop_back();
return s;
}
return "OK";
}
string Driver::previousDate()
{
return previous_date_;
setStringValue("prev_date",
tostrprintf("%d-%02d-%02dT02:00:00Z", prev_date_year+2000, prev_date_month, prev_date_day));
}
}
// Test: Smokey tsd2 91633569 NOKEY
// telegram=|294468506935639176F0A0_019F|
// {"media":"smoke detector","meter":"tsd2","name":"Smokey","id":"91633569","status":"ERROR","timestamp":"1111-11-11T11:11:11Z"}
// |Smokey;91633569;ERROR;null;1111-11-11 11:11.11
// telegram=|294468506935639176F0A0_009F2782290060822900000401D6311AF93E1BF93E008DC3009ED4000FE500|
// {"media":"smoke detector","meter":"tsd2","name":"Smokey","id":"91633569","status":"OK","prev_date":"2019-12-31T02:00:00Z","timestamp":"1111-11-11T11:11:11Z"}
// |Smokey;91633569;OK;2019-12-31T02:00:00Z;1111-11-11 11:11.11
// telegram=|294468506935639176F0A0_019F2782290060822900000401D6311AF93E1BF93E008DC3009ED4000FE500|
// {"media":"smoke detector","meter":"tsd2","name":"Smokey","id":"91633569","status":"SMOKE","prev_date":"2019-12-31T02:00:00Z","timestamp":"1111-11-11T11:11:11Z"}
// |Smokey;91633569;SMOKE;2019-12-31T02:00:00Z;1111-11-11 11:11.11

Wyświetl plik

@ -302,70 +302,6 @@ void MeterCommonImplementation::setMfctTPLStatusBits(Translate::Lookup &lookup)
mfct_tpl_status_bits_ = lookup;
}
void MeterCommonImplementation::addPrint(string vname, Quantity vquantity,
function<double(Unit)> getValueFunc, string help, PrintProperties pprops)
{
field_infos_.emplace_back(
FieldInfo(field_infos_.size(),
vname,
vquantity,
defaultUnitForQuantity(vquantity),
VifScaling::Auto,
FieldMatcher(),
help,
pprops,
getValueFunc,
NULL,
NULL,
NULL,
NoLookup, /* Lookup table */
NULL /* Formula */
));
}
void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, Unit unit,
function<double(Unit)> getValueFunc, string help, PrintProperties pprops)
{
field_infos_.emplace_back(
FieldInfo(field_infos_.size(),
vname,
vquantity,
unit,
VifScaling::Auto,
FieldMatcher(),
help,
pprops,
getValueFunc,
NULL,
NULL,
NULL,
NoLookup, /* Lookup table */
NULL /* Formula */
));
}
void MeterCommonImplementation::addPrint(string vname, Quantity vquantity,
function<string()> getValueFunc,
string help, PrintProperties pprops)
{
field_infos_.emplace_back(
FieldInfo(field_infos_.size(),
vname,
vquantity,
defaultUnitForQuantity(vquantity),
VifScaling::Auto,
FieldMatcher(),
help,
pprops,
NULL,
getValueFunc,
NULL,
NULL,
NoLookup, /* Lookup table */
NULL /* Formula */
));
}
void MeterCommonImplementation::addNumericFieldWithExtractor(string vname,
string help,
PrintProperties print_properties,
@ -469,31 +405,6 @@ void MeterCommonImplementation::addNumericFieldWithCalculatorAndMatcher(string v
));
}
void MeterCommonImplementation::addNumericField(
string vname,
Quantity vquantity,
PrintProperties print_properties,
string help,
function<void(Unit,double)> setValueFunc,
function<double(Unit)> getValueFunc)
{
field_infos_.emplace_back(
FieldInfo(field_infos_.size(),
vname,
vquantity,
defaultUnitForQuantity(vquantity),
VifScaling::None,
FieldMatcher(),
help,
print_properties,
getValueFunc,
NULL,
setValueFunc,
NULL,
NoLookup, /* Lookup table */
NULL /* Formula */
));
}
void MeterCommonImplementation::addNumericField(
string vname,
@ -543,39 +454,6 @@ void MeterCommonImplementation::addStringFieldWithExtractor(string vname,
));
}
void MeterCommonImplementation::addStringFieldWithExtractorAndLookup(
string vname,
Quantity vquantity,
DifVifKey dif_vif_key,
MeasurementType mt,
VIFRange vi,
StorageNr s,
TariffNr t,
IndexNr i,
PrintProperties print_properties,
string help,
function<void(string)> setValueFunc,
function<string()> getValueFunc,
Translate::Lookup lookup)
{
field_infos_.emplace_back(
FieldInfo(field_infos_.size(),
vname,
vquantity,
defaultUnitForQuantity(vquantity),
VifScaling::None,
FieldMatcher::build().set(dif_vif_key).set(mt).set(vi).set(s).set(t).set(i),
help,
print_properties,
NULL,
getValueFunc,
NULL,
setValueFunc,
lookup, /* Lookup table */
NULL /* Formula */
));
}
void MeterCommonImplementation::addStringFieldWithExtractorAndLookup(string vname,
string help,
PrintProperties print_properties,
@ -2566,6 +2444,35 @@ void MeterCommonImplementation::addOptionalCommonFields(string field_names)
{
set<string> fields = splitStringIntoSet(field_names, ',');
if (checkIf(fields, "actuality_duration_s"))
{
addNumericFieldWithExtractor(
"actuality_duration",
"Lapsed time between measurement and transmission.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ActualityDuration),
Unit::Second
);
}
if (checkIf(fields, "actuality_duration_h"))
{
addNumericFieldWithExtractor(
"actuality_duration",
"Lapsed time between measurement and transmission.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Time,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ActualityDuration)
);
}
if (checkIf(fields, "fabrication_no"))
{
addStringFieldWithExtractor(
@ -2805,6 +2712,20 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields(string field_names)
);
}
if (checkIf(fields,"external_temperature_c"))
{
addNumericFieldWithExtractor(
"external_temperature",
"Temperature outside of meter.",
DEFAULT_PRINT_PROPERTIES,
Quantity::Temperature,
VifScaling::Auto,
FieldMatcher::build()
.set(MeasurementType::Instantaneous)
.set(VIFRange::ExternalTemperature)
);
}
if (checkIf(fields,"return_temperature_c"))
{
addNumericFieldWithExtractor(

Wyświetl plik

@ -182,6 +182,7 @@ private:
vector<DriverDetect> detect_;
vector<string> default_fields_;
int force_mfct_index_ = -1; // Used for meters not declaring mfct specific data using the dif 0f.
bool has_process_content_ = false; // Mark this driver as having mfct specific decoding.
public:
DriverInfo() {};
@ -193,6 +194,8 @@ public:
void forceMfctIndex(int i) { force_mfct_index_ = i; }
void setConstructor(function<shared_ptr<Meter>(MeterInfo&,DriverInfo&)> c) { constructor_ = c; }
void addDetection(uint16_t mfct, uchar type, uchar ver) { detect_.push_back({ mfct, type, ver }); }
void usesProcessContent() { has_process_content_ = true; }
vector<DriverDetect> &detect() { return detect_; }
DriverName name() { return name_; }
@ -212,6 +215,7 @@ public:
bool isValidMedia(uchar type);
bool isCloseEnoughMedia(uchar type);
int forceMfctIndex() { return force_mfct_index_; }
bool hasProcessContentCode() { return has_process_content_; }
};
bool registerDriver(function<void(DriverInfo&di)> setup);

Wyświetl plik

@ -100,28 +100,6 @@ protected:
void addLinkMode(LinkMode lm);
void setMfctTPLStatusBits(Translate::Lookup &lookup);
// Print with the default unit for this quantity.
void addPrint(string vname, Quantity vquantity,
function<double(Unit)> getValueFunc, string help, PrintProperties pprops);
// Print with exactly this unit for this quantity.
void addPrint(string vname, Quantity vquantity, Unit unit,
function<double(Unit)> getValueFunc, string help, PrintProperties pprops);
// Print the dimensionless Text quantity, no unit is needed.
void addPrint(string vname, Quantity vquantity,
function<std::string()> getValueFunc, string help, PrintProperties pprops);
#define SET_FUNC(varname,to_unit) {[=](Unit from_unit, double d){varname = convert(d, from_unit, to_unit);}}
#define GET_FUNC(varname,from_unit) {[=](Unit to_unit){return convert(varname, from_unit, to_unit);}}
#define LOOKUP_FIELD(DVKEY) NoDifVifKey,VifScaling::Auto,MeasurementType::Instantaneous,VIFRange::Any,AnyStorageNr,AnyTariffNr,IndexNr(1)
#define FIND_FIELD(TYPE,INFO) NoDifVifKey,VifScaling::Auto,TYPE,INFO,StorageNr(0),TariffNr(0),IndexNr(1)
#define FIND_FIELD_S(TYPE,INFO,STORAGE) NoDifVifKey,VifScaling::Auto,TYPE,INFO,STORAGE,TariffNr(0),IndexNr(1)
#define FIND_FIELD_ST(TYPE,INFO,STORAGE,TARIFF) NoDifVifKey,VifScaling::Auto,,TYPE,INFO,STORAGE,TARIFF,IndexNr(1)
#define FIND_FIELD_STI(TYPE,INFO,STORAGE,TARIFF,INDEX) NoDifVifKey,VifScaling::Auto,TYPE,INFO,STORAGE,TARIFF,INDEX
#define FIND_SFIELD(TYPE,INFO) NoDifVifKey,TYPE,INFO,StorageNr(0),TariffNr(0),IndexNr(1)
#define FIND_SFIELD_S(TYPE,INFO,STORAGE) NoDifVifKey,TYPE,INFO,STORAGE,TariffNr(0),IndexNr(1)
#define FIND_SFIELD_ST(TYPE,INFO,STORAGE,TARIFF) NoDifVifKey,TYPE,INFO,STORAGE,TARIFF,IndexNr(1)
#define FIND_SFIELD_STI(TYPE,INFO,STORAGE,TARIFF,INDEX) NoDifVifKey,TYPE,INFO,STORAGE,TARIFF,INDEX
void addNumericFieldWithExtractor(
string vname, // Name of value without unit, eg "total" "total_month{storagenr}"
@ -149,14 +127,6 @@ protected:
FieldMatcher matcher, // We can generate a calculated field per match.
Unit display_unit = Unit::Unknown); // If specified use this unit for the json field instead instead of the default unit.
void addNumericField(
string vname, // Name of value without unit, eg total
Quantity vquantity, // Value belongs to this quantity.
PrintProperties print_properties, // Should this be printed by default in fields,json and hr.
string help,
function<void(Unit,double)> setValueFunc, // Use the SET macro above.
function<double(Unit)> getValueFunc); // Use the GET macro above.
void addNumericField(
string vname, // Name of value without unit, eg total
Quantity vquantity, // Value belongs to this quantity.
@ -173,21 +143,6 @@ protected:
PrintProperties print_properties,
FieldMatcher matcher);
void addStringFieldWithExtractorAndLookup(
string vname, // Name of value without unit, eg total
Quantity vquantity, // Value belongs to this quantity.
DifVifKey dif_vif_key, // You can hardocde a dif vif header here or use NoDifVifKey
MeasurementType mt, // If not using a hardcoded key, search for mt,vi,s,t and i instead.
VIFRange vi,
StorageNr s,
TariffNr t,
IndexNr i,
PrintProperties print_properties, // Should this be printed by default in fields,json and hr.
string help,
function<void(string)> setValueFunc, // Use the SET_STRING macro above.
function<string()> getValueFunc, // Use the GET_STRING macro above.
Translate::Lookup lookup); // Translate the bits/indexes.
void addStringFieldWithExtractorAndLookup(
string vname,
string help,