// Copyright (C) 2024 Fredrik Öhrström (gpl-3.0-or-later)
driver {
name = elf2
meter_type = HeatMeter
default_fields = name,id,status,total_energy_kwh,timestamp
detect {
mvt = APA,42,04
mvt = APA,42,0d
}
library {
use = fabrication_no
use = on_time_h
use = on_time_at_error_h
}
fields {
field {
name = meter
quantity = PointInTime
info = 'Meter date when telegram was sent.'
display_unit = date
match {
measurement_type = Instantaneous
vif_range = Date
}
}
field {
name = meter
quantity = PointInTime
info = 'Meter date when telegram was sent.'
display_unit = datetime
match {
measurement_type = Instantaneous
vif_range = DateTime
}
}
field {
name = t2_temperature
quantity = Temperature
info = 'Temperature of returned water.'
match {
measurement_type = Instantaneous
vif_range = ReturnTemperature
}
}
field {
name = t1_temperature
quantity = Temperature
info = 'Temperature of incoming water.'
match {
measurement_type = Instantaneous
vif_range = FlowTemperature
}
}
field {
name = current_power
quantity = Power
info = 'Instantaneous power consumed.'
match {
measurement_type = Instantaneous
vif_range = AnyPowerVIF
}
}
field {
name = current_volume_flow
quantity = Flow
info = 'Instantaneous water flow.'
match {
measurement_type = Instantaneous
vif_range = VolumeFlow
}
}
field {
name = total_volume
quantity = Volume
info = 'Total volume of water used for heating.'
match {
measurement_type = Instantaneous
vif_range = AnyVolumeVIF
}
}
field {
name = total_volume_cooling
quantity = Volume
info = 'Total volume of water used for cooling.'
match {
measurement_type = Instantaneous
vif_range = AnyVolumeVIF
subunit_nr = 1
tariff_nr = 0
storage_nr = 0
}
}
field {
name = input1_volume
quantity = Volume
info = 'Total volume of water used in input1.'
match {
measurement_type = Instantaneous
vif_range = AnyVolumeVIF
subunit_nr = 0
tariff_nr = 1
storage_nr = 0
}
}
field {
name = input2_volume
quantity = Volume
info = 'Total volume of water used in input2.'
match {
measurement_type = Instantaneous
vif_range = AnyVolumeVIF
subunit_nr = 0
tariff_nr = 2
storage_nr = 0
}
}
field {
name = total_energy
quantity = Energy
info = 'The total heat energy consumption recorded by this meter.'
match {
measurement_type = Instantaneous
vif_range = AnyEnergyVIF
}
}
field {
name = total_energy_cooling
quantity = Energy
info = 'The total cooling energy consumption recorded by this meter.'
match {
measurement_type = Instantaneous
vif_range = AnyEnergyVIF
subunit_nr = 1
tariff_nr = 0
storage_nr = 0
}
}
field {
name = total_energy_period
quantity = Energy
info = 'The total cooling energy consumption recorded by this meter.'
match {
measurement_type = Instantaneous
vif_range = AnyEnergyVIF
storage_nr = 1
}
}
field {
name = total_energy_cooling_period
quantity = Energy
info = 'The total cooling energy consumption recorded by this meter.'
match {
measurement_type = Instantaneous
vif_range = AnyEnergyVIF
subunit_nr = 1
tariff_nr = 0
storage_nr = 1
}
}
field {
name = status
quantity = Text
info = 'Status and error flags.'
attributes = 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 = MINIMUM_FLOW
info = 'Average minute flow is below the minimum flow and higher than the starting flow.'
value = 0x01
test = Set
}
map {
name = FLOW_METER_FAILURE
info = 'No flow detected and temperature diff is higher than threshold.'
value = 0x02
test = Set
}
map {
name = RETURN_TEMPERATURE_ERROR
info = 'Temperature is out of measurement range or the sensor is broken.'
value = 0x04
test = Set
}
map {
name = SUPPLY_TEMPERATURE_ERROR
info = 'Temperature is out of measurement range or the sensor is broken.'
value = 0x08
test = Set
}
map {
name = DIFFERENTIAL_TEMPERATURE_ERROR
info = 'The temperature differences (eg supply-return) exceeds a threshold.'
value = 0x10
test = Set
}
map {
name = MAXIMUM_FLOW
info = 'Average minute flow Qs is higher than Qp and equal to or lower than 2Qp (Qp < Qs ≤ 2 * Qp).'
value = 0x20
test = Set
}
map {
name = MEMORY_FAILURE
info = 'Critical internal error.'
value = 0x40
test = Set
}
map {
name = LOW_BATTERY_VOLTAGE
info = 'Low battery voltage.'
value = 0x80
test = Set
}
map {
name = DAILY_ABNORMAL_NOMINAL_FLOW
info = 'Nominal flow is detected at Q > Qp for 1 h per day (24 h)
where one day is measured from the moment the overrun is detected.'
value = 0x100
test = Set
}
map {
name = ANNUAL_ABNORMAL_NOMINAL_FLOW
info = 'Nominal flow is detected at Q > Qp for 200 h in 1 year
where one year is calculated from the start of billing period n to
the start of billing period n+1 (which is when the annual data is saved to the archive).'
value = 0x200
test = Set
}
map {
name = DIFFERENTIAL_TEMPERATURE_TOO_LOW
info = 'The volume count is incremented in 3 consecutive integration cycles while
the differential temperature is below the configured differential temperature
insensitivity threshold.'
value = 0x400
test = Set
}
map {
name = CRC_ERROR
info = 'Critical internal error in firmware vs saved model.'
value = 0x800
test = Set
}
map {
name = FLASH_ERROR
info = 'Critical internal error when reading non-volatile flash memory.'
value = 0x1000
test = Set
}
map {
name = CRITICAL_BATTERY_VOLTAGE
info = 'Critical low battery voltage.'
value = 0x2000
test = Set
}
map {
name = CPU_OVERTEMPERATURE
info = 'CPU is overheating.'
value = 0x4000
test = Set
}
map {
name = UART_LIMIT_OVERRUN
info = 'Sent and received bytes exceeds the byte count limit.'
value = 0x8000
test = Set
}
}
}
}
tests {
test {
args = 'Heato elf2 23170428 NOKEY'
telegram = 684E4E68080172280417230106420455000000_0C7828041723026C103902FD1700000E0A6342693401000C13634630000A3B00000A2D00000A5A49020A5E44020C22762101003C2215000000047E17090000_4116
json = '{"_":"telegram","current_power_kw": 0,"current_volume_flow_m3h": 0,"fabrication_no": "23170428","id": "23170428","media": "heat","meter": "elf2","meter_date": "2024-09-16","name": "Heato","on_time_at_error_h": 15,"on_time_h": 12176,"status": "OK","t1_temperature_c": 24.9,"t2_temperature_c": 24.4,"timestamp": "1111-11-11T11:11:11Z","total_energy_kwh": 3741.507306,"total_volume_m3": 304.663}'
fields = 'Heato;23170428;OK;3741.507306;1111-11-11 11:11.11'
}
test {
args = 'Heato elf2 24271170 ACA5769E7902B8A770A7118C11D5F0F6'
telegram = 6E44010670112724420D7A35006025B2F96D0306197B34C7E4E6F45F434FCB78BCBBE8837A2C253BEC3E958B3BBBFBE6AE465214941AF544237DB8A69E6C4CE8DFB7A1F64860200BB2E995F1A4F501E7702E0A581576F60B58CBDA39E1F150F29E04D689C38FD6C430ABD05DD789E0
json = '{"_":"telegram","media":"heat/cooling load","meter":"elf2","name":"Heato","id":"24271170","current_power_kw":0,"current_volume_flow_m3h":0,"input1_volume_m3":0.002,"input2_volume_m3":0.002,"meter_datetime":"2025-10-15 14:39","t1_temperature_c":22.5,"t2_temperature_c":22.6,"total_energy_kwh":144,"total_energy_cooling_kwh":1,"total_energy_cooling_period_kwh":1,"total_energy_period_kwh":72,"total_volume_m3":17.856,"total_volume_cooling_m3":1.576,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}'
fields = 'Heato;24271170;OK;144;1111-11-11 11:11.11'
}
}
}