What is this about?

The SAP HANA Cloud Platform IoT Services offer an out-of-the-box set of features for managing the Internet of Things. The service is currently being split into the Remote Device Management Service (RDMS) and the Message Management Service (MMS). While the RDMS can be fully managed via graphical user interface, it’s also possible to manage devices, device types, message types, etc. via API. The MMS can be used to send data from and to a device and is by default meant to be used via API. There are already some great blog posts available covering the IoT services (How to use RDMS APIs to create types on HCP IoT Services, HANA Car v1.0 – Internet of Things configuration, Communicate with IoT in your WebIDE project). In this blog post, I am going to introduce a small Node.js library that adds some abstraction on top of the API for a nicer interface.


The module was created as part of our gamificated IoT showcase.

The Node.js module (hcp-iot-api)

Node.js is a powerful asynchronous JavaScript runtime built on Chrome’s V8 JavaScript engine. You can read more about Node.js on the official website. Even in the SAP ecosystem Node.js is currently getting momentum, as its runtime is now a core part of SAP HANA (see SAP HANA SPS 11: New Developer Features; Node.js). For our showcase we used Node.js to access the sensor data of a Sphero robot, but there are other IoT devices out there with support for Node.js (e.g https://tessel.io/)

The hcp-iot-api Node module is Open Source and currently being hosted at: GitHub – teamfact/hcp-iot-api-node: Lightweight Node.js based wrapper for the SAP HANA Cloud Platform IoT Services API.

You can install the module through the public npm registry by running the following command in CLI:

npm install --save hcp-iot-api

While this is not a full description of all features, the following paragraphs show some examples of how to use the module. More detailed information can be found in the readme file in the github repository. Some example scripts will be added to the repository in the near future.

A small note on promises

Because of its asynchronous nature, requests in Node.js are non-blocking. While this can be a huge benefit, it makes things more complicated when they should be done in a strict order. This could either be achieved using nested callbacks or with promises. Because the libraries methods always return promises, it is much easer to deal with sychronous requests, avoid handling errors multiple times and keep everything more readable. An introduction to promises can be found here.

Remote Device Management Service (RDMS)

The IoT Services Cockpit inside HCP contains a user interface to manage devices, device types, message types, etc. The Remote Device Management Service can be used to programatically do the administration without the need to access the cockpit. Especially in large projects it may be a good solution to store the definition of message types, etc. as code fragements in one central place with versioning support (e.g. git).

Setup

The RDMS always needs the users HCP username and password for authentication. This information needs to be given when initializing a new object.

var API = require("hcp-iot-api");
var rdms = new API.RemoteDeviceManagementService({
    "account": "<user>",
    "password": "<password>"});

Reading and posting data

The API allows to completly configure the HCP IoT services without the need to access a GUI. Instead there are fuctions available to read, create and delete all kinds of entity types. A simple example showing how to fetch all message types:

rdms.getMessageTypes()
.then(function(messageTypes) {
    ...
})
.catch(function(error) { console.log(error.message) });

And another one showing how to create a device type and after that, create a corresponding message type. The available fields can be looked up in the official documentation.

rdms.createDeviceType({ "name": "Device Type 1" })
.then(function (deviceType) {
    return rdms.createMessageType({'device_type': deviceType.id, ...});
.then(function (messageType) {
    ...    
.catch(function(error) { console.log(error.message) });

Registering devices

It is possible to dynamically register devices via API. A key requirement is the former definition of a device type, because its id and token need to be applied. A possible call could look like:

rdms.registerDevice({
    "name": "Device 1",
    "device_type": deviceType.id,
        "attributes": [
            { "key": "customKey", "value": "custom value" }
        ]
    }, deviceType.token);
})
.then(function(device) {
    var deviceToken = device.token;
    var deviceId = device.id;
    ...
}); 

When registering a device, the API returns the devices JSON object containing a token. This is only returned once directly after creation, so it is important to store it for later usage. When using the MMS to send sensor data from a device, this token needs to be passed in as the OAuth token. If you do not store the token programatically, it can be set back and retrieved in the IoT Cockpit.

Message Management Service (MMS)

The Message Management Service is the actual service to send data from an IoT device into the HANA Cloud Platform and/or back to the device.

Setup

The MMS is a bit more complex in terms of authorization. There are several methods which are bound to a specific device. For exampe, if you want to send sensor data from a device into the HCP (mms.sendData(…)), you need the deviceId and deviceToken information. Other methods (e.g. mms.pushToDevice(…)) require a user authentication via HTTP Basic Auth (account/password) or OAuth (oauthToken). The authentication information could be assigned in the constructor or when calling a method. Passing all parameters in upfront would look like this:

var API = require("hcp-iot-api");
var mms = new API.MessageManagementService({
    "account": "<username>",        // This one is mandatory!
    "password": "<password>",     "deviceToken": "<deviceToken>",     "deviceId": "<deviceId>",     "oauthToken": "<oauthToken>"});

Sending sensor data (via HTTP)

The main purpose of the MMS is to send sensor data from the device into the HCP. The API is straight forward when using HTTP(S) as the message protocol:

mms.sendData({
  "messageType": "<messagetype_id>",
  "messages": [
    {
      "sensor1": "Value 1",
      "sensor2": "Value 2"    }
  ]
})
.catch(function(err) { console.log(err.message); });

If the deviceId and deviceToken have not been included in the constructor, they can be passed to the sendData method as the second and third parameter:

var mms = new API.MessageManagementService({ "account": "<username>" });
mms.sendData({
    "messageType": "<messageTypeId>",
    "messages": [{
        "sensor1": "Value 1",
        "sensor2": "Value 2"    }]
}, "<deviceId>", "<deviceToken>")
.catch(function(error) { console.log(error.message) });

Pushing data to a device

Using MMS it is also possible to send information to a device. When sending the information, it ca be specified if the message should be delivered via HTTP or a Websocket connection. It is also required to register a message type with direction toDevice (or bidirectional) and use this one, when sending the information. Authentication for using the pushToDevice function is either via password or via oauthToken.

var mms = new API.MessageManagementService({
  "account": "<username>",
  "password": "<password>"});

mms.pushToDevice("<deviceId>", {
  "method": "http", // or "ws" 
  "sender": "My IoT application",   "messageType": "<messageTypeId>",   "messages":[     {       "abc": "switch on",     }   ]  });

If HTTP is the transport protocol, the message is being stored inside the HCP in table T_IOT_HTTP_PUSH and can be retrieved by polling via the mms.getData() method. Again, if specificed in the constructor, the device related parameters can be omitted.

var mms = new API.MessageManagementService({
  "account": "<username>",
  "password": "<password>"});

mms.getData("<deviceId>", "<deviceToken>")
.then(function(messages) {
    // Fetch the existing messages for this device
});

Using websockets

The MMS also allows a persistent connection to the API via websocket protocol. This should be used if a bidirectional communication is required. When using websockets, messages send to a device do not need to be pulled, but are pushed through the websocket connection.

The following example shows how to establish a websocket connection and once this is established, send data into the HCP. The sendData method detects the open connection and will send the data over the connection instead of over HTTP(S). The onWebsocketMessage function registers a callback, which is being called whenever a message for the device arrives. Please note, that we are not using promises here, because this method may be called multiple times.

var mms = new API.MessageManagementService({
    "account": "<account>",
    "deviceId": "<deviceId>",
    "deviceToken": "<deviceToken>"});

mms.openWebsocketConnection()
.then(function() {

    // Register callback for when data is being pushed to the device
    mms.onWebsocketMessage(function(message) {         // Do something the message
    });     // Send data through the websocket
    mms.sendData({         "messageType": "<messageType>",         "messages": [{                 "sensor1": "Value 1",                 "sensor2": "Value 2"            }]     });  }); // Close, when not required anymore
mms.closeWebsocketConnection() .then(function() {     // Maybe do something when closed});

MMS API configuration

In the past I had the requirement to build an analytical data model on top of the generated HANA tables, but this was impossible due to fact, that all tables were generated in a row-store format. I changed this by hand and some SQL (ALTER TABLE … COLUMN) but it somehow felt dirty.

Since version 2.15.0 of the MMS, it is possible to set a wide range of options for configuration. This can be done either via GUI in the MMS Cockpit or via API.

To achieve what I did via SQL by hand, you can now just use an API call with passing the settings key and value.

mms.updateConfig({
  "config": [
    { "key": "mms.processing.sql.hana.table_type", "value": "column" }
  ]
}).catch(function(error) { console.log(error.message) });

Be aware, that in the official API documentation the configuration format is currently incorrect. So if you want to set settings programmatically, you have to explicitly use key and value like in the example shown above.

For a list of all existing options, there is a mms.getConfig() method available, wich returns the whole configuration.

Final words

In this blog post I introduced a small Node.js library to access the HCP IoT API. The library itself is not that complex, but adds a lightweight layer on top of the API for easy consumption. The module is still work in progress and currently covers around 90% of the current API definition. In the future, I am going to add some tests and more detailed example scripts. A first “real world” use case will be shown in the upcoming blog posts for our IoT racing blog series.

To report this post you need to login first.

8 Comments

You must be Logged on to comment or reply to a post.

  1. Anton Levin

    Hello Mike,

    you are right, a payload format specified in the Configuration API documentation is incorrect. This will be fixed asap. Thanks for bringing that out.

    Regards,

    Anton

    (0) 
    1. Mike Zaschka Post author

      Hi Iwona,

      I recently found out, that you can give direct feedback from the documentation. Next time I will do it this way.

      Kind regards,

      Mike

      (0) 
  2. Alessandro Spadoni

    Great blog!

    I’m doing some experiments around IoT Services and node.js , so I’ll try your module for sure!

    just a little question: is there a way to listen in a real time way (via WebSockets) the messages sent from the devices to hcp (for example for a Real Time monitoring web app)?

    I haven’t found a way…so I created a node.js app that pull the data from HCP every x seconds and then push it via web sockets to another third listening app …..so it’s not a true “real time” app…

    Do you have any tips?

    Thank you!

    (0) 
    1. Mike Zaschka Post author

      Hello Alessandro,

      I appreciate any feedback on the library. It’s still work in progress and at the moment used for one use case only.

      Regarding your question:

      As far as I know, it is sadly not possible to “listen” to the messages sent to the IoT services. I really hope that in the future there will be a way to have an active connection to the IoT service and receive incoming messages via push on a client.

      But in the meantime there are some other options:

      Message forwarding via HTTP

      The service offers the option to push incoming messages to another URL via HTTP (for more infos please read the documentation). This way you do not have to pull, but you need your own server part which takes the incoming messages and pipes them through a web socket connection to your client(s).

      A small note: The processing API is currently missing in the library implementation. But it won’t be much effort to add the missing functions.

      Message pulling

      You can poll for new messages (as you did). This can either be done on a server component or directly from a client.

      Use something else for real time

      In our showcase we actually build a small websocket server which is used for the real time part. We do send all the device data into the HCP service and in parallel the required real time data though the web socket directly to the client. Thus, we are having “real” real time on the client (see image below).

      /wp-content/uploads/2016/05/architecture_938469.jpg

      (0) 
      1. Alessandro Spadoni

        Thank you Mike and Anton for your valuable answers,

        very interesting possibilities…as a first step,I’ll try an http message forwarding in conjunction with an Ngrok web server just for testing

        (0) 
        1. Harikrishnan Panakkal

          Hello Alessandro,


          You could use HTTP message forwarding notification to your java servlet. So the servlet knows whenever a message comes and act as a listener for iot messages…


          -Hari

          (0) 

Leave a Reply