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

@Patrick_Beauchamp check out the “Dropdown of radio stations” example here, it’s similar:

There’s a trick to display a divider after the first item, you can use it or not (simply remove the first <li> and the ng-style in the second).

@ysc Thanks for the link, i wanted to learn more about how to do the widgets!

now i have a lot of reading to do!

1 Like

Is there a way not only to create a widget but a complete website?
https://community.openhab.org/t/habpanel-development-advanced-features-start-here/30755
This threat contains good examples what CAN be used, but not HOW it can be used.
Can you just drop some html, js, css … files somewhere and create your own dashboard using the same syntax mentioned in this threat? Where and how?

Did you manage to get a video stream using your login and password?

someone know the way to code a http slider to actually send command to openhab? I’ve got a good looking slider but the light doesnt respond.

i try to add ng-click=“sendCmd(‘dimmerGrandSalon’, slider.value)” to the input attribute but it doesn’t do anything.

Also, is there a way to see the code of the standard widget?


here’s what i got so far :

<!DOCTYPE html>
<html>
<head>
<meta name="homeControl" content="width=device-width, initial-scale=1">
<style>
.slidecontainer {
    width: 75%;
    margin: 50px;
}

.slider {
    -webkit-appearance: none;
    width: 100%;
    height: 10px;
    border-radius: 5px;
    background: #d3d3d3;
    outline: none;
    opacity: 0.7;
    -webkit-transition: .2s;
    transition: opacity .2s;
}

.slider:hover {
    opacity: 1;
}

.slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 48px;
    height: 48px;
    border: 0;
    background: url('/icon/bulb.png');
    cursor: pointer;
}

.slider::-moz-range-thumb {
    width: 48px;
    height: 48px;
    border: 0px;
    background: url('/icon/bulb.png');
    cursor: pointer;
}
  
button {
    width: 50px;
    height: 50px;
    border: 0;
    background: transparent;
    color: white;
    font-size: 1em;
    background-image: url("/icon/bulb.png");
}

table, td, th {
    border: 0px;
}

  th {
      border: 0px;
      padding: 20px;
  
  }
  
</style>
</head>
<body>

<table align="center">
  
  <tr width="75%">
    <th width="33%">
      <p> All ON </p>
      <button ng-click="sendCmd('allOn', 'ON')"></button>
    </th>
  
    <th width="33%">
      <p> All OFF </p>
      <button ng-click="sendCmd('allOff', 'ON')"></button>
    </th>
  
    <th width="33%">
      <p> Petit Salon </p>
      <button ng-click="sendCmd('prisePetitSalon', 'ON')"></button>
    </th>
    
  </tr>
</table>
  
  <div class="slidecontainer">
    <input type="range" min="1" max="100" value="0" class="slider" id="grandSalon">
  </div>

  <div class="slidecontainer">
    <input type="range" min="1" max="100" value="0" class="slider" id="tableCuisine">
  </div>
  
  <div class="slidecontainer">
    <input type="range" min="1" max="100" value="0" class="slider" id="comptoirCuisine">
  </div>


</body>
</html>

The visual:

Hello,
I am trying to use the great set of animated icons skycons found there: https://github.com/darkskyapp/skycons

here is my template widget code:

<script src="/static/skycons.js"></script>
<canvas id="icon1" width="64" height="64"></canvas>

<script>
  var skycons = new Skycons({"color: orange});
  skycons.add("icon1", Skycons.RAIN);
  skycons.play();
</script>

I have added the skycons.js to the html folder

The widget shows nothing

Where am I going wrong?

Thanks

Vincent

With reference to the here you have to do the following:

  • Install the Astro Binding (as you did)
  • create a file called astro.things and put it into the things folder
  • add the following to that file
astro:sun:home [ geolocation="50.102471, 8.403986", interval=300]
astro:moon:home [ geolocation="50.102471, 8.403986", interval=300]
  • Now go to your items file (or create an additional one e.g. “astro.items”) and put the following into it
String   astro_sun_local_season_name                {channel="astro:sun:home:season#name"}
DateTime astro_sun_local_rise_start "[%1$tH:%1$tM]" { channel="astro:sun:home:rise#start" }

Voila, now it will work (at least it did in my case as I had the same problem)

/Stefan

Follow-up on my last post:

I finally found a way to do it:

<!DOCTYPE html>
<html>
<head>
<meta name="homeControl" content="width=device-width, initial-scale=1">
<style>



#dimmer-slider .rz-bubble {
  color: white;
}
#dimmer-slider .rz-pointer {
  background: url('/icon/bulb.png');
  width: 48px;
  height: 48px;
}
#dimmer-slider .rzslider .rz-bar.rz-selection {
  background-color: #ffaa00;
}

  
button {
    width: 48px;
    height: 48px;
    border: 0;
    background: transparent;
    color: white;
    font-size: 1em;
}

table, td, th {
    border: 0px;
}

  th {
      border: 0px;
      padding: 20px;
  
  }
  
</style>
</head>
<body>

<table align="center">
  
  <tr width="75%">
    <th width="33%">
      
      <p>Prise Petit Salon</p>
      <div ng-if="itemValue('prisePetitSalon')=='ON'">
        <button class="btn btn-lg" style="background-image: url('/icon/bulb.png'); color: white" ng-click="sendCmd('prisePetitSalon', 'OFF')"></button>
      </div>

      <div ng-if="itemValue('prisePetitSalon')=='OFF'">
        <button class="btn btn-lg" style="background-image: url('/icon/eye.png'); color: white" ng-click="sendCmd('prisePetitSalon', 'ON')"></button>
      </div>
      
    </th>
  
    <th width="33%">
      <p> All OFF </p>
      <button style="background-image: url('/icon/bulb.png'); color=white" ng-click="sendCmd('allOff', 'ON')"></button>
    </th>
  
    <th width="33%">
      <p> Petit Salon </p>
      <button style="background-image: url('/icon/bulb.png'); color=white" ng-click="sendCmd('prisePetitSalon', 'ON')"></button>
    </th>
    
  </tr>
</table>
  
<div class="slider-div" ng-init='dimmerGSModel={"name": "dimmerGS", "item": "dimmerGrandSalon", "floor" : 0, "ceil": 100, "step": 1,"hidelabel" : "true", "hidelimits": "true" }'>
        <widget-slider ng-model="dimmerGSModel" id="dimmer-slider"/>
      </div>
  
<div class="slider-div" ng-init='dimmerCModel={"name": "dimmerC", "item": "dimmerComptoir", "floor" : 0, "ceil": 100, "step": 1,"hidelabel" : "true", "hidelimits": "true" }'>
  <widget-slider ng-model="dimmerCModel" id="dimmer-slider"/>
</div>
  
<div class="slider-div" ng-init='dimmerTCModel={"name": "dimmerTC", "item": "dimmerTableCuisine", "floor" : 0, "ceil": 100, "step": 1,"hidelabel" : "true", "hidelimits": "true" }'>
  <widget-slider ng-model="dimmerTCModel" id="dimmer-slider"/>
</div>


</body>
</html>

temp

That white dot and the fact that the icon is centered are annoying me but overall it’s great!

Hello and thanks for this great tutorial :slight_smile:
Actually I have some problem with this code:

<p>Value of Heizung: {{itemValue('Power_Plug_Socket_B')}}</p>

<div ng-if="itemValue('Power_Plug_Socket_B').split(',')[2]!='0'">
  <button class="btn btn-lg" style="background: red; color: white"
  ng-click="sendCmd('Power_Plug_Socket_B', 'OFF')">
  It's on! Switch off
  </button>
</div>
<div ng-if="itemValue('Power_Plug_Socket_B').split(',')[2]=='0'">
  <button class="btn btn-lg" style="background: green; color: black"
  ng-click="sendCmd('Power_Plug_Socket_B', 'ON')">
  It's off! Switch on
  </button>
</div>

The display of the value works fine but the switch doesnt work. It only turn off without switch the color of the button.

Are you sure the value is a string and not a number? Also, your duplicating a lot of stuff. Just use ng-style and inline-if for the text.

Well Im not good with all this stuff. I just get my things working with tutorials :smiley:
So I just copied the code above to use for my own :wink:
Well, I dont know what you mean exactly :sweat_smile:

The switch is just a small device on the GPIO-Ports…maybe this helps to solve the problem.

I want to compare 2 numbers (Temperatures) in an mg-if segment, but i think it compares these items as string, because if the temperature_item is 10 and the set_temperature_item is 17 it works, but if the temperature_item is 5 and the set_temperature_item is 17 it will not work. How must i used the item-fields in the ng-if-segment?
the 2 items are defined in the widget settings as items.

Here my code:

<div class="knob1" ng-if="itemValue(config.temperature_item')<= itemValue(config.set_temperature_item)" style="margin-top: -255px;"><widget-knob ng-model="temp2" /></div>
<div class="knob1" ng-if="itemValue('config.temperature_item') >itemValue(config.set_temperature_item)" style="margin-top: -255px;"><widget-knob ng-model="temp3" /></div>
			

My goal ist to color the the bar in the knob blue if the temperature is > set_temperature and red if the temperature < smaller than the set_temperature.

x == 3 is NOT the same as x == ‘3’ so make sure you make the right condition statement or just do x.toString() === ‘3’

I did create my own template widget now for danalock. it works pretty well… but what I have ask me is, if there is any layout and positioning tutorial? I dont find any documentation about predefined classes like row or col and how they works.

also what I dont understand is, why the habpanel is not screen resolution responsive? I feel so bad when I design on my 1080p pc monitor the widget for my 720p tablet :stuck_out_tongue: that let me looks back to html4 and css1 in the 2000 years :confused:

HABPanel uses bootstrap classes. I suggest you read on that, and everything else is straightforward css (assuming you know css, this should be fairly easy). Also, HABPanel uses gridster framework so everything is pretty much absolutely positioned and statically sized (fixed px). You can create a widget that is also absolutely positioned and based all children relative to that absolutely positioned parent.

Nothing unusual here that requires a separate css tutorial. Advanced settings for individual dashboards allow you to set your breakpoints. So you can override existing styles based in the queries.

Hello!
I made a simple button but i’m struggling with something…
this is my code:

<div class="box-content switch" ng-if="itemValue('Test_Button')=='OFF'">
  <button class="btn btn-lg" style="width: 100%; background: transparent; height: 100%;" ng-click="sendCmd('Test_Button', 'ON')">
  <img src="/icon/fts_yard_gate_2w?format=svg" width="90%" height="90%"/>
  </button>

</div>

<div class="box-content switch" style="background-color: rgb(204, 51, 0)" ng-if="itemValue('Test_Button')=='ON'">
  <button class="btn btn-lg" style="width: 100%; background: transparent; height: 100%;" ng-click="sendCmd('Test_Button', 'OFF')">
    <img src="/icon/fts_yard_gate_2w_open?format=svg" width="90%" height="90%"/>
  </button>
</div>

the button works but i’m not happy with the “design”… when i press the button while “onclick” the border of the button is visible for a short time - then it goes transparent again. can someone please tell me how to change this behaviour? i’ve looked so many times now but i can’t find the solution…

cheers

look up css transitions

Hey all,

just a little tipp regarding widget or even total dashboard design:
Presentation Tools like Powerpoint or Keynote really do a fine job to play around with colors, fonts and styles. Here is an example I did to create my personal Weather-Widget and try out different designs/transparencies/shadows:

Did you find a resolution this issue?
I also have a JSON string stored in an item which I loop over in habpanel.

My issues are:
Data doesn’t load on a hard refresh of the page in Chrome. Reading in item states in an ng-init results in “NaN”. Once I change the page and return, it works just fine.
I have tried to resolve it with this with no luck:

<div ng-if="getItem('WeatherHourly_json').toString() != 'NaN'" ng-init="hours = $eval(itemState('WeatherHourly_json'))">

I get thousands or errors in the console (i have read that this is related to Chrome loading the DOM before the data is loaded). In the following case, Chrome is complaining that the has CW as the length instead of a number. But this fixes itself.

Error: <svg> attribute width: Expected length, "{{CW * config.Nu…".
html @ vendor.js:196

I also get errors related to String Vs. Numbers which I can’t identify:

TypeError: [sprintf] expecting number but found string
    at b.format (vendor.js:365)
    at b (vendor.js:365)
    at vendor.js:524
    at vendor.js:255
    at c (vendor.js:130)
    at vendor.js:128
    at m.$digest (vendor.js:144)
    at m.$apply (vendor.js:147)
    at l (vendor.js:99)
    at K (vendor.js:103)

Does anybody know what “$exec(…)” does? Can’t find any documentation on it. I have only found a few examples where people do that. Removing it doesn’t work.

<div ng-init="hours = $eval(itemState('WeatherHourly_json'))">
...
<td class="hours" width={{CW}} ng-repeat="h in hours track by $index"><div>{{h.FCTTIME.weekday_name_abbrev}}<br />{{h.FCTTIME.civil}}</div></td>

Finally (sorry for all the questions), my widget doesn’t refresh over time when WeatherHourly_json updates in OH.

For those who want to try my Widget, it downloads the hourly weather data from WUnderground as JSON string, stores it in an Item. Then a rule parses the JSON and calculates the MAX/MIN values for the temperature. The Habpanel widget displays a table with the data including a graph for the temperature.
Requires the HTTP Binding and the JSONPath Transformation.

Widget:
http://faure.ca/~paul/WeatherHourly.widget.json

Items:

String  WeatherHourly_json  "JSON [%s]"       {http="<[weatherHourly:3000000:JSONPATH($.hourly_forecast)]"}
Number  WeatherHourly_max   "MAX [%.2f]"
Number  WeatherHourly_min   "MIN [%.2f]"
Number  WeatherHourly_avg   "AVG [%.2f]"

Rules:

rule "WUndergound hourly change"
when
        Time cron "0 */15 * * * ?" or
        Item WeatherHourly_json changed
then
        var String WeatherHourly_temp = "NULL"
        var String WeatherHourly_ftemp = "NULL"
        logInfo("HourlyWeather", "Data changed, recalculate min/max")
        if(WeatherHourly_json.state.toString == "NULL"){
          logInfo("HourlyWeather", "Data1 is NULL")
          //WeatherHourly_temp.sendCommand("NULL")
          //WeatherHourly_ftemp.sendCommand("NULL")
        }else{
          WeatherHourly_temp = transform("JSONPATH", "$.[0:13].temp.metric", WeatherHourly_json.state.toString)
          WeatherHourly_ftemp = transform("JSONPATH", "$.[0:13].feelslike.metric", WeatherHourly_json.state.toString)
          if(WeatherHourly_temp == "NULL" || WeatherHourly_ftemp == "NULL"){
            logInfo("HourlyWeather", "Data2 is NULL")
            WeatherHourly_max.sendCommand(0)
            WeatherHourly_min.sendCommand(0)
            WeatherHourly_avg.sendCommand(0)
          }else{
            var temp='{temp:[' + WeatherHourly_temp.replace('"', '').replace('[','').replace(']','') + ',' + WeatherHourly_ftemp.replace('"', '').replace('[','').replace(']','') + ']}'
            logInfo("HourlyWeather", "Data: " + temp)
            var max=transform("JSONPATH", "temp.max()", temp)
            var min=transform("JSONPATH", "temp.min()", temp)
            var avg=transform("JSONPATH", "temp.avg()", temp)
            WeatherHourly_max.sendCommand(max)
            WeatherHourly_min.sendCommand(min)
            WeatherHourly_avg.sendCommand(avg)
            logInfo("HourlyWeather", "MIN="+min+":MAX="+max)
          }
        }
end

HTTP Binding:

weatherHourly.url=http://api.wunderground.com/api/[APIKEY]/hourly/q/45.4347982,-75.5739861.json
weatherHourly.updateInterval=3000000