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.