Nice graphic alarm (Pir sensors) visualization in HABPanel

Great stuff @FrankCZ,

I felt like playing with your example yesterday to implement the direction detection you wanted.
I’ve done it with tags - for each sensor you’ll have to put one or more tags in the format: neighbor:direction to indicate their neighbors and the direction they (the neighbors) would be facing if they were still “hot” and the sensor was triggered.

I also added new associated items for the direction (and a group).

Example:

Group gPirs
Group gPirsOutside
Group gPirsInside
Group gPirTimers
Group gPirDirections

Number Pir01_raw "PIR1 [%s]" <dmotion> (gPirs, gPirsOutside) {mqtt=...}
Number Pir02_raw "PIR2 [%s]" <dmotion> (gPirs, gPirsOutside) {mqtt=...}

String Pir01 "PIR1 [%s]" <dmotion> (gPirs, gPirsOutside) ["Pir02:north"]
String Pir02 "PIR2 [%s]" <dmotion> (gPirs, gPirsOutside) ["Pir01:south"]

String Pir01_Direction "PIR1 Direction [%s]" (gPirDirections)
String Pir02_Direction "PIR2 Direction [%s]" (gPirDirections)

Switch Pir01_Timer (gPirTimers) {expire="5s,command=OFF"}
Switch Pir02_Timer (gPirTimers) {expire="5s,command=OFF"}

In this example the tags mean:

  • Pir02 should face north if it’s not ‘off’ and Pir01 is triggered
  • Pir01 should face south if it’s not ‘off’ and Pir02 is triggered

Then the new rules, I think they are quite self-explanatory (the second one is actually optional):

rule "PIR Direction"
when
    Item Pir01 received update red or
    Item Pir02 received update red
then
    triggeringItem.tags.forEach[t| {
        //logInfo("alarm.rules", "found tag: {}", t)
        val neighbor = gPirs.members.findFirst[p| p.name == t.split(":").get(0)]
        //logInfo("alarm.rules", "neighbor {} state = {}", neighbor.name, neighbor.state)
        if (neighbor.state.toString != "off" && neighbor.state != NULL) {
            val direction = t.split(":").get(1)
            val neighborDirItem = gPirDirections.members.findFirst[d| d.name == neighbor.name + "_Direction"]
            neighborDirItem.postUpdate(direction)
        }
    }]
end

rule "PIR Reset Direction"
when
    Item Pir01 received update off or
    Item Pir02 received update off
then
    val dirItem = gPirDirections.members.findFirst[d| d.name == triggeringItem.name + "_Direction"]
    dirItem.postUpdate(NULL)
end

Then the real reason I’ve done this is, I thought it was a good real use case for the SVG floorplan approach I explained a few days ago :grin: You should definitely consider it instead of positioning your sensor by hand in HTML!

So I created a quick example (right-click the image below and save in conf/html as floorplan.svg and/or open it in Inkscape to inspect how it’s done):
floorplan5
(Sources: https://upload.wikimedia.org/wikipedia/commons/a/af/White_House_State_Floor.svg, https://upload.wikimedia.org/wikipedia/commons/e/e0/EMVCoContactlessIndicator.svg)

The trick here is simply to use an ng-class attribute to add the states of both the PirXX item and the associated direction as CSS classes!

Here’s how it looks in the editor (right pane):

Then you define the CSS rules in conf/html/floorplan.css:


.pir-icon.red {
    fill: red !important;
}
.pir-icon.orange {
    fill: darkorange !important;
}
.pir-icon.orangelight {
    fill: orange !important;
}
.pir-icon.off {
    fill: black !important;
}
.pir-icon.south {
    transform: rotate(90deg);
    transform-origin: 50% 50%;
}
.pir-icon.west {
    transform: rotate(180deg);
    transform-origin: 50% 50%;
}
.pir-icon.north {
    transform: rotate(-90deg);
    transform-origin: 50% 50%;
}

(I had to read https://css-tricks.com/transforms-on-svg-elements/ to understand the transform-origin thing, and beware of translation transform attributes applied by Inkscape, the paths of the icons needed to be combined with the Path > Combine menu, http://www.inkscapeforum.com/viewtopic.php?t=17884 helped)

Then you can put it all together in a HABPanel template like this:

<div oc-lazy-load="'/static/floorplan.css'">
  <div ng-include="'/static/floorplan.svg'"></div>
</div>

and it will behave like you expect!

Hope you find this useful - continue to share your progress!

4 Likes