DSL Rules: JSON Loop elements

Hey DSL Rules Specilists,
I have a json string stored in a string item. i would like to iterate over all elements/tuples. the length (count of tuples) is not fixed. can this be achieved with dsl rules ?

somthing like:

for obj in transform(“JSONPATH”, “$.schedules”, json)

is that possible without migrating to JSR232 ?

thanks in advance

Lars

It would be helpful to see an example of the JSON. That would better able us to come up with alternative approaches.

the json string is:

{“mode”: “auto”, “acPowerOut”: 3000} or

{“mode”: “auto”, “acPowerIn”: 3000, “acPowerInMin”: 200, “acPowerInMax”: 3680, “acPowerOut”: 3000}

depending on energy asset

in a rule i would like to iterate over the elements to control e.g. my battery…
with that informations several items are set, but differnty depending on “mode”

thanks in advance

Lars

There are lots of ways but the simplest would probably be to check to see an element is there or not and choose the path as appropriate.

    if(jsonstring.contains("acPowerOut")){
        val pout = transform("JSONPATH", "$.acPowerOut", jsonstring)
        ...
    }
    else if(jsonstring.contains("acPowerIn")){
        val pin = transform("JSONPATH", "$.acPowerIn")
        val pinmin = transform("JSONPATH", "$.acPowerInMin")
        ...
    }

this is a good hint!
i thought on more generic way, to address flexible oh items…
iteration over elements is not possible at all ?

thanks again

Lars

you can split the string and iterate over the result, but why when you can much more simply pull the data out by name using the transformation?

Another way to put it is that your Item state has no elements, it is just a string so far as rules are concerned.
In DSL rules, you could split that out into an array of substrings for further parsing etc.,but there is no native JSON handling.
Or you might use another rules language, like javascript with its inbuilt JSON capabilities.

There is no way to get the keys out of the json using the version of jsonpath that is used by OH. It looks like you may know what the possible keys may be though, so you can make your own list to iterate over…

    val KEYS = newArrayList("mode", "acPowerIn", "acPowerInMin", "acPowerInMax", "acPowerOut", "thisDoesNotExistInTheCurrentData")
    val TEST_DATA = '{"mode": "auto", "acPowerIn": 3000, "acPowerInMin": 200, "acPowerInMax": 3680, "acPowerOut": 3000}'

    for (key: KEYS) {
        val RESULT = transform("JSONPATH", "$." + key, TEST_DATA)
        if (RESULT != TEST_DATA) {
            logWarn("Test", "{}: {}", key, RESULT)
        }
    }

Using a real scripting language, like Jython, you can easily convert to json and iterate through the keys. The logging will be simpler in a rule, but…

import json

from core.log import logging, LOG_PREFIX#, log_traceback

LOG = logging.getLogger("{}.TEST_3".format(LOG_PREFIX))
TEST = '{"mode": "auto", "acPowerIn": 3000, "acPowerInMin": 200, "acPowerInMax": 3680, "acPowerOut": 3000}'
CONVERTED_TO_JSON = json.loads(TEST)

for key, value in CONVERTED_TO_JSON.iteritems():
    LOG.warn("{}: {}".format(key, value))

thank you for your input, i played around with all your hints. my goal was to have a more or less compatible solution for DSL Rules, scripting and grafana (postgres). grafana to graph the settings…

i changed the json to:

{“parameter”:
[
{‘item’:‘mode’, ‘type’:‘String’, ‘value’:‘auto’},
{‘item’:‘Out’, ‘type’: ‘Number’,‘value’:3000},
{‘item’:‘OutLimitMax’, ‘value’:3000},
{‘item’:‘OutLimitMin’, ‘value’:3000},
{‘item’:‘In’, ‘value’:3000},
{‘item’:‘InLimitMax’, ‘value’:3000},
{‘item’:‘InLimitMin’, ‘value’:3000}
]
}

and my DSL rules like:

val jsonstring = mqtt_topic_ems_battery_activeLoadProfile.historicState(now().plusSeconds(10),‘influxdb’).state.toString

val arLength = Integer::parseInt(transform("JSONPATH","$.parameter.length()",jsonstring))

val i=0
while ( i < arLength ) {
    logInfo("rules","ems_battery_rules end...." + i)
    logInfo("rules","ems_battery_rules end...." + transform("JSONPATH","$.parameter.[" + i + "].item",jsonstring))
    logInfo("rules","ems_battery_rules end...." + transform("JSONPATH","$.parameter.[" + i + "].value",jsonstring))
    i++
}

i would like to get your feedback and addtional enhancements…

br Lars

Well that helps! Functionally, everything looks good to me, except…

… you can’t get a historicState from a time in the future :slightly_smiling_face:. If it doesn’t throw an error, then it must be returning the current state. Why not just use the state of mqtt_topic_ems_battery_activeLoadProfile?

Instead of the while loop, you could do…

(0..arLength - 1).forEach[i|

While not necessary, it is bets to use parameterized logging…

logInfo("rules", "ems_battery_rules end... {}", i)

I’ve never tried that. Surprised it does not throw an error, but can see how it works … historicState() will return the newest record at, or from before, the given time. Makes sense that a future date that would return the very newest record from db (which may or may not be the same as current state).

the json is calculated outside openhab and written directly to the database. with historicstate i cyclic fetch the value from database…

the dataset are aligned to 1/4-hour. i call the rule each 5 minute, without the additional 10 seconds, rule execution at the 1/4-hour will not retrieve the related value… i don’t know why

The clock might be ten seconds ahead on the mystery box inserting your db records? Who could say.

good hint, does all docker container on one machine have the same time ?

Why not just update an Item with the value through the REST API? The rule can then trigger when the Item is updated.

after updating solarforecast/market proces, i would like to calculate an loadprofile for the future 1/4-Hour intervals and i need some future calculations, as you mentioned, future calculations might be difficult in OH, isn’t it ?

as example, i would like to know, if and on how many hours pv generation will be > 3kWp
https://public.grafana.seume70.efficient-energies.de/d/bWYJueZGk/20-prognose

how can i do this on OH ? is there a way ?

thanks in advance

Probably, but it won’t be easy at all.
The difficulty you are up against is that openHAB is a home automation system, not crafted for extensive statistical analysis etc.
The good news is that openHAB provides lots of ways to interface with other more specialist tools - like Grafana or MySQL.

i’m fine with that … for that, i push the relevant data directly into database