I have three computers in the house which each support Bluetooth:
-
An old laptop (chimera) running Ubuntu 15 which is my openHAB server and Mosquitto server (MQTT broker) among other things (file server, VPN, Plex, etc). This machine is on the top floor of the house
-
A first gen Raspberry Pi (garage) in the garage which is wired to a dual relay for triggering the garage door openers and to a couple of reed switches that detect whether or not the garage doors are open. It has a bluetooth dongle attached and it is located on the ground floor of the house.
-
Another first gen Raspberry Pi (hydra) in the basement which is wired to the sensors the previous owner had installed on the doors and windows of the house. It too has a bluetooth dongle attached.
Having bluetooth sensing on each of the three floors guarantees that the whole house gets coverage.
I followed the tutorial here to get bluetooth support on all three machines and install access to bluetooth through Python. I used the inquiry.py script to discover the addresses of my and my wife’s phones and then adapted the
detect.py script to work with a script I already wrote for reporting the door and window sensor outputs over MQTT to do the same for whether or not the bluetooth address is detected nearby or not.
So all three devices are sensing and reporting whether or not the BT devices are present and only if all three report that the devices are not present does openHAB mark that person as away. If both people are way the house goes into “away” mode. However, a person has to be away for more than five minutes before the away states become active to prevent the house from flipping back and forth when going to get the mail.
The rules and Items are as follows. I make heavy use of lambdas and group filters in my rules which makes them short but they might seem daunting for someone not used to writing rules.
Items:
Group:Switch:OR(ON,OFF) gPresent "Present" <present>
Group:Switch:OR(ON,OFF) gRichPresent "Rich Present" <present> (gPresent)
Group:Switch:OR(ON,OFF) gJennPresent "Jenn Present" <present> (gPresent)
Switch S_V_RichChimeraBT <bluetooth> (gRichPresent) { mqtt="<[mosquitto:presence_sensors/bluetooth/chimeraRich:state:default]" }
Switch S_V_RichGarageaBT <bluetooth> (gRichPresent) { mqtt="<[mosquitto:presence_sensors/bluetooth/garageRich:state:default]" }
Switch S_V_RichHydraBT <bluetooth> (gRichPresent) { mqtt="<[mosquitto:presence_sensors/bluetooth/hydraRich:state:default]" }
Switch S_V_JennChimeraBT <bluetooth> (gJennPresent) { mqtt="<[mosquitto:presence_sensors/bluetooth/chimeraJenn:state:default]" }
Switch S_V_JennGarageBT <bluetooth> (gJennPresent) { mqtt="<[mosquitto:presence_sensors/bluetooth/garageJenn:state:default]" }
Switch S_V_JennHydraBT <bluetooth> (gJennPresent) { mqtt="<[mosquitto:presence_sensors/bluetooth/hydraJenn:state:default]" }
Rules:
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.eclipse.xtext.xbase.lib.*
import java.util.Map
import java.util.HashMap
val Map<SwitchItem, Timer> timers = newHashMap
val Functions$Function3 home = [ String name, SwitchItem homeSwitch, Map<SwitchItem, Timer> timers |
logInfo("Presence", name + " was detected at home")
var t = timers.get(homeSwitch)
if(t != null) {
t.cancel
timers.put(homeSwitch, null)
}
homeSwitch.sendCommand(ON)
]
val Functions$Function4 away = [ String name, SwitchItem trigger, SwitchItem homeSwitch, Map<SwitchItem, Timer> timers |
val timer = timers.get(homeSwitch)
if(timer == null){
logInfo("Presence", name + " is not home, setting timer before updating home state")
timers.put(homeSwitch, createTimer(now.plusMinutes(5), [|
if(trigger.state == OFF) {
logInfo("Presence", name + " is still away after five minutes")
homeSwitch.sendCommand(OFF)
} else {
logInfo("Presence", name + " came back before timer went off")
}
timers.put(homeSwitch, null)
]))
} else {
logInfo("Presence", "Timer for " + name + " is already set")
}
]
rule "Rich Presence Changed"
when
Item gRichPresent changed or
System started
then
logInfo("Presence", "Rich Group state = " + gRichPresent.state)
gRichPresent?.members.forEach(s|logDebug("Presence", s.name + " state = " + s.state))
if(gRichPresent?.members.filter(s|s.state == ON).size > 0) home.apply("Rich", RichHome, timers)
else away.apply("Rich", gRichPresent, RichHome, timers)
end
rule "Jenn Presence Changed"
when
Item gJennPresent changed or
System started
then
logInfo("Presence", "Jenn Group state = " + gJennPresent.state)
gJennPresent?.members.forEach(s|logDebug("Presence", s.name + " state = " + s.state))
if(gJennPresent?.members.filter(s|s.state == ON).size > 0) home.apply("Jenn", JennHome, timers)
else away.apply("Jenn", gJennPresent, JennHome, timers)
end