Skip to content

Accessing the API

This section describes the first few steps when developing a frontend application that uses the data made available by the coldwave backend. This section assumes that you want to create a new or enhance an already existing user interface. In very simple terms this document will guide you on how to

  • Sing up your user
  • Login with your user
  • Call some endpoints
  • Create a websocket connection

Authentication

INFO

The coldwave backend environment can be deployed without identity access management. This means that you can call any endpoint without a token (more on the token later). However, this will most likely be not the case for you. Should the backend be setup without any form of identity access management you can skip the authentication section and not provide a token in API requests that are listed as examples.

Sign Up

In order to access the API you need a token, and in order to get a token, you need a user. Therefore, the registration of a user is required. An administrator has to create a user for you. Creating a user will reserve the username, which has to be unique, and will generate a code with an expiration date. Both of these information, code and username, can be used to sign up at the appropriate endpoint. A curl example:

shell
curl --location '<<URL>>/api/v1/iam/signup' --header 'Content-Type: application/json' --data '{
 "name": "<<USERNAME>>",
 "code": "<<CODE>>",
 "password": "<<PASSWORD>>"
}'

The password has to be provided by you. This way, we can ensure, that entity, besides yourself, has access to the password.

Tokens

After the successful creation of a user, you can log in at the login-endpoint. This endpoint requires a Basic-Authorization-Header. This header is a string consistent of the username and password seperated by a colon and then base54 encoded. As a curl command this would look like the following:

shell
curl --location '<<URL>>/api/v1/iam/token' --header 'Authorization: Basic dGVzdDpQYXNzdzByZCE='

Where the header has to be replaced by your encoded string. The response body will look something like the following example:

json
{
  "token": "eyJhbGciOiJSUzI1NiIs..."
}

This token should be used as a Bearer-Authorization-Header. The header will be added in later examples if there are any questions on how to provide the token to the API.

It is also possible to generate a refresh token, which can be used to request a new token without the need to gather username and password information again. Please check out the token and refresh-token endpoint for further details.

Every endpoint requires a certain access which can be found in the access section of each endpoint. You can inspect a token, e.g. with a tool like jwt.io. The token is signed, to verify the token origin use the public key available at the well-known endpoint. There is also the option to manage access based on the device identifier and service identifier.

Data

While there are countless possibilites for applications within coldwave, most of them will probably be accessing data produced by devices in some or another way. For this, You will need to access the endpoints exposed by service, schema and meta. The service API will enumerate all the services and their properties, the schema API will give a better understanding of the properties to You and Your application's users and the meta API gives You access to associated data like a device's physical location or deployment info.

Service

The endpoints within the "service" core feature return the actual state of devices and their services. To gather the complete state of all devices and services call the devices or services endpoint with a depth query parameter of 2. This is recommended to reduce the amount of request and should be used when polling the service state of the coldwave backend application. In terms of a curl command, this would look like this:

shell
curl --location '<<URL>>/api/v1/devices?depth=2&raw=true' --header 'Authorization: Bearer <<TOKEN>>'

to get the complete state grouped by devices. To get the data grouped by services run

shell
curl --location '<<URL>>/api/v1/services?depth=2&raw=true' --header 'Authorization: Bearer <<TOKEN>>'

Furthermore, both examples show how to use the access token generated earlier. The string <<TOKEN>>, of course, has to be replaced with the access token.

TIP

It is recommended using the query parameter raw set to true and retrieve the property cleartext names and other information from the schema. They can then be displayed in a more human-readable format. This enables a more loose coupling between the schema and the service API. This makes it easier to rename properties, add enums (that can be used for a better user experience by providing a dropdown instead of e.g. a numeric input) or change other information such as description or the unit without affecting anything else.

Meta

The "Meta" core feature allows you to keep track of devices and add associated data unrelated to any service, but solely the device, e.g., the location of the device, like mentioned before. More information can be taken from the meta documentation.

Again, as with most other endpoints, the endpoints of the "Meta" core feature allow a depth query parameter. To retrieve all important information in a single request, the depth query parameter should be used.

shell
curl --location '<<URL>>/api/v1/meta?depth=1' --header 'Authorization: Bearer <<TOKEN>>'

Schema

As described in the introduction, a schema provides a description of a service. It is highly recommended using it to map property identifiers to human readable property names. The initial request to retrieve all schemas (at least the ones you have access to) should be (again as a curl example):

shell
curl --location '<<URL>>/api/v1/schemas?depth=1' --header 'Authorization: Bearer <<TOKEN>>'

An example response body in the json format might look like this:

json
[
  {
    "serviceIdentifier": "00000000-0000-0000-0000-000000000000",
    "schema": {
      "name": "Example",
      "description": "Schema for an example service",
      "properties": {
        "0x2000": {
          "name": "mode",
          "description": "The mode of the example service",
          "type": "uint8",
          "enum": {
            "1": {
              "name": "off"
            },
            "2": {
              "name": "silent"
            },
            "3": {
              "name": "running"
            }
          }
        },
        "0x2001": {
          "name": "humidity",
          "description": "The humidity of the example service",
          "type": "float",
          "unit": "%",
          "readonly": true
        }
      }
    }
  }
]

Adding a response-body for a service request to this example:

json
[
  {
    "serviceIdentifier": "00000000-0000-0000-0000-000000000000",
    "devices": [
      {
        "deviceIdentifier": "CFF349041010ADC",
        "properties": {
          "8192": {
            "tag": "value",
            "modifier": {
              "isReadOnly": false,
              "isActionable": false,
              "isVolatile": false,
              "isError": false,
              "isArray": false
            },
            "propId": 8192,
            "type": "UINT8",
            "value": 1
          },
          "8193": {
            "tag": "value",
            "modifier": {
              "isReadOnly": true,
              "isActionable": false,
              "isVolatile": false,
              "isError": false,
              "isArray": false
            },
            "propId": 8193,
            "type": "FLOAT",
            "value": 50
          }
        }
      }
    ]
  }
]

Both this information can be used to display properties with a human-readable name, unit information, information if the property can be changed and more. Furthermore, it allows, as mentioned, a loose coupling to rename properties or change enum values for example.

WARNING

The schema currently returns the property identifiers in hexadecimal notation (e.g. 0x2000) while the service API uses the decimal format (e.g. 8192). Make sure to parse them both as integer to not run into problems in comparing operations.

Websocket

In order to be able to reduce traffic for both, the backend and your application, it is considered best practice to create a websocket connection. To do so, on startup of you application:

  1. connect to the websocked (see also "Connect" below)
  2. poll all required data via the services or devices endpoints
  3. update the state of your application based on the events received through the websocket

This ensures no event is missed and no wasteful and non-realtime poll-requests need to be made.

Connect

Most browsers and libraries do not support headers while creating a websocket connection. Therefore, in order to create a connection, you need a ticket first. This ticket can be generated with the ticket endpoint

shell
curl --location '<<URL>>/api/v1/events/ticket' --header 'Authorization: Bearer <<TOKEN>>'

With this ticket, we can map the user to the websocket upgrade request. Using (e.g.) the WebSocket - Web API feature you can then create a websocket by running the code

javascript
new WebSocket("wss://<<URL>>/api/v1/events/<<TICKET>>");

The ticket has a very short lifetime, so make sure to run the requests back to back.

Events

All events you have access to will be sent over the websocket and are documented in the Events section of each feature. To receive events you need read access on the specific resource. An example event for the service feature is the update for a specific service of a specific device. The event will look similar to the following example:

json
{
  "type": "OBJECT_UPDATED",
  "flakeAddress": 88,
  "deviceIdentifier": "CFF349041010ADC",
  "serviceIdentifier": "00000000-0000-0000-0000-000000000000",
  "updates": [{
      "tag": "value",
      "modifier": {
        "isReadOnly": false,
        "isActionable": false,
        "isVolatile": false,
        "isError": false,
        "isArray": false
      },
      "propId": 8192,
      "type": "UINT8",
      "value": 1
    }, {
    "tag": "value",
    "modifier": {
      "isReadOnly": true,
      "isActionable": false,
      "isVolatile": false,
      "isError": false,
      "isArray": false
    },
    "propId": 8193,
    "type": "FLOAT",
    "value": 50
  }
  ],
  "timestamp": 1697098014386
}

INFO

Websocket messages are strings, the example above has already been parsed to json.

TIP

Furthermore, all events have a "type" property to parse them properly. The type value for most events start with the feature they belong to. For example the websocket message with the type ALARM_CONFIG_UPDATE will be documented in the alarm core feature.

To read more about the three core features mentioned, and all other features, head to each features' documentation.