This is a quick tutorial on how to integrate reelyActive’s BTLE presence detection software with openHAB.
This product uses the signal strength to detect the presence or absence of BTLE devices near your sensors. They offer their own hardware devices which you can install and daisy chain throughout your structure and their software works Raspberry Pi 3. Theoretically it will work with any Linux OS that has a BTLE dongle installed and can run Node.js.
This tutorial only runs on one node. If you have multiple nodes, you can receive events for when a BTLE device moves away from one sensor and towards another. This could be useful to track a person’s location throughout the house. Or in my case, it could be handy to find certain devices which the three-year-old has hidden.
reelyActive provides a basic proof of concept tutorial here:
If you just want to see if it will work, follow those instructions which creates a nice web front end
A related thread is located here:
User @reelyActive has been very helpful in my getting this up and working, despite my lack of Node.js coding skills.
Installing the Parts
reelyActive is made up of a number of libraries developed to run on Node.js. As such almost everything can be installed through npm. However, it does require at least Node 4.5 which is newer than the version that currently comes with Raspbian Jessie.
So first update and upgrade your system:
sudo apt-get update
sudo apt-get upgrade
Download and install Node.js 4.5
wget https://nodejs.org/dist/v4.5.0/node-v4.5.0-linux-armv7l.tar.xz
tar -xf node-v4.5.0-linux-armv7l.tar.xz
sudo mv node-v4.5.0-linux-armv7l /usr/local/node
cd /usr/local/bin
sudo ln -s /usr/local/node/bin/node node
sudo ln -s /usr/local/node/bin/npm npm
Now install all the libraries you will need:
npm install barnowl
npm install barnacles
npm install bluetooth-hci-socket
npm install mqtt
Barnowl is the middleware for processing events. Barnacles are use to communicate events. Bluetooth-hci-socket is how reelyActive communicates with the BT device. We are using the default MQTT library for Node.js which I can confirm works with the Mosquitto broker. Examples also show it working with HiveMQ.
Create the Script
The following is the script I wrote, with lots of help from @reelyActive to report the events to an MQTT topic.
ra-mqtt.js
var barnacles = require('barnacles');
var barnowl = require('barnowl');
var reelib = require('reelib');
// MQTT Setup
var mqtt = require('mqtt');
var PORT = 1883;
var ID = 'reelyActive-cerberos';
var USER = 'user';
var PW = 'password';
var LWT_TOPIC = 'status/reelyActive/cerberos';
var LWT_PAYLOAD = 'reelyActive cerberos is down';
var PUB_TOPIC = 'reelyActive/cerberos';
// See https://github.com/mqttjs/MQTT.js/blob/master/examples/client/secure-client.js for a TLS example
// See https://www.npmjs.com/package/mqtt#client for available options
var options = {
port: PORT,
clientId: ID,
username: USER,
password: PW,
will: {
topic: LWT_TOPIC,
payload: LWT_PAYLOAD
}
};
client = mqtt.connect('mqtt://192.168.1.200', options);
// END MQTT Setup
// reelyActive Setup
var notifications = new barnacles();
var middleware = new barnowl();
middleware.bind( { protocol: 'hci', path: null } );
notifications.bind( { barnowl: middleware } );
// END reelyActive setup
// Event Handler
notifications.on('appearance', function(event){
// appears
handleEvent(event);
});
notifications.on('displacement', function(event){
// moves to another device
handleEvent(event);
});
notifications.on('disappearance', function(event){
// no longer detected
handleEvent(event);
});
notifications.on('keep-alive', function(event){
// redetection
handleEvent(event);
});
function handleEvent(event) {
// var flattenedEvent = reelib.event.toFlattened(event);
console.log(JSON.stringify(event, null, 2))
client.publish(PUB_TOPIC, JSON.stringify(event));
};
All of the MQTT configuration parameters are listed at the top of the MQTT section. Modify these to match your configuration. See the following for the full set of options (e.g. how to enable SSL/TLS)
Run the Script
node ra-mqtt
If you see errors you may need to run it as root. The script will start up (it takes awhile before the first logging appears, be patient) and as it receives events they will be logged and sent to the MQTT topic configured.
Run as a Service
If on a systemd based machine, you can use the following reelyActive.service file to run this automatically as a service.
[Unit]
Description=reelyActive
After=network.target iptables.service firewalld.service
[Service]
Restart=always
WorkingDirectory=/opt/reelyActive/ra-mqtt
ExecStart=/usr/local/bin/node ra-mqtt
[Install]
WantedBy=multi-user.target
Note: change the paths and filename to what every you have chosen to use.
Copy this file to /etc/systemd/system
and run the following commands:
sudo systemctl daemon-reload
sudo systemctl enable reelyActive.service
sudo systemctl start reelyActive.service
systemctl status reelyActive.service
You should see that it is running and active. If you use a command line tool like mosquitto_sub to subscribe to the configured topic you should see a bunch of JSON messages.
About the Events
There are four events:
- appearance: issued when a device is seen for the first time
- keep-alive: issued when a device is seen again (i.e. a disappearance event has not been sent)
- displacement: issued when a device moves closer to one sensor and away from another
- disappearance: issued when a device is no longer seen by a node
The code above sends the full event JSON event. This is because some of the information in the event, particularly in the “advData” section can be very helpful in mapping the reelyActive deviceId to a physical device. For example, my Android phone showed up with a "companyName": "Google"
in this section whereas my FitBit had a "completeLocalName": "Charge HR"
in this section. You may have to watch the events and turn devices on and off to map everything you want to follow.
Once you map your devices to a deviceId, you can use the flattened version of the event (see the commented out code above) which just has the event and the event triad (time, device, and signal) without all that extra stuff.
openHAB Configuration
I’m sure there are lots of different ways to do this. The following is how I chose to do it. NOTE: If you are on openHAB 2 you need to install the JSONPATH addon if you haven’t already. In both versions of OH you need the MQTT binding.
Items
String ReelyActiveCerberos { mqtt="<[mosquitto:reelyActive/cerberos:state:default]" }
Switch S_V_RichCerberosPhoneRA "Moto X PE reelyActive Cerberos [%s]"
<bluetooth> (gRichPresent, gPresent, gCerberosSensors)
Switch S_V_RichCerberosFitbitRA "Fitbit reelyActive Cerberos [%s]"
<bluetooth> (gRichPresent, gPresent, gCerberosSensors)
Make the topic match what you configured in the script above. Create a Switch Item for all the devices you want to follow.
The JSON is parsed and Items updated by a Rule because the Item’s states depend on two fields in the message. You could probably also use a JavaScript transform instead of a Rule.
rule "ReelyActiveCerberos received an update"
when
Item ReelyActiveCerberos received update
then
val eventType = transform("JSONPATH", "$.event", ReelyActiveCerberos.state.toString).replace('\"', '')
val deviceId = transform("JSONPATH", "$.deviceId", ReelyActiveCerberos.state.toString).replace('\"', '')
logDebug(logNamePresence, "Event Type = " + eventType + " Device ID = " + deviceId)
val newState = if(eventType == "appearance" || eventType == "keep-alive") ON else OFF
switch(deviceId) {
case "a477339ec76b": {
logDebug(logNamePresence, "Sending " + newState + " to S_V_RichCerberosPhoneRA")
if(S_V_RichCerberosPhoneRA.state != newState) S_V_RichCerberosPhoneRA.sendCommand(newState)
}
case "c920d966ca48": {
logDebug(logNamePresence, "Sending " + newState + " to S_V_RichCerberosFitbitRA")
if(S_V_RichCerberosFitbitRA.state != newState) S_V_RichCerberosFitbitRA.sendCommand(newState)
}
}
end
The magic takes place in the switch statement. That is where the reelyActive deviceId is mapped to an openHAB Item which is switched ON or OFF depending on the event type.
There are lots of different things one could to to make this better, particularly if you have more than a few devices. I leave that as an exercise to the student.
Notes on Performance
reelyActive seems to be very jittery in its detection of devices. While my phone is sitting still no more than 15 feet from the sensor (granted the sensor is on the other side of a wall) it constantly flips between appearance and disappearance. To use this practically one would probably want to implement some debouncing with a Timer to only switch the Item to OFF if it has been a certain amount of time since the last disappearance event without a new appearance event. There also might be things one can do to make reelyActive itself more or less sensitive.
Over all the integration was not difficult and if you have more than one Pi 3 about the house, you could theoretically deploy one in multiple rooms/floors to get not just house presence detection but room presence detection.