This page is part of archived documentation for openHAB 4.3. Go to the current stable version
# Widget Expressions & Variables
When designing pages and widgets, you might want a config property of a widget (on pages) or of components (inside widgets) to be dynamically updated. - Expressions allow you to that.
Variables are a way to allow more complex scenarios in pages & personal widget development.
The widget expression system uses a JavaScript-like expression parser, jse-eval (opens new window). In order to remain light-weight and responsive, this is not a complete JavaScript library, but nearly all the basic functionality is provided along with some more advanced features.
# Expressions Overview
Expressions are string literals beginning with the symbol =
and everything after it is evaluated using a syntax very similar to JavaScript.
You can use arithmetic or string operations etc., the conditional (ternary) operator (opens new window), as well as the following objects (subject to evolutions):
items
is a dynamic key/value dictionary allowing you to retrieve the state of any Item: The result ofitems.Item1
will be an object like{ state: '23', displayState: '23 °C', numericState: 23, unit: '°C', type: 'Decimal' }
(displayState
,numericState
andunit
may be omitted). You can therefore useitems.Item1.state
to use the current state ofItem1
in your expression, and if the state changes, the expression will be reevaluated.props
is a dictionary of the key/values of self-defined props for the current personal widget, or page (pages, like any root UI components, may indeed have props). It is indispensable to use props in expressions when developing a personal widget to pass configuration from the page to the widget.config
is a dictionary of the key/values of the configuration of the current component/widget.vars
is a dictionary of variables that are available in the component's contextloop
is a dictionary containing iteration information when you're repeating components from a source collection. It is defined only when in the context of anoh-repeater
component.- The JavaScript
Math
object (so you can useMath.floor(...)
,Math.round(...)
and the like) (see mdn web docs: Math (opens new window)) - The JavaScript
Number
object (see mdn web docs: Number (opens new window)) - The JavaScript
JSON
object to parse or produce JSON (see mdn web docs: JSON (opens new window)) dayjs
to build instances of the day.js library (opens new window) that you can use to parse or manipulate date & timetheme
which holds the current theme:ios
,md
oraurora
themeOptions
anddevice
allow to use the relevant objects that you can see in the About page, Technical information, View details, underclientInfo
screen
returns theScreen
(opens new window) object. This allows you to access various information about the current screen, e.g. the available width and height. The two propertiesviewAreaWidth
andviewAreaHeight
are added on top. It's recommended to use CSScalc()
for dynamic positioning and styling.user
returns an object with information about the logged-in user: The name (user.name
) and an array of the assigned roles for the user (user.roles
).
# Variables
Variables can be used in several ways:
- The
variable
config parameter of anoh-gauge
(read-only),oh-input
,oh-knob
,oh-slider
,oh-stepper
,oh-toggle
will accept a variable name and control it instead of sending commands to an Item if set. Theitem
parameter can still be set to set the widget to the Item state if the variable has no value. - The
vars
object available in expressions (as mentioned above). For example=vars.var1
will evaluate to the value of the variablevar1
. - The
variable
action allows to set a fixed or computed (using an expression) value to a variable.
oh-button
& oh-link
have a special parameter clearVariable
, which allows to unset a variable when clicked, after performing the action.
This is useful when "validating" a variable, e.g. send the variable value as command to an Item and then reset the variable.
# Default Variable Values
The standard variables defined using interactive components do not exist until the first time they are given a value by a component. This means that it is not possible to set a default value for a variable. To provide a default value when a variable does not exist a simple OR construction can be used in expressions that require the variable:
text: =(vars.selectedNumber || '0')
Expressions such as the example above will return the value of the variable if it exists and if not will return the second value instead.
If a variable is used extensively in a widget, instead of using OR statements in many different expressions, an oh-context
component can be used to define variables that have default value at widget creation.
- component: oh-context
config:
variables:
setBrightness: 75
slots:
default:
- component: oh-slider
config:
variable: setBrightness
- component: Label
config:
text: =vars.setBrightness
The oh-context
can also create more complex default variable values such as arrays or objects:
- component: oh-context
config:
variables:
lightArray:
- ON
- OFF
- OFF
- OFF
userObject:
user: Guest
color: red
timeout: 5
# Variable Scope
Standard variables are also global in scope with one exception:
Changes to the variable's value made by the child components do not affect the parent component that defines the variable.
The scope of variables created using an oh-context
component is restricted to only the children of that component.
The value of an oh-context
variable will never pass to the parent of the oh-context
.
Within the scope of an oh-context
, however, variables are fully accessible even passing values from a custom widget to parent components.
Variables defined in an oh-context
will take precedence over standard variables of the same name.
# Item Expression Shortcuts
The @
symbol can be used in front of an Item name string as a shortcut to the displayState
from the items
dictionary with a fallback to the raw state:
footer: =@'Switch1'
is the same as
footer: =items['Switch1'].displayState || items['Switch1'].state
Similarly, @@
can be used as a shortcut for just the Item state.
The hashtag #
symbol is a shortcut to the numericState
with no fallback:
footer: =#'Temperature1'
is the same as
footer: =items['Temperature1'].numericState
These shortcuts have two major benefits over directly accessing displayState
, state
and numericState
:
Expressions become shorter when they are used, and if the Item name is a prop which is undefined, these shortcuts avoid that a request with Item name undefined
is sent to the server, which would cause this log message:
[WARN ] [se.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: undefined
Expressions are particularly useful in cases where one wants to combine the states of more than one Item, or use the state of more than one Item in a single widget element. For example, the icon of an Item can be based on the state of a different Item.
# Advanced Expression Features
# Arrow functions
Many standard JavaScript methods take a function as a parameter. The expression parser can parse arrow functions as the parameters of these methods.
Example:
title: =someItemList.find((x) => x.name === 'KitchenSwitch').label
In this example, an arrow function is used in conjunction with the .find()
method to get the Item object from an array of Items (such as is returned by a oh-repeater
) with a particular Item name.
The label of the found Item is then used as the title of a component.
# String templates
String templates are a much more human-readable way of creating strings with incorporated dynamic values.
String templates are surrounded by backticks (`string template`
) instead of single- or double-quotes.
Inside string templates, variable values can be inserted with the ${variable}
syntax.
Example:
text: =`This button opens the ${props.page} page`
This example includes the value of the widget property props.page
in the text of a component.
# Regular expressions
Regular expressions (regex) allow for complex search or replace string operations.
Many of the JavaScript string methods accept regex parameters expressed as the regex string between two forward slashes (/regex here/
).
Example:
label: =props.item.match(/_(.*)_/)[1]
In this example, a widget property named item
and containing an Item name is searched using regex and the first capture (in this case all characters between two underscores) is returned and used as the component label.
To learn more about regular expressions (regex), refer to mdn web docs: Regular expressions (opens new window). When creating a regex, consider using tools like regex101 (opens new window) to test your regex.
# Objects
The variable action allows components in widgets to pass information back and forth when there is user interaction. Often this information is simple, such as a single string or input value. Sometimes, however, it is helpful to add more information to a variable and for these instances JavaScript objects are useful.
The widget expression system can create objects in two different ways:
JavaScript object syntax:
Objects can be defined within the expression system using the standard JavaScript syntax:
{'key1':'value1','key2':'value2'}
. If a key doesn't contain special characters such as spaces, the quotes around keys can usually be omitted:{key1:'value1',key2:'value2'}
.TIP
Due to the special meaning of
:[space]
in yaml, it is best to have no spaces between the:
and the value. If you have:[space]
anywhere in your expression it will raise a YAML error unless you enclose the entire expression (= included) in another layer of quotes.Example:
actionVariable: myObject actionVariableValue: ={'name':props.item,'selected':true}
In this example, a variable is set to an object with keys
name
andselected
using JavaScript object expression syntax.YAML object syntax:
The other way to create objects is to take advantage of the relationship between YAML and JSON and place the key:value pairs as YAML keys under the initial key.
Here is a variable definition with the same results as the one above using the YAML syntax.
actionVariable: myObject actionVariableValue: name: =props.item selected: =true
In both cases, the variable can now be referenced by other components as vars.myObject
with keys vars.myObject.name
and vars.myObject.selected
.
# Using object expressions like a switch
control statement
Object expression can also be used to simulate a switch
control statement.
The most common flow control statement in expressions is the conditional (ternary) operator (opens new window), which is very efficient for selecting from two options based on a single boolean criterion.
If you have a list of possible options, you can combine multiple ternary operators together, but this grows cumbersome very quickly.
For example, if there is an HVAC with a mode Item that can be set to heat
, cool
, auto
, and off
modes, it requires 4 nested ternary operators to set a component's background color to match the current HVAC mode (with a fallback option if the Item has some other state, e.g. null
).
background: =(@@hvacModeItem == 'heat')?'orange':(@@hvacModeItem == 'cool')?'blue':(@@hvacModeItem == 'auto')?'green':(@@hvacModeItem == 'off')?'white':'red'
To use an object instead, simply create an object with keys for each of the Item's expected states, and give each key the desired output value.
Referencing that object using the Item's state will return the desired value.
Adding a simple OR
statement to that expression will provide the fallback condition if the object reference is undefined.
background: =({heat:'orange',cool:'blue',auto:'green',off:'white'})[@@hvacModeItem] || 'red'
# Constants
Named constants can be defined using an oh-context
component in a similar way to defining variables.
The significant difference is that if a constant's value is defined using an expression, that expression is evaluated only at the time the widget is rendered and will not change.
The example above using an object as a switch statement can be written even more clearly with the addition of a constant object:
- component: oh-context
config:
constants:
modeColor:
heat: orange
cool: blue
auto: green
off: white
slots:
default:
- component: Label
config:
text: =@@hvacModeItem
style:
background: =const.modeColor[@@hvacModeItem] || 'red'
# Custom Functions
There are times when a widget requires the same calculation in multiple locations and maintaining all the different locations can be a burden.
In these instances, an oh-context
component can be used to define named functions that are available to all children of the oh-context
.
Functions are defined using the arrow syntax and referenced with the fn
object in expressions:
- component: oh-context
config:
functions:
num2usd: =(x) => '$' + Number.parseFloat(x).toFixed(2)
slots:
default:
- component: Label
config:
text: =fn.num2usd(3.1)
# Examples
Translates the third part of the HSB state (brightness) of an Color
Item to "On" or "Off":
=(@@'Color1'.split(',')[2] !== '0') ? 'On ' + '(' + @@'Color1'.split(',')[2] + '%)' : 'Off'
Use a filled lightbulb
icon but only if the state of the Item passed in the prop item
is ON:
icon: =(@@props.item === 'ON') ? 'f7:lightbulb_fill' : 'f7:lightbulb'
Stacked ternary statements to translate the state of Item xxx
to a description:
=(items.xxx.state === '0') ? 'Off' : (items.xxx.state === '1') ? 'Heat' : (items.xxx.state === '11') ? 'Economy Heat' : (items.xxx.state === '15') ? 'Full Power': (items.xxx.state === '31') ? 'Manual' : 'Not Set'
Do the same using an object and the Item state shortcut:
={0:'Off',1:'Heat',11:'Economy Heat',15:'Full Power',31:'Manual'}[@@xxx] || 'Not Set'
Subtract one week from the state of DateTime
and return a relative time representation in the current locale ("3 weeks ago"):
=dayjs(items.DateItem.state).subtract(1, 'week').fromNow()
# Debugging Expressions
Expressions can be tested in the Widgets Expression Tester found in the Developer Sidebar (ShiftAltD).