How would i calculate Daily/Weekly/Monthly values off an item?

I’m trying to figure out the best way to determine Daily (Monday/Tuesday/Wed/Thur/Fri/Sat/Sun), Weekly, Monthly, Yearly numbers of an item that increments up each day and resets back to 0 at midnight.

For example i have water flow sensor hooked that sends the # of gallons used to an item called vHotWater_Total_Gallons. I then have a cron scheduled at midnight to reset the items value back to 0 so i know the daily consumed water.

Is there a easy way with Persistence to take the top value each day and add them together to find Daily (including the past week values in mon/tue/wed/…etc) and weekly/monthly yearly?

Or is there a different way i should go about calculating these item values

Would this be an efficient way to log Monday/Tuesday/Wed/Thur/Fri/Sat values?

rule "Log DOW Values and reset counter to 0 at midnight"
when
    Time cron "0 0 0 * * ?"  // Fires midnight every day
then
//var DOW = ""
switch now.getDayOfWeek{
        case 1: {
           // DOW = "Monday"
           //Log Monday
            vHotWater_Gallons_Monday.sendCommand((vHotWater_totalGallons.state as Number))
        }
        case 2: {
            //DOW = "Tuesday"
            vHotWater_Gallons_Tuesday.sendCommand((vHotWater_totalGallons.state as Number))
        }
        case 3: {
            //DOW= "Wednesday"
        }
        case 4: {
            //DOW= "Thursday"
        }
        case 5: {
            //DOW= "Friday"
        }
        case 6: {
            //DOW= "Saturday"
        }
        case 7: {
            //DOW= "Sunday"
        }
    }
    //Reset Water Flow Sensor Counter Back to 0
    vHotWater_totalGallons.sendCommand(0)
end

The answer is partially yes. Persistence will let you sum up (or average, or find the min, or find the max) of all the values stored in the database between now and some time in the past. So if you want to know how much water has been used in the past 30 days, for example, we can easily handle that.

But for that to work, you will need to transfer the running total Item’s value to an Item that just stores the value at midnight just before it gets reset.

Or, you can not have vHotWater_totalGallons persisted automatically and call vHotWater_totalGallons.persist at midnight to just store the value at midnight. But I don’t recommend that approach because if OH happens to restart during the day, you will lose all the data that was collected up to that point.

To handle periods like the lat 30 days you can just use calls like

vHotWater_totalGallons.sumSince(now.minusDays(30))

If you want just for the current month, you will have to do some tricks to calculate the number of days since the start of the month. The now.withDayOfMonth will probably be your best bet.

vHotWater_totalGallons.sumSince(now.minusDays(now.withDayOfMonth.dayOfMonth))

But if I understand correctly, you want to have a sum of values just for Mondays, and another one just for Tuesdays, and so on.

To do this I think your approach is probably the way to go but I think there is a better way to do it.

If you name your Items as vHotWater_Gallons_ByDay_1, for example, then you can completely replace the switch statement with a single line:

postUpdate("vHotWater_Gallons_ByDay_"+now.getDayOfWeek, vHotWater_totalGallons.state.toString)

Using the above, you can expand this to also update your current month and current year totals (there is a withDayOfYear).

4 Likes

Super awesome this gives me some go info to go by ! Thanks a lot when i have the rules working i’ll post my code as i think more people might want to do this :slight_smile:

I just build something like that for engery consumption. My energy meter send evry 3 minutes a new value. The difference between the two last values is added to a daily counter that is reset at midnight. So i see during the day an increaing value and with the yesterday counter i generate a Grafana diagramm.

//---------------------------------------------------------------------------------------------------------------------
//
//  Rule: Energy counter end of day processing
//
//---------------------------------------------------------------------------------------------------------------------
rule "Energy counter end of day processing"
when

    Time cron "59 59 23 ? * * *"

then

    val String ruleIdentifier = "userRule.Energy_counter_end_of_day_processing"
    var String logMessage

    gDailyCounter.members.forEach[ counter | 

        // Get thing identification
        val String groupPrefix = counter.name.substring(0, counter.name.lastIndexOf('_'))

        // Set yesterday counter
        (ScriptServiceUtil.getItemRegistry.getItem(groupPrefix+"_YesterdayCounter") as NumberItem).postUpdate(counter.state as DecimalType)

        // Reset daily counter
        counter.postUpdate(0)

    ]

    logMessage = String::format("Energy counter end of day processing finished!")
    logInfo(ruleIdentifier, logMessage)

end


//---------------------------------------------------------------------------------------------------------------------
//
//  Rule: Update daily energy counters
//
//---------------------------------------------------------------------------------------------------------------------
rule "Update daily energy counters"
when

    Member of gEnergyCounter changed

then

    val String ruleIdentifier = "userRule.Update_daily_energy_counters"
    var String logMessage

    var String groupPrefix = triggeringItem.name.substring(0, triggeringItem.name.lastIndexOf('_'))

    var NumberItem dailyCounterItem = ScriptServiceUtil.getItemRegistry.getItem(groupPrefix+"_DailyCounter")

    var Number previousEnergy = previousState as Number
    var Number currentEnergy = triggeringItem.state as DecimalType
    var Number correctionValue = 0
    var Number deltaValue
    var Number resultValue

    // Counter overflow correction
    if (previousEnergy > currentEnergy) { correctionValue = 838860.7 }

    deltaValue = currentEnergy - previousEnergy + correctionValue
    resultValue = (dailyCounterItem.state as DecimalType) + currentEnergy - previousEnergy + correctionValue
    dailyCounterItem.postUpdate(resultValue)

    logMessage = String::format("Daily energy value (%s) has been increaded by %.3f to %.3f.", groupPrefix, deltaValue, resultValue)
    logInfo(ruleIdentifier, logMessage)

end
1 Like

i want to use this in my rule:

rule "Stromverbrauch des laufenden Monats"
when
    Time cron  "0 0 0 * 1/1 ? *" 
    //Time cron "0 * * * * ?" //for debug 
then
    var Number MeterMonthUse = 0
    
    MeterMonthUse = ZWaveNode2_PowerNode_ElectricMeterkwH_Day.sumSince(now.minusDays(now.withDayOfMonth.dayOfMonth)) as Number
    
    ZWaveNode2_PowerNode_ElectricMeterkwH_Month.postUpdate(MeterMonthUse)
    logInfo("powerusageMonth", "powerusageMonth fertig " + MeterMonthUse.toString())
end

Unfortunately, I get the following error message:

  • Platform information:
    • Hardware: raspberry pi 3 b
    • OS: Raspbian GNU/Linux 9 (stretch)
    • Java Runtime Environment: Java™ SE Runtime Environment (build 1.8.0_181-b13)
    • openHAB version: 2.4.0 (Build) stable

I can’t read the error on my phone so I have no idea what it says.

Looking at the line, I suspect dayOfMonth isn’t returning an int which is what now.minusDays requires. See if they’re is an getInt or intValue method.

thank you, here are the 3 error messages in plain text:

Type mismatch: cannot convert from Property to int

Invalid number of arguments. The method withDayOfMonth(int) is not applicable without arguments

Ambiguous feature call.
The methods
	dayOfMonth() in DateTime and
	getDayOfMonth() in AbstractDateTime
both match.

Like I said, getDayOfMonth is not returning an int. It needs to be converted to an int to be used in minus days

Thank you for your help, the solution looks like this:

MeterMonthUse = ZWaveNode2_PowerNode_ElectricMeterkwH_Day.sumSince(now.minusDays(now.getDayOfMonth)) as Number

no more error messages and the rule works as desired

1 Like