Integrating reelyActive on a Raspberry Pi 3 with openHAB over MQTT

Tags: #<Tag:0x00007fc8fc3aac60> #<Tag:0x00007fc8fc3aab48>

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:

http://reelyactive.github.io/make-a-pi-hub.html

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)

https://www.npmjs.com/package/mqtt#client

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.

4 Likes

@rlkoshak
I just tried to install reelyActive following your guide. All went well besides the installation of the bluetooth-hci-socket npm module. It always spit an error on me no matter if I tried installing as normal user or root:

gyp WARN EACCES user "root" does not have permission to access the dev dir "/root/node_modules/bluetooth-hci-socket/.node-gyp/7.7.4"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/root/node_modules/bluetooth-hci-socket/.node-gyp"

In case someone else comes across this error - I solved it with this commandline instead of the one in rich’s recipe:

npm install --unsafe-perm  bluetooth-hci-socket
1 Like

Now I’m one step further. I can start reelyActive, but it exits with an error:

$ node ra-mqtt.js
reelyActive Barnacles instance is notifying an open IoT
reelyActive BarnOwl instance is listening for an open IoT
module.js:472
    throw err;
    ^

Error: Cannot find module '../build/Release/binding.node'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.Module._load (module.js:418:25)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/opt/RA/node_modules/bluetooth-hci-socket/lib/native.js:3:15)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)

@rlkoshak did you come across this error when you set up your Pi? I really don’t know node.js, but the error seems to be connected to the bluetooth module, do you agree?

I encountered no errors when setting this up. There have been a lot of changes to reelyActive since I first set it up and I’ve not kept up with it.

The error looks like the problem is with a missing library. You might need to revisit the reelyActive install instructions and tutorials to see if there is another module you need to install through NPM. It is talking about a binding module.

I’ll check the docs at reelyActive. When I find a solution I’ll post it here…

EDIT: i found the missing file at /usr/local/node/lib/node_modules/bluetooth-hci-socket/build/Release/binding.node (where /usr/local/node is the location node.ja is installed under). After copying build/Release/binding.node to node_modules/bluetooth-hci-socket/ everything starts just as expected.

In case someone wants to set up more than one Pi there are instructions in this post.

Is there any way to find out why I am not getting any events? The ra-mqtt script seems to be running, but I can’t get any events (any more). I’m connecting/disconnecting my Android device to/from the integrated bluetooth on the Raspberry Pi 3 (with openhabian / openhab 2.1) without problems, but that does not produce an event. What am I doing wrong?


[18:59:04] openhabian@openHABianPi:~$ sudo node ra-mqtt.js
reelyActive Barnacles instance is notifying an open IoT
reelyActive BarnOwl instance is listening for an open IoT
barnacles is listening on port 3005
HCI Bluetooth address is b827eb3cd6dd, query as receiver 001bc5094b3cd6dd

I did get three events (appearance, keep-alive and some third) once in the beginning, but I am not anymore. Not sure what changed or what I am doing wrong. I installed the Pi Suite as well, but I’m not getting anything there either.

I have Node version 7.10.1 installed. Is that a problem? Will I have to install the latest long term support version (6.x?)?

First I recommend reviewing the updated reelyActive Smart Spaces Revisited post. There have been a lot of changes since this was posted, most importantly they implement MQTT now so there is no need to implement it ourselves. Logging will be better as well and you will have access to the web UI for reelyActive to see what it is seeing.

I also show how to report to a central instance to aggregate the alerts.