JRuby OpenHAB Rules System

In case you haven’t seen it, here’s the docs on actions: Actions | OpenHAB JRuby Script Library

If you could you paste your existing code, we might be able to help further.

Hi
Thanks for the quick reply.

I did have a look but could not understand it completely.

Am trying to get this working. This is jython
“actions.get(“pushover”, “pushover:pushover-account:Pushover”).sendMessage(result, “Openhab 3 - Front door Lock”)”

How would that be written in Jruby?

The document also mentions about 2 ways to access actions. one being the action way and the other thing based.

things['pushover:pushover-account:Pushover'].sendMessage(result, 'Openhab 3 - Front door Lock')

Yes, there are two ways to access actions. The above code is what we’d recommend. The other way is to make the transition easier.

1 Like

That’s easy!!! Thanks

PS The other way is :

actions("pushover", "pushover:pushover-account:Pushover").sendMessage(result, "Openhab 3 - Front door Lock")

I have been severely delinquent in announcing new releases here as we are up to 4.8!
Lots of changes to go over, but I think the biggest one is that these rules has moved from alpha status to beta and are ready to be used in production systems.

Biggest changes since 3.9:

  1. All types and items are now implemented (location, color)
  2. ensure: add ensure feature
  3. timer: supports reentrant timers and timed commands for items
  4. Lots of other fixes, and changes. See changelog for more details.

Special shout out to @ccutrer who came in with his Ruby experience and highly simplified a lot of code and completed adding all types to the language.

The ensure feature performs a check and only sends a command if the item is not already in that state.
For example:
Switch.ensure.on is now valid and will only send the ON command if the Switch is not in the ON state, this replaces having to do Switch.on unless Switch.on? throughout the code. See the docs for more details.

One thing this rules system seeks to do is make everything easier. I, personally, have always found timers in the OpenHAB to complex, error prone and require a lot of boilerplate to accomplish very simple tasks. A couple of changes were introduced in the latest release to address that:

Timers are now reentrant (meaning self resettable) if they are assigned an ‘id’ and are currently running rather than create a new timer they will reschedule. See the docs for more details.

Related to the reentrant timers, all items now have an implicit timer associated with commands. This is modeled by how some automation panels (Leviton OmniPro and others) enable simple timers without the complex logic needed to cancel, reschedule, etc. These ‘timed commands’ automatically cancel the timers if the state of the item is updated in anyway. For ON and OFF commands at the expiration of the timer they change to their inverse state, for other commands they revert to the state they were in when the command was issued.

For example, to turn a light on for 10 minutes:

light.on for: 10.minutes

This will turn the light on for 10 minutes and then turn it off after 10.minutes, if that command is issued again, the 10 minute timer will be reset.

For example, if you have some closet lights you want to turn on for 10 minutes and then turn off if they closet door is closed or if 10 minutes expire, you can do this:

rule 'Closet Door Lights' do
  changed ClosetDoors.members
  run do |event|
     light = items["#{event.item.name}_Light"]
     case event.state
     when OPEN   then light.on for: 10.minutes
     when CLOSED then light.off
     end
  end
  not_if { |event| items["#{event.item.name}_Light"].nil? }
  otherwise { |event| logger.error "No matching light found for closet door #{event.item.name}"  } 
end

No need for complex timer logic, no creating virtual items with the expire binding…

More information is located in the docs.

1 Like

4.10.1 is now released.

Biggest changes since 4.8:

  1. guards (not_if/only_if) now support arrays of items.
  2. All triggers (except for cron style) now support trigger attachments.

Trigger attachments let you attach any object to trigger and retrieve them in the run block. This lets you reuse rules that have mostly the same logic with only a change in how they are triggered. For example:

rule 'Set Dark switch at sunrise and sunset' do
  channel 'astro:sun:home:rise#event', attach: OFF
  channel 'astro:sun:home:set#event', attach: ON
  run { |event| Dark << event.attachment }
end

Attach the OFF command to the sunrise event and the on command to the sunset event. The run block then just sends the attached command to the Dark item.

More details available here.

2 Likes

4.13.01 is now released!

Changes since 4.10.1:

  • Between guard now supports ‘Month-day’ values
  • JRuby 9.3 (Ruby 2.6) support!
  • percentType adds to_byte and scale methods
  • Big shoutout to @JimT who fixed an issue where the JRuby binding would cause scripts to not reload on Openhab 3.2M2 and above.
  • Many other bug fixes

New between guard examples:

rule 'Log an entry if started between March 9th and April 10ths' do
  on_start
  run { logger.info ("Started at #{Time.now}")}
  between '03-09'..'04-10'
end

new to_byte and scale examples:

HueBulb.red.to_byte # => 255
Volume.scale(-80..20) # Decibel scale

This blog post has a good review of the new features in ruby 2.6 now available in this binding.

As always big thanks to @ccutrer and @JimT for all of their fixes and contributions!

Other big news - a PR has been submitted to the OpenHAB add-ons repository to merge in the JRuby binding!

4 Likes

Nice!!! :+1:

I’m trying to run the binding from the internal addons repo that is shipped with OH 3.2.0.M5 and getting following errors:

[ERROR] [ng.internal.JRubyScriptEngineFactory] - bundle org.openhab.automation.jrubyscripting:3.2.0.M5 (244)[org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory(446)] : The activate method has thrown an exception
java.lang.BootstrapMethodError: bootstrap method initialization exception
	at uri_3a_classloader_3a_.META_minus_INF.jruby_dot_home.lib.ruby.stdlib.rubygems.stub_specification.RUBY$block$data$1(uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/rubygems/stub_specification.rb:118) ~[?:?]
	at org.jruby.runtime.CompiledIRBlockBody.yieldDirect(CompiledIRBlockBody.java:151) ~[?:?]

    ...

2021-12-15 21:38:02.311 [WARN ] [ipt.internal.ScriptEngineManagerImpl] - bundle org.openhab.core.automation.module.script:3.2.0.M5 (161)[org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl(173)] : Could not get service from ref {org.openhab.core.automation.module.script.ScriptEngineFactory}={service.id=702, service.bundleid=244, service.scope=bundle, rubylib=/etc/openhab/automation/lib/ruby/personal, component.name=org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory, gem_home=/etc/openhab/automation/lib/ruby/gem_home, service.config.label=JRuby Scripting, component.id=446, service.config.factory=false, service.config.category=automation, gems=openhab-scripting=~>4.0, service.config.description.uri=automation:jruby, service.pid=org.openhab.automation.jrubyscripting}
2021-12-15 21:38:02.313 [WARN ] [ipt.internal.ScriptEngineManagerImpl] - bundle org.openhab.core.automation.module.script:3.2.0.M5 (161)[org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl(173)] : DependencyManager : invokeBindMethod : Service not available from service registry for ServiceReference {org.openhab.core.automation.module.script.ScriptEngineFactory}={service.id=702, service.bundleid=244, service.scope=bundle, rubylib=/etc/openhab/automation/lib/ruby/personal, component.name=org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory, gem_home=/etc/openhab/automation/lib/ruby/gem_home, service.config.label=JRuby Scripting, component.id=446, service.config.factory=false, service.config.category=automation, gems=openhab-scripting=~>4.0, service.config.description.uri=automation:jruby, service.pid=org.openhab.automation.jrubyscripting} for reference ScriptEngineFactory
2021-12-15 21:38:12.265 [ERROR] [ng.internal.JRubyScriptEngineFactory] - bundle org.openhab.automation.jrubyscripting:3.2.0.M5 (244)[org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory(446)] : The activate method has thrown an exception
java.lang.BootstrapMethodError: bootstrap method initialization exception
	at java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:194) ~[?:?]
	at java.lang.invoke.CallSite.makeSite(CallSite.java:307) ~[?:?]

...

Caused by: java.lang.RuntimeException: java.lang.IllegalAccessException: no such method: org.jruby.runtime.Helpers.constructRubyStringArray(RubyString,RubyString,RubyString)RubyString[]/invokeStatic
	at org.jruby.runtime.Helpers.constructRubyStringArrayHandle(Helpers.java:1510) ~[?:?]

Caused by: java.lang.IllegalAccessException: no such method: org.jruby.runtime.Helpers.constructRubyStringArray(RubyString,RubyString,RubyString)RubyString[]/invokeStatic
	at java.lang.invoke.MemberName.makeAccessException(MemberName.java:959) ~[?:?]
	at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1101) ~[?:?]
...

Caused by: java.lang.LinkageError: loader constraint violation: when resolving method 'org.jruby.RubyString[] org.jruby.runtime.Helpers.constructRubyStringArray(org.jruby.RubyString, org.jruby.RubyString, org.jruby.RubyString)' the class loader 'bootstrap' of the current class, java/lang/Object, and the class loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @6116f3 for the method's defining class, org/jruby/runtime/Helpers, have different Class objects for the type org/jruby/RubyString used in the signature (java.lang.Object is in module java.base of loader 'bootstrap'; org.jruby.runtime.Helpers is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @6116f3, parent loader java.net.URLClassLoader @153b3e6)
	at java.lang.invoke.MethodHandleNatives.resolve(Native Method) ~[?:?]
...

2021-12-15 21:38:12.360 [WARN ] [al.provider.ScriptModuleTypeProvider] - bundle org.openhab.core.automation.module.script:3.2.0.M5 (161)[org.openhab.core.automation.module.script.internal.provider.ScriptModuleTypeProvider(177)] : Could not get service from ref {org.openhab.core.automation.module.script.ScriptEngineFactory}={service.id=702, service.bundleid=244, service.scope=bundle, rubylib=/etc/openhab/automation/lib/ruby/personal, component.name=org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory, gem_home=/etc/openhab/automation/lib/ruby/gem_home, service.config.label=JRuby Scripting, component.id=446, service.config.factory=false, service.config.category=automation, gems=openhab-scripting=~>4.0, service.config.description.uri=automation:jruby, service.pid=org.openhab.automation.jrubyscripting}
2021-12-15 21:38:12.361 [WARN ] [al.provider.ScriptModuleTypeProvider] - bundle org.openhab.core.automation.module.script:3.2.0.M5 (161)[org.openhab.core.automation.module.script.internal.provider.ScriptModuleTypeProvider(177)] : DependencyManager : invokeBindMethod : Service not available from service registry for ServiceReference {org.openhab.core.automation.module.script.ScriptEngineFactory}={service.id=702, service.bundleid=244, service.scope=bundle, rubylib=/etc/openhab/automation/lib/ruby/personal, component.name=org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory, gem_home=/etc/openhab/automation/lib/ruby/gem_home, service.config.label=JRuby Scripting, component.id=446, service.config.factory=false, service.config.category=automation, gems=openhab-scripting=~>4.0, service.config.description.uri=automation:jruby, service.pid=org.openhab.automation.jrubyscripting} for reference ScriptEngineFactory

I was able to install and run the external binding version under OH 3.2.0-M4 without any issues, and tried to delete any binding installation artefacts (cleared cache, anything under /conf/automation/…).

Any idea what’s going wrong here and how to fix it?
Happy to provide more details if this helps…

BTW: Many thanks to the authors of this great binding which for me has made scripting under OH more a fun than a pain.

@ccutrer or @JimT have either of you tried this in M5?

I see this error on M5 and possibly before M5. My rules still work though.

I am wondering if it’s related to the Java classes injected by scopeValues and the gem installation (which is evaluated in Ruby) during the bundle @Activate then tripped over the Java Classes. I was going to put a trace in the scopeValues to see if it occurred before @Activate, but I haven’t got around to it yet.

Does it go away on restart?

@broconne I updated my post above… it’s just a hunch.

I’m on a snapshot as of a day or two ago (so I think newer than M5?). I haven’t noticed any errors in the logs, but that doesn’t mean there weren’t any. My rules are all working fine.

I had these errors as well when I first installed the bundle, but they went away after a restart. I deleted the old jar file from the addons-directory, and made sure is wasn’t listed as installed in karaf, but perhaps there were still some remnants of the old bundle left causing conflicts?

I’ve tried to restart the bundle from the console and by restarting OH without success.

The diag for the bundle is:

openhab> bundle:diag 271
openHAB Add-ons :: Bundles :: Automation :: JRuby Scripting (271)
-----------------------------------------------------------------
Status: Waiting
Declarative Services
org.openhab.automation.jrubyscripting.internal.JRubyScriptEngineFactory (576)

Can you share more about how you installed it?

I did a clean install, downloaded m5, downloaded the kar file, put the kar file in addons, then I added ‘jrubyscripting’ to the automation section of the services.cfg and I didn’t have any errors in the logs.

On M5+ there should be no need to download the jar. jrubyscripting can be installed from the GUI, OR using text configuration by adding this inside conf/services/addons.cfg

automation = jrubyscripting

Can you try this in the console:

bundle:list -s | grep jruby

There should be just one listed.