// 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
detect {
// multical21
mvt = KAM,1b,06
// multical21
mvt = KAM,1b,16
// flowiq2200
mvt = KAW,3a,16
// flowiq2200
mvt = KAW,3c,16
// 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
// 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
}
fields {
field {
name = status
quantity = Text
info = 'Status and error flags.'
attributes = STATUS,INCLUDE_TPL_STATUS
match {
difvifkey = 04FF23
}
lookup {
name = ERROR_FLAGS
map_type = BitToString
mask_bits = 0xffffffff
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
}
map {
name = LOW_ABIENT_TEMPERATURE
bit = 6
test = Set
}
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
}
}
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.'
attributes = DEPRECATED
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 = flow_temperature
quantity = Temperature
info = 'The water temperature.'
match {
measurement_type = Minimum
vif_range = FlowTemperature
storage_nr = 1,2
}
}
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 = max_flow
quantity = Flow
info = 'The maximum flow recorded during previous period.'
match {
measurement_type = Maximum
vif_range = VolumeFlow
storage_nr = 2
}
}
field {
name = min_flow_temperature
quantity = Temperature
info = 'The minimum water temperature.'
match {
measurement_type = Minimum
vif_range = FlowTemperature
storage_nr = 2
}
}
field {
name = max_flow_temperature
quantity = Temperature
info = 'The maximum water temperature.'
match {
measurement_type = Maximum
vif_range = FlowTemperature
storage_nr = 2
}
}
field {
name = max_external_temperature
quantity = Temperature
info = 'The maximum temperature recorded during previous period.'
match {
measurement_type = Maximum
vif_range = ExternalTemperature
storage_nr = 1
}
}
field {
name = min_flow
quantity = Flow
info = 'The minimum flow recorded during previous period.'
match {
measurement_type = Minimum
vif_range = VolumeFlow
storage_nr = 2
}
}
field {
name = time_dry
quantity = Text
info = 'Amount of time the meter has been dry.'
match {
difvifkey = 02FF20
}
lookup {
name = DRY
map_type = IndexToString
mask_bits = 0x0070
map {
name = ''
value = 0x0000
test = Set
}
map {
name = '1-8 hours'
value = 0x0010
test = Set
}
map {
name = '9-24 hours'
value = 0x0020
test = Set
}
map {
name = '2-3 days'
value = 0x0030
test = Set
}
map {
name = '4-7 days'
value = 0x0040
test = Set
}
map {
name = '8-14 days'
value = 0x0050
test = Set
}
map {
name = '15-21 days'
value = 0x0060
test = Set
}
map {
name = '22-31 days'
value = 0x0070
test = Set
}
}
}
field {
name = time_reversed
quantity = Text
info = 'Amount of time the meter has been reversed.'
match {
difvifkey = 02FF20
}
lookup {
name = REVERSED
map_type = IndexToString
mask_bits = 0x0380
map {
name = ''
value = 0x0000
test = Set
}
map {
name = '1-8 hours'
value = 0x0080
test = Set
}
map {
name = '9-24 hours'
value = 0x0100
test = Set
}
map {
name = '2-3 days'
value = 0x0180
test = Set
}
map {
name = '4-7 days'
value = 0x0200
test = Set
}
map {
name = '8-14 days'
value = 0x0280
test = Set
}
map {
name = '15-21 days'
value = 0x0300
test = Set
}
map {
name = '22-31 days'
value = 0x0380
test = Set
}
}
}
field {
name = time_leaking
quantity = Text
info = 'Amount of time the meter has been leaking.'
match {
difvifkey = 02FF20
}
lookup {
name = LEAKING
map_type = IndexToString
mask_bits = 0x1c00
map {
name = ''
value = 0x0000
test = Set
}
map {
name = '1-8 hours'
value = 0x0400
test = Set
}
map {
name = '9-24 hours'
value = 0x0800
test = Set
}
map {
name = '2-3 days'
value = 0x0c00
test = Set
}
map {
name = '4-7 days'
value = 0x1000
test = Set
}
map {
name = '8-14 days'
value = 0x1400
test = Set
}
map {
name = '15-21 days'
value = 0x1800
test = Set
}
map {
name = '22-31 days'
value = 0x1c00
test = Set
}
}
}
field {
name = time_bursting
quantity = Text
info = 'Amount of time the meter has been bursting.'
match {
difvifkey = 02FF20
}
lookup {
name = BURSTING
map_type = IndexToString
mask_bits = 0xe000
map {
name = ''
value = 0x0000
test = Set
}
map {
name = '1-8 hours'
value = 0x2000
test = Set
}
map {
name = '9-24 hours'
value = 0x4000
test = Set
}
map {
name = '2-3 days'
value = 0x6000
test = Set
}
map {
name = '4-7 days'
value = 0x8000
test = Set
}
map {
name = '8-14 days'
value = 0xa000
test = Set
}
map {
name = '15-21 days'
value = 0xc000
test = Set
}
map {
name = '22-31 days'
value = 0xe000
test = Set
}
}
}
}
tests {
test {
args = 'MyTapWater multical21 76348799 NOKEY'
telegram = 2A442D2C998734761B168D2091D37CAC21576C78_02FF207100041308190000441308190000615B7F616713
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"MyTapWater","id":"76348799","min_external_temperature_last_month_c":19,"flow_temperature_c":127,"target_m3":6.408,"total_m3":6.408,"current_status":"DRY","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 multical21 76348799 NOKEY'
telegram = 23442D2C998734761B168D2087D19EAD217F1779EDA86AB6_710008190000081900007F13
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"MyTapWater","id":"76348799","min_external_temperature_last_month_c":19,"flow_temperature_c":127,"target_m3":6.408,"total_m3":6.408,"current_status":"DRY","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 multical21 44556677 NOKEY'
telegram = 2D442D2C776655441B168D2083B48D3A20_46887802FF20000004132F4E000092013B3D01A1015B028101E7FF0F03
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"Vadden","id":"44556677","flow_temperature_c":2,"max_flow_m3h":0.317,"min_flow_temperature_c":2,"total_m3":20.015,"current_status":"","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 multical21 44556677 NOKEY'
telegram = 21442D2C776655441B168D2079CC8C3A20_F4307912C40DFF00002F4E00003D010203
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"Vadden","id":"44556677","flow_temperature_c":2,"max_flow_m3h":0.317,"min_flow_temperature_c":2,"total_m3":20.015,"current_status":"","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 flowiq2200 52525252 NOKEY'
telegram = 4D44372C525252523A168D203894DF7920F93278_04FF23000000000413AEAC0000441364A80000426C812A023B000092013BEF01A2013B000006FF1B067000097000A1015B0C91015B14A1016713
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"VATTEN","id":"52525252","flow_m3h":0,"flow_temperature_c":12,"max_flow_m3h":0.495,"max_flow_temperature_c":20,"min_external_temperature_last_day_c":19,"min_flow_m3h":0,"min_flow_temperature_c":12,"target_m3":43.108,"target_date":"2020-10-01","total_m3":44.206,"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 flowiq2200 23813076 NOKEY'
telegram = 4744372C763081233C168D2056D1ED11205A3C78_04FF2300080000441300000000523B000006FF1B08900008F0FF426CC12B61670A51671B023B000004131F0F00008101E7FF0F13
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"Votten","id":"23813076","flow_m3h":0,"max_external_temperature_c":27,"target_m3":0,"target_date":"2022-11-01","total_m3":3.871,"min_external_temperature_last_month_c":10,"status":"ERROR_FLAGS_800","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'Votten;23813076;ERROR_FLAGS_800;3.871;0;1111-11-11 11:11.11'
}
test {
args = 'Votten flowiq2200 23813076 NOKEY'
telegram = 3244372C763081233C168D2057D2ED11205a817905095480_0008000000000000000008900008F0FFC12B0A1B23001F0F000013
json = '{"_":"telegram","media":"cold water","meter":"kamwater","name":"Votten","id":"23813076","min_external_temperature_last_month_c":10,"flow_m3h":0.035,"max_external_temperature_c":27,"target_m3":0,"target_date":"2022-11-01","total_m3":3.871,"status":"ERROR_FLAGS_800","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'Votten;23813076;ERROR_FLAGS_800;3.871;0;1111-11-11 11:11.11'
}
}
}