hedywings / mqtt-node

An ip-based smart thing that plays as a client of mqtt-shepherd server.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

mqtt-node

Table of Contents

  1. Overiew
  2. Features
  3. Installation
  4. Basic Usage
  5. Resources Planning
  6. APIs
  7. Code Templates

1. Overview

Lightweight MQTT machine network LWMQN is an architecture that follows part of OMA LWM2M v1.0 specification to meet the minimum requirements of machine network management.

  • This module, mqtt-node, is an implementation of LWMQN Client.
  • mqtt-shepherd is an implementation of LWMQN Server.
  • mqtt-shepherd and mqtt-node are working together to form an IoT machine network.
  • mqtt-node is suitable for devices that can run node.js, such as Linkit Smart 7688, Raspberry Pi, Beaglebone Black, Edison, and many more.
  • mqtt-node uses IPSO definitions as its fundamental of resource organizing on devices. This document also provides templates of many common devices defined by IPSO Smart Objects starter pack 1.0, i.e., temperature sensor, humidity sensor, light control.
  • mqtt-node is trying to let you build IoT peripheral machines with less pain.

Note:

  • IPSO uses Object, Object Instance and Resource to describe the hierarchical structure of resources on a Client Device, where oid, iid, and rid are identifiers of them respectively to allocate resources on a Client Device.
  • An IPSO Object is like a Class, and an Object Instance is an entity of such Class. For example, when you have many 'temperature' sensors, you have to use an iid on each Object Instance to distinguish one entity from the other.

Acronyms and Abbreviations

  • Server: LWMQN server
  • Client or Client Device: LWMQN client which is a machine node in an IoT network
  • MqttNode: class exposed by require('mqtt-node')
  • qnode: instance of MqttNode class
  • oid: identifier of an Object
  • iid: identifier of an Object Instance
  • rid: indetifier of a Resource

2. Features

  • Communication based on MQTT protocol and library mqtt.js
  • Resources structured in a hierarchical Smart-Object-style (IPSO)
  • Easy to create Resources on a Client Device
  • LWM2M-like interfaces for Client/Server interaction

3. Installation

$ npm install mqtt-node --save

4. Basic Usage

  • Client-side exmaple (here is how you use mqtt-node on a machine node):
var MqttNode = require('mqtt-node');

/********************************************/
/*** Client Device Initialzation          ***/
/********************************************/
var qnode = new MqttNode('my_foo_client_id');

// Initialize Resources that follow IPSO definition

// We have two humidity sensors here
// oid = 'humidity', iid = 0
qnode.initResrc('humidity', 0, {
    sensorValue: 20,
    units: 'percent'
});

// oid = 'humidity', iid = 1
qnode.initResrc('humidity', 1, {
    sensorValue: 16,
    units: 'percent'
});

// Initialize a custom Resource
qnode.initResrc('myObject', 0, {
    myResrc1: 20,
    myResrc2: 'hello world!'
});

qnode.on('ready', function () {
    // If the registration procedure completes successfully, 'ready' will be fired

    // start to run your application after registration
});

// Connect and register to a Server with the default account of mqtt-shepherd
qnode.connect('mqtt://192.168.0.2', {
    username: 'freebird',
    password: 'skynyrd'
});
  • Server-side example (please go to mqtt-shepherd document for details):
var qnode = qserver.findNode('my_foo_client_id');

if (qnode) {
    qnode.readReq('humidity/0/sensorValue', function (err, rsp) {
        console.log(rsp.data);      // 20
    });

    qnode.readReq('myObject/0/myResrc2', function (err, rsp) {
        console.log(rsp.data);      // 'hello world!'
    });
}

5. Resources Planning

The great benefit of using mqtt-node in your LWMQN Client is that you almost need not to tackle requests/responses by yourself. All you have to do is to plan and define your Resources well, and mqtt-node will automatically tackle many of the REQ/RSP things for you.

  • What Resources do you have on the Client Device?
  • Which Resource is readable?
  • Which Resource is writable?
  • And which Resource is remotely executable?

Here is a tutorial of how to plan your Resources with mqtt-node. Some quick examples are also given in the description of initResrc() method.

Once your Resources are initialized, mqtt-node itself will know how to respond to requests from a LWMQN Server.


6. APIs


MqttNode Class

Exposed by require('mqtt-node')



new MqttNode(clientId, devAttrs)

Create an instance of MqttNode class.

Arguments:

  1. clientId (String): clientId should be a string and should be unique in the network. Using mac address (with a prefix or suffix) as the clientId would be a good idea.
  2. devAttrs (Object): An object to describe information about the device. The following table shows details of each property within devAttrs object.
Property Type Mandatory Description
lifetime Number optional Default is 86400. Unit: seconds
version String optional Minimum supported LWMQN version (this is not really effective at this moment)
ip String optional Device ip address. By default, mqtt-node itself will query this parameter from system
mac String optional Device mac address. By default, mqtt-node itself will query this parameter from system

Returns:

  • (Object) qnode

Examples:

var MqttNode = require('mqtt-node');
var qnode = new MqttNode('my_foo_client_id', {
    lifetime: 21600,
    ip: '192.168.0.99',
    mac: '00:0c:29:3e:1b:d2',
    version: 'v0.0.6'
});
    
console.log(qnode.clientId);    // 'my_foo_client_id'
console.log(qnode.lifetime);    // 21600
console.log(qnode.ip);          // '192.168.0.99'
console.log(qnode.mac);         // '00:0c:29:3e:1b:d2'
console.log(qnode.version);     // 'v0.0.6'

// Do not change the device attributes with direct assigments, 
// i.e., qnode.lifetime = 2000.

// Use qnode.setDevAttrs() to change attributes, and qnode will 
// automatically check if it needs to publish an update message to the Server.

.initResrc(oid, iid, resrcs)

Initialize the Resources on qnode.

Arguments:

  1. oid (String | Number): Id of the Object that owns the Resources.
    oid can be an IPSO-defined or LWM2M-defined identifiers in string or in number. Please refer to the lwm2m-id for all pre-defined ids. If oid is not a pre-defined identifer, LWMQN will take it as a private one.

  2. iid (String | Number): Id of the Object Instance that owns the Resource.
    It is common to use numbers to enumerate Object Instances, but using a string for the iid is also accepted, e.g., 12, '12' and 'my_instance01' are all valid.

  3. resrcs (Object): An object with rid-value pairs to describe the Resources.
    Each key is a rid and each value is the corresponding value of the Resource. Resource value can be a primitive, an data object, or an object with specific methods, i.e. read(), write(), exec(). Please refer to section Resources Planning for more details of Resource initialization.

Returns:

  • (Object): qnode

Examples:

  • Resource is a simple value:
// use oid and rids in string
qnode.initResrc('humidity', 0, {
    sensorValue: 20,
    units: 'percent'
});

// use oid and rids in number
qnode.initResrc(3304, 0, {
    5700: 20,
    5701: 'percent'
});

// using string-numbers is ok too
qnode.initResrc('3304', 0, {
    '5700': 20,
    '5701': 'percent'
});
  • Resource value is got from particular operations:
// reading sensed value from an analog interface
qnode.initResrc('mySensor', 0, {
    sensorValue: {
        read: function (cb) {
            // assume readSensor() is an asynchronous function or a callback-style function
            readSensor('aio0', function (val) {
                cb(null, val);
            });
        }
        // if write method is not given, this Resource will be considered as unwritable
    }
});

qnode.initResrc('mySensor', 0, {
    sensorValue: {
        read: function (cb) {
            // if the callback of readSensor() is also in err-first style
            readSensor('aio0', cb);
        }
    }
});

qnode.initResrc('mySensor', 0, {
    sensorValue: {
        read: function (cb) {
            // assume readSensor() is a synchronous function
            try {
                var val = readSensor('aio0');
                cb(null, val);
            } catch (err) {
                cb(err);
            }
        }
    }
});
  • Resource value needs to be written through particular operations:
// writing a value to a digital interface
qnode.initResrc('mySwitch', 0, {
    onOff: {
        // if read method is not given, this Resource will be considered as unreadable
        read: function (cb) {
            // do the read procedure
        },
        write: function (val, cb) {
            gpio.write('gpio06', val);

            var currentVal = gpio.read('gpio06');
            cb(null, currentVal);
        }
    }
});
  • Resource is executable (a procedure on the Client Device that can be remotely called):
qnode.initResrc('myLight', 0, {
    blink: {
        exec: function (t, cb) {
            blinkLed('led1', t);    // bink led1 for t times
            cb(204, null);          // the 2nd argument of cb is a response object
        }
    }
});

qnode.initResrc('myCounter', 0, {
    count: {
        exec: function (cb) {
            countSomething(function (err, sum) {
                // responds back the status 200(OK) and result to the Server
                cb(200, sum);
            });
        }
    }
});

.readResrc(oid, iid, rid[, callback])

Read a value from the allocated Resource.

Arguments:

  1. oid (String | Number): Object id

  2. iid (String | Number): Object Instance id

  3. rid (String | Number): Resource id

  4. callback (Function): An err-fisrt callback function(err, val) to get the read value.

    • If the Resource is not a simple value and there has not a read method been initialized for it, the val passes to callback will be a string _unreadable_, e.g., you are trying to read a write-only Resource which is initialized without a read method.
    • If the Resource is an executable Resource, the val passes to callback will be a string _exec_.
    • If the allocating Resource is not found, an error will be passed to fisrt argument of the callback.

Returns:

  • none

Examples:

qnode.readResrc('humidity', 0, 'sensorValue', function (err, val) {
    if (!err)
        console.log(val);   // 20
});

qnode.readResrc('humidity', 12, 'sensorValue', function (err, val) {
    if (err)
        console.log(err);   // if Resource allocation fails 
});

qnode.readResrc('mySensor', 0, 'sensorValue', function (err, val) {
    console.log(val);       // '_unreadable_', if the Resource cannot be read
});

qnode.readResrc('myLight', 0, 'blink', function (err, val) {
    console.log(val);       // '_exec_', if the Resource is executable which cannot be read from
});

.writeResrc(oid, iid, rid, value[, callback])

Write a value to the allocated Resource.

Arguments:

  1. oid (String | Number): Object id

  2. iid (String | Number): Object Instance id

  3. rid (String | Number): Resource id

  4. value (Depends): The value to write to the allocated Resource.

  5. callback (Function): An err-fisrt callback function(err, val), where val is the value been successfully written.

    • If the Resource is not a simple value and there has not a write method been initialized for it, the val passes to callback will be a string _unwritable_.
    • If the allocating Resource is not found, an error will be passed to fisrt argument of the callback.

Returns:

  • none

Examples:

qnode.writeResrc('humidity', 0, 'sensorValue', 80, function (err, val) {
    console.log(val);       // '_unwritable_'
});

qnode.writeResrc('humidity', 12, 'sensorValue', 80, function (err, val) {
    if (err)
        console.log(err);   // if the Resource does not exist
});

qnode.writeResrc('mySwitch', 0, 'onOff', 1, function (err, val) {
    console.log(val);       // 1
});

.connect(url[, opts])

Connect and register to a LWMQN Server with the given url. If succeeds, qnode will fire a ready event.

Arguments:

  1. url (String): Url of the LWMQN Server, e.g. mqtt://localhost, mqtt://192.168.0.100, mqtt://192.168.0.20:3000.
  2. opts (Object): The connect options with properties given in the following table.
Property Type Default Description
username String 'freebird' The username required by your broker, if any
password String | Buffer 'skynyrd' the password required by your broker, if any
keepalive Number 10 10 seconds, set to 0 to disable
reconnectPeriod Number 3000 milliseconds, interval between two reconnections
connectTimeout Number 30000 milliseconds, time to wait before a CONNACK is received

Returns:

  • (Object): qnode

Examples:

qnode.on('ready', function () {
    // do your work here
});

// use default account
qnode.connect('mqtt://192.168.0.100');

// use your own account
qnode.connect('mqtt://192.168.0.100', {
    username: 'someone',
    password: 'somepassword'
});

// use the MQTT connection options other than defaults
qnode.connect('mqtt://192.168.0.100', {
    keepalive: 30,
    reconnectPeriod: 5000
});

.close([force,] [callback])

Disconnect from the Server. qnode will fire a close event if it is disconnected.

Arguments:

  1. force (Boolean): true will close the client right away, without waiting for the in-flight messages to be acked. This parameter is optional.
  2. callback (Function): Will be called when the Client is closed.

Returns:

  • (Object): qnode

Examples:

qnode.on('close', function () {
    console.log('Disconnected from the Server.');
});

qnode.close();

.pubRegister([callback])

Publish a registering request to the Server. Everytime you invoke connect(), qnode will do regiseter to the Server as well.

Arguments:

  1. callback (Function): function (err, rsp). An err occurs if qnode has no connection to a Server. rsp is a response object with a status code to tell the result of registration. The descriptions of possible rsp.status are given in the following table.
rsp.status Status Description
200 OK The Client was registered before and the record is successfully renewed on the Server
201 Created Registration is successful for this new Client
400 BadRequest Invalid paramter(s) for registration
408 Timeout No response from the Server in 60 secs
409 Conflict Client Id conflicts
500 InternalServerError The Server has some trouble

Returns:

  • (Object): qnode

Examples:

qnode.pubRegister(function (err, rsp) {
    console.log(rsp);  // { status: 201 }
});

.pubDeregister([callback])

Publish a deregistering request to the Server for the Client to leave the network.

Arguments:

  1. callback (Function): function (err, rsp) will be called when deregistering is done. An err occurs if qnode has no connection to a Server. rsp is a response object with a status code to tell the result of deregistration.
rsp.status Status Description
202 Deleted The Client was successfully deregistered
404 NotFound The Client is not found on the Server
408 Timeout No response from the Server in 60 secs
500 InternalServerError The Server has some trouble

Returns:

  • (Object) qnode

Examples:

qnode.pubDeregister(function (err, rsp) {
    console.log(rsp);  // { status: 202 }
});

.setDevAttrs(devAttrs[, callback])

Set device attribues on the qnode.

Arguments:

  1. devAttrs (Object): Device attributes.
    It is just like the devAttrs in the arguments of MqttNode constructor, but any change of clientId and mac is not allowed. If you want to change either clientId or mac, please deregister qnode from the Server and then re-register to it again. Any change of the device attributes will be published with an update message to the Server.
  2. callback (Function): function (err, rsp) will be called when updating procedure is done. An err occurs if qnode has no connection to a Server. rsp is a response object with a status code to tell the result of device attribues updating.
rsp.status Status Code Description
204 Changed The Server successfuly accepted this update message
400 BadRequest There is an unrecognized attribute in the update message
405 MethodNotAllowed If you are trying to change either clientId or mac, you will get this response
408 Timeout No response from the Server in 60 secs
500 InternalServerError The Server has some trouble

Returns:

  • (Object): qnode

Examples:

// this will set the ip on qnode and mqtt-node will publish the update of ip to the Server
qnode.setDevAttrs({
    ip: '192.168.0.211'
}, function (err, rsp) {
    console.log(rsp);   // { status: 204 }
});

.pubNotify(note[, callback])

Publish a notificatoin to the Server. The message note should be a well-formatted data object.

  • Notice that mqtt-node will automatically report notifications to the Server if the Client is observed by the Server. Therefore, use this API when you do have to notify something to the Server aggressively in your application.
  • If you like to publish a Resource, note should be an object with fields of oid, iid, rid and data, where data is the Resource value.
  • If you like to publish an Object Instance, note should be an object with fields of oid, iid and data fields, where data is the Object Instance containing all its Resources. Please refer to LWMQN Notify Channel for more info.

Arguments:

  1. note (Object): A Resource or an Object Instance you like to report to the Server.
  2. callback (Function): function (err, rsp) will be called when the acknowledgement is coming back from the Server.
rsp.status Status Description
204 Changed The Server has got the notification
400 BadRequest Invalid parameters, e.g., oid or iid is not given
404 NotFound The notified Object Instance or Resource cannot be allocated
500 InternalServerError The Server has some trouble

Returns:

  • (Object) qnode

Examples:

// pub a Resource
qnode.pubNotify({
    oid: 'humidity',
    iid: 0,
    rid: 'sensorValue',
    data: 32
}, function (err, rsp) {
    console.log(rsp);   // { status: 204 }
});

// pub an Object Instance
qnode.pubNotify({
    oid: 'humidity',
    iid: 0,
    data: {
        sensorValue: 32,
        units: 'percent'
    }
}, function (err, rsp) {
    console.log(rsp);   // { status: 204 }
});

// pub something that the Server cannot recognize
qnode.pubNotify({
    oid: 'foo',
    iid: 0,
    rid: 'bar',
    data: 200
}, function (err, rsp) {
    console.log(rsp);   // { status: 404 }, 404 NotFound
});

// pub something with invalid format
qnode.pubNotify('Hello World', function (err, rsp) {
    console.log(rsp);   // { status: 400 }, 400 BadRequest
});

.pingServer([callback])

Ping the Server.

Arguments:

  1. callback (Function): function (err, rsp) will be called upon receiving the response. An err occurs if qnode has no connection to the Server. rsp is a response object with a status code to tell the result of pinging. rsp.data is the approximate round trip time in milliseconds.
rsp.status Status Description
200 OK Pinging is successful with rsp.data roundtrip time in ms
408 Timeout No response from the Server in 60 secs. rsp.data will be null

Returns:

  • (Object) qnode

Examples:

qnode.pingServer(function (err, rsp) {
    console.log(rsp);   // { status: 200, data: 16 }, 16ms
});

.publish(topic, message[, options][, callback])

This is a generic method to publish a message to a topic.

If you are using mqtt-shepherd as the LWMQN Server, it accepts a registered Client to publish any message to any topic. In this case, the Server simply acts as an MQTT broker. The publishment is not authorized at the Server if the Client was not successfully registered.

Arguments:

  1. topic (String): Topic to publish to.
  2. message (String | Buffer): Message to publish.
  3. options (Object): Option to publish with, including the properties shown in the following table.
  4. callback (Function): Will be called when the QoS handling completes, or at the next tick if QoS 0.
Property Type Default Description
qos Number 0 QoS level
retain Boolean false Retain flag

Returns:

  • (Object) qnode

Examples:

qnode.publish('foo/bar/greet', 'Hello World!');

.subscribe(topics[, options][, callback])

This is a generic method to subscribe to a topic or topics listed in an array.

If you are using mqtt-shepherd as the LWMQN Server, it accepts the registered Client to subscribe to any topic. In this case, the Server simply acts as an MQTT broker. The generic subscription is not authorized at the Server if the Client was not successfully registered.

Arguments:

  1. topics (String | String[]): The topic(s) to subscribe to.
  2. options (Object): Option to subscribe with, including the property qos which is a Qos level of the subscription. qos is 0 by default.
  3. callback (Function): function (err, granted) callback will be called on suback, where err is a subscrtiption error and granted is an array of objects formatted in { topic, qos }

Returns:

  • (Object) qnode

Examples:

qnode.subscribe('foo/bar/score', function (err, granted) {
    console.log(granted);   // [ { topic: 'foo/bar/score', qos: 0 } ]
});

.unsubscribe(topics[, callback])

This is a generic method to unsubscribe from a topic or topics.

If you are using mqtt-shepherd as the LWMQN Server, the generic unsubscription is not authorized at the Server if the Client was not successfully registered.

Arguments:

  1. topic (String|String[]): Topic(s) to unsubscribe from.
  2. callback (Function): Callback will be fired on unsuback

Returns:

  • (Object) qnode

Examples:

qnode.unsubscribe('foo/bar/score');


7. Code Templates

Here is the document that provides you with many code templates of IPSO-defined devices. Each template gives the code snippet of how to initialize an Object Instance with its oid and iid, and lists every Resource the Object Instance may have.

The following example shows how to create an digital input Object Instance. In the code snippet, commented lines are optional Resources. A phrase < rid = 5500, R, Boolean > tells the access permission and data type of a Resource.

// Create an Object Instance: Digital Input (oid = 3200 or 'dIn')

qnode.initResrc('dIn', 0, {
    dInState: {                     // < rid = 5500, R, Boolean >
        read: function (cb) {}
    },
    // counter: ,                   // < rid = 5501,  R, Integer >
    // dInPolarity: ,               // < rid = 5502, RW, Boolean >
    // debouncePeriod: ,            // < rid = 5503, RW, Integer, ms >
    // edgeSelection: ,             // < rid = 5504, RW, Integer { 1: fall, 2: rise, 3: both } >
    // counterReset: ,              // < rid = 5505,  E, Opaque >
    // appType: ,                   // < rid = 5750, RW, String >
    // sensorType:                  // < rid = 5751,  R, String >
});

About

An ip-based smart thing that plays as a client of mqtt-shepherd server.

License:MIT License


Languages

Language:JavaScript 99.9%Language:Makefile 0.1%