Skip to content

Technical Documentation

In this documentation are the general structure and concepts of Proficloud.io described. Besides the documentation of the platform you will also get a detailed look into device registration and secure communication as well as the secure firmware update process.

In addition to the documentation you will find HowTwo’s to get in touch with the technology around Proficloud.io.

Content

1 Concepts
1.1 Architecture
1.2 Devices
2 HowTo’s
2.1 Devices
2.1.1 PLCnext
2.2 Node-RED
3 APIs & Services
3.2 Time Series Data
3.2.1 TSD REST API

1 Concepts

In this section you will find a growing number of articles covering basic concepts of Proficloud.io.

1.1 Architecture

Overview

The following two diagrams give a general overview about the architecture of Proficloud.io and our Cloud Connector SDK with focus of their corresponding user perspective.

Proficloud.io

The user focus of Proficloud.io is the customer’s end user, which is using the Smart Services, like EMMA, and directly or indirectly the Core Services, like User Management or Billing.

Underneath those services we find the technological base for Proficloud.io in the Core IoT Platform, which is especially responsible for the connectivity between device and cloud, our Scalable Kubernetes Cluster, where all services are running on and which is there for scaling Proficloud.io in respect to connected users and devices, and AWS as our cloud provider.

1.2 Devices

A device in Proficloud.io is a digital twin of a direct connected hardware device, a gateway or software device. It is identified by its UUID (https://tools.ietf.org/html/rfc4122). The minimal features set of every Proficloud.io device is the support of the Device Management Service. This means meta data like device type and firmware version and state of health information like the traffic light are provided from the device.

2 HowTo’s

2.1 Devices

In this section you will find information on how you must prepare a device in WBM, for example, to connect it to Proficloud.io.

2.1.1 PLCnext

Here you can find detailed information related to PLCnext Devices and how they interact with Proficloud.io: PLCnext Help Center

2.2 Node-RED

Installation of Node-RED: Getting Started : Node-RED

E-learning to use Node-RED with Proficloud.io: Get to know Node-RED and Proficloud.io

3 APIs & Services

3.1 Time Series Data

In this section you will find information about the Time Series Data Service and its REST API.

3.1.1 Time Series Data Service REST API

The REST API for the TSD service can be used to obtain TSD data from proficloud.io.

What you will need:

  • A user account
  • At least one device sending data to the time series data (you need the UUID of the device)
  • The name of the metrics you want to read (hint: check the dashboarding system)
  • Any tool or programming language you like to use to query the REST-API

To acess the API, authentication via OAuth is mandatory.

A token can be requested either via username and password (on initial authentication) or via a refresh token (provided by initial login).

The response yields an access token and refresh token with corresponding expiration times. An access token should be re-used until it expires, and the refresh token can be used to get new tokens without the need to supply username and password again.

The TSD REST APi uses JSON payloads and can be used from any programming language or tool capable of working with REST API’s. This tutorial provides Python examples, but of course other languages or tools can be used.

The following tutorial shows examples for the retrieval and renewal of tokens and also shows different possibilities for querying data!

#In this Python example we will use the requests library:
import requests
import json
url = "https://tsd.proficloud.io/epts/token"
username = "demohmi2020@phoenixcontact-sb.io"
password = "GoDigital2020!"
uuid = '355f6856-e9a4-469f-8f22-3f8b037d064a'
metrics = "auto~pressure,auto~temperature,auto~watt"

Authentication

First we need to authenticate at the REST API as a user. The access token is used to authenticate the user for each REST-call and the refresh token can be used to renew the tokens without the need for a username and password.

Initial token retrieval / username & password

Initially, tokens can be retrieved with the username and password:

#Headers:
header = {
    "Content-Type": "application/json"
}
#Payload:
data =  {
    "username": username,
    "password": password
}

#Send post request
token_response = requests.post(url, json=data, headers=header)
if token_response.ok:
    access_token = token_response.json().get("access_token")
    expires_in = token_response.json().get("expires_in")
    refresh_token = token_response.json().get("refresh_token")
    refresh_expires_in = token_response.json().get("refresh_expires_in")

print("Access token: ...{}...(length: {}, expires in {}s)".format(access_token[500:600], len(access_token), expires_in))
print("Refresh token: ...{}... (length: {}, expires in {}s, 0 means infinite)".format(refresh_token[500:600], len(refresh_token), refresh_expires_in))
Access token: ...MzQtODljYWYzMDE2MmZmIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwczovL3Byb2ZpY2xvdWQuaW8iLCJodHRw...(length: 1726, expires in 86400s)
Refresh token: ...mF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjJhMjFhZmRhLTRmMDUtNDA5Ny1hZTM0LTg5Y2FmMzAxNjJmZiIsInJlYWxtX... (length: 1079, expires in 0s, 0 means infinite)

Token renewal

The refresh token can be used to obtain new tokens, without the need for a username and password. In [3]:

#Headers:
header = {
    "Content-Type": "application/json"
}
#Payload:
data =  {
    "refresh_token": refresh_token
}

#Send post request
token_response = requests.post(url, json=data, headers=header)
if token_response.ok:
    access_token = token_response.json().get("access_token")
    expires_in = token_response.json().get("expires_in")
    refresh_token = token_response.json().get("refresh_token")
    refresh_expires_in = token_response.json().get("refresh_expires_in")

print("Access token: ...{}...(length: {}, expires in {}s)".format(access_token[500:600], len(access_token), expires_in))
print("Refresh token: ...{}... (length: {}, expires in {}s, 0 means infinite)".format(refresh_token[500:600], len(refresh_token), refresh_expires_in))
Access token: ...MzQtODljYWYzMDE2MmZmIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwczovL3Byb2ZpY2xvdWQuaW8iLCJodHRw...(length: 1726, expires in 86400s)
Refresh token: ...mF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjJhMjFhZmRhLTRmMDUtNDA5Ny1hZTM0LTg5Y2FmMzAxNjJmZiIsInJlYWxtX... (length: 1079, expires in 0s, 0 means infinite)

Querying data

There are three options when it comes to querying data from the TSD REST API:

  • Query last data points
  • Query historical raw data
  • Query historical data using database queries (advanced users)

Query last data points

The endpoint /epts/last returns the most recent time series data. (EPTS stands for “endpoint time-series”)

The base url is https://tsd.proficloud.io/epts/last with the following possible parameters. The request method is “GET”.

Authentication happens via header “Authorization” with value “Bearer ACCESS_TOKEN“.

Parameters:

  • uuid: One or more UUIDs, comma separated without spaces
  • timeSeriesName: One or more metrics, comma separated without spaces

The response yields in status code 200 and contains a JSON object containing the time series. Possible errors are:

  • 401 – Unauthorized.
  • 500 – e.g. wrong input parameters, content-type, etc. Error message given in response body.
header = {
    "Content-Type": "application/json",
    "Authorization": "Bearer {}".format(access_token)
}

url = "https://tsd.proficloud.io/epts/last?uuid={}&timeSeriesName={}".format(uuid, metrics)

result = requests.get(url, headers=header)
if result.ok:
    print(json.dumps(result.json(), indent=4))

{
    "355f6856-e9a4-469f-8f22-3f8b037d064a": {
        "auto~pressure": [
            {
                "timestamp": "2020-07-21T11:43:05Z",
                "values": {
                    "value": 1000
                }
            }
        ],
        "auto~temperature": [
            {
                "timestamp": "2020-07-21T11:43:05Z",
                "values": {
                    "value": 60
                }
            }
        ],
        "auto~watt": [
            {
                "timestamp": "2020-07-21T11:43:05Z",
                "values": {
                    "value": 5000
                }
            }
        ]
    }
}

Query historical raw data

The endpoint /epts/data returns a block time series data from the selected range. (EPTS stands for “endpoint time-series”)

The base url is https://tsd.proficloud.io/epts/data with the following possible parameters. The request method is “GET”.

Authentication happens via header “Authorization” with value “Bearer ACCESS_TOKEN“.

Parameters:

  • uuid: One or more UUIDs, comma separated without spaces
  • timeSeriesName: One or more metrics, comma separated without spaces
  • fromDate: ISO-8601 encoded start time to retrieve data points from (example: 2017-04-20T00:00:00.000Z)
  • toDate: ISO-8601 encoded end time to retrieve data points to (example: 2017-04-20T00:00:00.000Z)

The response yields in status code 200 and contains a JSON object containing the time series. Possible errors are:

  • 401 – Unauthorized.
  • 500 – e.g. wrong input parameters, content-type, etc. Error message given in response body.
header = {
    "Content-Type": "application/json",
    "Authorization": "Bearer {}".format(access_token)
}
#Dates are ISO-8601!
fromDate = "2020-07-21T10:45:00.000Z"
toDate = "2020-07-21T10:45:05.000Z"
url = "https://tsd.proficloud.io/epts/data?uuid={}&timeSeriesName={}&fromDate={}&toDate={}".format(uuid, metrics, fromDate, toDate)

result = requests.get(url, headers=header)
if result.ok:
    print(json.dumps(result.json(), indent=4))


[
    {
        "355f6856-e9a4-469f-8f22-3f8b037d064a": {
            "auto~pressure": [
                {
                    "timestamp": "2020-07-21T10:45:01Z",
                    "values": {
                        "value": 10
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:02Z",
                    "values": {
                        "value": 0
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:03Z",
                    "values": {
                        "value": 10
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:04Z",
                    "values": {
                        "value": 100
                    }
                }
            ]
        }
    },
    {
        "355f6856-e9a4-469f-8f22-3f8b037d064a": {
            "auto~temperature": [
                {
                    "timestamp": "2020-07-21T10:45:01Z",
                    "values": {
                        "value": 50
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:02Z",
                    "values": {
                        "value": 35
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:03Z",
                    "values": {
                        "value": 45
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:04Z",
                    "values": {
                        "value": 55
                    }
                }
            ]
        }
    },
    {
        "355f6856-e9a4-469f-8f22-3f8b037d064a": {
            "auto~watt": [
                {
                    "timestamp": "2020-07-21T10:45:01Z",
                    "values": {
                        "value": 800
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:02Z",
                    "values": {
                        "value": 300
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:03Z",
                    "values": {
                        "value": 800
                    }
                },
                {
                    "timestamp": "2020-07-21T10:45:04Z",
                    "values": {
                        "value": 1500
                    }
                }
            ]
        }
    }
]

Query historical data using database queries (advanced users)

For advanced users, we also offer the ability to formulate custom queries to read data through the endpoint /query.

The base url is https://tsd.proficloud.io/query with the following possible parameters. The request method is “GET”.

Authentication happens via header “Authorization” with value “Bearer ACCESS_TOKEN“.

Parameters:

  • q: Query for reading tsd data InfluxDB-Style. An example is shown below, detailed information on the queries can be found here

The response yields in status code 200 and contains a JSON object containing the time series. Possible errors are:

  • 401 – Unauthorized.
  • 500 – e.g. wrong input parameters, content-type, etc. Error message given in response body.
query = "SELECT mean(\"value\") FROM \"auto~watt\" WHERE (\"uuid\" = '355f6856-e9a4-469f-8f22-3f8b037d064a') AND time >= now() - 30s GROUP BY time(10s) fill(none)"
header = {
    "Content-Type": "application/json",
    "Authorization": "Bearer {}".format(access_token)
}
url = "https://tsd.proficloud.io/query?q={}".format(query)

result = requests.get(url, headers=header)
if result.ok:
    print(json.dumps(result.json(), indent=4))
{
    "results": [
        {
            "statement_id": 0,
            "series": [
                {
                    "name": "auto~watt",
                    "columns": [
                        "time",
                        "mean"
                    ],
                    "values": [
                        [
                            "2020-07-21T13:01:30Z",
                            4520
                        ],
                        [
                            "2020-07-21T13:01:40Z",
                            7690
                        ],
                        [
                            "2020-07-21T13:01:50Z",
                            23500
                        ]
                    ]
                }
            ]
        }
    ]
}