A beginners nightmare

I think the hardest part is the XTend-Rule engine. Almost every time I struggle with it, because it doesn’t allow the simplest things like classes, functions, etc…
I have my whole XTend-knowledge from examples from the forums and the wiki.
Something more common with better documentation (jsr223-addon is a step in the right direction) is essential.

The configuration of the addons was almost pretty straightforward. This is imho the only part where some GUI or Interface would make sense. Please don’t torture us with clickable rule creation. For the inexperienced users there is HABmin which allows you to create simple rules through a GUI.

What really would help is when you could test your Rules in the openHAB Designer.
I think this could really be possible by reading the variables and data via the web-API

1 Like

I agree, the most time I spend on OpenHAB is shared between Zwave devices and creating working rules.
Items and sitemaps are not a problem.

It doesn’t help that I can’t get the designer to work, so I edit using notepad++.

But as a software developer I would like to unittest my rules, that would really be great.

I agree, the lack of classes (which XTend does support but openHAB’s DSL does not) is a pain but almost every place I’ve wanted to use a class I’ve found a more elegant and proper XTend way to do it. But you can get functions (mostly) through lambdas. They have their limitations but I’ve managed to use them quite effectively.

And XTend is itself pretty well documented here with a pretty good example program with explanation showing how to use a lot of XTend specific stuff. That coupled with the examples in the wiki and here I’ve not had any more trouble learning XTend than I have had learning any new programming language.

Hey rlkoshak,
I use lambdas quite often, almost in every rule. But they are really quite limited, yet better than nothing.
Beeing unable to access global rule variables is a pain. I always have to declare additional parameters to access variables. Since I have quite a few I work with hashmaps all the time (in which I create a class alike data structure).
The setup and creation of the hashmaps seem to take lots of calculation time (first time execution ~60sec). While this is only annoying (it’s only the first time) not beeing able to call another lambda or the lambda function itself recursively makes structuring the code really hard.
As a workaround I basicly put all data in hashmaps, call the first function from the rule with the hashmap as parameter, call the second function from the rule with the hashmap as parameter, etc… This makes the code unnecessary complex.

What I haven’t figured out yet, is how to use common code in multiple rules (-files). Do you know how?

I have worked with the XTend documentation but unfortunately half the stuff there doesn’t work with openhab since we don’t have classes, functions, etc… It’s basicly just expression and properties.
Maybe that was why I have/had so much problems with it because of the reduced possibilities which I am not used to from other programming languages.
Greetings

I’ve been down that road of using the hashmaps and passing lambdas to lambdas. However, in every case I got to a point of saying to myself “This is ridiculous”, stepping back and in each case (so far) I’ve found a better way to do it that didn’t feel like abusing XTend/openHAB. For example, I was using the hashmap approach to help determine presence (iPhone doesn’t stay on the network reliably so I added in OwnTracks into the mix and I had this complicated code that ran to a few hundred lines. So I started over from scratch and now have something that works much better and is only about 30, there is one lambda and no hashmaps. I’ve found I can go a long way towards simplification by keeping some of the data in Items rather than in variables inside rules or global rules variables.

The key for me was to quit trying to force an OO Java/C++ type programming paradigm onto XTend and code it more like I would code in a functional language like Lisp.

I haven’t paid attention to the runtime of creating hashmaps. I’m running openHAB on an old but powerful laptop with a broken LCD rather than a resource constrained device like a Raspberry Pi so I’ve not seen any performance issues. I will probably pay closer attention eventually but it hasn’t become a problem for me yet.

You can call a lambda from another lambda if you pass it as an argument (or put it into your hashmap). A lambda is a variable like any other. I’ve done that in a number of places, though I’m slowly reworking my rules and eliminating the need for that in most places.

The only way I know you can call code in multiple files is to create a script. I’ve not used scripts yet as I’ve separated my rules based on function rather than location (e.g. I have an entry.rules and a lights.rules rather than a familyroom.rules and bedroom.rules). Doing this has made my files pretty much completely independently from each other with the only overlap being the need for some of the values in Items in my weather.items and presence.items files outside of weather.rules. Scripts have some limitations too but they are less constrained than lambdas. Another thing you can do if your function just causes a side effect or gathers some data is to write it in an external shell/python/etc script which you call either from an exec binding on your item or an executeCommandLine action (note that only the one that takes a timeout will return the output of the script).

I understand your struggles, I fought with them too. But things became easier for me once I just accepted that the XTend based DSL for openHAB rules is what it is and rather than trying to force it to do something it doesn’t want to and I spend time learning how the DSL would prefer I do it. I’m still learning but it has made my experience much happier and my rules code much cleaner.

It also might be worth your time to get the Jsr223 Script Engine working so you can work in a language you are used to, though there is no guarantee that it will continue to work in openHAB 2.

Good luck and semper gumby.

Rich

Hi,
thank you for your hint that you can pass a lambda to another lambda. I was not aware of that, even if it inflates the call additionally. I am normally used to structure problems into small fractions and then create a corresponding function. Now I can’t use that approach and I am really struggling with it.
Unfortunately I want to create really complex and mighty rules for my home and without the possibility of the OO-aproach things tend to quickly get hard to maintain.

As I have described in another thread it would helpful if you could access items by their name and get their values, like it is possible to post an update to an item-name. With this and a strickt naming schemata it would be possible to structure items and access them dynamically.
I already use this approach for most of my lighting. Most of my LEDs are controlled via dmx which is quite powerful and cheap. Unfortunately with the dmx-binding I have to create an item for every fade and then send an ON-Command to it. I named the items fade_dawn, fade_morning, fade_night, etc. and create the corresponding name on the fly.
In my bedrooms I put all the possible fades/items in an hashmap because and access them because I have multiple fade-senders which have different timeouts and different reactions. The bookkeeping is done in a second hashmap so I only have to pass two parameters to the functions.
What do you think about the getting items by name idea? Maybe you can contribute in the other thread. Thank you.

I will definately look in the jsr223, but I have no experience with python so I have to try out some stuff there first.

I totally understand where you are coming from. I’ve been primarily a Java programmer for over 15 years and C++ before that. Breaking problems down in an OO manner is second nature with me. But like you I kept running into limitations with XTend, which are doubly frustrating because XTend is built on and has access to Java, it should be able to do it darn it!

Oh yes, hashmaps galore. I’ve been there. I even had cases where I had hashmaps of hashmaps. I’ve manage to eliminate almost every single one of them though by using other constructs in openHAB. Don’t forget that you have Groups which you can iterate over and filter in a single line of code. I’ve used this to eliminate my need/desire to try to access the value of an item by name because items that I treat the same are part of the same group so there is no need to look anything up.

For example, I have four categories of lights: those that come on 90 minutes before sundown, those that come on at sundown, those that come on when the weather says it’s cloudy (a hack until I build some light sensors) and those that turn off at 11 pm (light can belong to more than one group). The one major gotcha is that that if I manually turn on or off a light that can be controlled by the weather, that value overrides the weather setting.

I used to have 140 lines of code, three lambdas (one which was called from the others as well as from some rules) and about a dozen rules and any time I added a new light I had to manually update the rules files, changing the hashmaps, the lambdas and add at least one rule. I also didn’t use Groups at all. It was complicated and brittle and I was unhappy, as you seem to be. It also felt like the most straightforward pseudo OO approach but it was ugly and complex.

I’ve since updated it significantly, dropping the LOC by half and now I can add or remove a light from any group to change its behavior. In my rules I iterate over the Group’s members and send the appropriate commands, or trigger on the Group and figure out what to do from there. My rules files are now much shorter, easier to understand, and easier to maintain.

Here is my rule which gets called when I manually switch a light, which marks it as overridden so the weather rule doesn’t change it (one of the cases where I still use a hashmap).

rule "Any light in gLight triggered"
when
    Item gLights received update
then
    Thread::sleep(250) // give lastUpdate time to be populated
    val mostRecent = gLights?.members.sortBy[lastUpdate].last as SwitchItem
    if(whoCalled == MANUAL) {
        overridden.put(mostRecent, true)
    }
end

The magic takes place on the val mostRecent line where it sorts all the members of mostRecent by its lastUpdate time and grabs the last one. Before I had to have a rule for each Item which called a lambda to record that the the light was overridden (assuming it was triggered manually). About 30 LOC collapsed to this one easy to understand rule.

Here is the rule to turn off the lights at 11:

rule "Lights Bedtime"
when
    Time cron "0 0 23 * * ? *"
then
    whoCalled = TIMER

    gOffTimerLights?.members.forEach[light |
        overridden.put(light as SwitchItem, false) // reset any overrides
        applySwitch.apply(false, false, TIMER, light) // a simple lambda that checks whether a light is overridden before thurning it on
    ]
end

I was able to even further simplify my entry sensor rules. I used to have a couple of hashmaps, a couple of lambdas, and a rule for every individual door sensor in order to do things like remind me that I left the garage door open for more than an hour. I had three lambdas and a rule for each door sensor. Now I’ve now put all the doors I want the reminder from into a group and I have the one rule:

rule "Reminder for doors that are open for over an hour that we want to know at any time"
when
    Time cron "0 0/15 * * * ?"
then
    gRemindDoorSensors?.members.filter(s|s.state == OPEN && !s.changedSince(now.minusHours(1))).forEach[ door |
        sendNotification("myID", door.name + " is still open"
    ]
end

I have other rules for those I want a reminder about if they are open at night or when noone is home. And once again, I don’t have to change the rules to change the behavior of the doors and I reduced this rule from a complicated lambda calling mess to three simple rules that almost all fit on one screen.

So I encourage you to take a step back and think about your rules. Based on my experience I bet there is a simpler way (you can PM me if you want to work through it together and don’t want to do so out in the open). Without knowing better how your rules are working I’m not sure I can offer any concrete examples beyond the above that might be directly applicable, but given what you have provided I wonder if you couldn’t use Groups and some Items to replace some or all of your hashmaps along with some rules which loop and filter on your groups and greatly simplify things.

Finally, I just realized in writing this you CAN get an item by name, provided it is in a known group with:

val byName = gGroup?.members.filter(s|s.name == "ItemName").head as SwitchItem // assumes only one item

So at least there is that.

Rich

4 Likes

I don’t want to hijack this topic. So if it is okay for you I’ll open another one! :smile: