Because it might inform the tutorial we are talking about on this thread, I can provide a bit of explanation.
When creating a widget (or a rule template) you have a choice. Do I make this specific or generic.
A specific implementation hard codes everything. All the Items, formatting, titles, labels etc are just written there inline with the configuration of the widget. If you have a widget that will only be used once and never shared that’s a perfectly acceptable approach. I do this with a couple of my own widgets.
However, if you want to make a widget more generic and usable more than once you need to code it more generically. This means you need a way for you and other users to supply information for the widget at the time you apply it. This information are the properties.
For example, we have that humidifier widget I posted above. I have two of these humidifiers so I chose to use a generic approach which means defining properties. In this case I want to supply the title for the widget so I know which humidifier it represents and the parent Equipment Item. I derive the names of all the rest of the Items based on the parent Equipment Item.
In the code that looks like this:
props:
parameters:
- default: Humidifier
description: Set the title for this Humidifier
label: Humidifier
name: title
required: false
type: TEXT
- context: item
description: Parent Equipment Group representing the humidifier
label: Equipment
name: equ
required: true
type: TEXT
And when I set the widget on an Item’s Default X Widget metadata or on a Page directly I’ll get a form with two options.
Notice how the Equipment property is an Item picker. What sort of field is rendered for each property is controlled by the context field of the property. You’ll see I set the context to “item” which tells MainUI I want an Item picker instead of just a plain text entry field.
I could further narrow things down by setting a filter so it only shows Items of the desired type (e.g. only Switch Items) but I find that’s often ignored and all Items are shown even with the fiter.
There are lots of contexts available which provide things like a time picker, map to select a location, booleans, enumerated values, etc. The code that handles properties is the same that handles properties for Things and binding configs and you can find that documented at Configuration Descriptions | openHAB.
I’ve also an example rule template in the marketplace (unpublished) that has examples for almost everything in that table to see how that table applies to the YAML used by widgets and rule templates. [Do not install] Rule Template Parameters Experiments and Examples.
You can control whether the property is required or not and if it’s not required provide a default value.
That covers how to define a property and why you might want to do that. But how do you us a property in the widget code?
It’s actually pretty simple. All the properties are stored in an Object called props under the value entered for the name field of the property. So, for example, to reference the user chosen value for property I defined above with the name “title” I simply use props.title. To reference the user chosen value for the property I defined above with the name “equ” I simply use props.equ.
You can see that in the following parts of the widget YAML.
slots:
default:
- component: oh-context
config:
constants:
it:
display: =props.equ+'_Display'
empty: =props.equ+'_WaterLowEmpty'
level: =props.equ+'_HumidityLevel'
mist: =props.equ+'_MistLevel'
mode: =props.equ+'_OperationMode'
power: =props.equ+'_SwitchedOn'
sp: =props.equ+'_HumiditySetPoint'
warm: =props.equ+'_WarmLevel'
warmOn: =props.equ+'_WarmModeEnabled'
Here I use the name of the chosen Equipment Item to derive the names of all the other Items I want to use in this widget. This oh-context part is in the docs and will be touched upon in the tutorial (section 2.2). But the key here is I need to start an expression (i.e. start with “=”) so MainUI knows to execute this code and not treat it as a string literal and then I access the equ property and add the last part of the Item name.
The title is a simpler example:
- component: Label
config:
style:
color: =fn.color(#const.it["level"])
font-size: 14px
font-weight: 700
margin-top: 0px
width: 200px
text: "=props.title + ((@@const.it['power'] == 'OFF') ? ' is off' : ' is set to
' + @@const.it['sp'])"
The text field is the part you want to look at. I start with a “=” again to indicate I want this to be executed. I start the text with the value entered by the user for the “title” property. Then I check the state of the power Item using the @@ shortcut to get the state of the Item with that name (it means the same as items[const.it['power']].state) and I set the rest of the title based on the state of that Item using a ternary operation. If it’s OFF the widget says " is off" and if it’s ON the widget says " is set to 45%" or something like that. For example, if I set the title to “Office Humidifier”, the power Item is ON, and the setpoint is 45%, this label will say “Office Humidifier is set to 45%” But if the power Item is OFF it will say “Office Humidifier is off”.
Sadly, what I don’t know is when and why I’d want to use a propertyGroup.
