Read Data Stream

Overview

In the Terra Streaming Protocol, to read a data stream, a consumer connection is formed between a program and the Terra WebSockets service. The program is then able to receive real-time data from any wearable which is currenty connected to Terra through a producer connection.

The following describes the steps required to setup a consumer connection to the Terra WebSockets service. See Setting up a WebSocket Consumer for a specific example written in Node.js.


Connecting

Upon opening a websocket connection to the server, you will immediately be sent an Op 2 HELLO payload containing the heartbeat interval (in milliseconds) that the client should use when sending heartbeat messages to the server.

{
  "op": 2,
  "d": {
    "heartbeat_interval": 40000
  }
}

Once this payload has been received by the client, it is expected to begin heartbeating. See below.


Heartbeating

Heartbeating is effectively a PING/PONG chain which is communicated to the server over a regular interval to inform the server that the connection is still in use.

The first heartbeat should be sent after heartbeat_interval * jitter milliseconds, where jitter is a random value between 0.1 and 1.0. After the initial heartbeat is sent, the client should continue heartbeating at the precise interval given in the HELLO payload. You do not need to account for network latency as the server gives the client some leeway either side of the interval before closing a potentially zombied connection.

Whenever the server receives an Op 0 HEARTBEAT, it will reply with an Op 1 HEARTBEAT_ACK message, indicating that the heartbeat was successfully received. If the client does not receive this response, the connection should be closed and a new connection established.

{
  "op" 0
}
{
  "op": 1
}

Authenticating

Authenticating the websocket connection is done through sending an IDENTIFY payload through the connection which contains a token generated by hitting the appropriate token generation endpoint.

Identify Data Schema

Field NameTypeDescription
tokenStringAuthentication token generated from the appropriate endpoint.
typeIntegerConnection type. Value from the IdentifyType enum.

IdentifyType Enum

NameValue
User (Producer)0
Developer (Consumer)1
{
	"op": 3,
	"d": {
		"token": "OTYwNWFi5ZWQMTAxMjg0Y2Qw.gzrPzZcS3Gy8QDOxbiPRwu30PTB3VxW0eE",
		"type": 0
	}
}

If identification is successful, the server will respond with an Op 4 READY payload:

{
	"op": 4
}

At this point, the connection is ready to send/receive data to/from the server.


Websocket Commands

There are two commands you can send through the websocket connection other than those used for authentication and heartbeating. These are Op 6 SUBMIT and Op 7 REPLAY.

The SUBMIT command is used by a producer client connection to send data to the server for dispatch to consumers.

The REPLAY command is used by a consumer client to request the re-sending of payloads between the given seq lower (and optionally upper) bounds. This is useful when the websocket connection may have closed erroneously so the consumer client missed out on a couple of payloads during that period.

If a producer connection attempts to use the SUBMIT command (and likewise if a consumer attempts to use the REPLAY command) the connection will be immediately closed by the server with a close code of 4004.

Replay Data Object Structure

Field NameTypeDescription
afterLongReplay all payloads after this seq value
before?LongOptional upper bound for replaying of payloads

Example Payloads

{
  "op": 7,
  "d": {
    "after": 28,
    "before": 43
  }
}
{
  "op": 6,
  "d": {...},
  "t": "HEART_RATE"
}

Receiving Data

Data can only be received through consumer connections. When new data is available, an Op 5 DISPATCH payload will be send through the websocket connection containing all information available for the event.

All DISPATCH events will always contains a uid key, which will contain the Terra user ID of the user that the data belongs to. It will also contain a t key, which will contain the event type, i.e. the type of data that the event represents (e.g. HEART_RATE etc). The d sub-object will contain the actual timestamps and values for the data.

{
  "op": 5,
  "d": {
    "t": "2022-05-04T10:26:11.268507+01:00",
    "val": 95
  },
  "uid": "8e864e3a-979a-4d04-b4e9-b6d020f20ba0",
  "seq": 73,
  "t": "HEART_RATE"
}