reelyActive Smart Spaces Revisited

A lot has changed since

and

to the reelyActive product and the best way to integrate it with OH using MQTT so I thought I would post an updated tutorial that combines a lot of the information that is split between both of those two threads.

Thanks to @reelyActive for the updates and answering questions on those threads.

What is reelyActive?

reelyActive is a commercial service that has open sourced their software. The software is intended to be used to track bluetooth devices as they move through a space. That is good news for us. First, not only is it opensource but the developers have posted tutorials on how to get it to work on Raspberry Pi’s Anand they have been helpful on this forum.

Design

I have this set up with one central instance of the reelyActive Hyperlocal Context Server running on a Raspberry Pi 3 and a second instance running on a Rapberry Pi Zero W. You can repeat the instructions for the Pi Zero W to add additional instances to your deployment.

The Pi 3 acts as the central aggregation point and as the gateway to MQTT, reporting all the events from all the running instances to OH through MQTT.

OH receives the events on one Item and uses a rule to parse out the JSON and make decisions accordingly.

Setting up the Central Server and Other Instances

Setup is now pretty simple.

  1. If it is not installed already, install the latest long term supported version of node.js. You can find instructions for the Pi 3 and the Pi Zero W (its a different version of node for each) here. It also has you install forever but I don’t know if that library is required or not. I installed it just in case.

  2. Create a new folder where your server’s files will reside. I choose /opt/reelyActive. Fix the file permissions and change to this directory.

sudo mkdir /opt/reelyActive
sudo chmod a+rw /opt/reelyActive
cd /opt/reelyActive
  1. Install the three required node.js libraries:
npm install hlc-server
npm install bluetooth-hci-socket
npm install mqtt
  1. Create the central node that will forward all detected and received events to OH using MQTT. For this tutorial we will call it server.js:
// User-defined
var CLIENT_OPTIONS = {
	port: 1883,
	clientId: 'reelyActive',
	username: 'username',
	password: 'password',
	will: {
		topic: 'lwt/topic/goes/here',
		payload: 'lwt message'
	}
 }
var TOPIC = 'topic/goes/here';
var HOSTNAME = 'ip-address-goes-here';


// Fire up the reelyActive hyperlocal context server
var server = require('hlc-server');
var app = new server();
 
// Listen on the BLE radio of the Pi 3
app.bind( { protocol: 'hci', path: 'null' } );

// Set up the MQTT client service
app.addNotificationService( {
    service: "barnaclesmqtt",
    hostname: HOSTNAME,
    topic: TOPIC,
    clientOptions: CLIENT_OPTIONS
});
  1. Run the server to make sure everything is OK. I think it needs to run as root to access the bluetooth.
sudo node server.js

It may take a minute to fully come up. You should see a handful of log lines ending with

hlc-server is listening on port 3001
  1. If that worked, <ctrl>c to stop the server and add it as a service that will start at boot. Create /lib/systemd/system/reelyActive.service
[Unit]
Description=reelyActive
After=network.target iptables.service firewalld.service

[Service]
Restart=always
WorkingDirectory=/opt/reelyActive
ExecStart=/usr/local/bin/node server.js

[Install]
WantedBy=multi-user.target
sudo systemctl enable reelyActive.service
sudo systemctl start reelyActive.service
sudo systemctl status reelyActive.service

You should see the same log statements showing that it is up and running.

  1. Log onto the Pi Zero W and repeat steps 1 through 3, only you do not need to npm install mqtt this time.

  2. The server.js file is a little different this time since it doesn’t need the MQTT settings.

// Fire up the reelyActive hyperlocal context server
var server = require('hlc-server');
var app = new server();
 
// Listen on the BLE radio of the Pi 3
app.bind( { protocol: 'hci', path: 'null' } );

// Set up the barnacles REST forwarding service
app.addNotificationService( {
    service: "barnaclesrest",
    hostname: "192.168.1.101", // IP address of central instance
    port: 3001
});
  1. Repeat steps 4 and 5

Congratulations, you now have two instances installed and both are reporting events over MQTT.

You can open the dashboard and bubblescape for each by opening:

http://ip-of-server:3001

If you open the central instance you will see the data from all the instances.

Configuring OH

This configuration assumes a setup similar to Generic Presence Detection. The Rule also uses the Associated Items Design Pattern.

Items:

Group:String gFloor

String vReelyActive	{ mqtt="<[mosquitto:'topic/goes/here':state:default]" }

// create Items for each device you want to track.
Switch vRichPhone_reelyActive "Rich's Phone" <present> (gPresent) { expire="2m,command=OFF" }

// create Items to keep track which sensor the device is closest to, add these Items to a Group
Switch vRichPhone_reelyActive_seenby (gFloor)

Note the use of the expire binding above. I do this to detect when the phone is no longer being detected because the reelyActive events are really jittery. I noticed that my Items kept flopping between ON and OFF quite frequently so the Rule below only pays attention to first seen and update events and ignores the dissapear events letting the expire binding handle that. This greatly smooths out the events from the OH perspective.

Rule:

rule "Received a reelyActive event"
when
	Item vReelyActive received update
then

	val jsonMsg = vReelyActive.state.toString
	val eventType   			= transform("JSONPATH", "$.event",       jsonMsg)
	val deviceId    			= transform("JSONPATH", "$.deviceId",    jsonMsg)
	val receiverId  			= transform("JSONPATH", "$.receiverId",  jsonMsg)

	// We will let the Expire binding set the switch to OFF rather than acting on the disappearance event from reelyActive
	if(eventType != "appearance" && eventType != "keep-alive" && eventType != "displacement") return;

	var deviceName = "UNKNOWN"
	// See if this is a known device ID
	switch deviceId{
        // make a case for each device that has a static deviceID (i.e. BT MAC, note the all lowercase and no : or -)
		case "1234567890ab": deviceName = "vRichPhone"
		case "ba0987654321": deviceName = "vRichWatch"
        ...
	}

	val swName = deviceName + "_reelyActive"
	val sw = gPresent.members.filter[s|s.name == swName].head
	sw?.sendCommand(ON)

	val receiver = gFloor.members.filter[f|f.name == swName + "_seenby"].head
	receiver?.sendCommand(transform("MAP", "presence.map", receiverId))
end

Theory of Operation: When an event comes in we parse out the needed fields and see if the device ID matches one we already know. If so we set deviceName to the first part of the Item name corresponding with that device. Using that we pull the switch out of gPresent by name and send it the ON command. We then set the corresponding seenby Item to indicate which receiver it was seen by.

It is an exercise left to the student to add to this rule and/or others to use the seenby Item to help track the device as it moves through the house.

presence.map
Provides a mapping between the receiverId and a human readable name for what it means when the device is seen nearest to that sensor. For example, you can map the receiverId that is located in the basement to “basement” and use that to determine the device is in or at least nearest to the basement receiver.

You can find the receiverId by looking at the handful of lines that are printed out when server.js starts (which you can see when it runs as a service by doing a sudo systemctl status reelyActive.

Observations and Limitations

  • The first thing you will want to do, particularly if you are logging out lots of stuff, is have the rule ignore your Chromecasts. They are CONSTANTLY broadcasting and reelyActive will constantly report events for them.

  • Sadly, this is not a perfect solution for iPhones. It appears that Apple forces the iPhones to generate a unique random deviceId relatively frequently (at least once per hour) so you cannot rely on the Device ID to detect an iPhone. The best I’ve been able to figure out is that you can determine it is an Apple Device by looking at the deviceAssociationIds JSON field, the first entry of which will be “004c” or by looking at the deviceUrl which will include the device’s company name. So, you can detect iPhones are present and track them through the house, you just can’t tell that a specific iPhone is present.

  • There are a lot of events occurring all the time. You will want to turn off events.log logging except when you are debugging as it will become largely full of reelyActive events and hard to use for anything else.

  • If you scatter a lot of these throughout the house (at Pi Zero W prices why not?) you will want to pay attention to the displacement events. These occur when a device moves closer to a different sensor based on signal strength. With some patient observation (or training an AI) one could start to track a device’s movement through the house with some accuracy.

  • I don’t own one but the docs indicate reelyActive does support beacons.

5 Likes

@rlkoshak great comprehensible article. Just to make it complete, could you add a short intro stating what reelyActive Smart Spaces actually is? :blush:

Oops. Corrected I hope.

1 Like

Thanks. Just went through the article and this indeed sounds interesting.

Regarding the iPhone topic: My brother owns an iPhone, iPad and iWatch. Any chance these could be differentiated?

I don’t know about others but my most intriguing aspect of presence detection right now is the quickness in realizing that someone left. I’ve implemented the Bee-in-a-Box principle in combination with Ping/DHCP and it works quite well for “Arriving” but not for “Leaving”. I have to wait ~1 minute to check if my phone left the wifi shortly after the entrance door was opened.
In your experience, is the signal strength reliable enough to decide that the phone is “in front of the door”?

I’m not entirely sure. My wife also has all three devices and the little testing I was able to do only one device at a time showed up in the logs. But because the deviceId Apple uses is random I had no way to determine which of them was reporting. After awhile my wife left and all the reports stopped so I suppose that showed that the iPad wasn’t one of them being detected (it probably has a dead battery).

I think that at any give time the three devices will show up as three separate devices to reelyActive so they will be distinct, but there will be no way to distinguish between the three unless you install and run the reelyActive app. This app will deliberately send out broadcasts with UUID information that does not change and it can be parsed out of the reelyActive event messages.

I want a solution that doesn’t require installing yet another app on the phone so I haven’t looked into this very closely.

Depending on the receiver’s BT location this should be very easy to accomplish. You get both which receiver the device is closest to but also the RSSI so you could use the fact that the device is being reported by the receiver near the door above a certain signal strength and know with pretty good accuracy that the device is in front of the door. The closer to the door you place the receiver the better your accuracy will be.

This is actually the use case the software was designed to address. They have them set up in stores and around advertisements or art exhibits and can determine what parts of the store, which billboards, or which artworks receive the most or longest attention and stuff like that. But because this is their use case, they don’t really care if they can uniquely identify iPhones for all time. They even discuss it as a privacy feature.

The more sensors you have the better resolution you will get.

Thanks @rlkoshak for revisiting this topic! Indeed we at reelyActive have
made a lot of project over the past year as more and more people have
started using our open source software, especially for the Pi.

Probably the most exciting thing for home automation is our integration
with sensor beacons (temperature, humidity, acceleration) which you can
read more about in our Forward BLE sensor data to Initial State
tutorial. In effect, you can simply stick an inexpensive sensor beacon to
whatever you want to measure (door opening, HVAC forced-air
temperature/humidity, 
) and it automatically gets picked up so long as
it’s in range of a Pi.

As you’ve noticed, it is difficult to track people uniquely via their
smartphones, wearables and trackers such as Tile which periodically cycle
their identifier. This is of course by design to protect privacy.
Simplest option to overcome this is to carry/wear an inexpensive beacon
which does NOT change its ID. For example, Minew Shenzhen Technology, the
company whose sensor beacons we recommend, makes a bunch of key-finder and
wearable beacons that serve this purpose well. The venerable Fitbit Charge
HR is one of the few that does NOT change its ID, so if you have one lying
around, that’ll work too


Regarding presence detection using signal strength (RSSI), you’ll likely
find that you can determine with reasonable confidence that a device is
really close (within 1-2m)
and then beyond that the confidence becomes
dismal. So detecting presence at a door, for instance, is feasible.

If you’re using multiple Pis to create zones within the home, consider
having just one Pi as master and all other Pis forwarding to the master’s
hlc-server software instance. The master will automatically combine the
decoding events, and decide which Pi had the strongest RSSI. If you go
this route, the non-master Pis can simply run our reelay software which is
even simpler.

Finally, we’ve also created a project called GeneraBLE where a Pi can drive
Philips Hue lights and MIDI instruments based on the presence of devices.
Perhaps not exactly what you’d want as an experience in your home, but
might be the source of some additional inspiration.

This is really interesting. I’m going to have to play with that some.

I thought that is how I set it up. So what you are saying is I don’t need to run the hlc on the non-master Pis? What would be different in the code to run this way. Currently my non-master node is using:

// Fire up the reelyActive hyperlocal context server
var server = require('hlc-server');
var app = new server();
 
// Listen on the BLE radio of the Pi 3
app.bind( { protocol: 'hci', path: 'null' } );

// Set up the barnacles REST forwarding service
app.addNotificationService( {
    service: "barnaclesrest",
    hostname: "192.168.1.101", // IP address of central instance
    port: 3001
});

While it is running an HLC server, as you can see it is only using the barnaclesrest notification service to send the events to my main server. I based the above off of the example posted by @reelyActive here

Oh, you would be surprised what some of us get up to, especially around Halloween and Christmas. :japanese_ogre:

Though in the openHAB world we usually have openHAB doing the controlling so we can control anything that can talk to openHAB, not just a few devices.

Thanks a bunch for the response!

Thank you Rich for your reply in the older post.

I tried switched over to this new version, but I’m not getting any closer to getting it to work, it seems


Still not seeing any events, also not in the dashboard. The server.js is running “almost fine”, except for a problem with connecting to MQTT, which I don’t understand either. The MQTT seems to be running fine, I am getting messages from OwnTrack.

openhabian@openHABianPi:/opt/reelyActive$ sudo node server.js
reelyActive BarnOwl instance is listening for an open IoT
reelyActive Barnacles instance is notifying an open IoT
reelyActive Barterer instance is exchanging data in an open IoT
reelyActive Chickadee instance is curious to associate metadata in an open IoT
hlc-server is listening on port 3001
Error with barnacles MQTT connection: Error: Connection refused: Not authorized
HCI Bluetooth address is b827eb3cd6dd, query as receiver 001bc5094b3cd6dd

Just to make sure, whenever I connect to the bluetooth service on the Pi with my Android for example, I should be seeing an event, same with disconnecting etc, right? If so, what could be the problem?

This is my server.js:

// User-defined
var CLIENT_OPTIONS = {
        port: 1883,
        clientId: 'reelyActive',
        username: 'openhabian',
        password: '<password>',
        will: {
                topic: 'status/reelyActive/bluetooth',
                payload: 'reelyActive server is down'
        }
 }
var TOPIC = 'reelyActive/bluetooth';
var HOSTNAME = '192.168.2.112';


// Fire up the reelyActive hyperlocal context server
var server = require('hlc-server');
var app = new server();

// Listen on the BLE radio of the Pi 3
app.bind( { protocol: 'hci', path: 'null' } );

// Set up the MQTT client service
app.addNotificationService( {
    service: "barnaclesmqtt",
    hostname: HOSTNAME,
    topic: TOPIC,
    clientOptions: CLIENT_OPTIONS
})

The error is pretty clear. The username or password used to connect to your broker are incorrect. So that will certainly be one of the problems.

No. reelyActive is passive and looks for BTLE broadcasts. Your phone just needs to be near with bluetooth turned on and reelyActive should pick it up. It works better with beacons actually because many/most phones will change the ID that they broadcast so while you can tell that AN Android phone is nearby, you can’t necessarily tell if it is YOUR Android phone.

However, I don’t know if reelyActive will continue to work if you pair and connect your phone with the BT on the Pi. That might cause it to lose access to the BT and no longer able to listen. I know that reelyActive appears to be designed to have exclusive access to the BT device on the Pi so this could be another problem.

Ah, that makes sense! I was wondering about that. Not having to connect via bluetooth is probably a lot better for battery


But: Username and password should really be correct. Not sure what’s causing the problem. I am connect to that MQTT with two different devices (OwnTrack on my Android, MQTT.fx via Windows), using the exact same credentials, without problems.

That I can’t answer but the error is clear. “Connection refused: Not authorized”. You can try to connect using those credentials from the reelyActive machine using an mqtt client and see if that works. Also look in the broker’s logs to see if any more information is provided for why the connection is refused.

It is important to do so from the reelyActive machine though to rule out firewall or other networking problems.

It’s all on the same machine, one Raspberry Pi 3. Or is that not possible? Or is there a better way to do it anyway (bluetooth presence detection to/in openHab)?

I tried using the IP of the Pi (192.168.2.112), “localhost”, 127.0.0.1, without change.

Sorry, still very new to all of this :slight_smile:

Edit: I just looked into the dashboard again, now after disconnecting the bluetooth connection on the Android device I am getting all kinds of events, especially keep-alives. This is great. Now all I have to do is to get the OH switch to turn on :wink:

This is good news. We are now down to just the MQTT problem.

It should all run on the same machine without any problem so long as reelyActive is the only device using the BT.

What does the broker log out when reelyActive tries to connect? Sometimes that can be informative.

Are you using certs and encrypted configs?

This client is the only one trying to connect using the clientID ‘reelyActive’?

What might have been a problem could be special characters (; and !) in my password. I was getting the same error when using mosquitto_pub on the command line. I changed the password, which now has no special characters.

I am now able to publish on the command line using the following:

mosquitto_pub -h 192.168.2.112 -u openhabian -P <password> -i reelyActive -t reelyActive/bluetooth -m "testpayload"

Unfortunately, I am still getting the authentication error with my “server.js”, even after having changed the password in that file as well.

// User-defined
var CLIENT_OPTIONS = {
        port: 1883,
        clientId: 'reelyActive',
        username: 'openhabian',
        password: '<password>',
        will: {
                topic: 'status/reelyActive/bluetooth',
                payload: 'reelyActive server is down'
        }
 }
var TOPIC = 'reelyActive/bluetooth';
var HOSTNAME = '192.168.2.112';


// Fire up the reelyActive hyperlocal context server
var server = require('hlc-server');
var app = new server();

// Listen on the BLE radio of the Pi 3
app.bind( { protocol: 'hci', path: 'null' } );

// Set up the MQTT client service
app.addNotificationService( {
    service: "barnaclesmqtt",
    hostname: HOSTNAME,
    topic: TOPIC,
    clientOptions: CLIENT_OPTIONS
});

And yea, ‘reelyActive’ is/was only used by one client.

For the third time, what do you see in the logs of the broker?

Sorry about that. I guess it’s the mosquitto log file? When connecting with the “server.js”, this is what’s happening:

1508995089: New connection from 192.168.2.112 on port 1883.
1508995089: Socket error on client <unknown>, disconnecting.

After switching the broker to “allow_anonymous true”, I am (obviously) not getting that error anymore. Somehow it seems that the server might be sending the credentials in a “wrong way”, if that’s even possible.

Now the next problem is that for whatever reason I’m again not seeing any events anymore, even with my Android device not being connected via BT to the RPi3. I’m confused :confused: Should not have anything to do with me allowing anonymous connections via MQTT. I did reboot the Pi once or twice since yesterday, when it started to work, but I’m not sure what I might have done to make it stop work. :confused:

OK, that is interesting. Mosquitto makes it sound like there is a networking problem but reelyActive is saying it is an authentication problem. I’m not sure what the problem can be.

I’ve always left the allow_anonymous true as the default in the mosquitto config so I can’t say for sure whether that is why I’ve never seen a problem. It seems like since you are supplying a user and password in reelyActive you should be able to connect. You might want to raise this as an issue or at least a conversation on the reelyActive repos.

Beyond setting reelyActive up and running it, which I managed to do without any errors, I know very little of how it actually works internally. The MQTT config should not make any difference to whether the Bluetooth events are detected or not. So again, you might need to move over to the reelyActive support community to solve this issue. I’m fresh out of ideas.

1 Like

Yea, thank you for your help!

I don’t think I want to keep the anonymous option on, because my OwnTrack on my Android device sends messages to that MQTT (via a dyndns). So I do need authentification, otherwise everyone could just connect to that server.

Somehow my reelyActive dashboard did show events after a while. It somehow looks like my Android smartphone doesn’t really gets noticed, but when I started up my iPad and activated bluetooth, I got hundreds of events over time. When I then started my AppleTV in the evening, it looked like there were even more messages (I don’t even know if the AppleTV has bluetooth?!). So it might be a “problem” with my Android device. Might be designed to save battery and not use its bluetooth whenever possible.

Rokus I think have BT too. Chromcasts definitely do. I was actually mislead when I first set reelyActive up because I keep seeing all these Google detects (I was looking at raw messages, not the web page) and it took a few minutes to realize that it was coming from the Chromcasts.

That’s a possibility or it might go to sleep like iPhone networking does when it isn’t actively in use.

There is a reelyActive app you can install that will add a unique identifier to the packets reelyActive receives which will help you identify your specific device and perhaps it will keep the BT active on the Android.