Accessing things without knowing their names / difficulties converting OSGI access from "JS" to "JRuby"

Hi,
I have a small JS rule that I would like to implement in JRuby. Unfortunately, I can’t get any further with it, as OSGI and JRuby are new territory for me and I can only find a few concrete examples.

Supplementary note (taken from post #3)
The actual background is that I want to write a rule that runs through all registered things to query certain properties without having to assign them e. g. to groups or other types of dictionaries.

A concrete example would be all battery charge states of battery-operated devices. I would like new devices to be monitored automatically without having to summarize them in a group.

A second concrete example would be querying the property “zwave_lastwakeup” for all things that have this property without having to name these things separately.

*Could someone help me to implement the small script in JRuby?

rules.JSRule({
	name: "JS-Test",
    triggers: [triggers.GenericCronTrigger("0 * * * * ?")],
	execute: (event) => {

        var ThingRegistry = osgi.getService('org.openhab.core.thing.ThingRegistry');
        var ThingUID = Java.type('org.openhab.core.thing.ThingUID');
        var myThing = ThingRegistry.get(new ThingUID('zwave:device:z_wave_ctrl_01:node20'));

        console.log("Debug 2 : " + myThing.getProperties()['zwave_lastwakeup'])
	}
});


Well, for one thing that’s a pretty round about way to implement this in JS Scripting in the first place.

Why not use the Helper Library? It’s reason for being there is so you don’t have to mess with these raw Java Objects and registries yourself.

rules.JSRule({
	name: "JS-Test",
    triggers: [triggers.GenericCronTrigger("0 * * * * ?")],
	execute: (event) => {

        var myThing = things.getThing('zwave:device:z_wave_ctrl_01:node20');
        console.log("Debug 2 : " + myThing.rawThing.getProperties()['zwave_lastwakeup']);
	}
});

See:

jRuby has similar access to Things: JRuby Scripting - Automation | openHAB

In neither rules language should you ever have to touch osgi ever. And you should only very rarely and under the most extreme circumstances ever need to import a raw Java Object (i.e. Java,.type in JS). According to the docs, you don’t even need to access the raw Java Thing Object to get the property like you do in JS Scripting.

1 Like

Thank you very much for your answer. I would like to explain why I am going down this path.

First of all, I’m interested in simply doing this. Along the lines of “Why are you doing it? - Because I want to try it out!” :wink:

The actual background is that I want to write a rule that runs through all registered things to query certain properties without having to assign them e. g. to groups or other types of dictionaries.

A concrete example would be all battery charge states of battery-operated devices. I would like new devices to be monitored automatically without having to summarize them in a group.

A second concrete example would be querying the property “zwave_lastwakeup” for all things that have this property without having to name these things separately.

Hence the original question, the answer to which I am still curious about.

Regarding your suggestion:
How could I query the list of available things with regular on-board tools in order to check them for certain properties without knowing their names in advance?

EDIT:
Oh, I found " getThings()" in the link you posted. This is a start - I will try… thank you :slight_smile:

But I would still be very grateful for the answer to the original question: What should the script in JRuby look like?

EDIT 2:
Great - that’s it (found here):

things.each { |thing| logger.info("Thing: #{thing.uid}")}

You don’t need OSGI for that. things.getThings() returns all the Things… You can loop though or do what ever you need from there.

You can’t get that information from the Thing in the first place though. A state like that will only be available if there’s a Channel linked to an Item.

things.getAllThings().filter(thing => thing.rawThing.getPropertys['zwave_lastwakeup'] !== undefined)
1 Like
rule "JRuby Test" do
  cron "0 * * * * ?"
  run do
    last_wakeup = things["zwave:device:z_wave_ctrl_01:node20"].properties["zwave_lastwakeup"]
    logger.info "Debug 2 : #{last_wakeup}"
  end
end

Or a simpler way to define the cron:

rule "JRuby Test" do
  every :minute
  run do
    last_wakeup = things["zwave:device:z_wave_ctrl_01:node20"].properties["zwave_lastwakeup"]
    logger.info "Debug 2 : #{last_wakeup}"
  end
end
module OpenHAB::Core::Things::Thing
  def has_battery?
    uid.binding_id == "zwave" && properties.key?("last_wakeup")
  end
end

things.select(&:has_battery?)
      .each do |thing|
        # do what you want with it
      end

Or without being fancy (which requires knowing the internals of the classes) like the above, you could do it like this

things.select { |thing| thing.uid.binding_id == "zwave" && thing.properties.key?("last_wakeup") }
      .each do |thing|
        # do what you want with it
      end
1 Like

OSGi

There’s nothing inherently bad about using OSGi directly, if you know what to do with it. However, for most tasks, JRuby library has already got you covered and most likely there’s something provided for you more conveniently than directly accessing OSGi.

The thing registry in jruby is available as things as you’ve discovered.

For the most part, you won’t need to manually create stuff like ThingUID yourself. jruby library bends over backwards to make everything so easy to do. Whenever you need a ThingUID you can supply a string and it will be converted to ThingUID for you where it’s needed.

As you may have already found out, to do this in jruby:

var ThingRegistry = osgi.getService('org.openhab.core.thing.ThingRegistry');
        var ThingUID = Java.type('org.openhab.core.thing.ThingUID');
        var myThing = ThingRegistry.get(new ThingUID('zwave:device:z_wave_ctrl_01:node20'));

You just need to write my_thing = things["zwave:device:z_wave_ctrl_01:node20"]

But if you do need to access an OSGi service and there isn’t an equivalent object / interface provided by the jruby library, here’s how to do it:

abcdService = OpenHAB::OSGi.service("a.b.c.d")

See: Module: OpenHAB::OSGi — openHAB JRuby

You can even create a new service to be used in openHAB!

Accessing Java classes

In JRuby you can access any java class (provided that it’s available within the openhab runtime) by simply writing its FQDN e.g.

my_uid = org.openhab.core.thing.ThingUID.new("xx:yy:zz")

Bearing in mind my note above that most likely most commonly used stuff is already available for you in a much more convenient manner in jruby library.

1 Like

All right, thank you both very much. :grinning:

Especially @JimTNG for this very detailed answer. I now have enough information and a base to look into this extensively. :+1:

I am also a programmer, but I come from C#. I have to admit that it is not so easy for me to familiarise myself with these structures and backgrounds, as I have had nothing to do with Java or even J(Ruby) so far.
I also understand that the way via OSGI is not the right one, but nevertheless it is very interesting for me to simply understand how it would work. So again: thank you :slightly_smiling_face:

Without the helper library, we’d all have to access openHAB’s OSGi service in order to do a lot of the more advanced things.

The helper library wraps openHAB’s API and make it easier to use and more familiar to the respective language’s conventions. The idea is that you shouldn’t have to deal with the raw openHAB API, although it doesn’t prevent you from doing so.

To familiarise yourself with JavaScript: JavaScript Scripting - Automation | openHAB

To learn about the Ruby language in general: File: Ruby Basics — openHAB JRuby

This page shows you a quick examples/comparison for some rules written in JavaScript, Ruby, and RulesDSL: File: Rule Conversions — openHAB JRuby

Avoid using Jython because that’s currently a dead end, no longer actively maintained.