Xtend Scripts vs JSR223?

  1. I am wondering which one should i use? Or the plan of OpenHab2 in the future?
  2. Is it possible to call a script within a rule, with the scription fucntion returns set of responses and make decision on changing state of certain items?
  1. I’m not aware of any plans to stop support of xtend rules. If you are fine with jsr223, this is a good option, because it’s way faster than xtend.
  2. It’s possible to call a script, but you can’t send or receive parameters directly, instead you have to define items and set them before calling respectively set them in the script for answer.
    However, you can define lambda functions and use them instead.
1 Like

Is the JSR223 being maintained? I thought I read that it was not being actively supported?

Thank you.

  1. Apart from performance , are there any other benefits of that JSR223 has but not Xtend? I mean in terms of available modules and flexibility to use certain ESH or Openhab functions which are not available in Xtend.

2.Will try to Google and see how this lamda applies to script. Do you have any links for examples?

Thanks

I was about to ask something similar. I’m pretty tired of using the openhab rules language. All the design principals and examples seems to be weird cheats and workarounds. I still have difficulty believing that functions seemed to have been forgotten and then bungled in as buggy lambdas. There are a few completely different design patterns I’ve seen but they all have major downsides and weaknesses, primarily around lots of copy and pasting of Items.

I only just realised that it was possible to use python using JSR223. So I was thinking about simplifying everything and just doing it in python. I could even create object and structures to use in python and just convert the objects to a string when writing them to an Item.

I was wondering what kind of performance penalty does JSR223/jython get compared to native rules if any?

Is there any benefit or reason to keep using Xtend? I know it’s design goals are different but in terms of syntax and the language I can’t think of a single benefit of using Xtend over jython.

Oh and should I consider node-red, I’m hearing lots about it but is it just a GUI thing?

1 Like

Well, to be honest, I don’t use JSR223/Jython, as it looks ridiculously complex to me :smile: but I have to admit, that I have stopped to evolve beyond BASIC in the 90’s :blush:
So, I never made more than very basic stuff in Python, PHP, Java, or even the openHAB rule DSL.

The Rule engine is very slow (@Spaceman_Spiff did a comparison here: XTend Performance example)
On the other hand, the Rule engine is built for automation, and if using Groups, there are very effective ways to save code. @rlkoshak did a great job to write tutorials for the rules DSL, take a look at
Design Pattern: Working with Groups in Rules (just an example; there are many more of them…)

I would just note that that was a very specific use case and I don’t think it is valid to go from that one use case to saying that Jython or JSR223 is always faster than XTend in all cases. I think it probably is, but that experiment is not sufficient to prove it. That experiment is also just on Jython and I don;t think it can say anything about the performance of JavaScript or Groovy.

It lets you use “real” programming languages as apposed to the Domain Specific Language. If you are proficient in one of the supported languages (Jython, JavaScript, or Groovy) or really, if you are already a programmer and can’t or wont learn to use the DSL in the ways it is intended.

However, JSR223 is not as well documented as the Rules DSL and there are far fewer people on this forum that will be able to help. You will likely need to dig into the source code for the OH libraries that expose OH objects to the language interpreter to figure some things out.

Ultimately, JSR223 will let you code the way one would code in more familiar programming languages. But at this point I don’t think there is anything available in JSR223 that isn’t available in Rules DSL except more freedom to code the way you want to.

The Rules DSL is purposefully constrained. It is a compromise to present a more limited coding capabilities so as not to overwhelm the newcomers. I’ve said it before and I’ll be forced to say it again. If you are a competent coder and do not have the patience to work within those constraints then you will be unhappy with the Rules DSL and JSR223 is going to be a better place for you. The Experimental Rules Engine is also showing promise, but I think it still lacks some important features like the ability to call Actions and create Timers.

The DPs are NOT weird cheats and workarounds. They are showing how to solve problems in the Rules DSL in the way the Rules DSL supports best. It doesn’t have classes and structs so they show how to organize the code and data (using Items) in the Rules DSL way. If you don’t like the Rules DSL, you will not like the DPs either. That can’t be helped.

Lambdas are first order parts of the language. Every time you create a Timer or run a forEach you are using a lambda. Every time you see [ | ] you are using a lambda. They are hardly “tacked on”.

Buggy in what way? If there are problems then issues need to be filed and the problems fixed.

All code will have some downsides. And I agree, there should be a better way to set up and configure Items. But nothing changes the fact that Items are how you model your home automation. Switching the JSR223 or Node Red is not going to make that problem go away. If you have three door sensors and want to put the last time they were opened on your sitemap, you are going to have to have six Items (three Contacts, three DateTimes) no matter what Rule engine you are using.

Most of the DPs take advantage of name correlation and Groups to more easily find and use the Items needed in a given Rule. You will still have that same problem in your JSR223 Rules. You have more direct access to the Item Registry so you may be able to save the creation of a Group or two, but the Items will still need to be created.

Much better documented in terms of how to access the various parts of OH from the language.

Far larger set of users who will be able to help you with problems on this forum. I think there are three people on this forum right now who are actively helping with JSR223 problems and they are all pretty much Jython users.

As Udo indicated, it likely will run faster in a lot of cases. The number of situations where this actually matters in a home automation context is pretty small so either way I wouldn’t let the relative performance of the two be the deciding factor.

Also, don’t forget that this isn’t an either/or decision. You can use JASR223, Jython, and Node Red all at the same time if you find benefits from doing so.

It’s a way to link up devices and code rules graphically. It is very powerful but I suspect you will run into some of the same frustrations with it that you are with the Rules DSL. As with any DSL type language, it is purposefully constrained in many ways. It also will depend on MQTT to communicate back and forth with OH.

There are probably as many Node Red users on this forum as there are JSR223 users, though I think there are more active users who are willing to help.

You are unhappy with the Rules DSL so I do think you should look at what is available with JSR223, but don’t expect it to be a panacea. They won’t eliminate the need to create Items and Groups entirely.

When I migrated to openHAB from another home automation system, I was very frustrated with the xtend language. Even the smallest tasks took me like forever to script. (That’s me, it doesn’t apply to others) so I tried JSR/Jython and I must say that I’ve never regret it. I love coding in Python (well Jython) and I could never do without proper functions. I even managed to script a multi zone DIY home alarm system for openHAB and I just don’t think it can be done in xtend. (At least not by me). There are some libraries that will help you a lot to get started. Especially openhab2-jython is very useful.

Just to get involved, why don’t you set up with a small project. Maybe this one. Just to get some ideas.

Cheers!

1 Like

I’ve got a rule working in Jython which is nice. Its faster than the equivalent rule. So I might try it a bit more here and there.

I currently have two thousand lines of rules written with Xtend and feel like I’ve given it a good try but I think there are some areas around groups and map reduce jobs I could utilise a bit more.

I have pretty much all the various home automation systems so my system is a way of trying to integrate an make everything run opaquely with the same behaviour. I’m still trying to figure out or come up with a proper architecture before I start reworking it all. But I’m really don’t understand Xtend.

@rlkoshak I think my issue with lambdas was it felt tacked on since they aren’t proper functions and kind of something else complately but can be made to look if you squint enough like a function.

I think the bugs I was encountering is know, something about not being about to use vars. The advice was to use a map reduce job or something.

I think my main issue is it’s not clear if the DSL is suppose to just make everything much simpler for beginners and just do basic stuff, which is fine and I should just jump over to Jython. Or if it’s just a different way of thinking about rules and is actually much more powerful when you get the hang of it. They are two completely different positions and I’m not really sure what people are saying about Xtend.

Lambdas are a function that is also an Object. If they feel tacked on it is because they kind of are tacked on by Java and Xtend runs on top of Java and inherits them from Java. But the design of Xtend is highly dependent on lambdas. Just about every method call one makes on collections requires a lambda as an argument.

Where we run into some awkwardness is the Rules DSL, which is built on Xtend, was not created with lambdas in mind as something that would be used independently from Rules. So I think some design decisions were made early on that imposed a couple of limitations.

A lambda is just an object. To make a lambda accessible to more than one Rule it needs to be created as a global variable. But the Rules DSL imposes some significant restrictions on global variables:

  • global variables can only be accessed by Rules in the same file
  • global variables cannot see each other
  • for some reason, lambdas only support up to seven arguments, which is imposed by Xtend and I don’t know why except that it might be a limitation of Java (i.e Java doesn’t support functions with variable arguments so Xtend had to draw the line somewhere).

The other awkwardness is how lambdas are defined. And just to make sure that I am not lying I went to the Xtend docs and I’ve discovered we’ve been doing it wrong all this time.

This may be the result of a recent change. First of all I discovered we no longer need to import Functions or Procedures. Second the warning has gone away when you don’t define everything in the < >. so we can define the following lambda:

import org.eclipse.xtext.xbase.lib.Functions

val Functions$Function1<HttpsURLConnection, String> readResponse = [ connection |
    // a bunch of code, last line returns a String
]

can be defined as:

val readResponse = [ HttpsURLConnection connection |
    // a bunch of code, last line returns a String
]

Or if you want to be a bit more explicit

val (HttpsURLConnection)=>String readResponse = [ connection |
    // a bunch of code, last line returns a String
]

If you want a Procedure, the last line need only return a void.

So while I will agree that lambdas feel somewhat like an outlier in Rules DSL, they are not. And if someone felt strongly enough about it, one should probably write a PR to provide some other mechanism to make real functions. Though I should say, this weirdness is not a problem with the Experimental Rules Engine which is designed to allow Rules to call each other and support libraries (no more copy and paste from DP postings and Examples on the Forum).

That isn’t a bug. That is just how it is designed to work. I suspect you wanted to reference a variable inside a forEach. The problem is you cannot reference non-final variables inside a lambda. I remember reading a big long thread at some point explaining why that is the case but I don’t remember the specifics. But it was a deliberate design choice.

Part of it was to remember, the stuff between [ | ] is an Object. It is instantiated and lives in memory. You can add it to a Collection, pass it to other lambdas or in method calls and such. So, when this new Object comes into existence, it is given a copy of the stack up to that point. But how should it actually deal with variables on that stack?

Take Timers as an example. A Rule runs, creates a lambda Object and that Object is added to the collection the Quartz timer owns to run later. When the timer does go off and the lambda is executed, the instance of the Rule that created it is long since gone. Thus it is meaningless to modify a variable that was defined outside the lambda. That variable no longer exists. But you can access final variables (i.e. vals) because you know that nothing outside the lambda can change it (nothing can change it, that’s what final means) so you can make a copy of it to give to the lambda.

But that does become inconvenient when you are wanting to loop through a List and sum the values or something. But the proper way to do that is a map/reduce anyway.

I don’t know what “people” are saying about the Rules DSL but what I say is that both are powerful and equally capable in the home automation space. Indeed the Rules DSL is simplified in a lot of ways. No regular functions, no classes, no structs, etc which do help non-programmers get started much more quickly. But I’ve found that trading some lines in my .items files I can write Rules code that is every bit as capable and as powerful in as few number of lines as I can in Python. For example: OAuth2 using just OH Rules and myopenhab.org compared to this Python example https://github.com/reddit-archive/reddit/wiki/OAuth2-Python-Example.

And honestly, this example is way more complex than 99% of the Rules DSL code most users, advanced or not, would ever need to write and all of it is dealing with making custom HTTP requests with my own header values which the sendHttp* Actions don’t support. Most stuff like this would be half the number of lines.

I’ve had a whole lot of success in finding code efficient (i.e. fewer lines of easier to understand code to do the same thing) ways to solve lots of user’s problems. If you have a particularly messy set of code you want to see what is possible with Rules DSL to make it less complex I’m willing to take a look.

I am usually able to find ways to reduce the LOC count by about 50%.

1 Like

Ooh, how about this. I was trying to figure out how to simply change the colour of any lights that are on when a scene changes. I’m a bit tired and probably just missing something obvious but maybe some help in the approach here.

Group gRoom
Group gLivingroom (gRoom)
Group gBedroom (gRoom)
Color LivingroomLight (gLivingroom, gColor)
Color LivingroomProxy (gLivingroom, gProxy)
Color BedroomLight (gBedroom, gColor)
Color BedroomProxy (gBedroom, gProxy)
.....

I want a rule that updates any lights that is currently on. The update should be via the proxy.


Scene.postUpdate("Morning")

rule "Update lights for scene"
when Item Scene changed
then
     gColor.members.filter[ f | (f.state as HSBType).brightness as DecimalType).intValue > 0

Then turn on the proxy light in the same room group. 

end

I kind of want to get a list of items that are in the two groups. e.g. if the light in the bedroom is on, then I want to get a list of proxy items that are in the same room (group) as a light that is on. It all feels like it should be very simple and it’s probably just because I’m unfamiliar with it but I seem to be going in circles.

I’ll take a look at this tomorrow morning band see if there is something I can suggest. Though I will admit, scenes are one area where OH is a little weak.

OK so your first requirement: “change the colour of any lights that are on when a scene changes” is pretty simple, assuming that all your Color Lights are in gColor.

gColor.members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f | f.sendCommand(<new color>) ]

where <new color> is the color to change the lights to. Filter so we have a List containing all the lights that are ON then send that light the new color.

You’re second requirement though is a bit different but not too bad. If we assume that all your Color proxy Items are in the group gProxy, and you have Rules that keep the proxy and real Item in sync (which should be the case for a proxy Item) then you need to filter on and loop through the gProxy instead.

gProxy.members.filter[ f | f instanceof ColorItem && f.getStateAs(OnOffType) == ON ].forEach[ f | f.sendCommand(<new color>) ]

If your proxy is acting as a true proxy then when the light is ON, the proxy will be ON and vise versa so we don’t really even need to search across multiple Groups.

So this solves your particular case, but what if there are other cases where you may want to filter and find Items like your original question. We can do this a number of ways using your existing Group structure. BTW, the following are implementations of Design Pattern: Associated Items.

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val proxy = gProxy.findFirst[ p | p.name == f.name.replace("Light", "Proxy") ]
        proxy.sendCommand(<new color>)
    ]

Filter to get a List of those lights that are ON and for each of those find the associated proxy Item based on name and send the new color to the proxy.

This assumes a one to one. If you had lots of proxy Items associated with this one light (not sure why one would have that but let’s just assume it for now):

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val proxies = gProxy.members.findFirst[ p | p.name.contians(f.name.replace("Light", "") ]
        proxies.forEach[ p | p.sendCommand(<new color>) ]
    ]

So for this we get the List of lights that are ON, and then get a list of proxies that have a matching name (assumes the naming scheme in the Items above) and send the new color to those proxies.

Or in another approach you can use the Groups, though this is a little more challenging because we don’t really have a hint as to what Group the member of gColor is also member of is that we care about. If we can assume that the members of gColor are only ever a member of one other Group then we can do something like:

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val relatedGroup = f.getGroupNames.findFirst[ gn | gn != "gColor" ]
        val proxies = gProxy.members.filter[ p | p.getGroupNames.contains(relatedGroup) ]
        proxies.forEach[ p | p.sendCommand(<new color>) ]
    ]

Filter to get a List of lights that are ON and for each of those get the name of the first Group it is a member of that isn’t “gColor”. Then filter gProxy for all Items that are also a member of the just found Group. Finally, for each proxy send the new color to the proxy.

If the lights can be a member of more than two Groups, then it gets a bit more complicated because we don’t have anything to tell us which of the two or more additional Groups that are not gColor we care about. You can use the name trick in this case again in some cases.

    gColor,members.filter[ f | f.getStateAs(OnOffType) == ON ].forEach[ f |
        val relatedGroup = f.getGroupNames.findFirst[ gn | gn.contains("room")  ]
        val proxies = gProxy.members.filter[ p | p.getGroupNames.contains(relatedGroup) ]
        proxies.forEach[ p | p.sendCommand(<new color>) ]
    ]

Which gives us the first Group name that contains “room” in the name. Of course this assumes that only one Group will have “room” in its name.

When Xtend and the Rules DSL started to click for me was when I realized that it really likes to work with Lists. All of the powerful operations (filter, forEach, findFirst, map, reduce, et. al.) are List operations. So if you can convert your problem into a List operation the language will help you solve it rather than feel like its fighting against you. So you will find that half of the DPs revolve around representing your problem as a List, usually using Groups, for which the language provides the best tools.

Thanks. The fourth/fifth example is exactly how I wanted to write the rule but I didn’t realise you could get a list of the groups an item was in. Where is the best place to get a list of what function/properties I can use in Xtend. I was just going off the group properties in the following post post so I didn’t realise I could do what I wanted. Design Pattern: Working with Groups in Rules.

I’ll try and implement your rule and also write a python equivalent.


http://www.eclipse.org/xtend/documentation/203_xtend_expressions.html // only the Expressions section is relevant
http://www.eclipse.org/xtend/documentation/102_moviesexample.html
https://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.xtend.doc%2Fcontents%2F14-movies.html
https://www.eclipse.org/smarthome/documentation/javadoc/index.html?overview-summary.html

But for the most part I just use VSCode and let the code completion tell me what is available:

image

The Working with Groups in Rules DP is my attempt to summarize the List operators that are not explained in the Xtend docs but demonstrated in the Xtend Movies Example.

Wow those links are a goldmine that are well and truly berried from normal users. I found out there are classes which could be game changing especially if I could use them with items. I can’t wait until I’m next at home to try it out.

Note my comment. Only the Expressions section applies. Rules DSL does not support classes. Rules DSL is similar to Xtend but != Xtend.

The first link is the official rules docs. The second link is linked to from the rules docs. So it isn’t exactly hidden.

The movies example doesn’t fully apply to rules DSL which is why I wrote the groups dp. So I wouldn’t usually point people to those links. I only posted them here because the documentation for working with lists is so poor for them in the Xtend

The javadocs are really the programmers interface. Not all of the methods shown are supported in Rules. This is why I recommend VSCode with openHAB extension.

Python/Jython is a programming language. Xtend is a programming language. People that write code in either one are “programming”. The declarative aspect of the Rules DSL is mostly limited to defining triggers, which can also be represented declaratively using decorators in Python. The “then” part of a the Rule DSL is a script (a program). Any nontrivial automation rule will require programming.

So, the question is which programming language do you want to use?

As a programming language, Xtend is not very popular, even among JVM-based languages. As one blog article put it, “xTend is not dead, because it has never been living.” I personally found the DSL/Xtend approach to be clunky, crippled and buggy. According to Rich, it’s just because I have had too much experience with many programming languages. According to him, if it didn’t know better, I’d be happier with Xtend. He might be correct, but that’s not a strong argument in favor of Xtend.

I agree there is more information in the OH forums about how to use the DSL and Xtend than for JSR223 Jython. However, Python is one of the most popular programming languages in the world. There is much, much more documentation about how to use Python as a language compared to Xtend. There are thousands of times more Python programmers than Xtend programmers. Multitudes of existing Python libraries can be used with Jython and OH2. And ironically, it’s even easier to integrate Java libraries using Jython. For those reasons and others (like performance), I think Jython is a net win compared to Xtend.

I’ve never said that and never will say that. I have and will say that Xtend, or more properly the Rules DSL is well suited to this specific problem space but if you feel like you are fighting it you should use JSR223. I can’t count the number of times I’ve posted people to JSR223. I’ve never once to my knowledge said anything negative about JSR223. I’ve never once to my recollection told anyone they should not move to JSR223.

But perhaps you know better what I think and what I’ve said than I do.

Agreed. But there are a large minority if not a majority of OH users who are not programmers and who will never be programmers. The barrior to those users for Python is quite a bit higher than Rules DSL. That is why Rules DSL is the default.

If we throw out Rules DSL before the Experimental Rules Engine is ready we will be abandoning a sizable chunk of the current and future OH users who will never even give OH a try let alone preservere to the point they have a working system.

My point then and my point now is the barrior to entry for none programmers is relatively lower with Rules DSL verses Jython et. al. and once past that barrior there is very little one cannot accomplish in the OH space by continuing to use the Rules DSL.

But if the limitations of the language are posing a barrior to you getting started because you are used to having programming features not available, you should use JSR223.

Though perhaps I’m wrong. Can you tell me what I think again? I keep forgetting.

You have actually made that point to me in the past (about programming preconceptions being a reason people have issues with Xtend, to be clear). It’s been awhile so it’s very possible you believe otherwise now. I never claimed you were saying negative things about JSR223 Jython or advising users to avoid it. On the other hand, I don’t agree with some of your points in favor of Xtend.

For example…

If OH users are writing DSL rules with nontrivial “then” Xtend scripts (or standalone Xtend scripts)… they are programmers. Maybe not professional programmers and maybe not self-identified as such, but they are programming and that’s what a programmer does. (We’ve had this discussion several times too.) Given that reality, it’s reasonable to discuss what programming tools are most effective for that purpose.