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.
-
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.
-
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
- Install the three required node.js libraries:
npm install hlc-server
npm install bluetooth-hci-socket
npm install mqtt
- 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
});
- 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
- 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.
-
Log onto the Pi Zero W and repeat steps 1 through 3, only you do not need to npm install mqtt this time.
-
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
});
- 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.