Tutorial on how to use transformations

Transformations are used for four purposes:
1. change the way item states are displayed
2. change the item states
3. convert data in rules
4. convert data inside a binding

States and displayStates

An item receives its raw state value from the binding via the channel link (or internally with rules). This value is often not well readable (eg. too many decimals, not the correct language, …).

You can add a displayState to the item to solve this. Some bindings provide both the state and displayState.

But in many cases there will be no displayState defined by default. This is by design as @ysc points out here.

You can test this in the API explorer / Widgets expression tester

eg. enter the expression

=items.uptime

and the result can be something like

{ "state": "564.4", "displayState": "564.4 Minutes" }

If you only see the state, then the displayState is not defined.

Persistence only records states, not displayStates.

1. Changing/defining the displayState

Patterns

The easiest way to do it is using a pattern when defining the item.

Using item files this can be:

Number Livingroom_Temperature "Temperature [%.1f °C]" {channel="..."}

If you use the Main UI you can define the pattern in Metadata / State Description / Pattern:

The syntax of possible patterns can be found here.

Transformations

When it becomes a little more complicated, you can use a transformation.

You need a transformation addon for this. It can be installed via Settings / Other Add-ons

A simple one is using a map transformation:

Contact Livingroom_Window " Living venster [MAP(window_nl.map):%s]"

where the map is a file located in the transform directory. The contents look like:

OPEN=open
CLOSED=gesloten

If you use the Main UI you should define it like this:


Unfortunately as for now, this doesn’t always work in Main UI, depending on the internals of the binding. There is a long discussion about it here.

As a workaround you can use the Metadata / State Description / Options:

Another example is the Javascript Transformation.

If for example you want to display the system uptime in a more readable form, you can use a program CPUTime.js written by @vzorglub : Javascript transform example with system uptime

For file based item definitions, the item definition can be

Number uptimeD "[JS(CPUTime.js):%s]" {channel= "systeminfo:computer:Systeminfo:cpu#uptime"}

If you use the Main UI you have to add this pattern:

2. Changing the state

You can also opt to change the item state instead of the displayState.

This way the raw state that comes from the binding will be changed in the channel link. There will be no displayState.

You have to use Profiles to do that.

For file based item definitions this is

String uptimeP {channel = "systeminfo:computer:Systeminfo:cpu#uptime" [profile="transform:JS", function="CPUTime.js", sourceFormat="%s"]}

In the Main UI it is easier:

As @rossko57 pointed out here, there is an important difference between changing displayStates and the use of profiles.

  • With displayStates the item can be of any type as the displayState is always a string.
  • With the transformation profile, the item can also be of any type, but the transformation will need to return something suitable for that type, e.g. “1.5 kWh” for a Number:Energy Item. In the example above, the item type must be a string.

The use of profiles can be interesting if you want to convert numbers.
An example: the CPU temperature in the Systeminfo binding is expressed in degrees Celsius. If you want to convert it to degrees fahrenheit you can use this Javascript transformation function:

(function(i) {
    var celsius = parseInt(i); // The value sent by OH is a string so we parse into an integer
    var fahrenheit = (celsius * 1.8) + 32
    return fahrenheit
})(input)

If you do it with the first method (displayStates), then you get the correct fahrenheit value displayed in your widgets. But the persistence only saves the raw state, so your charts will be in degrees celsius.

With the second method (profiles), the raw value is changed and recorded in the persistence. The charts correctly display degrees fahrenheit.

3. Rules

You can also use transformations inside rules, where you assign the result of the transformation to a variable.

Example:

From my solar panel inverter a JSON string is received in the form:

{ "ppv": 344, "active_power": 6, ... }

in an item called ZP_JSON_Out.
To split this string in its different components, a JSONPATH transform is used in a rule:

rule "ZP JSON transform" 
when 
    Item ZP_JSON_Out changed 
then
    val Pv = transform("JSONPATH","$.ppv",ZP_JSON_Out.state.toString)
    val ActivePower = transform("JSONPATH","$.active_power",ZP_JSON_Out.state.toString)
...

4. Convert data inside a binding

Some bindings allow to use transformations from the inside. Modbus, HTTP, MQTT and the Serial binding are examples of general bindings, that allow this.

An example:

If you want to display the OS version of your system, you can find this information in the properties of the Systeminfo binding:


If you want to put this in an item to display it in a widget, you can access the REST API with the use of a sendHttpGetRequest (example here). Or you could use the HTTP binding, which supports transformations.

Install the HTTP binding

Create a thing for this binding

The base URL should be
http://yourip:8080/rest/things/systeminfo:computer:Systeminfo
This will get all the Systeminfo information in a long JSON string.

In “Advanced” the authentication should remain Basic authentication.

You can fill in your username and password or use a token. I created a token named “resttoken” (via the admin user bottom left, generate a new API token). The token can then be entered in the headers of the thing advanced definition as

Authorization = Bearer oh.resttoken.6eQ9rLk…

Then you go to the Channels tab and create a new channel.

Now we add a transformation to filter out only the OS Version information.
The State Transformation is

JSONPATH:$.properties.["OS Version"]

The square brackets and quotation marks are only needed if there is a space in the json name.

Add a link to a new string item.

If all goes well, the item will show a state like “11 (bullseye) build 5.15.32-v7+”

Documentation

In the Openhab documentation, you can find more information in:
https://www.openhab.org/docs/configuration/transformations.html
https://www.openhab.org/docs/configuration/items.html#state-transformation
https://www.openhab.org/docs/configuration/rules-dsl.html#transformations
https://www.openhab.org/docs/tutorial/things_advanced.html
https://www.openhab.org/docs/configuration/items.html#profiles

I am not an expert, so there may be some errors. The examples have been tested in OH3.3.

7 Likes

I think maybe re-word this part. You can use transformation profile with any type of Item (in OH3).
The transformation will need to return something suitable for that type, e.g. "1.5 kWh" for a Number:Energy Item.

This allows you to truly transform binding output, e.g. take a string type channel output like “Last Tuesday” and (if your transformation scripting is clever enough) transform into a standard date to update a DateTime type Item

Done. Thanks.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.