To restart the user terminal, send a HTTP POST request to `/api/restart` with an empty entity body:
To restart the user terminal, send a HTTP POST request to `/api/restart` with an
empty entity body:
```sh
```sh
curl -X POST http://<hostname>:<port>/api/restart
curl -X POST http://<hostname>:<port>/api/restart
```
```
The above command issues a soft (clean) reboot by invoking `systemctl reboot` on the terminal. If you wish to perform a hard reboot instead, add the query parameter `hard=1` to the URL. In that case the terminal will perform a hard (unclean) restart by writing directly to `/proc/sysrq-trigger`.
The above command issues a soft (clean) reboot by invoking `systemctl reboot` on
the terminal. If you wish to perform a hard reboot instead, add the query
The terminal responds with 202 Accepted with the Transfer-Encoding header set to "chunked". The connection will be kept open until the terminal either reboots or fails to reboot. Data will be sent on the connection formatted line-by-line. The first line is always sent and provides a JSON document with the current boot session ID and uptime (in seconds). No additional lines will be sent unless the terminal fails to reboot.
parameter `hard=1` to the URL. In that case the terminal will perform a hard
(unclean) restart by writing directly to `/proc/sysrq-trigger`.
The terminal responds with 202 Accepted with the Transfer-Encoding header set to
"chunked". The connection will be kept open until the terminal either reboots or
fails to reboot. Data will be sent on the connection formatted line-by-line. The
first line is always sent and provides a JSON document with the current boot
session ID and uptime (in seconds). No additional lines will be sent unless the
If the terminal fails to reboot, it sends a second line with a plain text error message. The connection will then be terminated.
If the terminal fails to reboot, it sends a second line with a plain text error
message. The connection will then be terminated.
To test whether the terminal successfully rebooted and has come back online, the client can periodically send HTTP GET to the API endpoint '/api/restart':
To test whether the terminal successfully rebooted and has come back online, the
client can periodically send HTTP GET to the API endpoint '/api/restart':
```http
```http
GET/api/restartHTTP/1.1
GET/api/restartHTTP/1.1
Host:<hostname>:<port>
Host:<hostname>:<port>
User-Agent:curl/7.65.3
User-Agent:curl/7.65.3
Accept:*/*
Accept:*/*
```
```
Eventually, the terminal responds with 200 OK with the unique ID of the current boot session in a JSON document body:
Eventually, the terminal responds with 200 OK with the unique ID of the current
If the boot_id value is different from value provided in 202 Accepted, the terminal was successfully restarted. If the two values are the same, the terminal was not restarted. A clean user terminal restart can take a couple of minutes.
If the boot_id value is different from value provided in 202 Accepted, the
terminal was successfully restarted. If the two values are the same, the
terminal was not restarted. A clean user terminal restart can take a couple of
minutes.
# Ping
# Ping
To test connectivity to the user terminal, the client can send a HTTP GET request to the API endpoint `/api/ping`:
To test connectivity to the user terminal, the client can send a HTTP GET
request to the API endpoint `/api/ping`:
```sh
```sh
curl http://<hostname>:<port>/api/ping
curl http://<hostname>:<port>/api/ping
```
```
...
@@ -57,7 +73,8 @@ The terminal responds with a 200 OK immediately.
...
@@ -57,7 +73,8 @@ The terminal responds with a 200 OK immediately.
# Platform Information
# Platform Information
Send a HTTP GET request to the API endpoint to `/api/platform` to obtain a description of the terminal's hardware and software:
Send a HTTP GET request to the API endpoint to `/api/platform` to obtain a
description of the terminal's hardware and software:
```sh
```sh
curl http://<hostname>:<port>/api/platform
curl http://<hostname>:<port>/api/platform
```
```
...
@@ -89,7 +106,10 @@ The meaning of the attributes is as follows:
...
@@ -89,7 +106,10 @@ The meaning of the attributes is as follows:
# Configuration
# Configuration
The API endpoint `/api/config` can be used to obtain the current configuration of the user terminal. Currently, the configuration attributes are read-only. They can be only changed via SSH and the terminal needs to be restarted to apply the new configuration.
The API endpoint `/api/config` can be used to obtain the current configuration
of the user terminal. Currently, the configuration attributes are read-only.
They can be only changed via SSH and the terminal needs to be restarted to apply
POST to the API endpoint `/api/pager` to make the user terminal's LED flash briefly. This feature can be used to locate a particular user terminal device.
POST to the API endpoint `/api/pager` to make the user terminal's LED flash
briefly. This feature can be used to locate a particular user terminal device.
```sh
```sh
curl -X POST http://<hostname>:<port>/api/pager
curl -X POST http://<hostname>:<port>/api/pager
```
```
# Time Synchronization
# Time Synchronization
The API endpoint `/api/timesync` provides basic information about the state of NTP clock synchronization on the user terminal. The returned JSON object provides the IP address of the NTP server being used (the base station in most cases) and an offset of the terminal's clock from the NTP server clock in seconds, as measured by the NTP protocol.
The API endpoint `/api/timesync` provides basic information about the state of
NTP clock synchronization on the user terminal. The returned JSON object
provides the IP address of the NTP server being used (the base station in most
cases) and an offset of the terminal's clock from the NTP server clock in
The user terminal provides a set of API endpoints under `/api/settings` for managing settings. The settings are runtime-configurable attributes that control the behavior of the user terminal during the experiment.
The user terminal provides a set of API endpoints under `/api/settings` for
managing settings. The settings are runtime-configurable attributes that control
the behavior of the user terminal during the experiment.
Send a HTTP GET to `/api/settings` to obtain the current setting values:
Send a HTTP GET to `/api/settings` to obtain the current setting values:
In the current version, the API provides supported values only for the settings `codec` and `packet_time`. The first value in each array represents one supported value for the setting 'codec'. The last two numbers represent the minimum and maximum value for the setting 'packet_time' that can be used when the particular codec is selected.
In the current version, the API provides supported values only for the settings
`codec` and `packet_time`. The first value in each array represents one
To change a setting, send a HTTP POST to `/api/settings` with a JSON object with new setting values in the entity body:
supported value for the setting 'codec'. The last two numbers represent the
minimum and maximum value for the setting 'packet_time' that can be used when
the particular codec is selected.
To change a setting, send a HTTP POST to `/api/settings` with a JSON object with
The terminal checks all values submitted by the client, applies constraints, and returns the new setting values in a 200 OK response. To activate the new settings, send a HTTP POST to `/api/settings/apply` with an empty entity body:
The terminal checks all values submitted by the client, applies constraints, and
returns the new setting values in a 200 OK response. To activate the new
settings, send a HTTP POST to `/api/settings/apply` with an empty entity body:
```sh
```sh
curl -X POST http://<hostname>:<port>/api/settings/apply
curl -X POST http://<hostname>:<port>/api/settings/apply
```
```
Once the new settings have been applied, the terminal returns a 200 OK with the new settings in the entity body. Most settings can be applied without the need to restart the VoIP call. If the client changes 'peer', 'codec', or 'packet_time' settings, the VoIP call will be restarted.
Once the new settings have been applied, the terminal returns a 200 OK with the
new settings in the entity body. Most settings can be applied without the need
If you wish to return to the terminal's default (factory) settings, send a HTTP DELETE request to `/api/settings` followed by a HTTP POST to `/api/settings/apply`:
to restart the VoIP call. If the client changes 'peer', 'codec', or
'packet_time' settings, the VoIP call will be restarted.
If you wish to return to the terminal's default (factory) settings, send a HTTP
DELETE request to `/api/settings` followed by a HTTP POST to
curl -X POST http://<hostname>:<port>/api/settings/apply
curl -X POST http://<hostname>:<port>/api/settings/apply
...
@@ -241,25 +282,54 @@ curl -X POST http://<hostname>:<port>/api/settings/apply
...
@@ -241,25 +282,54 @@ curl -X POST http://<hostname>:<port>/api/settings/apply
# Speaker Monitor
# Speaker Monitor
The API endpoint `/audio/speaker` provides access to the audio stream played by the terminal through the speaker in real-time. This API is used by the web-based user interface to "listen in" on the audio played by the terminal. This can be used, for example, by experiment designers to remotely experiment with various kinds of impairments.
The API endpoint `/audio/speaker` provides access to the audio stream played by
the terminal through the speaker in real-time. This API is used by the web-based
To start streaming, send a HTTP GET to the API endpoint. The terminal will respond with a 200 OK with the Transfer-Encoding header set to "chunked". Close the connection to stop streaming. The format of the data stream can be controlled with the following query string parameters:
user interface to "listen in" on the audio played by the terminal. This can be
***type** - Content (stream) type. If unset or set to 'audio/wav', the terminal will include a wav header at the beginning of the stream. If set to 'audio/vnd.lmr.pcm', the header will be omitted and the terminal will stream raw audio data. If the wav header is included, the total length will be set to 0 in the header.
used, for example, by experiment designers to remotely experiment with various
***channels** - Number of audio channels between 1 and 32. The default value is 1.
kinds of impairments.
***rate** - Sample rate. The value must be between 8000 and 96000. The default value is 22050.
***format** - Sample format. One of u8, u-law, A-law, S16LE, S16BE, Float32LE, Float32BE, S32LE, S32BE, S24LE, S24BE, S24_32LE, S24_32BE. The default value is S16LE. Only u8 and S16LE can be used with stream type 'audio/wav'.
To start streaming, send a HTTP GET to the API endpoint. The terminal will
***fragmentSize** - This parameter can be used to control the size of the data fragment, i.e., how often the audio subsystem wakes up the application. The value indirectly controls latency. Please note that the value is advisory only. The actual fragment size will be determined based on a variety of factors and can differ significantly (in both directions) from the value specified by the client.
respond with a 200 OK with the Transfer-Encoding header set to "chunked". Close
the connection to stop streaming. The format of the data stream can be
The following command streams speaker audio from the terminal with sample rate 8000 Hz, 16-bits per sample (little endian). The client requests data chunk once every second (16000 bytes, 2 bytes per sample):
controlled with the following query string parameters:
***type** - Content (stream) type. If unset or set to 'audio/wav', the
terminal will include a wav header at the beginning of the stream. If set to
'audio/vnd.lmr.pcm', the header will be omitted and the terminal will stream
raw audio data. If the wav header is included, the total length will be set
to 0 in the header.
***channels** - Number of audio channels between 1 and 32. The default value
is 1.
***rate** - Sample rate. The value must be between 8000 and 96000. The default
value is 22050.
***format** - Sample format. One of u8, u-law, A-law, S16LE, S16BE, Float32LE,
Float32BE, S32LE, S32BE, S24LE, S24BE, S24_32LE, S24_32BE. The default value
is S16LE. Only u8 and S16LE can be used with stream type 'audio/wav'.
***fragmentSize** - This parameter can be used to control the size of the data
fragment, i.e., how often the audio subsystem wakes up the application. The
value indirectly controls latency. Please note that the value is advisory
only. The actual fragment size will be determined based on a variety of
factors and can differ significantly (in both directions) from the value
specified by the client.
The following command streams speaker audio from the terminal with sample rate
8000 Hz, 16-bits per sample (little endian). The client requests data chunk once
Each user terminal generates time-stamped events during an experiment. The events represent settings changes, user interactions (e.g., PTT button), and recorded audio streams. During the experiment, the terminal stores all generated events locally. At the end of the experiment, the base station retrieves events from all terminals. The API endpoints under `/api/events` can be used to retrieve events stored at the terminal.
Each user terminal generates time-stamped events during an experiment. The
events represent settings changes, user interactions (e.g., PTT button), and
recorded audio streams. During the experiment, the terminal stores all generated
events locally. At the end of the experiment, the base station retrieves events
from all terminals. The API endpoints under `/api/events` can be used to
retrieve events stored at the terminal.
To get all events currently stored at the user terminal, send a HTTP GET request to `/api/events`. The API returns a JSON array with all events currently stored at the terminal.
To get all events currently stored at the user terminal, send a HTTP GET request
to `/api/events`. The API returns a JSON array with all events currently stored
Each event is uniquely identified by a pair <timestamp,id> (timestamps can be duplicate). The attribute "event" identifies the event. An optional attribute "data" can carry additional information in the form of a JSON object.
Each event is uniquely identified by a pair <timestamp,id> (timestamps can be
duplicate). The attribute "event" identifies the event. An optional attribute
"data" can carry additional information in the form of a JSON object.
To obtain events in a specific time interval, the client can add query string parameters 'from' and 'until' to the URL:
To obtain events in a specific time interval, the client can add query string
The values must be in the ISO 8601 timestamp format and are inclusive.
The values must be in the ISO 8601 timestamp format and are inclusive.
To delete events stored at a terminal, e.g., after they have been transferred to the base station, the client can send a HTTP DELETE request to `/api/events`. Without the query string, all events stored at the terminal will be deleted. Use the query string parameters 'from' and 'until' (see above) to only delete events in the specified time range.
To delete events stored at a terminal, e.g., after they have been transferred to
the base station, the client can send a HTTP DELETE request to `/api/events`.
Without the query string, all events stored at the terminal will be deleted. Use
the query string parameters 'from' and 'until' (see above) to only delete events
in the specified time range.
# Real-Time Control & Notifications
# Real-Time Control & Notifications
Each user terminal provides a WebSocket based API for real-time control and notifications. Selected subsystems within the user terminal expose control points through the API or generate state-change notifications. The WebSocket API is based on [socket.io](https://socket.io/). JavaScript clients can use the [socket.io client library](https://socket.io/docs/client-api/) to connect to the user terminal's WebSocket server. The WebSocket server is available at `ws://<hostname>:<port>/socket.io`, where <hostname> is the hostname of the user terminal and <port> is an ephemeral port number of the terminal's HTTP server that can be discovered via ZeroConf.
Each user terminal provides a WebSocket based API for real-time control and
notifications. Selected subsystems within the user terminal expose control
points through the API or generate state-change notifications. The WebSocket API
is based on [socket.io](https://socket.io/). JavaScript clients can use the
[socket.io client library](https://socket.io/docs/client-api/) to connect to the
user terminal's WebSocket server. The WebSocket server is available at
`ws://<hostname>:<port>/socket.io`, where <hostname> is the hostname of
the user terminal and <port> is an ephemeral port number of the terminal's
HTTP server that can be discovered via ZeroConf.
## PTT Button Control
## PTT Button Control
To remotely control the PTT button on the user terminal, [emit](https://socket.io/docs/client-api/#socket-emit-eventName-%E2%80%A6args-ack) a 'ptt' event on the socket.io connection. The event parameter must be a boolean value that represents the desired state of the button (true for pressed, false for released). The terminal will generate a response once the button has been switched to the desired state. The terminal will automatically release the PTT button if the socket.io connection is terminated.
To remotely control the PTT button on the user terminal,
a 'ptt' event on the socket.io connection. The event parameter must be a boolean
value that represents the desired state of the button (true for pressed, false
for released). The terminal will generate a response once the button has been
switched to the desired state. The terminal will automatically release the PTT
button if the socket.io connection is terminated.
When multiple clients control the PTT button simultaneously, the button will be held depressed as long as at least one client keeps the button depressed. The button will be released when no client keeps the button depressed.
When multiple clients control the PTT button simultaneously, the button will be
held depressed as long as at least one client keeps the button depressed. The
button will be released when no client keeps the button depressed.
Note: If the microphone input on the speaker-mic is electrically disconnected while the physical PTT button is released (usually the case), remotely activating the PTT button will not activate the physical microphone.
Note: If the microphone input on the speaker-mic is electrically disconnected
while the physical PTT button is released (usually the case), remotely
activating the PTT button will not activate the physical microphone.
## LED Indicator State
## LED Indicator State
To get notified of changes in the state (color) of the LED indicator on the user terminal, the client emits a 'subscribe' event to the room 'indicator'. The value returned in the response represents the current color of the LED. On each LED color change, the terminal emits a 'change' event to the room with the new color. The subscription is terminated when the client either sends an 'unsubscribe' event to the room, or closes the WebSocket connection.
To get notified of changes in the state (color) of the LED indicator on the user
terminal, the client emits a 'subscribe' event to the room 'indicator'. The
value returned in the response represents the current color of the LED. On each
LED color change, the terminal emits a 'change' event to the room with the new
color. The subscription is terminated when the client either sends an
'unsubscribe' event to the room, or closes the WebSocket connection.
The LED color is represented with a string which contains either a well-known name of the color (white, black), or a CSS-formatted RGB string of the form '#RRGGBB'. All color names generated by the terminal can be parsed with the [color-parse](https://www.npmjs.com/package/color-parse) JavaScript library.
The LED color is represented with a string which contains either a well-known
name of the color (white, black), or a CSS-formatted RGB string of the form
'#RRGGBB'. All color names generated by the terminal can be parsed with the
The room 'state' provides notifications of changes in the state of the PTT button and VoIP call. To receive such notifications, emit a 'subscribe' event to the room. The terminal returns the initial state of the PTT button and the VoIP call in the response to the subscribe. When the state of the PTT button or the VoIP call changes, the terminal emits a 'change' event to the room.
The room 'state' provides notifications of changes in the state of the PTT
button and VoIP call. To receive such notifications, emit a 'subscribe' event to
the room. The terminal returns the initial state of the PTT button and the VoIP
call in the response to the subscribe. When the state of the PTT button or the
VoIP call changes, the terminal emits a 'change' event to the room.
Events emitted to the 'state' room carry a JSON object with the boolean attributes 'ptt' and 'call'. If either attribute is omitted, its value has not changed. The PTT button is depressed if the attribute 'ptt' is true and released otherwise. If the attribute 'call' is true, the terminal is in an active VoIP session with another terminal or the conference server on the base station.
Events emitted to the 'state' room carry a JSON object with the boolean
attributes 'ptt' and 'call'. If either attribute is omitted, its value has not
changed. The PTT button is depressed if the attribute 'ptt' is true and released
otherwise. If the attribute 'call' is true, the terminal is in an active VoIP
session with another terminal or the conference server on the base station.
## Clock State
## Clock State
The socket.io room 'timesync' on the terminal can be used to monitor the state of the NTP subsystem during an ongoing experiment. A NTP client on the user terminal periodically synchronizes the terminal's clock against the master clock on the base station. A 'change' event will be emitted to the room 'timesync' on each synchronization. The event carries a JSON object with two attributes: 'server' and 'offset'. The 'server' attribute contains the IP address of the NTP server (the base station). The attribute 'offset' provides the most recent measured time difference (in seconds) from the NTP server's time.
The socket.io room 'timesync' on the terminal can be used to monitor the state
of the NTP subsystem during an ongoing experiment. A NTP client on the user
terminal periodically synchronizes the terminal's clock against the master clock
on the base station. A 'change' event will be emitted to the room 'timesync' on
each synchronization. The event carries a JSON object with two attributes:
'server' and 'offset'. The 'server' attribute contains the IP address of the NTP
server (the base station). The attribute 'offset' provides the most recent
measured time difference (in seconds) from the NTP server's time.