How to use new ActionService for Semantics in Rules

Does anybody have some information or code examples on how to use @cweitkamp’s enhancement as described here:
[semantics] Add ActionService to support using Semantics features in Rules

  • boolean isLocation(Item) - checks if the given Item is is a Location
  • boolean isEquipment(Item) - checks if the given Item is is an Equipment
  • boolean isPoint(Item) - checks if the given Item is is a Point
  • Item getLocation(Item) - gets the Location Item of the Item
  • Class<? extends Location> getLocationType(Item) - gets the Location type of the Item
  • Item getEquipment(Item) - gets the Equipment Item an Item belongs to
  • Class<? extends Equipment> getEquipmentType(Item) - gets the Equipment type an Item belongs to
  • Class<? extends Point> getPointType(Item) - gets the Point type of an Item
  • Class<? extends Property> getPropertyType(Item) - gets the Property type an Item relates to
  • Class<? extends Tag> getSemanticType(Item) - gets the semantic type of an Item (i.e. a sub-type of Location, Equipment or Point)

Specifically I’d like to understand how to use getLocation in DSL rules

1 Like

It is documented at Actions | openHAB, though as with the rest of the Actions probably inadequately. But it’s really straight forward and works the same as any other Action like createTimer or executeCommandLine.

var location = getLocation(MyItem)

Simple as that. Core actions are imported by default.

In JavaScript you’d have to type/import the class first and pull the Item from the ItemRegistry instead of using just the name.

var Semantics = Java.type("org.openhab.core.model.script.actions.Semantics");

var location = Semantics.getLocation(ir.getItem("MyItem"));

Python would be the same overall approach.

import Semantics from org.openhab.core.model.script.actions

location = Semantics.getLocation(ir.getItem("MyItem"));

Hey Rick,
many thanks formyour reply.
I already tried exactly the same, but as I got an error message, I thought I was sure that I am using a wrong syntax.
This is the error message:


[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Test-1' failed: An error occurred during the script execution: Could not invoke method: org.openhab.core.model.script.actions.Semantics.getLocation(org.openhab.core.items.Item) on instance: null in Test

Did anybody got it working?

this is my test code:

var string strRaum = getLocation("TestItem")
logInfo(fileName,"Location: {}",strRaum)

Maybe I do not see the forrest for the trees?

That’s a string, not an Item object.

1 Like

thanks rossko!
anyway, here are two working code examples just in case someone else needs some guidance:

Example 1: Group is triggered

val fileName = "Test.rules"
rule "Test"
when
    Member of gTest changed
then
    var string strRaum = getLocation(triggeringItem)
    logInfo(fileName, "Raum: '{}'",strRaum)
end

Example 2: Item is triggered

import org.openhab.core.model.script.ScriptServiceUtil
val fileName = "Test.rules"
rule "Test"
when
    Item TestItem changed
then
    var string strRaum = getLocation(ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName))
    // you could also directly provide a string like this:
    // var string strRaum = getLocation(ScriptServiceUtil.getItemRegistry.getItem("TestItem"))
    logInfo(fileName, "Raum: '{}'",strRaum)
end

if someone else knows of a better/easier way, please let me know

1 Like

I’m not sure it will ever get easier than one line of code.

I think it is reasonable to ask if a code like in example 2

import org.openhab.core.model.script.ScriptServiceUtil
var string strRaum = getLocation(ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName))

compared to example 1

var string strRaum = getLocation(triggeringItem)

is the correct and efficient way just to get the item’s location?
Sure, you do not need to ask :slight_smile:

Well, getLocation requires an Item. If all you have is the name of the Item (e.g. triggeringItemName instead of triggeringItem) you have to pull the Item out of the registry. So that one line is as good as it gets.

In real rules, you’ll probably have the Item object already - it was the trigger, or you’re going to read or update the state, etc.
If you don’t, then that is how to get it.

One gotcha there though is in Rules DSL there isn’t a triggeringItem any more in OH 3 except for Member of rule triggers. Rules that have Item triggers will only have triggeringItemName which requires the actual Item to be pulled from the registry.

I’m inclined to file an issue to add versions of the actions that accept the name of the Item because of this. It’s not hard to implement and it would make rules in all the languages easier to write. Of course, it’s also easy to implement in something like the Helper Libraries too, but that won’t help Rules DSL users.

4 Likes

Thanks Rich

That explains why this week’s test rule didn’t work.

But the tip about member of will help streamline the rule I’m trying to create.

Thanks Rich

Your tip here helped me to streamline a group rule for colour pickers.

(I need to create a large number of these for a job soon, this makes the whole thing much easier)

		rule "colour"
		when
			Member of ColourPickers received command
		then
	
		 
		logInfo("Colour","Colour  = "+triggeringItem.name.toString+" Changed to "+triggeringItem.state.toString)
		if (triggeringItem.state.toString == "ON") {sendCommand(triggeringItem.name.toString,"0,0,100")}
		if (triggeringItem.state.toString == "OFF") {sendCommand(triggeringItem.name.toString,"0,0,0")}
		else
		{
				var HSBType hsbValue = triggeringItem.state as HSBType

		var int redValue = hsbValue.red.intValue
		var int greenValue = hsbValue.green.intValue
		var int blueValue = hsbValue.blue.intValue
		
	
		
		var RED = triggeringItem.name+"_RED"
		var GREEN = triggeringItem.name+"_GREEN"
		var BLUE = triggeringItem.name+"_BLUE"
		
		
		
		logInfo("Colour",triggeringItem.name.toString+" HSB Value ="+hsbValue+"\nRGB Values \n"+RED+" = "+redValue+"% \n"+GREEN+" = "+greenValue+"%\n"+BLUE+" = "+blueValue)	
		
		
		sendCommand(RED,redValue.toString)
		sendCommand(GREEN,greenValue.toString)
		sendCommand(BLUE,blueValue.toString)
		}

		end

Associated Items (for anyone interested)

	Group	ColourPickers		"All Colour Pickers"


// Edit the following to suit the Dimmer channels in use - Any colour item should be put into the (ColourPickers) group
	
	Color	BathroomColour			"Bathroom Colour"		<colorpicker>	(ColourPickers)	{ga="light",	alexa="BrightnessController.brightness,PowerController.powerState,ColorController.color"}
	Dimmer	BathroomColour_RED  	"Bathroom Red"					<slider>		{channel="velbus:vmb4dc:VelbusNetworkBridge:BF:CH1"}
	Dimmer	BathroomColour_GREEN  	"Bathroom Green"				<slider>		{channel="velbus:vmb4dc:VelbusNetworkBridge:BF:CH2"}
	Dimmer	BathroomColour_BLUE  	"Bathroom Blue"					<slider>		{channel="velbus:vmb4dc:VelbusNetworkBridge:BF:CH3"}
	Dimmer	Bathroom4DCCH4  	"Bathroom White"				<slider>		{channel="velbus:vmb4dc:VelbusNetworkBridge:BF:CH4", ga="Light" [roomHint="Bathroom"], alexa="PowerController.powerState,BrightnessController.brightness"}



	Color	MidRoomColour		"Middle Bedroom Colour"		<colorpicker>	(ColourPickers)	{ga="light",	alexa="BrightnessController.brightness,PowerController.powerState,ColorController.color"}
	Dimmer	MidRoomColour_RED    	"Middle Bedroom Red"					<slider>					{channel="velbus:vmb4dc:VelbusNetworkBridge:D2:CH1"}
	Dimmer	MidRoomColour_GREEN   	"Middle Bedroom Green"					<slider>					{channel="velbus:vmb4dc:VelbusNetworkBridge:D2:CH2"}
	Dimmer	MidRoomColour_BLUE    	"Middle Bedroom Blue"					<slider>					{channel="velbus:vmb4dc:VelbusNetworkBridge:D2:CH3"}
//	Dimmer	MidRoom4DCCH4  		"NotUsed"								<slider>					{channel="velbus:vmb4dc:VelbusNetworkBridge:D2:CH4", ga="Light" [roomHint="Middle Bedroom"], alexa="PowerController.powerState,BrightnessController.brightness"}

I just need to work out what to do with the WHITE channels, I’m hoping there is a hsbValue.white
but if not, I’ll just turn the colour off if they turn the white channel on, or turn off the White when they select a new colour

One thing that I noticed regarding this action is: the getEquipment method returns only the parent equipment of an item if this item is not an equipment itself. Otherwise the getEquipment returns the input item. Since I do have a multi layered equipment structure in my semantic model (e.g. washing machine → washing Maschine power socket) it would be great to always get the parent even if the input is an equipment itself.

As far as I can see there is no way to achieve this with the action today or am I mistaken?

I am trying (slightly differently) this but getting an error:

var Semantics = Java.type("org.openhab.core.model.script.actions.Semantics");    

const testItem = items.getItem('testItem');
const location = Semantics.getLocation(testItem);

console.info("**location: " + location );  
Failed to execute rule xxxxx: java.lang.UnsupportedOperationException: Unsupported operation identifier 'getGroupNames' and  object '[object Object]'(language: JavaScript, type: Item). Identifier is not executable or instantiable.

Has something changed since you wrote this?
I’m using the most recent versions of OpenHAB and JSScripting.

Based on what I’m reading in the various docs, I think I should be able to write this using ‘actions’ :

const testItem = items.getItem('testItem');
const location = actions.Semantics.getLocation(testItem) ;

But the same result

This is one of the places where understanding the when you are dealing with Java and where you are dealing with JavaScript becomes vital.

In the above code, you can see that you are pulling Semantics from Java. Java doesn’t know anything about JavaScript.

As described in the JS Scripting add-on, items['testItem'] returns a JavaScript Item which is a wrapper so that you don’t have to worry about weirdness with interacting with Java Classes and Objects in JavaScript.

So, of course, the JavaScript Items isn’t going to be a viable Object to pass to the Java Semantics Class. It needs the Java Item which you can get from the Item Registry (as demonstrated in my code) or by calling rawItem on the JavaScript Item.

testItem  = items.getItem('testItem').rawItem;

Excellent! Thank you!