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