// 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'
        }
    }
}