Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Tracking Online Presence of Devices with Emitter for IoT, Mobile or Web

0.00/5 (No votes)
22 Aug 2016 1  
Demonstrates how you can track online presence of devices or users using emitter.io platform

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;" />

[View Live Demo]

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.

Device Interested in Presence Information

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:

  1. the clients that are subscribed to that channel, and
  2. 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(){
    // Query the presence state
    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 strings.

/**
 * Function that toggles the connection on a particular emitter client.
 */
function toggleConnection(client, name) {
    if(client) {
        // If client is already connected, disconnect it
        client.disconnect();
        return null;
    } else {
        // If client is not yet connected, connect and subscribe to the channel
        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.

// on every presence event, print it out
client0.on('presence', function(msg){
    console.log(msg);    
    var users = vue.$data.users;
    switch(msg.event){
        // Occurs when we've received a full response with a complete list of clients
        // that are currently subscribed to this channel. 
        case 'status':
            for(var i=0; i<msg.who.length;++i){
                users.push({
                    name: msg.who[i]
                });
            }
        break;

        // Occurs when a user subscribes to a channel.
        case 'subscribe':
            users.push({ 
                name: msg.who
            });
        break;

        // Occurs when a user unsubscribes or disconnects from a channel.
        case 'unsubscribe':
            vue.$data.users = users.filter(function( obj ) {
                return obj.name !== msg.who;
            });
        break;
    }

    // Also, set the occupancy
    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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here