// Copyright (C) 2017-2026 Fredrik Öhrström (gpl-3.0-or-later)
driver {
    name           = kamwater
    aliases        = multical21,flowiq2200
    meter_type     = WaterMeter
    default_fields = name,id,status,total_m3,target_m3,timestamp
    manufacturer   = Kamstrup
    detect {
        // multical21
        mvt = KAM,1b,06
        // multical21
        mvt = KAM,1b,16
        // flowiq2200
        mvt = KAW,3a,16
        // flowiq2200
        mvt = KAW,3c,16
        mvt = KAW,3c,06
        // flowiq2200
        mvt = KAM,1d,16
        // KWM2230 / 3230, warm
        mvt = KAW,42,06
        // KWM2230 / 3230, cold
        mvt = KAW,42,16
    }
    compact_frame_formats {
        difvif = 02FF2004134413615B6167
        difvif = 02FF20041392013BA1015B8101E7FF0F
        difvif = 04FF234413523B06FF1B426C61675167023B04138101E7FF0F
        difvif = 02FF2004134413426C523B615B616751678101E7FF0F06FF1B
        // Unknown if KamHeat or KamWater
        difvif = 02FF2004134413A1015B8101E7FF0F
        difvif = 02FF2004134413615B5167
        difvif = 02FF2004134413
        difvif = 02FF200413523B
        // Kamstrup KWM2231
        difvif = 04FF2304134413426C023B92013BA2013B06FF1BA1015B91015BA10167
    }
    library {
        use = total_m3
        use = target_m3
        use = target_date
    }
    flags {
        flag {
            name = BURST
            info = 'The flow of water has been consistently high for half an hour, which indicates a pipe burst.'
        }
        flag {
            name = DRY
            info = 'The is no water in the meter. Nothing will be measured.'
        }
        flag {
            name = LEAK
            info = 'The flow of water has not stopped for at least one continuous hour during the last 24 hours.'
        }
        flag {
            name = REVERSE
            info = 'The water flows through the meter in the wrong direction.'
        }
        flag {
            name = TAMPER
            info = 'Attempt of fraud. The meter is no longer valid for billing.'
        }
        flag {
            name = LOW_BATTERY
            info = 'The battery is running low. Less than 6 months remaining.'
        }
        flag {
            name = FLOW_ABOVE_Q4
            info = 'The water flow has exceeded the upper flow limit Q4 which can cause too high error rate.'
        }
        flag {
            name = NO_CONSUMPTION
            info = 'No flow of water has occurred for the last 15 days.'
        }
        flag {
            name = LOW_AMBIENT_TEMPERATURE
            info = 'The ambient temperature is below the configured lower temperature limit.'
        }
        flag {
            name = HIGH_AMBIENT_TEMPERATURE
            info = 'The ambient temperature is above the configured upper temperature limit.'
        }
    }
    templates {
        template_field {
            name = duration_bucket
            lookup {
                name      = DURATION
                map_type  = IndexToString
                mask_bits = 0x7
                map {
                    name  = ''
                    value = 0x0
                    test  = Set
                }
                map {
                    name  = '1-8 hours'
                    value = 0x1
                    test  = Set
                }
                map {
                    name  = '9-24 hours'
                    value = 0x2
                    test  = Set
                }
                map {
                    name  = '2-3 days'
                    value = 0x3
                    test  = Set
                }
                map {
                    name  = '4-7 days'
                    value = 0x4
                    test  = Set
                }
                map {
                    name  = '8-14 days'
                    value = 0x5
                    test  = Set
                }
                map {
                    name  = '15-21 days'
                    value = 0x6
                    test  = Set
                }
                map {
                    name  = '22-31 days'
                    value = 0x7
                    test  = Set
                }
            }
        }
        template_field {
            // Common status fields for modern status fields:
            // - 04FF23 Info Cold environment
            // - 04FF24 Info Warm environment
            // The datasheet also mentions 16bit fields (02FF25, 12FF25), but they are
            // only partially compatible and we have no recursive inheritance.
            name = x2xx_status
            lookup {
                name     = ERROR_FLAGS
                map_type = BitToString
                // In these status fields, buts 0-10 are the instant info (with
                // bits 8 and reserved, and bit 6's different meaning between
                // meters configured for hot/cold *environment*). Bits 11-31
                // are for time indication. But since bits 29-31 are reserved,
                // let's roll them back into the "status" part and not "time
                // indication" part. That way, all parts of the 32bit field are
                // now covered.
                mask_bits       = 0xe00003ff
                default_message = OK
                map {
                    name = DRY
                    bit  = 0
                    test = Set
                }
                map {
                    name = REVERSE
                    bit  = 1
                    test = Set
                }
                map {
                    name = LEAK
                    bit  = 2
                    test = Set
                }
                map {
                    name = BURST
                    bit  = 3
                    test = Set
                }
                map {
                    name = TAMPER
                    bit  = 4
                    test = Set
                }
                map {
                    name = LOW_BATTERY
                    bit  = 5
                    test = Set
                }
                // bit 6 is different and will be filed in later
                map {
                    name = FLOW_ABOVE_Q4
                    bit  = 7
                    test = Set
                }
                // bit 8 is unused
                map {
                    name = NO_CONSUMPTION
                    bit  = 9
                    test = Set
                }
                // bit 10 is unused
            }
        }
    }
    fields {
        field {
            // A fake field which never matches, but propagates the TPL error bits.
            // This is required, because the *driver* cannot really define multiple
            // fields with INCLUDE_TPL_STATUS. If there was more than one such field,
            // the matchers would accidentally overwrite their own values. So, create
            // this virtual one, and let the "real" fields (of which there are multiple
            // alternatives in the driver, and only one of them will match in a real
            // packet) propagate thir stuff.
            name       = status
            quantity   = Text
            attributes = STATUS,INCLUDE_TPL_STATUS
        }
        field {
            name       = status_cold_env
            quantity   = Text
            template   = x2xx_status
            info       = 'Status and error flags.'
            attributes = INJECT_INTO_STATUS,HIDE
            match {
                // "Info Cold environment"
                difvifkey = 04FF23
            }
            lookup {
                name               = ERROR_FLAGS
                map_type           = BitToString
                default_message    = OK
                mark_reserved_bits = true
                map {
                    name = LOW_AMBIENT_TEMPERATURE
                    bit  = 6
                    test = Set
                }
            }
        }
        field {
            name       = status_hot_env
            quantity   = Text
            template   = x2xx_status
            info       = 'Status and error flags.'
            attributes = INJECT_INTO_STATUS,HIDE
            match {
                // "Info Hot environment"
                difvifkey = 04FF24
            }
            lookup {
                name               = ERROR_FLAGS
                map_type           = BitToString
                default_message    = OK
                mark_reserved_bits = true
                map {
                    name = HIGH_AMBIENT_TEMPERATURE
                    bit  = 6
                    test = Set
                }
            }
        }
        field {
            name       = status16
            quantity   = Text
            info       = 'Status and error flags.'
            attributes = INJECT_INTO_STATUS,HIDE
            match {
                difvifkey = 02FF20
            }
            lookup {
                name            = ERROR_FLAGS
                map_type        = BitToString
                mask_bits       = 0x000f
                default_message = OK
                map {
                    name = DRY
                    bit  = 0
                    test = Set
                }
                map {
                    name = REVERSE
                    bit  = 1
                    test = Set
                }
                map {
                    name = LEAK
                    bit  = 2
                    test = Set
                }
                map {
                    name = BURST
                    bit  = 3
                    test = Set
                }
            }
        }
        field {
            name          = current_status
            quantity      = Text
            info          = 'Status of meter. This field will go away use status instead.'
            deprecated_by = 'status, update before 2026-12-01'
            match {
                difvifkey = 02FF20
            }
            lookup {
                name      = ERROR_FLAGS
                map_type  = BitToString
                mask_bits = 0x000f
                map {
                    name = DRY
                    bit  = 0
                    test = Set
                }
                map {
                    name = REVERSE
                    bit  = 1
                    test = Set
                }
                map {
                    name = LEAK
                    bit  = 2
                    test = Set
                }
                map {
                    name = BURST
                    bit  = 3
                    test = Set
                }
            }
        }
        field {
            name     = flow
            quantity = Flow
            info     = 'The current flow of water through the meter.'
            match {
                measurement_type = Instantaneous
                vif_range        = VolumeFlow
            }
        }
        field {
            name     = min_flow_last_month
            quantity = Flow
            info     = 'The minimum flow recorded during the last month.'
            match {
                measurement_type = Minimum
                vif_range        = VolumeFlow
                storage_nr       = 1
            }
        }
        field {
            name     = min_flow_last_day
            quantity = Flow
            info     = 'The minimum flow recorded during the last day.'
            match {
                measurement_type = Minimum
                vif_range        = VolumeFlow
                storage_nr       = 2
            }
        }
        // there's no `min_flow_last_year` in the docs
        field {
            name     = max_flow_last_month
            quantity = Flow
            info     = 'The maximum flow recorded during the last month.'
            match {
                measurement_type = Maximum
                vif_range        = VolumeFlow
                storage_nr       = 1
            }
        }
        field {
            name     = max_flow_last_day
            quantity = Flow
            info     = 'The maximum flow recorded during the last day.'
            match {
                measurement_type = Maximum
                vif_range        = VolumeFlow
                storage_nr       = 2
            }
        }
        field {
            name     = max_flow_last_year
            quantity = Flow
            info     = 'The maximum flow recorded during the last year.'
            match {
                measurement_type = Maximum
                vif_range        = VolumeFlow
                storage_nr       = 3
            }
        }
        field {
            name     = min_flow_temperature_last_month
            quantity = Temperature
            info     = 'The lowest water temperature during the last month.'
            match {
                measurement_type = Minimum
                vif_range        = FlowTemperature
                storage_nr       = 1
            }
        }
        field {
            name     = min_flow_temperature_last_day
            quantity = Temperature
            info     = 'The lowest water temperature during the last day.'
            match {
                measurement_type = Minimum
                vif_range        = FlowTemperature
                storage_nr       = 2
            }
        }
        field {
            name     = min_flow_temperature_last_year
            quantity = Temperature
            info     = 'The lowest water temperature during the last year.'
            match {
                measurement_type = Minimum
                vif_range        = FlowTemperature
                storage_nr       = 3
            }
        }
        field {
            name     = min_external_temperature_last_month
            quantity = Temperature
            info     = 'The lowest external temperature outside of the meter during the last month.'
            match {
                measurement_type = Minimum
                vif_range        = ExternalTemperature
                storage_nr       = 1
            }
        }
        field {
            name     = min_external_temperature_last_day
            quantity = Temperature
            info     = 'The lowest external temperature outside of the meter during the last day.'
            match {
                measurement_type = Minimum
                vif_range        = ExternalTemperature
                storage_nr       = 2
            }
        }
        field {
            name     = min_external_temperature_last_year
            quantity = Temperature
            info     = 'The lowest external temperature outside of the meter during the last year.'
            match {
                measurement_type = Minimum
                vif_range        = ExternalTemperature
                storage_nr       = 3
            }
        }
        field {
            name     = max_flow_temperature_last_month
            quantity = Temperature
            info     = 'The maximum water temperature during the last month.'
            match {
                measurement_type = Maximum
                vif_range        = FlowTemperature
                storage_nr       = 1
            }
        }
        field {
            // Kamstrup docs don't show this field explicitly, but hey, the values are supposed to be examples,
            // and the test suite actually checks for it explicitly
            name     = max_flow_temperature_last_day
            quantity = Temperature
            info     = 'The maximum water temperature during the last day.'
            match {
                measurement_type = Maximum
                vif_range        = FlowTemperature
                storage_nr       = 2
            }
        }
        field {
            name     = max_flow_temperature_last_year
            quantity = Temperature
            info     = 'The maximum water temperature during the last year.'
            match {
                measurement_type = Maximum
                vif_range        = FlowTemperature
                storage_nr       = 3
            }
        }
        field {
            name     = max_external_temperature_last_month
            quantity = Temperature
            info     = 'The maximum temperature recorded during previous month.'
            match {
                measurement_type = Maximum
                vif_range        = ExternalTemperature
                storage_nr       = 1
            }
        }
        field {
            // Kamstrup docs don't show this field explicitly, but hey, the values are supposed to be examples, so let's include it
            name     = max_external_temperature_last_day
            quantity = Temperature
            info     = 'The maximum temperature recorded during previous day.'
            match {
                measurement_type = Maximum
                vif_range        = ExternalTemperature
                storage_nr       = 2
            }
        }
        field {
            name     = max_external_temperature_last_year
            quantity = Temperature
            info     = 'The maximum temperature recorded during previous year.'
            match {
                measurement_type = Maximum
                vif_range        = ExternalTemperature
                storage_nr       = 3
            }
        }
        field {
            name     = time_dry
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been dry.'
            match {
                difvifkey = 02FF20
            }
            lookup {
                pre_shift_right = 4
            }
        }
        field {
            name     = time_dry
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been dry.'
            match {
                difvifkey = 04FF23
            }
            lookup {
                pre_shift_right = 11
            }
        }
        field {
            name     = time_dry
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been dry.'
            match {
                difvifkey = 04FF24
            }
            lookup {
                pre_shift_right = 11
            }
        }
        field {
            name     = time_reversed
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been reversed.'
            match {
                difvifkey = 02FF20
            }
            lookup {
                pre_shift_right = 7
            }
        }
        field {
            name     = time_reversed
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been reversed.'
            match {
                difvifkey = 04FF23
            }
            lookup {
                pre_shift_right = 14
            }
        }
        field {
            name     = time_reversed
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been reversed.'
            match {
                difvifkey = 04FF24
            }
            lookup {
                pre_shift_right = 14
            }
        }
        field {
            name     = time_leaking
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been leaking.'
            match {
                difvifkey = 02FF20
            }
            lookup {
                pre_shift_right = 10
            }
        }
        field {
            name     = time_leaking
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been leaking.'
            match {
                difvifkey = 04FF23
            }
            lookup {
                pre_shift_right = 17
            }
        }
        field {
            name     = time_leaking
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been leaking.'
            match {
                difvifkey = 04FF24
            }
            lookup {
                pre_shift_right = 17
            }
        }
        field {
            name     = time_bursting
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been bursting.'
            match {
                difvifkey = 02FF20
            }
            lookup {
                pre_shift_right = 13
            }
        }
        field {
            name     = time_bursting
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been bursting.'
            match {
                difvifkey = 04FF23
            }
            lookup {
                pre_shift_right = 20
            }
        }
        field {
            name     = time_bursting
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the meter has been bursting.'
            match {
                difvifkey = 04FF24
            }
            lookup {
                pre_shift_right = 20
            }
        }
        field {
            name     = time_low_ambient_temperature
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the ambient temperature was too low.'
            match {
                difvifkey = 04FF23
            }
            lookup {
                pre_shift_right = 23
            }
        }
        field {
            name     = time_high_ambient_temperature
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the ambient temperature was too high.'
            match {
                difvifkey = 04FF24
            }
            lookup {
                pre_shift_right = 23
            }
        }
        field {
            name     = time_flow_above_q4
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the flow was above the Q_4 threshold.'
            match {
                difvifkey = 04FF23
            }
            lookup {
                pre_shift_right = 26
            }
        }
        field {
            name     = time_flow_above_q4
            quantity = Text
            template = duration_bucket
            info     = 'Amount of time the flow was above the Q_4 threshold.'
            match {
                difvifkey = 04FF24
            }
            lookup {
                pre_shift_right = 26
            }
        }
        field {
            /* The internal structure of this field is not known, it's likely not "just a number", so
               let's treat it as a magic blob, do not encode it as a number, and don't indicate any units. */
            name     = accoustic_noise
            quantity = Text
            info     = 'Acoustic noise'
            match {
                difvifkey = 06FF1B
            }
        }
    }
    tests {
        test {
            args     = 'MyTapWater kamwater 76348799 NOKEY'
            telegram = 2A442D2C998734761B168D2091D37CAC21576C78_02FF207100041308190000441308190000615B7F616713
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"MyTapWater","id":"76348799","min_external_temperature_last_month_c":19,"min_flow_temperature_last_month_c":127,"target_m3":6.408,"total_m3":6.408,"current_status":"DRY","current_status_deprecated_by": "status, update before 2026-12-01","status":"DRY","time_bursting":"","time_dry":"22-31 days","time_leaking":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'MyTapWater;76348799;DRY;6.408;6.408;1111-11-11 11:11.11'
        }
        test {
            args     = 'MyTapWater kamwater 76348799 NOKEY'
            telegram = 23442D2C998734761B168D2087D19EAD217F1779EDA86AB6_710008190000081900007F13
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"MyTapWater","id":"76348799","min_external_temperature_last_month_c":19,"min_flow_temperature_last_month_c":127,"target_m3":6.408,"total_m3":6.408,"current_status":"DRY","current_status_deprecated_by": "status, update before 2026-12-01","status":"DRY","time_bursting":"","time_dry":"22-31 days","time_leaking":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'MyTapWater;76348799;DRY;6.408;6.408;1111-11-11 11:11.11'
        }
        test {
            args     = 'Vadden kamwater 44556677 NOKEY'
            telegram = 2D442D2C776655441B168D2083B48D3A20_46887802FF20000004132F4E000092013B3D01A1015B028101E7FF0F03
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"Vadden","id":"44556677","max_flow_last_day_m3h":0.317,"min_flow_temperature_last_day_c":2,"total_m3":20.015,"current_status":"","current_status_deprecated_by": "status, update before 2026-12-01","status":"OK","time_bursting":"","time_dry":"","time_leaking":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'Vadden;44556677;OK;20.015;null;1111-11-11 11:11.11'
        }
        test {
            args     = 'Vadden kamwater 44556677 NOKEY'
            telegram = 21442D2C776655441B168D2079CC8C3A20_F4307912C40DFF00002F4E00003D010203
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"Vadden","id":"44556677","max_flow_last_day_m3h":0.317,"min_flow_temperature_last_day_c":2,"total_m3":20.015,"current_status":"","current_status_deprecated_by": "status, update before 2026-12-01","status":"OK","time_bursting":"","time_dry":"","time_leaking":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'Vadden;44556677;OK;20.015;null;1111-11-11 11:11.11'
        }
        test {
            args     = 'VATTEN kamwater 52525252 NOKEY'
            telegram = 4D44372C525252523A168D203894DF7920F93278_04FF23000000000413AEAC0000441364A80000426C812A023B000092013BEF01A2013B000006FF1B067000097000A1015B0C91015B14A1016713
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"VATTEN","id":"52525252","flow_m3h":0,"max_flow_last_day_m3h":0.495,"max_flow_temperature_last_day_c":20,"min_external_temperature_last_day_c":19,"min_flow_last_day_m3h":0,"min_flow_temperature_last_day_c":12,"target_m3":43.108,"target_date":"2020-10-01","time_bursting":"","time_dry":"","time_flow_above_q4":"","time_leaking":"","time_low_ambient_temperature":"","time_reversed":"","total_m3":44.206,"accoustic_noise":"067000097000","status":"OK","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'VATTEN;52525252;OK;44.206;43.108;1111-11-11 11:11.11'
        }
        test {
            args     = 'Votten kamwater 23813076 NOKEY'
            telegram = 4744372C763081233C168D2056D1ED11205A3C78_04FF2300080000441300000000523B000006FF1B08900008F0FF426CC12B61670A51671B023B000004131F0F00008101E7FF0F13
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"Votten","id":"23813076","flow_m3h":0,"max_external_temperature_last_month_c":27,"target_m3":0,"max_flow_last_month_m3h": 0,"target_date":"2022-11-01","total_m3":3.871,"accoustic_noise":"08900008F0FF","min_external_temperature_last_month_c":10,"status":"OK","time_bursting":"","time_dry":"1-8 hours","time_flow_above_q4":"","time_leaking":"","time_low_ambient_temperature":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'Votten;23813076;OK;3.871;0;1111-11-11 11:11.11'
        }
        test {
            args     = 'Votten kamwater 23813076 NOKEY'
            telegram = 3244372C763081233C168D2057D2ED11205a817905095480_0008000000000000000008900008F0FFC12B0A1B23001F0F000013
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","name":"Votten","id":"23813076","min_external_temperature_last_month_c":10,"flow_m3h":0.035,"max_external_temperature_last_month_c":27,"max_flow_last_month_m3h": 0,"target_m3":0,"target_date":"2022-11-01","total_m3":3.871,"accoustic_noise":"08900008F0FF","status":"OK","time_bursting":"","time_dry":"1-8 hours","time_flow_above_q4":"","time_leaking":"","time_low_ambient_temperature":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'Votten;23813076;OK;3.871;0;1111-11-11 11:11.11'
        }
        test {
            args     = 'MoreWater kamwater 54084283 NOKEY'
            telegram = 2f44372c834208543c168d209e926f9820903279d4364649000062ed000034c9000041373303110f1716077000077000
            json     = '{"_": "telegram","current_status": "","current_status_deprecated_by": "status, update before 2026-12-01","accoustic_noise": "077000077000","min_flow_temperature_last_month_c": 17,"id": "54084283","max_flow_last_month_m3h": 0.819,"max_external_temperature_last_month_c": 23,"media": "cold water","driver": "kamwater","min_external_temperature_last_month_c": 15,"name": "MoreWater","status": "OK","target_date": "2026-07-01","target_m3": 51.508,"time_bursting": "","time_dry": "","time_leaking": "","time_reversed": "","timestamp": "1111-11-11T11:11:11Z","total_m3": 60.77}'
            fields   = 'MoreWater;54084283;OK;60.77;51.508;1111-11-11 11:11.11'
        }
        test {
            comment  = 'Security mode 10 (OMS security profile D), AES-CCM with message counter. Radio corrupted telegram, the aes-ccm tag does not verify, the data is decoded anyway and marked FAILED_DECODE.'
            args     = 'MoreDWater kamwater 53820306 BF902DD6DFADAFE83E4AE6832B5FA14C'
            telegram = 4544372c060382533c16_8c20307a3010ff2a1001dc2e0d00cf1a85672e138d6c0af61338403fdeb528ab264e2c61e41b06073550aa296ae9815c8a7a9868ec6c624cfbbcecb3
            json     = '{"_":"telegram","accoustic_noise":"FFFFFFFFFFFF","driver":"kamwater","id":"53820306","media":"cold water","name":"MoreDWater","status":"TEMPORARY_ERROR FAILED_DECODE","target_m3":0,"timestamp":"1111-11-11T11:11:11Z","total_m3":0}'
            fields   = 'MoreDWater;53820306;TEMPORARY_ERROR FAILED_DECODE;0;0;1111-11-11 11:11.11'
        }
        test {
            comment  = 'Security mode 10 (OMS security profile D), AES-CCM with message counter, valid tag.'
            args     = 'MoreDWater kamwater 53820306 BF902DD6DFADAFE83E4AE6832B5FA14C'
            telegram = 4544372c060382533c16_8c20307a3010ff2a10018c570e006da7632471f42b0a3ceccd461353573ed02b87118bc5ffe982f7f00a0274543bc51938f8b9c8bb8928114d60788a
            json     = '{"_":"telegram","accoustic_noise":"FFFFFFFFFFFF","driver":"kamwater","id":"53820306","media":"cold water","name":"MoreDWater","status":"TEMPORARY_ERROR","target_m3":0,"timestamp":"1111-11-11T11:11:11Z","total_m3":0}'
            fields   = 'MoreDWater;53820306;TEMPORARY_ERROR;0;0;1111-11-11 11:11.11'
        }
        test {
            comment  = 'synthetic: cold env, dry, low ambient with some duration'
            args     = 'synth-cold-dry-low kamwater 52525252 NOKEY'
            telegram = 2044372C525252523A168D203894DF7920_1b42_78_04FF2301000001
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","id":"52525252","name":"synth-cold-dry-low","status":"DRY","time_bursting":"","time_dry":"","time_flow_above_q4":"","time_leaking":"","time_low_ambient_temperature":"9-24 hours","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'synth-cold-dry-low;52525252;DRY;null;null;1111-11-11 11:11.11'
        }
        test {
            comment  = 'synthetic: hot env, dry, high ambient with some duration'
            args     = 'synth-hot-dry-high kamwater 52525252 NOKEY'
            telegram = 2044372C525252523A168D203894DF7920_898b_78_04FF2401000001
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","id":"52525252","name":"synth-hot-dry-high","status":"DRY","time_bursting":"","time_dry":"","time_flow_above_q4":"","time_leaking":"","time_high_ambient_temperature":"9-24 hours","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'synth-hot-dry-high;52525252;DRY;null;null;1111-11-11 11:11.11'
        }
        test {
            comment  = 'syntetic: error propagation from TPL with no status'
            args     = 'synth-tpl-no-status kamwater 52525252 NOKEY'
            telegram = 1944372C525252523A168D203894DF7920_5053_7a44020000_441301000000
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","id":"52525252","name":"synth-tpl-no-status","status":"ERROR","target_m3":0.001,"timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'synth-tpl-no-status;52525252;ERROR;null;0.001;1111-11-11 11:11.11'
        }
        test {
            comment  = 'syntetic: error propagation from TPL with different error in status'
            args     = 'synth-tpl-dry kamwater 52525252 NOKEY'
            telegram = 1944372C525252523A168D203894DF7920_4bf4_7a44020000_04FF2301000000
            json     = '{"_":"telegram","media":"cold water","driver":"kamwater","id":"52525252","name":"synth-tpl-dry","status": "DRY ERROR","time_bursting":"","time_dry":"","time_flow_above_q4":"","time_leaking":"","time_low_ambient_temperature":"","time_reversed":"","timestamp":"1111-11-11T11:11:11Z"}'
            fields   = 'synth-tpl-dry;52525252;DRY ERROR;null;null;1111-11-11 11:11.11'
        }
    }
}