Iterating over/displaying a list of all members of the event object from a JavaScript rule

  • Platform information:
    • Hardware: x86_64
    • OS: Ubuntu 18.04
    • Java Runtime Environment: OpenJDK 64-Bit Server VM Zulu11.60+19-CA (build 11.0.17+8-LTS, mixed mode)
    • openHAB version: 3.3

Hi, first thank you all for the great work you did with OpenHAB, it’s great fun using it :smile:

I’m currently upgrading the lighting control in my home using UI based Javascript rules (the ECMAScript 2021+ version) and while I generally have no problems achieving what I want with the information available, no matter what I do, I don’t seem to be able to get the script to dump me all the available members of the event object.

Why would I want to do that? The docs (github .com/openhab/openhab-js#ui-event-object) hint at some available properties, but only talk about items. Because my event is a channel trigger the list is probably not complete and my first idea was to print all available members (Javascript is interpreted, so introspection shouldn’t be a problem, right). Surprisingly, I got a very different list:

[
  "getEvent",
  "toString",
  "getType",
  "getChannel",
  "getTopic",
  "getPayload",
  "getSource",
  "equals",
  "hashCode"
]

All of these are functions. Yet the properties mentioned in the docs are in fact available, even the editor seems to know about them. I tried all sorts (medium .com/swlh/javascript-iterate-over-object-properties-b20fe67b5b9b) of ways (stackoverflow .com/questions/8024149/is-it-possible-to-get-the-non-enumerable-inherited-property-names-of-an-object) to dump the members without success and eventually found out that event is of type object, but does not inherit from Object, hence cannot have inherited properties, which may be difficult to traverse. This and the mention of the docs that the properties are Java-properties brought me to the GraalVM docs (www.graalvm .org/22.1/reference-manual/js/JavaInteroperability/#iterating-properties) which state that a javascript for…in loop is supposed to work, yet it doesn’t, except for the mentioned functions.
I also found the older helper-libraries docs (openhab-scripters.github .io/openhab-helper-libraries/Guides/Event%20Object%20Attributes.html#event-object-attributes) which are helpful in listing some more properties. Funnily they suggest to do what I was trying, and while there seems to be a solution for Python (openhab-scripters.github .io/openhab-helper-libraries/Guides/But%20How%20Do%20I.html#view-the-names-of-an-object-s-attributes) on how to print the properties, the Javascript variant unfortunately only states TODO. :thinking:

The functions I can iterate have so far been sufficient for all my needs, so the question stems more from my surprise and interest in why this seemingly trivial task seems to elude my abilities, rather than from actual need, if you get my meaning :sweat_smile:
But if anybody knows how to do this I’d be very happy (but if you tell me that what I’m asking is technically impossible, then I’d be happy, too. Even more if you could add why ^^).

Thanks, Martin

(sorry, I had to to add a space before the TLD in the links, because of some new-user limit)

What you got was this Java object when the event was triggered by a channel trigger.
https://www.openhab.org/javadoc/latest/org/openhab/core/thing/events/channeltriggeredevent

Java has no “properties” (well it does have public instance variables I guess). You use “getter” methods, e.g. getChannel(), getType(), etc.

Thanks for the clarification, Jim. This means I can use the javadocs for reference :slight_smile:

Though I’m still wondering what mechanism maps the protected fields of the java class to be publicly accessible in Javascript like in the case of event.itemName for ItemStateChangedEvents. The docs state the field to be protected. The same goes for the TYPE field which is package-private, yet accessible through type from javascript. And what is the reasoning for this behaviour? Compatibility with older versions or should I favor the mapped fields over the public Java functions because they may be changed in newer releases?

A little test script for ItemStateChangedEvent:

console.log("=== TEST ===");

console.log("event.type: " + event.type);
console.log("event.TYPE: " + event.TYPE);
console.log("event.itemCommand: " + event.itemCommand);
console.log("event.itemName: " + event.itemName);
console.log("event.itemState: " + event.itemState);
console.log("event.oldItemState: " + event.oldItemState);
console.log("Java.isJavaObject(event): " + Java.isJavaObject(event));
console.log("Java.isScriptObject(event): " + Java.isScriptObject(event));
console.log("event.getClass().toString(): " + event.getClass().toString());
console.log("event.getClass().getFields(): " + event.getClass().getFields());
console.log("event.getClass().getMethods(): " + event.getClass().getMethods());

console.log("Javascript introspection:")
for (var i in event) {console.log("    " + i);}

And the output:

2022-12-11 14:01:03.914 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - === TEST ===
2022-12-11 14:01:03.915 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.type: ItemStateChangedEvent
2022-12-11 14:01:03.916 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.TYPE: undefined
2022-12-11 14:01:03.917 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.itemCommand: undefined
2022-12-11 14:01:03.917 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.itemName: WohnzimmerLedPanel_EinAus
2022-12-11 14:01:03.918 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.itemState: ON
2022-12-11 14:01:03.920 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.oldItemState: OFF
2022-12-11 14:01:03.921 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - Java.isJavaObject(event): true
2022-12-11 14:01:03.922 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - Java.isScriptObject(event): false
2022-12-11 14:01:03.922 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.getClass().toString(): class org.openhab.core.items.events.ItemStateChangedEvent
2022-12-11 14:01:03.923 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.getClass().getFields(): [public static final java.lang.String org.openhab.core.items.events.ItemStateChangedEvent.TYPE]
2022-12-11 14:01:03.924 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - event.getClass().getMethods(): [public org.openhab.core.types.State org.openhab.core.items.events.ItemStateChangedEvent.getOldItemState(), public org.openhab.core.types.State org.openhab.core.items.events.ItemStateChangedEvent.getItemState(), public java.lang.String org.openhab.core.items.events.ItemStateChangedEvent.toString(), public java.lang.String org.openhab.core.items.events.ItemStateChangedEvent.getType(), public java.lang.String org.openhab.core.items.events.ItemEvent.getItemName(), public java.lang.String org.openhab.core.events.AbstractEvent.getTopic(), public java.lang.String org.openhab.core.events.AbstractEvent.getPayload(), public java.lang.String org.openhab.core.events.AbstractEvent.getSource(), public boolean org.openhab.core.events.AbstractEvent.equals(java.lang.Object), public int org.openhab.core.events.AbstractEvent.hashCode(), public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
2022-12-11 14:01:03.924 [INFO ] [nhab.automation.script.ui.2aaad8ad80] - Javascript introspection:
2022-12-11 14:01:03.925 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getOldItemState
2022-12-11 14:01:03.926 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getItemState
2022-12-11 14:01:03.926 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     toString
2022-12-11 14:01:03.927 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getType
2022-12-11 14:01:03.927 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getItemName
2022-12-11 14:01:03.928 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getTopic
2022-12-11 14:01:03.928 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getPayload
2022-12-11 14:01:03.929 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     getSource
2022-12-11 14:01:03.930 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     equals
2022-12-11 14:01:03.930 [INFO ] [nhab.automation.script.ui.2aaad8ad80] -     hashCode

For inspection of the object try the dumpObject function from the helper library. Documentation is here: utils - Documentation

Thanks Jens, I don’t think I had seen the docs of the helper libraries yet.
Looking at the source of the dumpObject function, its doesn’t really help for Java-native objects though (which is the case for these events, as Jim pointed out), unless it’s a HasMap:

2022-12-11 14:45:23.265 [INFO ] [tomation.script.ui.flur_lightcontrol] - utils.dumpObject(event, true)
2022-12-11 14:45:23.266 [INFO ] [org.openhab.automation.script.utils ] - Dumping object...
2022-12-11 14:45:23.267 [INFO ] [org.openhab.automation.script.utils ] -   typeof obj = object
2022-12-11 14:45:23.267 [INFO ] [org.openhab.automation.script.utils ] -   Java.isJavaObject(obj) = true
2022-12-11 14:45:23.268 [INFO ] [org.openhab.automation.script.utils ] -   Java.isType(obj) = false
2022-12-11 14:45:23.268 [INFO ] [org.openhab.automation.script.utils ] -   Java.typeName(obj.class) = org.openhab.core.thing.events.ChannelTriggeredEvent

If there was some benefit in improving this function to also list fields and functions for arbitrary Java objects using the Reflection class, I think I could provide a PR, but I’m not sure whether there are many use-cases for that beyond this question.