Device Requests

This group of REST API allows performing generic requests to a remote device. This API is generally used to perform remote operations that are not supported by the built-in ones like for example device assets, device bundles, device certificates, device configurations etc.

Perform a Request

The following API will sends a request to a device and waits for a response synchronously.

POST /{scopeId}/devices/{deviceId}/requests

Required path parameters

  • {scopeId} is the scopeId of the tenant (account) to which the desired device belongs. It can be retrieved from the response of the authentication call. The "_" character can be used to specify the scopeId of the user currently authenticated with the token;
  • {deviceId} is the id of the desired device.

Optional path parameters

  • timeout an integer value which represents the timeout for the request in milliseconds. Default is 30 seconds.

Request Body

This call have a required request body that defines the request to be performed by the remote device. As such the body depends on the specific operation to be executed. In the following section there are some examples.

ESF/Kura Request Examples

For an ESF application the body of the request is mapped to a Kura Payload message and used to initiate a Request/Response conversation based on the Kura/Kapua MQTT topic namespace. See MQTT Namespace section in ESF guide for more details.

Preparatory Steps

Check prerequisites

Checks that need to be done to ensure that prerequisites are ok before start executing the examples.

  1. You have an active EC account. For this guide we'll use a conventional account name example-account
  2. You have an active user in account-name. For this guide we'll use a conventional user name example-user
  3. You have a ESF/Kura device registered in the the account. For this guide we'll use a conventional Client Id client-id
  4. You have a REST Client available in your workstation to execute REST Calls. Popular options are:
    1. Built in Swagger UI available for each EC instance. Usually Swagger UI is reachable at https://api.your-host/doc.
    2. Postman (see more at https://www.postman.com/)
    3. cURL (see more at https://curl.se/)

The Swagger UI shows the cURL equivalent of each executed call.

Get API Token

Once you have your account properly configured you need to get an API token. The API token is a JWT token used by EC to authorize the subsequent API calls.

With the REST client of your choice run the following:

  • POST /authentication/user

Request body

{
  "username": "example_user",
  "password": "<the example_user password>"
}

Request response:

{
  "id": "BXI86irC1Vg",
  "scopeId": "Aw",
  "tokenId": "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5lY2xpcHNlLm9yZy9rYXB1YSIsImlhdCI6MTcyODA1Mjc5OCwiZXhwIjoxNzI4MDU0NTk4LCJzdWIiOiJlS0Zpa2JYaFdtYyIsInNJZCI6IkF3IiwidG9rZW5JZGVudGlmaWVyIjoiN2ZiMzgxNTQtNDZjYy00ZDRjLWJlZDAtMDBiOGFkN2M5ZjcwIn0.NPWGJ10TpdtyCkYunvRYokKl06i4ko9lXyoKCPg3z-webT0CksXmcbs_k9mLjImKl5QYG2dtXw47BSfuWg0_1XgIDzRQs1eyefAaqWKyE5ctAnILUaSlXV-0OO7X-vTV9xmmTAVl_Ngb5HkRVwH7mqCVYp06vlT6nDqGCzXwiK4WVvvCJPqEXCpU66SWvnOfjsRj1v85_kjSugoHzXeCb6YcmnMTE6W579oE3_O31LDBswot3jo9Ba783rTuF5j2rxqF24v6etB_zuX1IomKOOCW7ktZP6CRAXl4_BvN6FtTn4XzHMd2mBQUA6eA36tFb-Kl3rHnYIEmUmJJcm0WLw",
  "expiresOn": "2024-10-04T15:09:58.012Z",
  "refreshToken": "ae9d364e-db3a-4cae-8ddb-15fb9b94e78f",
  "refreshExpiresOn": "2024-10-04T19:39:58.012Z"
  "userId": "eKFikbXhWmc",
  "createdOn": "2024-10-04T14:39:58.014Z",
  "createdBy": "eKFikbXhWmc",
  "modifiedOn": "2024-10-04T14:39:58.014Z",
  "modifiedBy": "eKFikbXhWmc",
  "optlock": 1,
  "type": "accessToken",
}

With Swagger UI, press the button Authorize at the top of the page. Copy paste the value of the field tokenId from the response above in the popup then press Authorize again, the token will be automatically added to the Authorization header of the subsequent http requests made via the UI. If using a different REST client you may need to set the header explicitly. For example:

curl -X 'GET' \
  'https://api.hostname/v1/_/devices' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5lY2xpcHNlLm9yZy9rYXB1YSIsImlhdCI6MTcyODA1Mjc5OCwiZXhwIjoxNzI4MDU0NTk4LCJzdWIiOiJlS0Zpa2JYaFdtYyIsInNJZCI6IkF3IiwidG9rZW5JZGVudGlmaWVyIjoiN2ZiMzgxNTQtNDZjYy00ZDRjLWJlZDAtMDBiOGFkN2M5ZjcwIn0.NPWGJ10TpdtyCkYunvRYokKl06i4ko9lXyoKCPg3z-webT0CksXmcbs_k9mLjImKl5QYG2dtXw47BSfuWg0_1XgIDzRQs1eyefAaqWKyE5ctAnILUaSlXV-0OO7X-vTV9xmmTAVl_Ngb5HkRVwH7mqCVYp06vlT6nDqGCzXwiK4WVvvCJPqEXCpU66SWvnOfjsRj1v85_kjSugoHzXeCb6YcmnMTE6W579oE3_O31LDBswot3jo9Ba783rTuF5j2rxqF24v6etB_zuX1IomKOOCW7ktZP6CRAXl4_BvN6FtTn4XzHMd2mBQUA6eA36tFb-Kl3rHnYIEmUmJJcm0WLw'

Get Device Id

To execute the examples below you need the id of the device. The id is used by the platform to send the command to the correct ESF/Kura device.

With the REST client of your choice run the following:

  • GET /_/devices?clientId=client-id

Request response:

{
  "type": "deviceListResult",
  "limitExceeded": false,
  "size": 1,
  "items": [
    {
      "id": "WyczTs_GuDM",
      "scopeId": "AQ",
      "createdOn": "2019-09-12T09:04:37.315Z",
      "createdBy": "Ag",
      "modifiedOn": "2019-09-12T09:04:37.603Z",
      "modifiedBy": "Ag",
      "optlock": 2,
      "clientId": "Client-Id-1",
      "connectionId": "Gd1BfeWwh3s",
      "status": "ENABLED",
      "displayName": null,
      "lastEventId": "YYHQ2i1S-P0",
      "serialNumber": "ESF-Docker-RHEL",
      "modelId": "ESF-Docker-RHEL",
      "modelName": "ESF-Docker-RHEL",
      "biosVersion": "N/A",
      "firmwareVersion": "N/A",
      "osVersion": "4.9.184-linuxkit",
      "jvmVersion": "25.161-b12 mixed mode",
      "osgiFrameworkVersion": "1.8.0",
      "applicationFrameworkVersion": "ESF_6.0.0",
      "connectionInterface": "lo (00:00:00:00:00:00)",
      "connectionIp": "127.0.0.1",
      "applicationIdentifiers": "heaterPROV-V2DEPLOY-V2VPNCLIENT-V2CONF-V1CERT-V1ASSET-V1CMD-V1",
      "acceptEncoding": "gzip",
      "tamperStatus": "NOT_TAMPERED",
      "type": "device",
      "tagIds": []
    }
  ]
}

Grab the value of field id; in the remaining of this guide this value will be referred as deviceId.

Wires graph Update Example

In this section there are three examples that illustrate how the API can be used to execute the following device management operations:

  • Download the snapshot of a Wires graph
  • Clean the current Wires graph
  • Update/Create a Wires graph

Download a Wires graph Snapshot

Scope of this example is to download the snapshot of the Wires graph of a remote device. This operation gets the same result obtained by a user when he/she presses the Download button in the Wires graph Composer page of the ESF/Kura web UI. See the Wires graph Composer guide for more details.

Request endpoint:

  • POST /_/devices/{deviceId}/requests

Request body:

{
  "channel": {
    "type": "genericRequestChannel",
    "method": "READ",
    "appName": "WIRE",
    "version": "V1",
    "resources": [
      "graph", "snapshot"
    ]
  },
  "payload": {}
}

Response body:

Check the value of the metric response.code, a value 2xx means the operation completed successfully.

{
  "channel": {
    "appName": "WIRE",
    "version": "V1"
  },
  "payload": {
    "body": "eyJzaW1wbGUiOiJKU09OIn0=", // base-64 encoded string of the Snapshot in JSON format
    "metrics": [
      {
        "valueType": "integer",
        "value": "200",
        "name": "response.code"
      }
    ]
  },
  "scopeId": "Aw",
  "capturedOn": "2024-10-04T13:24:49.089Z",
  "receivedOn": "2024-10-04T13:24:49.26Z",
  "sentOn": "2024-10-04T13:24:49.089Z"
}

The content of the body is the base-64 encoded JSON of the Wires graph Snapshot returned by ESF. The string above is just an example, usually it is much longer. To decode the string into the JSON use the following shell command.

echo -n 'eyJzaW1wbGUiOiJKU09OIn0=' | base64 --decode

which will output the following:

{"simple":"JSON"}

Update a Wires graph from a Snapshot

Scope of this example is to update the Wires graph configured in the remote device with a new one but can be used to create a Wires graph from scratch in the target device as well. The procedure described in this example can be used to distribute a Wires graph to a fleet of devices.

📘

Best practice

To update a Wires graph in a target device it's a good practice to first cleanup any existing Wires graph in the target device. Sending down a new Wires graph on top an existing one will not remove components of the old one that are no longer used by the new graph. These orphaned components will then be visible in the resulting merged Wires graph.

Clean existing Wires graph

Scope of this step is to clean any existing graph in the target device.

Request endpoint:

  • POST /_/devices/{deviceId}/requests

Request body:

{
  "channel": {
    "type": "genericRequestChannel",
    "method": "DELETE",
    "appName": "WIRE",
    "version": "V1",
    "resources": [
      "graph"
    ]
  },
  "payload": {}
}

Response Body:

Check the value of the metric response.code, a value 2xx means the operation completed successfully.

{
  "channel": {
    "appName": "WIRE",
    "version": "V1"
  },
  "payload": {
    "metrics": [
      {
        "valueType": "integer",
        "value": "200",
        "name": "response.code"
      }
    ]
  },
  "scopeId": "Aw",
  "capturedOn": "2024-10-04T13:56:16.085Z",
  "receivedOn": "2024-10-04T13:56:16.748Z",
  "sentOn": "2024-10-04T13:56:16.085Z"
}

Update/Create a Wires graph

Scope of this step is to update or create a graph in the target device.


Scope of this step is to update or create a graph in the target device.

Information required:

  • body : For this example copy-paste the content of the field body from the response obtained in the section Download a Wires graph Snapshot above. The value must be the base-64 encoding of the JSON.

To encode the JSON of the snapshot in base-64 use the following shell command:

echo -n '{"simple":"JSON"}' | base64

which will output the following:

eyJzaW1wbGUiOiJKU09OIn0=

Request endpoint:

  • POST /_/devices/{deviceId}/requests

Request body:

{
  "channel": {
    "type": "genericRequestChannel",
    "method": "WRITE",
    "appName": "WIRE",
    "version": "V1",
    "resources": [
      "graph", "snapshot"
    ]
  },
  "payload": {
    "body": "eyJzaW1wbGUiOiJKU09OIn0=" // base-64 encoded string of the Snapshot in JSON
  }
}

Response body:

{
  "channel": {
    "appName": "WIRE",
    "version": "V1"
  },
  "payload": {
    "metrics": [
      {
        "valueType": "integer",
        "value": "200",
        "name": "response.code"
      }
    ]
  },
  "scopeId": "Aw",
  "capturedOn": "2024-10-04T14:42:22.235Z",
  "receivedOn": "2024-10-04T14:42:22.417Z",
  "sentOn": "2024-10-04T14:42:22.235Z"
}

Remote Command Execution Example

Scope of this example is to execute the shell command sleep 180 in the remote device using the ESF Command Service. The action performed by this request is equivalent to execute the same command via the built-in device management operation described here in this guide.

Request endpoint:

  • POST /_/devices/{deviceId}/requests

Request body:

{
  "channel": {
    "type": "genericRequestChannel",
    "method": "EXECUTE",
    "appName": "CMD",
    "version": "V1",
    "resources": [
      "command"
    ]
  },
  "payload": {
    "metrics": [
      {
        "valueType": "string",
        "value": "sleep",
        "name": "command.command"
      },
      {
        "valueType": "string",
        "value": "180",
        "name": "command.argument"
      }
    ]
  }
}

Response body:

Check the value of the metric response.code, a value 2xx means the command was executed. However, the result of the command execution is reported by the metric command.exit.code.

{
  "channel": {
    "appName": "CMD",
    "version": "V1"
  },
  "payload": {
    "metrics": [
      {
        "valueType": "string",
        "value": "false",
        "name": "command.timedout"
      },
      {
        "valueType": "string",
        "value": "sleep: missing operand\nTry 'sleep --help' for more information.\n",
        "name": "command.stderr"
      },
      {
        "valueType": "string",
        "name": "kapua.response.exception.stack"
      },
      {
        "valueType": "string",
        "name": "kapua.response.exception.message"
      },
      {
        "valueType": "string",
        "value": "1",
        "name": "command.exit.code"
      },
      {
        "valueType": "string",
        "value": "",
        "name": "command.stdout"
      },
      {
        "valueType": "string",
        "value": "200",
        "name": "response.code"
      }
    ]
  },
  "scopeId": "AQ",
  "capturedOn": "2019-09-13T13:53:09.447Z",
  "receivedOn": "2019-09-13T13:53:09.466Z",
  "sentOn": "2019-09-13T13:53:09.447Z"
}