Skip to content
peter edited this page Apr 8, 2019 · 28 revisions



1. Overview

OMA Lightweight M2M (LWM2M) is a resource constrained device management protocol relies on CoAP. And CoAP is an application layer protocol that allows devices to communicate with each other RESTfully over the Internet.

coap-shepherd, coap-node and lwm2m-bs-server modules aim to provide a simple way to build and manage a LWM2M machine network.

coap-shepherd net

LWM2M Server: coap-shepherd

  • It is a LWM2M Server application framework running on node.js.
  • It follows most parts of LWM2M specification to meet the requirements of a machine network and devices management.
  • It works well with Leshan and Wakaama.
  • Supports functionalities, such as permission of device joining, reading resources, writing resources, observing resources, and executing a procedure on a remote device.
  • It follows IPSO data model to let you allocate and query resources on remote devices with semantic URIs in a comprehensive manner. Here is an example, all these requests is to read a value from the same resource but with different addressing style.
// numeric style
cnode.readReq('/3303/0/5700', function (err, rsp) {
    console.log(rsp);   // { status: '2.05', data: 21 }
});

// semantic style
cnode.readReq('/temperature/0/sensedValue', function (err, rsp) {
    console.log(rsp);   // { status: '2.05', data: 21 }
});

// hybrid style
cnode.readReq('/temperature/0/5700', function (err, rsp) {
    console.log(rsp);   // { status: '2.05', data: 21 }
});

Note:

  • You can find all pre-defined IPSO/OMA-LWM2M identifiers from lwm2m-id module. You are also welcome to use your own private identifiers in coap-shepherd.
  • Here is the client-side module coap-node that can help you with implementing a LWM2M client node. coap-node uses IPSO data model to well organize and define resources on a machine node.

Acronyms and Abbreviations

  • Server: LWM2M Server (server running with coap-shepherd)
  • Client or Client Device: LWM2M Client (machine running with coap-node)
  • Bootstrap Server: LWM2M Bootstrap Server (bootstrap server running with lwm2m-bs-server)
  • cserver: instance of CoapShepherd Class
  • cnode: instance of CoapNode Class



2. Features

  • Constrained Application Protocol (CoAP)
  • Based on node-coap, a node.js CoAP client/server library
  • CoAP network and devices management
  • Hierarchical data model in Smart-Object-style (IPSO)
  • Client/server interaction through LWM2M-defined interfaces



3. Installation

$ npm install coap-shepherd --save



4. Usage

This example shows how to start a server and allow devices to join the network within 180 seconds after the server is ready:

var cserver = require('coap-shepherd');

cserver.on('ready', function () {
    console.log('Server is ready.');

    // when server is ready, allow devices to join the network within 180 secs
    cserver.permitJoin(180);  
});

cserver.start(function (err) {  // start the server
    if (err)
        console.log(err);
});

// That's all to start a LWM2M server.
// Now cserver is going to automatically tackle most of the network managing things.

Or you can pass a config object as an argument to the CoapShepherd constructor and instance the CoapShepherd by yourself:

var CoapShepherd = require('coap-shepherd').constructor;
var cshepherd = new CoapShepherd({
    connectionType: 'udp6',
    port: 5500,
    defaultDbPath: __dirname + '/../lib/database/myShepherd.db'
});



5. CoapShepherd Class

Exposed by require('coap-shepherd')

  • This class brings you a LWM2M Server with network managing facilities. This document uses cserver to denote the instance of this class.
  • Server default port is 5683.
  • Server configuration is read from file config.js in the lib folder of this module.



cserver.start([callback])

Start the cserver.

Arguments:

  1. callback (Function): function (err) { }. Get called after starting procedure is done.

Returns:

  • (none)

Examples:

cserver.start(function (err) {
    console.log('server is started.');
});



cserver.stop([callback])

Stop the cserver.

Arguments:

  1. callback (Function): function (err) { }. Get called after stopping procedure is done.

Returns:

  • (none)

Examples:

cserver.stop(function (err) {
    console.log('server stopped.');
});



cserver.reset([callback])

Reset the cserver.

Arguments:

  1. callback (Function): function (err) { }. Get called after the server restarted.

Returns:

  • (none)

Examples:

cserver.reset(function (err) {
    if (!err) 
        console.log('server restarted.');
});



cserver.permitJoin(time)

Allow or disallow devices to join the network.

Arguments:

  1. time (Number): Time in seconds for csever to allow devices to join the network. Set time to 0 can immediately close the admission.

Returns:

  • (_Boolea_n): true for a success, otherwise false if cserver is not enabled.

Examples:

cserver.permitJoin(180);    // true 



cserver.list()

List records of the registered Client Devices.

Arguments:

  1. none

Returns:

  • (Array): Information of Client Devices. Each record in the array is an object with the properties shown in the following table.

    Property Type Description
    clientName String Client name of the device.
    clientId String Client id of the device.
    lifetime Number Lifetime of the device.
    version String LWM2M version.
    ip String Ip address of the device.
    mac String Mac adderss of the device.
    port Number Port of the device.
    objList Object The list of Objects supported and Object Instances available on the device.
    observedList Object The list of observing Object Instance or Resource on the device.
    status String 'online', 'offline', or 'sleep'

Examples:

console.log(cserver.list());

/*
[
    { 
        clientName: 'node1',
        clientId: 1,
        lifetime: 86400,
        version: '1.0.0',
        ip: '192.168.1.112',
        mac: '00:0c:29:3c:fc:7d',
        port: 56643,
        objList: {
            '1': ['0'],
            '3303': ['0']
        },
        observedList: [ '/temperature/0/sensorValue' ],
        status: 'online'
    }, { 
        clientName: 'node2',
        clientId: 2,
        lifetime: 86400,
        version: '1.0.0',
        ip: '192.168.1.111',
        mac: '30:1a:9f:5d:af:c8',
        port: 56643,
        objList: {
            '1': ['0'],
            '3303': ['0']
        },
        observedList: [],
        status: 'offline'
    }
] 
*/



cserver.find(clientName)

Find a registered Client Device (cnode) on cserver.

Arguments:

  1. clientName (String): Name of the Client Device to find for.

Returns:

  • (Object): cnode. Returns undefined if not found.

Examples:

var cnode = cserver.find('foo_name');

if (cnode) {
    // do something upon the cnode, like cnode.readReq()
}



cserver.remove(clientName[, callback])

Deregister and remove a cnode from cserver.

Arguments:

  1. clientName (String): Name of the Client Device to be removed.
  2. callback (Function): function (err, clientName) { }. Get called after the removal is done. clientName is client name of the removed cnode.

Returns:

  • (none)

Examples:

cserver.remove('foo_name', function (err, clientName) {
    if (!err)
        console.log(clientName);
});



cserver.announce(msg[, callback])

The cserver can use this method to announce(/broadcast) any message to all Client Devices.

Arguments:

  1. msg (String): The message to announce.
  2. callback (Function): function (err) { }. Get called after message announced.

Returns:

  • (none)

Examples:

cserver.announce('Hum!');



cserver.acceptDevIncoming(predicate)

coap-shepherd default accept all registered devices to join the network. If you only want to accept some devices. You can use this api to set a predicate function to decide whether to register the Client Device to join the network.

Arguments:

  1. predicate (Function): function (devInfo, callback) {}. Predicate function.

    • devInfo: An object with Client Device information shown in the following table.
    Property Type Description
    clientName String Client name of the device.
    lifetime Number Lifetime of the device.
    version String LWM2M version.
    ip String Ip address of the device.
    mac String Mac adderss of the device.
    port Number Port of the device.
    objList Object The list of Objects supported and Object Instances available on the device.
    • callback: function (err, accept) {} It should fill true or false to accept to let cserver know whether or not to add the Client Device to the network.

Returns:

  • (none)

Examples:

cserver.acceptDevIncoming(function (devInfo, callback) {
    if (devInfo.ip === '192.168.10.21') {
        callback(null, false);   // Not accept dev incoming
    }

    callback(null, true);  // Accept dev incoming
});



Event: 'ready'

function () { }
Fired when cserver is ready.



Event: 'error'

function (err) { }
Fired when there is an error occurs.



Event: 'permitJoining'

function (joinTimeLeft) { }
Fired when cserver 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.



Event: 'ind'

function (msg) { }
Fired when there is an incoming indication message. There are 5 types of indication including 'devIncoming', 'devLeaving', 'devUpdate', 'devStatus', and 'devNotify'.

  • devIncoming
    Fired when a Client Device registers to cserver.

    • msg.type: 'devIncoming'

    • msg.cnode: cnode, the cnode instance of which cnode is incoming

    • msg.data: undefined

    • message examples

      {
          type: 'devIncoming',
          cnode: cnode instance
      }
  • devLeaving
    Fired when a Client Device deregisters from cserver.

    • msg.type: 'devLeaving'

    • msg.cnode: 'foo_name', the clientName of which cnode is leaving

    • msg.data: 30:1a:9f:5d:af:c8, the mac address of which cnode is leaving.

    • message examples

      {
          type: 'devLeaving',
          cnode: 'foo_name',
          data: '30:1a:9f:5d:af:c8'
      }
  • devUpdate
    Fired when a Client Device updates its device attributes.

    • msg.type: 'update'

    • msg.cnode: cnode

    • msg.data: this object at least has a device field to denote the name of a Client Device, and it may have fields of lifetime, objList, ip, and port.

    • message examples

      {
          type: 'devUpdate',
          cnode: cnode instance,
          data: {
              lifetime: 42000
          }
      }
  • devStatus
    Fired when there is a cnode going online, going offline, or going to sleep.

    • msg.type: 'devStatus'

    • msg.cnode: cnode

    • msg.data: 'online', 'offline', or 'sleep'

    • message examples

      {
          type: 'devStatus',
          cnode: cnode instance,
          data: 'offline'
      }
  • devNotify
    Fired upon receiving an notification of Object Instance or Resource from a Client Device.

    • msg.type: 'devNotify'

    • msg.cnode: cnode

    • msg.data: notification from a Client Device. This object has fields of device, path, and value.

    • message examples

      // A Resource notification
      {
          type: 'devNotify',
          cnode: cnode instance,
          data: {
              path: '/temperature/0/sensorValue',
              value: 21
          }
      }
      
      // An Object Instance notification
      {
          type: 'devNotify',
          cnode: cnode instance,
          data: {
              path: '/temperature/0',
              value: {
                  sensorValue: 21
              }  
          }
      }



CoapNode Class

  • This class provides you with methods to perform remote operations upon a registered Client Device. This document uses cserver to denote the instance of this class.
  • You can use cserver.find() with a clientName to find the registered cnode on cserver. And you can invoke methods on the cnode to operate the remote device.



cnode.readReq(path[, callback])

Remotely read an Object, an Object Instance, or a Resource from the Client Device.

Arguments:

  1. path (String): path of the allocated Object, Object Instance or Resource on the remote Client Device.

  2. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.05', '4.04', '4.05' and '4.08'.

      rsp.status Status Description
      '2.05' Content Read operation is completed successfully.
      '4.04' Not Found Target is not found on the Client.
      '4.05' Not Allowed Target is not allowed for Read operation.
      '4.08' Timeout No response from the Client in 60 secs.
    • rsp.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 status code will be '4.05' and the returned value will be a string '_unreadable_'.

Returns:

  • (none)

Examples:

// read a Resource
cnode.readReq('/temperature/0/sensedValue', function (err, rsp) {
    console.log(rsp);   // { status: '2.05', data: 21 }
});

// read an Object Instance
cnode.readReq('/temperature/0', function (err, rsp) {
    console.log(rsp);

    // {
    //    status: '2.05',
    //    data: { 
    //      sensedValue: 21 
    //    } 
    // }
});

// read an Object
cnode.readReq('/temperature', function (err, rsp) {
    console.log(rsp);

    // {
    //    status: '2.05',
    //    data: { 
    //      0: { 
    //            sensedValue: 21 
    //         } 
    //    }
    // }
});

// target not found
cnode.readReq('/temperature/0/foo', function (err, rsp) {
    console.log(rsp);   // { status: '4.04' }
});

// target is unreadable
cnode.readReq('/temperature/0/bar', function (err, rsp) {
    console.log(rsp);   // { status: '4.05', data: '_unreadable_' }
});



cnode.discoverReq(path[, callback])

Discover report settings of an Object, an Object Instance, or a Resource on the Client Device.

Arguments:

  1. path (String): path of the allocated Object, Object Instance, or Resource on the remote Client Device.

  2. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.05', '4.04' and '4.08'.

      rsp.status Status Description
      '2.05' Content Discover operation is completed successfully.
      '4.04' Not Found Target is not found on the Client.
      '4.08' Timeout No response from the Client in 60 secs.
    • rsp.data (Object): This is an object of the report settings. data.attrs contains parameters of the setting. If the discovered target is an Object, there will be an additional field data.resrcList to list all its Resource identifiers under each Object Instance.

Returns:

  • (none)

Examples:

// discover a Resource
cnode.discoverReq('/temperature/0/sensedValue', function (err, rsp) {
    console.log(rsp);

    // {
    //    status: '2.05',
    //    data: {
    //      attrs: { 
    //        pmin: 10, 
    //        pmax: 90,
    //        gt: 0
    //      }
    //    }
    // }
});

// discover an Object
cnode.discoverReq('/temperature', function (err, rsp) {
    console.log(rsp);

    // {
    //    status: '2.05',
    //    data: {
    //      attrs: { 
    //        pmin: 1, 
    //        pmax: 60
    //      },
    //      resrcList: {
    //          0: [ 'sensorValue', 'units' ]
    //      }
    //    }
    // }

// target not found
cnode.discoverReq('/temperature/0/foo', function (err, rsp) {
    console.log(rsp);   // { status: '4.04' }
});



cnode.writeReq(path, data[, callback])

Remotely write a data to an Object Instance or a Resource on the Client Device.

Arguments:

  1. path (String): path of the allocated Object Instance or Resource on the remote Client Device.

  2. data (Depends): data to write to the Object Instance or the Resource. If target is a Object Instance, then the data is an Object Instance containing the Resource values.

  3. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.04', '4.00', '4.04', '4.05' and '4.08'.

Returns:

  • (none)

Examples:

// target is a Resource
cnode.writeReq('/temperature/0/sensedValue', 19, function (err, rsp) {
    console.log(rsp);   // { status: '2.04' }
});

// target is a Object Instance
cnode.writeReq('/temperature/0', { sensedValue: 87, units: 'F' }, function (err, rsp) {
    console.log(rsp);   // { status: '2.04' }
});

// target not found
cnode.writeReq('/temperature/0/foo', 19, function (err, rsp) {
    console.log(rsp);   // { status: '4.04' }
});

// target is unwritable
cnode.writeReq('/temperature/0/bar', 19, function (err, rsp) {
    console.log(rsp);   // { status: '4.05' }
});



cnode.writeAttrsReq(path, attrs[, callback])

Configure the parameters of the report settings of an Object, an Object Instance, or a Resource.

Arguments:

  1. path (String): path of the allocated Object, Object Instance, or Resource on the remote Client Device.

  2. attrs (Object): parameters of the report settings.

    Property Type Required Description
    pmin Number No Minimum Period. Minimum time in seconds the Client Device should wait between two notifications.
    pmax Number No Maximum Period. Maximum time in seconds the Client Device should wait between two notifications. When maximum time expires after the last notification, a new notification should be sent.
    gt Number No Greater Than. The Client Device should notify the Server each time the Observed Resource value greater than this setting with respect to pmin parameter. Only valid for the Resource typed as a number.
    lt Number No Less Than. The Client Device should notify the Server each time the Observed Resource value less than this setting with respect to pmin parameter. Only valid for the Resource typed as a number.
    stp Number No Step. The Client Device should notify the Server when the change of the Resource value, since the last report happened, is greater or equal to this setting. Only valid for the Resource typed as a number.
  3. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.04', '4.04', '4.05' and '4.08'.

Returns:

  • (none)

Examples:

cnode.writeAttrsReq('/temperature/0/sensedValue', { pmin: 10, pmax: 90, gt: 0 }, function (err, rsp) {
    console.log(rsp);   // { status: '2.04' }
});

// target not found
cnode.writeAttrsReq('/temperature/0/foo', { lt: 100 }, function (err, rsp) {
    console.log(rsp);   // { status: '4.04' }
});

// parameter cannot be recognized
cnode.writeAttrsReq('/temperature/0/sensedValue', { foo: 0 }, function (err, rsp) {
    console.log(rsp);   // { status: '4.00' }
});



cnode.executeReq(path[, args][, callback])

Invoke an executable Resource on the Client Device. An executable Resource is like a remote procedure call.

Arguments:

  1. path (String): path of the allocated Resource on the remote Client Device.

  2. args (Array): arguments to the procedure.

  3. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.04', '4.00', '4.04', '4.05' and '4.08'.

Returns:

  • (none)

Examples:

// assume there in an executable Resource with the signature
// function(t) { ... } to blink an LED t times.
cnode.executeReq('/led/0/blink', [ 10 ] ,function (err, rsp) {
    console.log(rsp);   // { status: '2.04' }
});

// target not found
cnode.executeReq('/temperature/0/foo', function (err, rsp) {
    console.log(rsp);   // { status: '4.04' }
});

// target is unexecutable
cnode.executeReq('/temperature/0/bar', function (err, rsp) {
    console.log(rsp);   // { status: '4.05' }
});



cnode.observeReq(path[, callback])

Start observing an Object Instance or a Resource on the Client Device. coap-shepherd don't support the observation on an Object at this moment.

Note

  • Server always re-initiate observation requests for the previous observations whenever client registers.
  • When a client deregisters, server will assume past states are nullified including the previous observations.

Arguments:

  1. path (String): path of the allocated Object Instance or Resource on the remote Client Device.

  2. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.05', '4.04', '4.05' and '4.08'.
    • rsp.data (Depends): data can be the value of an Object Instance or a Resource. Note that when an unreadable Resource is observe, the returned value will be a string '_unreadble_'.

Returns:

  • (none)

Examples:

cnode.observeReq('/temperature/0/sensedValue', function (err, rsp) {
    console.log(rsp);   // { status: '2.05', data: 27 }
});

// target not found
cnode.observeReq('/temperature/0/foo', function (err, rsp) {
    console.log(rsp);   // { status: '4.04' }
});

// target is not allowed for observation
cnode.observeReq('/temperature/0/bar', function (err, rsp) {
    console.log(rsp);   // { status: '4.05' }
});



cnode.cancelObserveReq(path[, callback])

Stop observing an Object Instance or a Resource on the Client Device. coap-shepherd don't support the observation on an Object at this moment.

Arguments:

  1. path (String): path of the allocated Object Instance or Resource on the remote Client Device.

  2. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.05', '4.04', '4.05' and '4.08'.

Returns:

  • (none)

Examples:

cnode.cancelObserveReq('/temperature/0/sensedValue', function (err, rsp) {
    console.log(rsp);   // { status: '2.05' }
});

// target has not yet been observed
cnode.cancelObserveReq('/temperature/0/foo', function (err, rsp) {
    console.log(rsp);   // { status: '4.05' }
});



cnode.pingReq([callback])

Ping the Client Device.

Arguments:

  1. callback (Function): function (err, rsp) { }. The rsp object has a status code to indicate whether the operation is successful.

    • rsp.status (String): Status code of the response. Possible status code is '2.05' and '4.08'.
    • rsp.data (Number): The approximate round trip time in milliseconds.

Returns:

  • (none)

Examples:

cnode.pingReq(function (err, rsp) {
    console.log(rsp);   // { status: '2.05', data: 10 }
});



cnode.dump()

Dump record of the Client Device.

Arguments:

  1. none

Returns:

  • (Object): A data object of cnode record.

    Property Type Description
    clientName String Client name of the device.
    clientId String Client id of the device.
    lifetime Number Lifetime of the device.
    version String LWM2M version.
    ip String Ip address of the device.
    mac String Mac adderss of the device.
    port Number Port of the device.
    objList Object The list of Objects supported and Object Instances available on the device.
    observedList Object The list of observing Object Instance or Resource on the device.
    so Object All of the Objects, Object Instances and Resources.

Examples:

console.log(cnode.dump());

/*
{
    'clientName': 'foo_Name',
    'clientId': 1,
    'lifetime': 86400,
    'version': '1.0.0',
    'ip': '127.0.0.1',
    'mac': '00:0c:29:3c:fc:7d',
    'port': 56643,
    'objList': {
        '1':['0'],
        '3303':['0']
    },
    'observedList': [ '/temperature/0/sensorValue' ]
    'so': {
        'lwm2mServer': {
            '0': { 
                'lifetimev:86400,
                'defaultMinPeriod':1,
                'defaultMaxPeriod':60
            }
        },
        'temperature': {
            '0': { 
                'sensorValue':19,
                'units':'C'
            }
        }
    }
}
*/



6. Status Code

Code Status Description
'2.00' OK Everything is fine
'2.04' Changed The remote cnode accepted this writing request successfully
'2.05' Content The remote cnode accepted this reading request successfully
'4.00' Bad Request There is an unrecognized attribute/parameter within the request message
'4.04' Not Found The cnode is not found
'4.05' Method Not Allowed If you are trying to change either clientName or mac, to read something unreadable, to write something unwritable, and execute something unexecutable, then you will get this response
'4.08' Timeout Request timeout
'5.00' Internal Server Error The remote cnode has some trouble