Template widget - tutorial & examples - make your own widget!

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:

  1. it’s possible to do basic arithmetic and string manipulation in Angular expressions, like itemValue(...)/10000
  2. the sprintf filter is available to format values;
  3. 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!

58 Likes

Wow! This looks great!

I will try to have a play with this today and let you know how I get on.

Hi @ysc

EDIT: Is there a list of what glyphicons are available in HABpanel?

Also, this leads to another question, I have been working on some custom icons and need to know where I can add these and also what files I need to update for them to work. Do you have any plans for custom icon functionality because I really do not want to deviate from your standard application as it will make future updates more difficult??

Cheers,
Mike

Very cool widget, this is my first effort.

This widget controls a z-wave window actuator which has the same UP & DOWN commands as a roller shutter, hence the need to map the status values to OPEN and CLOSED.

Needs quite a bit of tidying up on my side, e.g. changing icon colours dynamically and improving the layout, but the functionality is excellent!!

Following is the code…

<h4>Window 01</h4>
<div class ="row";>
<button style="width: 2em; height: 2em;
border: 0; color: green; background: midnightblue; text-align: center;
padding: 0px; margin: 0px auto; 
font-size: 30px" ng-click="sendCmd('Window1_GF_mancave', 'UP')">
<i class="glyphicon glyphicon-chevron-right"></i>
</button>

<button style="width: 2em; height: 2em;
border: 0; color: red; background: midnightblue; text-align: center;
padding: 0px; margin: 0px auto; 
font-size: 30px" ng-click="sendCmd('Window1_GF_mancave', 'STOP')">
<i class="glyphicon glyphicon-remove"></i>
</button>

<button style="width: 2em; height: 2em;
border: 0; color: red; background: midnightblue; text-align: center;
padding: 0px; margin: 0px auto; 
font-size: 30px" ng-click="sendCmd('Window1_GF_mancave', 'DOWN')">
<i class="glyphicon glyphicon-chevron-left"></i>
</button>
</div>

<br />

<div ng-if="itemValue('Window1_GF_mancave')== '100'">
Status: CLOSED
</div>
<div ng-if="itemValue('Window1_GF_mancave')== 'DOWN'">
Status: CLOSED
</div>
<div ng-if="itemValue('Window1_GF_mancave')== '0'">
Status: OPEN
</div>
<div ng-if="itemValue('Window1_GF_mancave')== 'UP'">
Status: OPEN
</div>

Time for some AngularJS lessons :wink:

1 Like

Nice!
Glyphicons are those included in Bootstrap and listed here.
If you want to add icons, you could make a OSGi bundle like the classic UI set (the proper way) - or the quick & dirty way, putting your image files in conf/html/whatever and referencing them with <img src="/static/whatever"/>.

Maybe I’ll add a few builtin iconsets like I did with the three already there though, if I come across one I like.

1 Like

Thanks for the reply, I will probably go with the quick and dirty solution for now as it is only for a small number of custom icons.

@ysc

I am going through a steep learning curve at present as I work to perfect my first ‘template’ widget.
Is there an easy way define a string variable in the template code. At this stage I just thinking about the “item name” so that I don’t have to repeat it throughout the code, making it easier to create new widgets for the next similar item. In this case I have 12 window actuators to control, hence 12 individual widgets (before I address the need for “group widgets”, i.e. ‘4 rooms’ and ‘ALL’ ) .

I have tried various options but no success yet!, so some suggestions would be appreciated.

I have already made numerous code & layout improvements to my first effort above and will post the results a little later. Should I post it here or maybe start a “Template Widget Gallery thread”??

Cheers,
Mike

You’ll want to use ng-init for that, since you can’t define a controller.

In your case, that’ll be <div ng-init="item='Your_Item'"> in the top-level element (or wrap everything in a div). Then you can use itemValue(item) below.
Go ahead and make a new thread to discuss your widget, there’s plenty of room in the HABPanel category!

Thanks for the reply.

I was close, but I had ng-init in the wrong place and also a syntax error. Will get back to this tonight after work.

Whats with something like a Dropdown for Input Selection of a Smart TV? How could i do this?

Take a look at the bootstrap button options http://getbootstrap.com/components/

I haven’t tried yet but am planning to see what can be done over the week or so.

1 Like

Incidentally you also have Angular UI Bootstrap directives at your disposal because HABPanel includes it. You can’t code a proper Javascript controller but still can do a lot with them just with markup (your template).

For example :

<div style="height: 610px">
  <div uib-carousel active="0" interval="5000" no-wrap="false">
    <div uib-slide index="0">
      <img src="//unsplash.it/600/600?random&r=1" style="margin:auto;"/>
    </div>
    <div uib-slide index="1">
      <img src="//unsplash.it/600/600?random&r=2" style="margin:auto;"/>
    </div>
    <div uib-slide index="2">
      <img src="//unsplash.it/600/600?random&r=3" style="margin:auto;"/>
    </div>
    <div uib-slide index="3">
      <img src="//unsplash.it/600/600?random&r=4" style="margin:auto;"/>
    </div>
  </div>
</div>

Gives you a nice carousel (adapted from their example) with fancy arrows and auto cycle, I suppose that could be useful for security cameras and the like :slight_smile:

(note to self : I should include ng-animate and ng-touch for the transition and swiping support).

A simple:

Humidity:
<uib-progressbar class="progress-striped active"
max="100" value="itemValue('Outside_Humidity')" type="primary">
  {{itemValue('Outside_Humidity')}} %
</uib-progressbar>

Will make you a progressbar:

For a dropdown, you could use their dropdown directive as well (just tested successfully with one my own items), it’s could be styled better but it’s a good start:

<h4>Yamaha receiver input</h4>
<div class="btn-group" uib-dropdown>
  <button id="single-button" type="button" class="btn btn-primary" uib-dropdown-toggle>
    {{itemValue('Yamaha_Input')}} <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
    <li role="menuitem"><a ng-click="sendCmd('Yamaha_Input', 'HDMI1')">HDMI1 (Kodi)</a></li>
    <li role="menuitem"><a ng-click="sendCmd('Yamaha_Input', 'HDMI2')">HDMI2 (Xbox)</a></li>
    <li class="divider"></li>
    <li role="menuitem"><a ng-click="sendCmd('Yamaha_Input', 'Spotify')">Spotify</a></li>
    <li role="menuitem"><a ng-click="sendCmd('Yamaha_Input', 'NET RADIO')">Net Radio</a></li>
  </ul>
</div>

8 Likes

My original gripe with HABPanel was that I couldn’t get it to look as good as my Rotini setup. My gripe with Rotini was that it’s an app and therefore not platform agnostic.

I think this template widget may be on to a winner. I can define it using config, access on any browser and it still looks good! :grinning:

A question for you @ysc, is it on the roadmap that we can ‘convert’ some of the more popular template widgets that people come up with to standard widgets?

Now that is really cool!! There goes my spare time this weekend :slight_smile:

Of course, there has to be something to improve the reusability of those templates, both for technical (caching) and practical reasons. This implies having some sort of configurability i.e. setting some initial variables to define in the scope of each widget instance, like which item etc. Those templates could also be served from the filesystem instead of being stored along with the rest of the dashboard configuration.
It will all come in due time. Like I said, I released the template widget early while we’re still in the experimental phase, to see what the feedback of the community would be and would it would do with it.

In the long term, if the idea has some traction, there might even be a gallery of widgets to choose from, right from the settings dialog, a little bit like the “web store” for your Chrome or Firefox extensions.

Thank you so much for your work! This looks amazing!

Now it will be possible to do any dashboard you want!

2 Likes

I was wondering if it would be possible to create an analog clock widget using the template widget. I was looking at http://deepu.js.org/angular-clock/ which provides a nice Angular based analog clock. Since it is only based on Angular I would think it should be possible.
What is not clear to me is, where and how I would need to install the needed files so can use the markup to create such a clock inside a template.
Any insight on this is very welcome.
Take care
Ingo

I’m excited to try the template widget, but my openhab still has “ui-habpanel - 2.0.0.SNAPSHOT” installed.

Is there an easy way to download and install the binary for the latest HABPanel?

I installed it using PaperUI. First you need to enable experimental extensions:

Then under Extension -> User Interfaces you can install HABpanel

Have fun. Don’t forget to share cool widgets. :wink:

I was wondering if it would be possible to create an analog clock widget using the template widget. I was looking at http://deepu.js.org/angular-clock/17 which provides a nice Angular based analog clock. Since it is only based on Angular I would think it should be possible.
What is not clear to me is, where and how I would need to install the needed files so can use the markup to create such a clock inside a template.
Any insight on this is very welcome.
Take care
Ingo

@ysc is this possible at all?

I´m just starting with angularjs and would like to do a similar thing:

I want to take the following code into a template:
http://plnkr.co/edit/ukqmThpvi4aRGLZEiSSe?p=preview

Would be glad if you could help me / us…