Interacting with Items in rules
A frequent operation in rules is to calculate other values from Item states or compare Item states against other values or perform other operations that are either responses to changes in item states or intended to change the item state. For a complete and up-to-date list of what item types are currently allowed in OpenHAB and the command types each item can accept see the openHab documentation for items.
In openHAB, every item carries a state that is reflective of the status the item is in (for switch the state can be ON or OFF, for an RGB LED the state could be hue, saturation and brightness, HSB, for dimmer it could a value such as 68%). The state of an item is an object itself and can be accessed with
Just to recap some nomenclature, type refers here to the kind of data structure with which the item status reported by the Item. For example, some of the simplest data structures are of the type integers (whole numbers) or boolean (0 or 1) and other so-called primitives. But more often they are of more complex structure such as the HSB type mentioned above and those more complex structures are referred to generally as Objects. The conversion of one datatype into another is called type casting, or just casting.
To use the state of an item in rules for comparisons and other operations it is necessary to know what type of state the item is carrying and how to convert it into types that can be used in such operations.
Conversely, to use the result of a calculation in a rule that intends to change the state of an item will require the data type used for calculation and how to cast it into a type the item can accept as a modification of its state.
Items and their types: retrieving, commanding and updating
There are two parts that will be considered separately, how to update or send commands to items and how to retrieve item states. For a more detailed discussion about updating and commanding, please see [the section on Causing effects on items](LINK TO TEXT ONCE #PR469 HAS BEEN MERGED).
Types for commanding and updating
The section on items in the openHAB documentation gives a full overview on the datatypes that each item can accept. This section differentiates between command type and state type. For ease of reading, it is possible to simply add “type” to the end of a command type thereby obtaining the state type. For example the “Switch” can accept the “Command Type” labeled “OnOff”. Thus the state type is referred to as “OnOffType” in the second part of the introduction to items linked above.
And to present a more complex example, a Color Item can receive an OnOffType, IncreaseDecreaseType, PercentType, or HSBType. Therefore the following are all valid commands one can send to a Color Item:
MyColorItem.sendCommand(new HSBType(new DecimalType(123), new PercentType(45), new PercentType(67)))
An alternative way to command or update the state of an item is through the use of specially formatted strings. The section in the item documentation on formatting details the requirements for the formatting.
Methods used to update or command items can use as their argument either the appropriate object or an appropriately formatted string. In addition, as shown in the example above for the Color item, the two types “PercentType” and “DecimalType” can also accept numerical value is also permissible.
Types for Retrieving
Even though many Items accept commands and updates of various different types, each stores its state internally using only one type. For example, even though a Color Item will accept OnOffType, IncreaseDecreaseType, PercentType, and HSBType, when you call MyColorItem.state it will only return an HSBType. For a complete and up-to-date list of what item types are currently allowed in OpenHAB and the command types each item can accept see the section on items in the openHAB documentation.
Groups can be declared with any Item type so it’s internal state will match that type. For example, Group:Switch will return an OnOffType for its state.
Each State Type provides a number of convenience methods that will greatly aid in conversion and calculations. Unfortunately, the best way to discover these methods are to either:
- Use the Eclipse SmartHome Designer and the
<ctrl><space> key combo to list all the available methods
- Look at the JavaDocs for the given type (e.g. the JavaDoc for HSBType shows getRed, getBlue, and getGreen methods which would be called without the “get” part in name as in
(MyColorItem.state as HSBType).red), which retrieves the state of MyColorItem and then casts it as HSBType to be able to use the methods associated with the HSBType.
Conversion Examples for retrieving and working with item states
Reminder: For a complete and up-to-date list of what item types are currently allowed in openHAB and the command types each item can accept refer to the section on items in the openHAB documentation.
Below a non-exhaustive list of some more common conversions
A Color Item stores an HSBType.
The HSB stands for Hue, Saturation, and Brightness. Often one has the desired color as an RGB values (Red, Green, Blue). The following code can be used to send an RGB value to a Color Item.
// in rule body
val newColor = new Color(red, blue, green) // where red, blue, and green are ints between 0 and 255
When individual color values from a HSBType as a PercentType are retrieved, it will be necessary to multiply that PercentType by 255 to obtain a standard 8-bit per color channel RGB. Correspondingly, the for 16 or 32 bit representation, the percent type needs to be multiplied the percent type by 16^2 or 32^2, respectively.
//Example for conversion to 8-bit representation
// In rule body
val red = (MyColorItem.state as HSBType).red * 255
val green = (MyColorItem.state as HSBType).green * 255
val blue = (MyColorItem.state as HSBType).blue * 255
A Contact Item carries a OpenClosedType.
OpenClosedType is an Enumeration. One can convert from Open and Closed to 1 and 0 with code similar to:
val contactNum = if(MyContactItem.state == OPEN) 1 else 0
A DateTime Item carries a DateTimeType.
DateTimeType presents the biggest challenge when converting and performing calculations. The problems stem from the fact that by default the Rules use a Joda DateTime class to represent time, most notably
now. However, DateTimeType is not a Joda DateTime and in fact the two are incompatible, requiring some conversion in order to use the two together.
The lowest common denominator when working with time is to get at the epoc value. Epoc is the number of milliseconds that has passed since 1 January 1970 GMT and stored in a
long. With epoc, one can compare two dates together, convert a Joda DateTime to a DateTimeType and visa versa.
// Get epoc from DateTimeType
val Number epoc = (MyDateTimeItem.state as DateTimeType).calendar.timeInMillis
// Get epoc from Joda DateTime
val Number nowEpoc = now.millis
// Convert DateTimeType to Joda DateTime
val joda = new DateTime((MyDateTimeItem.state as DateTimeType).calendar.timeInMillis)
// Convert Joda DateTime to DateTimeType
val calendar = java.util.Calendar::getInstance
calendar.timeInMillis = now.millis
val dtt = new DateTimeType(calendar)
There are always more than one way. In certain cases it is needed to convert an epoch timestamp to a human readable and/or store it in a DateTimeType and a DateTime Item. Here’s another option to do so utilizing SimpleDateFormat:
// Convert epoch to a human readable
val SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
val String timestampString = sdf.format(new Date(timestampEpoch))
// Convert human readable time stamp to DateTimeType
val DateTimeType timestamp = DateTimeType.valueOf(timestampString)
//convert state from Item of DateTimeType into a string
val String datetime_string = DateTime_Item.state.format("%1$td.%1$tm.%1$ty %1$tH:%1$tM"))
Both Joda DateTime as well as DateTimeType (primarily through the calendar data member) provide a number of useful methods for comparing date times together and/or extracting parts of the date. For some examples (these examples are not comprehensive):
// See if DateTimeType is before Joda DateTime
if(now.isBefore((MyDateTimeItem.state as DateTimeType).calendar.timeInMillis)) ...
// See if DateTimeType is after Joda DateTime
if(now.isAfter((MyDateTimeItem.state as DateTimeType).calendar.timeInMillis))...
// Get the hour in the day from a DateTimeType
val hours = (MyDateTimeItem.state as DateTimeType).calendar.get(Calendar::HOUR_OF_DAY)
// See the Calendar javadocs for the full set of parameters available
A Dimmer Item carries a PercentType.
PercentType can be cast to and treated like a java.lang.Number. Number represents any type of numerical value. The Rules language supports doing comparisons with Numbers and doing math with Numbers and the Number supports methods for getting primitive versions of that number if needed.
// to convert a hex_code (a number expressed in hexadecimals) to a Number type
val dimVal = Integer.parseInt(hex_code, 16) as Number
//for very large_hex_codes use
val dimVal = Long.valueOf(large_hex_code, 16).longValue() as Number
//to get state as Number
val dimVal = MyDimmerItem.state as Number
if(dimVal > 50)...
val newDimVal = dimVal + 10
val int dimAsInt = dimVal.intValue
val float dimAsFloat = dimVal.floatValue
// to convert an integer_value to hex_code string
var String hex = Long.toHexString(integer_value);
Additional conversions that migt be useful are listed below under NumberItem
A Location Items carries a PointType.
A PointType consist of two or three DecimalType numbers representing latitude and longitude in degrees, and an optional altitude in meters. Here are a few examples:
val location = new PointType(new DecimalType(50.12345), new DecimalType(10.12345))
// Saving to an Item
// Loading from an Item
val PointType location = Device_Coordinates.state as PointType
A Number Items carries a DecimalType. A DecimalType is also a java.lang.Number so all the conversions listed above under Dimmer Item apply to Number Item as well.
Here some other commonly needed conversions:
//convert integer_number to string containing hex_code
var String hex_code = Long.toHexString(integer_number);
//convert hex_code to Number type
var MyNumber = Integer.parseInt(hex_code, 16) as Number
//use the following for large_hex_code
var MyNumber = Long.parseLong(hex, 16) as Number
// coverting hex_code into DecimalType
var DecimalType parsedResult = DecimalType.valueOf(Long.parseLong(hex_code, 16).toString);
One warning comes with DecimalType. The full exmplanation is beyond the scope of this introduction. To avoid an error mentioning an “Ambiguous Method Call” always cast the state of a DecimalType to a Number, not DecimalType.
The Player item allows to control players (e.g. audio players) with commands such as Play, Pause, Next, Previous, Rewind and Fastforward.The Player Item carries three types with predefined commands
These types can be convert from Open and Closed to 1 and 0 with code similar to the OpenClosedType
val Playing = if(MyPlayerItem.state == PLAY) 1 else 0
See Location item
In addition to the command types of the item type Dimmer, the Rollershutter item accepts the StopMoveType with the commands STOP and MOVE
To convert from a StringType just call toString.
val stateAsString = MyStringItem.state.toString
In case an item returns a string containing a value as a hexadecimal number, it can be converted to an integer by using
val itemvalue = new java.math.BigDecimal(Integer::parseInt(myHexValue, 16))
See Contact Item.