kopia lustrzana https://github.com/weetmuts/wmbusmeters
Print OPTIONAL fields that have previously received a value, even if the field is not part of the current telegram. Merge driver whe5x into qcaloric.
rodzic
94683240ea
commit
79e1395fdc
7
CHANGES
7
CHANGES
|
|
@ -1,3 +1,10 @@
|
||||||
|
|
||||||
|
OPTIONAL fields were only printed in the json, if they appeared in the telegram,
|
||||||
|
even if the field had received a value before. Now a field will be printed
|
||||||
|
in the json whenever there is a value stored in the meter object.
|
||||||
|
I.e. an OPTIONAL field that has never received a value will not be printed.
|
||||||
|
A NON-OPTIONAL field that has never received a value will be printed with the value null.
|
||||||
|
|
||||||
Version: 1.9.0 2022-09-04
|
Version: 1.9.0 2022-09-04
|
||||||
|
|
||||||
ATTENTION! The multical21 and flowiq drivers have been refactored to the new driver style.
|
ATTENTION! The multical21 and flowiq drivers have been refactored to the new driver style.
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ telegram=|314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000
|
||||||
|
|
||||||
# Test another pair of QCalric C1 telegrams
|
# Test another pair of QCalric C1 telegrams
|
||||||
telegram=|49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA227|
|
telegram=|49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA227|
|
||||||
{"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":null,"set_date":null,"consumption_at_set_date_hca":null,"set_date_1":null,"consumption_at_set_date_1_hca":null,"set_date_17":null,"consumption_at_set_date_17_hca":null,"error_date":null,"device_date_time":"2021-07-02 15:34","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":null,"set_date":null,"consumption_at_set_date_hca":null,"set_date_1":null,"consumption_at_set_date_1_hca":null,"device_date_time":"2021-07-02 15:34","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
telegram=|314493449392919034087a520000200b6e9700004b6e700200426c9f2ccb086e970000c2086cbe26326cffff046d2d16a227|
|
telegram=|314493449392919034087a520000200b6e9700004b6e700200426c9f2ccb086e970000c2086cbe26326cffff046d2d16a227|
|
||||||
{"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":97,"set_date":"2020-12-31","consumption_at_set_date_hca":270,"set_date_1":"2020-12-31","consumption_at_set_date_1_hca":270,"set_date_17":"2021-06-30","consumption_at_set_date_17_hca":97,"error_date":"2127-15-31","device_date_time":"2021-07-02 22:45","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":97,"set_date":"2020-12-31","consumption_at_set_date_hca":270,"set_date_1":"2020-12-31","consumption_at_set_date_1_hca":270,"set_date_17":"2021-06-30","consumption_at_set_date_17_hca":97,"error_date":"2127-15-31","device_date_time":"2021-07-02 22:45","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Test a HCA WHE465 Qundis
|
# Test a HCA WHE465 Qundis
|
||||||
|
|
||||||
telegram=|244465323251839134087a4f0000000b6e0403004b6e660300426c9e29326cffff046d1416b921dd2f|
|
telegram=|244465323251839134087a4f0000000b6e0403004b6e660300426c9e29326cffff046d1416b921dd2f|
|
||||||
{"media":"heat cost allocation","meter":"whe5x","name":"HCA","id":"91835132","status":"OK","current_consumption_hca":304,"set_date":"2020-09-30","consumption_at_set_date_hca":366,"set_date_1":"2020-09-30","consumption_at_set_date_1_hca":366,"error_date":"2127-15-31","device_date_time":"2021-01-25 22:20","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat cost allocation","meter":"qcaloric","name":"HCA","id":"91835132","status":"OK","current_consumption_hca":304,"set_date":"2020-09-30","consumption_at_set_date_hca":366,"set_date_1":"2020-09-30","consumption_at_set_date_1_hca":366,"error_date":"2127-15-31","device_date_time":"2021-01-25 22:20","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|HCA;91835132;304;2020-09-30;366;1111-11-11 11:11.11
|
|HCA;91835132;304;2020-09-30;366;1111-11-11 11:11.11
|
||||||
|
|
||||||
# Test another HCA from Qundis
|
# Test another HCA from Qundis
|
||||||
|
|
|
||||||
|
|
@ -288,12 +288,12 @@ namespace
|
||||||
|
|
||||||
// Type T1A2 telegram:
|
// Type T1A2 telegram:
|
||||||
// telegram=|DA44496A5555445588077A320200002F2F_04140000000084800114000000008280016C2124C480011400000080C280016CFFFF84810114000000808281016CFFFFC481011400000080C281016CFFFF84820114000000808282016CFFFFC482011400000080C282016CFFFF84830114000000808283016CFFFFC483011400000080C283016CFFFF84840114000000808284016CFFFFC484011400000080C284016CFFFF84850114000000808285016CFFFFC485011400000080C285016CFFFF84860114000000808286016CFFFFC486011400000080C286016CFFFF|
|
// telegram=|DA44496A5555445588077A320200002F2F_04140000000084800114000000008280016C2124C480011400000080C280016CFFFF84810114000000808281016CFFFFC481011400000080C281016CFFFF84820114000000808282016CFFFFC482011400000080C282016CFFFF84830114000000808283016CFFFFC483011400000080C283016CFFFF84840114000000808284016CFFFFC484011400000080C284016CFFFF84850114000000808285016CFFFFC485011400000080C285016CFFFF84860114000000808286016CFFFFC486011400000080C286016CFFFF|
|
||||||
// {"media":"water","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":0,"total_volume_m3":0,"status":"ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED","prev_1_month":"2017-04-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_m3":0,"prev_2_month_m3":21474836.48,"prev_3_month_m3":21474836.48,"prev_4_month_m3":21474836.48,"prev_5_month_m3":21474836.48,"prev_6_month_m3":21474836.48,"prev_7_month_m3":21474836.48,"prev_8_month_m3":21474836.48,"prev_9_month_m3":21474836.48,"prev_10_month_m3":21474836.48,"prev_11_month_m3":21474836.48,"prev_12_month_m3":21474836.48,"prev_13_month_m3":21474836.48,"prev_14_month_m3":21474836.48,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"water","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":0,"total_volume_m3":0,"status":"ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED","prev_1_month":"2017-04-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_m3":0,"prev_2_month_m3":21474836.48,"prev_3_month_m3":21474836.48,"prev_4_month_m3":21474836.48,"prev_5_month_m3":21474836.48,"prev_6_month_m3":21474836.48,"prev_7_month_m3":21474836.48,"prev_8_month_m3":21474836.48,"prev_9_month_m3":21474836.48,"prev_10_month_m3":21474836.48,"prev_11_month_m3":21474836.48,"prev_12_month_m3":21474836.48,"prev_13_month_m3":21474836.48,"prev_14_month_m3":21474836.48,"total_energy_consumption_last_month_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |Heat;55445555;0.000000;0.000000;ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED;1111-11-11 11:11.11
|
// |Heat;55445555;0.000000;0.000000;ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Type T1B telegram:
|
// Type T1B telegram:
|
||||||
// telegram=|5E44496A5555445588047A0A0050052F2F_04061A0000000413C20800008404060000000082046CC121043BA4000000042D1900000002591216025DE21002FD17000084800106000000008280016CC121948001AE25000000002F2F2F2F2F2F|
|
// telegram=|5E44496A5555445588047A0A0050052F2F_04061A0000000413C20800008404060000000082046CC121043BA4000000042D1900000002591216025DE21002FD17000084800106000000008280016CC121948001AE25000000002F2F2F2F2F2F|
|
||||||
// {"media":"heat","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":26,"total_volume_m3":2.242,"status":"OK","prev_1_month":"2022-01-01","prev_1_month_kwh":0,"due_energy_consumption_kwh":0,"due_date":"2022-01-01","volume_flow_m3h":0.164,"power_kw":2.5,"total_energy_consumption_last_month_kwh":0,"max_power_last_month_kw":0,"flow_temperature_c":56.5,"return_temperature_c":43.22,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"heat","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":26,"total_volume_m3":2.242,"status":"OK","prev_1_month":"2022-01-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_kwh":0,"prev_2_month_kwh":2147483648,"prev_3_month_kwh":2147483648,"prev_4_month_kwh":2147483648,"prev_5_month_kwh":2147483648,"prev_6_month_kwh":2147483648,"prev_7_month_kwh":2147483648,"prev_8_month_kwh":2147483648,"prev_9_month_kwh":2147483648,"prev_10_month_kwh":2147483648,"prev_11_month_kwh":2147483648,"prev_12_month_kwh":2147483648,"prev_13_month_kwh":2147483648,"prev_14_month_kwh":2147483648,"prev_2_month_m3":21474836.48,"prev_3_month_m3":21474836.48,"prev_4_month_m3":21474836.48,"prev_5_month_m3":21474836.48,"prev_6_month_m3":21474836.48,"prev_7_month_m3":21474836.48,"prev_8_month_m3":21474836.48,"prev_9_month_m3":21474836.48,"prev_10_month_m3":21474836.48,"prev_11_month_m3":21474836.48,"prev_12_month_m3":21474836.48,"prev_13_month_m3":21474836.48,"prev_14_month_m3":21474836.48,"due_energy_consumption_kwh":0,"due_date":"2022-01-01","volume_flow_m3h":0.164,"power_kw":2.5,"total_energy_consumption_last_month_kwh":0,"max_power_last_month_kw":0,"flow_temperature_c":56.5,"return_temperature_c":43.22,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |Heat;55445555;26.000000;2.242000;OK;1111-11-11 11:11.11
|
// |Heat;55445555;26.000000;2.242000;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Test: Heat c5isf 32002044 NOKEY
|
// Test: Heat c5isf 32002044 NOKEY
|
||||||
|
|
|
||||||
|
|
@ -204,5 +204,5 @@ namespace
|
||||||
// |Water;13963399;nan;nan;null;null;null;2021-12-01 00:24;1111-11-11 11:11.11
|
// |Water;13963399;nan;nan;null;null;null;2021-12-01 00:24;1111-11-11 11:11.11
|
||||||
|
|
||||||
// telegram=|2D4465329933961318067ADA000000_0C13567100004C1300000000426CFFFF02BB560000326CFFFF046D2307A12C|
|
// telegram=|2D4465329933961318067ADA000000_0C13567100004C1300000000426CFFFF02BB560000326CFFFF046D2307A12C|
|
||||||
// {"media":"warm water","meter":"lse_07_17","name":"Water","id":"13963399","total_m3":7.156,"due_date_m3":0,"due_date":"2127-15-31","error_code":"OK","error_date":"2127-15-31","device_date_time":"2021-12-01 07:35","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"warm water","meter":"lse_07_17","name":"Water","id":"13963399","total_m3":7.156,"due_date_m3":0,"due_date":"2127-15-31","what_date_m3":7,"what_date":"2021-11-30","error_code":"OK","error_date":"2127-15-31","device_date_time":"2021-12-01 07:35","meter_version":"11","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |Water;13963399;7.156000;0.000000;2127-15-31;OK;2127-15-31;2021-12-01 07:35;1111-11-11 11:11.11
|
// |Water;13963399;7.156000;0.000000;2127-15-31;OK;2127-15-31;2021-12-01 07:35;1111-11-11 11:11.11
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace
|
||||||
.set(StorageNr(8))
|
.set(StorageNr(8))
|
||||||
);
|
);
|
||||||
|
|
||||||
/* The wire mbus telegram contains 4 totals and dates. For the moment we only print nr 1 which is the latest. */
|
// The wire mbus telegram contains 4 totals and dates. For the moment we only print nr 1 which is the latest.
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"target",
|
"target",
|
||||||
"The total water consumption recorded at the beginning of this month.",
|
"The total water consumption recorded at the beginning of this month.",
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,13 @@ namespace
|
||||||
static bool ok = registerDriver([](DriverInfo&di)
|
static bool ok = registerDriver([](DriverInfo&di)
|
||||||
{
|
{
|
||||||
di.setName("qcaloric");
|
di.setName("qcaloric");
|
||||||
|
di.addNameAlias("whe5x");
|
||||||
di.setDefaultFields("name,id,current_consumption_hca,set_date,consumption_at_set_date_hca,timestamp");
|
di.setDefaultFields("name,id,current_consumption_hca,set_date,consumption_at_set_date_hca,timestamp");
|
||||||
di.setMeterType(MeterType::HeatCostAllocationMeter);
|
di.setMeterType(MeterType::HeatCostAllocationMeter);
|
||||||
di.addLinkMode(LinkMode::C1);
|
di.addLinkMode(LinkMode::C1);
|
||||||
di.addLinkMode(LinkMode::T1);
|
di.addLinkMode(LinkMode::T1);
|
||||||
|
di.addLinkMode(LinkMode::S1);
|
||||||
|
di.addDetection(MANUFACTURER_LSE, 0x08, 0x34);
|
||||||
di.addDetection(MANUFACTURER_LSE, 0x08, 0x35);
|
di.addDetection(MANUFACTURER_LSE, 0x08, 0x35);
|
||||||
di.addDetection(MANUFACTURER_QDS, 0x08, 0x35);
|
di.addDetection(MANUFACTURER_QDS, 0x08, 0x35);
|
||||||
di.addDetection(MANUFACTURER_QDS, 0x08, 0x34);
|
di.addDetection(MANUFACTURER_QDS, 0x08, 0x34);
|
||||||
|
|
@ -104,7 +107,7 @@ namespace
|
||||||
addStringFieldWithExtractor(
|
addStringFieldWithExtractor(
|
||||||
"set_date_17",
|
"set_date_17",
|
||||||
"The 17 billing period date.",
|
"The 17 billing period date.",
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::OPTIONAL,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Date)
|
.set(VIFRange::Date)
|
||||||
|
|
@ -114,7 +117,7 @@ namespace
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"consumption_at_set_date_17",
|
"consumption_at_set_date_17",
|
||||||
"Heat cost allocation at the 17 billing period date.",
|
"Heat cost allocation at the 17 billing period date.",
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::OPTIONAL,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
|
|
@ -126,7 +129,7 @@ namespace
|
||||||
addStringFieldWithExtractor(
|
addStringFieldWithExtractor(
|
||||||
"error_date",
|
"error_date",
|
||||||
"Date when the meter entered an error state.",
|
"Date when the meter entered an error state.",
|
||||||
PrintProperty::JSON,
|
PrintProperty::JSON | PrintProperty::OPTIONAL,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::AtError)
|
.set(MeasurementType::AtError)
|
||||||
.set(VIFRange::Date)
|
.set(VIFRange::Date)
|
||||||
|
|
@ -153,7 +156,7 @@ namespace
|
||||||
// Test: MyElement2 qcaloric 90919293 NOKEY
|
// Test: MyElement2 qcaloric 90919293 NOKEY
|
||||||
// Comment: Test mostly proprietary telegram without values
|
// Comment: Test mostly proprietary telegram without values
|
||||||
// telegram=|49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA227|
|
// telegram=|49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA227|
|
||||||
// {"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":null,"set_date":null,"consumption_at_set_date_hca":null,"set_date_1":null,"consumption_at_set_date_1_hca":null,"set_date_17":null,"consumption_at_set_date_17_hca":null,"error_date":null,"device_date_time":"2021-07-02 15:34","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":null,"set_date":null,"consumption_at_set_date_hca":null,"set_date_1":null,"consumption_at_set_date_1_hca":null,"device_date_time":"2021-07-02 15:34","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |MyElement2;90919293;null;null;null;1111-11-11 11:11.11
|
// |MyElement2;90919293;null;null;null;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Comment: Normal telegram that fills in values.
|
// Comment: Normal telegram that fills in values.
|
||||||
|
|
@ -165,3 +168,10 @@ namespace
|
||||||
// telegram=|49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA228|
|
// telegram=|49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA228|
|
||||||
// {"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":97,"set_date":"2020-12-31","consumption_at_set_date_hca":270,"set_date_1":"2020-12-31","consumption_at_set_date_1_hca":270,"set_date_17":"2021-06-30","consumption_at_set_date_17_hca":97,"error_date":"2127-15-31","device_date_time":"2021-08-02 15:34","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"heat cost allocation","meter":"qcaloric","name":"MyElement2","id":"90919293","status":"OK","current_consumption_hca":97,"set_date":"2020-12-31","consumption_at_set_date_hca":270,"set_date_1":"2020-12-31","consumption_at_set_date_1_hca":270,"set_date_17":"2021-06-30","consumption_at_set_date_17_hca":97,"error_date":"2127-15-31","device_date_time":"2021-08-02 15:34","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |MyElement2;90919293;97;2020-12-31;270;1111-11-11 11:11.11
|
// |MyElement2;90919293;97;2020-12-31;270;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Comment: Another version of the heat cost allocator. But for historical reasons got its
|
||||||
|
// own driver name, which is now aliased to qcaloric.
|
||||||
|
// Test: HCA whe5x 91835132 NOKEY
|
||||||
|
// telegram=|244465323251839134087a4f0000000b6e0403004b6e660300426c9e29326cffff046d1416b921dd2f|
|
||||||
|
// {"media":"heat cost allocation","meter":"qcaloric","name":"HCA","id":"91835132","status":"OK","current_consumption_hca":304,"set_date":"2020-09-30","consumption_at_set_date_hca":366,"set_date_1":"2020-09-30","consumption_at_set_date_1_hca":366,"error_date":"2127-15-31","device_date_time":"2021-01-25 22:20","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
// |HCA;91835132;304;2020-09-30;366;1111-11-11 11:11.11
|
||||||
|
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include"meters_common_implementation.h"
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
struct Driver : public virtual MeterCommonImplementation
|
|
||||||
{
|
|
||||||
Driver(MeterInfo &mi, DriverInfo &di);
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool ok = registerDriver([](DriverInfo&di)
|
|
||||||
{
|
|
||||||
di.setName("whe5x");
|
|
||||||
di.setDefaultFields("name,id,current_consumption_hca,set_date,consumption_at_set_date_hca,timestamp");
|
|
||||||
di.setMeterType(MeterType::HeatCostAllocationMeter);
|
|
||||||
di.addLinkMode(LinkMode::S1);
|
|
||||||
di.addDetection(MANUFACTURER_LSE, 0x08, 0x34);
|
|
||||||
|
|
||||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
|
||||||
});
|
|
||||||
|
|
||||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
|
||||||
{
|
|
||||||
addStringField(
|
|
||||||
"status",
|
|
||||||
"Meter status from tpl status field.",
|
|
||||||
PrintProperty::JSON | PrintProperty::FIELD | PrintProperty::IMPORTANT |
|
|
||||||
PrintProperty::STATUS | PrintProperty::JOIN_TPL_STATUS);
|
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
|
||||||
"current_consumption",
|
|
||||||
"The current temperature.",
|
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
|
||||||
Quantity::HCA,
|
|
||||||
VifScaling::Auto,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::HeatCostAllocation)
|
|
||||||
);
|
|
||||||
|
|
||||||
addStringFieldWithExtractor(
|
|
||||||
"set_date",
|
|
||||||
"The most recent billing period date.",
|
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::Date)
|
|
||||||
.set(StorageNr(1))
|
|
||||||
);
|
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
|
||||||
"consumption_at_set_date",
|
|
||||||
"Heat cost allocation at the most recent billing period date.",
|
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
|
||||||
Quantity::HCA,
|
|
||||||
VifScaling::Auto,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::HeatCostAllocation)
|
|
||||||
.set(StorageNr(1))
|
|
||||||
);
|
|
||||||
|
|
||||||
addStringFieldWithExtractor(
|
|
||||||
"set_date_1",
|
|
||||||
"The most recent billing period date.",
|
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::Date)
|
|
||||||
.set(StorageNr(1))
|
|
||||||
);
|
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
|
||||||
"consumption_at_set_date_1",
|
|
||||||
"Heat cost allocation at the most recent billing period date.",
|
|
||||||
PrintProperty::JSON | PrintProperty::FIELD,
|
|
||||||
Quantity::HCA,
|
|
||||||
VifScaling::Auto,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::HeatCostAllocation)
|
|
||||||
.set(StorageNr(1))
|
|
||||||
);
|
|
||||||
|
|
||||||
addStringFieldWithExtractor(
|
|
||||||
"error_date",
|
|
||||||
"Date when the meter entered an error state.",
|
|
||||||
PrintProperty::JSON,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::AtError)
|
|
||||||
.set(VIFRange::Date)
|
|
||||||
);
|
|
||||||
|
|
||||||
addStringFieldWithExtractor(
|
|
||||||
"device_date_time",
|
|
||||||
"Date and time when the meter sent the telegram.",
|
|
||||||
PrintProperty::JSON,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::DateTime)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Test: HCA whe5x 91835132 NOKEY
|
|
||||||
// telegram=|244465323251839134087a4f0000000b6e0403004b6e660300426c9e29326cffff046d1416b921dd2f|
|
|
||||||
// {"media":"heat cost allocation","meter":"whe5x","name":"HCA","id":"91835132","status":"OK","current_consumption_hca":304,"set_date":"2020-09-30","consumption_at_set_date_hca":366,"set_date_1":"2020-09-30","consumption_at_set_date_1_hca":366,"error_date":"2127-15-31","device_date_time":"2021-01-25 22:20","timestamp":"1111-11-11T11:11:11Z"}
|
|
||||||
// |HCA;91835132;304;2020-09-30;366;1111-11-11 11:11.11
|
|
||||||
|
|
@ -1225,3 +1225,63 @@ const char *toString(MeasurementType mt)
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string FieldMatcher::str()
|
||||||
|
{
|
||||||
|
string s = "";
|
||||||
|
|
||||||
|
if (match_dif_vif_key)
|
||||||
|
{
|
||||||
|
s = s+"DVK("+dif_vif_key.str()+") ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_measurement_type)
|
||||||
|
{
|
||||||
|
s = s+"MT("+toString(measurement_type)+") ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_vif_range)
|
||||||
|
{
|
||||||
|
s = s+"VR("+toString(vif_range)+") ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vif_combinables.size() > 0)
|
||||||
|
{
|
||||||
|
s += "Comb(";
|
||||||
|
|
||||||
|
for (auto vc : vif_combinables)
|
||||||
|
{
|
||||||
|
s = s+toString(vc)+" ";
|
||||||
|
}
|
||||||
|
|
||||||
|
s.pop_back();
|
||||||
|
s += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_storage_nr)
|
||||||
|
{
|
||||||
|
s = s+"S("+to_string(storage_nr_from.intValue())+"-"+to_string(storage_nr_to.intValue())+") ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_tariff_nr)
|
||||||
|
{
|
||||||
|
s = s+"T("+to_string(tariff_nr_from.intValue())+"-"+to_string(tariff_nr_to.intValue())+") ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_subunit_nr)
|
||||||
|
{
|
||||||
|
s += "U("+to_string(subunit_nr_from.intValue())+"-"+to_string(subunit_nr_to.intValue())+") ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index_nr.intValue() != 1)
|
||||||
|
{
|
||||||
|
s += "I("+to_string(index_nr.intValue())+")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.size() > 0)
|
||||||
|
{
|
||||||
|
s.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,7 @@ struct FieldMatcher
|
||||||
FieldMatcher &set(IndexNr i) { index_nr = i; return *this; }
|
FieldMatcher &set(IndexNr i) { index_nr = i; return *this; }
|
||||||
|
|
||||||
bool matches(DVEntry &dv_entry);
|
bool matches(DVEntry &dv_entry);
|
||||||
|
std::string str();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool loadFormatBytesFromSignature(uint16_t format_signature, std::vector<uchar> *format_bytes);
|
bool loadFormatBytesFromSignature(uint16_t format_signature, std::vector<uchar> *format_bytes);
|
||||||
|
|
|
||||||
101
src/meters.cc
101
src/meters.cc
|
|
@ -1820,6 +1820,27 @@ void MeterCommonImplementation::setNumericValue(FieldInfo *fi, Unit u, double v)
|
||||||
numeric_values_[pair<string,Quantity>(field_name_no_unit,fi->xuantity())] = NumericField(u, v, fi);
|
numeric_values_[pair<string,Quantity>(field_name_no_unit,fi->xuantity())] = NumericField(u, v, fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MeterCommonImplementation::hasValue(FieldInfo *fi)
|
||||||
|
{
|
||||||
|
return hasStringValue(fi) || hasNumericValue(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeterCommonImplementation::hasNumericValue(FieldInfo *fi)
|
||||||
|
{
|
||||||
|
if (fi->hasGetNumericValueOverride()) return true;
|
||||||
|
|
||||||
|
pair<string,Quantity> key(fi->vname(),fi->xuantity());
|
||||||
|
|
||||||
|
return numeric_values_.count(key) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MeterCommonImplementation::hasStringValue(FieldInfo *fi)
|
||||||
|
{
|
||||||
|
if (fi->hasGetStringValueOverride()) return true;
|
||||||
|
|
||||||
|
return string_values_.count(fi->vname()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
double MeterCommonImplementation::getNumericValue(FieldInfo *fi, Unit to)
|
double MeterCommonImplementation::getNumericValue(FieldInfo *fi, Unit to)
|
||||||
{
|
{
|
||||||
if (fi->hasGetNumericValueOverride())
|
if (fi->hasGetNumericValueOverride())
|
||||||
|
|
@ -2033,12 +2054,14 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over the meter field infos...
|
// Iterate over the meter field infos...
|
||||||
|
map<FieldInfo*,DVEntry*> found; // Found from the telegram
|
||||||
|
set<string> found_vnames;
|
||||||
|
|
||||||
for (FieldInfo& fi : field_infos_)
|
for (FieldInfo& fi : field_infos_)
|
||||||
{
|
{
|
||||||
if (fi.printProperties().hasJSON())
|
if (fi.printProperties().hasJSON())
|
||||||
{
|
{
|
||||||
// The field should be printed in the json. (Most usually should.)
|
// The field should be printed in the json. (Most usually should.)
|
||||||
bool found = false;
|
|
||||||
for (auto& i : t->dv_entries)
|
for (auto& i : t->dv_entries)
|
||||||
{
|
{
|
||||||
// Check each telegram dv entry.
|
// Check each telegram dv entry.
|
||||||
|
|
@ -2046,24 +2069,50 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
||||||
// Has the entry been matches to this field, then print it as json.
|
// Has the entry been matches to this field, then print it as json.
|
||||||
if (dve->hasFieldInfo(&fi))
|
if (dve->hasFieldInfo(&fi))
|
||||||
{
|
{
|
||||||
debug("(meters) render field %s(%s)[%d] with dventry @%d key %s data %s\n",
|
assert(found.count(&fi) == 0);
|
||||||
fi.vname().c_str(), toString(fi.xuantity()), fi.index(),
|
found[&fi] = dve;
|
||||||
dve->offset,
|
found_vnames.insert(fi.vname());
|
||||||
dve->dif_vif_key.str().c_str(),
|
}
|
||||||
dve->value.c_str());
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FieldInfo& fi : field_infos_)
|
||||||
|
{
|
||||||
|
if (fi.printProperties().hasJSON())
|
||||||
|
{
|
||||||
|
if (found.count(&fi) != 0)
|
||||||
|
{
|
||||||
|
DVEntry *dve = found[&fi];
|
||||||
|
debug("(meters) render field %s(%s)[%d] with dventry @%d key %s data %s\n",
|
||||||
|
fi.vname().c_str(), toString(fi.xuantity()), fi.index(),
|
||||||
|
dve->offset,
|
||||||
|
dve->dif_vif_key.str().c_str(),
|
||||||
|
dve->value.c_str());
|
||||||
|
string out = fi.renderJson(this, &conversions());
|
||||||
|
debug("(meters) %s\n", out.c_str());
|
||||||
|
s += indent+out+","+newline;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ok, no value found in received telegram.
|
||||||
|
// Print field anyway, if it is not OPTIONAL
|
||||||
|
// or if a value has been received before and this field has not been received using a different rule.
|
||||||
|
// Why this complicated rule?
|
||||||
|
// E.g. the minmoess mbus seems to use storage 1 for target_m3 but the wmbus version uses storage 8.
|
||||||
|
// I.e. we have two rules that store into target_m3, this check will prevent target_m3 from being printed twice.
|
||||||
|
if (!fi.printProperties().hasOPTIONAL() || (found_vnames.count(fi.vname()) == 0 && hasValue(&fi)))
|
||||||
|
{
|
||||||
|
// No telegram entries found, but this field should be printed anyway.
|
||||||
|
// It will be printed with any value received from a previous telegram.
|
||||||
|
// Or if no value has been received, null.
|
||||||
|
debug("(meters) render field %s(%s)[%d] without dventry\n",
|
||||||
|
fi.vname().c_str(), toString(fi.xuantity()), fi.index());
|
||||||
string out = fi.renderJson(this, &conversions());
|
string out = fi.renderJson(this, &conversions());
|
||||||
debug("(meters) %s\n", out.c_str());
|
debug("(meters) %s\n", out.c_str());
|
||||||
s += indent+out+","+newline;
|
s += indent+out+","+newline;
|
||||||
found = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found && !fi.printProperties().hasOPTIONAL())
|
|
||||||
{
|
|
||||||
// No telegram entries found, but this field should be printed anyway.
|
|
||||||
// It will be printed with any value received from a previous telegram.
|
|
||||||
// Or if no value has been received, null.
|
|
||||||
s += indent+fi.renderJson(this, &conversions())+","+newline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2492,6 +2541,19 @@ bool FieldInfo::matches(DVEntry *dve)
|
||||||
return matcher_.matches(*dve);
|
return matcher_.matches(*dve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string FieldInfo::str()
|
||||||
|
{
|
||||||
|
// 15 target Volume x<> :<3A> Auto XUZ "The total water consumption recorded at the beginning of this month."
|
||||||
|
return tostrprintf("%d %s_%s (%s) %s [%s] \"%s\"",
|
||||||
|
index_,
|
||||||
|
vname_.c_str(),
|
||||||
|
unitToStringLowerCase(default_unit_).c_str(),
|
||||||
|
toString(xuantity_),
|
||||||
|
toString(vif_scaling_),
|
||||||
|
matcher_.str().c_str(),
|
||||||
|
help_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
DriverName MeterInfo::driverName()
|
DriverName MeterInfo::driverName()
|
||||||
{
|
{
|
||||||
if (driver_name.str() == "")
|
if (driver_name.str() == "")
|
||||||
|
|
@ -2923,6 +2985,17 @@ void MeterCommonImplementation::addOptionalFlowRelatedFields()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *toString(VifScaling s)
|
||||||
|
{
|
||||||
|
switch (s)
|
||||||
|
{
|
||||||
|
case VifScaling::None: return "None";
|
||||||
|
case VifScaling::Auto: return "Auto";
|
||||||
|
case VifScaling::NoneSigned: return "NoneSigned";
|
||||||
|
case VifScaling::AutoSigned: return "AutoSigned";
|
||||||
}
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,8 @@ enum class VifScaling
|
||||||
AutoSigned // Scale and assume the value is signed.
|
AutoSigned // Scale and assume the value is signed.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* toString(VifScaling s);
|
||||||
|
|
||||||
enum PrintProperty
|
enum PrintProperty
|
||||||
{
|
{
|
||||||
JSON = 1, // This field should be printed when using --format=json
|
JSON = 1, // This field should be printed when using --format=json
|
||||||
|
|
@ -382,10 +384,13 @@ struct FieldInfo
|
||||||
// The vname is then a pattern total_at_month_{storagenr-32} that gets translated into
|
// The vname is then a pattern total_at_month_{storagenr-32} that gets translated into
|
||||||
// total_at_month_2 (for the dventry with storage nr 34.)
|
// total_at_month_2 (for the dventry with storage nr 34.)
|
||||||
string generateFieldName(DVEntry *dve);
|
string generateFieldName(DVEntry *dve);
|
||||||
|
// Check if the meter object stores a value for this field.
|
||||||
|
bool hasValue(Meter *m);
|
||||||
|
|
||||||
Translate::Lookup& lookup() { return lookup_; }
|
Translate::Lookup& lookup() { return lookup_; }
|
||||||
|
|
||||||
|
string str();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int index_; // The field infos for a meter are ordered.
|
int index_; // The field infos for a meter are ordered.
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,12 @@ protected:
|
||||||
double getNumericValue(FieldInfo *fi, Unit u);
|
double getNumericValue(FieldInfo *fi, Unit u);
|
||||||
void setStringValue(FieldInfo *fi, std::string v);
|
void setStringValue(FieldInfo *fi, std::string v);
|
||||||
std::string getStringValue(FieldInfo *fi);
|
std::string getStringValue(FieldInfo *fi);
|
||||||
|
|
||||||
|
// Check if the meter has received a value for this field.
|
||||||
|
bool hasValue(FieldInfo *fi);
|
||||||
|
bool hasNumericValue(FieldInfo *fi);
|
||||||
|
bool hasStringValue(FieldInfo *fi);
|
||||||
|
|
||||||
std::string decodeTPLStatusByte(uchar sts);
|
std::string decodeTPLStatusByte(uchar sts);
|
||||||
|
|
||||||
void addOptionalCommonFields();
|
void addOptionalCommonFields();
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue