Previous attribute value access

Hi,

I have a problem accessing the previous values of an attribute
Let’s say we get a value every 15 minutes via MQTT, how do we access previous values (e.g. one hour ago) to subtract it from the current value.

@Rich are retained messages implemented in the new broker?

@Joh must it be over mqtt? else you can use http.

https://domainname/api/master/asset/AssetID
check out the swagger documentation: yourdomainname/swagger

@Denis Yes It must be on MQTT.
I actually get the data from liveobject every 15 minutes. For example I would like to be able to subtract the current value from the one collected an hour ago and display this difference.

What you try to achieve?

You can solve this for example by a rule and put the result in a attribute, which you can subscribe.

I would like to use a rule for this, but the problem is the access to the previous data.
As I said, let’s suppose that I receive a data at 12:00 and another one at 13:00. The value collected at 12:00 is overwritten by the one at 13:00. My question is : how can I get access to the 12:00 value using the rules?

If you are writing groovy rules the previous value of the attribute is available in the AssetState object.

This will just get you the previous value, if you need something like all the values for the last hour then you can use the RULE_EVENT configuration item (an example groovy rule using this can be found in the test suite).

1 Like

Thanks @Rich
I tried to retrieve the previous value using the groovy rule. I get no result.

val is the attribute that contains the retrieved values
and oldval is the attribute that will contain the old value of val

Here is the code:

package demo.rules

import org.openremote.manager.rules.RulesBuilder
import org.openremote.model.notification.*
import org.openremote.model.rules.AssetState
import org.openremote.model.asset.Asset
import org.openremote.model.asset.impl.*
import org.openremote.model.query.*
import org.openremote.model.query.filter.*
import org.openremote.model.rules.Assets
import org.openremote.model.rules.Notifications
import org.openremote.model.rules.Users
import org.simplejavamail.email.Email

import java.util.logging.Logger
import java.util.stream.Collectors

Logger LOG = binding.LOG
RulesBuilder rules = binding.rules
Notifications notifications = binding.notifications
Users users = binding.users
Assets assets = binding.assets
rules.add()
.name(“oldvalue”)
.when({
facts →

    // Find first matching asset state using an asset query

    facts.matchFirstAssetState.map { assetState ->
            // Find asset state by asset type and attribute name
            new AssetQuery().types(GroundwaterSensorAsset).attributeNames("val")
            //new AssetQuery().types(GroundwaterSensorAsset).attributeNames("minold")


    ).map { assetState ->

        // Use logging to help with debugging if needed            //LOG.info("ATTRIBUTE FOUND")

        // Check if this rule really should fire this time
        Optional<Long> lastFireTimestamp = facts.getOptional("val")
        if (lastFireTimestamp.isPresent() && assetState.getTimestamp() <= lastFireTimestamp.get()) {
            return false
        }

        // OK to fire if we reach here

        // Compute and bind any facts required for the then closure
        facts.bind("assetState", assetState)
        true
    }.orElseGet {
        // Asset state didn't match so clear out any custom facts to allow the rule to fire next time the when closure matches
        facts.remove("val")
        false
    }

})
.then({
facts →

    // Extract any binded facts
    AssetState assetState = facts.bound("assetState")

    // Insert the custom fact to prevent rule loop
    facts.put("val", assetState.getTimestamp())
    Optional<Long> oldval = assetState.getOldValue()
    assets.dispatch(assetState.id, "oldval", oldval)

})

In your then you are trying to push the Optional<Long> into the attribute rather than the value you probably want something like:

assets.dispatch(assetState.id, "oldval", oldval.orElse(null))
1 Like

Hi @Rich,
I am also interested in getting not only the previous value. In which test suite is the example groovy rule? I checked the Smart City Demo as well as the Manufacturer Demo, but could not find it. Can you help me out?
Cheers

Would be very happy about an answer :slight_smile:

You can access historical data of an attribute from groovy rules by using the HistoricDatapoints object from the rule binding:

import org.openremote.model.rules.HistoricDatapoints

HistoricDatapoints historicDatapoints = binding.historicalDatapoints

def datapoints = historicDatapoints.getValueDatapoints(ref, new AssetDatapointAllQuery(LocalDateTime.now().minusHours(1), LocalDateTime.now())

Be careful where you use this in your rule (i.e. it should be in the then part so it only executes when needed to avoid affecting performance too much).

Hi,
I have tried to use the HistoricDatapoints example shown, however I cannot get it to work. I have assumed “ref” to be the attribute name as a string, is there something else I should be doing not shown in the example. I am using the demo groovy rules template and have modified it to get the current attribute value successfully.

The class type AttributeRef we use is an object containing two fields, like so:

{
    "id": "<your asset id>"
    "name": "<name of your attribute>"
}

The class can be found at this path;
/model/src/main/java/org.openremote.model/attribute/AttributeRef.java

Thanks, but how do I use the object in this context. I have tried the following without success.

def datapoints = historicDatapoints.getValueDatapoints((“id”) “2jII1qSCPO2dwoC1kVdKm7”, (“name”) “Gravity”, new AssetDatapointAllQuery(LocalDateTime.now().minusHours(1), LocalDateTime.now())

I’ve tried this too without success.

import groovy.json.JsonSlurper

def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText(‘{“id”: “2jII1qSCPO2dwoC1kVdKm7”, “name”: “Gravity” }’)

def datapoints = historicDatapoints.getValueDatapoints(object, new AssetDatapointAllQuery(LocalDateTime.now().minusHours(1), LocalDateTime.now())

Using JsonSlurper should not be needed. You can use a variable like normal.

What error are you receiving exactly for the getValueDatapoints() function?
Does an exception get thrown?

Oh, and can you make sure that, in the Manager UI, you can see the datapoints of that attribute?
You can verify through either the Assets- or the Insights page. In our system, not every attribute has data points; they need the meta item STORE_DATAPOINTS to historically store their data. :slight_smile:

Yes I can see the datapoints in Assets page and Insights page, and I can access the current value using groovy rules script, but I cannot make the example historical datapoints work.

This is my error message from the log page - startup failed:
Script358.groovy: 86: unable to resolve class AssetDatapointAllQuery
@ line 86, column 113.

This is the part of the script that fails:
def datapoints = historicDatapoints.getValueDatapoints(AttributeRef(assetState.getId(), “iSpindelGravity”), new AssetDatapointAllQuery(LocalDateTime.now().minusHours(1), LocalDateTime.now()))

I have now removed Jsonslurper, as per your previous reply.

I’ve resolved the issue, I was missing this crucial declaration:
import org.openremote.model.datapoint.query.AssetDatapointAllQuery

All working well now!

1 Like