JRuby OpenHAB Rules System

JRuby OpenHAB Scripting

The OpenHAB JRuby scripting helpers bring the power of the Ruby language to OpenHAB. Rather than being a pure pass-through to OpenHAB, they provide a Ruby-like experience when building automation rules within OpenHAB.

Latest Release
GitHub release (latest SemVer including pre-releases)

Usage

Usage documentation is located here.

Library Status

This is an alpha and syntax and all elements are subject to change as the library evolves.

9 Likes

Post Edit

Content was removed as it no longer up to date. The first post has links to the latest documentation.

10 Likes

So have you been working on this in isolation or have you been working with any of our devs on this? I ask because we already have developers wiring on Ruby integration and it’s can be kind of hurtful to swoop in and replace all their effort without even giving an opportunity to collaborate.

What you’ve paid looks good and impressive but I worry it might step on since toes.

Hi @rlkoshak -
It was certainly never my intent to swoop in and replace anyone’s effort on anything. If that is what has happened I sincerely apologize. I would be happy to collaborate.

I am sharing what I have now, which is certainly a work in progress, for the purpose of collaborating and getting feedback.

When I started working on this in early November all I found in the forum was a couple of very old posts where people said they were going to work on JRuby, but couldn’t get it working. This led to be believe there were no large active JRuby projects underway. JRuby didn’t work “out of the box” and provided some very confusing error messages. I did a lot of debugging and working with the JRuby devs to understand exactly what was happening and found the root cause, which I opened as an issue against the core. Given the fact that no one had solved this issue (that I was aware of) and JRuby doesn’t work without it led me to believe there was not a concerted effort around building a ruby based rules system when I started.

Obviously, I am aware of the incredible work that @5iver and others have done in the arena of bringing python to the OpenHAB community. Without the groundbreaking work by that team I wouldn’t have been able to build this. @5iver and I have had a some conversations and I have shared some my lessons learned and he has shared his which let me overcome some of the hurdles I hit as well as guide me through some of the complexities (groups, etc) of the rules engine.

I did share a very early version of the proposed syntax with @5iver. My takeaway from that conversation was that the project he works on has a goal of aligning closely to the scripting API as a guiding principle. This is different from my guiding principles which I outline above. I certainly think there is room for collaboration and it would be great to have a common base. Once I get community feedback my plan was and is to submit it to the openhab-scripters repo to see what can be shared and if it can be part of that project.

My goal is to combine my passion for Ruby with OpenHAB to create a rules system that I (and hopefully others) would enjoy. If there is another group of people working on JRuby in OpenHAB and I have subverted there efforts, that was not my intent and came from lack of knowledge not malice. I am happy to rectify and collaborate and push forward together.

2 Likes

I have released version 0.1.0 with an update to support item updates in addition to state changes.

Changelog
Latest Documentation

Thank you for this and a big :+1:
I don’t know zip about jruby but the syntax looks really super easy, really liking it

if this was a nod toward the obvious simplicity then good on you!
Thank you for your contribution

it’s been mentioned lots of times in GitHub. I don’t know the status of the effort but I’ve seen it mentioned just a couple weeks ago.

Fairly recently in fact there where since changes to how openGAB does some stuff to fix done if what made Ruby not work. It had something to do with overridng File or something like that if I recall correctly. Maybe that was the issue you mentioned.

I can’t say any more than that in terms of what people are actually working on but it would have been a good idea to open an issue I think to ask.

Beyond that, I personally have concerns about different rules language implementations being too radically different in how the others work. That will shunt each language into it’s own little stove pipe and knowledge from one will not be portable. Consequently it will be challenging if not impossible to support on the forum and very difficult to move between languages. Rules will become fragmented.

So personally, I’d love to see these changes and innovations implemented in the core as opposed to in just one language. Otherwise we’ll see the balkanization of rules which would be a beer negative.

Fairly recently in fact there where since changes to how openGAB does some stuff to fix done if what made Ruby not work. It had something to do with overridng File or something like that if I recall correctly. Maybe that was the issue you mentioned.

That was the issue mentioned, and I was the one who opened it and provided the suggested resolution.

Beyond that, I personally have concerns about different rules language implementations being too radically different in how the others work. That will shunt each language into it’s own little stove pipe and knowledge from one will not be portable. Consequently it will be challenging if not impossible to support on the forum and very difficult to move between languages. Rules will become fragmented.

You raise a lot of good and valid points. I am grateful for all that you do in shepherding the community, patiently assisting new users and carefully articulating how to solve complex automation problems so anyone can understand them. No one else on the forum knows the burden of how additional languages or features could create a complex support matrix more than you.

The counterpoint to that argument is that if we aren’t using the idiomatic properties of the scripting language, there is less value in supporting that language.
Right or wrong, I didn’t approach the design with ‘How would I make Ruby work in OpenHAB’, but with ‘How would a Rubyist build a rules language to leverage the power of OpenHAB’.
I draw a lot of inspiration from Ruby based automation systems like Chef.
Maybe it is the wrong approach, but as someone who is passionate about the Ruby language, this expression felt right to me.

So personally, I’d love to see these changes and innovations implemented in the core as opposed to in just one language. Otherwise we’ll see the balkanization of rules which would be a beer negative.

Again, great point, if would be awesome if all these features were available in all the languages.
I do think there are two methods of getting there.

  1. We can drive all innovation into the core, make it available to all languages at the same time. However, the core tends to evolve slowly and for good reasons, changing the core is a big deal and shouldn’t taken lightly. When you put something into the core, you are, to a degree, hoping it will be useful and worth the effort of putting it into the core.

  2. You can drive innovation into the scripting languages and the features that prove out to be valuable can be elevated to the core and then the scripting language(s) can all begin to consume that core functionality when it is available. For example, maybe the ‘for’ syntax I have defined is useful to only execute a rule if a condition is true for a specified duration. If it is, we could look at adding that the core so its available everywhere. If it isn’t, then we haven’t burdened the core with features the community doesn’t use.

1 Like

Doing some crowdsourcing here. The JRuby rules I have outlined above lacks an implementation for received command and channels. Working on received command syntax now as the channels will be more complex.

Would appreciate some feedback on the various possible syntaxes and I am open to other suggestions.

Syntax 1

 rule 'Execute rule when item received command' do
        command Alarm_Mode, command: <command>
        run { logger.info("Item received command") }
      end

This is probably my least favorite option. Command is repeated twice (one as the property, the other as an option to the property). Command is also an active word here, where the rest are passive (updated, changed, etc).

Syntax 2

rule 'Execute rule when item received command' do
  command Alarm_Mode, only: <command>
  run { logger.info("Item received command") }
end

Slightly better, the only option syntax matches the guards for only_if. Command still has the same problem.

Syntax 3

rule 'Execute rule when item received command' do
  commanded Alarm_Mode, command: <command>
  run { logger.info("Item received command") }
end

Command becomes past tense ‘commanded’ which matches tense of the other triggers. But commanded sounds a bit weird? Command as an option is still too close to commanded.

Syntax 4

rule 'Execute rule when item received command' do
  commanded Alarm_Mode, only: <command>
  run { logger.info("Item received command") }
end

Leaning towards this syntax, although it still doesn’t feel “natural” when I look at it. It is in the past tense like the other triggers and the only option feels consistent with the other guards. (Although inconsistent with updated.).

I am open to other ideas, please provide them!
I did rule out received_command because none of the other triggers are two words.

I’m one of the people who tried to get jruby to work without success, and I was really excited when I saw this thread, great work!

I do however share @rlkoshak’s concerns that it deviates quite a lot from openhab conventions. Especially for the triggers I think it’s important that users can use them like in other scripting languages (where the rules DSL would be the model to strive for). Would it be possible to implement the trigger construction somewhat like this:

rule 'Execute rule when item received command' do
  when(Alarm_Mode, received command: <command>)
  run { logger.info("Item received command") }
end

where when is a method that creates a native openhab trigger that gets added to the rule. recieved is another method that can take either a command: or update: keyword argument with a valid command or state as value (you could also use a changed method with to:/from: optional kwargs for that type of trigger). I think this would seem more natural to write, and in line with how triggers are defined elsewhere in OH.

Another way would be to just pass a string to the when-method like the @when-decorators in the jython HL’s.

Also, I just quickly browsed through your code, and it seems like the Guards you have implemented is handled by the ruby code. Would it be possible to have these converted to a OH-native Condition that gets added to the rule instead?

I’m not as well versed in ruby as you seem to be, but have written a number of programs for personal use (often to interface with OH), and I would gladly help where I can to write the ruby helper libraries and tutorials.

2 Likes

Great to have another Rubyist around and appreciate your feedback!

when is a reserved word in ruby, so that exact syntax would not be possible.
Another method name could be possible, I can look into it. The current syntax supports arrays of items not just a single item so we might lose that without requiring them to be explicitly enclosed in an array. I can take a look at it, although I think I would add that as an optional syntax, not the only syntax. Are you proposing this just for items, or would you try to move the ‘every’ syntax into the when clause as well?

Additionally, if we have to repeat “when” and “received” everywhere is there benefit in that or is just noise hiding the true intent? I am not sure… I would be open to modeling it out if another method name can be found that doesn’t conflict.

This is all personal opinion and everyone’s will be different. I have it in my example conversion above but I will put it here as well:

@when("Item Office_Temperature changed")
@when("Item Thermostats_Upstairs_Temp changed")
@when("Item Office_Occupied changed")
@when("Item OfficeDoor changed")

vs.

changed Office_Temperature, Thermostats_Upstairs_Temp, Office_Occupied, OfficeDoor

To me, there is a lot of “noise” in the when the syntax that doesn’t provide a lot of value and is repeated without benefit. However, I can see both sides of it as it is close to what others are doing.

It would certainly be possible to also alias changed, updated etc to when_changed, when_updated which might be closer to the DSL syntax? That might be a good middle ground for those people who are drawn to the ‘when’ syntax. WDYT?

At the bare minimum a table needs to be created in the documentation that maps when syntax to the ruby syntax.

I don’t personally like the idea of using decorators or really string processing to create rules in Ruby. You lose syntax highlighting and make any of the meta-programming (that ruby is famous for) really hard. You would also lose compatibility with Rubocop and other tooling to format rules and enforce best practices automatically.

Great catch on the guards! I did look at using the condition syntax and would consider moving to that but the core doesn’t currently (that I know of) have the concept of otherwise to run a block of code when the conditions aren’t met. Without otherwise the guards become a lot less useful as a concept.

If the guard syntax is something people end up liking, then pushing otherwise or something else like it into the core (or at least notifying that the rule didn’t execute because of a condition) could be something added.

Right, forgot about the when keyword, that is out of the question then. I understand your reasoning wrt the trigger’s and the when_*-methods seems like a good compromise imo. There’s always the possibility to add string-parsing in addition to the short version if it makes people happy (I could look into some form of translation). The every-syntax is a different story imo, and a great way to simplify cron timers!

As for the guards, I believe most people who want to execute alternative actions depending on e.g. an Item state would just use an if/else in the rule. Not sure how much the conditions are used in general, but as more people possibly move to ui created rules, that might change.

At a minimum the I would potentially support the string syntax as a way of migrating rules without a lot of friction. Feel free to open an issue in GH to track and discuss.

I am curious if the when_* or string processing would alleviate some of @rlkoshak’s concerns or if they are much deeper rooted than that.

1 Like

One other area of concern is writing text based code is not the only way to write rules. If this is going to be fully supportable by openHAB it also needs to support the UI rules approach where all the rule stuff is already handled and the scripting is sued as one option for Conditions (i.e. only run this rule if this script evaluates to true) and Actions (when triggered, if the Conditions are true, run this script).

It’s not clear to me that is going to be the case here which means, if this plug-in is installed users will see “jRuby” listed as an option for these in the UI but won’t actually be able to use it because stuff that is required to support your approach is not available.

This comes to mind because of

which is doable using Script Conditions.

But openHAB itself isn’t Ruby. So you will have to compromise at some point at the interface of jRuby and Java.

There’s a lot of great work here and a lot of great idea. But honestly, if it continues in this direction its going to be another HABApp. A small project maintained by one or two people with a small userbase that ultimately is adjacent to openHAB and not really a part of openHAB.

To me, if you plan on taking a Ruby first/openHAB second approach than the question is answered. It doesn’t make much sense to me to only go half way one way or the other. But then perhaps taking an approach like HABApp took and implement this outside of openHAB (interacting through the REST API) would lead to better results over all, from a Ruby first perspective.

They aren’t really supported directly in the Jython, JavaScript and Groovy text defined rules so right now I think they are only used in UI created rules. But I’ve found them to be very powerful there. But as you say, on text rules an if/else can handle it.

They are deeper than that.

  1. The concept of guards doesn’t really exist in the other languages for textually defined rules. For UI defined rules there are Conditionals already but your guards implementation is different.

  2. You’ve invented a whole bunch of new stuff that either doesn’t exist anywhere else in openHAB or exists but is implemented in different ways, is called something different, or the features don’t quite line up. For example, delay appears to replace Timers in some (but not all) use cases.

It’s also incomplete, though that’s not as much of a problem as it’s still in work.

I can’t actually speak to what the maintainers will and will not accept to add to the baseline. But I do fear that the farther you deviate from the other languages in how interact with openHAB and present openHAB concepts I fear the likelihood of acceptance will go down, feelings get hurt and it ends up being a bad experience all the way around.

In the best of worlds one should be able to copy the rule body (which is what’s in the run-blocks in @broconne’s examples) between file- and UI- based rules and get the same result. But does this work for jython/javascript? Is the additional capabilities the helper libraries provide available to UI-rules as well? (e.g easier access to metadata and actions)

If it is, that could be made the case for jruby as well (by placing the delay inside the block instead of between two blocks for instance), and if not the same problem exist regardless of the language chosen.

Yes if you also copy over the imports. It’s not clear to me, and it’s may be my own ignorence, whether the implementation above may depend on imports or the like that would not occur when oh spins up the rule loaded from JSON.

Yes if it’s imported. A lot of the helper libraries are irrelevant though when used in ui created rules since the rule and trigger creation is handled separately.

In that case it should work with jruby as well, by require 'openhab' in the rule. We just need to make sure that there’s a separation between triggers, actions and conditions in the rules created by scripts, and that the same code would run when writing it in a scripted action or condition in the ui.

As long as these conditions are met I don’t see any problem with the syntax deviating from the openhab conventions in favor of a more ‘rubyist’ way of writing.

The one problem I see is that when using text based ruby rules you will have options and capabilities that only exist there. When using the UI the creation of the rule and the definition of the triggers is done outside the code. So there will definitely be a disconnect between what can be done in text rules and UI rules (and you’ll hear all about it on the forum I can guarantee it).

At first sight this looks similar to what the jython helper library does. It adds methods to easier write rules in either language. If such a rule is created it is build on top of openHAB and thus should be visible in the ui, but can’t be edited as a ui rule. It does create a different syntax for creating rules, which may cause some confusion. But that is the same for @rule created rules in jython and possibly just the consequences of having a powerful scripting engine.
One thing that we might want to look into is some additional (utility) functions these libraries add. If such functionality would be implemented in Java and/or added to core all libraries and possible ui rules can also use them and not every helper library would reinvent the wheel. If I have a concern it’s all these helper libraries added lots of features that also need to be maintained. The jruby variant here looks nice though.

1 Like

Hello all, as a common user I can only say waau, this just could be the scripting language I have been waiting for.
Rules seem to end up shorter and more readable then in the other languages, much less “conversion stuff” before getting down to what one really needs to accomplish.
It may just be the examples shown here but also less importing of more or less obscure packages that one would only know about from reading an example (thank you to all who provide those examples to the common user).

If I can live a long and prosperous life to the end of my days without ever writing a cron expression again I will be a happy camper for sure, this seems to solve that need in me nicely.

And if i could make a timer without needing to really learn what a hash map is and how it works even better.

I welcome the effort and will be trying it out in the near future. I hope that i will be able to handle my new scripts through the interface as with the dsl rules and python