Use case: Convert cumulative meter reading (e.g. for heat/energy (kWh) or water (l)) into daily / hourly consumptions (W or l/h)

I’m using the excellent wmbusmeters software and AMB8465 USB stick a to capture and store the Wireless M-Bus values broadcasted by my heat meter (Techem / Kamstrup vario 3 type 3.2.1) and water meter (Techem radio 4). Unfortunately, both meters only broadcast the cumulative (ever growing) values of the energy / water consumed irregularly (every 5 - 20 minutes). These cumulative values are also only updated irregularly every ~24 hours.

Simply logging these values in openHAB results in something like this:


While this gives you a good rough impression on the total energy consumed, understanding more nuanced differences in consumption (e.g. between days, times of the year, etc) is difficult.

The goal was to come up with something that calculates and persists the consumption per time. As indicated here by @Sven_Festersen , since energy is the time integral of power, for e.g. the power meter the time derivative of that value P = dE/dt needs to be calculated. @hhors provided a solution here, but my requirement was slightly different, and I wanted to understand this bottom-up myself.

This is the result. Comments & suggestions are welcome.

rule "Konvertierung - Warmwasserzaehler"


when
    Item warmwasserzaehler_gesamt changed

then

    // The following rule calculates the average consumption (e.g. W or l/h) between two (cummulative) meter readings (e.g. kWh or l).
    // This is needed when the connected meter frequently only provides cummulative meter readings, instead of average consumption values.
    // To do so, the script stores the last two meter readings and their timestamps (Step 1), then calculates the delta between the two 
    // timestamps and meter readings (Step 2), and then calculates the consumption per timeframe by dividing consumption by time (Step 3).
    // It must be triggered only by a change in the meter reading.
    // The following variables are at play:
    // warmwasserzaehler_zeit_t_minus_1 - Stores the timestamp (# of epoch minutes since 1970) of the last meter reading, manually created, Type: Number, Semantic Class: Point
    // warmwasserzaehler_zeit_t_minus_2 - Stores the timestamp (# of epoch minutes since 1970) of the second last meter reading, manually created, Type: Number, Semantic Class: Point
    // warmwasserzaehler_zeit_delta - Stores the difference in minutes between the last meter reading and the second last meter reading, manually created, Type: Number, Semantic Class: Point
    // warmwasserzaehler_zaehlerstand_t_minus_1 - Stores the meter reading (e.g. kWh or l) of the last meter reading, manually created, Type: Number, Semantic Class: Point
    // warmwasserzaehler_zaehlerstand_t_minus_2 - Stores the meter reading (e.g. kWh or l) of the second last meter reading, manually created, Type: Number, Semantic Class: Point
    // warmwasserzaehler_zaehlerstand_delta - Stores the difference in meter reading (e.g. kWh or l) between the last meter reading and the second last meter reading, manually created, Type: Number, Semantic Class: Point
    // warmwasserzaehler_leistung : Stores the calculated average consumption (e.g. W or l/h) for the time between the last and second last meter reading, manually created, Type: Number, Semantic Class: Point

    // Step 0: Check if the current meter reading is different from the latest stored meter reading (warmwasserzaehler_zaehlerstand_t_minus_1)
    // If yes, an energy consumption can be calculated: Execute the script and calculate the energy consumption.
    // If no, no new meter reading is available, and executing the script would "reset" the time difference",
    // which would result in wrong values for the subsequent calculation

    if(warmwasserzaehler_gesamt.getState() != warmwasserzaehler_zaehlerstand_t_minus_1.state){

        // Step 1.1: Store the last two meter readings
        // Make room to store the new (latest) meter reading by moving the old (second last) meter reading to the second last meter reading variable warmwasserzaehler_zaehlerstand_t_minus_2
        warmwasserzaehler_zaehlerstand_t_minus_2.postUpdate(warmwasserzaehler_zaehlerstand_t_minus_1.state)
        // Capture the new (latest) meter reading in warmwasserzaehler_zaehlerstand_t_minus_1 (freed up before)
        warmwasserzaehler_zaehlerstand_t_minus_1.postUpdate(warmwasserzaehler_gesamt.getState())
        // Print the values as cross-check
        logWarn("status", "t-2: " + warmwasserzaehler_zaehlerstand_t_minus_2.getState() + " MWh; " + "t-1: " + warmwasserzaehler_zaehlerstand_t_minus_1.getState() + " MWh")

        // Step 1.2: Store the timestamps of the last two meter readings
        // Make room to store the new (latest) timestamp by moving the old (second last) timestamp to the second last timestamp variable warmwasserzaehler_time_t_minus_2
        warmwasserzaehler_zeit_t_minus_2.postUpdate(warmwasserzaehler_zeit_t_minus_1.state)
        // Capture the new (latest) timestamp in warmwasserzaehler_zeit_t_minus_1 (freed up before)
        var current_Time = now.toInstant().toEpochMilli() / 60000
        warmwasserzaehler_zeit_t_minus_1.postUpdate(current_Time)
        // Print the values as cross-check
        logWarn("status", "t-2: " + warmwasserzaehler_zeit_t_minus_2.getState() + " minutes; " + "t-1: " + warmwasserzaehler_zeit_t_minus_1.getState() + " minutes")
    
        // Step 2.1: Calculate the differences between the two timestamps
        val delta_zeit = (warmwasserzaehler_zeit_t_minus_1.state as Number) - (warmwasserzaehler_zeit_t_minus_2.state as Number)
        warmwasserzaehler_zeit_delta.postUpdate(delta_zeit)
        // Print the values as cross-check 
        logWarn("notifications", "Delta Zeit: " + delta_zeit + " Minutes")

        // Step 2.1: Calculate the differences between the two meter readings
        val delta_zaehlerstand = (warmwasserzaehler_zaehlerstand_t_minus_1.state as Number) - (warmwasserzaehler_zaehlerstand_t_minus_2.state as Number)
        warmwasserzaehler_zaehlerstand_delta.postUpdate(delta_zaehlerstand)
        // Print the values as cross-check
        logWarn("notifications", "Delta Zählerstand: " + delta_zaehlerstand + " MWh")

        // Step 3: Calculate the energy consumption by dividing the the delta in meter reading by the delta in time
        val leistung = ((delta_zaehlerstand) / (delta_zeit/60))*1000
        warmwasserzaehler_leistung.postUpdate(String.format("%.1f", leistung))
        // Print the values as cross-check (rounded to 1 digit)
        logWarn("notifications", "Leistung: " + String.format("%.1f", leistung) + " kW")

    }
    else {
        logWarn("notifications", "No heat meter differnece in meter reading detected: Nothing updated.")    
    }
end

Update: Adjusted by an additional check that protects against unintended script execution (and thus wrong results)

4 Likes

Is it a requirement to have this data in openhab? Because when you create graphs with Grafana, everything is done for you.
But this is a nice solution to have all this data stored.

Fair point. For now I’m trying to keep things simple / working in openHAB only without any additional software. But I’ll have a look.

Do you run wmbusmeter same CPU as openhabian?
I am on RPi 4 and looking to go for it (value of metres)

Yes. Running openHAB and wmbusmeters as Docker containers on a Raspi 3.

Thx, good to know that one Pi is enough.

Do you think it could be possible to install wmbusmeters to existing OH openhabian edition?

Changes from openhabian to docker installation scares me a little as unknown ground to myself :frowning:

Can understand your hesitation.

Since openHABian is based on Raspberry Pi OS Lite this could work.

I once managed to install wmbusmeters manually on my Linux Mint via installing snap (1) Install wmbusmeters on Linux Mint using the Snap Store | Snapcraft (not sure if it’s the same for Raspberry Pi OS), and then went the recommended way via (2) Installation of snapd | wmbusmeterswiki and then (3) afterwards https://github.com/weetmuts/wmbusmeters/blob/master/README.md.

Went pretty smooth even though I’m not a pro.

Thanks a lot Cplant,
Have done step 1&2 however step 3 stopped me for good.

While trying to modify wmbusmeters.conf with nano, the warnings appears only:
[ File ‘wmbusmeters.conf’ is unwritable ]
[ Error writing wmbusmeters.conf: Permission denied ]

whether I should be logged in somehow other than: openhabian@openhabian:/var/snap/wmbusmeters/common/etc

so far tried with systemctl stop snap.wmbusmeters.wmbusmeters.service but same access issue found :frowning:

With regards to Linux I can’t be of much help, other than saying that sudo nano does get the job done for me most of the time.

When it comes to wmbusmeters, I’m running it as a docker container and made it accessible as a Samba share to edit the wmbusmeters.conf.

openHAB itself runs under user ‘openhab’, not openhabian.

In case this is still relevant: As part of my „firedrill“ (SD card fails and I have to install everything forms scratch) I cleaned up my protocol and also published it here.“ as a step-by-step. Maybe you find this useful to do the step.

Wow Cplant, it looks great - lot of work to do that step-by-step documentation! Will try it for sure on the weekend