Running Eclipse HONO and Ditto on Google Cloud (3)

MichaelChi
Google Cloud - Community
5 min readAug 20, 2022

--

Eclipse Ditto is a Digital Twins service which allows you to create a twin in the cloud and communicate with the device through the twin.

Set up Eclipse Ditto

To have Eclipse Ditto talk with Eclipse HONO, first we create a Digital Twin in Eclipse Ditto.

Here we create a Digital Twin that represents the device which has two properties, temperature and humidity, later our device will send telemetry to update these properties.

Note that when calling Eclipse’s REST API, you specify a user name and password as the credential. Eclipse Ditto come with its authentication and authorization mechanism, details please refer to Eclipse Ditto documents.

curl -X PUT -i -u ditto:ditto -H ‘Content-Type: application/json’ -d '{
“attributes”: {
“location”: “Taiwan”
},
“features”: {
“temperature”: {
“properties”: {
“value”: null
}
},
“humidity”: {
“properties”: {
“value”: null
}}}}' http://${DITTO_API_IP}:${DITTO_API_PORT_http}/api/2/things/${TENANT_NAME}:${DEVICE_ID}

You should see below message indicating that the digital twin has successfully created.

You can do a HTTP GET to get digital twin configurations.

curl -i -u ditto:ditto http://${DITTO_API_IP}:${DITTO_API_PORT_http}/api/2/things/${TENANT_NAME}:${DEVICE_ID}

Create a Connection to connect Eclipse HONO and Eclipse Ditto

Next step, I want to have Eclipse HONO connect with Eclipse Ditto. Eclipse Ditto provides Connectivity API to integrate with external systems. But before I create the connection to integrate both sides, let’s first take a look at how Eclipse Ditto and HONO handles incoming messages.

When a device send telemetry to Eclipse HONO through MQTT protocol, it goes to Eclipse HONO MQTT Adapter, depending on installation steps, the message will then be dispatched to AMQP network or Kafka for further routing. In our case, we will route these telemetry to Eclipse Ditto, which accepts a Eclipse Ditto protocol message format.

So before we can route telemetry to Eclipse Ditto, we first need to define a mapping to map telemetry coming from device to Ditto Protocol. For simplicity sake, I assume the device is always sending JSON messages in below format

{ “temp”: 20.5, “hum”:50}

In Eclipse Ditto, you can define a JavaScript function to map messages. Here is the simple JavaScript function.

function mapToDittoProtocolMsg(
headers,
textPayload,
bytePayload,
contentType)
{
if (contentType !== "application/json") { return null; }
var jsonData = JSON.parse(textPayload);
var temperature = jsonData.temp;
var humidity = jsonData.hum;
var path;
var value;

if (temperature != null && humidity != null) {
path = "/features";
value = {
temperature:
{
properties: { value: temperature }
},
humidity: { properties: { value: humidity } }
};
} else if (temperature != null) {
path = "/features/temperature/properties/value";
value = temperature;
} else if (humidity != null) {
path = "/features/humidity/properties/value";
value = humidity;
}
if (!path || !value) {
return null;
}
return Ditto.buildDittoProtocolMsg(
"org.eclipse.ditto2",
headers["device_id"].split(":")[1],
"things",
"twin",
"commands",
"modify",
path,
headers,
value
);
}

Let’s take a close look of these codes.

First the codes checks if content-type is application/json. In our case we only handles JSON messages, so if the content-type is not JSON message we simple discard it. Note that this also means in our device codes we need to specify content-type header.

Then once we have the message body, we start to parse the message and get required information, in this case, temp and hum.

var jsonData = JSON.parse(textPayload);
var temperature = jsonData.temp;
var humidity = jsonData.hum;

Then we specify path value, this path aligns with the Digital Twin property we created earlier.

   } else if (temperature != null) {
path = "/features/temperature/properties/value";
value = temperature;
} else if (humidity != null) {
path = "/features/humidity/properties/value";
value = humidity;

Finally we create the Ditto protocol message, note that the second parameter is the device’s id in Eclipse Ditto, which is demo-device-001 in our case. When telemetry sent from device to Eclipse HONO, device id is in ${TENANT_NAME}:${DEVICE_ID} format, so here we do a string split to extract the device id.

return Ditto.buildDittoProtocolMsg(
"org.eclipse.ditto2",
headers["device_id"].split(":")[1],
"things",
"twin",
"commands",
"modify",
path,
headers,
value
);

In order to add the mapping function to Connection, we do a JSON.stringify() against it, so the value becomes this.

“function mapToDittoProtocolMsg(\n headers,\n textPayload,\n bytePayload,\n contentType\n) {\n\n if (contentType !== \”application/json\”) {\n return null;\n }\n\n var jsonData = JSON.parse(textPayload);\n var temperature = jsonData.temp;\n var humidity = jsonData.hum;\n \n var path;\n var value;\n if (temperature != null && humidity != null) {\n path = \”/features\”;\n value = {\n temperature: {\n properties: {\n value: temperature\n }\n },\n humidity: {\n properties: {\n value: humidity\n }\n }\n };\n } else if (temperature != null) {\n path = \”/features/temperature/properties/value\”;\n value = temperature;\n } else if (humidity != null) {\n path = \”/features/humidity/properties/value\”;\n value = humidity;\n }\n \n if (!path || !value) {\n return null;\n }\n\n return Ditto.buildDittoProtocolMsg(\n \”org.eclipse.ditto2\”,\n headers[\”device_id\”].split(\”:\”)[1],\n \”things\”,\n \”twin\”,\n \”commands\”,\n \”modify\”,\n path,\n headers,\n value\n );\n}”

Now that we have our mapping function, let’s create Connection to connect Eclipse HONO and Eclipse Ditto.

Note that

  • When calling create connection, we use devops:devopsPw1! as the credentials.
  • The connection listens to the AMQP network, the URL of the network is specified in the “uri”.
  • The connection id, hono-sandbox-connection, will be used as part of MQTT client Id, note down the Id, we will be using it later.
curl -X POST -i -u devops:devopsPw1! -H 'Content-Type: application/json' -d '{
"targetActorSelection": "/system/sharding/connection",
"headers": {
"aggregate": false
},
"piggybackCommand": {
"type": "connectivity.commands:createConnection",
"connection": {
"id": "hono-sandbox-connection",
"connectionType": "amqp-10",
"connectionStatus": "open",
"uri": "amqp://consumer%40HONO:verysecret@'${AMQP_NETWORK_IP}:${AMQP_NETWORK_PORT_amqp}'",
"failoverEnabled": true,
"sources": [{
"addresses": [
"telemetry/org.eclipse.ditto",
"event/org.eclipse.ditto"
],
"authorizationContext": ["nginx:ditto"]
}],
"mappingContext": {
"mappingEngine": "JavaScript",
"options": {
"incomingScript": "function mapToDittoProtocolMsg(\n headers,\n textPayload,\n bytePayload,\n contentType\n) {\n\n if (contentType !== \"application/json\") {\n return null;\n }\n\n var jsonData = JSON.parse(textPayload);\n var temperature = jsonData.temp;\n var humidity = jsonData.hum;\n \n var path;\n var value;\n if (temperature != null && humidity != null) {\n path = \"/features\";\n value = {\n temperature: {\n properties: {\n value: temperature\n }\n },\n humidity: {\n properties: {\n value: humidity\n }\n }\n };\n } else if (temperature != null) {\n path = \"/features/temperature/properties/value\";\n value = temperature;\n } else if (humidity != null) {\n path = \"/features/humidity/properties/value\";\n value = humidity;\n }\n \n if (!path || !value) {\n return null;\n }\n\n return Ditto.buildDittoProtocolMsg(\n \"org.eclipse.ditto2\",\n headers[\"device_id\"].split(\":\")[1],\n \"things\",\n \"twin\",\n \"commands\",\n \"modify\",\n path,\n headers,\n value\n );\n}"}}}}}' http://${DITTO_API_IP}:${DITTO_API_PORT_http}/devops/piggyback/connectivity?timeout=8s

To verify if the connection is successfully created, you do a retrieve connection API call.

curl -X POST -i -u devops:devopsPw1! -H ‘Content-Type: application/json’ -d '{
“targetActorSelection”: “/system/sharding/connection”,
“headers”: {},
“piggybackCommand”: {
“type”: “connectivity.commands:retrieveConnection”,
“connectionId”: “hono-sandbox-connection”
}
}'
http://${DITTO_API_IP}:${DITTO_API_PORT_http}/devops/piggyback/connectivity?timeout=8s

To verify if the connection is running properly, run a HTTP client to submit telemetry.

curl -X POST -i -u ${DEVICE_ID}-auth@${TENANT_NAME}:my-password -H ‘Content-Type: application/json’ -d ‘{“temp”: 33.07}’ http://${HTTP_ADAPTER_IP}:${HTTP_ADAPTER_PORT_http}/telemetry

You fetch connection metrics to check if telemetry is routed to Eclipse Ditto.

curl -X POST -i -u devops:devopsPw1! -H ‘Content-Type: application/json’ -d ‘{
“targetActorSelection”: “/system/sharding/connection”,
“headers”: {},
“piggybackCommand”: {
“type”: “connectivity.commands:retrieveConnectionMetrics”,
“connectionId”: “hono-sandbox-connection”
}
}’
http://${DITTO_API_IP}:${DITTO_API_PORT_http}/devops/piggyback/connectivity?timeout=8s

At this point, we have successfully integrated Eclipse HONO and Eclipse Ditto. In the next blog I will integrate Eclipse Ditto and Google Cloud Functions.

Part(4)

--

--