Notifications

Notifications are implemented over WebSocket connection.

The standard way of using this feature is

  1. authentication + getting the WebSocket endpoint
  2. connection to the endpoint
  3. subscribing for events
  4. closing connection

Requesting endpoint

For getting WebSocket endpoint you need to call

POST https://the.suite.st/api/public/v4/notifications

Body can be empty. For authentication, only service token can be used.

Response:

{
    "url": "wss://example.com/endpoint"
}

The returned url is the temporary endpoint for WebSocket connection. Please note that the url is valid only for a single connection.

Connection

Each connection has maximum duration of 24 hours. After that time, connection will be closed with the code 1001 (going away). For keeping the stream of events uninterrupted, it is needed to create an additional connection before expiration of the current one.

During connection, it is allowed to subscribe and unsubscribe to different events as needed. We strongly recommend unsubscribing from events which are not relevant anymore instead of ignoring them.

The total limit of connections is 3 for each organization. If an extra connection will be created it will be closed with the code 1008 (policy violation).

The WebSocket endpoint is also protected by IP whitelisting. If the connection comes from a not allowed IP address it will be refused before a WebSocket connection is established.

Messages

All messages are encoded in JSON and sent in text frames. Every subscribe/unsubscribe requested must have request id parameter rid of type string. This id is generated by a client, and it will be sent back with the response to the message. It can be e.g. sequence number starting from 1 and incrementing with every message. We don't require this value to be unique, but recommend to keep it unique during a single connection.

The notifications itself don't contain rid identifier.

Responses generally have the following form in case of success:

{
    rid: string,
    ok: true    
}

And the following in case of any errors:

{
    rid: string,
    ok: false,
    errors: Array,    
}

Typical errors include incorrectly formatted messages and attempting to subscribe to non-existing assets. Trying to subscribe to non-existing or non-accessible assets is considered as soft error. This means that if an application attempts to subscribe to a set of accessible and non-accessible assets, the subscription to accessible assets will succeed, while non-accessible assets will be reported.

Please note that in case of a major problem in the message format the connection will be closed with code 1002 (protocol error).

Subscribe to device status

Request

{
    rid: string,
    type: 'subscribeDeviceStatus',
    scope: 'controlUnits'|'devices'|'videoCaptureDevices',
    filter: '*'|string[]
}

The filter contains either a list of ids which should be watched or * which watches all accessible devices. In case there is already an existing subscription for given scope adding other devices will extend the filter of existing subscription. If * is used, it will overwrite any previous explicit list of devices and everything will be listened to. Due to these reasons it is not recommended to combine * and an explicit list for the same scope.

The request initialize watching and the first notification contains the status of all watched devices. The following messages will contain only changes to the previous state.

It can happen that information about some item will be sent repeatedly even there is no change in the value. The receiver of these messages must be able to correctly handle this situation.

The events differ based on scope:

Control Units

{
    type: 'deviceStatus',
    scope: 'controlUnits',
    items: Array<{
        id: Uuid,
        online: boolean,
    }>,
}

Devices

{
    type: 'deviceStatus',
    scope: 'devices',
    items: Array<{
        id: Uuid,
        readyToTest: boolean,
        cameraStatus: 'not-assigned'|'disconnected'|'busy'|'ready',
        error: boolean,
        status?: string,
        failedPlatforms: string[],
    }>,
}

Properties

readyToTest

true if device is ready for testing and there is at least one working platform, false in case of errors or when the device is busy (e.g. executing another test).

cameraStatus

A test with recording requested will be launched only when status is not-assigned, ready or disconnected. For disconnected it will be executed without recording, for busy it will be delayed until a camera changes state. ready can also be shown when camera is streaming or recording.

error

true if there is at least one failed platform or there is another issue (e.g. not connected controller)

status

Present only if error is true. It contains device status returned in device detail feed.

failedPlatforms

Contains not working platforms, for verification that you can launch the test immediately, readyToTest must be true and the test platform must not be listed in failedPlatforms

Video capture devices (cameras)

{
    type: 'deviceStatus',
    scope: 'videoCaptureDevices',
    items: Array<{
        id: Uuid,
        online: boolean,
        batteryState?: {
            isCharging: boolean,
            batteryLevel: number,
        },
        needsUpdate?: boolean, 
    }>,
}

batteryState is present only for online android cameras. batteryLevel is percentage from 0 to 100. needsUpdate indicates that the android camera app does not meet minimal version requirement (it might be missing some features).

Unsubscribe device status

For unsubscribing, it is needed to use the filter complement to subscribe. When subscribe was called with * (all devices) * must be also used for unsubscribing.

{
    rid: string,
    type: 'unsubscribeDeviceStatus',
    scope: 'controlUnits'|'devices'|'videoCaptureDevices',
    filter: '*'|string[],
}

It can happen that shortly after unsubscribing the receiver will get some items which were already unsubscribed. This situation can not be completely prevented and the receiver must handle this situation gracefully.

Subscribe to test pack results

{
    rid: string,
    type: 'subscribeTestPack',
    trigger: 'testPackFinished'|'testPackRecordingUploadFinished'|'firstTestFailed'
    filter?: 'all'|'onlyFailed'
    appVersionId?: string,
    testPackId?: number,
}

Parameters

trigger

  • firstTestFailed - is triggered on the first detected failure for each device
  • testPackFinished - is triggered when all tests of the test pack are finished
  • testPackRecordingUploadFinished - is triggered when all recorded videos are stored

filter

Filter is effective only for trigger testPackFinished, for other triggers it is ignored.

  • all - is reported for all test packs
  • onlyFailed - it reports only failed test packs

Default is all.

appVersionId

Optional. Allows filtering by app version.

testPackId

Optional. Allows filtering by test pack id.

Incoming events

Trigger contains the same value as trigger field for subscribing.

{
    type: 'testPack',
    trigger: 'testPackFinished'|'testPackRecordingUploadFinished',
    testPack: {
        testPackId: number,
        testPackRunId: string,
        appId: string,
        versionId: string,
    }
}
{
    type: 'testPack',
    trigger: 'firstTestFailed',
    testPack: {
        testPackId: number,
        testPackRunId: string,
        appId: string,
        versionId: string,
    },
    failedTest: {
        deviceId: string,
        definitionId: string,
    }
}

Unsubscribe from test pack results

{
    rid: string,
    type: 'unsubscribeTestPack',
    trigger: 'testPackFinished'|'testPackRecordingUploadFinished'|'firstTestFailed'
    filter: 'all'|'onlyFailed'
    appVersionId?: string,
    testPackId?: number,
}

For unsubscribing, it is needed to unsubscribe with the same parameters as for subscribing. Otherwise, the subscription will not be removed.