Connector-as-a-Service Murano Application Integration

This page describe how to enable make a Murano application compatible with Connector-as-a-Services.

Table of Contents

Requirements:


Application Setup

  1. Follow the Create application guide and choose the Application From Scratch template element.
  2. Find & Link the Connector-as-a-Service to your application.

To test my application I have already created a Murano product solution, in the same business, from the IoT-Connector template. Products in the same business are by default available as services to application solutions, but we need to manually enable it in our application product setup. In my case, I will enable iotconnectordev product that I created for testing.

enable product

Once enabled, it should show up as a service in the list to the left.

service list with product


Default Integration

By default any Murano applications are compatible with any Connector-as-a-service, (as long as their data model are compatible) and the application is always fully agnostic of whether the Connector is single or multi-tenant.

In that setup, once the connector is added to the application, devices claiming can be done either:

That's it.. nothing else to be done on the application side. You can read more about handling device events or any general application topics.

However following section will show you how to integrate the claiming logic directly into your application to remove the required navigation step to the Connector Portal.

ExoSense

ExoSense already integrates the below logic, as user, follow ExoSense Claiming Guide.

Connector Service Configuration

As application manager, you can open the Application Services panel, under the connected Connector-as-a-Service entry.

Connector portal Url

In the top of the page you will see a link to the Connector Portal where users can configure applications & claim their devices.

Claim Code

Under the Settings tab, you will see the Claim code text box which enables you as application manager to link devices to your application without the need of accessing the Connector Portal.

Devices claimed this way will not have any specific user attached to them.

App Info

You will also find there configuration option for how the Connector presents your application to the users, including name & description. Feel free to customize them.

User Managed Application

It is also possible to add our application to an existing Connector Portal user. In this case the user first needs to go to the Apps tab in the IoT-Connector portal and select "Connect App".

pdaas-apps-create-btn

You will then get a popup, select type 'Murano' and entry the application ID (copied from the Application management page).

pdaas-apps-create-murano.png

The default options will enable new devices for this application automatically.

Click 'CONNECT APP' and the application will show on your list. You can now associate existing devices to it or claim new ones from the Devices page.

Devices can belong to multiple applications at the same time.


Application API

IoT-Connector exposes a subset of device2 service API's as well as some service calls specific to the connector template.

For Murano Applications this means that your existing solutions will most likely already be IoT connector compatible without any code change.

<product>.setIdentityState()
<product>.getIdentity()
<product>.getIdentityState()
<product>.listIdentities()

See device2 documentation for more details on how to use this API.

For applications that provides a more seamless experience there are additional API's that gives solution access to IoT-Connector internal features.

<product>.claimDevice({code = "", context = ""})
<product>.getTempClaimURL({context = ""})

getTempClaimURL returns a URL to the IoT-Connector portal device claiming UI with a temporary session token. This token will allow a user to claim devices under his own user & context.

claimDevice will claim device(s) into the IoT-Connector application.

The context parameter is optional for both and allows us to add an application specific context to every claimed device. For multi-tenant applications this could be an application specific user id to know which application user claimed this device.


Example

Adding endpoints

In order to test the available IoT-Connector API's we will add two web endpoints to our application that will call methods on our IoT-Connector product.

Webservice endpoints can be added by first pressing the ENDPOINTS tab to the left.

endpoints tab

And then +NEW ENDPOINT button in the top right corner.

Murano add token

List Devices

First we will add a device listing endpoint at. GET /api/v1/{product}/devices

add route

we will add the following code that will:

  1. fetch a list of connected products.
  2. verify that the product parameter is a valid connected product.
  3. call listIdentities API on that product and return the result.

devices endpoint

local product_list = Config.listService({type = "product"})
local product_map = {}
for _, product in ipairs(product_list) do
  product_map[product.script_key] = product.service
end

local service = product_map[request.parameters.product]

if service then
  local operation = "listIdentities"
  response.message = murano.services[service][operation]()
else
  response.message = "no such product"
  response.code = 404
end
Device Claim

Next we will add an endpoint to test the device claiming functionality. GET /api/v1/{product}/claim/{code} Similar to the previous endpoint, this endpoint will do the following:

  1. fetch a list of connected products.
  2. verify that the product parameter is a valid connected product.
  3. call claimDevices API on that product with the provided code parameter as argument and return the result.

claim endpoint

local product_list = Config.listServices({type = "product"})
local product_map = {}
for _, product in ipairs(product_list) do
  product_map[product.script_key] = product.service
end

local service = product_map[request.parameters.product]

if service then
  local operation = "claimDevices"
  response.message = murano.services[service][operation]({codes = {request.parameters.code}})
else
  response.message = "no such product"
  response.code = 404
end

Add a device

Go to your IoT-Connector product in the Murano UI, click devices and then +NEW DEVICE(S).

add devices

we will add a device called device001.

add device001

After we have added our device we will manually set device credentials for it.

set device credentials

set device credentials

Modify the following script with your product id and device credentials and save as send_data.sh

#!/bin/bash
  curl -v -i https://<your_product_id>.m2.exosite.io/onep:v1/stack/alias?state \
      -H "X-Exosite-CIK: <your_device_credentials>" \
      -H "Accept: application/x-www-form-urlencoded; charset=utf-8" \
      -d "$1"

We can now set a claim_code for our device to use, ex: ./send_data_demo.sh claim_code=device001

In case we want to reset the claim (remove owner) we can send the following data to the IoT-Connector: ./send_data_demo.sh reset=1


Test App endpoints

If we get our current device list, we will see that it is currently empty.

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/devices
> {"devices":{},"mayLoadMore":false}

We can claim our device001 using the claim API

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/claim/device001
> {"device001":{}}

If we call the list API again we will now see our device

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/devices
> {"devices":[{"identity":"device001","lastip":"211.75.143.219","lastseen":1562303143046084,"online":false,"state":{"data_in":{"reported":"3","set":"3","timestamp":1561554542336156}}}],"mayLoadMore":false}

Calling the claim device API again with the same code will return error 2 indicating that the device has already been claimed.

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/claim/device001
> {"device001":{"error":2}}

Giving an invalid code will result in an empty response

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/claim/wrongcode
> {}

Add claim context

Let us update our device claiming API to set a context as part of the claim.

GET /api/v1/{product}/claim/{code}

local product_list = Config.listServices({type = "product"})
local product_map = {}
for _, product in ipairs(product_list) do
  product_map[product.script_key] = product.service
end


local service = product_map[request.parameters.product]

if service then
  local operation = "claimDevices"
  response.message = murano.services[service][operation]({codes = {request.parameters.code}, context = "test"})
else
  response.message = "no such product"
  response.code = 404
end

Notice the added context = "test" to the murano.services[service][operation]() call.

Let us reset our device (so it can be claimed again) ./send_data_demo.sh reset=1

We can claim our device001 using the claim API

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/claim/device001
> {"device001":{}}

If we now call the device list API, there is a new tags field with a claim_context tag with value set to 'test'

> curl https://<yourappname>.apps.exosite.io/api/v1/<yourproductname>/devices
> {"devices":[{"identity":"device001","lastip":"211.75.143.219","lastseen":1562578799934194,"online":false,"state":{"data_in":{"reported":"3","set
":"3","timestamp":1561554542336156}},"tags":[{"name":"claim_context","value":"test"}]}],"mayLoadMore":false}

React to device events

Our application will also receive events from claimed devices.

We can dynamically handle these events, even as products are added/removed from our application, by creating a config_fallback handler function in the Config service.

This is a generic event fallback, for all events that does not have dedicated event handlers.

We will add a simple check in our handler to make sure the event is coming from our IoT-Connector devices, by validating the type is product and the event name is event (the even name emitted by device2 service). You can also validate the id of the IoT-Connector product from context.service attribute. The event payload object is provided by config_fallback handler as an argument variable called event.

--[[
Use this script editor to add and maintain eventhandlers for this service.
See documentation to learn more.
(/reference/services/)
--]]
if context.service_type == "product" and context.event == "event" then
  print(event)
end

fallback event handler

Now if we send an event to our device, we should see it printed in our application Murano log.

> ./send_data_demo.sh temp=2.9

murano app log


Conclusion

We now have a sample application that connects to an IoT-Connector product and can list and claim devices using device claim codes that can be extended into a fully featured application with UI and authentication.