Type Conversions

Attention: This article is now also available on the openHAB 2 documentation page. If you want to add improvements or additions, please do so there.

http://docs.openhab.org/configuration/rules-dsl.html#conversions

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 MyItem.state.

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(ON)
  • MyColorItem.sendCommand(INCREASE)
  • MyColorItem.sendCommand(new PercentType(50))
  • 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

Color Item

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.

import java.awt.Color

...

// in rule body
val newColor = new Color(red, blue, green) // where red, blue, and green are ints between 0 and 255
MyColorItem.sendCommand(new HSBType(newColor))

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

Contact Item

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

DateTime Item

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:

import java.text.SimpleDateFormat
import java.util.Date

// 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

Dimmer Item

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

Location Item

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:

// Creation
val location = new PointType(new DecimalType(50.12345), new DecimalType(10.12345))

// Saving to an Item
Device_Coordinates.postUpdate(location)

// Loading from an Item
val PointType location = Device_Coordinates.state as PointType

Number Item

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.

Player Item

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

State Type Commands
PlayPauseType PLAY, PAUSE
RewindFastforwardType REWIND, FASTFORWARD
NextPreviousType NEXT, PREVIOUS

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

####PointType

See Location item

Rollershutter Item

See Dimmer
In addition to the command types of the item type Dimmer, the Rollershutter item accepts the StopMoveType with the commands STOP and MOVE

String Item

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))

Switch Item

See Contact Item.

40 Likes

Thanks for this. Type conversions have been at the center of some of my biggest openHAB frustrations. Next time I’ll come back here first!

2 Likes

@rlkoshak I’ve added the alternative Epoch conversion (doesn’t hurt) and the Location Item and PointType to the listing above. You can see all changes on the history. Are you going to open the article as a wiki article?

Thanks!

Totally forgot about the wiki. That is done now too, I think…

1 Like

Great! :+1:

The next thing we should try to achieve is a common structure. I’ve already started with the first sentence for each Item type. Next I’d like to add Create, Save and Retrieve examples to all Item types (see Location). I think this kind of unity will help users to quickly find what they are looking for.

I’d also suggest to add Links to the JavaDoc article of all Items and Types mentioned.

@lipp_markus you wanna join the effort? The first article is a “wiki article” now, meaning everyone can add content.

Wow, this thread has some perfect candidates for VSCode extension snippets :wink:

4 Likes

Works for me, thanks!

just looking thought he documentation: https://meta.discourse.org/t/what-is-a-wiki-post/30801
While a neat feature, I am unclear whether discourse provides a full version history for these wiki posts.
Say I am starting to edit and we cannot agree, or I won’t finish (just presume), could all edits be undone and wiki returned to the original post? I would hate to risk a scenario, where edits would render Rich’s original post unusable.

Not urgent, but in case of doubt, I will rather put a new version just here in the thread instead of modifying the original post…also as it may take a while to write it all up, and I surely do not want to leave a half-baked wiki post hanging around while I ponder over the next sentences…

@rlkoshak as promised here some edits and rewrites.
In addition to general comments, here some specific comments to discuss:

  • I deleted the Contact and Switch; did not find the example of conversion too helpful and Player item description is really helpful here.
  • Added references to the eclipse descriptions of items, I had the impression that it may be the primary source (maybe I sprinkled them too generously throughout the document, but it appeared to me to be a pretty important reference that may have been underutilized so far)
  • Analogous to the Contact, I deleted the Player item; after checking eclipse docu, it appears that it only accepts the binary enum types PlayPauseType (PLAY, PAUSE), RewindFastforwardType (REWIND, FASTFORWARD), NextPreviousType (NEXT, PREVIOUS)
  • changed some of the formatting (level of indent, etc) to try to match the doc style in the rules document

Happy to post over your original article, but as stated above, was not fully comfortable doing so without hearing from you first

It appears that I am lacking the privileges to make this a wiki (would have made for easier editing)…


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.

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 MyItem.state.

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.

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 Eclipse documentation for items.

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 here (LINK TO TEXT ONCE #PR469 HAS MERGED)

Types for commanding and updating

The section on items in the Eclipse documentation and 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(ON)
  • MyColorItem.sendCommand(INCREASE)
  • MyColorItem.sendCommand(new PercentType(50))
  • 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 Eclipse documentation for items.

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 Eclispe documentation.

Below a non-exhaustive list of some more common conversions

Color Item

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.

import java.awt.Color

...

// in rule body
val newColor = new Color(red, blue, green) // where red, blue, and green are ints between 0 and 255
MyColorItem.sendCommand(new HSBType(newColor))

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

DateTime Item

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 epoch value. Epoch 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:

import java.text.SimpleDateFormat
import java.util.Date

// 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)

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

Dimmer Item

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.

val dimVal = MyDimmerItem.state as Number

if(dimVal > 50)...

val newDimVal = dimVal + 10

val int dimAsInt = dimVal.intValue

val float dimAsFloat = dimVal.floatValue

Location Item

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:

// Creation
val location = new PointType(new DecimalType(50.12345), new DecimalType(10.12345))

// Saving to an Item
Device_Coordinates.postUpdate(location)

// Loading from an Item
val PointType location = Device_Coordinates.state as PointType

Comment: This does not give any indication on how to extract a single value out of this tuple(?). Is there a method getLongitude?

Number Item

A Number Items carries a DecimalType. A DecimalType is also a java.lang.Number so all the conversions listed above apply.

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.

PointType

See Location item

Rollershutter Item

See Dimmer
In addition to the command types of the item type Dimmer, the Rollershutter item accepts the StopMoveType with the commands STOP and MOVE

String Item

To convert from a StringType just call toString.

val stateAsString = MyStringItem.state.toString
1 Like

I think both switch and contact should be included for completeness. Every Item type and State type should be mentioned, even if the content is minimal.

Player, location, and call all need to be added to the Items page types table as well. We should open an issue for that if there isn’t one already.

But I still maintain that the enum types (OnOffType, OpenClosedType, and apparently PlayerType) need to be kept in this section as well. Even if the content is minimal (show how to convert to a number and a String) I’d rather the section be complete than cause confusion to a newcomer wondering why they are missing.

I think all of that content gets copied over to the OH docs automatically. If not it needs to be. I’d much rather avoid confusion and only point to the OH docs for documents like this. It might cause confusion otherwise.

And in truth that page looks identical to the current Items under concepts page where the table of types exists. So maybe the issue to add the missing types needs to be on the esh docs.

I think you can only make an original posting a wiki, not a reply.

Except for my comments about wanting to keep all the types and to point to the OH version of the Item Type descriptions it all looks good to me.

And I’m willing to be convinced on dropping the enum types.

Adding this here as a reminder to myself. We should include this Hex parsing example in the Number section:

@rlkoshak took all your suggestions form post #11 and incorporated them. This time I edited the wiki as discussed.

It did not make sense to me to add this to the Number item section, as it is stated there:

Which would make it confusing to me, how we end up with a hex-value. Thus, I added it to the string section, please check whether you are OK with it and whether I captured it correctly.

Finally, if we are all OK, I am not sure how to proceed as it may cause conflicts with PR#469. Both should go into the rules documentation and #469 should come before this one here. I guess I will wait until #469 is merged and open another PR for issue #472 only after it merged.

The conversion is to go from hex to DecimalType, or at least. The article, in my mind at least, should cover conversions in both directions. We show both directions in the DateTime section.

It comes up frequently. All that being said, the String section is as good a place to put it as the Number section. We should probably include an example of parsing a Date Time String.

I’m on my phone for the next few days so can’t easily look up the the issue numbers. But I have no problems with what you say.

Ok, good points, I added some more conversions in particular hex to Number and DecimalType (and vice versa) to the wiki entry

Hey guys, short reply from me. I’m currently on a surprise vacation (invited on Thursday, flight Friday morning). I think we should try to make the article a bit more systematic, as already mentioned here. Definitely something I can work on in the next couple of day (I have my laptop with me).

The rest of the changes look great! Later guys :wink:

Really great text and discussion!

What bothered me for some time - is there a way to convert string (the name of the item) to the object, so the item object further be manipulated.

If there is no way for this, it would be great to have this clearly stated as well.

No, from a programming perspective that doesn’t make any sense. We could add a statement that one cannot do this but if we start going down the path of listing everything that one cannot do the article becomes infinite in length.

Having said that, if you do a little but of setup up front you can get the Item or if a group by name. See Design Pattern: Associated Items

Thanks, fair enough. I just thought about something like FAQ where “NO” would be said to questions of dummies, even if they do not make sense from a programming perspective.

Just wanted to mention that here for everyone to be aware, the tutorial posted here is now also available at:

http://docs.openhab.org/configuration/rules-dsl.html#conversions

If you want to add improvements or additions, please do so there!

3 Likes