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 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):
(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!