// Copyright (C) 2019-2023 Fredrik Öhrström (gpl-3.0-or-later)
driver {
name = qcaloric
aliases = whe5x,whe46x
meter_type = HeatCostAllocationMeter
default_fields = name,id,current_consumption_hca,set_date,consumption_at_set_date_hca,timestamp
detect {
mvt = LSE,34,08
mvt = LSE,35,08
mvt = QDS,35,08
mvt = QDS,34,08
mvt = QDS,36,08
mvt = LSE,18,08
mvt = ZRI,fd,08
}
library {
use = model_version
}
fields {
field {
name = status
quantity = Text
info = 'Meter status from tpl status field.'
attributes = STATUS,INCLUDE_TPL_STATUS
match {
difvifkey = 01FD73
}
lookup {
name = ERROR_FLAGS
map_type = BitToString
mask_bits = 0xff
default_message = OK
}
}
field {
name = current_consumption
quantity = HCA
info = 'The current heat cost allocation.'
vif_scaling = Auto
dif_signedness = Signed
match {
measurement_type = Instantaneous
vif_range = HeatCostAllocation
}
}
field {
name = set
quantity = PointInTime
info = 'The most recent billing period date.'
display_unit = DateLT
match {
measurement_type = Instantaneous
vif_range = Date
storage_nr = 1
}
}
field {
name = consumption_at_set_date
quantity = HCA
info = 'Heat cost allocation at the most recent billing period date.'
vif_scaling = Auto
dif_signedness = Signed
match {
measurement_type = Instantaneous
vif_range = HeatCostAllocation
storage_nr = 1
}
}
field {
name = set_date_1
quantity = Text
info = 'The most recent billing period date.'
match {
measurement_type = Instantaneous
vif_range = Date
storage_nr = 1
}
}
field {
name = consumption_at_set_date_1
quantity = HCA
info = 'Heat cost allocation at the most recent billing period date.'
vif_scaling = Auto
dif_signedness = Signed
match {
measurement_type = Instantaneous
vif_range = HeatCostAllocation
storage_nr = 1
}
}
field {
name = 'set_date_{storage_counter}'
quantity = Text
info = 'The nth billing period date.'
match {
measurement_type = Instantaneous
vif_range = Date
storage_nr = 2,17
}
}
field {
name = 'consumption_at_set_date_{storage_counter}'
quantity = HCA
info = 'Heat cost allocation at the nth billing period date.'
vif_scaling = Auto
dif_signedness = Signed
match {
measurement_type = Instantaneous
vif_range = HeatCostAllocation
storage_nr = 2,17
}
}
field {
name = error_date
quantity = Text
info = 'Date when the meter entered an error state.'
match {
measurement_type = AtError
vif_range = Date
}
}
field {
name = mfct_wbds
quantity = Text
attributes = HIDE
match {
difvifkey = 0DFF5F
}
// Siemens/Qundis WalkByDataSet block: DIF=0D (variable-len), VIF=FF (mfct), VIFE=5F, 53 bytes.
// 9-byte proprietary header (skipped as quad, quad, byte):
// [0] 0x00 reserved
// [1] 0x82 wbdsId: Type 2 WalkByDataSet
// [2] counter rolling transmission counter (increments each send)
// [3] 0x00 reserved
// [4] 0x00 reserved
// [5] flags device-specific status/flags (format unknown)
// [6] 0x00/01 extra flag
// [7] 0x07 constant (possibly number of data records)
// [8] 0xB0 meter type: 0xB0=HCA, 0xC0=heat, 0xC1=warm/cold water
// Byte [9] is the VIF (0x6E = HCA), which is matched below.
ixml = "decode = quad, quad, byte, hca.
hca = '6E', error_date, current_hca, cutoff_date, cutoff_hca, last_date, last_hca, byte*.
error_date = word, @DV_426C.
current_hca = quad, @DV_0C6E.
cutoff_date = word, @DV_426C.
cutoff_hca = quad, @DV_4C6E.
last_date = word, @DV_C2086C.
last_hca = quad, @DV_CC086E.
-hex = ['A'-'F';'0'-'9'].
-byte = hex, hex.
-word = byte, byte.
-quad = byte, byte, byte, byte.
DV_426C>dvk = +'426C'.
DV_0C6E>dvk = +'0C6E'.
DV_4C6E>dvk = +'4C6E'.
DV_C2086C>dvk = +'C2086C'.
DV_CC086E>dvk = +'CC086E'."
}
field {
name = device
quantity = PointInTime
info = 'Date and time when the meter sent the telegram.'
display_unit = datetime
match {
measurement_type = Instantaneous
vif_range = DateTime
}
}
field {
name = flow_temperature
quantity = Temperature
info = 'Forward media temperature.'
vif_scaling = Auto
dif_signedness = Signed
match {
measurement_type = Instantaneous
vif_range = FlowTemperature
}
}
}
tests {
test {
args = 'MyElement1 qcaloric 78563412 NOKEY'
telegram = 314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"MyElement1","id":"78563412","status":"OK","current_consumption_hca":127,"set_date":"2018-12-31","consumption_at_set_date_hca":145,"set_date_1":"2018-12-31","consumption_at_set_date_1_hca":145,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79,"error_date":"2127-15-31","device_datetime":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'MyElement1;78563412;127;2018-12-31;145;1111-11-11 11:11.11'
}
test {
args = 'MyElement2 qcaloric 90919293 NOKEY'
comment = 'Walkby telegram with WalkByDataSet manufacturer block (Type2, Bcd6, VIF=HCA).'
telegram = 49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA227
json = '{"_":"telegram","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,"device_datetime":"2021-07-02 15:34","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'MyElement2;90919293;97;2020-12-31;270;1111-11-11 11:11.11'
}
test {
args = 'zenner_heat qcaloric 25932395 NOKEY'
telegram = 5E44496A95239325FD087A2CC050052F2F_0B6E030100426CDF2C4B6EFFFFFF82046CE1228B046E6200008D04EE132C3BFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F2F2F2F
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"zenner_heat","id":"25932395","status":"UNKNOWN_C0","current_consumption_hca":103,"set_date":"2022-12-31","set_date_1":"2022-12-31","set_date_8":"2023-02-01","consumption_at_set_date_8_hca":62,"timestamp":"1111-11-11T11:11:11Z"}'
fields = 'zenner_heat;25932395;103;2022-12-31;null;1111-11-11 11:11.11'
}
test {
args = 'MyElement3 qcaloric 90919293 NOKEY'
comment = 'Normal telegram that fills in values.'
telegram = 314493449392919034087a520000200b6e9700004b6e700200426c9f2ccb086e970000c2086cbe26326cffff046d2d16a227
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"MyElement3","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_datetime":"2021-07-02 22:45","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'MyElement3;90919293;97;2020-12-31;270;1111-11-11 11:11.11'
}
test {
args = 'MyElement4 qcaloric 90919293 NOKEY'
comment = 'Another mostly empty telegram, but values are now valid.'
telegram = 49449344939291903408780DFF5F350082180000800007B06EFFFF970000009F2C70020000BE26970000000000010018002E001F002E0023FF210008000500020000002F046D220FA228
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"MyElement4","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,"device_datetime":"2021-08-02 15:34","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'MyElement4;90919293;97;2020-12-31;270;1111-11-11 11:11.11'
}
test {
args = 'HCA1 whe5x 91835132 NOKEY'
comment = 'Another version of the heat cost allocator. Was known as whe5x, so a name alias exist that maps to qcaloric.'
telegram = 284465323251839134087a4f0000000b6e0403004b6e660300426c9e29326cffff046d1416b921dd2f
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"HCA1","id":"91835132","consumption_at_set_date_hca":366,"consumption_at_set_date_1_hca":366,"current_consumption_hca":304,"device_datetime":"2021-01-25 22:20","error_date":"2127-15-31","set_date":"2020-09-30","set_date_1":"2020-09-30","status":"OK","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'HCA1;91835132;304;2020-09-30;366;1111-11-11 11:11.11'
}
test {
args = 'HCA2 whe46x 60366655 NOKEY'
comment = 'Another version of the heat cost allocator. Was known as whe46x, which now is a name alias mapped to qcaloric.'
telegram = 344465325566366018087A90040000046D1311962C01FD0C03326CFFFF01FD7300025AC2000DFF5F0C0008003030810613080BFFFC
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"HCA2","id":"60366655","status":"POWER_LOW","error_date":"2127-15-31","device_datetime":"2020-12-22 17:19","model_version":"03","flow_temperature_c":19.4,"timestamp":"1111-11-11T11:11:11Z"}'
fields = 'HCA2;60366655;null;null;null;1111-11-11 11:11.11'
}
test {
args = 'HCA3 whe46x 60366655 NOKEY'
telegram = 2a4465325566366018087ac3040000046d1617Ba210B6e890000426c9f2c4B6e520600326cffff01fd7300
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"HCA3","id":"60366655","status":"POWER_LOW","current_consumption_hca":89,"set_date":"2020-12-31","consumption_at_set_date_hca":652,"set_date_1":"2020-12-31","consumption_at_set_date_1_hca":652,"error_date":"2127-15-31","device_datetime":"2021-01-26 23:22","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'HCA3;60366655;89;2020-12-31;652;1111-11-11 11:11.11'
}
test {
args = 'HCA4 qcaloric 30535282 NOKEY'
comment = 'Mostly mfct specific data. Not yet decoded.'
telegram = 49449344825253303608780DFF5F350082430035E3DFC4EAC97A58B8610713D93549E2601258D617D267E7515C764B002A88CD341A9F9DF3C6034DE5B6D1FAB3619CBA9F046D250DDA2C
json = '{"_":"telegram","media":"heat cost allocation","meter":"qcaloric","name":"HCA4","id":"30535282","status":"OK","device_datetime":"2022-12-26 13:37","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'HCA4;30535282;null;null;null;1111-11-11 11:11.11'
}
}
}