Extract data from JSON

Hello ,
I want to extract value from JSON (attribute) to Humidity , so I made a groovy rule but I am getting below error

“On RulesEngine{id=‘RulesEngineId{scope=RealmRuleset, realm=‘master’, assetId=‘null’}’, running=‘true’, deployments=‘[RulesetDeployment{id=168512, name=‘MQTT Humidity Extraction’, version=35, status=DEPLOYED}]’}, error executing rules of: RulesetDeployment{id=168512, name=‘MQTT Humidity Extraction’, version=35, status=DEPLOYED} – Error evaluating condition of rule ‘MQTT Humidity Extraction’: No such property: 4Lv31Wa3GPJ6Ogfc0Ufb5X for class: org.openremote.manager.rules.facade.AssetsFacade”

my code is below

import groovy.json.JsonSlurper
import org.openremote.manager.rules.RulesBuilder
import java.util.logging.Logger

Logger LOG = java.util.logging.Logger.getLogger(“OpenRemoteLogger”)
RulesBuilder rules = binding.rules

def extractHumidityFromPayload(payload) {
def jsonSlurper = new JsonSlurper()
def jsonData = jsonSlurper.parseText(payload)
return jsonData.HUMIDITY
}

rules.add()
.name(“MQTT Humidity Extraction”)
.when({
def asset = assets.getAt(‘4Lv31Wa3GPJ6Ogfc0Ufb5X’)
if (!asset) {
LOG.warning(“Asset not found.”)
return false
}

    def jsonValue = asset.Json  // Try accessing the Json attribute directly
    if (!jsonValue) {
        LOG.warning("Json attribute not found or empty.")
        return false
    }

    def humidity = extractHumidityFromPayload(jsonValue)
    LOG.info("Extracted humidity: $humidity")

    return humidity != null
})
.then({
 
    LOG.info("Attempted to save extracted humidity to asset's HUMIDITY attribute.")
})

where I have done mistake @pcr @Don @martin.peeters @Pierre

I don’t really know Groovy a lot, but just by reading your code (sorry if I say something stupid)

Single quotes maybe?

hi ,
right now error is gone but i cant get the extract data from json to HUMIDITY attribute

the code is below

import groovy.json.JsonSlurper
import org.openremote.manager.rules.RulesBuilder
import java.util.logging.Logger

Logger LOG = java.util.logging.Logger.getLogger("OpenRemoteLogger")
RulesBuilder rules = binding.rules

def extractHumidityFromPayload(payload) {
    def jsonSlurper = new JsonSlurper()
    def jsonData = jsonSlurper.parseText(payload)
    return jsonData?.HUMIDITY
}

rules.add()
    .name("MQTT Humidity Extraction")
    .when({
        // Check if the method exists
        if (!assets.respondsTo("getAttributeValue")) {
            LOG.warning("Method getAttributeValue not found in assets.")
            return false
        }

        def jsonValue = assets.getAttributeValue("4Lv31Wa3GPJ6Ogfc0Ufb5X", "Json")
        if (!jsonValue) {
            LOG.warning("Json attribute not found or empty.")
            return false
        }

        LOG.info("JSON Value retrieved: $jsonValue") // Log the retrieved JSON value

        def humidity = extractHumidityFromPayload(jsonValue)
        if (humidity == null) {
            LOG.warning("Failed to extract humidity from payload.")
            return false
        }

        LOG.info("Extracted humidity: $humidity") // Log the extracted humidity
        return true
    })
    .then({
        // Check if the method exists
        if (!assets.respondsTo("setAttributeValue")) {
            LOG.warning("Method setAttributeValue not found in assets.")
            return
        }

        LOG.info("Inside 'then' clause.") // Confirming entering the 'then' clause

        def updated = assets.setAttributeValue("4Lv31Wa3GPJ6Ogfc0Ufb5X", "HUMIDITY", humidity.toString())
        if (updated) {
            LOG.info("Successfully updated asset's HUMIDITY attribute.")
        } else {
            LOG.warning("Failed to update asset's HUMIDITY attribute.")
        }
    })

log now below
Starting: RulesEngine{id=‘RulesEngineId{scope=RealmRuleset, realm=‘master’, assetId=‘null’}’, running=‘false’, deployments=‘[RulesetDeployment{id=168512, name=‘MQTT Humidity Extraction’, version=45, status=READY}]’}

and i also get this
Method getAttributeValue not found in assets

any idea @Don @pcr

I’ve never written a Groovy rule myself to be honest. Where did you get these methods that you are using?
Maybe you can take the example that pops up when you create a Groovy rule in the UI as a starting point to rewrite your rule. I’ll paste it here for your convenience:

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

/*
* A groovy rule is made up of a when closure (LHS) which must return a boolean indicating whether the then closure (RHS)
* should be executed. The rule engine will periodically evaluate the when closure and if it evaluates to true then the
* rule then closure will execute.
*
* NOTE: DO NOT MODIFY THE FACTS IN THE WHEN CLOSURE THIS SHOULD BE DONE IN THE THEN CLOSURE
*
* To avoid an infinite rule loop the when closure should not continually return true for subsequent executions
* so either the then closure should perform an action that prevents the when closure from matching on subsequent
* evaluations, or custom facts should be used, some ideas:
*
* - Change the value of an attribute being matched in the when closure (which will prevent it matching on subsequent evaluations)
* - Insert a custom fact on first match and test this fact in the when closure to determine when the rule should match again (for
*   example if a rule should match whenever the asset state changes the asset state timestamp can be used)
*/

rules.add()
        .name("Example rule")
        .when({
    facts ->

        // Find first matching asset state using an asset query

        facts.matchFirstAssetState(

                // Find asset state by asset type and attribute name
                new AssetQuery().types(ThingAsset).attributeNames("someAttribute")

                // Find asset state by asset ID and attribute name
                //new AssetQuery().ids("7CaBoyiDhtdf2kn1Xso1w5").attributeNames("someAttribute")

                // Find asset state by asset type, attribute name and value string predicate
                //new AssetQuery().types(ThingAsset).attributes(
                //        new AttributePredicate()
                //                .name("someAttribute")
                //                .value(new StringPredicate()
                //                            .value("someValue")
                //                            .match(AssetQuery.Match.EXACT)
                //                            .caseSensitive(true)))

                // Find asset state by asset type and location attribute predicate
                //new AssetQuery().types(ThingAsset).attributes(
                //        new AttributePredicate()
                //                .name(Asset.LOCATION)
                //                .value(new RadialGeofencePredicate()
                //                            .radius(100)
                //                            .lat(50.0)
                //                            .lng(0.0)))

        ).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("someAttribute")
            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("someAttribute")
            false
        }

})
        .then({
    facts ->

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

        // Insert the custom fact to prevent rule loop
        facts.put("someAttribute", assetState.getTimestamp())

        // Write to attributes
        def otherAttributeValue = null
        if (assetState.getValue().orElse{null} == "Value 1") {
            otherAttributeValue = "Converted Value 1"
        } else if (assetState.getValue().orElse{null} == "Value 2") {
            otherAttributeValue = "Converted Value 2"
        } else {
            otherAttributeValue = "Unknown"
        }
        assets.dispatch(assetState.id, "otherAttribute", otherAttributeValue)

        // Generate notifications (useful for rules that check if an attribute is out of range)
        //notifications.send(new Notification()
        //        .setName("Attribute alert")
        //        .setMessage(new EmailNotificationMessage()
        //                .setTo("no-reply@openremote.io")
        //                .setSubject("Attribute out of range: Attribute=${assetState.name} Asset ID=${assetState.id}")
        //                .setText("Some text body")
        //                .setHtml("<p>Or some HTML body</p>")
        //        )
        //)
})

Hi @Don ,
What i actually need
Create a attribute A (Json) from i extract the value of attribute B(text/no) for same asset without using any service outside of openremote.

@Rich any input here

As @Don mentioned, not sure where you found the rule syntax you used, guess you wrote it to explain what you are trying to do.

Explaining groovy is not a quick answer as it is it has access to java language with groovy syntax on top…

Sounds like you want to extract a value from one attribute and write it through to another attribute; you could try using agent link and value filters to extract the value you want into the attribute.

If you were to use groovy rules then the template @Don provided is quite helpful; facts.matchFirstAssetState() will allow you to find the source attribute value and then you can apply regex or substring to get the value you want and then bind this value to the facts for use in the right hand side.

If the values being searched for are not dynamic then maybe a when then rule could be used