// 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
    detect {
        mvt = QDS,33,37
        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,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.
            ixml = "decode = quad, quad, byte, 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": "2128-03-31","id": "12353648","media": "warm water","meter": "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","meter": "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","meter":"qwaterv2","name":"AnotherQWater","id":"66666666","device_error_date":"2128-03-31","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","meter":"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":"2128-03-31","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","meter":"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":"2128-03-31","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": "2128-03-31","id": "13144514","media": "warm water","meter": "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": "2128-03-31","id": "13176890","media": "water","meter": "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","meter": "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","meter": "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": "2128-03-31","id": "13492674","media": "water","meter": "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","meter": "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)","meter": "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)","meter": "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": "2128-03-31","due_date_m3": 0,"error_date": "2128-03-31","id": "60101441","media": "warm water","meter": "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;2128-03-31;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": "2128-03-31","due_date_m3": 0,"error_date": "2128-03-31","id": "60113189","media": "water","meter": "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;2128-03-31;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": "2128-03-31","due_17_date_m3": 0,"due_date": "2128-03-31","due_date_m3": 0,"error_date": "2128-03-31","id": "61193115","media": "warm water","meter": "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;2128-03-31;OK;1111-11-11 11:11.11'
        }
        test {
            args     = 'QQ5 qwater 61193115 NOKEY'
            telegram = 49449344153119611D06780DFF5F3500826E0000F00007C113FFFF66000000FFFF00000000FFFF000000000080008000800080008000800080008000800080008000802F046D33125F31
            json     = '{"_": "telegram","due_17_date": "2128-03-31","due_17_date_m3": 0,"due_date": "2128-03-31","due_date_m3": 0,"id": "61193115","media": "warm water","meter": "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;2128-03-31;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": "2128-03-31","due_date_m3": 0,"id": "61193115","media": "warm water","meter": "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;2128-03-31;OK;1111-11-11 11:11.11'
        }
    }
}