Design your SVG floorplan or dashboard for HABPanel with Inkscape

I know openHAB 3 will bring many changes to the UIs, specifically, that some or all of them will be merged into a new universal UI.

Does anyone in the know have any insight into how that will affect our ability to display SVGs in a HABPanel style as we have in the past? I’m really hoping that kiosk mode will remain available.

hello, always work with lights and UP&DOWN rollershutters
I would like to intert a kind of slider for rullershutters position (read-only)
Is there an object or I’m thinking about a “line” and trasform: scaleX or scaleY on % of the roller position (itemState) : can anybody help me?

PS: I have tryed scaleX on chrome but the line moves in a strange and non-standard way

hello, I would like to share my solution: this is my living room and my kitchen whit lamps and rollershutters.
The up and down button are green if the position is 0%(opened) and red if is 100% (closed) and normally black from 1% to 99%.

3 Likes

What app did you use to draw the floorplan? Looks very nice!

Sweethome 3d
Very easy

1 Like

Apparently, it’s possible to add interactive elements with SweetHome3D that work with openHAB2.

+1 for SweetHome 3D. I prefer the 2D floor plan but it’s very easy to export the SVG elements and edit them in Inkscape.

Thanks to @ysc and all, for this wonderful SVG dashboard design.
But is there a way one can navigate between the buttons on a dashboard using only the arrows keys on a keyboard? and also click by using the enter key? eg move focus using a keyboard.

1 Like

Thank you all very much for this tutorial - I have something working, with a rough outline of my house and my first lightbulb icon controlling a switch! When I tried to add more, I ran into a problem…

I moved the svg file on my server to floorplan.svg.old, and uploaded a new version to my server that I saved from inkscape. When I reloaded habpanel, I still have the previous version of the svg file. I tried restarting openhab2, but it still shows the old version of the floorplan. Any idea where my problem might be?

I have also had this problem.

I guess it’s a cache issue somewhere, but gave up after 5 minutes. Instead, I just save with a new name, and adjust the reference to the SVG file in HABPanel to point to the new name. This is a minor annoyance during development, but it’s a workaround…!

I’ve found the clearing the cache usually fixes this. Occasionally I need to restart the browser as well.

This problem is about the browser cache. The new file is not pulled again, instead the browser uses the svg already loaded.

Harry

Thank you all - copying the URL, closing the browser tab, and opening a new tab seems to have fixed the problem. Back to the matter of the day - getting light bulb icons to change color…

I did a little research and came up with this keyboard navigation that works on Habpanel using AngularJS. https://stackoverflow.com/questions/18086865/angularjs-move-focus-to-next-control-on-enter
https://stackoverflow.com/questions/14833326/how-to-set-focus-on-input-field?rq=1
You can navigate the grid of items using the arrow keys. You also have to make your items focusable.
Finally, you load or reload the page with the focus set on one item.
HTML below

<div oc-lazy-load="['/static/app1.js']">
<style>
g:focus {
  outline: none !important;
}
</style>
<body ng-app="App">

<svg width="1500px" height="500px"  xmlns="http://www.w3.org/2000/svg">
<g  next-focus=""  auto-focus="" id="recta0" >
<rect  x="10" y="20" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta0.focusin" end="recta0.focusout" to="red"/>
 <set attributeName="width" begin="recta0.focusin" end="recta0.focusout" to="200"/>
</rect>
<text x="42" y="58" fill="white"  font-weight="20" font-weight=normal >Raspberry Pi
<set attributeName="font-size" begin="recta0.focusin" end="recta0.focusout" to="25"/>
<set attributeName="font-weight" begin="recta0.focusin" end="recta0.focusout" to="bold"/>
</text>
</g>

<g  next-focus id="recta1" >
<rect  x="250" y="20" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta1.focusin" end="recta1.focusout" to="red"/>
 <set attributeName="width" begin="recta1.focusin" end="recta1.focusout" to="200"/>
</rect>
<text x="280" y="58" fill="white"  font-weight="20" font-weight=normal >Strawberry Pi
<set attributeName="font-size" begin="recta1.focusin" end="recta1.focusout" to="25"/>
<set attributeName="font-weight" begin="recta1.focusin" end="recta1.focusout" to="bold"/>
</text>
</g>


<g  next-focus="" id="recta2" >
<rect   x="490" y="20" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta2.focusin" end="recta2.focusout" to="red"/>
 <set attributeName="width" begin="recta2.focusin" end="recta2.focusout" to="200"/>
</rect>
<text x="532" y="58" fill="white"  font-weight="20" font-weight=normal >Blueberry Pi
<set attributeName="font-size" begin="recta2.focusin" end="recta2.focusout" to="25"/>
<set attributeName="font-weight" begin="recta2.focusin" end="recta2.focusout" to="bold"/>
</text>
</g>

<g  next-focus="" id="recta3" >
<rect   x="10" y="120" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta3.focusin" end="recta3.focusout" to="red"/>
 <set attributeName="width" begin="recta3.focusin" end="recta3.focusout" to="200"/>
</rect>
<text x="42" y="152" fill="white"  font-weight="20" font-weight=normal >Cranberry Pi
<set attributeName="font-size" begin="recta3.focusin" end="recta3.focusout" to="25"/>
<set attributeName="font-weight" begin="recta3.focusin" end="recta3.focusout" to="bold"/>
</text>
</g>

<g  next-focus="" id="recta4" >
<rect   x="250" y="120" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta4.focusin" end="recta4.focusout" to="red"/>
 <set attributeName="width" begin="recta4.focusin" end="recta4.focusout" to="200"/>
</rect>
<text x="280" y="152" fill="white"  font-weight="20" font-weight=normal >Blackberry Pi
<set attributeName="font-size" begin="recta4.focusin" end="recta4.focusout" to="25"/>
<set attributeName="font-weight" begin="recta4.focusin" end="recta4.focusout" to="bold"/>
</text>
</g>


<g  next-focus="" id="recta5" >
<rect   x="490" y="120" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta5.focusin" end="recta5.focusout" to="red"/>
 <set attributeName="width" begin="recta5.focusin" end="recta5.focusout" to="200"/>
</rect>
<text x="512" y="152" fill="white"  font-weight="20" font-weight=normal >Huckleberry Pi
<set attributeName="font-size" begin="recta5.focusin" end="recta5.focusout" to="25"/>
<set attributeName="font-weight" begin="recta5.focusin" end="recta5.focusout" to="bold"/>
</text>
</g>


<g  next-focus="" id="recta6" >
<rect   x="10" y="220" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta6.focusin" end="recta6.focusout" to="red"/>
 <set attributeName="width" begin="recta6.focusin" end="recta6.focusout" to="200"/>
</rect>
<text x="35" y="252" fill="white"  font-weight="20" font-weight=normal >Gooseberry Pi
<set attributeName="font-size" begin="recta6.focusin" end="recta6.focusout" to="25"/>
<set attributeName="font-weight" begin="recta6.focusin" end="recta6.focusout" to="bold"/>
</text>
</g>


<g  next-focus="" id="recta7" >
<rect   x="250" y="220" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta7.focusin" end="recta7.focusout" to="red"/>
 <set attributeName="width" begin="recta7.focusin" end="recta7.focusout" to="200"/>
</rect>
<text x="290" y="252" fill="white"  font-weight="20" font-weight=normal >Mulberry Pi
<set attributeName="font-size" begin="recta7.focusin" end="recta7.focusout" to="25"/>
<set attributeName="font-weight" begin="recta7.focusin" end="recta7.focusout" to="bold"/>
</text>
</g>


<g  next-focus="" id="recta8" >
<rect   x="490" y="220" fill="blue"  width="190" height="60"   >
 <set attributeName="fill" begin="recta8.focusin" end="recta8.focusout" to="red"/>
 <set attributeName="width" begin="recta8.focusin" end="recta8.focusout" to="200"/>
</rect>
<text x="522" y="252" fill="white"  font-weight="20" font-weight=normal >Pineberry pi
<set attributeName="font-size" begin="recta8.focusin" end="recta8.focusout" to="25"/>
<set attributeName="font-weight" begin="recta8.focusin" end="recta8.focusout" to="bold"/>
</text>
</g>

<g  next-focus="" id="recta9" >
<rect   x="10" y="320" fill="blue"  width="130" height="40"   >
 <set attributeName="fill" begin="recta9.focusin" end="recta9.focusout" to="red"/>
 <set attributeName="width" begin="recta9.focusin" end="recta9.focusout" to="150"/>
</rect>
<text x="30" y="345" fill="white"  font-weight="20" font-weight=normal >Next Page
<set attributeName="font-size" begin="recta9.focusin" end="recta9.focusout" to="25"/>
<set attributeName="font-weight" begin="recta9.focusin" end="recta9.focusout" to="bold"/>
</text>
</g>

</svg>

</body>
</div>

app1.js

var App = angular.module('App', []);
App.directive("nextFocus", nextFocus);
    function nextFocus() {
      var directive = {
        restrict: 'A',
        link: function($scope, elem, attrs) {       
          elem.bind('keydown', function(e) {
            var partsId = attrs.id.match(/recta(\d{1})/);
            var currentId = parseInt(partsId[1]);

            var code = e.keyCode || e.which;
            if (code === 39) {
              e.preventDefault();
              document.querySelector('#recta' + (currentId + 1)).focus();
            }
            
            if (code === 37) {
              e.preventDefault();
              document.querySelector('#recta' + (currentId - 1)).focus();
            }
            
            if (code === 40) {
              e.preventDefault();
              document.querySelector('#recta' + (currentId + 3)).focus();
            }
            
            if (code === 38) {
              e.preventDefault();
              document.querySelector('#recta' + (currentId - 3)).focus();
            }
            
          });
        }
      };
      return directive;

    }  
 App.directive('autoFocus', function($timeout) {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            $timeout(function(){
                _element[0].focus();
            }, 1);
        }
    };
});

I guess someone can improve on the code by minimizing it using style.

Great post, all working as expected.

One (lame) question: how to force openhabian (raspberry) to re-read floorplan/svg/css uploaded to HTML directory? I need to change filename each time and edit template widget settings to new filename. There must be some sort of cache in built in web server, but not sure where to force update.
Thanks in advance!

Try a “force reload” on the browser side. I have the same issue using Safari/macOS. A simple refresh doesn’t help to reload a new floorplan (embedded in HABpanel) uploaded to html. Instead, I have to click Safari ‣ Develop ‣ Clear Caches and reload afterwards. There will be a similar function for other browsers.

1 Like

Thanks. Forced reload worked without need to clear cache (Firefox)

I tried this with Number:Temperature, but it does not work:
My item:

Number:Temperature               CarTemp                "Temperatur am Auto [%.1f °C]"                 <temperature>    (G_Num,gOutdoorCar)                ["Measurement", "Temperature"]    {widgetOrder="03"}

But I get:

image

With this svg code:
ng-bind="itemState('CarTemp')"

I also tried
[%.1f %unit%]

And various formating stuff besides your suggestions above:

ng-bind={{itemValue('CarTemp') | number:1}}

ng-bind={{'%.1f' | sprintf:itemValue('CarTemp').split(' ')[0]}} {{itemValue('CarTemp').split(' ')[1]}}

Any suggestion would be greatly appreciated.

@ysc is this also working for openhab 3 or is there a better solution now?

HABPanel is still in OH3 so yes, it’s still working. The main UI has floorplan pages but they are not the same and arguably not as powerful.