Hi all,
Since HABPanel went official, the added exposure has made more people try it and, as a result, there has been a number of requests for specialized widgets. While I don’t think I’m done with adding new widgets, I also believe the standard set should remain simple and focused to generic tasks, and match openHAB concepts, like items, as closely as possible.
Still, I understand the appeal of having specialized, more complex displays, which may make sense in a lot of situations. I also think it’s fair to say that the openHAB community is one of power users, who are able to code their own rules and sitemaps, and get their mind around more technical concepts.
That’s why I want to try something new.
I’m excited to share with you the newest HABPanel addition, available now in the repository (the builds will surely follow): the “Template” widget!
It allows you to code your own widgets with some simple HTML snippets, inline CSS and AngularJS expressions.
If you’re not familiar with them, I encourage you to read up on the base concepts, there’s a lot of documentation available and not that hard to grasp, and the results might just be worth it.
I’m also eager to see what the community will come up with, and hopefully share back their own templates. Who knows, there might be some sort of community-curated gallery of widgets at some point…
Below is a quick crash couse and showcase below, which will act as a tutorial before I get some proper documentation done.
Your first widget
Add a template widget and bring up its settings, you should be presented with a code editor (courtesy of CodeMirror).
Let’s begin with the basics:
<h4>Hello world!</h4>
It's {{itemValue('astro_sun_local_season_name')}}
and the sunrise is at {{itemValue('astro_sun_local_rise_start')}}
which gives you:
As you can see, the AngularJS template system allows you to bind values using double curly braces {{ }}
, and HABPanel defines a function, itemValue(item)
, in the template’s scope, returning the value of the item in the parameter. Best of all, the value will update automatically when it’s updated.
The formatting isn’t that great though, so let’s add a few Angular filters - lowercase and date:
<h4>Hello world!</h4>
It's {{itemValue('astro_sun_local_season_name') | lowercase}}
and the sunrise is at {{itemValue('astro_sun_local_rise_start') | date:'HH:mm'}}
That’s right: you have all the flexibility offered to you with by Angular filters, directives, expressions as well as the Bootstrap framework for presentation.
Going further
Let’s try a more complicated and polished example:
<div class="row">
<div class="col-xs-4"><span><widget-icon iconset="'smarthome-set'" icon="'temperature'" /></span>Temperature</div>
<div class="col-xs-4"><span><widget-icon iconset="'smarthome-set'" icon="'drop'" /></span>Humidity</div>
<div class="col-xs-4"><span><widget-icon iconset="'smarthome-set'" icon="'thermostat'" /></span>Pressure</div>
</div>
<div class="row">
<div class="col-xs-4"><span style="color: red; font-size: 28pt">{{itemValue('Outside_Temperature')}}°C</span></div>
<div class="col-xs-4"><span style="color: cyan; font-size: 28pt">{{itemValue('Outside_Humidity')}}%</span></div>
<div class="col-xs-4">
<span style="color: green; font-size: 28pt">
{{'%.02f' | sprintf:itemValue('Outside_Pressure')/10000}}<small style="font-size:8pt">MPa</small>
</span>
</div>
</div>
<table class="table">
<tr>
<td class="text-right">Sunrise:</td>
<td><strong>{{itemValue('astro_sun_local_rise_start') | date:'HH:mm'}}</strong></td>
</tr>
<tr>
<td class="text-right">Sunset:</td>
<td><strong>{{itemValue('astro_sun_local_set_start') | date:'HH:mm'}}</strong></td>
</tr>
<tr>
<td class="text-right">Season:</td>
<td><strong>{{itemValue('astro_sun_local_season_name')}}</strong></td>
</tr>
</table>
This example shows a few interesting things:
- it’s possible to do basic arithmetic and string manipulation in Angular expressions, like
itemValue(...)/10000
- the sprintf filter is available to format values;
- The
<widget-icon>
directive is the one used by other HABPanel widgets to display icons, and it’s available in your templates as well.
AngularJS is full of useful stuff that can be used in your templates - take a look especially at ng-if, ng-show, ng-repeat, ng-style…
For example, here is ng-repeat and ng-style in action:
<div class="row" ng-repeat="item in itemsInGroup('LivingRoom')">
<div class="col-xs-8 text-right">{{item.name}}</div>
<div class="col-xs-4 text-left" ng-style="{ color: itemValue(item.name)=='OFF'?'red':'green' }">
{{itemValue(item.name)}}
</div>
</div>
ng-repeat iterates over the array returned by itemsInGroup('LivingRoom')
(another function defined by HABPanel for you to use, itemsWithTag(tag)
is there as well), and defines a item
variable in the block underneath, which is an item as returned by openHAB’s /rest/items
API.
From there, you can use its name
, label
and call itemValue
and so on.
ng-style allows some conditional formatting on top.
Let’s add interactivity
ng-click and others are there as well, so there’s no reason why widgets should remain static!
Here’s an example:
<p>Value of Hue_Bureau: {{itemValue('Hue_Bureau')}}</p>
<div ng-if="itemValue('Hue_Bureau').split(',')[2]!='0'">
<button class="btn btn-lg" style="background: red; color: white"
ng-click="sendCmd('Hue_Bureau_Toggle', 'OFF')">
It's on! Switch off
</button>
</div>
<div ng-if="itemValue('Hue_Bureau').split(',')[2]=='0'">
<button class="btn btn-lg" style="background: green; color: black"
ng-click="sendCmd('Hue_Bureau_Toggle', 'ON')">
It's off! Switch on
</button>
</div>
Here you have 2 blocks defined, the visibility of which conditioned by an expression splitting up the comma-separated HSB value, taking the third component (brightness): itemValue('Hue_Bureau').split(',')[2]
, then comparing it to zero. The complementarity of the ng-if conditions will ensure only one of the 2 blocks is displayed:
Last but not least: a click on the buttons will call the third function made available to you by HABPanel, sendCmd(item,cmd)
which allows to send a command to an item.
Here’s how you would implement an ugly (!) roller shutter triple button:
<button style="width: 100%; height: 3em;
border: 0; color: white; background: midnightblue;
font-size: 30px" ng-click="sendCmd('Bedroom_Rollershutter', 'UP')">
<i class="glyphicon glyphicon-menu-up"></i>
</button>
<br />
<button style="width: 100%; height: 3em;
border: 0; color: white; background: red;
font-size: 30px" ng-click="sendCmd('Bedroom_Rollershutter', 'STOP')">
STOP
</button>
<br />
<button style="width: 100%; height: 3em;
border: 0; color: white; background: midnightblue;
font-size: 30px" ng-click="sendCmd('Bedroom_Rollershutter', 'DOWN')">
<i class="glyphicon glyphicon-menu-down"></i>
</button>
I’ll leave you as an exercice how you could change colors depending on the status of the item…
That’s it for now, it’s all very raw, I’m sure there will be more stuff coming up, but I wanted to release early to anticipate issues, and to be surprised at what you will do with it!
Enjoy!