[SOLVED] Rules / xtend syntax for arrays and lambda's

Trying to create some rules and i have some syntax problems. The documentation about xtend is not as easy to understand as i hoped (atleast to me). So i ask it here:

This rule would close the screens if the expected conditionId is in a preset array. What is the most readable xtend syntax? I have use the keyword ‘in’ (like SQL syntax) as example of what i try to achieve, but that doesn’t work:

val int[] ExpectedClearConditionIds= newArrayList(800,801,802)

rule "Screen down"
when
Time cron "0 0 8 ? * * *"
then
if (ExpectedWeatherCondition.state in ExpectedClearConditionIds)
{
 // close the screens
}
end

or something like:

If (ExpectedClearConditionIds.contains(ExpectedWeatherCondition.state))

Anyone can help solve the puzzle? and if possible point me to some good documentation about arrays, lambda’s or other syntax for xtend?

Not sure if this is any help but here is link to xtend documentation
https://eclipse.org/xtend/documentation/203_xtend_expressions.html

Arrays and global lambdas are a “code smell” in Rules DSL. Where they appear it almost always indicates a case where someone is trying to force Rules DSL to for with how they want it to work rather than adjusting their coding style to Rules DSL.

Almost always this is done by users who already know how to code and want Roles DSL to work like languages they are used to. So for those users I recommend using JSR223 Rules instead and coding rules in Python, JavaScript, or Groovy instead.

Looking at the code I notice the following:

  • avoid the use of primitives, by default Rules DSL will use BigDecimal and there are known issues with the use of primitives at compile time

  • arrays are not supported in Rules DSL, though ArrayList is.

  • the type returned by newArrayList is List<BigDecimal>, not int[]

  • the type stored by a Number Item is DecimalType, not int, so even if arrays were supported like it’s defined, the in would never find it

  • I’m pretty sure there is no support for in in Rules DSL (Python programmer?), It would be ExpectedClearConditionIds.contains(ExpectedWeatherCondition.state as Number)

There are a number of ways to solve something like this using a Rules DSL friendly way, and in this case an ArrayList is probably a good approach, though if you have more than one role that is going to need that ArrayList, if recommend Design Pattern: Separation of Behaviors so you only have the coffee in one place (this is a preferable approach to using a global lambda.

val List<Number> ExpectedClearConditionIds= newArrayList(800,801,802)

...

if ( ExpectedClearConditionIds.contains(ExpectedWeatherCondition.state as Number)){
...

Thanks alot for the extensive feedback. I struggle to get good documentation that is readable to me. I’m an intermediate C# programmer and find it hard to work with al these types. int vs BigDecimal vs Number vs item.state (wich can be anything)
While vscode is WAY better than the old openHAB editor, it lacks stuff like type checking for these rules. Your post helps a lot.

Edit 1:
Minor addition: don’t forget add the imports, or you will get ‘… refers to the missing type Object’ error.

import java.util.List

Edit 2:
This is what i meant with not understanding all the type hocus pocus.

ExpectedWeatherCondition.state as Number

leads to:

Could not cast 804 to java.lang.Number; line 18, column 42, length 36

It’s a standard OO approach. No, .state can’t be just anything. It can be of type org.openhab.core.State (or something like that). Then there are types that inherit from State to store information that is appropriate for that Item type. There are a further set of Commands that can be sent to the various Item types. Items | openHAB has a table showing the command types you can send to an Item of a given type.

You know that ExpectedWeatherCondition is a Number Item. Number Items carry their state as a DecimalType. Therefore you often have to cast ExpectedWeatherCondition.state to a DecimalType (or a Number with is the parent class of both DecimalType and BigDecimal) in order to compare them.

Then ExpectedWeatherCondition is either NULL or UNDEF or it is not a Number Item. If it is not a Number Item than your List will never work as written because your List if of Numbers. So what type is ExepctedWeatherCondition?

I know about the OO / inheritance part. I’m just not familiair with all the types and inheritance chains that openHAB uses. I find them confusing.

Anyway.

You know that ExpectedWeatherCondition is a Number Item. Number Items carry their state as a DecimalType. Therefore you often have to cast ExpectedWeatherCondition.state to a DecimalType (or a Number with is the parent class of both DecimalType and BigDecimal) in order to compare them.

So ExpectedWeatherCondition.state is a BigDecimal that inherits Number. The array list (ExpectedClearConditionIds) contains Numbers. i don’t see a type problem here.

Then ExpectedWeatherCondition is either NULL or UNDEF or it is not a Number Item. If it is not a Number Item than your List will never work as written because your List if of Numbers. So what type is ExepctedWeatherCondition?

Well it is not NULL or UNDEF, the error (maybe you overlooked) is telling the value is 804. And that is imho a perfect number. Not sure about all the casting goodies, could it be that the ExpectedWeatherCondition.state is actually a String that can’t be casted to Number. But needs parseNumber method or similar?
What is the best way to detect the state type?

The error indicates that either ExpectedWeatherCondition is not a Number Item, or it has one of the two “no value” states: NULL meaning uninitialized, or UNDEF meaning the state is unknown. Neither NULL nor UNDEF are DecimalTypes so you can’t cast them to Number.

Since you are the one who create the ExpectedWeatherCondition Item, shouldn’t you know whether it is a Number Item or String Item or some other type? If you don’t know, you can look in PaperUI or in the .items file to see what type it is.

I wouldn’t bother with that. Just use Strings in your List instead of Numbers. And if you want to be really thorough call toString on the state.

val List<String> ExpectedClearConditionIds= newArrayList("800","801","802")    
...
if ( ExpectedClearConditionIds.contains(ExpectedWeatherCondition.state.toString)){
...

I didn’t over look it. I’m always thorough in my answers. NULL and UNDEF are a common cause for this error. Future readers will come across this post and if their problem is caused by NULL or UNDEF they will find the answer.

When you are dealing with an individual Item, the best way is to just know. If you have a Number Item you know it can only ever be DecimalType, NULL or UNDEF. If you have a Switch it can only ever be OnOffType, NULL or UNDEF. If you have a Color Item it can only ever be HSBType, NULL, or UNDEF. A Dimmer can only ever be PercentType, NULL or UNDEF.

There are cases in Rules where you may not know the type of an Item and you may need to. Usually this is when you are iterating over a Group or have a Rule with multiple diverse triggers and you need to do something different based on the type. In this case you can use instanceof, as in if(SomeItem instanceof SwitchItem).

The ExpectedWeatherCondition.state is actually a string. Just read the binding documentation. Thanks for pointing me into the right direction.

I saw the value 804 and somehow never left the path that it was an int → BigDecimal → Number instead of a string.