I have got lots of door and window sensors and radiator controllers. They are all are configured in a similar way, having several items. So I was looking for a way to avoid typing the configurations again and again. After some investigation I found a nice Java tool named STST http://hardlikesoftware.com/weblog/stst/ . This combines templates and data to generate any kind of text files. It reads data from a JSON format, which is very concise and handy. STST is based on the very powerful template engine StringTemplate https://www.stringtemplate.org/ .
Here is an excerpt of my generated file to configure my window items:
Group gWindows "Alle Fenster"
// Window_Kitchen "Fenster KĆ¼che"
Contact Window_Kitchen "Fenster KĆ¼che" <window> (gWindows, gContacts) {channel="zwave:device:77ecbab9:node19:sensor_door"}
Number Window_Kitchen_battery "Batterie Fenster KĆ¼che [%d]" <battery> (gBatteries) {channel="zwave:device:77ecbab9:node19:battery-level"}
Number:Temperature Window_Kitchen_sensorTemperature "Temperatur Fenster KĆ¼che [%d]" <temperature> (gDeviceStatuses) {expire="13h", channel="zwave:device:77ecbab9:node19:sensor_temperature"}
Switch Window_Kitchen_alarmPower "Batteriewarnung Fenster KĆ¼che" <lowbattery> (gPowerAlarms) {channel="zwave:device:77ecbab9:node19:alarm_power"}
// Window_Office "Fenster BĆ¼ro"
Contact Window_Office "Fenster BĆ¼ro" <window> (gWindows, gContacts) {channel="zwave:device:77ecbab9:node42:sensor_door"}
Number Window_Office_battery "Batterie Fenster BĆ¼ro [%d]" <battery> (gBatteries) {channel="zwave:device:77ecbab9:node42:battery-level"}
Number:Temperature Window_Office_sensorTemperature "Temperatur Fenster BĆ¼ro [%d]" <temperature> (gDeviceStatuses) {expire="13h", channel="zwave:device:77ecbab9:node42:sensor_temperature"}
Switch Window_Office_alarmPower "Batteriewarnung Fenster BĆ¼ro" <lowbattery> (gPowerAlarms) {channel="zwave:device:77ecbab9:node42:alarm_power"}
ā¦
You see the definition of one Group on top of the file and two nearly equal sensors. Item IDs are in English, but labels in German.
This is the template I used to generate it:
Group gWindows "Alle Fenster"
$windows : {
// Window_$it.id$ "Fenster $it.label$"
Contact Window_$it.id$ "Fenster $it.label$" <window> (gWindows, gContacts) {channel="$device$:$it.node$:sensor_door"}
Number Window_$it.id$_battery "Batterie Fenster $it.label$ [%d]" <battery> (gBatteries) {channel="$device$:$it.node$:battery-level"}
Number:Temperature Window_$it.id$_sensorTemperature "Temperatur Fenster $it.label$ [%d]" <temperature> (gDeviceStatuses) {expire="13h", channel="$device$:$it.node$:sensor_temperature"}
Switch Window_$it.id$_alarmPower "Batteriewarnung Fenster $it.label$" <lowbattery> (gPowerAlarms) {channel="$device$:$it.node$:alarm_power"}
}$
The data file looks like this. It only holds variable parts.
{
"device" : "zwave:device:77ecbab9",
"windows": [
{
"id": "Kitchen",
"label": "KĆ¼che",
"node": "node19"
},
{
"id": "Office",
"label": "BĆ¼ro",
"node": "node42"
}
]
}
Letās have a deeper look at the template:
You see variables and logical statements are surrounded by $ signs.
The first one is $windows{ ā¦ }$ . This defines an iteration over all elements of the array named āwindowsā in the JSON file.
Next is $it.id$. $it is the current item of the iteration. Separated by a dot follows the name of the JSON sub-element holding the data. In this case it is āidā.
Congruously the next statement $it.label$ is the ālabelā element of the iteration object.
$device$ does not reference an iteration object. So it gets replaced by the value of the ādeviceā object outside of the window array, at the beginning of the file.
Now it should be clear what $it.node$ means
As mentioned before the template language is very powerful. A use case Iāve got is to add some Items to additional groups, but some windows must not be added to this group. So I added an additional field named groups to the data structure. Not all window definitions contains it. The data for one window like this looks like this:
{
"id": "LivingroomRight",
"label": "Wohnzimmer rechts",
"node": "node24",
"groups": "gWindows_Livingroom"
},
I modified the first item definition in the template like this:
Contact Window_$it.id$ "Fenster $it.label$" <window> (gWindows, gContacts$if(it.groups)$, $it.groups$$endif$) {channel="$device$:$it.node$:sensor_door"}
The start of the optional part is marked as $if(it.groups)$ which means that the following part is only included when the data contains a value for the āgroupsā attribute.
The end of the optional part is marked with $endif$. Everything in between is used as a template with all itās template logic.
More about the template language can be found at https://www.stringtemplate.org/
Of course the template mechanism can not only be used to generate items. Sitemaps can be generated the same way or persistence configurations. Potentially you can combine the same JSON data file with different templates to generate separate types of target files. Attributes that are only required in one template, do hurt if they are not used in another one. They are simply ignored. So you can have a mix of data for both template in just one data file.
Template files have the file extension .st . Data files in JSON format usually have the extension .js.
In my OH conf directoryI created a subfolder named generator in parallel to the items and sitemap folder. Here I store all my templates and data files and a batch file that generates all target files and stores the directly to the correct OH conf directory. STST is a command line tool. stst -h lists all supported command line options.
For the example above the command line looks like this:
stst -o ../items/windows_generated.items windows_items.st windows.js
Using templates helps to meet your own naming conventions without typos and similar items are named in a similar way. This helps keeping overview and ensures generic coding (see Design Pattern: Associated Items)
While your OH system grows, the configurations need to be adapted from time to time. With the templates it is easy to change all scripted items in one go. Just modify the template and start the generator. So it totally makes sense to reimplement similar configurations using templates, even if you already have an working configuration written manually. Itās so easy!