-
Notifications
You must be signed in to change notification settings - Fork 5
Home
Lightweight MQTT machine network (LWMQN) is an open source project that follows part of OMA LWM2M v1.0 specification to meet the minimum requirements of machine network management.
###Server-side and Client-side Libraries:
- LWMQN project provides you with a server-side mqtt-shepherd library and a machine-side mqtt-node library to run your machine network with JavaScript and node.js. By these two libraries and node.js, you can have your own authentication, authorization and encryption subsystems to secure your network easily.
- Communication based on MQTT protocol and Mosca broker.
- Embedded persistence (NeDB) and auto-reloads Client Devices at boot-up.
- Build your IoT network with or without cloud services.
- LWM2M-like interfaces for Client/Server interaction.
- Hierarchical Smart Object data model (IPSO), which leads to a comprehensive and consistent way in describing real-world gadgets.
- Easy to query resources on a Client Device with the URI-style path, and everything has been well-organized to ease the pain for you to create RPC interfaces for your webapps, such as RESTful and websocket-based APIs.
- LWMQN Server is your local machine gateway and application runner. But if you like to let your machines go up cloud, why not? It's node.js!
#### Acronyms and Abbreviations * **Server**: LWMQN server * **Client** or **Client Device**: LWMQN client (machine) * **MqttShepherd**: Class exposed by `require('mqtt-shepherd')` * **MqttNode**: Class to create a software endpoint(proxy) of a remote Client Device on the server * **qserver**: Instance of MqttShepherd Class * **qnode**: Instance of MqttNode Class
$ npm install mqtt-shepherd --save
var MqttShepherd = require('mqtt-shepherd');
var qserver = new MqttShepherd(); // create a LWMQN server
qserver.on('ready', function () {
console.log('Server is ready.');
// when server is ready, allow devices to join the network within 180 secs
qserver.permitJoin(180);
});
qserver.start(function (err) { // start the sever
if (err)
console.log(err);
});
// That's all to start a LWMQN server.
// Now qserver is going to automatically tackle most of the network managing things.
Here is a demo webapp that shows a simple smart home application built with LWMQN.
- This module, mqtt-shepherd, is an implementation of LWMQN server and application framework that can run on platforms equipped with node.js.
- It's easy to allocate and query resources on remote devices with an URI-style path in the shape of
'oid/iid/rid'
. In the following example, both of these two requests is to remotely read the sensed value from a temperature sensor on a machine.qnode.readReq('temperature/0/sensorValue', function (err, rsp) { console.log(rsp); // { status: 205, data: 18 } }); qnode.readReq('3304/0/5700', function (err, rsp) { console.log(rsp); // { status: 205, data: 18 } });
- This moudle provides you with MqttShepherd and MqttNode two major classes.
###MqttShepherd Class
- This class brings you a LWMQN Server with network managing facilities, i.e., permission of device joining, device authentication, reading resources, writing resources, observing resources, and executing procedures on remote devices. This document uses
qserver
to denote the instance of this class. - Each asynchronous API supports both callback style and promise backed by q 1.4.x.
###MqttNode Class
- This is the class for creating a software endpoint(proxy) to represent the remote Client Device at server-side. This document uses
qnode
to denote the instance of this class. You can invoke methods on aqnode
to operate the remote Client. - Each asynchronous API supports both callback style and promise backed by q 1.4.x.
Exposed by require('mqtt-shepherd')
Create a server instance of the MqttShepherd
class. This document will use qserver
to denote the server.
Arguments:
-
name
(String): Server name. A default name'mqtt-shepherd'
will be used if not given. -
settings
(Object): Optional settings for qserver.Property Type Description broker Object Broker settings in shape of { port, backend }
, where backend is apubsubsettings
object given in Mosca wiki page. You can set up your own MQTT backend, like mongoDB, Redis, Mosquitto, or RabbitMQ, through this option.account Object Set default account with a { username, password }
object, where username and password are strings. Default isnull
to accept all incoming Clients.reqTimeout Number Number of milliseconds, a global timeout for all requests. dbPath String Set database file path, default is __dirname + '/database/mqtt.db'
.
Returns:
- (Object): qserver
Examples:
- Create a server and name it
var MqttShepherd = require('mqtt-shepherd');
var qserver = new MqttShepherd('my_iot_server');
- Create a server that starts on a specified port
var qserver = new MqttShepherd('my_iot_server', {
broker: {
port: 9000
}
});
- Create a server with other backend (example from Mosca wiki)
var qserver = new MqttShepherd('my_iot_server', {
broker: {
port: 1883,
backend: { // backend is the pubsubsettings seen in Mosca wiki page
type: 'mongo',
url: 'mongodb://localhost:27017/mqtt',
pubsubCollection: 'ascoltatori',
mongo: {}
}
}
});
- Create a server with a default account. Only Clients connecting with this account is authenticated if you don't have an authentication subsystem.
var qserver = new MqttShepherd('my_iot_server', {
account: {
username: 'skynyrd',
password: 'lynyrd'
}
});
Start qserver.
Arguments:
-
callback
(Function):function (err) { }
. Get called after the initializing procedure is done.
Returns:
- (Promise): promise
Examples:
// callback style
qserver.start(function (err) {
if (!err)
console.log('server initialized.');
});
// promise style
qserver.start().then(function() {
console.log('server initialized.');
}).done();
Stop qserver.
Arguments:
-
callback
(Function):function (err) { }
. Get called after the server closed.
Returns:
- (Promise): promise
Examples:
qserver.stop(function (err) {
if (!err)
console.log('server stopped.');
});
Reset qserver. After qserver restarted, a 'ready'
event will be fired. A hard reset (mode == true
) will clear all joined qnodes in the database.
Arguments:
-
mode
(Boolean):true
for a hard reset, andfalse
for a soft reset. Default isfalse
. -
callback
(Function):function (err) { }
. Get called after the server restarted.
Returns:
- (Promise): promise
Examples:
qserver.on('ready', function () {
console.log('server is ready');
});
// default is soft reset
qserver.reset(function (err) {
if (!err)
console.log('server restarted.');
});
// hard reset
qserver.reset(true, function (err) {
if (!err)
console.log('server restarted.');
});
Allow or disallow devices to join the network. A 'permitJoining` event will be fired every tick of countdown (per second) when qserver is allowing device to join its network.
Arguments:
-
time
(Number): Time in seconds for qserver allowing devices to join the network. Settime
to0
can immediately close the admission.
Returns:
- (Boolean):
true
for a success, otherwisefalse
if qserver is not enabled.
Examples:
qserver.on('permitJoining', function (joinTimeLeft) {
console.log(joinTimeLeft);
});
// allow devices to join for 180 seconds, this will also trigger
// a 'permitJoining' event at each tick of countdown.
qserver.permitJoin(180); // true
Returns qserver information.
Arguments:
- none
Returns:
-
(Object): An object that contains information about the server. Properties in this object are given in the following table.
Property Type Description name String Server name enabled Boolean Server is up( true
) or down(false
)net Object Network information, { intf, ip, mac, routerIp }
devNum Number Number of devices managed by this qserver startTime Number Unix Time (secs from 1970/1/1) joinTimeLeft Number How many seconds left for allowing devices to join the Network
Examples:
qserver.info();
/*
{
name: 'my_iot_server',
enabled: true,
net: {
intf: 'eth0',
ip: '192.168.1.99',
mac: '00:0c:29:6b:fe:e7',
routerIp: '192.168.1.1'
},
devNum: 36,
startTime: 1454419506,
joinTimeLeft: 28
}
*/
List records of the registered qnode(s). This method always returns an array.
Arguments:
-
clientIds
(String | String[]): A single client id or an array of client ids to query for their records. All device records will be returned ifclientIds
is not given.
Returns:
-
(Array): Information of qnodes. Each record in the array is an object with the properties shown in the following table. An element in the array will be
undefined
if the corresponding qnode is not found.Property Type Description clientId String Client id of the qnode joinTime Number Unix Time (secs). When a qnode joined the network. lifetime Number Lifetime of the qnode. If there is no message coming from the qnode within lifetime, qserve will deregister this qnode ip String Ip address of qserver mac String Mac address version String LWMQN version objList Object IPSO Objects and Object Instances. Each key in objList
is theoid
and each value is an array ofiid
under thatoid
.status String 'online'
,'offline'
, or'sleep'
Examples:
console.log(qserver.list([ 'foo_id', 'bar_id', 'no_such_id' ]));
/*
[
{
clientId: 'foo_id', // record for 'foo_id'
joinTime: 1454419506,
lifetime: 12345,
ip: '192.168.1.112',
mac: 'd8:fe:e3:e5:9f:3b',
version: '',
objList: {
3: [ 1, 2, 3 ],
2205: [ 7, 5503 ]
},
status: 'online'
},
{
clientId: 'bar_id', // record for 'bar_id'
joinTime: 1454419706,
lifetime: 12345,
ip: '192.168.1.113',
mac: '9c:d6:43:01:7e:c7',
version: '',
objList: {
3: [ 1, 2, 3 ],
2205: [ 7, 5503 ]
},
status: 'sleep',
},
undefined // record not found for 'no_such_id'
]
*/
console.log(qserver.list('foo_id'));
/* An array will be returned even a single string is argumented.
[
{
clientId: 'foo_id', // record for 'foo_id'
joinTime: 1454419506,
lifetime: 12345,
ip: '192.168.1.112',
mac: 'd8:fe:e3:e5:9f:3b',
version: '',
objList: {
3: [ 1, 2, 3 ],
2205: [ 7, 5503 ]
},
status: 'online'
}
]
*/
Find a registered qnode on qserver by clientId.
Arguments:
-
clientId
(String): Client id of the qnode to find for.
Returns:
- (Object): qnode. Returns
undefined
if not found.
Examples:
var qnode = qserver.find('foo_id');
if (qnode) {
// do something upon the qnode, like qnode.readReq()
}
Find registered qnodes by the specified mac address. This method always returns an array, because there may be many qnodes living in the same machine to share the same mac address.
Arguments:
-
macAddr
(String): Mac address of the qnode(s) to find for. The address is case-insensitive.
Returns:
- (qnode[]): Array of found qnodes. Returns an empty array if not found.
Examples:
var qnodes = qserver.findByMac('9e:65:f9:0b:24:b8');
if (qnodes.length) {
// do something upon the qnodes
}
Deregister and remove a qnode from the network by its clientId.
Arguments:
-
clientId
(String): Client id of the qnode to be removed. -
callback
(Function):function (err, clientId) { ... }
will be called after removal.clientId
is client id of the removed qnode.
Returns:
- (Promise): promise
Examples:
qserver.remove('foo', function (err, clientId) {
if (!err)
console.log(clientId);
});
The qserver can use this method to announce(/broadcast) any message to all qnodes.
Arguments:
-
msg
(String | Buffer): The message to announce. Remember to stringify if the message is a data object. -
callback
(Function):function (err) { ... }
. Get called after message announced.
Returns:
- (Promise): promise
Examples:
qserver.announce('Rock on!');
Listener: function () { }
Fired when qserver is ready.
Listener: function (err) { }
Fired when there is an error occurs.
Listener: function (joinTimeLeft) {}
Fired when qserver is allowing for devices to join the network, where joinTimeLeft
is number of seconds left to allow devices to join the network. This event will be triggered at each tick of countdown (per second).
Listener: function (msg) { }
Fired when there is an incoming indication message. The msg
is an object with the properties given in the table:
Property | Type | Description |
---|---|---|
type | String | Indication type, can be 'devIncoming' , 'devLeaving' , 'devUpdate' , 'devNotify' , 'devChange' , and 'devStatus' . |
qnode | Object | String | qnode instance, except that when type === 'devLeaving' , qnode will be a clientId (since qnode has been removed) |
data | Depends | Data along with the indication, which depends on the type of indication |
-
Fired when there is a qnode incoming to the network. The qnode can be either a new registered one or an old one that logs in again.
- msg.type:
'devIncoming'
- msg.qnode:
qnode
- msg.data:
undefined
- message examples
// example of an Object Instance notification { type: 'devIncoming', qnode: qnode instance }
- msg.type:
-
Fired when there is a qnode leaving the network.
- msg.type:
'devLeaving'
- msg.qnode:
'foo_clientId'
, the clientId of which qnode is leaving - msg.data:
9e:65:f9:0b:24:b8
, the mac address of which qnode is leaving. - message examples
// example of an Object Instance notification { type: 'devLeaving', qnode: 'foo_clientId', data: '9e:65:f9:0b:24:b8' }
- msg.type:
-
Fired when there is a qnode that publishes an update of its device attribute(s).
- msg.type:
'devUpdate'
- msg.qnode:
qnode
- msg.data: An object that contains the updated attribute(s). There may be fields of
status
,lifetime
,ip
, andversion
in this object. - message examples
// example of an Object Instance notification { type: 'devUpdate', qnode: qnode instance, data: { ip: '192.168.0.36', lifetime: 82000 } }
- msg.type:
-
Fired when there is qnode that publishes a notification of its Object Instance or Resource.
- msg.type:
'devNotify'
- msg.qnode:
qnode
- msg.data: Content of the notification. This object has fields of
oid
,iid
,rid
, anddata
.-
data
is an Object Instance ifoid
andiid
are given butrid
is null or undefined -
data
is a Resource ifoid
,iid
andrid
are given (data type depends on the Resource)
-
- message examples
// example of an Object Instance notification { type: 'devNotify', qnode: qnode instance, data: { oid: 'humidity', iid: 0, data: { // Object Instance sensorValue: 32 } } } // example of a Resource notification { type: 'devNotify', qnode: qnode instance, data: { oid: 'humidity', iid: 0, rid: 'sensorValue', data: 32 // Resource value } }
- msg.type:
-
Fired when the Server perceives that there is any change of Resources from notifications or read/write responses.
- msg.type:
'devChange'
- msg.qnode:
qnode
- msg.data: Content of the changes. This object has fields of
oid
,iid
,rid
, anddata
.-
data
is an object that contains only the properties changed in an Object Instance. In this case,oid
andiid
are given butrid
is null or undefined -
data
is the new value of a Resource. If a Resource itself is an object, thendata
will be an object that contains only the properties changed in that Resource. In this case,oid
,iid
andrid
are given (data type depends on the Resource)
-
- message examples
// changes of an Object Instance { type: 'devChange', qnode: qnode instance, data: { oid: 'temperature', iid: 0, data: { sensorValue: 12, minMeaValue: 12 } } } // change of a Resource { type: 'devChange', qnode: qnode instance, data: { oid: 'temperature', iid: 1, rid: 'sensorValue', data: 18 } }
-
Notice!!! The difference between
'devChange'
and'devNotify'
:- Data along with
'devNotify'
is what a qnode like to notify of even if there is nothing changed. A periodical notification is a good example, a qnode has to report something under observation even there is no change of that thing. - If qserver does notice there is really something changed, it will then fire
'devChange'
to report the change(s). It is suggested to use'devChange'
indication to update your GUI views, and to use'devNotify'
indication to log data.
- Data along with
- msg.type:
-
Fired when there is a qnode going online, going offline, or going to sleep.
- msg.type:
'devStatus'
- msg.qnode:
qnode
- msg.data:
'online'
,'sleep'
, or'offline'
- message examples
// example of an Object Instance notification { type: 'devStatus', qnode: qnode instance, data: 'online' }
- msg.type:
Listener: function(topic, message, packet) {}
Fired when the qserver receives any published packet from any remote qnode.
-
topic
(String): topic of the received packet -
message
(Buffer): payload of the received packet -
packet
(Object): the received packet, as defined in mqtt-packet
******************************************** ## MqttNode Class This class is to create proxy instance for each remote Client Device joined the network. An instance of this class is denoted as `qnode` in this document.
### qnode.readReq(path, callback) Remotely read a target from the qnode. Response will be passed through the second argument of the callback.
Arguments:
-
path
(String): Path of the allocated Object, Object Instance, or Resource on the remote qnode. -
callback
(Function):function (err, rsp) { }
-
err
(Object): Error object. -
rsp
(Object): The response is an object that has a status code along with the returned data.
Property Type Description status Number Status code of the response. Possible status codes are 205, 400, 404, 405, and 408. data Depends data
can be the value of an Object, an Object Instance, or a Resource. Note that when an unreadable Resource is read, the returned status will be 405 and data will be a string'_unreadable_'
. -
Returns:
- (Promise): promise
Examples:
qnode.readReq('temperature/1/sensorValue', function (err, rsp) {
console.log(rsp); // { status: 205, data: 87 }
});
// Target not found
qnode.readReq('/noSuchObject/0/foo', function (err, rsp) {
console.log(rsp); // { status: 404, data: undefined }
});
// Target not found
qnode.readReq('/temperature/0/noSuchResource/', function (err, rsp) {
console.log(rsp); // { status: 404, data: undefined }
});
// Target is unreadable
qnode.readReq('/temperature/0/foo', function (err, rsp) {
console.log(rsp); // { status: 405, data: '_unreadable_' }
});
// promise-style
qnode.readReq('temperature/1/sensorValue').then(function (rsp) {
console.log(rsp); // { status: 205, data: 87 }
}).fail(function (err) {
console.log(err);
}).done();
Remotely write a value to the allocated Resource on a qnode. The response will be passed through the second argument of the callback.
Arguments:
-
path
(String): Path of the allocated Resource on the remote qnode. -
val
(Depends): The value to write to the Resource. -
callback
(Function):function (err, rsp) { }
. Thersp
object that has a status code along with the written data.Property Type Description status Number Status code of the response. Possible status codes are 204, 400, 404, 405, and 408. data Depends data
is the written value. It will be a string'_unwritable_'
along with a status code 405 if the Resource is not allowed for writing.
Returns:
- (Promise): promise
Examples:
// write successfully
qnode.writeReq('digitalOutput/0/appType', 'lightning', function (err, rsp) {
console.log(rsp); // { status: 204, data: 'lightning' }
});
qnode.writeReq('digitalOutput/0/dOutState', 0, function (err, rsp) {
console.log(rsp); // { status: 204, data: 0 }
});
// target not found
qnode.writeReq('temperature/0/noSuchResource', 1, function (err, rsp) {
console.log(rsp); // { status: 404, data: undefined }
});
// target is unwritable
qnode.writeReq('digitalInput/1/dInState', 1, function (err, rsp) {
console.log(rsp); // { status: 405, data: '_unwritable_' }
});
// promise-style
qnode.writeReq('digitalOutput/0/appType', 'lightning').then(function (rsp) {
console.log(rsp); // { status: 204, data: 'lightning' }
}).fail(function (err) {
console.log(err);
}).done();
Invoke an executable Resource on the remote qnode. An executable Resource is like a remote procedure call.
Arguments:
-
path
(String): Path of the allocated Resource on the remote qnode. -
args
(Array): The arguments to the procedure. -
callback
(Function):function (err, rsp) { }
. Thersp
object has a status code to indicate whether the operation succeeds. There will be adata
field if the procedure does return something back, and the data type depends on the implementation at client-side.Property Type Description status Number Status code of the response. Possible status codes are 204, 400, 404, 405, 408, and 500. data Depends What will be returned depends on the client-side implementation.
Returns:
- (Promise): promise
Examples:
// assuming there is an executable Resource (procedure) with signature
// function(n) { ... } to blink an LED n times.
qnode.executeReq('led/0/blink', [ 10 ] ,function (err, rsp) {
console.log(rsp); // { status: 204 }
});
// assuming there is an executable Resource with singnatue
// function(edge, duration) { ... } to count how many times the button
// was pressed within `duration` seconds.
qnode.executeReq('button/0/blink', [ 'falling', 20 ] ,function (err, rsp) {
console.log(rsp); // { status: 204, data: 71 }
});
// Something went wrong at remote qnode
qnode.executeReq('button/0/blink', [ 'falling', 20 ] ,function (err, rsp) {
console.log(rsp); // { status: 500 }
});
// arguments cannot be recognized, in this example, 'up' is an invalid parameter
qnode.executeReq('button/0/blink', [ 'up', 20 ] ,function (err, rsp) {
console.log(rsp); // { status: 400 }
});
// Resource not found
qnode.executeReq('temperature/0/noSuchResource', function (err, rsp) {
console.log(rsp); // { status: 404 }
});
// invoke an unexecutable Resource
qnode.executeReq('temperature/0/sensorValue', function (err, rsp) {
console.log(rsp); // { status: 405 }
});
// promise-style
qnode.executeReq('button/0/blink', [ 'falling', 20 ]).then(function (rsp) {
console.log(rsp); // { status: 204, data: 71 }
}).fail(function (err) {
console.log(err);
}).done();
Configure the report settings of a Resource, an Object Instance, or an Object. This method can also be used to cancel an observation by assigning the attrs.cancel
to true
.
Note
- This API won't start reporting of notifications, call observe() method if you want to turn the reporting on.
Arguments:
-
path
(String): Path of the allocated Resource, Object Instance, or Object on the remote qnode. -
attrs
(Object): Parameters of the report settings.Property Type Mandatory Description pmin Number optional Minimum Period. Minimum time in seconds the qnode should wait from the time when sending the last notification to the time when sending a new notification. pmax Number optional Maximum Period. Maximum time in seconds the qnode should wait from the time when sending the last notification to the time sending the next notification (regardless if the value has changed). gt Number optional Greater Than. The qnode should notify its value when the value is greater than this setting. Only valid for the Resource typed as a number. lt Number optional Less Than. The qnode should notify its value when the value is smaller than this setting. Only valid for the Resource typed as a number. stp Number optional Step. The qnode should notify its value when the change of the Resource value, since the last report happened, is greater than this setting. cancel Boolean optional Set to true
for a qnode to cancel observation on the allocated Resource or Object Instance. -
callback
(Function):function (err, rsp) { }
. Thersp
object has a status code to indicate whether the operation is successful.Property Type Description status Number Status code of the response. Possible status codes are 204, 400, 404, and 408.
Returns:
- (Promise): promise
Examples:
// set successfully
qnode.writeAttrsReq('temperature/0/sensorValue', {
pmin: 10,
pmax: 600,
gt: 45
}, function (err, rsp) {
console.log(rsp); // { status: 200 }
});
// cancel the observation on a Resource
qnode.writeAttrsReq('temperature/0/sensorValue', {
cancel: true
}, function (err, rsp) {
console.log(rsp); // { status: 200 }
});
// target not found
qnode.writeAttrsReq('temperature/0/noSuchResource', {
gt: 20
}, function (err, rsp) {
console.log(rsp); // { status: 404 }
});
// parameter cannot be recognized
qnode.writeAttrsReq('temperature/0/noSuchResource', {
foo: 60
}, function (err, rsp) {
console.log(rsp); // { status: 400 }
});
// promise-style
qnode.writeAttrsReq('temperature/0/sensorValue', {
pmin: 10,
pmax: 600,
gt: 45
}).then(function (rsp) {
console.log(rsp); // { status: 200 }
}).fail(function (err) {
console.log(err);
}).done();
Discover report settings of a Resource or, an Object Instance ,or an Object on the remote qnode.
Arguments:
-
path
(String): Path of the allocated Resource, Object Instance, or Object on the remote qnode. -
callback
(Function):function (err, rsp) { }
. Thersp
object has a status code along with the parameters of report settings.Property Type Description status Number Status code of the response. Possible status codes are 205, 400, 404, 408, and 408. data Object data
is an object of the report settings. If the discovered target is an Object, there will be an additional fielddata.resrcList
to list all its Resource identifiers under each Object Instance.
Returns:
- (Promise): promise
Examples:
// discover a Resource successfully
qnode.discoverReq('temperature/0/sensorValue', function (err, rsp) {
console.log(rsp); // { status: 205, data: { pmin: 10, pmax: 600, gt: 45 }
});
// discover an Object successfully
qnode.discoverReq('temperature/', function (err, rsp) {
console.log(rsp);
/*
{
status: 205,
data: {
pmin: 10,
pmax: 600,
resrcList: {
0: [ 1, 3, 88 ], // Instance 0 has Resources 1, 3, and 88
1: [ 1, 2, 6 ] // Instance 1 has Resources 1, 2, and 6
}
}
}
*/
});
// promise-style
qnode.discoverReq('temperature/0/sensorValue').then(function (rsp) {
console.log(rsp); // { status: 205, data: { pmin: 10, pmax: 600, gt: 45 }
}).fail(function (err) {
console.log(err);
}).done();
Start observing a Resource on the remote qnode. Please listen to event 'ind'
with type of 'devNotify'
to get the reports.
Arguments:
-
path
(String): Path of the allocated Resource on the remote qnode. -
opt
(Number): Set to1
to cancel the observation. Default is0
to enable the observation. -
callback
(Function):function (err, rsp) { }
. Thersp
object has a status code to indicate whether the operation succeeds.Property Type Description status Number Status code of the response. Possible status codes are 205, 400, 404, 408, and 408.
Returns:
- (Promise): promise
Examples:
// observation starts successfully
qnode.observeReq('temperature/0/sensorValue', function (err, rsp) {
console.log(rsp); // { status: 205 }
});
// An Object is not allowed for observation
qnode.observeReq('temperature/', function (err, rsp) {
console.log(rsp); // { status: 400 }
});
// target is not allowed for observation
qnode.observeReq('temperature/0', function (err, rsp) {
console.log(rsp); // { status: 405 }
});
// target not found
qnode.observeReq('temperature/0/noSuchResource', function (err, rsp) {
console.log(rsp); // { status: 404 }
});
// promise-style
qnode.observeReq('temperature/0/sensorValue').then(function (rsp) {
console.log(rsp); // { status: 205 }
}).fail(function (err) {
console.log(err);
}).done();
Ping the remote qnode.
Arguments:
-
callback
(Function):function (err, rsp) { }
. Thersp
is a response object with a status code to tell the result of pinging.rsp.data
is the approximate round-trip time in milliseconds.Property Type Description status Number Status code of the response. Possible status code is 200 and 408. data Number Approximate round trip time in milliseconds.
Returns:
- (Promise): promise
Examples:
qnode.pingReq(function (err, rsp) {
if (!err)
console.log(rsp); // { status: 200, data: 12 }, round-trip time is 12 ms
});
qnode.pingReq(function (err, rsp) {
if (!err)
console.log(rsp); // { status: 408 }, request timeout
});
// promise-style
qnode.pingReq().then(function (rsp) {
console.log(rsp); // { status: 200, data: 12 }, round-trip time is 12 ms
}).fail(function (err) {
console.log(err);
}).done();
Maintain this qnode. This will refresh its record on qserver by rediscovering the remote qnode.
Arguments:
-
callback
(Function):function (err, lastTime) { ... }
. Get called with the timestamplastTime
(ms) after this maintenance finished. An error occurs when the request is timeout or the qnode is offline.
Returns:
- (Promise): promise
Examples:
qnode.maintain(function (err, lastTime) {
if (!err)
console.log(lastTime); // 1470192227322 (ms, from 1970/1/1)
});
// promise-style
qnode.maintain().then(function (lastTime) {
console.log(lastTime); // 1470192227322 (ms, from 1970/1/1)
}).fail(function (err) {
console.log(err);
}).done();
Synchronously dump qnode record.
Arguments:
- none
Returns:
- (Object): A data object of qnode record.
Property | Type | Description |
---|---|---|
clientId | String | Client id of the device |
ip | String | Ip address of the server |
mac | String | Mac address |
lifetime | Number | Lifetime of the device |
version | String | LWMQN version |
joinTime | Number | Unix Time (secs) |
objList | Object | Resource ids of each Object Instance |
so | Object | Contains IPSO Object(s) |
Examples:
qnode.dump();
/*
{
clientId: 'foo_id',
ip: '192.168.1.114',
mac: '9e:65:f9:0b:24:b8',
lifetime: 86400,
version: 'v0.0.1',
joinTime: 1460448761,
objList: {
'1': [ 0 ],
'3': [ 0 ],
'4': [ 0 ],
'3303': [ 0, 1 ],
'3304': [ 0 ]
},
so: {
lwm2mServer: { // oid is 'lwm2mServer' (1)
'0': {
shortServerId: null,
lifetime: 86400,
defaultMinPeriod: 1,
defaultMaxPeriod: 60,
regUpdateTrigger: '_unreadable_'
}
},
device: { // oid is 'device' (3)
'0': {
manuf: 'LWMQN_Project',
model: 'dragonball',
reboot: '_unreadable_',
availPwrSrc: 0,
pwrSrcVoltage: 5,
devType: 'Env Monitor',
hwVer: 'v0.0.1',
swVer: 'v0.2.1'
}
},
connMonitor: { // oid is 'connMonitor' (4)
'0': {
ip: '192.168.1.114',
routeIp: ''
}
},
temperature: { // oid is 'temperature' (3303)
0: { // iid = 0
sensorValue: 18, // rid = 'sensorValue' (5700), its value is 18
appType: 'home' // rid = 'appType' (5750), its value is 'home'
},
1: {
sensorValue: 37,
appType: 'fireplace'
}
},
humidity: { // oid is 'humidity' (3304)
0: {
sensorValue: 26,
appType: 'home'
}
}
}
}
*/
By default, qserver won't encrypt the message. You can override the qserver.encrypt() and qserver.decrypt() methods to implement your own message encryption and decryption. If you did, you should implement the encrypt() and decrypt() methods at your remote Client Devices as well.
Note: You may like to distribute pre-configured keys to your Clients and utilize the authentication approach to build your own security subsystem.
Method of encryption. Overridable.
Arguments:
-
msg
(String | Buffer): The outgoing message. -
clientId
(String): Indicates the Client of this message going to. -
cb
(Function):function (err, encrypted) {}
, the callback you should call and pass the encrypted message to it after encryption.
Method of decryption. Overridable.
Arguments:
-
msg
(Buffer): The incoming message which is a raw buffer. -
clientId
(String): Indicates the Client of this message coming from. -
cb
(Function):function (err, decrypted) {}
, the callback you should call and pass the decrypted message to it after decryption.
Encryption/Decryption Example:
var qserver = new MqttShepherd('my_iot_server');
// In this example, I simply encrypt the message with a constant password 'mysecrete'.
// You may like to get the password according to different qnodes by `clientId` if you have
// a security subsystem.
qserver.encrypt = function (msg, clientId, cb) {
var msgBuf = new Buffer(msg),
cipher = crypto.createCipher('aes128', 'mysecrete'),
encrypted = cipher.update(msgBuf, 'binary', 'base64');
try {
encrypted += cipher.final('base64');
cb(null, encrypted);
} catch (err) {
cb(err);
}
};
qserver.decrypt = function (msg, clientId, cb) {
msg = msg.toString();
var decipher = crypto.createDecipher('aes128', 'mysecrete'),
decrypted = decipher.update(msg, 'base64', 'utf8');
try {
decrypted += decipher.final('utf8');
cb(null, decrypted);
} catch (err) {
cb(err);
}
};
Override methods within qserver.authPolicy
to authorize a Client. These methods include authenticate()
, authorizePublish()
, and authorizeSubscribe()
.
Method of user authentication. Override at will.
The default implementation authenticates all Clients.
Arguments:
-
client
(Object): A mqtt client instance from Mosca. -
username
(String): Username given by a qnode during connection. -
password
(Buffer): Password given by a qnode during connection. -
cb
(Function):function (err, valid) {}
, the callback you should call and pass a boolean flagvalid
to tell if this qnode is authenticated.
Example:
qserver.authPolicy.authenticate = function (client, username, password, cb) {
var authorized = false,
clientId = client.id;
// This is just an example.
queryUserFromSomewhere(username, function (err, user) { // maybe query from a local database
if (err) {
cb(err);
} else if (username === user.name && password === user.password) {
client.user = username;
authorized = true;
cb(null, authorized);
} else {
cb(null, authorized);
}
});
};
Method of authorizing a Client to publish to a topic. Override at will.
The default implementation authorizes every Client, that was successfully registered, to publish to any topic.
Arguments:
-
client
(Object): A mqtt client instance from Mosca. -
topic
(String): The topic to publish to. -
payload
(String | Buffer): The data to publish out. -
cb
(Function):function (err, authorized) {}
, the callback you should call and pass a boolean flagauthorized
to tell if a Client is authorized to publish the topic.
Example:
qserver.authPolicy.authorizePublish = function (client, topic, payload, cb) {
var authorized = false,
clientId = client.id,
username = client.user;
// This is just an example.
passToMyAuthorizePublishSystem(clientId, username, topic, function (err, authorized) {
cb(err, authorized);
});
};
Method of authorizing a Client to subscribe to a topic. Override at will.
The default implementation authorizes every Client, that was successfully registered, to subscribe to any topic.
Arguments:
-
client
(Object): A mqtt client instance from Mosca. -
topic
(String): The topic to subscribe to. -
cb
(Function):function (err, authorized) {}
, the callback you should call and pass a boolean flagauthorized
to tell if a Client is authorized to subscribe to the topic.
Example:
qserver.authPolicy.authorizeSubscribe = function (client, topic, cb) {
var authorized = false,
clientId = client.id,
username = client.user;
// This is just an example.
passToMyAuthorizeSubscribeSystem(clientId, username, topic, function (err, authorized) {
cb(err, authorized);
});
};
Please refer to Mosca Wiki to learn more about Authentication & Authorization
Status Code | Description |
---|---|
200 (OK) | Everything is fine |
204 (Changed) | The remote qnode accepted this writing request successfully |
400 (BadRequest) | There is an unrecognized attribute/parameter within the request message |
404 (NotFound) | The qnode is not found |
405 (MethodNotAllowed) | If you are trying to change either clientId or mac , to read something unreadable, to write something unwritable, and execute something unexecutable, then you will get this response |
408 (Timeout) | Request timeout |
500 (InternalServerError) | The remote qnode has some trouble |
Like many node.js modules do, mqtt-shepherd utilizes debug module to print out messages that may help in debugging. The namespaces include mqtt-shepherd
, mqtt-shepherd:init
, mqtt-shepherd:request
, and mqtt-shepherd:msgHdlr
. The mqtt-shepherd:request
logs requests that qserver sends to qnodes, and mqtt-shepherd:msgHdlr
logs the requests that comes from qnodes.
If you like to print the debug messages, run your app.js with the DEBUG environment variable:
$ DEBUG=mqtt-shpeherd* app.js # use wildcard to print all mqtt-shepherd messages
$ DEBUG=mqtt-shpeherd:msgHdlr app.js # if you are only interested in mqtt-shpeherd:msgHdlr messages
Example:
simen@ubuntu:~/develop/mqtt-shepherd$ DEBUG=mqtt-shepherd* node server.js
mqtt-shepherd:init mqtt-shepherd booting... +0ms
mqtt-shepherd:init Loading qnodes from database done. +26ms
mqtt-shepherd:init Broker is up. +64ms
mqtt-shepherd:init Auth policy is set. +32ms
mqtt-shepherd:init Create a mqtt client for shepherd. +42ms
mqtt-shepherd:init Internal pub/sub testing done. +848ms
mqtt-shepherd:init mqtt-shepherd is up and ready. +2ms
mqtt-shepherd:msgHdlr REQ <-- register, transId: 101 +5s
mqtt-shepherd:request REQ --> read, transId: 0 +11ms
mqtt-shepherd:request REQ --> read, transId: 1 +3ms
mqtt-shepherd:request REQ --> read, transId: 2 +1ms
mqtt-shepherd:request REQ --> read, transId: 3 +0ms
mqtt-shepherd:request RSP <-- read, transId: 0, status: 205 +33ms
mqtt-shepherd:request RSP <-- read, transId: 1, status: 205 +40ms
mqtt-shepherd:request RSP <-- read, transId: 3, status: 205 +0ms
mqtt-shepherd:request RSP <-- read, transId: 2, status: 205 +32ms
mqtt-shepherd:msgHdlr RSP --> register, transId: 101, status: 201 +39ms
mqtt-shepherd:msgHdlr REQ <-- schedule, transId: 102 +4s
mqtt-shepherd:msgHdlr RSP --> schedule, transId: 102, status: 200 +1ms
mqtt-shepherd:request REQ --> write, transId: 4 +2s
...
mqtt-shepherd:request RSP <-- ping, transId: 5, status: 200 +1ms
mqtt-shepherd:request REQ --> discover, transId: 11 +7ms