// 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'
        }
    }
}