[SOLVED] Rule - multiple itemstates in one summary item

Hi,

Im trying to build a rule to safe the values and states of multiple items into one “summary-item”. Later I want to display this item on my sitemap.

E.g.:
the OpenClosed state of a gasstation + the gasprice of that exact station

This is what i got so far:

rule "Tankstelle 1 Summary update"
when
    System started or
    Item Station_Open_1 changed or
    Item E5_1 changed 
    
then
    logInfo("Tankstelle 1 Summary", "--> update")
    if (Station_Open_1.state == UNDEF || E5_1.state == NULL) { return }
    
    if (Station_Open_1.state == "OPEN") {
        val String state = (Station_Open_1.state as OpenClosedType).toString
        val String price = (E5_1.state as DecimalType).toString
        var String summary = "ist" state + " - Super: " + price
        //summary = String::format("%s - %s", state, price)
        Station_Summary_1.postUpdate(summary)
    } 
    
    else {
        Station_Summary_1.postUpdate("geschlossen")
    }
end

Im getting this Error:

2019-12-03 18:53:29.699 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'tankstellen.rules', using it anyway:

This expression is not allowed in this context, since it doesn't cause any side effects.

2019-12-03 18:53:29.761 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'tankstellen.rules'

2019-12-03 18:53:35.161 [INFO ] [me.model.script.Tankstelle 1 Summary] - --> update

The “summary-item” state is “geschlossen” all the time.

Station_Open_1 and E5_1 can both be UNDEF or NULL. You need to check both Items for both states.

You need to have a semicolon after the return command: return; This is probably the source of the warning in the log about the statement not being allowed.

Station_Open_1.state is a State type and you are trying to compare it to a String type. if(Station_Open_1.state.toString == "OPEN"). This is probably why the if statement never runs.

val String state... is needlessly complex. val state = Station_Open_1.state.toString

Same for price and summary.

Over all, the whole Rule can be made significantly shorter and simpler.

then
    logInfo("Tankstelle 1 Summary", "--> update")

    if(Station_Open_1.state == UNDEF || Station_Open_1.state == NULL ||
       E5_1.state == UNDEF || E5_1.state == NULL)
        return;

    val state = Station_Open_1.state.toString
    val price = E5_1.state.toString
    val summary = if(state == "OPEN") "ist " + state + " - Super: " + price else "geschlossen"
    
    Station_Summary_1.postUpdate(summary)
end

@rlkoshak :heart_eyes:

If you now could tell me if its possible to map the OpenClosed state to german somehow, ill include you into my bedtime prayer. :stuck_out_tongue:

https://www.openhab.org/docs/configuration/actions.html#openhab-subsystem-actions and https://www.openhab.org/addons/transformations/map/.

1 Like

Maybe my discussion is helpfull for you. I setup a set of fuelstation, rules and sitemap to dosplay the fuelprices in my region

1 Like

@rlkoshak Okay, i think Im more confused then before, after trying to understand where to put the mapping.

I guess i have to use something like this:

var summary= transform("MAP", "de.map", "OPEN")

If so, where do i put it?

Sorry, but Im totally new to all this and i dont find any guides to make my start easier. The documentaion is really good, but requires basekonowledge Im missing atm.

In your Rule. Replace

val state = Station_Open_1.state.toString

with

val state = transform("MAP", "de.map", Station_Open_1.state.toString)

Make sure to update the if statement two lines later and replace “OPEN” with what ever it was mapped to.

I think i need your help again. :see_no_evil:

Im trying to use the rule for a speedtest summary.

rule "Speedtest Summary update"
when
    System started or
    Item Speedtest_LUD changed from NULL
    
then
    logInfo("Speedtest Summary", "--> update")

    if  (Speedtest_ResultDown.state == UNDEF || Speedtest_ResultDown.state == NULL ||
         Speedtest_ResultUp.state == UNDEF || Speedtest_ResultUp.state == NULL) 
        return;

    val downlink = Speedtest_ResultDown.state.toString
    val uplink = Speedtest_ResultUp.state.toString
    val time = Speedtest_LUD.state.toString
    val summary = "Down ▼ " downlink + " - Up ▲ " + uplink + " um " + time + " Uhr"
    
    Speedtest_Summary.postUpdate(summary)
end

But:

2019-12-14 21:14:24.053 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'SpeedSummary.rules', using it anyway:

This expression is not allowed in this context, since it doesn't cause any side effects.

The summaryItem is only showing “Down ▼”

If i try it like this

val downlink = Speedtest_ResultDown.state as DecimalType.toString

i get the following error:

2019-12-14 21:13:42.163 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'Speedtest Summary update': Could not cast 100.0633163552 Mbit/s to void; line 14, column 20, length 49

Whats my mistake? :confused:

You are missing a + .

Awww :speak_no_evil: I looked for typos like 15 times and didnt see that :confused:

Is there a way to shorten the strin to 4 numbers? Or do i have to use a numberItem and format as %.2f?

:+1:

Hey Rich, :slight_smile:

how was your x-mas?

Ive got another question, haha:

then
//schütteln
    if (receivedEvent.getEvent() == "1000", "2000", "3000", "4000", "5000", "6000") {
        Test_Li_Trad_RBG_Color.sendCommand(ON)
    }

Is that possible in any way? If im right, “or” only works as trigger, right? “,” doesnt work here eather. :confused:

When you wanted “or” in an if() statement earlier, you used ||. The syntax has not changed since then.

You might alternatively consider using a switch-case structure.

I tried that, but im getting this error:

[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Test Würfel': An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.BooleanExtensions.operator_or(boolean,boolean) on instance: null

Sorry for the dumb question, but whats that?

If only we knew what your rule code was, we might be able to comment on the error. You must realise that?

Switch-case is a special kind of extended “if”, you set up a switch value and have multiple case statements to do things dependent on the value. Search this forum for examples.

rule "Test Würfel"
when
    Channel "deconz:switch:homeserver:00158d00032c718b020012:buttonevent" triggered 
then
//schütteln
    if (receivedEvent.getEvent() == "1000" || "2000"|| "3000"|| "4000"|| "5000"|| "6000") {
        Test_Li_Trad_RBG_Color.sendCommand(ON)
    }
//schütteln
    if (receivedEvent.getEvent() == "7007") {
        Test_Li_Trad_RBG_Color.sendCommand(ON)
    }
//fallen
    if (receivedEvent.getEvent() == "7008") {
        Test_Li_Trad_RBG_Color.sendCommand(OFF)
    }
end

This is my code.

Its a Aqara cube. The problem im facing is, that it has like 40+ different zigbee codes.

Normally you have options like: “Flip 90°” or “Push”. But the deCont Binding receives the zigbee code and its different for each side of the cube.

eg: if side 4 is on the bottom and i flip it 90° to the right, ill get "4005, if i flip it back ill get 5004. But its both “Flip 90°”.

So what im trying to do is, put all “Flip 90” codes in one if statement. Same for like “Flip 180” and so on.

You cannot do
If (fred == 1 || 2)
it’s just bad syntax. You have to be more explicit
if (fred == 1 || fred ==2)

Might be worth you spending some time understanding your encoded values and using the pattern they may form, rather than just number matching.

Ah ok. Thx. This is working.

I did already and thats why i started writing this rule.

x00x → first “x” is the side its on → second “x” is the side it came from. And 7007 and 7008 are shaking and throwing the cube.

What would you suggest to do with this?

rule "Test Würfel"
when
    Channel "deconz:switch:homeserver:00158d00032c718b020012:buttonevent" triggered 
then
// schieben
    if (receivedEvent.getEvent() == "1000" || receivedEvent.getEvent() == "2000" || 
        receivedEvent.getEvent() == "3000" || receivedEvent.getEvent() == "4000" || 
        receivedEvent.getEvent() == "5000" || receivedEvent.getEvent() == "6000") {
            Test_Li_Trad_RBG_Color.sendCommand(ON)
    }
// klopfen
    if (receivedEvent.getEvent() == "1001" || receivedEvent.getEvent() == "2002" || 
        receivedEvent.getEvent() == "3003" || receivedEvent.getEvent() == "4004" || 
        receivedEvent.getEvent() == "5005" || receivedEvent.getEvent() == "6006") {
            //do stuff
    }
// schütteln
    if (receivedEvent.getEvent() == "7007") {
        //do stuff
    }
// fallen
    if (receivedEvent.getEvent() == "7008") {
        //do stuff
    }
// flip 90°
    if (receivedEvent.getEvent() == "2001" || receivedEvent.getEvent() == "5001" || receivedEvent.getEvent() == "3001" || receivedEvent.getEvent() == "4001" || // von 1
        receivedEvent.getEvent() == "1002" || receivedEvent.getEvent() == "3002" || receivedEvent.getEvent() == "4002" || receivedEvent.getEvent() == "6002" || // von 2
        receivedEvent.getEvent() == "1003" || receivedEvent.getEvent() == "2003" || receivedEvent.getEvent() == "5003" || receivedEvent.getEvent() == "6003" || // von 3
        receivedEvent.getEvent() == "1004" || receivedEvent.getEvent() == "2004" || receivedEvent.getEvent() == "5004" || receivedEvent.getEvent() == "6004" || // von 4
        receivedEvent.getEvent() == "1005" || receivedEvent.getEvent() == "3005" || receivedEvent.getEvent() == "4005" || receivedEvent.getEvent() == "6005" || // von 5
        receivedEvent.getEvent() == "2006" || receivedEvent.getEvent() == "3006" || receivedEvent.getEvent() == "4006" || receivedEvent.getEvent() == "5006") { // von 6
            //do stuff
        }
// flip 180°
    if (receivedEvent.getEvent() == "6001" || receivedEvent.getEvent() == "1006" || 
        receivedEvent.getEvent() == "5002" || receivedEvent.getEvent() == "2005" || 
        receivedEvent.getEvent() == "3004" || receivedEvent.getEvent() == "4003") {
            //do stuff
    }
end

This is what i got so far. :confused: Its working, but not rly professional i guess

That is the most important thing!

You could save some typing and repeated fetching of the event -

val xx = receivedEvent.getEvent()
if (xx == "1000" || xx == "2000" ...

You could save processing using if-else structure.
When an if() section is satisfied, you do not want to carry on working through all the others that you now know will not match.
Put the likeliest cases at the top for maximum efficiency.

    if (receivedEvent.getEvent() == "1001" || receivedEvent.getEvent() == "2002" ... )  {
            //do stuff
    }
    else if (receivedEvent.getEvent() == "7007") {
        //do stuff
    }
    else if (receivedEvent.getEvent() == "7008") {
         //. etc

There will be some clever algorithm to deal with that, invented in the 1950’s. I do not know it.