// Copyright (C) 2026 Fredrik Öhrström (gpl-3.0-or-later)
driver {
    name              = qwaterv2
    meter_type        = WaterMeter
    default_fields    = name,id,status,total_m3,timestamp
    manufacturer      = Qundis
    transform_payload = try_qundis_decode
    detect {
        mvt = QDS,33,37
        mvt = QDS,35,06
        mvt = QDS,35,07
        mvt = QDS,35,37
        mvt = QDS,16,06
        mvt = QDS,16,07
        mvt = QDS,17,06
        mvt = QDS,17,07
        mvt = QDS,18,06
        mvt = QDS,18,07
        mvt = QDS,19,06
        mvt = QDS,19,07
        mvt = QDS,1A,06
        mvt = QDS,1A,07
        mvt = QDS,1D,06
        mvt = QDS,1D,07
        mvt = QDS,36,06
        mvt = QDS,36,07
        mvt = LSE,19,07
        mvt = LSE,1A,06
        mvt = LSE,1A,07
        mvt = LSE,1D,06
        mvt = LSE,1D,07
        mvt = LSE,33,37
        mvt = LSE,35,37
        mvt = LSE,36,06
        mvt = LSE,36,07
    }
    library {
        use = total_m3
        use = meter_datetime
        use = enhanced_id
    }
    fields {
        field {
            name       = status
            quantity   = Text
            info       = 'Status and error flags'
            attributes = STATUS,INCLUDE_TPL_STATUS
            match {
                measurement_type = Instantaneous
                vif_range        = ErrorFlags
            }
            lookup {
                name            = ERROR_FLAGS
                map_type        = BitToString
                mask_bits       = 0xffff
                default_message = OK
                map {
                    name = NO_FLOW
                    // F0
                    value = 0x0001
                    test  = Set
                }
                map {
                    name = SUPPLY_SENSOR_INTERRUPTED
                    // F1
                    value = 0x0002
                    test  = Set
                }
                map {
                    name = RETURN_SENSOR_INTERRUPTED
                    // F2
                    value = 0x0004
                    test  = Set
                }
                map {
                    name = TEMPERATURE_ELECTRONICS_ERROR
                    // F3
                    value = 0x0008
                    test  = Set
                }
                map {
                    name = BATTERY_VOLTAGE_ERROR
                    // F4
                    value = 0x0010
                    test  = Set
                }
                map {
                    name = SHORT_CIRCUIT_SUPPLY_SENSOR
                    // F5
                    value = 0x0020
                    test  = Set
                }
                map {
                    name = SHORT_CIRCUIT_RETURN_SENSOR
                    // F6
                    value = 0x0040
                    test  = Set
                }
                map {
                    name = MEMORY_ERROR
                    // F7
                    value = 0x0080
                    test  = Set
                }
                map {
                    name = SABOTAGE
                    // F8 - F1,2,3,5,6 longer than 8 hours, latching error, no more measurements performed.
                    value = 0x0100
                    test  = Set
                }
                map {
                    name = ELECTRONICS_ERROR
                    // F9
                    value = 0x0200
                    test  = Set
                }
            }
        }
        field {
            name     = target
            quantity = Volume
            match {
                measurement_type = Instantaneous
                vif_range        = AnyVolumeVIF
                storage_nr       = 17
            }
        }
        field {
            name     = volume_flow
            quantity = Flow
            match {
                measurement_type = Instantaneous
                vif_range        = VolumeFlow
                add_combinable   = DurationExceedsLowerLast
            }
        }
        field {
            name         = target
            quantity     = PointInTime
            display_unit = date
            match {
                measurement_type = Instantaneous
                vif_range        = Date
                storage_nr       = 17
            }
        }
        field {
            name     = target_year
            quantity = Volume
            match {
                measurement_type = Instantaneous
                vif_range        = AnyVolumeVIF
                storage_nr       = 1
            }
        }
        field {
            name         = target_year
            quantity     = PointInTime
            display_unit = date
            match {
                measurement_type = Instantaneous
                vif_range        = Date
                storage_nr       = 1
            }
        }
        field {
            name         = device_error
            quantity     = PointInTime
            display_unit = date
            match {
                measurement_type = AtError
                vif_range        = Date
            }
        }
        field {
            name       = mfct_specific_data
            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]   0xC1     meter type: 0xB0=HCA, 0xC0=heat, 0xC1=warm/cold water
            // Byte [9] is the VIF (0x13 = volume in liters), which is matched below.
            // The 9-byte header is validated against its fixed bytes ([0]=00, [1]=82,
            // [3]=00, [4]=00, [7]=07, [8]=C1 warm/cold water) so that a block with an
            // unexpected layout (e.g. an encrypted/non-walk-by payload that merely
            // happens to carry the 0DFF5F difvifkey) fails closed instead of having
            // total_m3/target_*/volume_delta_* invented from the wrong offsets.
            // See issue #1916.
            ixml = "decode = -'00', -'82', byte, -'00', -'00', byte, byte, -'07', -'C1', volume_l.

                    volume_l = '13', error_date, total_l, target_year_date, target_year_l, target_date, target_l, delta1, delta2, delta3, delta4, delta5, delta6, delta7, delta8, delta9, delta10, delta11, delta12, -pad.

                    error_date = word, @DV_426C.
                    target_year_date = word, @DV_426C.
                    target_date = word, @DV_C2086C.

                    total_l = quad, @DV_0C13.
                    target_year_l = quad, @DV_4C13.
                    target_l = quad, @DV_CC0813.

                    delta1  = word, @DV_820113.
                    delta2  = word, @DV_C20113.
                    delta3  = word, @DV_820213.
                    delta4  = word, @DV_C20213.
                    delta5  = word, @DV_820313.
                    delta6  = word, @DV_C20313.
                    delta7  = word, @DV_820413.
                    delta8  = word, @DV_C20413.
                    delta9  = word, @DV_820513.
                    delta10 = word, @DV_C20513.
                    delta11 = word, @DV_820613.
                    delta12 = word, @DV_C20613.
                    -pad = byte.

                    -hex  = ['A'-'F';'0'-'9'].
                    -byte = hex, hex.
                    -word = byte, byte.
                    -triplet = byte, byte, byte.
                    -quad = byte, byte, byte, byte.

                    DV_0C13>dvk = +'0C13'.
                    DV_4C13>dvk = +'4C13'.

                    DV_CC0813>dvk = +'CC0813'.

                    DV_426C>dvk = +'426C'.
                    DV_C2086C>dvk = +'C2086C'.

                    DV_820113>dvk = +'820113'.
                    DV_C20113>dvk = +'C20113'.
                    DV_820213>dvk = +'820213'.
                    DV_C20213>dvk = +'C20213'.
                    DV_820313>dvk = +'820313'.
                    DV_C20313>dvk = +'C20313'.
                    DV_820413>dvk = +'820413'.
                    DV_C20413>dvk = +'C20413'.
                    DV_820513>dvk = +'820513'.
                    DV_C20513>dvk = +'C20513'.
                    DV_820613>dvk = +'820613'.
                    DV_C20613>dvk = +'C20613'."
        }
        field {
            name           = 'volume_delta_{storage_counter}'
            quantity       = Volume
            info           = 'Monthly volume delta.'
            vif_scaling    = Auto
            dif_signedness = Signed
            match {
                measurement_type = Instantaneous
                vif_range        = AnyVolumeVIF
                storage_nr       = 2,13
            }
        }
    }
    tests {
        test {
            args     = 'MyQWater qwaterv2 12353648 NOKEY'
            telegram = 3b4493444836351218067ac70000200c13911900004c1391170000426cbf2ccc081391170000c2086cbf2c02bb560000326cffff046d1e02de21fed0
            json     = '{"_": "telegram","device_error_date":null,"id": "12353648","media": "warm water","driver": "qwaterv2","meter_datetime": "2022-01-30 02:30","name": "MyQWater","status": "OK","target_date": "2021-12-31","target_m3": 1.791,"target_year_date": "2021-12-31","target_year_m3": 1.791,"timestamp": "1111-11-11T11:11:11Z","total_m3": 1.991,"volume_flow_m3h": 0}'
            fields   = 'MyQWater;12353648;OK;1.991;1111-11-11 11:11.11'
        }
        test {
            args     = 'MyQWater qwaterv2 12353648 NOKEY'
            comment  = 'A walk by telegram with mfct data.'
            telegram = 4b449344483635121806780dff5f350082da0000600107c113ffff48200000bf2c91170000df2120200000008001000000060019001000160018000d001300350017002f046d370cc422c759
            json     = '{"_": "telegram","id": "12353648","media": "warm water","driver": "qwaterv2","meter_datetime": "2022-02-04 12:55","name": "MyQWater","status": "OK","target_date": "2022-01-31","target_m3":2.02,"target_year_date": "2021-12-31","target_year_m3": 1.791,"timestamp": "1111-11-11T11:11:11Z","total_m3": 2.048,"volume_delta_2_m3":-32.768,"volume_delta_3_m3":0.001,"volume_delta_4_m3":0,"volume_delta_5_m3":0.006,"volume_delta_6_m3":0.025,"volume_delta_7_m3":0.016,"volume_delta_8_m3":0.022,"volume_delta_9_m3":0.024,"volume_delta_10_m3":0.013,"volume_delta_11_m3":0.019,"volume_delta_12_m3":0.053,"volume_delta_13_m3":0.023}'
            fields   = 'MyQWater;12353648;OK;2.048;1111-11-11 11:11.11'
        }
        test {
            args     = 'AnotherQWater qwaterv2 66666666 NOKEY'
            telegram = 3C449344682268363537726666666693443507720000200C13670512004C1361100300426CBF2CCC081344501100C2086CDF28326CFFFF046D0813CF29
            json     = '{"_":"telegram","media":"water","driver":"qwaterv2","name":"AnotherQWater","id":"66666666","device_error_date":null,"target_m3":115.044,"target_date":"2022-08-31","target_year_m3":31.061,"target_year_date":"2021-12-31","total_m3":120.567,"meter_datetime":"2022-09-15 19:08","status":"OK","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'AnotherQWater;66666666;OK;120.567;1111-11-11 11:11.11'
        }
        test {
            args     = 'YetAnoter qwaterv2 33333333 NOKEY'
            telegram = 3C449344333333333537723333333393443506B8000020_0C13350000004C1300000000426CBF2CCC081300000000C2086CDF25326CFFFF046D0516CE262
            json     = '{"_":"telegram","media":"warm water","driver":"qwaterv2","name":"YetAnoter","id":"33333333","status":"OK","total_m3":0.035,"target_year_m3":0,"target_year_date":"2021-12-31","target_m3":0,"target_date":"2022-05-31","device_error_date":null,"meter_datetime":"2022-06-14 22:05","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'YetAnoter;33333333;OK;0.035;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWater-7-18 qwaterv2 12230094 NOKEY'
            telegram = 394493449400231218077ad30000200c13536712004c1307920500426cBf2ccc081373621200c2086cde2B02BB560000326cffff046d3714c32c
            json     = '{"_":"telegram","media":"water","driver":"qwaterv2","name":"QWater-7-18","id":"12230094","status":"OK","total_m3":126.753,"target_year_m3":59.207,"target_year_date":"2021-12-31","target_m3":126.273,"target_date":"2022-11-30","device_error_date":null,"volume_flow_m3h":0,"meter_datetime":"2022-12-03 20:55","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'QWater-7-18;12230094;OK;126.753;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWoo qwaterv2 13144514 NOKEY'
            coment   = 'Warm water'
            telegram = 394493441445141316067A31000020_0C13671605004C1348160500426CDF2CCC081348160500C2086CDF2C02BB560000326CFFFF046D3414E121
            json     = '{"_":"telegram","target_date": "2022-12-31","target_m3": 51.648,"target_year_date": "2022-12-31","target_year_m3": 51.648,"device_error_date":null,"id": "13144514","media": "warm water","driver": "qwaterv2","meter_datetime": "2023-01-01 20:52","name": "QWoo","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 51.667,"volume_flow_m3h": 0}'
            fields   = 'QWoo;13144514;OK;51.667;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWooo qwaterv2 13176890 NOKEY'
            comment  = 'Cold water'
            telegram = 394493449068171316077A0B000020_0C13358612004C1307851200426CDF2CCC081307851200C2086CDF2C02BB560000326CFFFF046D3014E121
            json     = '{"_":"telegram","target_date": "2022-12-31","target_m3": 128.507,"target_year_date": "2022-12-31","target_year_m3": 128.507,"device_error_date":null,"id": "13176890","media": "water","driver": "qwaterv2","meter_datetime": "2023-01-01 20:48","name": "QWooo","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 128.635,"volume_flow_m3h": 0}'
            fields   = 'QWooo;13176890;OK;128.635;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWooo qwaterv2 78563412 NOKEY'
            comment  = 'Proprietary Q walk-by message'
            telegram = 49449344123456781606780DFF5F3500824E00007F0007C113FFFF63961300DF2C82731200FE2463811300A400F200D100A900DD00E000E90006011601EA0027010F012F046D0211F225
            json     = '{"_":"telegram","id": "78563412","media": "warm water","driver": "qwaterv2","meter_datetime": "2023-05-18 17:02","name": "QWooo","status": "OK","target_date": "2023-04-30","target_m3": 138.163,"target_year_date": "2022-12-31","target_year_m3": 127.382,"timestamp": "1111-11-11T11:11:11Z","total_m3": 139.663,"volume_delta_2_m3":0.164,"volume_delta_3_m3":0.242,"volume_delta_4_m3":0.209,"volume_delta_5_m3":0.169,"volume_delta_6_m3":0.221,"volume_delta_7_m3":0.224,"volume_delta_8_m3":0.233,"volume_delta_9_m3":0.262,"volume_delta_10_m3":0.278,"volume_delta_11_m3":0.234,"volume_delta_12_m3":0.295,"volume_delta_13_m3":0.271}'
            fields   = 'QWooo;78563412;OK;139.663;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWaaa qwater 51220588 NOKEY'
            telegram = 4944934488052251190778_0DFF5F350082930000810007C113FFFF91670400FF2C265402001E34332204000000EE00F201A501DB01C1015401B70178019701B901C9012F046D06091D35
            json     = '{"_":"telegram","due_17_date": "2024-04-30","due_17_date_m3": 42.233,"due_date": "2023-12-31","due_date_m3": 25.426,"id": "51220588","media": "water","driver": "qwater","meter_datetime": "2024-05-29 09:06","name": "QWaaa","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 46.791}'
            fields   = 'QWaaa;51220588;46.791;25.426;2023-12-31;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWccc qwater 13492674 NOKEY'
            telegram = 394493447426491317077ADD0000200C13975710004C1330970700426CFF2CCC081387471000C2086C1E3B02BB560000326CFFFF046D1E0F113C
            json     = '{"_": "telegram","due_17_date": "2024-11-30","due_17_date_m3": 104.787,"due_date": "2023-12-31","due_date_m3": 79.73,"error_date":null,"id": "13492674","media": "water","driver": "qwater","meter_datetime": "2024-12-17 15:30","name": "QWccc","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 105.797,"volume_flow_m3h": 0}'
            fields   = 'QWccc;13492674;105.797;79.73;2023-12-31;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QWddd qwater 13334995 NOKEY'
            telegram = 49449344954933131706780DFF5F3500823A0000600107C113FFFF29970300FF2C580303001E3B269703000A006500750073005B0070007D0061005B004200160000002F046D1E0F113C
            json     = '{"_": "telegram","due_17_date": "2024-11-30","due_17_date_m3": 39.726,"due_date": "2023-12-31","due_date_m3": 30.358,"id": "13334995","media": "warm water","driver": "qwater","meter_datetime": "2024-12-17 15:30","name": "QWddd","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 39.729}'
            fields   = 'QWddd;13334995;39.729;30.358;2023-12-31;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ1 qwater 37439212 NOKEY'
            telegram = 5344934412924337353778077912924337934435070DFF5F3500828A0000100007C113FFFF966600001F3C000000003E3419580000008000800080008000800080008000005A0094009C00BB002F046D010F3235
            json     = '{"_": "telegram","due_17_date": "2025-04-30","due_17_date_m3": 5.819,"due_date": "2024-12-31","due_date_m3": 0,"id": "37439212","media": "radio converter (meter side)","driver": "qwater","meter_datetime": "2025-05-18 15:01","name": "QQ1","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 6.696}'
            fields   = 'QQ1;37439212;6.696;0;2024-12-31;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ2 qwater 37432649 NOKEY'
            telegram = 5344934449264337353778077949264337934435070DFF5F350082560000110007C113FFFF245300001F3C210400003E348946000000800080008000800080008000002A0066005F00730072002F046D000F3235
            json     = '{"_": "telegram","due_17_date": "2025-04-30","due_17_date_m3": 4.689,"due_date": "2024-12-31","due_date_m3": 0.421,"id": "37432649","media": "radio converter (meter side)","driver": "qwater","meter_datetime": "2025-05-18 15:00","name": "QQ2","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 5.324}'
            fields   = 'QQ2;37432649;5.324;0.421;2024-12-31;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ3 qwater 60101441 NOKEY'
            telegram = 39449344411410601A067ADB000020_0C13780000004C1300000000426CFFFFCC081335000000C2086C3F3502BB560000326CFFFF046D14173136
            json     = '{"_": "telegram","due_17_date": "2025-05-31","due_17_date_m3": 0.035,"due_date":null,"due_date_m3": 0,"error_date":null,"id": "60101441","media": "warm water","driver": "qwater","meter_datetime": "2025-06-17 23:20","name": "QQ3","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 0.078,"volume_flow_m3h": 0}'
            fields   = 'QQ3;60101441;0.078;0;null;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ4 qwater 60113189 NOKEY'
            telegram = 39449344893111601a077a580000200c13200300004c1300000000426cffffcc081334000000c2086c3f3502BB560000326cffff046d13173136
            json     = '{"_": "telegram","due_17_date": "2025-05-31","due_17_date_m3": 0.034,"due_date":null,"due_date_m3": 0,"error_date":null,"id": "60113189","media": "water","driver": "qwater","meter_datetime": "2025-06-17 23:19","name": "QQ4","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 0.32,"volume_flow_m3h": 0}'
            fields   = 'QQ4;60113189;0.32;0;null;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ5 qwater 61193115 NOKEY'
            comment  = 'Qundis Qwater5.5 Model 2026 (warm water)'
            telegram = 39449344153119611D067A870000200C13370000004C1300000000426CFFFFCC081300000000C2086CFFFF02BB560000326CFFFF046D320D5E31
            json     = '{"_": "telegram","due_17_date":null,"due_17_date_m3": 0,"due_date":null,"due_date_m3": 0,"error_date":null,"id": "61193115","media": "warm water","driver": "qwater","meter_datetime": "2026-01-30 13:50","name": "QQ5","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 0.037,"volume_flow_m3h": 0}'
            fields   = 'QQ5;61193115;0.037;0;null;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ5 qwater 61193115 NOKEY'
            telegram = 49449344153119611D06780DFF5F3500826E0000F00007C113FFFF66000000FFFF00000000FFFF000000000080008000800080008000800080008000800080008000802F046D33125F31
            json     = '{"_": "telegram","due_17_date":null,"due_17_date_m3": 0,"due_date":null,"due_date_m3": 0,"id": "61193115","media": "warm water","driver": "qwater","meter_datetime": "2026-01-31 18:51","name": "QQ5","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 0.066}'
            fields   = 'QQ5;61193115;0.066;0;null;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ5 qwater 61193115 NOKEY'
            telegram = 49449344153119611D06780DFF5F350082EC0000F10007C113FFFF69000000FFFF000000005F31680000000080008000800080008000800080008000800080008006002F046D1C0A4132
            json     = '{"_": "telegram","due_17_date": "2026-01-31","due_17_date_m3": 0.068,"due_date":null,"due_date_m3": 0,"id": "61193115","media": "warm water","driver": "qwater","meter_datetime": "2026-02-01 10:28","name": "QQ5","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 0.069}'
            fields   = 'QQ5;61193115;0.069;0;null;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'MyQWater19 qwaterv2 51341234 NOKEY'
            comment  = 'Version 19 warm water meter, walk by telegram with mfct data.'
            telegram = 49449344341234511906780dff5f3500822d0000600107c113ffff748702003f3c215102005f374386020031003600330033003600360031003600320037002e002c002f046d0f114b38
            json     = '{"_":"telegram","media":"warm water","driver":"qwaterv2","name":"MyQWater19","id":"51341234","target_m3":28.643,"target_date":"2026-07-31","target_year_m3":25.121,"target_year_date":"2025-12-31","total_m3":28.774,"volume_delta_2_m3":0.049,"volume_delta_3_m3":0.054,"volume_delta_4_m3":0.051,"volume_delta_5_m3":0.051,"volume_delta_6_m3":0.054,"volume_delta_7_m3":0.054,"volume_delta_8_m3":0.049,"volume_delta_9_m3":0.054,"volume_delta_10_m3":0.05,"volume_delta_11_m3":0.055,"volume_delta_12_m3":0.046,"volume_delta_13_m3":0.044,"meter_datetime":"2026-08-11 17:15","status":"OK","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'MyQWater19;51341234;OK;28.774;1111-11-11 11:11.11'
        }
        test {
            args     = 'AESTest qwaterv2 14356101 000102030405060708090A0B0C0D0E0F'
            comment  = 'Encrypted Q walk-by block (header byte[4]=0x35). AES-128-CBC EN 13757-7 Mode 5 with the configured key, Mode-5 IV (M-field+A-field+ACC*8, ACC=header byte[2]). The body decrypts to the legacy plaintext layout and is parsed by the unchanged ixml grammar. The telegram and key are synthetic (not real Qundis material) so the test is self-contained. See issue #1916/#2025.'
            telegram = 49449344016135141907780DFF5F350082C100353E44AAFEDB237BDBA569404984A09F6DD12E32C2F03DD335E259DD446711256309CC4E894B4C7D8CC427C7491B94FF05046D160A4F38
            json     = '{"_": "telegram","id": "14356101","media": "water","driver": "qwaterv2","meter_datetime": "2026-08-15 10:22","name": "AESTest","status": "OK","target_date":null,"target_m3": 0.03,"target_year_date":null,"target_year_m3": 0.02,"timestamp": "1111-11-11T11:11:11Z","total_m3": 0.01,"volume_delta_10_m3": 0.009,"volume_delta_11_m3": 0.01,"volume_delta_12_m3": 0.011,"volume_delta_13_m3": 0.012,"volume_delta_2_m3": 0.001,"volume_delta_3_m3": 0.002,"volume_delta_4_m3": 0.003,"volume_delta_5_m3": 0.004,"volume_delta_6_m3": 0.005,"volume_delta_7_m3": 0.006,"volume_delta_8_m3": 0.007,"volume_delta_9_m3": 0.008}'
            fields   = 'AESTest;14356101;OK;0.01;1111-11-11 11:11.11'
        }
        test {
            args     = 'Kaltwasser qwaterv2 14356101 NOKEY'
            comment  = 'Q Water 5.5 walk-by block with an unsupported layout: no 13FFFF volume marker after the 9-byte header. The ixml grammar must fail closed and must not invent total_m3/target_*/volume_delta_* values from a block it cannot decode. Only the trailing 046D meter_datetime record is real. See issue #1916.'
            telegram = 49449344016135141907780DFF5F350082C100352CAA738213487EAF84ED6BFD4DA668870285651771A8D3CFB7DE5ECC22081CDD1A3FFBA6506F901027DB29B4672708D9046D160A4F38
            json     = '{"_": "telegram","id": "14356101","media": "water","driver": "qwaterv2","meter_datetime": "2026-08-15 10:22","name": "Kaltwasser","status": "MISSING_KEY","timestamp": "1111-11-11T11:11:11Z"}'
            fields   = 'Kaltwasser;14356101;MISSING_KEY;null;1111-11-11 11:11.11'
        }
        test {
            args     = 'RealKeyWater qwaterv2 60170138 5787C5961E48C039A225D4DB4F8ECF06'
            comment  = 'Real encrypted Q Water 5.5 walk-by block with the real key that the meter owner published in issue #1497: proves the AES path end-to-end with genuine key material (the AESTest above uses a synthetic key). The expected values match what the owner posted in #1497. The fail-closed counterpart without a key is covered by Kaltwasser.'
            telegram = 49449344380117601A07780DFF5F350082B50035512F79A321C70F8FD90041D03F7633718457556C82E3A6CB5A20617A9523867033C80F9D21054140A90BC438C04F1E9D046D3B125832
            json     = '{"_":"telegram","driver":"qwaterv2","id":"60170138","media":"water","meter_datetime":"2026-02-24 18:59","name":"RealKeyWater","status":"OK","target_date":"2026-01-31","target_m3":24.086,"target_year_date":"2025-12-31","target_year_m3":20.552,"timestamp":"1111-11-11T11:11:11Z","total_m3":26.546,"volume_delta_10_m3":0.338,"volume_delta_11_m3":0.338,"volume_delta_12_m3":0.363,"volume_delta_13_m3":0.353,"volume_delta_2_m3":-32.768,"volume_delta_3_m3":-32.768,"volume_delta_4_m3":-32.768,"volume_delta_5_m3":-32.768,"volume_delta_6_m3":0.003,"volume_delta_7_m3":0.334,"volume_delta_8_m3":0.343,"volume_delta_9_m3":0.336}'
            fields   = 'RealKeyWater;60170138;OK;26.546;1111-11-11 11:11.11'
        }
    }
}