Introduction
This article describes a way to track presence of various devices such as IoT devices, servers or even web sessions. It may be often necessary to query the state of a group of such devices and see how many are currently connected and transmitting.
<img src="1119730/demo.gif" style="height: 384px; width: 600px;" />
In this example, we are going to build a small web application that tracks presence of logged-in users, and for the sake of simplicity, we are going to create 3 different connections inside the same page. It is worth noting that the technique is not limited to the web, and is using MQTT protocol underneath so all the code described here can be easily written in C/C++ and run on low-memory integrated boards.
Background
In this tutorial, we are going to use the service called emitter.io to handle the online presence for us. The presence feature is integrated throughout the entire service and is transparent for its users. In emitter, there are two ways of retrieving presence information, which are complementary in nature:
- Retrieve a list of currently online devices which is a request/response style API call and returns a status message that describes the number of subscribers and their client identifiers.
- Join/Leave update notifications that are pushed by the service each time a device subscribes or unsubscribes to a particular MQTT channel or wildcard.
Now, emitter is a distributed, publish-subscribe, MQTT broker. In this article, we assume some basic knowledge of MQTT and we won't go in detail through the specifications of the protocol and how to use it, however, here's a couple of important points:
- In MQTT, clients can subscribe to topics (channels), which are represented by a hierarchical strings (e.g.: sensor/1/temperature/).
- Clients can publish to those channels arbitrary binary data.
- MQTT header for packets is only 2 bytes and a variety of client libraries exists out there.
Suppose we have a number of MQTT clients (connections) that are subscribed to a particular channel. In our demo, the channel is "presence-demo/" + Math.random().toString(16).substr(2, 8)
, which creates an isolated demo for everyone. We are going to retrieve:
- the clients that are subscribed to that channel, and
- the notifications when new clients are subscribed to that channel or when the existing ones are unsubscribed from the channel
In the example, we first create main connection that will subscribe to notifications. Notice we use secure: true
argument in this case, as we want our devices to be connected through TLS/SSL underneath.
var client0 = emitter.connect({ secure: true });
Once our client which wants to monitor the presence is connected, we query the presence for the channel.
client0.on('connect', function(){
client0.presence({
key: key,
channel: channel
})
});
When we first call our presence()
function, we receive a response in the format below, telling us that there's no-one currently subscribed occupancy: 0
. Since we didn't specify otherwise, the client0
is also subscribed to join/leave push notifications for this channel, and emitter broker will forward us such events when they occur.
{event: "status", channel: "presence-demo/8758f6d3/", occupancy: 0, time: 1471850444, who: []}
Devices to Monitor
Next, we create a couple of clients we are going to test. Initially, these connections are disabled.
var client1 = null;
var client2 = null;
We are going to toggle the connections on and off in order to simulate devices connecting and disconnecting. The name
argument in this case will actually set MQTT's clientId
option, allowing us to identify various devices. As we control the clientId
, we can associate any state to them as well. In the example, we simply give users some names, but ideally one would use a unique identifier such as GUID
/UUID
or strong pseudo random string
s.
function toggleConnection(client, name) {
if(client) {
client.disconnect();
return null;
} else {
client = emitter.connect({ secure: true, clientId: name });
client.on('connect', function(){
client.subscribe({
key: key,
channel: channel
});
});
return client;
}
}
When one client joins, we receive a subscribe
event with the current occupancy, timestamp (in UNIX time) and the client id. In this demo, we have set client ids to usernames, but ideally you would have a GUID
/UUID
as mentioned previously.
{event: "subscribe",
channel: "presence-demo/8758f6d3/", occupancy: 1, time: 1471850449, who: "Alan"}
Another user may subscribe and we would receive another event.
{event: "subscribe",
channel: "presence-demo/8758f6d3/", occupancy: 2, time: 1471850450, who: "Margaret"}
And when user unsubscribers, we get an unsubscribe
event along with the current occupancy.
{event: "unsubscribe",
channel: "presence-demo/8758f6d3/", occupancy: 1, time: 1471850453, who: "Alan"}
Now, the client/device subscribed to the presence events will receive and handle various notifications. See the code snippet below, which is rather straightforward. In this case, it updates our UI using vuejs databinding.
client0.on('presence', function(msg){
console.log(msg);
var users = vue.$data.users;
switch(msg.event){
case 'status':
for(var i=0; i<msg.who.length;++i){
users.push({
name: msg.who[i]
});
}
break;
case 'subscribe':
users.push({
name: msg.who
});
break;
case 'unsubscribe':
vue.$data.users = users.filter(function( obj ) {
return obj.name !== msg.who;
});
break;
}
vue.$data.occupancy = msg.occupancy;
});
Further Reading
That's it for the presence using emitter. On a final note, there are few more resources you might want to check out, including some demos and source code for a complete application that demonstrates our presence feature.
History
- 08/22/2016 - Initial version of the article